Frecon code: stats.pal
From PROSE Programming Language - Wiki
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Statistics Gathering for Frecon
% Author: Mark R. Bannister
% Date: 16/Jan/2018
%
% Provides routines for storing and reporting on various interesting
% file statistics
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
~Module
EQUS {[frecon]}
._init
attr/load P0, [psPointer], P1, [psString], P2, [psInteger],
P3, [psFloat], P4, [psTime]
func/def [stat_reset], &[.stat_reset]
func/def [stat_store], &[.stat_store], NULL, P0, [file]
func/def [stat_record], &[.stat_record], NULL,
P1, [path], P2, [mode], P2, [size],
P2, [uid], P1, [uidname],
P2, [gid], P1, [gidname],
P4, [mtime]
func/def [id_report], &[.id_report], NULL, P1, [name]
func/def [top10_report], &[.top10_report], NULL, P1, [c1], P1, [c2]
func/def [owners], &[.owners]
func/def [groups], &[.groups]
func/def [biggest], &[.biggest]
func/def [smallest], &[.smallest]
func/def [newest], &[.newest]
func/def [oldest], &[.oldest]
xtree/global NULL, [uidmap] % UIDs to user names
xtree/global NULL, [gidmap] % GIDs to group names
mtx/global NULL, P2, [size.big.top], #10 % top 10 largest files by size
mtx/global NULL, P1, [size.big.file], #10 % top 10 largest files by name
mtx/global NULL, P2, [size.small.top], #10 % top 10 smallest files by size
mtx/global NULL, P1, [size.small.file], #10 % top 10 smallest files by name
mtx/global NULL, P3, [age.new.top], #10 % top 10 newest files by size
mtx/global NULL, P1, [age.new.file], #10 % top 10 newest files by name
mtx/global NULL, P3, [age.old.top], #10 % top 10 oldest files by size
mtx/global NULL, P1, [age.old.file], #10 % top 10 oldest files by name
local/rtn
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Reset statistics ready for new data collection run
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.stat_reset
obj/addr P0, ![-size.big.top]
obj/addr P1, ![-size.big.file]
obj/addr P2, ![-size.small.top]
obj/addr P3, ![-size.small.file]
obj/addr P4, ![-age.new.top]
obj/addr P5, ![-age.new.file]
obj/addr P6, ![-age.old.top]
obj/addr P7, ![-age.old.file]
reg/clr A
.reset_loop
mtx/set P0, A, [-1]
mtx/set P2, A, [-1]
mtx/set P4, A, [-1]
mtx/set P6, A, [-1]
mtx/set P1, A, []
mtx/set P3, A, []
mtx/set P5, A, []
mtx/set P7, A, []
op/incr
reg/jmplt &[.reset_loop], A, #10
func/rtn
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Record statistics on a given file object
%
% - Take UID/GID and associated names and store in a global tree array
% - Record top ten largest vs. smallest files and newest vs. oldest files
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.stat_store
var/addr P0, [file], P10, [uidmap]
attr/addr P0, P0, [psPointer]
attr/load P12, [psInteger], P13, [psString],
P14, [statSize], P15, [psTime]
%
% Add entry to uidmap xtree
%
var/addr P10, [uidmap]
attr/load P1, [statUid], P2, [statUidName]
attr/xcopy P3, P0, P14
local/jsr &[.get_name_from_id]
local/jsr &[.add_to_map]
%
% Add entry to gidmap xtree
%
var/addr P10, [gidmap]
attr/load P1, [statGid], P2, [statGidName]
attr/xcopy P3, P0, P14
local/jsr &[.get_name_from_id]
local/jsr &[.add_to_map]
%
% Store file size and modification time in top ten tables
% (for regular files only)
%
attr/index A, P0, [statMode]
opa/and #0170000
reg/cmp A, #0100000
ne: func/rtn
attr/xcopy P1, P0, P14
reg/load P10, ![-size], P11, [big], P12, [small]
local/jsr &[.add_to_top10]
var/local P1, P15, [t]
attr/direct P1, P15, P0, [statLastModify]
attr/xcopy P1, P1, [psTimeSeconds]
reg/load P10, ![-age], P11, [new], P12, [old]
local/jsr &[.add_to_top10]
func/rtn
% -------------------------------------------------------------------------- %
%
% Get UID/GID and name from file object, where
% P0 is file object
% P1 is UID or GID attribute
% P2 is UID or GID name attribute
%
.get_name_from_id
attr/copy P1, P0, P1
error/jmp &[.no_name], ![.prose.error.sys.NoEntry]
attr/copy P2, P0, P2
error/jmp
local/rtn
.no_name
error/clr
reg/copy P2, P1 % name is missing, use number for both
local/rtn
%
% Add UID or GID to the appropriate map, where
% P1 is UID or GID number (as a string)
% P2 is user name or group name
% P3 is size of file in bytes (as psInteger)
% P10 is a pointer to the appropriate xtree (uidmap or gidmap)
% P12 is psInteger attribute
% P13 is psString attribute
%
.add_to_map
xtree/set P10, P13, P1, P2
%
% Keep count of the number of times seen
%
xtree/addr P2, P10, P1
var/def P4, P12, P2, [count]
attr/test P4, P12, NULL
eq: op/incr P4
ne: attr/add P4, P12, #1
%
% Keep count of total bytes owned by this ID
%
var/def P4, P12, P2, [bytes]
attr/test P4, P12, NULL
eq: opo/add P4, P4, P3
ne: attr/add P4, P12, P3
local/rtn
% -------------------------------------------------------------------------- %
%
% Add a top 10 statistic (based on size or age of file), where
% P0 is file or variable object containing the file path
% P1 is the value to add
% P10 is a pointer to the 'size' or 'age' container underneath
% which the top 10 tables can be found
% P11 is the text 'big' or 'new' depending on the type of table
% P12 is the text 'small' or 'old' depending on the type of table
%
.add_to_top10
attr/def PUSH, P1 % duplicate XVALUE in P1
reg/load P2, #2
obj/addr P11, P10, P11
stack/push P12
local/jsr &[.store_top10]
reg/load P2, #1
obj/addr P11, P10, PULL
stack/pull P1
local/jmp &[.store_top10]
% -------------------------------------------------------------------------- %
%
% Add a top 10 statistic, called by add_to_top10, where
% P0 is file or variable object containing the file path
% P1 is number to compare
% P2 is 2 for table of biggest or 1 for table of smallest
% P11 is a pointer to the 'big' or 'small' container
%
.store_top10
obj/addr P12, P11, [file]
obj/addr P11, P11, [top]
op/shl P2, P2, #1
reg/clr A
.top10_loop
mtx/get P4, P11, A
reg/jmpeq &[.top10_insert], P4, [-1]
reg/cmp P1, P4
reg/jmpeq &[.top10_insert], SCMP, P2
op/incr
reg/jmplt &[.top10_loop], A, #10
local/rtn
.top10_insert
%
% Insert value in P1 into array in P11, position A
% Also keep array P12 up-to-date with file path
%
reg/load P3, #8, P4, #9
.insert_loop
reg/jmplt &[.insert_end], P3, A
mtx/get P5, P11, P3
reg/jmpeq &[.insert_next], P5, [-1]
mtx/get P6, P12, P3
mtx/set P11, P4, P5
mtx/set P12, P4, P6
.insert_next
reg/jmpeq &[.insert_end], P3, A
op/decr P3, P4
local/jmp &[.insert_loop]
.insert_end
mtx/set P11, A, P1
class/test P0, [psFile]
eq: attr/copy PUSH, P0, [psFilePath]
ne: attr/copy PUSH, P0, [psString]
mtx/set P12, A, PULL
local/rtn
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Record statistics (as stat_store) but when reloaded from a database record
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.stat_record
%
% Add entry to uidmap xtree
%
var/addr P1, [uid], P2, [uidname], P3, [size], P10, [uidmap]
attr/load P12, [psInteger], P13, [psString]
attr/copy P1, P1, P12
attr/copy P2, P2, P13
attr/xcopy P3, P3, P12
local/jsr &[.add_to_map]
%
% Add entry to gidmap xtree
%
var/addr P1, [gid], P2, [gidname], P3, [size], P10, [gidmap]
attr/copy P1, P1, P12
attr/copy P2, P2, P13
attr/xcopy P3, P3, P12
local/jsr &[.add_to_map]
%
% Store file size and modification time in top ten tables
% (for regular files only)
%
var/addr P0, [mode]
attr/index A, P0, P12
opa/and #0170000
reg/cmp A, #0100000
ne: func/rtn
var/addr P1, [size]
attr/xcopy P1, P1, P12
var/addr P0, [path]
reg/load P10, ![-size], P11, [big], P12, [small]
local/jsr &[.add_to_top10]
var/addr P1, [mtime]
attr/xcopy P1, P1, [psTimeSeconds]
reg/load P10, ![-age], P11, [new], P12, [old]
local/jsr &[.add_to_top10]
func/rtn
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Display owners report
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.owners
func/call NULL, [id_report], [uidmap]
func/rtn
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Display groups report
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.groups
func/call NULL, [id_report], [gidmap]
func/rtn
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Report back-end for owners and groups reports
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.id_report
var/addr P0, [name]
attr/copy P1, P0, [psString]
var/addr P0, P1
reg/load P0, (P0)
reg/jmpeq &[.report_end], P0, NULL
attr/load P10, [pn], P11, [psString], P12, [psStreamOut], P13, [psInteger]
reg/load P15, ![.prose.sys.io.stdout]
reg/copy P2,
[Owner UID Files Bytes
==============================================================================
]
reg/cmp P1, [gidmap]
eq: reg/save P2, ([Group], #0)
eq: reg/save P2, ([GID], #33)
attr/add P15, P12, P2
.report_loop
reg/load P1, (P0)
reg/jmpeq &[.report_end], P1, NULL
attr/copy P2, P1, P10
attr/copy P3, P1, P11
obj/child PUSH, P1, [count]
attr/copy P4, PULL, P13
obj/child PUSH, P1, [bytes]
attr/copy P5, PULL, P13
reg/copy P6, [ \n]
reg/save P6, (P3, #0)
reg/save P6, (P2, #33)
reg/save P6, (P4, #44)
reg/save P6, (P5, #56)
attr/add P15, P12, P6
local/jmp &[.report_loop]
.report_end
func/call NULL, [get_enter]
func/rtn
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Display biggest files report
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.biggest
func/call NULL, [top10_report], [size], [big]
func/rtn
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Display smallest files report
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.smallest
func/call NULL, [top10_report], [size], [small]
func/rtn
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Display newest files report
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.newest
func/call NULL, [top10_report], [age], [new]
func/rtn
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Display oldest files report
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.oldest
func/call NULL, [top10_report], [age], [old]
func/rtn
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Report back-end for Top 10 reports
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.top10_report
var/addr P0, [c1], P1, [c2]
attr/copy P0, P0, [psString]
attr/copy P1, P1, [psString]
obj/addr P2, ![-], P0, P1, [top]
obj/addr P3, ![-], P0, P1, [file]
mtx/get P4, P2, #0
reg/jmpeq &[.top10_report_end], P4, [-1]
attr/load P10, [pn], P11, [psString], P12, [psStreamOut],
P13, [psInteger], P14, [psFloat]
reg/load P15, ![.prose.sys.io.stdout]
reg/copy P4,
[Size in Bytes Path
==============================================================================
]
reg/cmp P0, [age]
eq: reg/save P4, ([Last Modified], #0)
attr/add P15, P12, P4
reg/clr A
.top10_report_loop
mtx/get P4, P2, A
reg/jmpeq &[.top10_report_end], P4, [-1]
mtx/get P5, P3, A
reg/cmp P0, [age]
eq: local/jsr &[.top10_convert_time]
ne: reg/copy P4, P4
reg/copy P5, P5
reg/copy P6, [ ]
reg/save P6, (P4, #0)
reg/xload P7, (P5)
opx/add P7, P7, #32
reg/save P6, (P7) % resize to include full path
reg/save P6, (P5, #31) % write path to position 31
op/decr P7
reg/save P6, (#0xa000000, P7, #0xff000000) % append newline
attr/add P15, P12, P6
op/incr
reg/jmplt &[.top10_report_loop], A, #10
.top10_report_end
func/call NULL, [get_enter]
func/rtn
~time_pos
EQUB { 0, 4, 0, 4 }
EQUS { [/] }
EQUB { 4, 2, 5, 7 }
EQUS { [/] }
EQUB { 6, 2, 8, 10 }
EQUS { [ ] }
EQUB { 8, 2, 11, 13 }
EQUS { [:] }
EQUB { 10, 2, 14, 16 }
EQUS { [:] }
EQUB { 12, 2, 17, 19 }
EQUS { [.] }
EQUB { 15, -1, 20, -1 }
.top10_convert_time
%
% Convert time as psFloat in P4 to something
% a bit more human-friendly
%
stack/push P8, P9, P10, P11, P12, P13, P14, P15, A
var/local P4, [psTime], [t], P4
attr/copy P4, P4, [psTime]
reg/copy P8, [ ]
reg/load P15, (&[~time_pos])
.ct_loop
reg/load P10, (P15)
reg/load P11, (P15)
reg/load P12, (P15)
reg/load P13, (P15)
reg/load P14, (P15)
reg/copy P9, (P4, P10, P11)
reg/save P8, (P9, P12)
reg/cmp P14, NULL
ne: reg/save P8, (P14, P13)
ne: local/jmp &[.ct_loop]
reg/load P9, (P8, #19) % strip final dot
reg/cmp P9, #0x2e202020 % if no sub-second component
eq: reg/save P8, ([ ], #19)
reg/xscan P9, P8, [Z] % strip timezone Z identifier
reg/cmp P9, NULL
ne: reg/save P8, (P9)
reg/move P4, P8
stack/pull A, P15, P14, P13, P12, P11, P10, P9, P8
local/rtn