PigsInSpaceProc: Commander.CommandProc = {
[cmd: REF CommandObject] RETURNS [result: REF ← NIL, msg: ROPE ← NIL]
CommandObject = [
in, out, err: STREAM, commandLine, command: ROPE,
propertyList: List.AList, procData: CommandProcHandle]
Enumerate the cache, and find the "count" largest files. Enumeration is fairly slow since the files must actually be opened to discover their size. We keep an unsorted sequence of the largest files found so far. This data structure is OK since we are so heavily disk bound.
out: STREAM ← cmd.out;
count: INT ← 10;
volume: File.Volume;
fileCounter: INT ← 0 ;
pigItem:
TYPE =
RECORD [
fullGName: ROPE ← NIL,
size: INT ← 0
];
pigArray:
TYPE =
RECORD [
items: SEQUENCE length: NAT OF pigItem
];
pigs: REF pigArray;
minSize: INT ← 0;
lastEntry: FSBackdoor.EntryPtr;
localCount, cachedCount, attachedCount: INT ← 0;
matchProc:
UNSAFE
PROC [entry: FSBackdoor.EntryPtr]
RETURNS [accept, stop:
BOOLEAN] ={
lastEntry ← entry;
RETURN[TRUE, FALSE];
};
acceptProc:
SAFE
PROC
RETURNS [stop:
BOOLEAN] =
TRUSTED {
thisFP: File.FP ← File.nullFP;
file: File.Handle;
fileName: ROPE;
fileNameLessVersion: ROPE;
nameBodyRopeText: Rope.Text;
stop ← FALSE;
nameBodyRopeText ← FSBackdoor.TextFromTextRep[@lastEntry[lastEntry.nameBody]];
fileNameLessVersion ← IF nameBodyRopeText.InlineFetch[0] # '[ THEN Rope.Concat["[]<>", nameBodyRopeText] ELSE nameBodyRopeText;
fileName ← Rope.Cat[fileNameLessVersion, "!", Convert.RopeFromCard[from: lastEntry.version, showRadix: FALSE]];
WITH e: lastEntry^
SELECT
FROM
local => {
thisFP ← e.fp;
localCount ← localCount + 1;
};
cached => {
thisFP ← e.fp;
cachedCount ← cachedCount + 1;
};
attached => {
attachedCount ← attachedCount + 1;
};
ENDCASE ;
IF thisFP # File.nullFP
THEN {
-- skips attachments
errorFree: BOOL ← TRUE;
fileCounter ← fileCounter + 1;
IF fileCounter
MOD 100 = 0
THEN {
IF fileCounter MOD 1000 = 0 THEN cmd.out.PutF["(%g) ", IO.int[fileCounter]]
ELSE cmd.out.PutRope[". "];
};
doAFile[thisFP: thisFP, fileName: fileName];
file ← File.Open[volume, thisFP ! File.Error => {
SELECT why FROM
unknownFile, unknownPage, inconsistent, software => {
errorFree ← FALSE;
CONTINUE;
};
ENDCASE;
};
];
IF errorFree THEN {
size: File.PageCount;
victim: NAT ← count+1;
minSizeSoFar: INT ← LAST[INT];
Process.CheckForAbort[];
[size: size] ← File.Info[file];
IF size < minSize THEN RETURN[FALSE];
FOR index: INT IN [0..count) DO
IF pigs[index].size <= minSize THEN {victim ← index; EXIT};
IF pigs[index].size < minSizeSoFar THEN {
victim ← index;
minSizeSoFar ← pigs[index].size;
};
ENDLOOP;
IF victim = count+1 THEN RETURN[TRUE];
pigs[victim].fullGName ← fileName;
minSize ← pigs[victim].size;
pigs[victim].size ← size;
RETURN[FALSE];
};
};
};
doAFile:
PROC [thisFP: File.
FP, fileName:
ROPE]= {
errorFree: BOOL ← TRUE;
file: File.Handle;
file ← File.Open[volume, thisFP ! File.Error => {
SELECT why
FROM
unknownFile, unknownPage, inconsistent, software => {
errorFree ← FALSE;
CONTINUE;
};
ENDCASE;
};
];
IF errorFree
THEN {
size: File.PageCount;
victim: NAT ← count+1;
minSizeSoFar: INT ← LAST[INT];
Process.CheckForAbort[];
[size: size] ← File.Info[file];
IF size < minSize THEN RETURN;
FOR index:
INT
IN [0..count)
DO
IF pigs[index].size <= minSize THEN {victim ← index; EXIT};
IF pigs[index].size < minSizeSoFar
THEN {
victim ← index;
minSizeSoFar ← pigs[index].size;
};
ENDLOOP;
IF victim = count+1 THEN RETURN;
pigs[victim].fullGName ← fileName;
minSize ← pigs[victim].size;
pigs[victim].size ← size;
};
};
argv: CommandTool.ArgumentVector ← CommandTool.Parse[cmd
! CommandTool.Failed => {msg ← errorMsg; GO TO Usage}];
counter: INT ← 1;
expectType: TYPE = {num, volName, switch};
expecting: expectType ← num;
doCached: BOOL ← TRUE;
doLocal: BOOL ← TRUE;
doRoot: BOOL ← TRUE;
volumeName: ROPE ← NIL;
pattern: Rope.Text;
IF argv.argc < 1 THEN GOTO Usage;
WHILE counter < argv.argc
DO
arg: ROPE ← argv[counter];
IF arg.IsEmpty[] THEN GOTO Usage;
IF arg.InlineFetch[0] = '-
THEN {
c: CHAR;
IF arg.Length[] # 2 THEN GOTO Usage;
c ← arg.InlineFetch[1] ;
SELECT c
FROM
'n => expecting ← num;
'v => expecting ← volName;
'c => doCached ← FALSE;
'l => doLocal ← FALSE;
'r => doRoot ← FALSE;
ENDCASE;
}
ELSE {
-- NOT '-
SELECT expecting
FROM
num => count ← Convert.IntFromRope[arg ! Convert.Error => GOTO Usage];
volName => volumeName ← arg;
switch => GOTO Usage;
ENDCASE;
expecting ← switch;
};
counter ← counter + 1;
ENDLOOP;
IF count <= 0 THEN GOTO Usage;
pigs ← NEW[pigArray[count]];
SELECT
TRUE
FROM
doCached AND doLocal => pattern ← "";
~doCached AND doLocal => pattern ← "";
doCached AND ~doLocal => pattern ← "[*";
~doCached AND ~doLocal => GOTO Usage;
ENDCASE;
volume ← IF volumeName = NIL THEN File.SystemVolume[] ELSE File.FindVolumeFromName[volumeName];
IF volume = NIL THEN GOTO Usage;
IF doRoot
THEN {
FOR rootFile: File.VolumeFile IN File.VolumeFile
DO
rootFP: File.FP ← File.nullFP;
fileName: ROPE;
rootFP ← FileBackdoor.GetRoot[volume, rootFile ! File.Error => LOOP].fp;
IF rootFP = File.nullFP THEN LOOP;
fileName ←
SELECT rootFile
FROM
checkpoint => "Checkpoint",
microcode => "Microcode",
germ => "Germ",
bootFile => "BootFile",
debugger => "Debugger",
debuggee => "Debuggee",
VM => "VM",
VAM => "VAM",
client => "FS-root",
alpine => "Alpine-root",
ENDCASE => "Other-root";
fileName ← Rope.Concat[fileName, " Root File"];
doAFile[rootFP, fileName];
ENDLOOP;
};
cmd.out.PutRope["Starting enumerate of FS BTree. A dot is printed for every 100 files\n"];
FSBackdoor.Enumerate[volName: volumeName, nameBodyPattern: pattern, localOnly: ~doCached, allVersions: TRUE, version: FSBackdoor.noVersion, matchProc: matchProc, acceptProc: acceptProc];
cmd.out.PutF["\n File counts: %g local, %g cached, and %g attachments\n", [integer[localCount]], [integer[cachedCount]], [integer[attachedCount]]];
FOR outputLine:
INT
IN [0..count)
DO
biggestIndex: NAT ← LAST[NAT];
biggestSize: INT ← 0;
FOR index:
INT
IN [0..count)
DO
IF pigs[index].size > biggestSize
THEN {
biggestIndex ← index;
biggestSize ← pigs[index].size;
};
ENDLOOP;
IF biggestIndex = LAST[NAT] THEN EXIT;
IO.PutF[out, " %-30g %g pages\n", [rope[pigs[biggestIndex].fullGName]], [integer[pigs[biggestIndex].size]]];
pigs[biggestIndex].size ← -1;
ENDLOOP;
pigs ← NIL;
EXITS
Usage => RETURN[$Failure, "Usage: PigsInSpace {count ← 10} {-n count | -v volume | -c | -l}* "];
};