Frecon code: stats.pal

From PROSE Programming Language - Wiki
Jump to: navigation, search
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% 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