123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890 |
- (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
- MODULE Info; (** AUTHOR "pjm/staubesv"; PURPOSE "System information"; *)
- IMPORT
- SYSTEM, Machine, Heaps, Objects, Streams, Reflection, Modules, Commands, Options, Strings, D := Debugging, Kernel;
- CONST
- AddressSize = SIZEOF(ADDRESS);
- RecordBlock = 1;
- ProtRecBlock = 2;
- ArrayBlock = 3;
- SystemBlock = 4;
- MaxNofTypes = 2048;
- (* Analyzer.Sort *)
- SortNone = 0;
- SortByCount = 1;
- SortBySize = 2;
- SortByTotalSize = 3; (* whereas TotalSize = Count * Size *)
- SortByName = 4;
- AllocatorHistorySize = 4096; (* recent history of allocators to be able to trace memory wasting sources *)
- TYPE
- Type = RECORD
- ptag : ADDRESS;
- count : LONGINT;
- size : SIZE;
- type : SHORTINT;
- pc: ADDRESS;
- END;
- Analyzer = OBJECT
- VAR
- types : POINTER TO ARRAY OF Type;
- nofElements : LONGINT;
- (* global statistics *)
- nofHeapBlocks, nofFreeBlocks, nofSystemBlocks, nofRecordBlocks, nofProtRecBlocks, nofArrayBlocks: LONGINT;
- sizeHeapBlocks, sizeFreeBlocks, sizeSystemBlocks, sizeRecordBlocks, sizeProtRecBlocks, sizeArrayBlocks: SIZE;
- PROCEDURE &Init(size : LONGINT);
- BEGIN
- ASSERT(size > 0);
- NEW(types, size);
- Reset;
- END Init;
- PROCEDURE Reset;
- VAR i : LONGINT;
- BEGIN
- nofElements := 0;
- IF (types # NIL) THEN
- FOR i := 0 TO LEN(types)-1 DO
- types[i].ptag := Heaps.NilVal;
- types[i].count := 0;
- types[i].size := 0;
- END;
- END;
- nofHeapBlocks := 0; sizeHeapBlocks := 0;
- nofFreeBlocks := 0; sizeFreeBlocks := 0;
- nofSystemBlocks := 0; sizeSystemBlocks := 0;
- nofRecordBlocks := 0; sizeRecordBlocks := 0;
- nofProtRecBlocks := 0; sizeProtRecBlocks := 0;
- nofArrayBlocks := 0; sizeArrayBlocks := 0;
- END Reset;
- PROCEDURE SortBy(mode : LONGINT);
- VAR i, j : LONGINT; temp : Type;
- PROCEDURE IsGreaterThan(CONST entry1, entry2 : Type; mode : LONGINT) : BOOLEAN;
- VAR name1, name2: ARRAY 256 OF CHAR; count1,count2, size1, size2: SIZE;
- BEGIN
- IF mode = SortByName THEN
- GetName(entry1.ptag,name1);
- GetName(entry2.ptag,name2);
- RETURN name1 > name2;
- ELSE
- count1 := entry1.count;
- size1 := entry1.size DIV count1;
- count2 := entry2.count;
- size2 := entry2.size DIV count2;
- RETURN
- ((mode = SortByCount) & (count1 > count2)) OR
- ((mode = SortBySize) & (size1 > size2)) OR
- ((mode = SortByTotalSize) & (size1*count1 > size2 * count2))
- ;
- END;
- END IsGreaterThan;
- BEGIN
- ASSERT((mode = SortByCount) OR (mode = SortBySize) OR (mode = SortByTotalSize) OR (mode=SortByName));
- (* sort descending... *)
- FOR i := 0 TO nofElements-1 DO
- FOR j := 1 TO nofElements-1 DO
- IF IsGreaterThan(types[j], types[j-1], mode) THEN
- temp := types[j-1];
- types[j-1] := types[j];
- types[j] := temp;
- END;
- END;
- END;
- END SortBy;
- PROCEDURE Add(CONST block : Heaps.HeapBlock; byPC: BOOLEAN);
- VAR type: SHORTINT;
- PROCEDURE AddByType(type: SHORTINT);
- VAR tag: ADDRESS; i: LONGINT;
- BEGIN
- SYSTEM.GET(block.dataAdr + Heaps.TypeDescOffset, tag);
- i := 0; WHILE (i < LEN(types)) & (i < nofElements) & (types[i].ptag # tag) DO INC(i) END;
- IF (i < nofElements) THEN
- INC(types[i].count);
- INC(types[i].size, block.size);
- ELSIF (i = nofElements) & (i < LEN(types)) THEN
- types[i].ptag := tag;
- types[i].count := 1;
- types[i].size := block.size;
- types[i].type := type;
- types[i].pc := 0;
- INC(nofElements)
- END;
- END AddByType;
- PROCEDURE AddByPC(type: SHORTINT);
- VAR pc: ADDRESS; i: LONGINT;
- BEGIN
- SYSTEM.GET(block.dataAdr + Heaps.HeapBlockOffset, pc);
- SYSTEM.GET(pc + Heaps.HeapBlockOffset, pc);
- IF pc # 0 THEN
- i := 0; WHILE (i < LEN(types)) & (i < nofElements) & (types[i].pc # pc) DO INC(i) END;
- IF (i < nofElements) THEN
- INC(types[i].count);
- INC(types[i].size, block.size);
- ELSIF (i = nofElements) & (i < LEN(types)) THEN
- types[i].ptag := 0;
- types[i].count := 1;
- types[i].size := block.size;
- types[i].type := type;
- types[i].pc := pc;
- INC(nofElements)
- END;
- END;
- END AddByPC;
- BEGIN
- INC(nofHeapBlocks); INC(sizeHeapBlocks, block.size);
- IF (block IS Heaps.RecordBlock) OR (block IS Heaps.ProtRecBlock) OR (block IS Heaps.ArrayBlock) THEN
- IF (block IS Heaps.ProtRecBlock) THEN
- type := ProtRecBlock;
- INC(nofProtRecBlocks); INC(sizeProtRecBlocks, block.size);
- ELSIF (block IS Heaps.RecordBlock) THEN
- type := RecordBlock;
- INC(nofRecordBlocks); INC(sizeRecordBlocks, block.size);
- ELSIF (block IS Heaps.ArrayBlock) THEN
- type := ArrayBlock;
- INC(nofArrayBlocks); INC(sizeArrayBlocks, block.size);
- ELSE
- HALT(99);
- END;
- IF byPC THEN
- AddByPC(type)
- ELSE
- (* all these heap blocks have a type tag *)
- AddByType(type)
- END;
- ELSIF (block IS Heaps.SystemBlock) THEN
- INC(nofSystemBlocks); INC(sizeSystemBlocks, block.size);
- (* system blocks do not have a type tag *)
- AddByPC(SystemBlock);
- ELSIF (block IS Heaps.FreeBlock) THEN
- INC(nofFreeBlocks); INC(sizeFreeBlocks, block.size);
- END;
- END Add;
- PROCEDURE ShowBlocks(CONST mask : ARRAY OF CHAR; out : Streams.Writer);
- VAR
- module : Modules.Module; typedesc : Modules.TypeDesc;
- size, totalSize: SIZE;
- startpc: ADDRESS;
- i, selected, total : LONGINT;
- string : ARRAY 256 OF CHAR; copy: ARRAY 256 OF CHAR;
- BEGIN
- ASSERT(out # NIL);
- size := 0; totalSize := 0;
- selected := 0; total := 0;
- FOR i := 0 TO nofElements-1 DO
- INC(total, types[i].count);
- module := NIL;
- IF (types[i].pc # 0) THEN
- module := Modules.ThisModuleByAdr(types[i].pc);
- ELSIF (types[i].ptag # 0) THEN
- Modules.ThisTypeByAdr(types[i].ptag, module, typedesc);
- END;
- IF (module # NIL) THEN
- IF (types[i].ptag # 0) THEN
- string := "";
- COPY(module.name,copy);
- Strings.AppendX(string, copy);
- Strings.AppendX(string, ".");
- COPY(typedesc.name,copy);
- Strings.AppendX(string, copy);
- ELSE
- string := "";
- COPY(module.name,copy);
- Strings.AppendX(string, copy);
- Strings.AppendX(string, ".");
- Reflection.GetProcedureName(types[i].pc, copy,startpc);
- Strings.AppendX(string, copy);
- Strings.Append(string,":");
- Strings.IntToStr(LONGINT(types[i].pc - startpc), copy);
- Strings.Append(string,copy);
- END;
- IF Strings.Match(mask, string) THEN
- CASE types[i].type OF
- |RecordBlock: out.String("R ");
- |ProtRecBlock: out.String("P ");
- |ArrayBlock: out.String("A ");
- |SystemBlock: out.String("S ");
- ELSE
- out.String("U ");
- END;
- INC(selected, types[i].count);
- out.Int(types[i].count, 8); out.Char(" ");
- INC(size, types[i].size);
- out.Int(types[i].size DIV types[i].count, 6); out.String("B ");
- out.Int(types[i].size, 10); out.String("B ");
- out.String(string);
- out.String(" (total ");
- WriteB(types[i].size, out); out.String(")"); out.Ln
- END;
- END;
- END;
- out.Ln;
- IF (selected # total) THEN
- out.String("Selected "); out.Int(selected, 1); out.String(" of ");
- out.Int(total, 1); out.String(" dynamic records of ");
- out.Int(nofElements, 1); out.String(" unique types (total size : ");
- WriteB(size, out); out.String(" of "); WriteB(totalSize, out); out.String(")");
- out.Ln;
- ELSE
- out.Int(total, 1); out.String(" dynamic records of ");
- out.Int(nofElements, 1); out.String(" unique types found");
- out.String(" (total size : "); WriteB(sizeHeapBlocks, out); out.String(")");
- out.Ln;
- END;
- END ShowBlocks;
- PROCEDURE Show(out : Streams.Writer; CONST mask : ARRAY OF CHAR; sortMode : LONGINT; byPC: BOOLEAN);
- VAR nofUsedBlocks, sizeUsedBlocks :SIZE;
- PROCEDURE ShowBlock(CONST name : ARRAY OF CHAR; nofBlocks: SIZE; size: SIZE; totalNofBlocks: SIZE; totalSize : SIZE; out : Streams.Writer);
- BEGIN
- out.Int(nofBlocks, 8); out.Char(" "); ShowPercent(nofBlocks, totalNofBlocks, out); out.Char(" ");
- out.String(name);
- out.String(" ("); WriteB(size, out); out.String(", "); ShowPercent(size, totalSize, out); out.String(")");
- out.Ln;
- END ShowBlock;
- PROCEDURE ShowPercent(cur, max : SIZE; out : Streams.Writer);
- VAR percent : LONGINT;
- BEGIN
- IF (max > 0) THEN
- percent := ENTIER(100 * (cur / max) + 0.5);
- ELSE
- percent := 0;
- END;
- IF (percent < 10) THEN out.String(" ");
- ELSIF (percent < 100) THEN out.Char(" ");
- END;
- out.Int(percent, 0); out.Char("%");
- END ShowPercent;
- BEGIN
- ASSERT(out # NIL);
- nofUsedBlocks := nofHeapBlocks - nofFreeBlocks;
- sizeUsedBlocks := sizeHeapBlocks - sizeFreeBlocks;
- out.Char(0EX); (* non-proportional font *)
- ShowBlock("HeapBlocks", nofHeapBlocks, sizeHeapBlocks, nofHeapBlocks, sizeHeapBlocks, out);
- ShowBlock("UsedBlocks", nofUsedBlocks, sizeUsedBlocks, nofHeapBlocks, sizeHeapBlocks, out);
- ShowBlock("FreeBlocks", nofFreeBlocks, sizeFreeBlocks, nofHeapBlocks, sizeHeapBlocks, out);
- out.Ln;
- ShowBlock("UsedBlocks", nofUsedBlocks, sizeUsedBlocks, nofUsedBlocks, sizeUsedBlocks, out);
- ShowBlock("SystemBlocks", nofSystemBlocks, sizeSystemBlocks, nofUsedBlocks, sizeUsedBlocks, out);
- ShowBlock("RecordBlocks", nofRecordBlocks, sizeRecordBlocks, nofUsedBlocks, sizeUsedBlocks, out);
- ShowBlock("ProtRectBlocks", nofProtRecBlocks, sizeProtRecBlocks, nofUsedBlocks, sizeUsedBlocks, out);
- ShowBlock("ArrayBlocks", nofArrayBlocks, sizeArrayBlocks, nofUsedBlocks, sizeUsedBlocks, out);
- IF (mask # "") THEN
- out.Ln;
- IF (sortMode = SortByCount) OR (sortMode = SortBySize) OR (sortMode = SortByTotalSize) OR (sortMode = SortByName) THEN
- SortBy(sortMode);
- END;
- ShowBlocks(mask, out);
- END;
- out.Char(0FX); (* proportional font *)
- END Show;
- END Analyzer;
- VAR
- currentMarkValueAddress : ADDRESS;
- recentAllocators*: ARRAY AllocatorHistorySize OF RECORD pc*: ARRAY 3 OF ADDRESS; time*: HUGEINT END;
- numRecentAllocators*: LONGINT;
- PROCEDURE LogAlloc(p: ANY);
- VAR time: HUGEINT; bp: ADDRESS; i: LONGINT;pc: ADDRESS;
- BEGIN
- time := Machine.GetTimer();
- bp := SYSTEM.GetFramePointer();
- SYSTEM.GET(bp+SIZEOF(ADDRESS),pc);
- FOR i := 0 TO LEN(recentAllocators[numRecentAllocators].pc)-1 DO
- recentAllocators[numRecentAllocators].pc[i] := pc;
- IF bp # 0 THEN
- SYSTEM.GET(bp, bp);
- SYSTEM.GET(bp+SIZEOF(ADDRESS),pc);
- END;
- END;
- recentAllocators[numRecentAllocators].time := time;
- INC(numRecentAllocators); numRecentAllocators := numRecentAllocators MOD LEN(recentAllocators);
- END LogAlloc;
- PROCEDURE WriteB(b : SIZE; out : Streams.Writer);
- VAR shift : LONGINT; suffix : ARRAY 2 OF CHAR;
- BEGIN
- IF b < 100*1024 THEN suffix := ""; shift := 0
- ELSIF b < 100*1024*1024 THEN suffix := "K"; shift := -10
- ELSE suffix := "M"; shift := -20
- END;
- IF b # ASH(ASH(b, shift), -shift) THEN out.Char("~") END;
- out.Int(ASH(b, shift), 1);
- IF TRUE THEN
- out.String(suffix); out.Char("B")
- ELSE
- out.Char(" ");
- out.String(suffix); out.String("byte");
- IF b # 1 THEN out.Char("s") END
- END
- END WriteB;
- (** Show the details of the specified module. *)
- PROCEDURE ModuleDetails*(context : Commands.Context); (** [Options] module ~ *)
- VAR
- m : Modules.Module; i, j, k: LONGINT;
- p, procAdr: ADDRESS;
- adr : ADDRESS;
- modn : ARRAY 33 OF CHAR;
- options : Options.Options;
- BEGIN
- NEW(options);
- options.Add("d", "details", Options.Flag);
- IF options.Parse(context.arg, context.error) THEN
- context.arg.SkipWhitespace; context.arg.String(modn);
- m := Modules.root;
- WHILE (m # NIL) & (m.name # modn) DO m := m.next END;
- IF m # NIL THEN
- context.out.String(m.name);
- context.out.String(" refcnt = "); context.out.Int(m.refcnt, 1);
- context.out.String(" sb ="); context.out.Hex(m.sb, 9);
- context.out.String(" dataSize = "); context.out.Int(LEN(m.data), 1);
- context.out.String(" staticTdSize = "); context.out.Int(LEN(m.staticTypeDescs), 1);
- context.out.String(" codeSize = "); context.out.Int(LEN(m.code), 1);
- context.out.String(" refSize = "); context.out.Int(LEN(m.refs), 1);
- context.out.String(" entries = "); context.out.Int(LEN(m.entry), 1);
- context.out.String(" commands = "); context.out.Int(LEN(m.command), 1);
- context.out.String(" modules = "); context.out.Int(LEN(m.module), 1);
- context.out.String(" types = "); context.out.Int(LEN(m.typeInfo), 1);
- context.out.String(" pointers = "); context.out.Int(LEN(m.ptrAdr), 1);
- context.out.Ln; context.out.String(" ptrAdr:");
- FOR i := 0 TO LEN(m.ptrAdr)-1 DO
- context.out.Char(" "); context.out.Int(m.ptrAdr[i]-m.sb, 1)
- END;
- context.out.Ln;
- IF options.GetFlag("details") THEN
- context.out.String("Pointer Details: ");
- IF (m.ptrAdr # NIL) THEN
- context.out.Ln;
- FOR i := 0 TO LEN(m.ptrAdr) - 1 DO
- context.out.Int(i, 0); context.out.String(": ");
- context.out.Address(m.ptrAdr[i]); context.out.String(" -> ");
- SYSTEM.GET(m.ptrAdr[i], adr);
- context.out.Address(adr);
- context.out.Ln;
- END;
- ELSE
- context.out.String("none"); context.out.Ln;
- END;
- END;
- FOR i := 0 TO LEN(m.typeInfo) - 1 DO
- context.out.Ln; context.out.String(" type:");
- context.out.Hex(m.typeInfo[i].tag, 9);
- context.out.Char(" "); context.out.String(m.typeInfo[i].name);
- context.out.Hex(SYSTEM.VAL(LONGINT, m.typeInfo[i].flags), 9);
- (* type descriptor info *)
- context.out.Ln; context.out.String(" typedesc1:");
- p := m.typeInfo[i].tag; (* address of static type descriptor *)
- REPEAT
- SYSTEM.GET(p, k);
- IF ABS(k) <= 4096 THEN context.out.Char(" "); context.out.Int(k, 1)
- ELSE context.out.Hex(k, 9)
- END;
- INC(p, AddressSize)
- UNTIL k < -40000000H;
- (* methods *)
- context.out.Ln; context.out.String(" typedescmths:");
- p := SYSTEM.VAL(ADDRESS, m.typeInfo[i].tag) + Modules.Mth0Ofs;
- j := 0;
- SYSTEM.GET(p, procAdr);
- WHILE procAdr # Heaps.MethodEndMarker DO
- context.out.Ln; context.out.Int(j, 3); context.out.Char(" ");
- Reflection.WriteProc(context.out, procAdr);
- DEC(p, AddressSize);
- SYSTEM.GET(p, procAdr);
- INC(j)
- END
- END;
- context.out.Ln
- END;
- END;
- END ModuleDetails;
- (** Find a procedure, given the absolute PC address. *)
- PROCEDURE ModulePC*(context : Commands.Context); (** pc *)
- VAR pc : LONGINT;
- BEGIN
- context.arg.SkipWhitespace; context.arg.Int(pc, FALSE);
- IF Modules.ThisModuleByAdr(pc) # NIL THEN
- Reflection.WriteProc(context.out, pc);
- ELSE
- context.out.Hex(pc, 8); context.out.String(" not found")
- END;
- context.out.Ln;
- END ModulePC;
- PROCEDURE AllObjects*(context : Commands.Context); (** [Options] mask ~ *)
- VAR
- options : Options.Options; sortMode : LONGINT;
- analyzer : Analyzer;
- memBlock {UNTRACED}: Machine.MemoryBlock;
- heapBlock : Heaps.HeapBlock;
- p : ADDRESS;
- mask : ARRAY 128 OF CHAR;
- BEGIN
- NEW(options);
- options.Add("s", "sort", Options.Integer);
- options.Add(0X, "pc", Options.Flag);
- options.Add(0X, "gc", Options.Flag);
- IF options.Parse(context.arg, context.error) THEN
- IF ~options.GetInteger("sort", sortMode) THEN sortMode := SortNone; END;
- context.arg.SkipWhitespace; context.arg.String(mask);
- NEW(analyzer, MaxNofTypes);
- IF options.GetFlag("gc") THEN Heaps.LazySweepGC END; (* slight inaccuracy here: other processes can kick in now *)
- Machine.Acquire(Machine.Heaps);
- Heaps.FullSweep(); (* the heap might contain wrong pointers in the freed part *)
- memBlock := Machine.memBlockHead;
- WHILE memBlock # NIL DO
- p := memBlock.beginBlockAdr;
- WHILE p # memBlock.endBlockAdr DO
- heapBlock := SYSTEM.VAL(Heaps.HeapBlock, p + Heaps.BlockHeaderSize); (* get heap block *)
- analyzer.Add(heapBlock, options.GetFlag("pc"));
- p := p + heapBlock.size
- END;
- memBlock := memBlock.next
- END;
- Machine.Release(Machine.Heaps);
- analyzer.Show(context.out, mask, sortMode, options.GetFlag("pc"));
- END;
- END AllObjects;
- PROCEDURE ShowRecentAllocators*(out: Streams.Writer; scale: HUGEINT);
- VAR
- i,from,to,num, pcs: LONGINT;
- pc,startpc: ADDRESS;
- module: Modules.Module; name: ARRAY 256 OF CHAR;
- time: HUGEINT;
- timer: Kernel.MilliTimer;
- BEGIN
- time := Machine.GetTimer();
- IF scale <= 0 THEN
- Kernel.SetTimer( timer, 100 ); scale := Machine.GetTimer();
- WHILE ~Kernel.Expired( timer ) DO END;
- scale := (Machine.GetTimer() - scale) DIV 100; (* 1 ms resolution *)
- END;
- out.String("----------- recent allocators, t = ");
- out.Hex(recentAllocators[i].time, -16);
- out.String(" ---------------"); out.Ln;
- Machine.Acquire(Machine.Heaps);
- i := numRecentAllocators;
- DEC(i); i := i MOD LEN(recentAllocators);
- from := i;
- pc := recentAllocators[i].pc[0];
- WHILE (i # numRecentAllocators) & (pc # 0) DO
- DEC(i); i := i MOD LEN(recentAllocators);
- pc := recentAllocators[i].pc[0];
- END;
- to := i;
- Machine.Release(Machine.Heaps);
- i := from; num := 0;
- WHILE i # to DO
- out.Int(num,1); out.String(": ");
- out.Hex(recentAllocators[i].time, -16);
- out.String("(");
- out.Int( SHORT((recentAllocators[i].time-time) DIV scale), 1);
- out.String(")");
- out.String(": ");
- FOR pcs := 0 TO LEN(recentAllocators[i].pc)-1 DO
- pc := recentAllocators[i].pc[pcs];
- module := Modules.ThisModuleByAdr(pc);
- out.String(module.name);
- out.String(".");
- Reflection.GetProcedureName(pc, name,startpc);
- out.String(name);
- out.String(":");
- out.Int(pc-startpc,1);
- out.String(" ");
- END;
- out.Ln;
- DEC(i); i := i MOD LEN(recentAllocators);
- INC(num);
- END;
- END ShowRecentAllocators;
- PROCEDURE ClearRecentAllocators*;
- VAR i: LONGINT;
- BEGIN
- Machine.Acquire(Machine.Heaps);
- i := (numRecentAllocators - 1) MOD LEN(recentAllocators);
- recentAllocators[i].pc[0] := 0;
- Machine.Release(Machine.Heaps);
- END ClearRecentAllocators;
- PROCEDURE AddAllocatorLogger*;
- BEGIN
- Heaps.SetAllocationLogger(LogAlloc);
- END AddAllocatorLogger;
- PROCEDURE RecentAllocators*(context : Commands.Context); (** [Options] mask ~ *)
- VAR
- options : Options.Options;
- scale: HUGEINT;
- num: LONGINT;
- BEGIN
- NEW(options);
- options.Add("c", "clear", Options.Flag);
- options.Add("s", "scale", Options.Integer);
- IF options.Parse(context.arg, context.error) THEN
- IF options.GetInteger("scale", num) & (num > 0 ) THEN
- scale := num
- ELSE (* autoscale to ms *)
- scale := 0;
- END;
- ShowRecentAllocators(context.out, scale);
- IF options.GetFlag("clear") THEN ClearRecentAllocators END;
- END;
- END RecentAllocators;
- PROCEDURE TraceModule*(context : Commands.Context); (** moduleName mask ~ *)
- VAR
- options : Options.Options; sortMode : LONGINT;
- analyzer : Analyzer;
- mask : ARRAY 128 OF CHAR;
- moduleName : Modules.Name; module : Modules.Module;
- BEGIN
- NEW(options);
- options.Add("s", "sort", Options.Integer);
- IF options.Parse(context.arg, context.error) THEN
- IF ~options.GetInteger("sort", sortMode) THEN sortMode := SortNone; END;
- context.arg.SkipWhitespace; context.arg.String(moduleName);
- context.arg.SkipWhitespace; context.arg.String(mask);
- module := Modules.ModuleByName(moduleName);
- IF (module # NIL) THEN
- NEW(analyzer, MaxNofTypes);
- Machine.Acquire(Machine.Heaps);
- IncrementCurrentMarkValue;
- module.FindRoots;
- AnalyzeMarkedBlocks(analyzer);
- Machine.Release(Machine.Heaps);
- context.out.String("Heap block referenced by module "); context.out.String(moduleName); context.out.Char(":");
- context.out.Ln;
- analyzer.Show(context.out, mask, sortMode, FALSE);
- ELSE
- context.error.String("Module "); context.error.String(moduleName); context.error.String(" is not loaded."); context.error.Ln;
- END;
- END;
- END TraceModule;
- PROCEDURE TraceReference*(context : Commands.Context); (** ModuleName.VariableName mask ~ *)
- VAR
- options : Options.Options; sortMode : LONGINT;
- analyzer : Analyzer; address : ADDRESS;
- module : Modules.Module; variable : Reflection.Variable;
- mask, modVar : ARRAY 256 OF CHAR; array : Strings.StringArray;
- varName : ARRAY 64 OF CHAR;
- BEGIN
- NEW(options);
- options.Add("s", "sort", Options.Integer);
- IF options.Parse(context.arg, context.error) THEN
- IF ~options.GetInteger("sort", sortMode) THEN sortMode := SortNone; END;
- context.arg.SkipWhitespace; context.arg.String(modVar);
- context.arg.SkipWhitespace; context.arg.String(mask);
- array := Strings.Split(modVar, ".");
- IF (LEN(array) = 2) THEN
- module := Modules.ModuleByName(array[0]^);
- IF (module # NIL) THEN
- COPY(array[1]^, varName);
- IF Reflection.FindVar(module, varName, variable) THEN
- IF (variable.type = 13) OR (variable.type = 29) THEN
- NEW(analyzer, MaxNofTypes);
- context.out.String("Heap blocks reference by variable "); context.out.String(modVar);
- context.out.Char(":"); context.out.Ln;
- IF (variable.adr # 0) THEN
- SYSTEM.GET(variable.adr, address);
- MarkReference(analyzer, SYSTEM.VAL(ANY, address));
- analyzer.Show(context.out, mask, sortMode, FALSE);
- END;
- ELSE
- context.error.String("Variable is not a pointer"); context.error.Ln;
- END;
- ELSE
- context.error.String("Variable "); context.error.String(array[1]^); context.error.String(" not found");
- context.error.Ln;
- END;
- ELSE
- context.error.String("Module "); context.error.String(array[0]^); context.error.String(" not found");
- context.error.Ln;
- END;
- ELSE
- context.error.String("Expected ModuleName.VariableName parameter"); context.error.Ln;
- END;
- END;
- END TraceReference;
- PROCEDURE MarkReference(analyzer : Analyzer; ref : ANY);
- BEGIN
- ASSERT(analyzer # NIL);
- Machine.Acquire(Machine.Heaps);
- IncrementCurrentMarkValue;
- Heaps.Mark(ref);
- AnalyzeMarkedBlocks(analyzer);
- Machine.Release(Machine.Heaps);
- END MarkReference;
- PROCEDURE TraceProcessID*(context : Commands.Context); (** ProcessID mask ~ *)
- VAR
- options : Options.Options; sortMode : LONGINT;
- analyzer : Analyzer;
- process : Objects.Process;
- processID : LONGINT; mask : ARRAY 256 OF CHAR;
- BEGIN
- NEW(options);
- options.Add("s", "sort", Options.Integer);
- IF options.Parse(context.arg, context.error) THEN
- IF ~options.GetInteger("sort", sortMode) THEN sortMode := SortNone; END;
- IF context.arg.GetInteger(processID, FALSE) THEN
- context.arg.SkipWhitespace; context.arg.String(mask);
- process := FindProcessByID(processID);
- IF (process # NIL) THEN
- NEW(analyzer, MaxNofTypes);
- Machine.Acquire(Machine.Heaps);
- IncrementCurrentMarkValue;
- process.FindRoots;
- Heaps.CheckCandidates;
- AnalyzeMarkedBlocks(analyzer);
- Machine.Release(Machine.Heaps);
- context.out.String("Heap blocks referenced by process ID = "); context.out.Int(processID, 0); context.out.Char(":");
- context.out.Ln;
- analyzer.Show(context.out, mask, sortMode, FALSE);
- ELSE
- context.error.String("Process ID = "); context.error.Int(processID, 0); context.error.String(" not found");
- context.error.Ln;
- END;
- ELSE
- context.error.String("Expected ProcessID parameter"); context.error.Ln;
- END;
- END;
- END TraceProcessID;
- PROCEDURE FindProcessByID(id : LONGINT) : Objects.Process;
- VAR
- memBlock {UNTRACED}: Machine.MemoryBlock;
- heapBlock {UNTRACED}: Heaps.HeapBlock;
- blockAdr, tag : ADDRESS;
- process : Objects.Process;
- i : LONGINT;
- BEGIN
- i := 0;
- Machine.Acquire(Machine.Heaps);
- process := NIL;
- memBlock := Machine.memBlockHead;
- WHILE (memBlock # NIL) & (process = NIL) DO
- blockAdr := memBlock.beginBlockAdr;
- WHILE (blockAdr # memBlock.endBlockAdr) & (process = NIL) DO
- heapBlock := SYSTEM.VAL(Heaps.HeapBlock, blockAdr + Heaps.BlockHeaderSize);
- IF (heapBlock IS Heaps.RecordBlock) THEN
- SYSTEM.GET(heapBlock.dataAdr + Heaps.TypeDescOffset, tag);
- IF (tag = SYSTEM.TYPECODE(Objects.Process)) THEN
- process := SYSTEM.VAL(Objects.Process, heapBlock.dataAdr);
- IF (process.id # id) THEN process := NIL; END;
- END;
- END;
- blockAdr := blockAdr + heapBlock.size
- END;
- memBlock := memBlock.next
- END;
- Machine.Release(Machine.Heaps);
- RETURN process;
- END FindProcessByID;
- (* Caller MUST hold Machine.Heaps lock!! *)
- PROCEDURE AnalyzeMarkedBlocks(analyzer : Analyzer);
- VAR
- memBlock {UNTRACED}: Machine.MemoryBlock;
- heapBlock : Heaps.HeapBlock;
- currentMarkValue : LONGINT;
- blockAdr : ADDRESS;
- mark : LONGINT;
- BEGIN
- ASSERT(analyzer # NIL);
- currentMarkValue := GetCurrentMarkValue();
- memBlock := Machine.memBlockHead;
- WHILE memBlock # NIL DO
- blockAdr := memBlock.beginBlockAdr;
- WHILE blockAdr # memBlock.endBlockAdr DO
- heapBlock := SYSTEM.VAL(Heaps.HeapBlock, blockAdr + Heaps.BlockHeaderSize); (* get heap block *)
- mark := SYSTEM.GET32(blockAdr + Heaps.BlockHeaderSize); (* access to private field heapBlock.mark *)
- IF (mark = currentMarkValue) THEN
- analyzer.Add(heapBlock, FALSE);
- SYSTEM.PUT32(blockAdr + Heaps.BlockHeaderSize, currentMarkValue - 1);
- END;
- blockAdr := blockAdr + heapBlock.size
- END;
- memBlock := memBlock.next
- END;
- SetCurrentMarkValue(currentMarkValue - 1); (* restore Heaps.currentMarkValue *)
- END AnalyzeMarkedBlocks;
- PROCEDURE WriteType(adr : LONGINT; out : Streams.Writer);
- VAR m : Modules.Module; t : Modules.TypeDesc; name: ARRAY 256 OF CHAR;
- BEGIN
- Modules.ThisTypeByAdr(adr, m, t);
- IF m # NIL THEN
- out.String(m.name); out.Char(".");
- IF (t # NIL) THEN
- IF t.name = "" THEN out.String("TYPE") ELSE
- COPY(t.name,name);
- out.String(name) END
- ELSE
- out.String("NOTYPEDESC");
- END;
- ELSE
- out.String("NIL")
- END
- END WriteType;
- PROCEDURE GetName(adr: ADDRESS; VAR name: ARRAY OF CHAR);
- VAR m : Modules.Module; t : Modules.TypeDesc;
- BEGIN
- Modules.ThisTypeByAdr(adr, m, t);
- name := "";
- IF m # NIL THEN
- COPY(m.name,name);
- IF (t # NIL) THEN
- Strings.Append(name,".");
- Strings.Append(name,t.name);
- END;
- END;
- END GetName;
- (* Access to private field Heaps.currentMarkValue *)
- PROCEDURE GetCurrentMarkValue() : LONGINT;
- BEGIN
- RETURN SYSTEM.GET32(currentMarkValueAddress);
- END GetCurrentMarkValue;
- PROCEDURE SetCurrentMarkValue(value : LONGINT);
- BEGIN
- SYSTEM.PUT32(currentMarkValueAddress, value);
- END SetCurrentMarkValue;
- PROCEDURE IncrementCurrentMarkValue;
- BEGIN
- SetCurrentMarkValue(GetCurrentMarkValue() + 1);
- END IncrementCurrentMarkValue;
- PROCEDURE GetCurrentMarkValueAddress() : ADDRESS;
- VAR address : ADDRESS; module : Modules.Module; variable : Reflection.Variable;
- BEGIN
- address := Heaps.NilVal;
- module := Modules.ModuleByName("Heaps");
- ASSERT(module # NIL);
- IF (module # NIL) THEN
- IF Reflection.FindVar(module, "currentMarkValue", variable) THEN
- (*
- ASSERT(variable.n = 1); (* currentMarkValue is not an array *)
- ASSERT(variable.type = 6); (*? type is LONGINT, currently no support for 64-bit addresses *)
- *)
- address := variable.adr;
- ELSE HALT(100);
- END;
- END;
- RETURN address;
- END GetCurrentMarkValueAddress;
- PROCEDURE Terminate;
- BEGIN
- IF Heaps.allocationLogger = LogAlloc THEN Heaps.SetAllocationLogger(NIL) END;
- END Terminate;
- BEGIN
- currentMarkValueAddress := GetCurrentMarkValueAddress();
- ASSERT(currentMarkValueAddress # Heaps.NilVal);
- Modules.InstallTermHandler(Terminate);
- END Info.
- System.Free Info ~
- Debugging.DisableGC
- Debugging.EnableGC
- Compiler.Compile -p=Win32 FoxIntermediateBackend.Mod ~
- Info.AllObjects ~
- Info.AllObjects * ~
- (* view by type *)
- Info.AllObjects --sort=0 * ~ sort by none
- Info.AllObjects --sort=1 * ~ sort by count
- Info.AllObjects --sort=2 * ~ sort by size
- Info.AllObjects --sort=3 * ~ sort by total size
- Info.AllObjects --sort=4 * ~ sort by name
- (* view by allocation pc *)
- Info.AllObjects --pc --sort=0 * ~ sort by none
- Info.AllObjects --pc --sort=1 * ~ sort by count
- Info.AllObjects --pc --sort=2 * ~ sort by size
- Info.AllObjects --pc --sort=3 * ~ sort by total size
- Info.AllObjects --pc --sort=4 * ~ sort by name
- Info.TraceModule PET ~
- Info.TraceModule Info ~
- Info.TraceModule FoxIntermediateBackend * ~
- Info.TraceReference HotKeys.hotkeys ~
- Info.TraceReference HotKeys.hotkeys * ~
- Info.TraceProcessID 7180 * ~
- Info.ModuleDetails -d Modules ~
- System.CollectGarbage ~
- Compiler.Compile --symbolFilePrefix=/temp/objEO/ --objectFilePrefix=/temp/objEO/ Info.Mod ~
- Info.AddAllocatorLogger
- Info.RecentAllocators --clear --scale=2400000 ~
- Info.RecentAllocators --scale=2400000 ~
- Info.RecentAllocators --clear ~
- Info.ClearRecentAllocators ~
|