-- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved. -- NewChainCruiser.mesa -- HGM, 15-Sep-85 1:11:10 -- Brenda Hankins 20-Sep-84 13:44:01 DIRECTORY BitMapDefs USING [Clear, Create, Map, MapIndex, Set, Test], File USING [File, nullFile], FormSW USING [ AllocateItemDescriptor, BooleanItem, ClientItemsProcType, CommandItem, newLine, nextPlace, NumberItem, ProcType, StringItem], Heap USING [systemZone], Inline USING [LongCOPY, LowHalf], HeapFile USING [ ChainBlock, headerSize, noSegment, SegmentIndex, segmentsPerPage, segmentSize, SerialAndHead], MFile USING [Error, GetLength, Handle, ReadOnly, Release], Put USING [CR, Decimal, Line, Text], Runtime USING [IsBound], Space USING [CopyOut, InsufficientSpace, Interval, Kill, Map, nullInterval], SpecialMFile USING [GetCapaWithAccess], Tool USING [ Create, MakeFileSW, MakeFormSW, MakeMsgSW, MakeSWsProc, UnusedLogName], ToolWindow USING [TransitionProcType], VMDefs USING [PageNumber, pageSize], Window USING [Box, Handle]; NewChainCruiser: PROGRAM IMPORTS BitMapDefs, FormSW, Heap, Inline, MFile, Put, Runtime, Space, SpecialMFile, Tool SHARES HeapFile = BEGIN --VARIABLES: toolData: MACHINE DEPENDENT RECORD [ -- tool window stuff: msgSW(0): Window.Handle ← NIL, fileSW(2): Window.Handle ← NIL, formSW(4): Window.Handle ← NIL, -- vars: heapFileName(6): LONG STRING, chainFileName(8): LONG STRING, chainFile(10): File.File, chainFileHandle(17): MFile.Handle, segmentToChange(19): CARDINAL, segmentToPointTo(20): CARDINAL, segmentBound(21): CARDINAL, -- last valid real segment no. heapSize(22): CARDINAL, chainSize(23): CARDINAL, filesOpened(24): BOOLEAN, permanentChange(25): BOOLEAN, chain(26): LONG POINTER TO HeapFile.ChainBlock, chainSpace(28): Space.Interval]; --**************************************************************************** OpenFiles: FormSW.ProcType = BEGIN --looks for the files in Mesa File System. errorOnOpen: BOOLEAN ← FALSE; IF toolData.heapFileName # NIL THEN BEGIN ENABLE BEGIN MFile.Error => {errorOnOpen ← TRUE; CONTINUE}; Space.InsufficientSpace => { Put.Line[toolData.msgSW, "Space.InsufficientSpace."L]; errorOnOpen ← TRUE; CONTINUE}; END; heapFileHandle: MFile.Handle ← MFile.ReadOnly[ toolData.heapFileName, [NIL, NIL] ! MFile.Error => Put.Line[toolData.msgSW, "MFile error on open of data file."L]]; toolData.heapSize ← Inline.LowHalf[ MFile.GetLength[heapFileHandle] / 512 --bytes/pg-- ]; MFile.Release[heapFileHandle]; END; IF errorOnOpen THEN { Put.Line[toolData.fileSW, "Specify the heap size and try again."L]; RETURN}; toolData.chainSize ← ((toolData.heapSize / HeapFile.segmentSize) + HeapFile.segmentsPerPage - 1) / HeapFile.segmentsPerPage; toolData.segmentBound ← (toolData.heapSize / HeapFile.segmentSize) + toolData.chainSize * HeapFile.headerSize - 1; IF toolData.chainFileName # NIL THEN BEGIN ENABLE MFile.Error => {errorOnOpen ← TRUE; CONTINUE}; tempSize: CARDINAL ← 0; toolData.chainFileHandle ← MFile.ReadOnly[ toolData.chainFileName, [NIL, NIL] ! MFile.Error => Put.Line[toolData.msgSW, "MFile error on open of chain file."L]]; toolData.chainFile ← SpecialMFile.GetCapaWithAccess[ toolData.chainFileHandle]; Put.Line[toolData.fileSW, "Chain file opened."L]; IF toolData.heapSize > toolData.chainSize * 2 THEN Put.Line[ toolData.msgSW, "Warning: size of chainfile is larger than that of Heap."L]; IF toolData.heapSize < toolData.chainSize * 2 THEN Put.Line[ toolData.msgSW, -- must stop "Size of chainfile is smaller than that of Heap - you should quit."L]; -- build chain data structure: toolData.chainSpace ← Space.Map[ window: [file: File.nullFile, base: 0, count: toolData.chainSize]]; toolData.chain ← toolData.chainSpace.pointer; FOR index: CARDINAL IN [0..toolData.chainSize) DO tempSpace: Space.Interval; chain0, chain1, chainChoice: LONG POINTER TO HeapFile.SerialAndHead; -- look at the chain in duplicate page pairs and use the 'correct' one. tempSpace ← LOOPHOLE[Space.Map[ window: [toolData.chainFile, index * 2 + 1, 2], access: readOnly]]; -- remember to skip over leader page chain0 ← LOOPHOLE[tempSpace.pointer]; chain1 ← LOOPHOLE[chain0 + VMDefs.pageSize]; chainChoice ← IF chain1.serialNumber > chain0.serialNumber THEN chain1 ELSE chain0; Inline.LongCOPY[ from: chainChoice, nwords: VMDefs.pageSize, to: toolData.chain + index * VMDefs.pageSize]; Space.Kill[tempSpace]; ENDLOOP; IF toolData.chain.header[0].chainHead = HeapFile.noSegment THEN Put.Line[ toolData.msgSW, "There are no segments in chain (head pts to end)."L]; END ELSE Put.Line[toolData.msgSW, "No chain file specified."L]; IF ~errorOnOpen THEN toolData.filesOpened ← TRUE; END; -- proc. OpenFiles --**************************************************************************** CheckChain: FormSW.ProcType = BEGIN segmentCount: CARDINAL ← 0; lastSeg: CARDINAL; foundProblem: BOOLEAN ← FALSE; freeMap: BitMapDefs.Map; IF NOT toolData.filesOpened THEN { Put.Line[toolData.msgSW, "No open file."L]; RETURN}; freeMap ← BitMapDefs.Create[toolData.segmentBound + 1]; FOR index: CARDINAL IN [0..toolData.chainSize) DO -- make nonexistent seg indices look 'busy' firstBit: BitMapDefs.MapIndex = index * VMDefs.pageSize; FOR j: CARDINAL IN [0..HeapFile.headerSize) DO BitMapDefs.Set[freeMap, firstBit + j] ENDLOOP; ENDLOOP; FOR s: CARDINAL ← toolData.chain.header[0].chainHead, toolData.chain.next[s] UNTIL s = HeapFile.noSegment DO IF NOT BitMapDefs.Test[freeMap, s] THEN BEGIN BitMapDefs.Set[freeMap, s]; lastSeg ← s; segmentCount ← segmentCount + 1; END ELSE -- have found a circularity. BEGIN foundProblem ← TRUE; Put.Text[toolData.fileSW, "Logical segment number "L]; Put.Decimal[toolData.fileSW, segmentCount]; Put.Text[toolData.fileSW, " (segment index "L]; Put.Decimal[toolData.fileSW, lastSeg]; Put.Line[ toolData.fileSW, ") should be changed to point to rest of chain."L]; Put.Text[toolData.fileSW, "It now points to segment index "L]; Put.Decimal[toolData.fileSW, s]; Put.Line[toolData.fileSW, "."L]; Put.Text[toolData.fileSW, "That change will leave "L]; Put.Decimal[toolData.fileSW, segmentCount]; Put.Text[toolData.fileSW, " segments in the heap."L]; Put.Text[toolData.fileSW, "When it is full there are "L]; Put.Decimal[ toolData.fileSW, toolData.heapSize / HeapFile.segmentSize]; Put.Line[toolData.fileSW, " segments in the heap."L]; EXIT; END; ENDLOOP; IF NOT foundProblem THEN Put.Line[toolData.fileSW, "No circularity found in chain."L]; BitMapDefsDelete[freeMap]; END; -- proc. CheckChain --**************************************************************************** ChainChanges: FormSW.ProcType = BEGIN IF NOT toolData.filesOpened THEN { Put.Line[toolData.msgSW, "No open file."L]; RETURN}; Put.Line[ toolData.msgSW, "Segments are specified as real indices NOT logical values (2 is Chain head)."L]; toolData.chain.next[toolData.segmentToChange] ← toolData.segmentToPointTo; IF toolData.permanentChange THEN BEGIN -- update chain file: page: CARDINAL = toolData.segmentToChange / VMDefs.pageSize; chainPage: VMDefs.PageNumber = page * 2 + (IF toolData.chain.header[page].serialNumber MOD 2 = 0 THEN 0 ELSE 1); Put.Line[toolData.fileSW, "This will be a permanent change"L]; toolData.chain.header[page].serialNumber ← toolData.chain.header[page].serialNumber + 1; --old: FileDefs.WritePageToFile[to: client.chainHandle, --old: page: chainPage, from: @client.chain.header[page]]; [] ← Space.CopyOut[ pointer: @toolData.chain.header[page], window: [ file: toolData.chainFile, base: chainPage + 1, -- incr to skip over leader page count: 1]]; END; END; -- proc. ChainChanges --************************************************************************** ShowChainSeq: FormSW.ProcType = BEGIN -- run thru and print out indices. segCount: CARDINAL ← 0; IF NOT toolData.filesOpened THEN { Put.Line[toolData.msgSW, "No open file."L]; RETURN}; FOR s: CARDINAL ← toolData.chain.header[0].chainHead, toolData.chain.next[s] UNTIL s = HeapFile.noSegment DO Put.Decimal[toolData.fileSW, s]; Put.Text[toolData.fileSW, " "L]; segCount ← segCount + 1; IF segCount > toolData.segmentBound THEN { Put.Line[toolData.fileSW, "Stopping due to an apparent circularity."L]; EXIT}; ENDLOOP; IF segCount <= toolData.segmentBound THEN Put.Line[toolData.fileSW, "end of chain."L]; END; -- proc. ShowChainSeq --************************************************************************** GenerateChainSeqs: FormSW.ProcType = BEGIN -- look for all disjoint sequences in chain file, -- it only prints subsequences not on chain. seg, lastSeg: CARDINAL; seqHeads, chainEntries: BitMapDefs.Map; IF NOT toolData.filesOpened THEN { Put.Line[toolData.msgSW, "No open file."L]; RETURN}; seqHeads ← BitMapDefs.Create[toolData.segmentBound + 1]; chainEntries ← BitMapDefs.Create[toolData.segmentBound + 1]; FOR index: CARDINAL IN [0..toolData.chainSize) DO -- make nonexistent seg indices look 'busy' firstBit: BitMapDefs.MapIndex = index * VMDefs.pageSize; FOR j: CARDINAL IN [0..HeapFile.headerSize) DO BitMapDefs.Set[chainEntries, firstBit + j] ENDLOOP; ENDLOOP; seg ← toolData.chain.header[0].chainHead; UNTIL seg = HeapFile.noSegment DO noOfSegs: CARDINAL ← 0; FOR s: CARDINAL ← seg, toolData.chain.next[s] DO IF BitMapDefs.Test[chainEntries, s] OR s = HeapFile.noSegment THEN BEGIN -- a circularity or can append another sequence Put.Text[toolData.fileSW, "Found a sequence "L]; Put.Decimal[toolData.fileSW, noOfSegs]; Put.Text[toolData.fileSW, " segments long starting at "L]; Put.Decimal[toolData.fileSW, seg]; Put.Text[toolData.fileSW, " and ending at "L]; Put.Decimal[toolData.fileSW, s]; Put.CR[toolData.fileSW]; IF seg = toolData.chain.header[0].chainHead THEN Put.Line[toolData.fileSW, " this is first segment in the chain"L]; IF s = toolData.chain.header[0].chainHead THEN Put.Line[ toolData.fileSW, " this last points to current head of the chain"L]; IF s # HeapFile.noSegment AND BitMapDefs.Test[seqHeads, s] AND s # toolData.chain.header[0].chainHead THEN BEGIN -- s is head of a chain we've saved, replace that head with seg BitMapDefs.Clear[seqHeads, s]; Put.Line[ toolData.fileSW, " it was prepended to a previous sequence."L]; END; BitMapDefs.Set[seqHeads, seg]; -- store seg as another chain start seg ← FindFree[chainEntries, seg]; EXIT; END ELSE { BitMapDefs.Set[chainEntries, s]; lastSeg ← s; noOfSegs ← noOfSegs + 1} ENDLOOP; ENDLOOP; BitMapDefsDelete[chainEntries]; BitMapDefsDelete[seqHeads]; END; -- proc. GenerateChainSeqs --************************************************************************** FindFree: PROCEDURE [m: BitMapDefs.Map, near: HeapFile.SegmentIndex] RETURNS [new: BitMapDefs.MapIndex] = BEGIN -- stolen from StableStorageImpl.FindSegment -- find 'nearest' free segment; high: BitMapDefs.MapIndex ← near; low: BitMapDefs.MapIndex ← ( -- skip over headers at chain page boundaries IF near > HeapFile.headerSize THEN near - (IF near MOD VMDefs.pageSize = HeapFile.headerSize THEN HeapFile.headerSize + 1 ELSE 1) ELSE HeapFile.headerSize); DO IF ~BitMapDefs.Test[m, high] THEN {new ← high; EXIT}; IF ~BitMapDefs.Test[m, low] THEN {new ← low; EXIT}; IF high = toolData.segmentBound AND low = HeapFile.headerSize THEN RETURN[HeapFile.noSegment]; -- ever get? high ← MIN[ high + (IF high + 1 MOD VMDefs.pageSize = 0 THEN HeapFile.headerSize ELSE 1), toolData.segmentBound]; low ← (IF low > HeapFile.headerSize THEN low - (IF low MOD VMDefs.pageSize = HeapFile.headerSize THEN HeapFile.headerSize + 1 ELSE 1) ELSE HeapFile.headerSize); ENDLOOP; END; --************************************************************************** BitMapDefsDelete: PROCEDURE [m: BitMapDefs.Map] = BEGIN -- inverse of BitMapDefs.Create Heap.systemZone.FREE[@m.data.BASE]; Heap.systemZone.FREE[@m]; END; --************************************************************************** HelpProc: FormSW.ProcType = BEGIN Put.Line[ toolData.fileSW, "Specify chain file name. If data file is on this volume then specify its name else specify its size in pages. Do 'Open Files'."L]; Put.Line[ toolData.fileSW, "Now do 'Check Chain', this will indicate whether there is a circularity in the chain."L]; Put.Line[ toolData.fileSW, "If there is, do 'Generate Chain Seqs' to see if you think any thing has been chopped off the chain (anything real long will look suspicious)."L]; Put.Line[ toolData.fileSW, "Do 'Chain Changes' to propose changes to the chain and when you're satisfied with all the changes you've made, do them all again with the boolean 'Permanent Changes' set."L]; Put.Line[ toolData.fileSW, "Show Chain Seq will show you what the chain looks like."L]; END; -- proc. HelpProc --************************************************************************** -- Tool needed routines: ClientTransition: ToolWindow.TransitionProcType = BEGIN SELECT TRUE FROM new = active => BEGIN toolData.heapFileName ← NIL; toolData.chainFileName ← NIL; toolData.chainFileHandle ← NIL; toolData.segmentToChange ← 2; -- head of chain. toolData.segmentToPointTo ← LAST[CARDINAL]; -- end of chain. toolData.chainSpace ← Space.nullInterval; toolData.chain ← NIL; toolData.heapSize ← 0; toolData.filesOpened ← FALSE; toolData.permanentChange ← FALSE; END; old = active => BEGIN IF toolData.chainSpace # Space.nullInterval THEN Space.Kill[toolData.chainSpace]; IF toolData.chainFileHandle # NIL THEN MFile.Release[toolData.chainFileHandle]; END; ENDCASE; END; -- proc. ClientTransition --*************************************************************************** MakeTool: PROCEDURE [initialBox: Window.Box] = BEGIN window: Window.Handle ← Tool.Create[ makeSWsProc: MakeSWs, initialState: default, initialBox: initialBox, clientTransition: ClientTransition, name: "NewChainCruiser"L, tinyName1: "NewChain"L, tinyName2: "Cruiser"L]; END; -- proc. MakeTool --****************************************************************************** tIndex: TYPE = { heapFileName, chainFileName, openFiles, heapSize, permanentChange, segmentToChange, segmentToPointTo, checkChain, chainChanges, showChainSeq, generateChainSeqs, help}; noToolIndices: CARDINAL = tIndex.LAST.ORD + 1; MakeCommon: FormSW.ClientItemsProcType = BEGIN OPEN FormSW; items ← AllocateItemDescriptor[noToolIndices]; items[tIndex.heapFileName.ORD] ← StringItem[ tag: "Heap File Name"L, place: newLine, string: @toolData.heapFileName, inHeap: TRUE]; items[tIndex.chainFileName.ORD] ← StringItem[ tag: "Chain File Name"L, place: newLine, string: @toolData.chainFileName, inHeap: TRUE]; items[tIndex.openFiles.ORD] ← CommandItem[ tag: "OpenFiles"L, proc: OpenFiles, place: newLine]; items[tIndex.heapSize.ORD] ← NumberItem[ tag: "HeapSize (in pages)"L, value: @toolData.heapSize, radix: decimal, place: nextPlace]; items[tIndex.permanentChange.ORD] ← BooleanItem[ tag: "Make Permanent Change"L, switch: @toolData.permanentChange, place: nextPlace]; items[tIndex.segmentToChange.ORD] ← NumberItem[ tag: "Segment Index To Change"L, value: @toolData.segmentToChange, radix: decimal, place: newLine]; items[tIndex.segmentToPointTo.ORD] ← NumberItem[ tag: "Segment Index To Point To"L, value: @toolData.segmentToPointTo, radix: decimal, place: nextPlace]; items[tIndex.checkChain.ORD] ← CommandItem[ tag: "Check Chain"L, proc: CheckChain, place: newLine]; items[tIndex.chainChanges.ORD] ← CommandItem[ tag: "Chain Changes"L, proc: ChainChanges, place: nextPlace]; items[tIndex.showChainSeq.ORD] ← CommandItem[ tag: "Show Chain Seq"L, proc: ShowChainSeq, place: nextPlace]; items[tIndex.generateChainSeqs.ORD] ← CommandItem[ tag: "Generate Chain Seqs"L, proc: GenerateChainSeqs, place: nextPlace]; items[tIndex.help.ORD] ← CommandItem[ tag: "Help"L, proc: HelpProc, place: nextPlace]; RETURN[items: items, freeDesc: TRUE]; END; -- proc. MakeCommon. --**************************************************************************** MakeSWs: Tool.MakeSWsProc = BEGIN logName: STRING ← [40]; Tool.UnusedLogName[unused: logName, root: "ChainCruiser.log"L]; toolData.msgSW ← Tool.MakeMsgSW[window: window, h: 32]; toolData.formSW ← Tool.MakeFormSW[ window: window, formProc: MakeCommon, h: 96]; toolData.fileSW ← Tool.MakeFileSW[window: window, name: logName, h: 350]; END; --************************************************************************ -- mainline code: IF Runtime.IsBound[LOOPHOLE[BitMapDefs.Create]] THEN MakeTool[[[0, 35], [482, 650]]] ELSE Put.Line[s: "BitMapDefsImpl not available!"L]; END. LOG: 4-Sep-84 12:31:29 converted from MSScanTool