<> <> <> DIRECTORY Commander, CommandTool, File, FileBackdoor, IO, Real, RealFns, Rope USING[ROPE ], VM; HistoVAM: CEDAR PROGRAM IMPORTS Commander, CommandTool, File, FileBackdoor, IO, Real, RealFns, VM = BEGIN LogRunLenght: TYPE = [2..6]; Histogram: TYPE = RECORD[ counts: ARRAY LogRunLenght OF INT, freeSpace: ARRAY LogRunLenght OF INT, mixEndsAlloc: INT, mixEndsFree: INT, mixAdjMix: INT ] _ [ALL[0], ALL[0], 0, 0, 0]; computeHistogram: PROCEDURE[volume: File.Volume] RETURNS[ histogram: Histogram ] = TRUSTED BEGIN vamFile: File.Handle = File.Open[volume, FileBackdoor.GetRoot[volume, VAM].fp]; filePages: INT = File.Info[vamFile].size; fileWords: INT = File.wordsPerPage * filePages; limit: INT = FileBackdoor.GetVolumePages[volume].size; interval: VM.Interval = VM.Allocate[VM.PagesForWords[fileWords]]; buffer: LONG POINTER TO bufferObject _ VM.AddressForPageNumber[interval.page]; bufferObject: TYPE = RECORD[ SEQUENCE COMPUTED CARDINAL OF WORD ]; state: {alloc, free, mixed} _ alloc; all0: WORD = 000000B; all1: WORD = 177777B; updateHistogram: SAFE PROCEDURE = CHECKED BEGIN OPEN histogram; logRun _ MIN[ Real.Fix[RealFns.Log[4, runCount]], 6 ]; counts[logRun] _ counts[logRun]+1; freeSpace[logRun] _ freeSpace[logRun]+runCount; END; runCount: INT _ 0; -- Length of the current free run logRun: LogRunLenght; File.Read[file: vamFile, from: [0], to: LOOPHOLE[buffer], nPages: filePages]; BEGIN OPEN histogram; <> FOR i: INT _ 0, i+1 WHILE i < limit/16 DO SELECT state FROM alloc => { SELECT buffer[i] FROM all1 => NULL; all0 => { state _ free; runCount _ 16; }; ENDCASE => { state _ mixed; mixEndsAlloc _ mixEndsAlloc+1; }; }; free => { SELECT buffer[i] FROM all1 => { updateHistogram; state _ alloc; }; all0 => { runCount _ runCount+16; }; ENDCASE => { updateHistogram; state _ mixed; mixEndsFree _ mixEndsFree+1; }; }; mixed => { SELECT buffer[i] FROM all1 => { state _ alloc; }; all0 => { state _ free; runCount _ 16; }; ENDCASE => { mixAdjMix _ mixAdjMix+1; }; }; ENDCASE; ENDLOOP; IF state=free THEN { updateHistogram; }; VM.Free[interval]; END; END; HistoVAMProc: Commander.CommandProc = { <<[cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]>> argv: CommandTool.ArgumentVector; volume: File.Volume; volumeName: Rope.ROPE; histogram: Histogram; argv _ CommandTool.Parse[cmd: cmd ! CommandTool.Failed => { msg _ errorMsg; CONTINUE; }]; IF argv = NIL THEN GOTO Failed; IF argv.argc = 1 THEN { volume _ File.SystemVolume[]; volumeName _ File.GetVolumeName[volume]; } ELSE { IF argv.argc # 2 THEN GOTO Usage; volume _ File.FindVolumeFromName[argv[1]]; volumeName _ argv[1]; }; IF volume = NIL THEN GOTO NoVolume; histogram _ computeHistogram[volume]; cmd.out.PutF[ "Distribution of free page run sizes on Volume: %g:\n", IO.rope[volumeName] ]; cmd.out.PutF[ "\tSizes\t\tnRuns\t\tSpace\n" ]; cmd.out.PutF[ "\t1..15\t\t\t%g\t\t~8*%g\n", IO.int[histogram.mixEndsAlloc+histogram.mixEndsFree+histogram.mixAdjMix], IO.int[histogram.mixEndsAlloc+histogram.mixEndsFree+histogram.mixAdjMix] ]; cmd.out.PutF[ "\t16..63\t\t\t%g\t\t%g\n", IO.int[histogram.counts[2]], IO.int[histogram.freeSpace[2]] ]; cmd.out.PutF[ "\t64..255\t\t%g\t\t%g\n", IO.int[histogram.counts[3]], IO.int[histogram.freeSpace[3]] ]; cmd.out.PutF[ "\t256..1023\t\t%g\t\t%g\n", IO.int[histogram.counts[4]], IO.int[histogram.freeSpace[4]] ]; cmd.out.PutF[ "\t1024..4095\t\t%g\t\t%g\n", IO.int[histogram.counts[5]], IO.int[histogram.freeSpace[5]] ]; cmd.out.PutF[ "\t4096...\t\t\t%g\t\t%g\n", IO.int[histogram.counts[6]], IO.int[histogram.freeSpace[6]] ]; EXITS Failed => RETURN[$Failure, msg]; Usage => RETURN[$Failure, " Usage: ShowVAM volumename\n"]; NoVolume => RETURN[$Failure, " No such volume\n"]; }; Commander.Register[key: "HistoVAM", proc: HistoVAMProc, doc: "Show a histogram of free run lenghts in the VAM (volume allocation map) of the given logical volume (SystemVolume if omitted)"]; END. <<>> <> <> <> <<>> <> <> <<>>