PigsInSpace.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bob Hagmann July 4, 1986 9:19:31 am PDT
DIRECTORY
Commander USING [CommandProc, Handle, Register],
CommandTool USING [ArgumentVector, Failed, Parse],
Convert,
File,
FileBackdoor,
FSBackdoor,
IO,
Process USING [CheckForAbort],
Rope;
PigsInSpace: CEDAR PROGRAM
IMPORTS Commander, CommandTool, Convert, File, FileBackdoor, FSBackdoor, IO, Process, Rope
= BEGIN
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
PigsInSpaceProc: Commander.CommandProc = {
[cmd: REF CommandObject] RETURNS [result: REFNIL, msg: ROPENIL]
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: ROPENIL,
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: BOOLTRUE;
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: INTLAST[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: BOOLTRUE;
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: INTLAST[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: BOOLTRUE;
doLocal: BOOLTRUE;
doRoot: BOOLTRUE;
volumeName: ROPENIL;
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: NATLAST[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}* "];
};
Init: PROCEDURE = {
Commander.Register[
"///Commands/PigsInSpace", PigsInSpaceProc,
"PigsInSpace {count ← 10} {-n count | -v volume | -c | -l}* -- list `count' largest files (default 10) on volume 'volume' (default is the system volume). The 'c' switch means don't list cached file and the 'l' switch means don't list local files"];
};
Initialization
Init[];
END.
Bob Hagmann April 10, 1985 7:01:05 am PST
created
Bob Hagmann May 20, 1985 8:17:12 am PDT
changes to: DIRECTORY, PigsInSpace, infoProc (local of PigsInSpaceProc)
Bob Hagmann June 20, 1985 10:14:59 am PDT
modified to do local files and any logical volume
Bob Hagmann July 4, 1986 9:14:34 am PDT
changes to: PigsInSpaceProc, DIRECTORY, PigsInSpace, doAFile (local of PigsInSpaceProc), acceptProc (local of PigsInSpaceProc)