LogInfo.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last Edited by: Willie-Sue, August 9, 1985 1:22:03 pm PDT
DIRECTORY
Commander USING [CommandProc, Register],
FS USING [Error, PagesForBytes],
FSRope USING [StreamFromRope],
IO,
Process USING [Detach],
RefTab USING [EachPairAction, Ref, Val, Create, Fetch, Store, Pairs],
Rope USING [ROPE],
ViewerClasses USING [Viewer],
ViewerIO USING [CreateViewerStreams],
ViewerOps USING [FindViewer, OpenIcon],
WalnutDefs USING [Error],
WalnutLog USING [LogLength, OpenLogStreams, ReturnCurrentLogStreams, ShutdownLog],
WalnutLogExpunge USING [CopyEntry, EndExpunge, GetExpungeProgress, PeekEntry, SetPosition, Shutdown, SkipEntry, StartExpunge],
WalnutRoot USING [CommitAndContinue, Open, RegisterStatsProc, Shutdown, StartTransaction, SwapLogs, UnregisterStatsProc],
WalnutStream USING [FindNextEntry, Open, PeekEntry];
LogInfo: CEDAR PROGRAM
IMPORTS
Commander, FS, FSRope, IO, Process, RefTab, ViewerIO, ViewerOps,
WalnutDefs, WalnutLog, WalnutLogExpunge, WalnutRoot, WalnutStream
= BEGIN
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
out: STREAMNIL;
debugging: BOOLFALSE;
Xyz: TYPE = REF ValueObject;
ValueObject: TYPE = RECORD[num, bytes: INT ← 0];
TSStream: PROC[name: ROPE] RETURNS [in, out: STREAM] = {
v: ViewerClasses.Viewer ← ViewerOps.FindViewer[name];
[in, out] ← ViewerIO.CreateViewerStreams[name, v];
IF v#NIL THEN IF v.iconic THEN ViewerOps.OpenIcon[v];
};
SetupScan: PROC[commandLine: ROPE] = {
clStream: STREAM ← FSRope.StreamFromRope[commandLine];
logFile: ROPE;
logFile ← clStream.GetTokenRope[IO.IDProc ! IO.EndOfStream => CONTINUE].token;
Scan[logFile];
};
Scan: PROC[logFile: ROPE] = {
strm: STREAM;
logLength: INT;
out ← TSStream["Walnut Rescue"].out;
out.PutRope["\n\n************************************************************\n"];
out.PutF["\t\t Scan Log for Entries, started @ %g\n", IO.time[] ];
IF logFile = NIL THEN {
out.PutRope["No input file specified - quitting\n"];
RETURN;
};
strm ← WalnutStream.Open[name: logFile, readOnly: TRUE ! FS.Error => CONTINUE];
IF strm = NIL THEN {
out.PutF["Could not open %g\n", IO.rope[logFile] ];
RETURN
};
IF (logLength ← strm.GetLength[]) = 0 THEN {
out.PutF["The log file %g is empty\n", IO.rope[logFile] ];
strm.Close[];
RETURN
};
out.PutF["Scanning the logfile: %g\n\n(0)", IO.rope[logFile] ];
strm.SetIndex[0];
DoScan[strm, logLength];
strm.Close[];
};
SetupFix: PROC[commandLine: ROPE] = {
clStream: STREAM ← FSRope.StreamFromRope[commandLine];
rootFile: ROPE;
entriesToIgnore, lastToIgnore: LIST OF ATOM;
this: ATOM;
rootFile ← clStream.GetTokenRope[IO.IDProc ! IO.EndOfStream => CONTINUE].token;
DO
this ← clStream.GetAtom[ ! IO.EndOfStream => { this ← NIL; CONTINUE} ];
IF this = NIL THEN EXIT;
IF entriesToIgnore = NIL THEN entriesToIgnore ← lastToIgnore ← LIST[this]
ELSE { lastToIgnore.rest ← LIST[this]; lastToIgnore ← lastToIgnore.rest; };
ENDLOOP;
DoFix[rootFile, entriesToIgnore];
};
Fix: PROC[rootFile: ROPE, entriesToIgnore: LIST OF REF ANY] = {
toBeIgnored: LIST OF ATOM;
FOR ei: LIST OF REF ANY ← entriesToIgnore, ei.rest UNTIL ei = NIL DO
ax: ATOMNARROW[ei.first];
toBeIgnored ← CONS[ax, toBeIgnored];
ENDLOOP;
DoFix[rootFile, toBeIgnored];
};
DoFix: PROC[rootFile: ROPE, toBeIgnored: LIST OF ATOM] = {
previousAt, at: INT ← -1;
ident: ATOM;
logLength, newLen: INT;
expungeID: INT;
num, numSinceFlush: INT ← 0;
bytesBetweenFlushes: INT = 200000;
pagesNeeded: INT;
out ← TSStream["Walnut Rescue"].out;
out.PutRope["\n\n************************************************************\n"];
out.PutF["\t\tFix Log, started at %g\n", IO.time[] ];
IF rootFile = NIL THEN {
out.PutRope["\nNo input file specified\n"];
RETURN;
};
out.PutF["\tFixing the rootfile: %g\n", IO.rope[rootFile] ];
WalnutRoot.RegisterStatsProc[Report];
[] ← WalnutRoot.Open[rootFile];
[] ← WalnutRoot.StartTransaction[];
[] ← WalnutLog.OpenLogStreams[];
pagesNeeded ← FS.PagesForBytes[logLength ← WalnutLog.LogLength[]];
WalnutLog.ReturnCurrentLogStreams[];
expungeID ← WalnutLogExpunge.StartExpunge[pagesNeeded];
IF toBeIgnored = NIL THEN
out.PutRope[" No entries except invalid ones will be ignored\n"]
ELSE {
out.PutRope["\n Ignoring the following entries: "];
FOR ignore: LIST OF ATOM ← toBeIgnored, ignore.rest UNTIL ignore = NIL DO
out.PutF[" %g,", IO.atom[ignore.first]];
ENDLOOP;
out.PutChar['\n];
};
[] ← WalnutLogExpunge.SetPosition[0];
BEGIN ENABLE WalnutDefs.Error => {
out.PutF["WalnutDefs Error: code: %g, info: %g @ %g",
IO.atom[code], IO.rope[explanation], IO.time[] ];
GOTO error;
};
DO
bytesThisCopy, newPos: INT;
didSkip: BOOLFALSE;
previousAt ← at;
[ident, , at]← WalnutLogExpunge.PeekEntry[];
IF ident = NIL AND at # -1 THEN EXIT;
IF at = previousAt THEN {  -- probably transAbort
[] ← WalnutLogExpunge.SkipEntry[];
out.PutF["\n At pos %g a second time\n", IO.int[at]];
LOOP
};
FOR ignore: LIST OF ATOM ← toBeIgnored, ignore.rest UNTIL ignore = NIL DO
IF ident = ignore.first THEN
{ [] ← WalnutLogExpunge.SkipEntry[]; didSkip ← TRUE; EXIT; };
ENDLOOP;
IF didSkip THEN LOOP;
IF ident = $LogFileInfo THEN { [] ← WalnutLogExpunge.SkipEntry[]; LOOP };
IF ~ValidIdent[ident, at] THEN
{ [] ← WalnutLogExpunge.SkipEntry[]; LOOP };
[newPos, bytesThisCopy] ← WalnutLogExpunge.CopyEntry[];
IF debugging THEN out.PutF[" - copied to %g", IO.int[newPos]];
IF ident = $CreateMsg THEN {
IF (num← num + 1) MOD 10 = 0 THEN
IF num MOD 100 = 0 THEN out.PutF["(%g)", IO.int[num]]
ELSE out.PutChar['.];
};
numSinceFlush ← numSinceFlush + bytesThisCopy;
IF numSinceFlush >= bytesBetweenFlushes THEN {
[] ← WalnutLogExpunge.GetExpungeProgress[];
WalnutRoot.CommitAndContinue[];
numSinceFlush ← 0;
};
ENDLOOP;
[] ← WalnutLogExpunge.GetExpungeProgress[];
WalnutLogExpunge.EndExpunge[];
WalnutRoot.CommitAndContinue[];
[ , newLen] ← WalnutRoot.SwapLogs[expungeID];
WalnutRoot.UnregisterStatsProc[Report];
WalnutRoot.Shutdown[];
out.PutF["\n The old log was %g bytes, the new log is %g bytes",
IO.int[logLength], IO.int[newLen]];
out.PutF["\n The log file contains %g messages", IO.int[num]];
EXITS
error => {
WalnutLogExpunge.EndExpunge[];
WalnutRoot.UnregisterStatsProc[Report];
WalnutLogExpunge.Shutdown[];
WalnutLog.ShutdownLog[];
WalnutRoot.Shutdown[];
};
END;
};
ValidIdent: PROC[ident: ATOM, at: INT] RETURNS[valid: BOOL] = {
this changes whenever WalnutKernelDefs.LogEntry changes
SELECT ident FROM
$LogFileInfo => RETURN[TRUE];
$CreateMsg => RETURN[TRUE];
$ExpungeMsgs => RETURN[TRUE];
$WriteExpungeLog => RETURN[TRUE];
$CreateMsgSet => RETURN[TRUE];
$DestroyMsgSet => RETURN[TRUE];
$EmptyMsgSet => RETURN[TRUE];
$HasBeenRead => RETURN[TRUE];
$AddMsg => RETURN[TRUE];
$RemoveMsg => RETURN[TRUE];
$MoveMsg => RETURN[TRUE];
$RecordNewMailInfo => RETURN[TRUE];
$StartCopyNewMail => RETURN[TRUE];
$EndCopyNewMailInfo => RETURN[TRUE];
$AcceptNewMail => RETURN[TRUE];
$StartReadArchiveFile => RETURN[TRUE];
$EndReadArchiveFile => RETURN[TRUE];
$StartCopyReadArchive => RETURN[TRUE];
$EndCopyReadArchiveInfo => RETURN[TRUE];
ENDCASE => NULL;
out.PutF["\n~~~~ Invalid Entry with identifier $%g at log pos %g\n",
IO.atom[ident], IO.int[at] ];
RETURN[FALSE]
};
DoScan: PROC[strm: STREAM, logLength: INT] = {
ident: ATOM;
length: INT;
table: RefTab.Ref ← RefTab.Create[];
found: BOOL;
val: RefTab.Val;
count: INT ← 0;
at : INT ← strm.GetIndex[];
ForPrinting: RefTab.EachPairAction = {
ident: ATOMNARROW[key];
this: Xyz ← NARROW[val];
out.PutF["Ident: %g, num: %g, bytes: %g\n",
IO.atom[ident], IO.int[this.num], IO.int[this.bytes] ];
RETURN[FALSE];  -- don't quit
};
DO
this: Xyz;
[ident, , length] ← WalnutStream.PeekEntry[strm];
IF ident = NIL THEN {
curPos: INT ← strm.GetIndex[];
IF curPos = logLength THEN EXIT;
out.PutF["\n No entry found at %g\n", IO.int[curPos]];
at ← WalnutStream.FindNextEntry[strm];
IF at = -1 THEN
{ out.PutRope["No more entries found"]; EXIT };
out.PutF["Next entry found at %g - continuing\n", IO.int[at] ];
LOOP;
};
strm.SetIndex[at ← at + length];
[found, val] ← RefTab.Fetch[table, ident];
IF found THEN {
this ← NARROW[val];
this.num ← this.num + 1;
this.bytes ← this.bytes + length;
}
ELSE this ← NEW[ValueObject ← [1, length]];
[] ← RefTab.Store[table, ident, this];
IF (count ← count + 1) MOD 10 = 0 THEN {
IF count MOD 100 = 0 THEN out.PutF["(%g)", IO.int[count]]
ELSE out.PutChar['~];
};
ENDLOOP;
out.PutF["\n\n\t\tThe log (%g bytes) contained the following %g entries:\n",
IO.int[logLength], IO.int[count]];
[] ← RefTab.Pairs[table, ForPrinting];
};
Report: PROC[msg: ROPE] =
{ out.PutF["\n %g @ %g\n", IO.rope[msg], IO.time[]] };
CmdScan: Commander.CommandProc =
TRUSTED { Process.Detach[FORK SetupScan[cmd.commandLine]] };
CmdFix: Commander.CommandProc =
TRUSTED { Process.Detach[FORK SetupFix[cmd.commandLine]] };
Commander.Register["Scan", CmdScan, "Summarizes a Walnut log; syntax is \"Scan logFile\""];
Commander.Register["Fix", CmdFix,
"Remove entries from a Walnut log; syntax is \"Fix rootFile {AtomNames of entries to remove}\""];
END.