|
@@ -1,14 +1,35 @@
|
|
import format.hl.Data;
|
|
import format.hl.Data;
|
|
|
|
+using format.hl.Tools;
|
|
|
|
|
|
abstract Pointer(Int) {
|
|
abstract Pointer(Int) {
|
|
public inline function isNull() {
|
|
public inline function isNull() {
|
|
return this == 0;
|
|
return this == 0;
|
|
}
|
|
}
|
|
|
|
+ public inline function offset( i : Int ) : Pointer {
|
|
|
|
+ return cast (this + i);
|
|
|
|
+ }
|
|
|
|
+ public inline function sub( p : Pointer ) : Int {
|
|
|
|
+ return this - cast(p);
|
|
|
|
+ }
|
|
|
|
+ public inline function pageAddress() : Pointer {
|
|
|
|
+ return cast (this & ~0xFFFF);
|
|
|
|
+ }
|
|
|
|
+ public inline function toString() {
|
|
|
|
+ return "0x"+StringTools.hex(this, 8);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+@:enum abstract PageKind(Int) {
|
|
|
|
+ var PDynamic = 0;
|
|
|
|
+ var PRaw = 1;
|
|
|
|
+ var PNoPtr = 2;
|
|
|
|
+ var PFinalizer = 3;
|
|
}
|
|
}
|
|
|
|
|
|
class Page {
|
|
class Page {
|
|
|
|
+ var memory : Memory;
|
|
public var addr : Pointer;
|
|
public var addr : Pointer;
|
|
- public var kind : Int;
|
|
|
|
|
|
+ public var kind : PageKind;
|
|
public var size : Int;
|
|
public var size : Int;
|
|
public var blockSize : Int;
|
|
public var blockSize : Int;
|
|
public var firstBlock : Int;
|
|
public var firstBlock : Int;
|
|
@@ -18,13 +39,27 @@ class Page {
|
|
public var bmp : haxe.io.Bytes;
|
|
public var bmp : haxe.io.Bytes;
|
|
public var sizes : haxe.io.Bytes;
|
|
public var sizes : haxe.io.Bytes;
|
|
|
|
|
|
- public function new() {
|
|
|
|
|
|
+ public function new(m) {
|
|
|
|
+ memory = m;
|
|
}
|
|
}
|
|
|
|
|
|
public function isLiveBlock( bid : Int ) {
|
|
public function isLiveBlock( bid : Int ) {
|
|
- if( bmp == null ) return true;
|
|
|
|
- return bmp.get(bid >> 3)
|
|
|
|
|
|
+ if( sizes != null && sizes.get(bid) == 0 ) return false;
|
|
|
|
+ return (bmp.get(bid >> 3) >>> (bid & 7)) != 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public function getBlockSize( bid : Int ) {
|
|
|
|
+ return sizes == null ? 1 : sizes.get(bid);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public function goto( bid : Int ) {
|
|
|
|
+ memory.memoryDump.seek(dataPosition + bid * blockSize, SeekBegin);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ public function getPointer( bid : Int ) {
|
|
|
|
+ return addr.offset(bid * blockSize);
|
|
|
|
+ }
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
|
|
class Stack {
|
|
class Stack {
|
|
@@ -34,16 +69,37 @@ class Stack {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+class DataSeg {
|
|
|
|
+ public static inline var NO_OWNER = 0x80000000;
|
|
|
|
+ public var page : Page;
|
|
|
|
+ public var bid : Int;
|
|
|
|
+ public var ownerType : Int = NO_OWNER;
|
|
|
|
+ public function new() {
|
|
|
|
+ }
|
|
|
|
+ public inline function getPointer() {
|
|
|
|
+ return page.getPointer(bid);
|
|
|
|
+ }
|
|
|
|
+ public inline function getMemSize() {
|
|
|
|
+ return page.getBlockSize(bid) * page.blockSize;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
class Memory {
|
|
class Memory {
|
|
|
|
|
|
- var memoryDump : sys.io.FileInput;
|
|
|
|
|
|
+ public var memoryDump : sys.io.FileInput;
|
|
var code : format.hl.Data;
|
|
var code : format.hl.Data;
|
|
var is64 : Bool;
|
|
var is64 : Bool;
|
|
var pages : Array<Page>;
|
|
var pages : Array<Page>;
|
|
var roots : Array<Pointer>;
|
|
var roots : Array<Pointer>;
|
|
var stacks : Array<Stack>;
|
|
var stacks : Array<Stack>;
|
|
var types : Array<Pointer>;
|
|
var types : Array<Pointer>;
|
|
|
|
+ var closures : Array<Pointer>;
|
|
var pointerType : Map<Pointer, HLType>;
|
|
var pointerType : Map<Pointer, HLType>;
|
|
|
|
+ var pointerTypeIndex : Map<Pointer, Int>;
|
|
|
|
+ var closuresType : Map<Pointer, HLType>;
|
|
|
|
+ var pointerPage : Map<Pointer, Page>;
|
|
|
|
+ var dataPointers : Array<DataSeg>;
|
|
|
|
+ var ptrBits : Int;
|
|
|
|
|
|
function new() {
|
|
function new() {
|
|
}
|
|
}
|
|
@@ -62,13 +118,25 @@ class Memory {
|
|
return cast memoryDump.readInt32();
|
|
return cast memoryDump.readInt32();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ inline function readType() {
|
|
|
|
+ return pointerType.get(readPointer());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ static function MB( v : Float ) {
|
|
|
|
+ if( v < 1024 * 1024 )
|
|
|
|
+ return (Math.round(v * 10 / 1024) / 10)+"KB";
|
|
|
|
+ return (Math.round(v * 10 / (1024 * 1024)) / 10)+"MB";
|
|
|
|
+ }
|
|
|
|
+
|
|
function loadMemory( arg : String ) {
|
|
function loadMemory( arg : String ) {
|
|
memoryDump = sys.io.File.read(arg);
|
|
memoryDump = sys.io.File.read(arg);
|
|
if( memoryDump.read(3).toString() != "HMD" )
|
|
if( memoryDump.read(3).toString() != "HMD" )
|
|
throw "Invalid memory dump file";
|
|
throw "Invalid memory dump file";
|
|
|
|
+
|
|
var version = memoryDump.readByte() - "0".code;
|
|
var version = memoryDump.readByte() - "0".code;
|
|
var flags = readInt();
|
|
var flags = readInt();
|
|
is64 = (flags & 1) != 0;
|
|
is64 = (flags & 1) != 0;
|
|
|
|
+ ptrBits = is64 ? 3 : 2;
|
|
if( is64 ) throw "64 bit not supported";
|
|
if( is64 ) throw "64 bit not supported";
|
|
|
|
|
|
// load pages
|
|
// load pages
|
|
@@ -79,18 +147,19 @@ class Memory {
|
|
while( true ) {
|
|
while( true ) {
|
|
var addr = readPointer();
|
|
var addr = readPointer();
|
|
if( addr.isNull() ) break;
|
|
if( addr.isNull() ) break;
|
|
- var p = new Page();
|
|
|
|
|
|
+ var p = new Page(this);
|
|
p.addr = addr;
|
|
p.addr = addr;
|
|
- p.kind = readInt();
|
|
|
|
|
|
+ p.kind = cast readInt();
|
|
p.size = readInt();
|
|
p.size = readInt();
|
|
p.blockSize = readInt();
|
|
p.blockSize = readInt();
|
|
p.firstBlock = readInt();
|
|
p.firstBlock = readInt();
|
|
p.maxBlocks = readInt();
|
|
p.maxBlocks = readInt();
|
|
p.nextBlock = readInt();
|
|
p.nextBlock = readInt();
|
|
|
|
|
|
- var flags = readInt();
|
|
|
|
p.dataPosition = memoryDump.tell();
|
|
p.dataPosition = memoryDump.tell();
|
|
memoryDump.seek(p.size, SeekCur);
|
|
memoryDump.seek(p.size, SeekCur);
|
|
|
|
+
|
|
|
|
+ var flags = readInt();
|
|
if( flags & 1 != 0 )
|
|
if( flags & 1 != 0 )
|
|
p.bmp = memoryDump.read((p.maxBlocks + 7) >> 3); // 1 bit per block
|
|
p.bmp = memoryDump.read((p.maxBlocks + 7) >> 3); // 1 bit per block
|
|
if( flags & 2 != 0 )
|
|
if( flags & 2 != 0 )
|
|
@@ -99,7 +168,7 @@ class Memory {
|
|
totalSize += p.size;
|
|
totalSize += p.size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- log(pages.length + " pages, " + (totalSize >> 10) + "KB memory");
|
|
|
|
|
|
+ log(pages.length + " pages, " + MB(totalSize) + " memory");
|
|
|
|
|
|
// load roots
|
|
// load roots
|
|
roots = [for( i in 0...readInt() ) readPointer()];
|
|
roots = [for( i in 0...readInt() ) readPointer()];
|
|
@@ -117,7 +186,8 @@ class Memory {
|
|
|
|
|
|
// load types
|
|
// load types
|
|
types = [for( i in 0...readInt() ) readPointer()];
|
|
types = [for( i in 0...readInt() ) readPointer()];
|
|
- log(types.length + " types");
|
|
|
|
|
|
+ closures = [for( i in 0...readInt() ) readPointer()];
|
|
|
|
+ log(types.length + " types, " + closures.length + " closures");
|
|
}
|
|
}
|
|
|
|
|
|
function check() {
|
|
function check() {
|
|
@@ -126,20 +196,199 @@ class Memory {
|
|
if( code.types.length != this.types.length ) throw "Types count mismatch";
|
|
if( code.types.length != this.types.length ) throw "Types count mismatch";
|
|
|
|
|
|
pointerType = new Map();
|
|
pointerType = new Map();
|
|
- for( i in 0...types.length )
|
|
|
|
|
|
+ pointerTypeIndex = new Map();
|
|
|
|
+ closuresType = new Map();
|
|
|
|
+ var cid = 0;
|
|
|
|
+ for( i in 0...types.length ) {
|
|
pointerType.set(types[i], code.types[i]);
|
|
pointerType.set(types[i], code.types[i]);
|
|
|
|
+ pointerTypeIndex.set(types[i], i);
|
|
|
|
+ switch( code.types[i] ) {
|
|
|
|
+ case HFun(f):
|
|
|
|
+ var ptr = closures[cid++];
|
|
|
|
+ var args = f.args.copy();
|
|
|
|
+ closuresType.set(ptr, args.shift());
|
|
|
|
+ pointerType.set(ptr, HFun({ args : args, ret : f.ret }));
|
|
|
|
+ pointerTypeIndex.set(ptr, -i);
|
|
|
|
+ default:
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ var count = 0, used = 0, free = 0;
|
|
for( p in pages ) {
|
|
for( p in pages ) {
|
|
var bid = p.firstBlock;
|
|
var bid = p.firstBlock;
|
|
while( bid < p.maxBlocks ) {
|
|
while( bid < p.maxBlocks ) {
|
|
- if( !p.isBlockLive(bid) ) {
|
|
|
|
|
|
+ if( !p.isLiveBlock(bid) ) {
|
|
bid++;
|
|
bid++;
|
|
|
|
+ free += p.blockSize;
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
+ var sz = p.getBlockSize(bid);
|
|
|
|
+ bid += sz;
|
|
count++;
|
|
count++;
|
|
|
|
+ used += sz * p.blockSize;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ log(count + " live blocks " + MB(used) + " used, " + MB(free) + " free");
|
|
|
|
+
|
|
|
|
+ pointerPage = new Map();
|
|
|
|
+ for( p in pages ) {
|
|
|
|
+ pointerPage.set(p.addr, p);
|
|
|
|
+ for( i in 0...p.size >> 16 )
|
|
|
|
+ pointerPage.set(p.addr.offset(i << 16), p);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ inline function iterLiveBlocks( callb, withPtr = false ) {
|
|
|
|
+ for( p in pages ) {
|
|
|
|
+ if( withPtr && p.kind == PNoPtr ) continue;
|
|
|
|
+ var bid = p.firstBlock;
|
|
|
|
+ while( bid < p.maxBlocks ) {
|
|
|
|
+ if( !p.isLiveBlock(bid) ) {
|
|
|
|
+ bid++;
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ var sz = p.getBlockSize(bid);
|
|
|
|
+ callb(p, bid, sz);
|
|
|
|
+ bid += sz;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- log(count + " live blocks");
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function getStats(dataTID=0,doPrint=true) {
|
|
|
|
+
|
|
|
|
+ var byT = new Map();
|
|
|
|
+ var allT = [];
|
|
|
|
+
|
|
|
|
+ dataPointers = [];
|
|
|
|
+
|
|
|
|
+ iterLiveBlocks(function(p,bid,size) {
|
|
|
|
+ p.goto(bid);
|
|
|
|
+
|
|
|
|
+ var tid : Int = pointerTypeIndex.get(readPointer());
|
|
|
|
+ if( tid == dataTID ) {
|
|
|
|
+ var s = new DataSeg();
|
|
|
|
+ s.bid = bid;
|
|
|
|
+ s.page = p;
|
|
|
|
+ dataPointers.push(s);
|
|
|
|
+ }
|
|
|
|
+ var inf = byT.get(tid);
|
|
|
|
+ if( inf == null ) {
|
|
|
|
+ inf = { t : code.types[tid < 0 ? -tid : tid], count : 0, mem : 0 };
|
|
|
|
+ byT.set(tid, inf);
|
|
|
|
+ allT.push(inf);
|
|
|
|
+ }
|
|
|
|
+ inf.count++;
|
|
|
|
+ inf.mem += size * p.blockSize;
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ allT.sort(function(i1, i2) return i1.mem - i2.mem);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ if( doPrint )
|
|
|
|
+ for( t in allT )
|
|
|
|
+ log('${t.count} count ${MB(t.mem)} ${t.t.toString()}');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function locate( type : String ) {
|
|
|
|
+ if( type == null ) {
|
|
|
|
+ log("Require <type> parameter");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ for( i in 0...code.types.length )
|
|
|
|
+ if( code.types[i].toString() == type ) {
|
|
|
|
+ getStats(i, false);
|
|
|
|
+ log(dataPointers.length + " values found");
|
|
|
|
+ lookupData();
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ log("Unknown type");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function printRoots() {
|
|
|
|
+ var rid = 0;
|
|
|
|
+ for( g in code.globals ) {
|
|
|
|
+ if( !g.isPtr() ) continue;
|
|
|
|
+ var r = roots[rid++];
|
|
|
|
+ var ppage = r.pageAddress();
|
|
|
|
+ var p = pointerPage.get(ppage);
|
|
|
|
+ var bid = Std.int(r.sub(p.addr) / p.blockSize);
|
|
|
|
+ p.goto(bid);
|
|
|
|
+ var t = readPointer();
|
|
|
|
+ Sys.println(r.toString()+" "+pointerType.get(t).toString()+" "+g.toString());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function gotoAddress( val : Pointer ) {
|
|
|
|
+ var ppage = val.pageAddress();
|
|
|
|
+ var p = pointerPage.get(ppage);
|
|
|
|
+ if( p == null )
|
|
|
|
+ return false;
|
|
|
|
+ var bid = Std.int(val.sub(p.addr) / p.blockSize);
|
|
|
|
+ if( !p.isLiveBlock(bid) )
|
|
|
|
+ return false;
|
|
|
|
+ p.goto(bid);
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function lookupData() {
|
|
|
|
+ if( dataPointers == null ) {
|
|
|
|
+ Sys.println("No data pointers found, use <stats> first");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ var data = new Map();
|
|
|
|
+ for( d in dataPointers )
|
|
|
|
+ data.set(d.getPointer(), d);
|
|
|
|
+
|
|
|
|
+ var byT = [];
|
|
|
|
+
|
|
|
|
+ iterLiveBlocks(function(p, bid, sz) {
|
|
|
|
+ p.goto(bid);
|
|
|
|
+ var type = 0;
|
|
|
|
+ for( i in 0...Std.int((sz * p.blockSize) >> ptrBits) ) {
|
|
|
|
+ var p = readPointer();
|
|
|
|
+ if( i == 0 ) {
|
|
|
|
+ type = pointerTypeIndex.get(p);
|
|
|
|
+ if( type < 0 ) type = -type;
|
|
|
|
+ }
|
|
|
|
+ var d = data.get(p);
|
|
|
|
+ if( d != null && d.ownerType == DataSeg.NO_OWNER ) {
|
|
|
|
+ d.ownerType = type;
|
|
|
|
+ var inf = byT[type];
|
|
|
|
+ if( inf == null ) {
|
|
|
|
+ inf = { t : code.types[type], mem : 0, count : 0 };
|
|
|
|
+ byT[type] = inf;
|
|
|
|
+ }
|
|
|
|
+ inf.count++;
|
|
|
|
+ inf.mem += d.getMemSize();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }, true);
|
|
|
|
+
|
|
|
|
+ var allT = [for( t in byT ) if( t != null ) t];
|
|
|
|
+ allT.sort(function(t1, t2) return t1.mem - t2.mem);
|
|
|
|
+ for( t in allT )
|
|
|
|
+ Sys.println(t.count + " count " + MB(t.mem) + " " + t.t.toString());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function printPartition() {
|
|
|
|
+ var part = sys.io.File.write("part.txt",false);
|
|
|
|
+ for( p in pages ) {
|
|
|
|
+ var bid = p.firstBlock;
|
|
|
|
+ part.writeString("[" + p.blockSize+"]");
|
|
|
|
+ while( bid < p.maxBlocks ) {
|
|
|
|
+ if( !p.isLiveBlock(bid) ) {
|
|
|
|
+ part.writeByte(".".code);
|
|
|
|
+ bid++;
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ var sz = p.getBlockSize(bid);
|
|
|
|
+ for( i in 0...sz ) part.writeByte("#".code);
|
|
|
|
+ bid += sz;
|
|
|
|
+ }
|
|
|
|
+ part.writeByte("\n".code);
|
|
|
|
+ }
|
|
|
|
+ part.close();
|
|
|
|
+ log("Partition saved in part.txt");
|
|
}
|
|
}
|
|
|
|
|
|
function log(msg:String) {
|
|
function log(msg:String) {
|
|
@@ -149,6 +398,8 @@ class Memory {
|
|
static function main() {
|
|
static function main() {
|
|
var m = new Memory();
|
|
var m = new Memory();
|
|
|
|
|
|
|
|
+ //hl.Gc.dumpMemory(); Sys.command("cp memory.hl memory_test.hl");
|
|
|
|
+
|
|
var args = Sys.args();
|
|
var args = Sys.args();
|
|
while( args.length > 0 ) {
|
|
while( args.length > 0 ) {
|
|
var arg = args.shift();
|
|
var arg = args.shift();
|
|
@@ -159,6 +410,29 @@ class Memory {
|
|
m.loadMemory(arg);
|
|
m.loadMemory(arg);
|
|
}
|
|
}
|
|
m.check();
|
|
m.check();
|
|
|
|
+
|
|
|
|
+ var stdin = Sys.stdin();
|
|
|
|
+ while( true ) {
|
|
|
|
+ Sys.print("> ");
|
|
|
|
+ var args = ~/ +/g.split(StringTools.trim(stdin.readLine()));
|
|
|
|
+ var cmd = args.shift();
|
|
|
|
+ switch( cmd ) {
|
|
|
|
+ case "exit", "quit", "q":
|
|
|
|
+ break;
|
|
|
|
+ case "stats":
|
|
|
|
+ m.getStats();
|
|
|
|
+ case "roots":
|
|
|
|
+ m.printRoots();
|
|
|
|
+ case "data":
|
|
|
|
+ m.lookupData();
|
|
|
|
+ case "locate":
|
|
|
|
+ m.locate(args.shift());
|
|
|
|
+ case "part":
|
|
|
|
+ m.printPartition();
|
|
|
|
+ default:
|
|
|
|
+ Sys.println("Unknown command " + cmd);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|