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