MobCompare.mesa
Copyright Ó 1985, 1986, 1991 by Xerox Corporation. All rights reserved.
Satterthwaite March 8, 1986 5:21:03 pm PST
Russ Atkinson (RRA) December 22, 1986 6:12:51 pm PST
Andy Litman March 3, 1988 3:21:33 pm PST
JKF May 25, 1990 10:30:32 am PDT
Willie-s, February 11, 1991 6:38 pm PST
Michael Plass, November 26, 1991 4:29 pm PST
DIRECTORY
Commander USING [CommandProc, Handle, Register],
IO,
MobDefs,
PFS USING [Error, PathFromRope, RopeOpen],
Rope USING [ActionType, Cat, Concat, Equal, Fetch, Flatten, Length, Map, Match, ROPE, Run, Substr],
SymbolSegment USING [BlockDescriptor, FGHeader, STHeader];
MobCompare: CEDAR PROGRAM
IMPORTS Commander, IO, PFS, Rope
= BEGIN
bytesPerUnit: CARDINAL = BYTES[WORD];
T Y P E S & C O N S T A N T S
Mob: TYPE = MobDefs.Mob;
MobPtr: TYPE = LONG POINTER TO Mob;
BlockDescriptor: TYPE = SymbolSegment.BlockDescriptor;
Buffer: TYPE = REF BufferRep;
BufferRep: TYPE = RECORD[start: INT¬0, len: NAT¬0, words: SEQUENCE max: NAT OF WORD];
FGHeader: TYPE = SymbolSegment.FGHeader;
NameRecord: TYPE = MobDefs.NameRecord;
ROPE: TYPE = Rope.ROPE;
Stats: TYPE = REF StatsRep;
StatsRep: TYPE = RECORD[
debug, doCode, doSymbols, doAll: BOOL¬FALSE,
name1,name2: ROPE¬NIL,
rope1,rope2: ROPE¬NIL,
base1,base2: LONG POINTER¬NIL,
symOff1,symOff2: INT¬0,
leader: ROPE¬NIL,
out: STREAM¬NIL,
maxTabLim1: CARD¬0,
maxTabLim2: CARD¬0,
errors: NAT¬0,
maxErrors: NAT¬0
];
STREAM: TYPE = IO.STREAM;
SymbolTableHeader: TYPE = REF SymbolSegment.STHeader;
VersionStamp: TYPE = MobDefs.VersionStamp;
WordPtr: TYPE = LONG POINTER TO WORD;
E R R O R S
GiveUp: ERROR[err: ROPE] = CODE;
F L A G S & V A R I A B L E S
initMaxErrors: NAT ¬ 24;
blockList: LIST OF ROPE = LIST[
"hvBlock", "htBlock", "ssBlock", "outerPackBlock", "innerPackBlock", "constBlock", "seBlock", "ctxBlock", "mdBlock", "bodyBlock", "extBlock", "treeBlock", "litBlock", "sLitBlock", "epMapBlock", "spareBlock"];
P R O C S
DoCompare: Commander.CommandProc = {
[cmd: Handle] RETURNS[result: REFNIL, msg: Rope.ROPENIL]
in: STREAM = IO.RIS[cmd.commandLine];
name1,name2: ROPE;
name1 ¬ in.GetTokenRope[IO.IDProc
! IO.EndOfStream => GO TO usage].token;
name2 ¬ in.GetTokenRope[IO.IDProc
! IO.EndOfStream => GO TO usage].token;
CompareFilesAsMobs[
cmd, name1, name2
! GiveUp => { msg ¬ err; GO TO errOut };
PFS.Error => { msg ¬ error.explanation; GO TO errOut }];
msg ¬ "No differences found.";
EXITS
errOut => result ¬ $Failure;
usage => {result ¬ $Failure; msg ¬ "Usage: CodeCompare file1 file2"};
};
RopeFileCreate: PROC [name: ROPE] RETURNS [ROPE] ~ {
RETURN [PFS.RopeOpen[PFS.PathFromRope[name]].rope]
};
CompareFilesAsMobs: PROC[cmd: Commander.Handle, name1,name2: ROPE] = {
rope1: ROPE = RopeFileCreate[name1 ¬ ForceExtension[name1, ".mob"]];
rope2: ROPE = RopeFileCreate[name2 ¬ ForceExtension[name2, ".mob"]];
stats: Stats = NEW[StatsRep];
ProcessOptions[stats, cmd.procData.clientData];
stats.maxErrors ¬ (IF stats.debug THEN initMaxErrors*4 ELSE initMaxErrors);
stats.out ¬ cmd.out;
stats.name1 ¬ name1;
stats.name2 ¬ name2;
stats.rope1 ¬ rope1;
stats.rope2 ¬ rope2;
CompareRopesAsMobs[rope1, rope2, stats]};
ProcessOptions: PROC[stats: Stats, data: REF] = {
WITH data SELECT FROM
atom: ATOM =>
SELECT atom FROM
$Code => stats.doCode ¬ TRUE;
$All => stats.doAll ¬ TRUE;
$Symbols => stats.doSymbols ¬ TRUE;
$Debug => stats.debug ¬ stats.doAll ¬ TRUE;
ENDCASE;
list: LIST OF REF =>
WHILE list # NIL DO
ProcessOptions[stats, list.first];
list ¬ list.rest;
ENDLOOP;
ENDCASE;
};
ForceExtension: PROC[name: ROPE, ext: ROPE] RETURNS[ROPE] = {
match: ROPE ¬ Rope.Concat["*", ext];
SELECT TRUE FROM
Rope.Match["*!*", name], match.Match[name, FALSE] => {};
ENDCASE => name ¬ name.Concat[ext];
RETURN[name]};
CompareRopesAsMobs: PROC[rope1,rope2: ROPE, stats: Stats] = TRUSTED {
MobBuf1: Buffer = NEW[BufferRep[SIZE[Mob]]];
Mob1: MobPtr = LOOPHOLE[@MobBuf1[0]];
MobBuf2: Buffer = NEW[BufferRep[SIZE[Mob]]];
Mob2: MobPtr = LOOPHOLE[@MobBuf2[0]];
len: INT = MIN[rope1.Length[], rope2.Length[]];
GetWords[rope1, 0, SIZE[Mob], MobBuf1];
GetWords[rope2, 0, SIZE[Mob], MobBuf2];
stats.base1 ¬ Mob1;
stats.base2 ¬ Mob2;
(stats.out).PutRope[stats.name1];
(stats.out).PutRope["\n version: "];
VersionOut[Mob1.version, stats, TRUE];
(stats.out).PutF1[", bytes: %g\n source: ", [integer[rope1.Length[]]]];
NameOut[rope1, Mob1.ssOffset.units, Mob1.source, stats];
(stats.out).PutRope["\n sourceVersion: "];
VersionOut[Mob1.sourceVersion, stats];
(stats.out).PutRope["\n"];
(stats.out).PutRope[stats.name2];
(stats.out).PutRope["\n version: "];
VersionOut[Mob2.version, stats, TRUE];
(stats.out).PutF1[", bytes: %g\n source: ", [integer[rope2.Length[]]]];
NameOut[rope2, Mob2.ssOffset.units, Mob2.source, stats];
(stats.out).PutRope["\n sourceVersion: "];
VersionOut[Mob2.sourceVersion, stats];
(stats.out).PutRope["\n"];
IF rope1.Length[] # rope2.Length[] THEN {
(stats.out).PutRope["Warning, files have different lengths.\n"];
stats.errors ¬ stats.errors +1};
CompareInts[
"In versionIdent\n", Mob1.versionIdent, Mob2.versionIdent, stats];
CompareVersionStamps[
"In version\n", Mob1.version, Mob2.version, stats];
CompareVersionStamps[
"In creator\n", Mob1.creator, Mob2.creator, stats];
CompareVersionStamps[
"In sourceVersion\n", Mob1.sourceVersion, Mob2.sourceVersion, stats];
CompareInts[
"In source\n", Mob1.source, Mob2.source, stats];
CompareInts[
"In nBytes\n", Mob1.nBytes, Mob2.nBytes, stats];
CompareCard16s[
"In nConfigs\n", Mob1.nConfigs, Mob2.nConfigs, stats];
CompareCard16s[
"In nModules\n", Mob1.nModules, Mob2.nModules, stats];
CompareCard16s[
"In nImports\n", Mob1.nImports, Mob2.nImports, stats];
CompareBools[
"In definitions\n", Mob1.definitions, Mob2.definitions, stats];
CompareBools[
"In repackaged\n", Mob1.repackaged, Mob2.repackaged, stats];
CompareBools[
"In typeExported\n", Mob1.typeExported, Mob2.typeExported, stats];
CompareBools[
"In inlineFloat\n", Mob1.inlineFloat, Mob2.inlineFloat, stats];
CompareBools[
"In mappingStarted\n", Mob1.mappingStarted, Mob2.mappingStarted, stats];
CompareBools[
"In mappingFinished\n", Mob1.mappingFinished, Mob2.mappingFinished, stats];
CompareBools[
"In versions\n", Mob1.versions, Mob2.versions, stats];
CompareBools[
"In extended\n", Mob1.extended, Mob2.extended, stats];
CompareCard16s[
"In nDummies\n", Mob1.nDummies, Mob2.nDummies, stats];
IF stats.debug THEN (stats.out).PutRope["** Tables\n"];
CompareTables[
"string table", @Mob1.ssOffset, @Mob2.ssOffset, stats];
CompareTables[
"config table", @Mob1.ctOffset, @Mob2.ctOffset, stats];
CompareTables[
"module table", @Mob1.mtOffset, @Mob2.mtOffset, stats];
CompareTables[
"import table", @Mob1.impOffset, @Mob2.impOffset, stats];
CompareTables[
"export table", @Mob1.expOffset, @Mob2.expOffset, stats];
CompareTables[
"external variable table", @Mob1.evOffset, @Mob2.evOffset, stats];
CompareTables[
"segment table", @Mob1.sgOffset, @Mob2.sgOffset, stats];
CompareTables[
"file table", @Mob1.ftOffset, @Mob2.ftOffset, stats];
CompareTables[
"space table", @Mob1.spOffset, @Mob2.spOffset, stats];
CompareTables[
"name table", @Mob1.ntOffset, @Mob2.ntOffset, stats];
CompareTables[
"type table", @Mob1.typOffset, @Mob2.typOffset, stats];
CompareTables[
"type map table", @Mob1.tmOffset, @Mob2.tmOffset, stats];
CompareTables[
"frame pack table", @Mob1.fpOffset, @Mob2.fpOffset, stats];
IF Mob1.extended AND Mob2.extended THEN {
CompareTables[
"link fragment table", @Mob1.lfOffset, @Mob2.lfOffset, stats, TRUE];
CompareTables[
"ref literal fragment table", @Mob1.rfOffset, @Mob2.rfOffset, stats, TRUE];
CompareTables[
"type fragment table", @Mob1.tfOffset, @Mob2.tfOffset, stats, TRUE];
CompareInts[
"In rtPages\n", Mob1.rtOffset.units, Mob2.rtOffset.units, stats]};
IF stats.debug THEN {
(stats.out).PutRope["** Segments for file 1\n"];
SegmentsOut[rope1, Mob1, stats];
(stats.out).PutRope["** Segments for file 2\n"];
SegmentsOut[rope2, Mob2, stats]};
IF stats.debug THEN {
(stats.out).PutRope["** Files for file 1\n"];
FilesOut[rope1, Mob1, stats];
(stats.out).PutRope["** Files for file 2\n"];
FilesOut[rope2, Mob2, stats]};
{-- compare the code in the various modules
mti1: CARD ¬ LOOPHOLE[FIRST[MobDefs.MTIndex], CARD]+Mob1.mtOffset.units;
mti2: CARD ¬ LOOPHOLE[FIRST[MobDefs.MTIndex], CARD]+Mob2.mtOffset.units;
FOR i: NAT IN [0..MIN[Mob1.nModules,Mob2.nModules]) DO
mtr1: MobDefs.MTRecord;
mtr2: MobDefs.MTRecord;
sgr1: MobDefs.SGRecord;
sgr2: MobDefs.SGRecord;
nw1, nw2, sgi1, sgi2: CARD;
byteOff1,byteOff2: INT;
sb1,sb2: INT;
GetWordsToPtr[rope1, mti1, SIZE[MobDefs.MTRecord], @mtr1];
GetWordsToPtr[rope2, mti2, SIZE[MobDefs.MTRecord], @mtr2];
nw1 ¬ SIZE[MobDefs.MTRecord];
nw2 ¬ SIZE[MobDefs.MTRecord];
IF stats.doCode OR stats.doAll THEN {
Compare the code portions of this module.
First, get the proper SGRecords for the code
sgi1 ¬ Mob1.sgOffset.units + LOOPHOLE[mtr1.code.sgi, CARD];
sgi2 ¬ Mob2.sgOffset.units + LOOPHOLE[mtr2.code.sgi, CARD];
GetWordsToPtr[rope1, sgi1, SIZE[MobDefs.SGRecord], @sgr1];
GetWordsToPtr[rope2, sgi2, SIZE[MobDefs.SGRecord], @sgr2];
sb1 ¬ sgr1.base.units;
sb2 ¬ sgr2.base.units;
byteOff1 ¬ (mtr1.code.offset+sb1)*bytesPerUnit;
byteOff2 ¬ (mtr2.code.offset+sb2)*bytesPerUnit;
SELECT TRUE FROM
mtr1.code.length = 0 AND mtr2.code.length = 0 =>
(stats.out).PutRope["No code.\n"];
mtr1.code.length = 0 =>
(stats.out).PutF1[
"No code for file 1, %g bytes for file 2.\n",
[integer[mtr2.code.length]]];
mtr1.code.length = 0 =>
(stats.out).PutF1[
"No code for file 2, %g bytes for file 1.\n",
[integer[mtr1.code.length]]];
ENDCASE =>
Finally, compare the code
CompareBytes[
IO.PutFR[
"In code for %g (module %g)\n",
[rope[GetName[rope1, Mob1.ssOffset.units, mtr1.name]]],
[integer[i]]],
byteOff1, byteOff2, MIN[mtr1.code.length, mtr2.code.length], stats];
};
IF stats.doSymbols OR stats.doAll THEN {
Compare the symbol portions of this module.
First, get the proper SGRecords for the symbols
sgi1 ¬ Mob1.sgOffset.units + LOOPHOLE[mtr1.sseg, CARD];
sgi2 ¬ Mob2.sgOffset.units + LOOPHOLE[mtr2.sseg, CARD];
GetWordsToPtr[rope1, sgi1, SIZE[MobDefs.SGRecord], @sgr1];
GetWordsToPtr[rope2, sgi2, SIZE[MobDefs.SGRecord], @sgr2];
sb1 ¬ sgr1.base.units;
sb2 ¬ sgr2.base.units;
Set the symbol offset in stats for use by Diagnose
stats.symOff1 ¬ sb1;
stats.symOff2 ¬ sb2;
Compare the basic symbols
CompareWords[
IO.PutFR[
"In basic symbols for %g (module %g)\n",
[rope[GetName[rope1, Mob1.ssOffset.units, mtr1.name]]], [integer[i]]],
sb1, sb2, MIN[sgr1.units.units, sgr2.units.units], stats];
Compare the extended symbols
stats.symOff1 ¬ stats.symOff2 ¬ 0; -- inhibit Diagnose
CompareWords[
IO.PutFR[
"In extended symbols for %g (module %g)\n",
[rope[GetName[rope1, Mob1.ssOffset.units, mtr1.name]]],
[integer[i]]],
sb1+sgr1.units.units,
sb2+sgr2.units.units,
MIN[sgr1.extraUnits.units, sgr2.extraUnits.units],
stats
];
};
mti1 ¬ mti1 + nw1;
mti2 ¬ mti2 + nw2;
ENDLOOP;
};
IF stats.debug THEN {
stats.maxTabLim1 ¬ stats.maxTabLim2 ¬ SIZE[Mob];
CompareWords[
"In whole file\n",
stats.maxTabLim1, stats.maxTabLim2,
MIN[len/bytesPerUnit-stats.maxTabLim1, len/bytesPerUnit-stats.maxTabLim2],
stats];
};
SELECT stats.errors FROM
0 => {};
1 => ERROR GiveUp["Too bad, only one little difference."];
ENDCASE =>
ERROR GiveUp[
IO.PutFR1["%g differences encountered.", [integer[stats.errors]]]];
};
CompareWords: PROC
[name: ROPE, offset1,offset2: INT, nWords: INT, stats: Stats] = TRUSTED {
r1: ROPE = (stats.rope1).Substr[0, (offset1+nWords)*bytesPerUnit];
lim1: INT = r1.Length[];
r2: ROPE = (stats.rope2).Substr[0, (offset2+nWords)*bytesPerUnit];
lim2: INT = r2.Length[];
sth1,sth2: SymbolTableHeader ¬ NIL;
off1: INT ¬ offset1*bytesPerUnit;
off2: INT ¬ offset2*bytesPerUnit;
IF stats.debug THEN
(stats.out).PutFL[
"** %g CompareWords[offset1: %g, offset2: %g, nWords: %g]\n",
LIST[[rope[name]], [integer[offset1]], [integer[offset2]], [integer[nWords]]]];
WHILE off1 < lim1 AND off2 < lim2 DO
nc: INT = r1.Run[off1, r2, off2];
nOff1: INT ¬ off1+nc;
nOff2: INT ¬ off2+nc;
nwOff1,nwOff2: INT;
w1,w2: WORD;
f1,f2: ROPE;
which1,which2: ROPE ¬ NIL;
IF nOff1 >= lim1 OR nOff2 >= lim2 THEN EXIT;
IF name # NIL THEN {(stats.out).PutRope[name]; name ¬ NIL};
nwOff1 ¬ nOff1/bytesPerUnit;
nwOff2 ¬ nOff2/bytesPerUnit;
f1 ¬ r1.Flatten[nOff1 ¬ nwOff1*bytesPerUnit, bytesPerUnit];
w1 ¬ (LOOPHOLE[f1, WordPtr]+SIZE[TEXT])­;
f2 ¬ r2.Flatten[nOff2 ¬ nwOff2*bytesPerUnit, bytesPerUnit];
w2 ¬ (LOOPHOLE[f2, WordPtr]+SIZE[TEXT])­;
IF nOff1 = nOff2 THEN (stats.out).PutF1[" at word offset %g", [integer[nwOff1]]]
ELSE (stats.out).PutF[
" at word offsets (%g, %g)", [integer[nwOff1]], [integer[nwOff2]]];
(stats.out).PutFL[
", %b # %b (\"%q\" # \"%q\")\n",
LIST[[cardinal[w1]], [cardinal[w2]], [rope[f1]], [rope[f2]]]];
IF stats.symOff1 > 0 THEN {
IF sth1 = NIL THEN {
sth1 ¬ NEW[SymbolSegment.STHeader];
GetWordsToPtr[
stats.rope1, stats.symOff1, SIZE[SymbolSegment.STHeader], LOOPHOLE[sth1]]
};
Try to determine which block it is in
which1 ¬ Diagnose[stats.rope1, sth1, offset1, nwOff1];
(stats.out).PutRope[which1];
(stats.out).PutRope["\n"]};
IF stats.symOff2 > 0 THEN {
IF sth2 = NIL THEN {
sth2 ¬ NEW[SymbolSegment.STHeader];
GetWordsToPtr[
stats.rope2, stats.symOff2, SIZE[SymbolSegment.STHeader], LOOPHOLE[sth2]];
};
Try to determine which block it is in
which2 ¬ Diagnose[stats.rope2, sth2, offset2, nwOff2];
IF NOT which1.Equal[which2] THEN {
Different diagnoses for the two files!
(stats.out).PutRope[" (Mob1)\n"];
(stats.out).PutRope[which2];
(stats.out).PutRope[" (Mob2)\n"]};
};
IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN {
ERROR GiveUp["Too many differences encountered."]};
off1 ¬ nOff1 + bytesPerUnit;
off2 ¬ nOff2 + bytesPerUnit;
ENDLOOP;
};
CompareBytes: PROC
[name: ROPE, offset1,offset2: INT, nBytes: INT, stats: Stats] = TRUSTED {
r1: ROPE = (stats.rope1).Substr[0, offset1+nBytes];
lim1: INT = r1.Length[];
r2: ROPE = (stats.rope2).Substr[0, offset2+nBytes];
lim2: INT = r2.Length[];
IF stats.debug THEN
(stats.out).PutFL[
"** %g CompareBytes[offset1: %g, offset2: %g, nBytes: %g]\n",
LIST[[rope[name]], [integer[offset1]], [integer[offset2]], [integer[nBytes]]]];
WHILE offset1 < lim1 AND offset2 < lim2 DO
nc: INT = r1.Run[offset1, r2, offset2];
nOff1: INT = offset1+nc;
nOff2: INT = offset2+nc;
f1,f2: ROPE;
IF nOff1 >= lim1 OR nOff2 >= lim2 THEN EXIT;
IF name # NIL THEN {
(stats.out).PutRope[name];
name ¬ NIL};
f1 ¬ r1.Flatten[nOff1, 1];
f2 ¬ r2.Flatten[nOff2, 1];
IF nOff1 = nOff2 THEN
(stats.out).PutF1[
" at byte offset %g",
[integer[nOff1]]]
ELSE
(stats.out).PutF[
" at byte offsets (%g, %g)",
[integer[nOff1]], [integer[nOff2]]];
(stats.out).PutFL[
", %b # %b ('%q # '%q)\n",
LIST[[cardinal[f1.Fetch[0]-0C]], [cardinal[f2.Fetch[0]-0C]], [rope[f1]], [rope[f2]]]];
IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN {
ERROR GiveUp["Too many differences encountered."]};
offset1 ¬ nOff1+1;
offset2 ¬ nOff2+1;
ENDLOOP;
};
CompareCard16s: PROC
[name: ROPE, src1, src2: CARD16, stats: Stats] = TRUSTED {
IF src1 # src2 THEN {
IF name # NIL THEN {
(stats.out).PutRope[name];
name ¬ NIL};
(stats.out).PutF[" %g # %g\n", [cardinal[CARD[src1]]], [cardinal[CARD[src2]]]];
};
};
CompareInts: PROC
[name: ROPE, src1, src2: INT, stats: Stats] = TRUSTED {
IF src1 # src2 THEN {
IF name # NIL THEN {
(stats.out).PutRope[name];
name ¬ NIL};
(stats.out).PutF[" %g # %g\n", [integer[src1]], [integer[src2]]];
};
};
CompareVersionStamps: PROC
[name: ROPE, src1, src2: MobDefs.VersionStamp, stats: Stats] = TRUSTED {
IF src1 # src2 THEN {
IF name # NIL THEN {
(stats.out).PutRope[name];
name ¬ NIL};
(stats.out).PutFL[" %g,%g # %g, %g\n", LIST[[cardinal[src1[0]]], [cardinal[src1[1]]], [cardinal[src2[0]]], [cardinal[src2[1]]]]];
};
};
CompareBools: PROC
[name: ROPE, src1, src2: BOOL, stats: Stats] = TRUSTED {
IF src1 # src2 THEN {
IF name # NIL THEN {
(stats.out).PutRope[name];
name ¬ NIL};
(stats.out).PutF[" %g # %g\n", [boolean[src1]], [boolean[src2]]];
};
};
<<
CompareFileTables: PROC [name: ROPE, mob1, mob2: MobPtr, stats: Stats] = TRUSTED {
ftb1: MobDefs.Base = LOOPHOLE[mob1 + mob1.ftOffset.units];
ftb2: MobDefs.Base = LOOPHOLE[mob2 + mob2.ftOffset.units];
ftLimit: MobDefs.FTIndex;
IF mob1.ftOffset.units # mob2.ftOffset.units THEN {
(stats.out).PutF[
"Offsets for %g not equal, %g # %g\n",
[rope[name]], [cardinal[mob1.ftOffset.units]], [cardinal[mob1.ftOffset.units]]];
IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN
ERROR GiveUp["Too many differences encountered."];
};
IF mob1.ftLimit # mob2.ftLimit THEN {
(stats.out).PutF[
"Limits for %g not equal, %g # %g\n",
[rope[name]], [cardinal[LOOPHOLE[mob1.ftLimit]]], [cardinal[LOOPHOLE[mob1.ftLimit]]]];
IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN
ERROR GiveUp["Too many differences encountered."];
};
IF LOOPHOLE[mob1.ftLimit, CARD] > LOOPHOLE[mob2.ftLimit, CARD]
THEN ftLimit ¬ mob2.ftLimit
ELSE ftLimit ¬ mob1.ftLimit;
FOR fti: MobDefs.FTIndex ¬ FIRST[MobDefs.FTIndex], fti + SIZE[MobDefs.FTRecord] UNTIL fti = ftLimit DO
IF ftb1[fti] # ftb2[fti] THEN {
(stats.out).PutF[
"Entries for %g not equal, %g # %g\n",
[rope[name]], [cardinal[LOOPHOLE[mob1.ftLimit]]], [cardinal[LOOPHOLE[mob1.ftLimit]]]];
IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN
ERROR GiveUp["Too many differences encountered."];
};
ENDLOOP;
};
CompareSpaceTables: PROC [name: ROPE, mob1, mob2: MobPtr, stats: Stats] = TRUSTED {
spb1: MobDefs.Base = LOOPHOLE[mob1 + mob1.spOffset.units];
spb2: MobDefs.Base = LOOPHOLE[mob2 + mob2.spOffset.units];
spLimit: MobDefs.SPIndex;
IF mob1.spOffset.units # mob2.spOffset.units THEN {
(stats.out).PutF[
"Offsets for %g not equal, %g # %g\n",
[rope[name]], [cardinal[mob1.spOffset.units]], [cardinal[mob1.spOffset.units]]];
IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN
ERROR GiveUp["Too many differences encountered."];
};
IF mob1.spLimit # mob2.spLimit THEN {
(stats.out).PutF[
"Limits for %g not equal, %g # %g\n",
[rope[name]], [cardinal[LOOPHOLE[mob1.spLimit]]], [cardinal[LOOPHOLE[mob1.spLimit]]]];
IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN
ERROR GiveUp["Too many differences encountered."];
};
IF LOOPHOLE[mob1.spLimit, CARD] > LOOPHOLE[mob2.spLimit, CARD]
THEN spLimit ¬ mob2.spLimit
ELSE spLimit ¬ mob1.spLimit;
FOR spi: MobDefs.SPIndex ¬ FIRST[MobDefs.SPIndex], spi + SIZE[MobDefs.SPRecord] UNTIL spi = spLimit DO
IF (spb1[spi].seg # spb2[spi].seg) OR (spb1[spi].name # spb2[spi].name) OR (spb1[spi].length # spb2[spi].length) THEN {
(stats.out).PutF[
"Entries for %g not equal, %g # %g\n",
[rope[name]], [cardinal[LOOPHOLE[mob1.spLimit]]], [cardinal[LOOPHOLE[mob1.spLimit]]]];
IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN
ERROR GiveUp["Too many differences encountered."];
}
ELSE {
FOR i: CARDINAL IN [0..spb1[spi].length) DO
IF spb1[spi].spaces[i] # spb2[spi].spaces[i] THEN {
(stats.out).PutF[
"Entries for %g not equal, %g # %g\n",
[rope[name]], [cardinal[LOOPHOLE[mob1.spLimit]]], [cardinal[LOOPHOLE[mob1.spLimit]]]];
IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN
ERROR GiveUp["Too many differences encountered."];
};
ENDLOOP;
};
ENDLOOP;
};
>>
CompareTables: PROC
[name: ROPE, ptr1, ptr2: LONG POINTER, stats: Stats, rt: BOOL ¬ FALSE] = TRUSTED {
src1: WordPtr ¬ LOOPHOLE[ptr1, WordPtr];
targetBitsPerUnit: CARD ¬ LOOPHOLE[ptr1, MobPtr].format.bitsPerUnit[0];
targetBytesPerUnit: CARD ¬ targetBitsPerUnit/BITS[BYTE];
offset1: CARD ¬ (LOOPHOLE[src1, WordPtr]­ * targetBytesPerUnit)/BYTES[UNIT];
len1: CARD = (src1+SIZE[MobDefs.MobOffset])­;
lim1: CARD = len1+offset1;
src2: WordPtr ¬ LOOPHOLE[ptr2, WordPtr];
offset2: CARD ¬ src2­;
len2: CARD = (src2+SIZE[MobDefs.MobOffset])­;
lim2: CARD = len2+offset2;
nWords: CARD = MIN[lim1, lim2];
oldErrors: CARDINAL;
rtOff1,rtOff2: INT ¬ 0;
IF stats.debug THEN {
(stats.out).PutFL[
" %g, range1 = [%g, %g), range2 = [%g, %g).\n",
LIST[[rope[name]], [integer[offset1]], [integer[lim1]], [integer[offset2]], [integer[lim2]]]];
};
stats.maxTabLim1 ¬ MAX[stats.maxTabLim1, lim1];
stats.maxTabLim2 ¬ MAX[stats.maxTabLim2, lim2];
IF offset1 # offset2 THEN {
(stats.out).PutF[
"Offsets for %g not equal, %g # %g\n",
[rope[name]], [cardinal[offset1]], [cardinal[offset2]]];
IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN {
ERROR GiveUp["Too many differences encountered."]};
};
IF len1 # len2 THEN {
(stats.out).PutF[
"Lengths for %g not equal, %g # %g\n",
[rope[name]], [cardinal[len1]], [cardinal[len2]]];
IF (stats.errors ¬ stats.errors + 1) > stats.maxErrors THEN {
ERROR GiveUp["Too many differences encountered."]};
};
oldErrors ¬ stats.errors;
IF rt THEN {
This table should be interpreted relative to the RTMob extension
rtOff1 ¬ LOOPHOLE[stats.base1, MobPtr].rtOffset.units;
rtOff2 ¬ LOOPHOLE[stats.base2, MobPtr].rtOffset.units};
Now compare the words within the table.
CompareWords[
Rope.Cat["In ", name, "\n"],
rtOff1+offset1, rtOff2+offset2, MIN[len1, len2], stats
! GiveUp => {
stats.errors ¬ oldErrors+1;
(stats.out).PutRope["... too many differences for this table.\n"];
CONTINUE}];
};
GetWords: PROC[rope: ROPE, wordOffset: INT, nWords: NAT, buffer: Buffer] = TRUSTED {
wordIndex: NAT ¬ 0;
byteIndex: [0..bytesPerUnit) ¬ 0;
word: PACKED ARRAY [0..bytesPerUnit) OF CHAR ¬ ALL[0C];
action: Rope.ActionType = TRUSTED {
word[byteIndex] ¬ c;
IF byteIndex = bytesPerUnit-1 THEN {
byteIndex ¬ 0;
buffer[wordIndex] ¬ LOOPHOLE[word];
wordIndex ¬ wordIndex + 1}
ELSE byteIndex ¬ byteIndex + 1};
buffer.start ¬ wordOffset;
buffer.len ¬ nWords;
[] ¬ rope.Map[wordOffset*bytesPerUnit, nWords*bytesPerUnit, action]};
GetWordsToPtr: UNSAFE PROC
[rope: ROPE, wordOffset: INT, nWords: NAT, ptr: LONG POINTER] = TRUSTED {
byteIndex: [0..bytesPerUnit) ¬ 0;
word: PACKED ARRAY [0..bytesPerUnit) OF CHAR ¬ ALL[0C];
action: Rope.ActionType = TRUSTED {
word[byteIndex] ¬ c;
IF byteIndex = bytesPerUnit-1
THEN {
byteIndex ¬ 0;
LOOPHOLE[ptr, WordPtr]­ ¬ LOOPHOLE[word];
ptr ¬ ptr + UNITS[WORD]}
ELSE byteIndex ¬ byteIndex + 1};
[] ¬ rope.Map[wordOffset*bytesPerUnit, nWords*bytesPerUnit, action]};
GetWord: PROC[rope: ROPE, wordOffset: INT] RETURNS[WORD] = {
byteIndex: NAT ¬ 0;
word: PACKED ARRAY [0..bytesPerUnit) OF CHAR ¬ ALL[0C];
action: Rope.ActionType = TRUSTED {
word[byteIndex] ¬ c;
byteIndex ¬ byteIndex + 1};
[] ¬ rope.Map[wordOffset*bytesPerUnit, bytesPerUnit, action];
RETURN[LOOPHOLE[word]]};
Diagnose: PROC[rope: ROPE, sth: SymbolTableHeader, base, offset: INT]
RETURNS[which: ROPE ¬ NIL] = TRUSTED {
ptr: LONG POINTER TO BlockDescriptor ¬ @sth.hvBlock;
list: LIST OF ROPE ¬ blockList;
fromBase: INT = offset-base;
First look at the fine grain table (if any).
IF sth.fgCount # 0 THEN {
fgOffset: INT = base+sth.fgRelBase;
IF CARD[(fgOffset+SIZE[FGHeader[0]])*bytesPerUnit] <= CARD[rope.Length[]] THEN {
FGHeader: TYPE = RECORD[offset: CARD, length: CARDINAL ¬ 0];
fgHeader: FGHeader;
inner: CARD;
GetWordsToPtr[rope, fgOffset, SIZE[FGHeader], @fgHeader];
inner ¬ offset - fgHeader.offset;
IF inner < fgHeader.length THEN
The difference is in the fine grain table
RETURN[
IO.PutFR[
" at offset %g in fine grain table (%g)",
[integer[fromBase]], [cardinal[inner]]]];
};
};
Now try the block descriptors
WHILE list # NIL DO
inner: CARD = fromBase - ptr.offset;
IF --inner>=0 AND-- inner < ptr.size THEN
RETURN[
IO.PutFR[
" at offset %g in %g (%g)",
[integer[fromBase]], [rope[list.first]], [cardinal[inner]]]];
list ¬ list.rest;
ptr ¬ ptr + SIZE[BlockDescriptor];
ENDLOOP;
which ¬ IO.PutFR1[" at offset %g in symbols ??", [integer[fromBase]]]};
NameOut: PROC[rope: ROPE, base: CARD, nameRecord: INT, stats: Stats] = {
byteIndex: INT = base+nameRecord+4;
length: CARDINAL = MIN[64, rope.Fetch[byteIndex-1]-0C];
action: Rope.ActionType = {
(stats.out).PutChar[c]};
[] ¬ rope.Map[byteIndex, length, action]};
GetName: PROC[rope: ROPE, base: CARD, nameRecord: INT] RETURNS[ROPE] = {
byteIndex: INT = base*bytesPerUnit+nameRecord+4;
length: CARDINAL = MIN[64, rope.Fetch[byteIndex-1]-0C];
RETURN[rope.Substr[byteIndex, length]]};
FilesOut: PROC[rope: ROPE, Mob: MobPtr, stats: Stats] = TRUSTED {
fti: CARD ¬ LOOPHOLE[FIRST[MobDefs.FTIndex]];
ftr: MobDefs.FTRecord;
WHILE fti < LOOPHOLE[Mob.ftLimit, CARD] DO
GetWordsToPtr[rope, fti+Mob.ftOffset.units, SIZE[MobDefs.FTRecord], @ftr];
(stats.out).PutF1[" file #%g: ", [integer[fti]]];
NameOut[rope, Mob.ssOffset.units, ftr.name, stats];
(stats.out).PutRope[", {"];
VersionOut[ftr.version, stats, TRUE];
(stats.out).PutRope["}\n"];
fti ¬ fti + SIZE[MobDefs.FTRecord];
ENDLOOP;
};
SegmentsOut: PROC[rope: ROPE, Mob: MobPtr, stats: Stats] = TRUSTED {
sgi: CARD ¬ LOOPHOLE[FIRST[MobDefs.SGIndex], CARD];
sgr: MobDefs.SGRecord;
WHILE sgi < LOOPHOLE[Mob.sgLimit, CARD] DO
GetWordsToPtr[rope, sgi+Mob.sgOffset.units, SIZE[MobDefs.SGRecord], @sgr];
(stats.out).PutF1[" seg #%g", [integer[sgi]]];
SELECT sgr.class FROM
code => (stats.out).PutRope[" (code)"];
symbols => (stats.out).PutRope[" (symbols)"];
acMap => (stats.out).PutRope[" (acMap)"];
other => (stats.out).PutRope[" (other)"];
ENDCASE => ERROR;
SELECT sgr.file FROM
MobDefs.FTNull =>
(stats.out).PutRope[", file: null"];
MobDefs.FTSelf =>
(stats.out).PutRope[", file: self"];
ENDCASE =>
(stats.out).PutF1[", file: %g", [integer[LOOPHOLE[sgr.file, CARD]]]];
(stats.out).PutF[
", base: %g, pages: %g, extraPages: %g\n",
[integer[sgr.base.units]], [integer[sgr.units.units]], [integer[sgr.extraUnits.units]]];
sgi ¬ sgi + SIZE[MobDefs.SGRecord];
ENDLOOP;
};
VersionOut: PROC[version: VersionStamp, stats: Stats, stampOnly: BOOL ¬ FALSE] = {
versionNibbles: PACKED ARRAY [0..16) OF [0..16) = LOOPHOLE[version];
FOR i: NAT IN [0..16) DO
nibble: [0..16) = versionNibbles[i];
(stats.out).PutChar[IF nibble < 10 THEN '0+nibble ELSE 'a+(nibble-10)];
ENDLOOP;
IF stampOnly THEN RETURN;
IF version.net # 0 OR version.host # 0 THEN
(stats.out).PutF[" (%g, %g, ", [cardinal[version.net]], [cardinal[version.host]]]
ELSE (stats.out).PutRope[" ("];
This next statement is separate to deal with potential errors in time conversion.
{ENABLE RuntimeError.UNCAUGHT => GO TO dreck;
(stats.out).PutF[
"%g)",
[time[BasicTime.FromNSTime[version.time]]]
];
EXITS
dreck => (stats.out).PutRope["??)"];
};
};
Commander.Register[
"CompareCode", DoCompare,
"Compare the code of two Mob files, reporting the differences.", $Code];
Commander.Register[
"CompareMob", DoCompare,
"Compare two Mob files, reporting the differences.", $All];
Commander.Register[
"CompareSymbol", DoCompare,
"Compare the symbols two Mob files, reporting the differences.", $Symbols];
Commander.Register[
"CompareDebug", DoCompare,
"Compare two Mob files, reporting the differences.", $Debug];
END.