AnalyzeAlpineLog.mesa
Scan an alpine log listing the types and sizes of the records found in it.
Last Edited by: Hauser, May 6, 1985 2:55:17 pm PDT
Carl Hauser, November 22, 1985 10:20:41 am PST
DIRECTORY
IO, AlpineLog, LogBasic, LogRep, Commander, Rope, Process, RestartFile, FileLog, RuntimeError, RedBlackTree
;
AnalyzeAlpineLog: CEDAR PROGRAM
IMPORTS IO, AlpineLog, LogBasic, Commander, Process, RestartFile, RuntimeError, RedBlackTree
EXPORTS
SHARES FileLog
= BEGIN
WordNumber: TYPE = LogBasic.WordNumber;
logSize: INT;
FileLogRecord: TYPE = FileLog.FileLogRecord;
pageNode: TYPE = RECORD [
file: INT,
firstPage, count: INT,
occurrenceCount: INT
];
GetKey: RedBlackTree.GetKey ~ {
RETURN[data]
};
Compare: RedBlackTree.Compare ~ {
key: REF pageNode ← NARROW[k];
d: REF pageNode ← NARROW[data];
IF key.file < d.file THEN RETURN[less];
IF key.file > d.file THEN RETURN[greater];
IF key.firstPage < d.firstPage THEN RETURN[less];
IF key.firstPage > d.firstPage THEN RETURN[greater];
IF key.count < d.count THEN RETURN[less];
IF key.count > d.count THEN RETURN[greater];
RETURN[equal];
};
Analyze: PROC [out: IO.STREAM] ~ TRUSTED {
wordNumberForCheckpointCompleteRecord: WordNumber ← RestartFile.ReadRestartRecord[].wordNumberForCheckpointCompleteRecord;
currentRecord, checkpointRecord, startAnalysisRecord: AlpineLog.RecordID;
currentRecordType: LocalRecordType;
pageTable: RedBlackTree.Table ← RedBlackTree.Create[ GetKey, Compare ];
PrintPageNode: RedBlackTree.EachNode ~ CHECKED {
node: REF pageNode ← NARROW[data];
out.PutF[ "File %g Page %g Length %g Occurrences %g \n", IO.int[node.file], IO.int[node.firstPage], IO.int[node.count], IO.int[node.occurrenceCount] ];
};
newNode, oldNode: REF pageNode;
TRUSTED { logSize ← LogBasic.LogFileSize[]*256 };
[checkpointRecord: checkpointRecord, startAnalysisRecord: startAnalysisRecord] ← CheckpointCompleteRecordFromWord[wordNumberForCheckpointCompleteRecord];
currentRecord ← LogBasic.OpenRecordStreamFromCheckpoint[wordNumberForCheckpointCompleteRecord, checkpointRecord, startAnalysisRecord].currentRecord;
TRUSTED {currentRecord ← LogBasic.OpenRecordStreamFromWord[LogBasic.LocateFirstRecord[]].currentRecord;};
DO
currentRecordType ← PeekType[currentRecord];
RecordTypeCount[currentRecordType] ← RecordTypeCount[currentRecordType]+1;
IF (currentRecordType = writePages) OR (currentRecordType = writeLeaderPage) THEN
out.PutF[ "At position %g the record type is %g \n", IO.int[currentRecord.lowBits MOD logSize], IO.rope[RecordTypeName[currentRecordType]]
-- ! IO.Error => EXIT
];
SELECT currentRecordType FROM
writePages => {
header: FileLogRecord[writePages];
[] ← AlpineLog.ReadForRecovery[thisRecord: currentRecord, to: [base: @header, length: SIZE[FileLogRecord[writePages]]] ! RuntimeError.UNCAUGHT => CONTINUE];
out.PutF[ "\tFileID: %g Pages: %g %g\n", IO.card[LOOPHOLE[header.fileID.id]], IO.card[header.pageRun.firstPage], IO.card[header.pageRun.count]];
oldNode ← NARROW[RedBlackTree.Lookup[ pageTable, newNode ← NEW[pageNode ← [LOOPHOLE[header.fileID.id], header.pageRun.firstPage, header.pageRun.count, 1] ]]];
IF oldNode # NIL THEN {
oldNode.occurrenceCount ← oldNode.occurrenceCount + 1;
}
ELSE RedBlackTree.Insert[pageTable, newNode, newNode];
};
writeLeaderPage => {
header: FileLogRecord[writeLeaderPage];
[] ← AlpineLog.ReadForRecovery[thisRecord: currentRecord, to: [base: @header, length: SIZE[FileLogRecord[writeLeaderPage]]] ! RuntimeError.UNCAUGHT => CONTINUE];
out.PutF[ "\tFileID: %g Count: %g\n", IO.card[LOOPHOLE[header.fileID.id]], IO.card[header.pageCount]];
oldNode ← NARROW[RedBlackTree.Lookup[ pageTable, newNode ← NEW[pageNode ← [LOOPHOLE[header.fileID.id], -1, 1, 1] ]]];
IF oldNode # NIL THEN {
oldNode.occurrenceCount ← oldNode.occurrenceCount + 1;
}
ELSE RedBlackTree.Insert[pageTable, newNode, newNode];
};
ENDCASE => NULL;
{
endOfLog, truncatedRecord: BOOL;
[endOfLog: endOfLog, truncatedRecord: truncatedRecord, currentRecord: currentRecord] ← LogBasic.AdvanceRecordStream[];
IF LOOPHOLE[currentRecord.lowBits, INT] MOD logSize = LOOPHOLE[startAnalysisRecord.lowBits, INT] MOD logSize THEN EXIT;
};
Process.CheckForAbort[];
ENDLOOP;
LogBasic.CloseRecordStream[];
RedBlackTree.EnumerateIncreasing[ pageTable, PrintPageNode ];
};
PeekType: PROC [thisRecord: AlpineLog.RecordID] RETURNS [localRecordType: LocalRecordType] = TRUSTED {
recordTypeHeader: LogRep.RecordTypeHeader;
status: AlpineLog.ReadProcStatus;
[status: status] ← LogBasic.GetCurrentRecord[currentRecord: thisRecord,
to: [base: @recordTypeHeader, length: LogRep.RecordTypeHeader.SIZE]];
IF status = sourceExhausted THEN ERROR ;
RETURN [GetLocalType[recordTypeHeader.type]];
};
LocalRecordType: TYPE = { noop, checkpointBegin, checkpointComplete, coordinatorBegin, coordinatorRegisterWorker, coordinatorCollecting, coordinatorCompleting, coordinatorComplete, workerBegin, workerReady, workerCompleting, workerComplete, writePages, writeLeaderPage, setSize, create, delete, lock, reserved, none };
RecordTypeName: ARRAY LocalRecordType OF Rope.ROPE = [ "noop", "checkpointBegin", "checkpointComplete", "coordinatorBegin", "coordinatorRegisterWorker", "coordinatorCollecting", "coordinatorCompleting", "coordinatorComplete", "workerBegin", "workerReady", "workerCompleting", "workerComplete", "writePages", "writeLeaderPage", "setSize", "create", "delete", "lock", "reserved", "none" ];
RecordTypeCount: ARRAY LocalRecordType OF INT;
GetLocalType: PROC [recordType: AlpineLog.RecordType] RETURNS [localRecordType: LocalRecordType] ~ {
SELECT recordType FROM
noop => localRecordType ← noop;
checkpointBegin => localRecordType ← checkpointBegin;
checkpointComplete => localRecordType ← checkpointComplete;
coordinatorBegin => localRecordType ← coordinatorBegin;
coordinatorRegisterWorker => localRecordType ← coordinatorRegisterWorker;
coordinatorCollecting => localRecordType ← coordinatorCollecting;
coordinatorCompleting => localRecordType ← coordinatorCompleting;
coordinatorComplete => localRecordType ← coordinatorComplete;
workerBegin => localRecordType ← workerBegin;
workerReady => localRecordType ← workerReady;
workerCompleting => localRecordType ← workerCompleting;
workerComplete => localRecordType ← workerComplete;
writePages => localRecordType ← writePages;
writeLeaderPage => localRecordType ← writeLeaderPage;
setSize => localRecordType ← setSize;
create => localRecordType ← create;
delete => localRecordType ← delete;
lock => localRecordType ← localRecordType ← lock;
reserved => localRecordType ← reserved;
ENDCASE => localRecordType ← none;
};
CheckpointCompleteRecordFromWord: PROC [checkpointWord: WordNumber] RETURNS [
ok: BOOL, checkpointRecord, startAnalysisRecord: AlpineLog.RecordID] = TRUSTED {
Updates: Recover.myFileStore.
Returns ok = FALSE if any error detected.
{
checkpointCompleteRecordBody: LogRep.CheckpointCompleteRecord;
notStartOfRecord: BOOL;
currentRecord: AlpineLog.RecordID;
[notStartOfRecord: notStartOfRecord, currentRecord: currentRecord] ←
LogBasic.OpenRecordStreamFromWord[checkpointWord ! LogBasic.InvalidPage =>
GOTO failReturn ];
IF notStartOfRecord THEN GOTO failReturn;
IF LogBasic.CheckCurrentRecord[].truncated THEN GOTO failReturn;
IF LogBasic.GetCurrentRecord[currentRecord, [base: @checkpointCompleteRecordBody,
length: SIZE[LogRep.CheckpointCompleteRecord]]].status = destinationFull OR
checkpointCompleteRecordBody.type # checkpointComplete THEN GOTO failReturn;
checkpointRecord ← checkpointCompleteRecordBody.thisRecordID;
startAnalysisRecord ← checkpointCompleteRecordBody.startAnalysisRecordID;
ok ← TRUE;
EXITS
failReturn => { ok ← FALSE };
};
LogBasic.CloseRecordStream[];
RETURN [ok, checkpointRecord, startAnalysisRecord];
};--CheckpointCompleteRecordFromWord
AnalyzeCommand: Commander.CommandProc -- [cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL] -- = {
RecordTypeCount ← ALL[0];
Analyze[cmd.out];
FOR rt: LocalRecordType IN [noop .. none] DO
IF RecordTypeCount[rt]#0 THEN cmd.out.PutF[ " %g: %g ", IO.rope[RecordTypeName[rt]], IO.int[RecordTypeCount[rt]] ];
ENDLOOP;
};
Commander.Register[key: "AnalyzeAlpineLog", proc: AnalyzeCommand, doc: "Scan an alpine log listing the types and sizes of the records found in it."]
END.