2
0

Memory.hx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  1. import format.hl.Data;
  2. using format.hl.Tools;
  3. import Block;
  4. class Stats {
  5. var mem : Memory;
  6. var byT = new Map();
  7. var allT = [];
  8. public function new(mem) {
  9. this.mem = mem;
  10. }
  11. public function add( t : TType, mem : Int ) {
  12. return addPath([t == null ? 0 : t.tid], mem);
  13. }
  14. public inline function makeID( t : TType, field : Int ) {
  15. return t.tid | (field << 24);
  16. }
  17. public function addPath( tl : Array<Int>, mem : Int ) {
  18. var key = tl.join(" ");
  19. var inf = byT.get(key);
  20. if( inf == null ) {
  21. inf = { tl : tl, count : 0, mem : 0 };
  22. byT.set(key, inf);
  23. allT.push(inf);
  24. }
  25. inf.count++;
  26. inf.mem += mem;
  27. }
  28. public function print( withSum = false ) {
  29. if( @:privateAccess mem.sortByCount )
  30. allT.sort(function(i1, i2) return i1.count - i2.count);
  31. else
  32. allT.sort(function(i1, i2) return i1.mem - i2.mem);
  33. var totCount = 0;
  34. var totMem = 0;
  35. for( i in allT ) {
  36. totCount += i.count;
  37. totMem += i.mem;
  38. var tpath = [];
  39. for( tid in i.tl ) {
  40. var t = mem.types[tid & 0xFFFFFF];
  41. var tstr = t.toString();
  42. var fid = tid >>> 24;
  43. if( fid > 0 ) {
  44. var f = t.memFieldsNames[fid-1];
  45. if( f != null ) tstr += "." + f;
  46. }
  47. tpath.push(tstr);
  48. }
  49. mem.log(i.count + " count, " + Memory.MB(i.mem) + " " + tpath.join(" > "));
  50. }
  51. if( withSum )
  52. mem.log("Total: "+totCount+" count, "+Memory.MB(totMem));
  53. }
  54. }
  55. class Memory {
  56. public var memoryDump : sys.io.FileInput;
  57. public var is64 : Bool;
  58. public var bool32 : Bool;
  59. public var ptrBits : Int;
  60. public var types : Array<TType>;
  61. var privateData : Int;
  62. var markData : Int;
  63. var sortByCount : Bool;
  64. var displayFields : Bool = true;
  65. var code : format.hl.Data;
  66. var pages : Array<Page>;
  67. var roots : Array<Pointer>;
  68. var stacks : Array<Stack>;
  69. var typesPointers : Array<Pointer>;
  70. var closuresPointers : Array<Pointer>;
  71. var blocks : Array<Block>;
  72. var baseTypes : Array<{ t : HLType, p : Pointer }>;
  73. var all : Block;
  74. var toProcess : Array<Block>;
  75. var tdynObj : TType;
  76. var tdynObjData : TType;
  77. var pointerBlock : PointerMap<Block>;
  78. var pointerType : PointerMap<TType>;
  79. var falseCandidates : Array<{ b : Block, f : Block, idx : Int }>;
  80. var currentTypeIndex = 0;
  81. var resolveCache : Map<String,TType> = new Map();
  82. function new() {
  83. }
  84. public function typeSize( t : HLType ) {
  85. return switch( t ) {
  86. case HVoid: 0;
  87. case HUi8: 1;
  88. case HUi16: 2;
  89. case HI32, HF32: 4;
  90. case HF64: 8;
  91. case HBool:
  92. return bool32 ? 4 : 1;
  93. default:
  94. return is64 ? 8 : 4;
  95. }
  96. }
  97. public function getType( t : HLType, isNull = false ) : TType {
  98. // this is quite slow, but we can't use a Map, maybe try a more per-type specific approach ?
  99. for( t2 in types )
  100. if( t == t2.t )
  101. return t2;
  102. if( isNull )
  103. return null;
  104. throw "Type not found " + t.toString();
  105. return null;
  106. }
  107. function loadBytecode( arg : String ) {
  108. if( code != null ) throw "Duplicate code";
  109. code = new format.hl.Reader(false).read(new haxe.io.BytesInput(sys.io.File.getBytes(arg)));
  110. log(arg + " code loaded");
  111. }
  112. inline function readInt() {
  113. return memoryDump.readInt32();
  114. }
  115. inline function readPointer() : Pointer {
  116. var low = memoryDump.readInt32();
  117. var high = is64 ? memoryDump.readInt32() : 0;
  118. return cast haxe.Int64.make(high,low);
  119. }
  120. public static function MB( v : Float ) {
  121. if( v < 1000 )
  122. return Std.int(v) + "B";
  123. if( v < 1024 * 1000 )
  124. return (Math.round(v * 10 / 1024) / 10)+"KB";
  125. return (Math.round(v * 10 / (1024 * 1024)) / 10)+"MB";
  126. }
  127. function loadMemory( arg : String ) {
  128. memoryDump = sys.io.File.read(arg);
  129. if( memoryDump.read(3).toString() != "HMD" )
  130. throw "Invalid memory dump file";
  131. var version = memoryDump.readByte() - "0".code;
  132. if( version != 1 )
  133. throw "Unsupported format version "+version;
  134. var flags = readInt();
  135. is64 = (flags & 1) != 0;
  136. bool32 = (flags & 2) != 0;
  137. ptrBits = is64 ? 3 : 2;
  138. var ptrSize = 1 << ptrBits;
  139. privateData = readInt();
  140. markData = readInt();
  141. // load pages
  142. var count = readInt();
  143. pages = [];
  144. blocks = [];
  145. for( i in 0...count ) {
  146. var addr = readPointer();
  147. var p = new Page();
  148. p.addr = addr;
  149. p.kind = cast readInt();
  150. p.size = readInt();
  151. p.reserved = readInt();
  152. var readPtr = !p.memHasPtr();
  153. while( true ) {
  154. var ptr = readPointer();
  155. if( ptr.isNull() ) break;
  156. var size = readInt();
  157. var b = new Block();
  158. b.page = p;
  159. b.addr = ptr;
  160. b.size = size;
  161. if( readPtr && size >= ptrSize ) b.typePtr = readPointer();
  162. blocks.push(b);
  163. }
  164. if( p.memHasPtr() ) {
  165. p.dataPosition = memoryDump.tell();
  166. memoryDump.seek(p.size, SeekCur);
  167. }
  168. pages.push(p);
  169. }
  170. // load roots
  171. roots = [for( i in 0...readInt() ) readPointer()];
  172. // load stacks
  173. stacks = [];
  174. for( i in 0...readInt() ) {
  175. var s = new Stack();
  176. s.base = readPointer();
  177. s.contents = [for( i in 0...readInt() ) readPointer()];
  178. stacks.push(s);
  179. }
  180. // load types
  181. baseTypes = [];
  182. while( true ) {
  183. var tid = readInt();
  184. if( tid < 0 ) break;
  185. var ptr = readPointer();
  186. baseTypes.push({ t : Type.createEnumIndex(HLType, tid), p : ptr });
  187. }
  188. typesPointers = [for( i in 0...readInt() ) readPointer()];
  189. closuresPointers = [for( i in 0...readInt() ) readPointer()];
  190. }
  191. function printStats() {
  192. var pagesSize = 0, reserved = 0;
  193. var used = 0, gc = 0;
  194. for( p in pages ) {
  195. pagesSize += p.size;
  196. reserved += p.reserved;
  197. }
  198. for( b in blocks )
  199. used += b.size;
  200. log(pages.length + " pages, " + MB(pagesSize) + " memory");
  201. log(roots.length + " roots, "+ stacks.length + " stacks");
  202. log(code.types.length + " types, " + closuresPointers.length + " closures");
  203. log(blocks.length + " live blocks " + MB(used) + " used, " + MB(pagesSize - used - reserved) + " free, "+MB(privateData + markData)+" gc");
  204. }
  205. function getTypeNull( t : TType ) {
  206. if( t.nullWrap != null )
  207. return t.nullWrap;
  208. for( t2 in types )
  209. switch( t2.t ) {
  210. case HNull(base) if( base == t.t ):
  211. t.nullWrap = t2;
  212. return t2;
  213. default:
  214. }
  215. var r = new TType(types.length, HNull(t.t));
  216. t.nullWrap = r;
  217. types.push(r);
  218. return r;
  219. }
  220. function goto( b : Block ) {
  221. var p = b.page.dataPosition;
  222. if( p < 0 ) throw "assert";
  223. memoryDump.seek(p + b.addr.sub(b.page.addr), SeekBegin);
  224. }
  225. function check() {
  226. if( code == null ) throw "Missing .hl file";
  227. if( memoryDump == null ) throw "Missing .dump file";
  228. if( code.types.length != this.typesPointers.length ) throw "Types count mismatch";
  229. pointerType = new PointerMap();
  230. var cid = 0;
  231. types = [for( i in 0...code.types.length ) new TType(i, code.types[i])];
  232. for( i in 0...typesPointers.length ) {
  233. pointerType.set(typesPointers[i], types[i]);
  234. switch( code.types[i] ) {
  235. case HFun(f):
  236. var tid = types.length;
  237. var args = f.args.copy();
  238. var clparam = args.shift();
  239. if( clparam == null ) {
  240. cid++;
  241. continue;
  242. }
  243. switch( clparam ) {
  244. case HEnum(p) if( p.name == "" ):
  245. p.name = '<closure$i context>';
  246. default:
  247. }
  248. var ct = new TType(tid, HFun({ args : args, ret : f.ret }), clparam);
  249. types.push(ct);
  250. var pt = closuresPointers[cid++];
  251. if( pt != null )
  252. pointerType.set(pt, ct);
  253. case HObj(o):
  254. if( o.tsuper != null )
  255. for( j in 0...types.length )
  256. if( types[j].t == o.tsuper ) {
  257. types[i].parentClass = types[j];
  258. break;
  259. }
  260. default:
  261. }
  262. }
  263. for( b in baseTypes ) {
  264. var t = getType(b.t, true);
  265. if( t == null ) {
  266. t = new TType(types.length, b.t);
  267. types.push(t);
  268. }
  269. pointerType.set(b.p, t);
  270. }
  271. var progress = 0;
  272. pointerBlock = new PointerMap();
  273. for( b in blocks ) {
  274. progress++;
  275. if( progress % 1000 == 0 )
  276. Sys.print((Std.int(progress * 1000 / blocks.length) / 10) + "% \r");
  277. if( b.page.memHasPtr() ) {
  278. goto(b);
  279. b.typePtr = readPointer();
  280. }
  281. b.type = pointerType.get(b.typePtr);
  282. b.typePtr = null;
  283. if( b.type != null && b.type.hasPtr && b.page.kind == PNoPtr ) {
  284. if( b.type.t.match(HEnum(_)) ) {
  285. // most likely one of the constructor without pointer parameter
  286. } else
  287. b.type = null; // false positive
  288. }
  289. if( b.type != null && !b.type.isDyn )
  290. b.type = getTypeNull(b.type);
  291. if( b.type != null )
  292. b.typeKind = KHeader;
  293. pointerBlock.set(b.addr, b);
  294. }
  295. printStats();
  296. // look in roots (higher ownership priority)
  297. all = new Block();
  298. var broot = new Block();
  299. broot.type = new TType(types.length, HAbstract("roots"));
  300. types.push(broot.type);
  301. broot.depth = 0;
  302. broot.addParent(all);
  303. var rid = 0;
  304. for( g in code.globals ) {
  305. if( !g.isPtr() ) continue;
  306. var r = roots[rid++];
  307. var b = pointerBlock.get(r);
  308. if( b == null ) continue;
  309. b.addParent(broot);
  310. if( b.type == null ) {
  311. b.type = getType(g);
  312. b.typeKind = KRoot;
  313. }
  314. }
  315. var tvoid = getType(HVoid);
  316. for( t in types )
  317. t.buildTypes(this, tvoid);
  318. tdynObj = getType(HDynObj);
  319. tdynObjData = new TType(types.length, HAbstract("dynobjdata"));
  320. types.push(tdynObjData);
  321. toProcess = blocks.copy();
  322. falseCandidates = [];
  323. while( toProcess.length > 0 )
  324. buildHierarchy();
  325. // look in stacks (low priority of ownership)
  326. var tstacks = new TType(types.length, HAbstract("stack"));
  327. var bstacks = [];
  328. types.push(tstacks);
  329. for( s in stacks ) {
  330. var bstack = new Block();
  331. bstack.depth = 10000;
  332. bstack.type = tstacks;
  333. bstack.addParent(all);
  334. bstacks.push(bstack);
  335. for( r in s.contents ) {
  336. var b = pointerBlock.get(r);
  337. if( b != null )
  338. b.addParent(bstack);
  339. }
  340. }
  341. for( f in falseCandidates )
  342. if( f.f.owner == null ) {
  343. f.f.addParent(f.b);
  344. f.b.type.falsePositive++;
  345. f.b.type.falsePositiveIndexes[f.idx]++;
  346. }
  347. // precompute Arrays (no NativeArray intermediate)
  348. function shortCircuit( native, haxe ) {
  349. var tnat = resolveType(native, false);
  350. var tarr = resolveType(haxe, false);
  351. if( tnat == null || tarr == null ) return;
  352. for( b in blocks ) {
  353. if( b.type == tnat && b.owner != null && b.owner.type == tarr && b.subs != null ) {
  354. for( s in b.subs )
  355. s.b.addParent(b.owner);
  356. }
  357. }
  358. }
  359. shortCircuit("hl.NativeArray","Array<T>");
  360. // disable for now, this generates unknowns and "Void" links
  361. //shortCircuit("hl_bytes_map","Map<String,Dynamic>");
  362. //shortCircuit("hl_int_map","Map<Int,Dynamic>");
  363. //shortCircuit("hl_obj_map","Map<{},Dynamic>");
  364. // assign depths
  365. Sys.println("Computing depths...");
  366. broot.markDepth();
  367. for( b in bstacks ) b.markDepth();
  368. var changed = -1;
  369. while( changed != 0 ) {
  370. changed = 0;
  371. for( b in blocks ) {
  372. var minD = -1;
  373. if( b.parents == null ) {
  374. if( b.owner != null && b.owner.depth >= 0 )
  375. minD = b.owner.depth;
  376. } else {
  377. for( p in b.parents )
  378. if( p.depth >= 0 && (minD < 0 || p.depth < minD) )
  379. minD = p.depth;
  380. }
  381. if( minD >= 0 ) {
  382. minD++;
  383. if( b.depth < 0 || b.depth > minD ) {
  384. b.depth = minD;
  385. changed++;
  386. }
  387. }
  388. }
  389. }
  390. for( b in blocks )
  391. b.finalize();
  392. var unk = 0, unkMem = 0, unRef = 0;
  393. for( b in blocks ) {
  394. if( b.owner == null ) {
  395. unRef++;
  396. if( unRef < 100 )
  397. log(" "+b.addr.toString() + " is not referenced");
  398. continue;
  399. }
  400. if( b.type != null ) continue;
  401. var o = b.owner;
  402. while( o != null && o.type == null )
  403. o = o.owner;
  404. if( o != null )
  405. switch( o.type.t ) {
  406. case HAbstract(_):
  407. b.type = o.type; // data inside this
  408. b.typeKind = KAbstractData;
  409. continue;
  410. default:
  411. }
  412. unk++;
  413. unkMem += b.size;
  414. }
  415. var falseCount = 0;
  416. for( t in types )
  417. falseCount += t.falsePositive;
  418. log("Hierarchy built, "+unk+" unknown ("+MB(unkMem)+"), "+falseCount+" false positives, "+unRef+" unreferenced");
  419. }
  420. function printFalsePositives( ?typeStr : String ) {
  421. var falses = [for( t in types ) if( t.falsePositive > 0 && (typeStr == null || t.toString().indexOf(typeStr) >= 0) ) t];
  422. falses.sort(function(t1, t2) return t1.falsePositive - t2.falsePositive);
  423. for( f in falses )
  424. log(f.falsePositive+" count " + f + " "+f.falsePositiveIndexes+"\n "+[for( f in f.memFields ) f.t.toString()]);
  425. }
  426. function printUnknown() {
  427. var byT = new Map();
  428. for( b in blocks ) {
  429. if( b.type != null ) continue;
  430. var o = b;
  431. while( o != null && o.type == null )
  432. o = o.owner;
  433. var t = o == null ? null : o.type;
  434. var tid = t == null ? -1 : t.tid;
  435. var inf = byT.get(tid);
  436. if( inf == null ) {
  437. inf = { t : t, count : 0, mem : 0 };
  438. byT.set(tid, inf);
  439. }
  440. inf.count++;
  441. inf.mem += b.size;
  442. }
  443. var all = [for( k in byT ) k];
  444. all.sort(function(i1, i2) return i1.count - i2.count);
  445. for( a in all )
  446. log("Unknown "+a.count + " count, " + MB(a.mem)+" "+(a.t == null ? "" : a.t.toString()));
  447. }
  448. function buildHierarchy() {
  449. var progress = 0;
  450. var blocks = toProcess;
  451. toProcess = [];
  452. for( b in blocks )
  453. b.removeChildren();
  454. for( b in blocks ) {
  455. progress++;
  456. if( progress % 10000 == 0 )
  457. Sys.print((Std.int(progress * 1000.0 / blocks.length) / 10) + "% \r");
  458. if( !b.page.memHasPtr() )
  459. continue;
  460. if( b.type != null && !b.type.hasPtr )
  461. switch(b.type.t) {
  462. case HFun(_):
  463. default:
  464. log(" Scanning "+b.type+" "+b.addr.toString());
  465. }
  466. goto(b);
  467. var fields = null;
  468. var start = 0;
  469. var ptrTags = null;
  470. var hasFieldNames = false;
  471. if( b.type != null ) {
  472. hasFieldNames = b.type.memFieldsNames != null;
  473. fields = b.type.memFields;
  474. ptrTags = b.type.ptrTags;
  475. // enum
  476. if( b.type.constructs != null ) {
  477. start++;
  478. fields = b.type.constructs[readInt()];
  479. if( is64 ) readInt(); // skip, not a pointer anyway
  480. }
  481. }
  482. for( i in start...(b.size >> ptrBits) ) {
  483. var r = readPointer();
  484. //if( ptrTags != null && ((ptrTags.get(i >> 5) >>> (i & 31)) & 1) == 0 ) continue;
  485. var bs = pointerBlock.get(r);
  486. if( bs == null ) continue;
  487. var ft = fields != null ? fields[i] : null;
  488. if( b.type == tdynObj && (i == 1 || i == 2 || i == 3) ) {
  489. if( bs.typeKind != KHeader && (bs.typeKind != null || bs.type != null) )
  490. trace(bs.typeKind, bs.type);
  491. else {
  492. bs.type = tdynObjData;
  493. bs.typeKind = KDynObjData;
  494. }
  495. }
  496. if( ft != null && !ft.t.isPtr() ) {
  497. falseCandidates.push({ b : b, f:bs, idx : i });
  498. continue;
  499. }
  500. bs.addParent(b,hasFieldNames ? (i+1) : 0);
  501. if( bs.type == null && ft != null ) {
  502. if( ft.t.isDynamic() ) {
  503. trace(b.typeKind, b.addr.toString(), b.type.toString(), ft.toString());
  504. continue;
  505. }
  506. bs.type = ft;
  507. bs.typeKind = KInferred(b.type, b.typeKind);
  508. if( bs.subs != null )
  509. toProcess.push(bs);
  510. }
  511. }
  512. }
  513. }
  514. function printByType() {
  515. var ctx = new Stats(this);
  516. for( b in blocks )
  517. ctx.add(b.type, b.size);
  518. ctx.print();
  519. }
  520. function resolveType( str, showError = true ) {
  521. var t = resolveCache.get(str);
  522. if( t != null )
  523. return t;
  524. for( i in currentTypeIndex...types.length ) {
  525. var t = types[i];
  526. var tstr = t.toString();
  527. if (tstr != null) {
  528. resolveCache.set(tstr, t);
  529. currentTypeIndex = i + 1;
  530. if( tstr == str )
  531. return t;
  532. }
  533. }
  534. if( showError )
  535. log("Type not found '"+str+"'");
  536. return null;
  537. }
  538. function locate( tstr : String, up = 0 ) {
  539. var lt = resolveType(tstr);
  540. if( lt == null ) return;
  541. var ctx = new Stats(this);
  542. for( b in blocks )
  543. if( b.type != null && b.type.match(lt) ) {
  544. var tl = [];
  545. var owner = b.owner;
  546. if( owner != null ) {
  547. var ol = [owner];
  548. tl.push(owner.type == null ? 0 : owner.type.tid);
  549. var k : Int = up;
  550. while( owner.owner != null && k-- > 0 && ol.indexOf(owner.owner) < 0 && owner.owner != all ) {
  551. var prev = owner;
  552. owner = owner.owner;
  553. ol.push(owner);
  554. tl.unshift(owner.makeTID(prev,displayFields));
  555. }
  556. }
  557. ctx.addPath(tl, b.size);
  558. }
  559. ctx.print();
  560. }
  561. function count( tstr : String, excludes : Array<String> ) {
  562. var t = resolveType(tstr);
  563. if( t == null ) return;
  564. var texclude = [];
  565. for( e in excludes ) {
  566. var t = resolveType(e);
  567. if( t == null ) return;
  568. texclude.push(t);
  569. }
  570. var ctx = new Stats(this);
  571. Block.MARK_UID++;
  572. var mark = [];
  573. for( b in blocks )
  574. if( b.type == t )
  575. visitRec(b,ctx,[],mark);
  576. while( mark.length > 0 ) {
  577. var b = mark.pop();
  578. for( s in b.subs )
  579. visitRec(s.b,ctx,texclude,mark);
  580. }
  581. ctx.print(true);
  582. }
  583. function visitRec( b : Block, ctx : Stats, exclude : Array<TType>, mark : Array<Block> ) {
  584. if( b.mark == Block.MARK_UID ) return;
  585. b.mark = Block.MARK_UID;
  586. if( b.type != null ) for( t in exclude ) if( b.type.match(t) ) return;
  587. ctx.addPath(b.type == null ? [] : [b.type.tid],b.size);
  588. if( b.subs != null )
  589. mark.push(b);
  590. }
  591. function parents( tstr : String, up = 0 ) {
  592. var lt = null;
  593. for( t in types )
  594. if( t.t.toString() == tstr ) {
  595. lt = t;
  596. break;
  597. }
  598. if( lt == null ) {
  599. log("Type not found");
  600. return;
  601. }
  602. var ctx = new Stats(this);
  603. for( b in blocks )
  604. if( b.type == lt )
  605. for( b in b.getParents() )
  606. ctx.addPath([if( b.type == null ) 0 else b.type.tid], 0);
  607. ctx.print();
  608. }
  609. function subs( tstr : String, down = 0 ) {
  610. var lt = null;
  611. for( t in types )
  612. if( t.t.toString() == tstr ) {
  613. lt = t;
  614. break;
  615. }
  616. if( lt == null ) {
  617. log("Type not found");
  618. return;
  619. }
  620. var ctx = new Stats(this);
  621. var mark = new Map();
  622. for( b in blocks )
  623. if( b.type == lt ) {
  624. function addRec(tl:Array<Int>,b:Block, k:Int) {
  625. if( k < 0 ) return;
  626. if( mark.exists(b) )
  627. return;
  628. mark.set(b, true);
  629. tl.push(b.type == null ? 0 : b.type.tid);
  630. ctx.addPath(tl, b.size);
  631. if( b.subs != null ) {
  632. k--;
  633. for( s in b.subs )
  634. addRec(tl.copy(),s.b, k);
  635. }
  636. }
  637. addRec([], b, down);
  638. }
  639. ctx.print();
  640. }
  641. public function log(msg:String) {
  642. Sys.println(msg);
  643. }
  644. static function main() {
  645. var m = new Memory();
  646. //hl.Gc.dumpMemory(); Sys.command("cp memory.hl test.hl");
  647. var code = null, memory = null;
  648. var args = Sys.args();
  649. while( args.length > 0 ) {
  650. var arg = args.shift();
  651. if( StringTools.endsWith(arg, ".hl") ) {
  652. code = arg;
  653. m.loadBytecode(arg);
  654. continue;
  655. }
  656. memory = arg;
  657. m.loadMemory(arg);
  658. }
  659. if( code != null && memory == null ) {
  660. memory = new haxe.io.Path(code).dir+"/hlmemory.dump";
  661. if( sys.FileSystem.exists(memory) ) m.loadMemory(memory);
  662. }
  663. m.check();
  664. var stdin = Sys.stdin();
  665. while( true ) {
  666. Sys.print("> ");
  667. var args = ~/ +/g.split(StringTools.trim(stdin.readLine()));
  668. var cmd = args.shift();
  669. switch( cmd ) {
  670. case "exit", "quit", "q":
  671. break;
  672. case "types":
  673. m.printByType();
  674. case "stats":
  675. m.printStats();
  676. case "false":
  677. m.printFalsePositives(args.shift());
  678. case "unknown":
  679. m.printUnknown();
  680. case "locate":
  681. m.locate(args.shift(), Std.parseInt(args.shift()));
  682. case "count":
  683. m.count(args.shift(), args);
  684. case "parents":
  685. m.parents(args.shift());
  686. case "subs":
  687. m.subs(args.shift(), Std.parseInt(args.shift()));
  688. case "sort":
  689. switch( args.shift() ) {
  690. case "mem":
  691. m.sortByCount = false;
  692. case "count":
  693. m.sortByCount = true;
  694. case mode:
  695. Sys.println("Unknown sort mode " + mode);
  696. }
  697. case "fields":
  698. switch( args.shift() ) {
  699. case "true":
  700. m.displayFields = true;
  701. case "false":
  702. m.displayFields = false;
  703. case mode:
  704. Sys.println("Unknown fields mode " + mode);
  705. }
  706. default:
  707. Sys.println("Unknown command " + cmd);
  708. }
  709. }
  710. }
  711. }