tickprocessor.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952
  1. // Copyright 2012 the V8 project authors. All rights reserved.
  2. // Redistribution and use in source and binary forms, with or without
  3. // modification, are permitted provided that the following conditions are
  4. // met:
  5. //
  6. // * Redistributions of source code must retain the above copyright
  7. // notice, this list of conditions and the following disclaimer.
  8. // * Redistributions in binary form must reproduce the above
  9. // copyright notice, this list of conditions and the following
  10. // disclaimer in the documentation and/or other materials provided
  11. // with the distribution.
  12. // * Neither the name of Google Inc. nor the names of its
  13. // contributors may be used to endorse or promote products derived
  14. // from this software without specific prior written permission.
  15. //
  16. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  17. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  18. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  19. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  20. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  21. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  22. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  26. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. function inherits(childCtor, parentCtor) {
  28. childCtor.prototype.__proto__ = parentCtor.prototype;
  29. };
  30. function V8Profile(separateIc) {
  31. Profile.call(this);
  32. if (!separateIc) {
  33. this.skipThisFunction = function(name) { return V8Profile.IC_RE.test(name); };
  34. }
  35. };
  36. inherits(V8Profile, Profile);
  37. V8Profile.IC_RE =
  38. /^(?:CallIC|LoadIC|StoreIC)|(?:Builtin: (?:Keyed)?(?:Call|Load|Store)IC_)/;
  39. /**
  40. * A thin wrapper around shell's 'read' function showing a file name on error.
  41. */
  42. function readFile(fileName) {
  43. try {
  44. return read(fileName);
  45. } catch (e) {
  46. print(fileName + ': ' + (e.message || e));
  47. throw e;
  48. }
  49. }
  50. /**
  51. * Parser for dynamic code optimization state.
  52. */
  53. function parseState(s) {
  54. switch (s) {
  55. case "": return Profile.CodeState.COMPILED;
  56. case "~": return Profile.CodeState.OPTIMIZABLE;
  57. case "*": return Profile.CodeState.OPTIMIZED;
  58. }
  59. throw new Error("unknown code state: " + s);
  60. }
  61. function SnapshotLogProcessor() {
  62. LogReader.call(this, {
  63. 'code-creation': {
  64. parsers: [null, parseInt, parseInt, parseInt, null, 'var-args'],
  65. processor: this.processCodeCreation },
  66. 'code-move': { parsers: [parseInt, parseInt],
  67. processor: this.processCodeMove },
  68. 'code-delete': { parsers: [parseInt],
  69. processor: this.processCodeDelete },
  70. 'function-creation': null,
  71. 'function-move': null,
  72. 'function-delete': null,
  73. 'sfi-move': null,
  74. 'snapshot-pos': { parsers: [parseInt, parseInt],
  75. processor: this.processSnapshotPosition }});
  76. V8Profile.prototype.handleUnknownCode = function(operation, addr) {
  77. var op = Profile.Operation;
  78. switch (operation) {
  79. case op.MOVE:
  80. print('Snapshot: Code move event for unknown code: 0x' +
  81. addr.toString(16));
  82. break;
  83. case op.DELETE:
  84. print('Snapshot: Code delete event for unknown code: 0x' +
  85. addr.toString(16));
  86. break;
  87. }
  88. };
  89. this.profile_ = new V8Profile();
  90. this.serializedEntries_ = [];
  91. }
  92. inherits(SnapshotLogProcessor, LogReader);
  93. SnapshotLogProcessor.prototype.processCodeCreation = function(
  94. type, kind, start, size, name, maybe_func) {
  95. if (maybe_func.length) {
  96. var funcAddr = parseInt(maybe_func[0]);
  97. var state = parseState(maybe_func[1]);
  98. this.profile_.addFuncCode(type, name, start, size, funcAddr, state);
  99. } else {
  100. this.profile_.addCode(type, name, start, size);
  101. }
  102. };
  103. SnapshotLogProcessor.prototype.processCodeMove = function(from, to) {
  104. this.profile_.moveCode(from, to);
  105. };
  106. SnapshotLogProcessor.prototype.processCodeDelete = function(start) {
  107. this.profile_.deleteCode(start);
  108. };
  109. SnapshotLogProcessor.prototype.processSnapshotPosition = function(addr, pos) {
  110. this.serializedEntries_[pos] = this.profile_.findEntry(addr);
  111. };
  112. SnapshotLogProcessor.prototype.processLogFile = function(fileName) {
  113. var contents = readFile(fileName);
  114. this.processLogChunk(contents);
  115. };
  116. SnapshotLogProcessor.prototype.getSerializedEntryName = function(pos) {
  117. var entry = this.serializedEntries_[pos];
  118. return entry ? entry.getRawName() : null;
  119. };
  120. function TickProcessor(
  121. cppEntriesProvider,
  122. separateIc,
  123. callGraphSize,
  124. ignoreUnknown,
  125. stateFilter,
  126. snapshotLogProcessor,
  127. distortion,
  128. range,
  129. sourceMap) {
  130. LogReader.call(this, {
  131. 'shared-library': { parsers: [null, parseInt, parseInt],
  132. processor: this.processSharedLibrary },
  133. 'code-creation': {
  134. parsers: [null, parseInt, parseInt, parseInt, null, 'var-args'],
  135. processor: this.processCodeCreation },
  136. 'code-move': { parsers: [parseInt, parseInt],
  137. processor: this.processCodeMove },
  138. 'code-delete': { parsers: [parseInt],
  139. processor: this.processCodeDelete },
  140. 'sfi-move': { parsers: [parseInt, parseInt],
  141. processor: this.processFunctionMove },
  142. 'snapshot-pos': { parsers: [parseInt, parseInt],
  143. processor: this.processSnapshotPosition },
  144. 'tick': {
  145. parsers: [parseInt, parseInt, parseInt,
  146. parseInt, parseInt, 'var-args'],
  147. processor: this.processTick },
  148. 'heap-sample-begin': { parsers: [null, null, parseInt],
  149. processor: this.processHeapSampleBegin },
  150. 'heap-sample-end': { parsers: [null, null],
  151. processor: this.processHeapSampleEnd },
  152. 'timer-event-start' : { parsers: [null, null, null],
  153. processor: this.advanceDistortion },
  154. 'timer-event-end' : { parsers: [null, null, null],
  155. processor: this.advanceDistortion },
  156. // Ignored events.
  157. 'profiler': null,
  158. 'function-creation': null,
  159. 'function-move': null,
  160. 'function-delete': null,
  161. 'heap-sample-item': null,
  162. // Obsolete row types.
  163. 'code-allocate': null,
  164. 'begin-code-region': null,
  165. 'end-code-region': null });
  166. this.cppEntriesProvider_ = cppEntriesProvider;
  167. this.callGraphSize_ = callGraphSize;
  168. this.ignoreUnknown_ = ignoreUnknown;
  169. this.stateFilter_ = stateFilter;
  170. this.snapshotLogProcessor_ = snapshotLogProcessor;
  171. this.sourceMap = sourceMap;
  172. this.deserializedEntriesNames_ = [];
  173. var ticks = this.ticks_ =
  174. { total: 0, unaccounted: 0, excluded: 0, gc: 0 };
  175. distortion = parseInt(distortion);
  176. // Convert picoseconds to nanoseconds.
  177. this.distortion_per_entry = isNaN(distortion) ? 0 : (distortion / 1000);
  178. this.distortion = 0;
  179. var rangelimits = range ? range.split(",") : [];
  180. var range_start = parseInt(rangelimits[0]);
  181. var range_end = parseInt(rangelimits[1]);
  182. // Convert milliseconds to nanoseconds.
  183. this.range_start = isNaN(range_start) ? -Infinity : (range_start * 1000);
  184. this.range_end = isNaN(range_end) ? Infinity : (range_end * 1000)
  185. V8Profile.prototype.handleUnknownCode = function(
  186. operation, addr, opt_stackPos) {
  187. var op = Profile.Operation;
  188. switch (operation) {
  189. case op.MOVE:
  190. print('Code move event for unknown code: 0x' + addr.toString(16));
  191. break;
  192. case op.DELETE:
  193. print('Code delete event for unknown code: 0x' + addr.toString(16));
  194. break;
  195. case op.TICK:
  196. // Only unknown PCs (the first frame) are reported as unaccounted,
  197. // otherwise tick balance will be corrupted (this behavior is compatible
  198. // with the original tickprocessor.py script.)
  199. if (opt_stackPos == 0) {
  200. ticks.unaccounted++;
  201. }
  202. break;
  203. }
  204. };
  205. this.profile_ = new V8Profile(separateIc);
  206. this.codeTypes_ = {};
  207. // Count each tick as a time unit.
  208. this.viewBuilder_ = new ViewBuilder(1);
  209. this.lastLogFileName_ = null;
  210. this.generation_ = 1;
  211. this.currentProducerProfile_ = null;
  212. };
  213. inherits(TickProcessor, LogReader);
  214. TickProcessor.VmStates = {
  215. JS: 0,
  216. GC: 1,
  217. COMPILER: 2,
  218. OTHER: 3,
  219. EXTERNAL: 4,
  220. IDLE: 5
  221. };
  222. TickProcessor.CodeTypes = {
  223. CPP: 0,
  224. SHARED_LIB: 1
  225. };
  226. // Otherwise, this is JS-related code. We are not adding it to
  227. // codeTypes_ map because there can be zillions of them.
  228. TickProcessor.CALL_PROFILE_CUTOFF_PCT = 2.0;
  229. TickProcessor.CALL_GRAPH_SIZE = 5;
  230. /**
  231. * @override
  232. */
  233. TickProcessor.prototype.printError = function(str) {
  234. print(str);
  235. };
  236. TickProcessor.prototype.setCodeType = function(name, type) {
  237. this.codeTypes_[name] = TickProcessor.CodeTypes[type];
  238. };
  239. TickProcessor.prototype.isSharedLibrary = function(name) {
  240. return this.codeTypes_[name] == TickProcessor.CodeTypes.SHARED_LIB;
  241. };
  242. TickProcessor.prototype.isCppCode = function(name) {
  243. return this.codeTypes_[name] == TickProcessor.CodeTypes.CPP;
  244. };
  245. TickProcessor.prototype.isJsCode = function(name) {
  246. return !(name in this.codeTypes_);
  247. };
  248. TickProcessor.prototype.processLogFile = function(fileName) {
  249. this.lastLogFileName_ = fileName;
  250. var line;
  251. while (line = readline()) {
  252. this.processLogLine(line);
  253. }
  254. };
  255. TickProcessor.prototype.processLogFileInTest = function(fileName) {
  256. // Hack file name to avoid dealing with platform specifics.
  257. this.lastLogFileName_ = 'v8.log';
  258. var contents = readFile(fileName);
  259. this.processLogChunk(contents);
  260. };
  261. TickProcessor.prototype.processSharedLibrary = function(
  262. name, startAddr, endAddr) {
  263. var entry = this.profile_.addLibrary(name, startAddr, endAddr);
  264. this.setCodeType(entry.getName(), 'SHARED_LIB');
  265. var self = this;
  266. var libFuncs = this.cppEntriesProvider_.parseVmSymbols(
  267. name, startAddr, endAddr, function(fName, fStart, fEnd) {
  268. self.profile_.addStaticCode(fName, fStart, fEnd);
  269. self.setCodeType(fName, 'CPP');
  270. });
  271. };
  272. TickProcessor.prototype.processCodeCreation = function(
  273. type, kind, start, size, name, maybe_func) {
  274. name = this.deserializedEntriesNames_[start] || name;
  275. if (maybe_func.length) {
  276. var funcAddr = parseInt(maybe_func[0]);
  277. var state = parseState(maybe_func[1]);
  278. this.profile_.addFuncCode(type, name, start, size, funcAddr, state);
  279. } else {
  280. this.profile_.addCode(type, name, start, size);
  281. }
  282. };
  283. TickProcessor.prototype.processCodeMove = function(from, to) {
  284. this.profile_.moveCode(from, to);
  285. };
  286. TickProcessor.prototype.processCodeDelete = function(start) {
  287. this.profile_.deleteCode(start);
  288. };
  289. TickProcessor.prototype.processFunctionMove = function(from, to) {
  290. this.profile_.moveFunc(from, to);
  291. };
  292. TickProcessor.prototype.processSnapshotPosition = function(addr, pos) {
  293. if (this.snapshotLogProcessor_) {
  294. this.deserializedEntriesNames_[addr] =
  295. this.snapshotLogProcessor_.getSerializedEntryName(pos);
  296. }
  297. };
  298. TickProcessor.prototype.includeTick = function(vmState) {
  299. return this.stateFilter_ == null || this.stateFilter_ == vmState;
  300. };
  301. TickProcessor.prototype.processTick = function(pc,
  302. ns_since_start,
  303. is_external_callback,
  304. tos_or_external_callback,
  305. vmState,
  306. stack) {
  307. this.distortion += this.distortion_per_entry;
  308. ns_since_start -= this.distortion;
  309. if (ns_since_start < this.range_start || ns_since_start > this.range_end) {
  310. return;
  311. }
  312. this.ticks_.total++;
  313. if (vmState == TickProcessor.VmStates.GC) this.ticks_.gc++;
  314. if (!this.includeTick(vmState)) {
  315. this.ticks_.excluded++;
  316. return;
  317. }
  318. if (is_external_callback) {
  319. // Don't use PC when in external callback code, as it can point
  320. // inside callback's code, and we will erroneously report
  321. // that a callback calls itself. Instead we use tos_or_external_callback,
  322. // as simply resetting PC will produce unaccounted ticks.
  323. pc = tos_or_external_callback;
  324. tos_or_external_callback = 0;
  325. } else if (tos_or_external_callback) {
  326. // Find out, if top of stack was pointing inside a JS function
  327. // meaning that we have encountered a frameless invocation.
  328. var funcEntry = this.profile_.findEntry(tos_or_external_callback);
  329. if (!funcEntry || !funcEntry.isJSFunction || !funcEntry.isJSFunction()) {
  330. tos_or_external_callback = 0;
  331. }
  332. }
  333. this.profile_.recordTick(this.processStack(pc, tos_or_external_callback, stack));
  334. };
  335. TickProcessor.prototype.advanceDistortion = function() {
  336. this.distortion += this.distortion_per_entry;
  337. }
  338. TickProcessor.prototype.processHeapSampleBegin = function(space, state, ticks) {
  339. if (space != 'Heap') return;
  340. this.currentProducerProfile_ = new CallTree();
  341. };
  342. TickProcessor.prototype.processHeapSampleEnd = function(space, state) {
  343. if (space != 'Heap' || !this.currentProducerProfile_) return;
  344. print('Generation ' + this.generation_ + ':');
  345. var tree = this.currentProducerProfile_;
  346. tree.computeTotalWeights();
  347. var producersView = this.viewBuilder_.buildView(tree);
  348. // Sort by total time, desc, then by name, desc.
  349. producersView.sort(function(rec1, rec2) {
  350. return rec2.totalTime - rec1.totalTime ||
  351. (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
  352. this.printHeavyProfile(producersView.head.children);
  353. this.currentProducerProfile_ = null;
  354. this.generation_++;
  355. };
  356. TickProcessor.prototype.printStatistics = function() {
  357. print('Statistical profiling result from ' + this.lastLogFileName_ +
  358. ', (' + this.ticks_.total +
  359. ' ticks, ' + this.ticks_.unaccounted + ' unaccounted, ' +
  360. this.ticks_.excluded + ' excluded).');
  361. if (this.ticks_.total == 0) return;
  362. // Print the unknown ticks percentage if they are not ignored.
  363. if (!this.ignoreUnknown_ && this.ticks_.unaccounted > 0) {
  364. this.printHeader('Unknown');
  365. this.printCounter(this.ticks_.unaccounted, this.ticks_.total);
  366. }
  367. var flatProfile = this.profile_.getFlatProfile();
  368. var flatView = this.viewBuilder_.buildView(flatProfile);
  369. // Sort by self time, desc, then by name, desc.
  370. flatView.sort(function(rec1, rec2) {
  371. return rec2.selfTime - rec1.selfTime ||
  372. (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
  373. var totalTicks = this.ticks_.total;
  374. if (this.ignoreUnknown_) {
  375. totalTicks -= this.ticks_.unaccounted;
  376. }
  377. // Our total time contains all the ticks encountered,
  378. // while profile only knows about the filtered ticks.
  379. flatView.head.totalTime = totalTicks;
  380. // Count library ticks
  381. var flatViewNodes = flatView.head.children;
  382. var self = this;
  383. var libraryTicks = 0;
  384. this.processProfile(flatViewNodes,
  385. function(name) { return self.isSharedLibrary(name); },
  386. function(rec) { libraryTicks += rec.selfTime; });
  387. var nonLibraryTicks = totalTicks - libraryTicks;
  388. this.printHeader('Shared libraries');
  389. this.printEntries(flatViewNodes, null,
  390. function(name) { return self.isSharedLibrary(name); });
  391. this.printHeader('JavaScript');
  392. this.printEntries(flatViewNodes, nonLibraryTicks,
  393. function(name) { return self.isJsCode(name); });
  394. this.printHeader('C++');
  395. this.printEntries(flatViewNodes, nonLibraryTicks,
  396. function(name) { return self.isCppCode(name); });
  397. this.printHeader('GC');
  398. this.printCounter(this.ticks_.gc, totalTicks);
  399. this.printHeavyProfHeader();
  400. var heavyProfile = this.profile_.getBottomUpProfile();
  401. var heavyView = this.viewBuilder_.buildView(heavyProfile);
  402. // To show the same percentages as in the flat profile.
  403. heavyView.head.totalTime = totalTicks;
  404. // Sort by total time, desc, then by name, desc.
  405. heavyView.sort(function(rec1, rec2) {
  406. return rec2.totalTime - rec1.totalTime ||
  407. (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
  408. this.printHeavyProfile(heavyView.head.children);
  409. };
  410. function padLeft(s, len) {
  411. s = s.toString();
  412. if (s.length < len) {
  413. var padLength = len - s.length;
  414. if (!(padLength in padLeft)) {
  415. padLeft[padLength] = new Array(padLength + 1).join(' ');
  416. }
  417. s = padLeft[padLength] + s;
  418. }
  419. return s;
  420. };
  421. TickProcessor.prototype.printHeader = function(headerTitle) {
  422. print('\n [' + headerTitle + ']:');
  423. print(' ticks total nonlib name');
  424. };
  425. TickProcessor.prototype.printHeavyProfHeader = function() {
  426. print('\n [Bottom up (heavy) profile]:');
  427. print(' Note: percentage shows a share of a particular caller in the ' +
  428. 'total\n' +
  429. ' amount of its parent calls.');
  430. print(' Callers occupying less than ' +
  431. TickProcessor.CALL_PROFILE_CUTOFF_PCT.toFixed(1) +
  432. '% are not shown.\n');
  433. print(' ticks parent name');
  434. };
  435. TickProcessor.prototype.printCounter = function(ticksCount, totalTicksCount) {
  436. var pct = ticksCount * 100.0 / totalTicksCount;
  437. print(' ' + padLeft(ticksCount, 5) + ' ' + padLeft(pct.toFixed(1), 5) + '%');
  438. };
  439. TickProcessor.prototype.processProfile = function(
  440. profile, filterP, func) {
  441. for (var i = 0, n = profile.length; i < n; ++i) {
  442. var rec = profile[i];
  443. if (!filterP(rec.internalFuncName)) {
  444. continue;
  445. }
  446. func(rec);
  447. }
  448. };
  449. TickProcessor.prototype.getLineAndColumn = function(name) {
  450. var re = /:([0-9]+):([0-9]+)$/;
  451. var array = re.exec(name);
  452. if (!array) {
  453. return null;
  454. }
  455. return {line: array[1], column: array[2]};
  456. }
  457. TickProcessor.prototype.hasSourceMap = function() {
  458. return this.sourceMap != null;
  459. };
  460. TickProcessor.prototype.formatFunctionName = function(funcName) {
  461. if (!this.hasSourceMap()) {
  462. return funcName;
  463. }
  464. var lc = this.getLineAndColumn(funcName);
  465. if (lc == null) {
  466. return funcName;
  467. }
  468. // in source maps lines and columns are zero based
  469. var lineNumber = lc.line - 1;
  470. var column = lc.column - 1;
  471. var entry = this.sourceMap.findEntry(lineNumber, column);
  472. var sourceFile = entry[2];
  473. var sourceLine = entry[3] + 1;
  474. var sourceColumn = entry[4] + 1;
  475. return sourceFile + ':' + sourceLine + ':' + sourceColumn + ' -> ' + funcName;
  476. };
  477. TickProcessor.prototype.printEntries = function(
  478. profile, nonLibTicks, filterP) {
  479. var that = this;
  480. this.processProfile(profile, filterP, function (rec) {
  481. if (rec.selfTime == 0) return;
  482. var nonLibPct = nonLibTicks != null ?
  483. rec.selfTime * 100.0 / nonLibTicks : 0.0;
  484. var funcName = that.formatFunctionName(rec.internalFuncName);
  485. print(' ' + padLeft(rec.selfTime, 5) + ' ' +
  486. padLeft(rec.selfPercent.toFixed(1), 5) + '% ' +
  487. padLeft(nonLibPct.toFixed(1), 5) + '% ' +
  488. funcName);
  489. });
  490. };
  491. TickProcessor.prototype.printHeavyProfile = function(profile, opt_indent) {
  492. var self = this;
  493. var indent = opt_indent || 0;
  494. var indentStr = padLeft('', indent);
  495. this.processProfile(profile, function() { return true; }, function (rec) {
  496. // Cut off too infrequent callers.
  497. if (rec.parentTotalPercent < TickProcessor.CALL_PROFILE_CUTOFF_PCT) return;
  498. var funcName = self.formatFunctionName(rec.internalFuncName);
  499. print(' ' + padLeft(rec.totalTime, 5) + ' ' +
  500. padLeft(rec.parentTotalPercent.toFixed(1), 5) + '% ' +
  501. indentStr + funcName);
  502. // Limit backtrace depth.
  503. if (indent < 2 * self.callGraphSize_) {
  504. self.printHeavyProfile(rec.children, indent + 2);
  505. }
  506. // Delimit top-level functions.
  507. if (indent == 0) {
  508. print('');
  509. }
  510. });
  511. };
  512. function CppEntriesProvider() {
  513. };
  514. CppEntriesProvider.prototype.parseVmSymbols = function(
  515. libName, libStart, libEnd, processorFunc) {
  516. this.loadSymbols(libName);
  517. var prevEntry;
  518. function addEntry(funcInfo) {
  519. // Several functions can be mapped onto the same address. To avoid
  520. // creating zero-sized entries, skip such duplicates.
  521. // Also double-check that function belongs to the library address space.
  522. if (prevEntry && !prevEntry.end &&
  523. prevEntry.start < funcInfo.start &&
  524. prevEntry.start >= libStart && funcInfo.start <= libEnd) {
  525. processorFunc(prevEntry.name, prevEntry.start, funcInfo.start);
  526. }
  527. if (funcInfo.end &&
  528. (!prevEntry || prevEntry.start != funcInfo.start) &&
  529. funcInfo.start >= libStart && funcInfo.end <= libEnd) {
  530. processorFunc(funcInfo.name, funcInfo.start, funcInfo.end);
  531. }
  532. prevEntry = funcInfo;
  533. }
  534. while (true) {
  535. var funcInfo = this.parseNextLine();
  536. if (funcInfo === null) {
  537. continue;
  538. } else if (funcInfo === false) {
  539. break;
  540. }
  541. if (funcInfo.start < libStart && funcInfo.start < libEnd - libStart) {
  542. funcInfo.start += libStart;
  543. }
  544. if (funcInfo.size) {
  545. funcInfo.end = funcInfo.start + funcInfo.size;
  546. }
  547. addEntry(funcInfo);
  548. }
  549. addEntry({name: '', start: libEnd});
  550. };
  551. CppEntriesProvider.prototype.loadSymbols = function(libName) {
  552. };
  553. CppEntriesProvider.prototype.parseNextLine = function() {
  554. return false;
  555. };
  556. function UnixCppEntriesProvider(nmExec, targetRootFS) {
  557. this.symbols = [];
  558. this.parsePos = 0;
  559. this.nmExec = nmExec;
  560. this.targetRootFS = targetRootFS;
  561. this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ([0-9a-fA-F]{8,16} )?[tTwW] (.*)$/;
  562. };
  563. inherits(UnixCppEntriesProvider, CppEntriesProvider);
  564. UnixCppEntriesProvider.prototype.loadSymbols = function(libName) {
  565. this.parsePos = 0;
  566. libName = this.targetRootFS + libName;
  567. try {
  568. this.symbols = [
  569. os.system(this.nmExec, ['-C', '-n', '-S', libName], -1, -1),
  570. os.system(this.nmExec, ['-C', '-n', '-S', '-D', libName], -1, -1)
  571. ];
  572. } catch (e) {
  573. // If the library cannot be found on this system let's not panic.
  574. this.symbols = ['', ''];
  575. }
  576. };
  577. UnixCppEntriesProvider.prototype.parseNextLine = function() {
  578. if (this.symbols.length == 0) {
  579. return false;
  580. }
  581. var lineEndPos = this.symbols[0].indexOf('\n', this.parsePos);
  582. if (lineEndPos == -1) {
  583. this.symbols.shift();
  584. this.parsePos = 0;
  585. return this.parseNextLine();
  586. }
  587. var line = this.symbols[0].substring(this.parsePos, lineEndPos);
  588. this.parsePos = lineEndPos + 1;
  589. var fields = line.match(this.FUNC_RE);
  590. var funcInfo = null;
  591. if (fields) {
  592. funcInfo = { name: fields[3], start: parseInt(fields[1], 16) };
  593. if (fields[2]) {
  594. funcInfo.size = parseInt(fields[2], 16);
  595. }
  596. }
  597. return funcInfo;
  598. };
  599. function MacCppEntriesProvider(nmExec, targetRootFS) {
  600. UnixCppEntriesProvider.call(this, nmExec, targetRootFS);
  601. // Note an empty group. It is required, as UnixCppEntriesProvider expects 3 groups.
  602. this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ()[iItT] (.*)$/;
  603. };
  604. inherits(MacCppEntriesProvider, UnixCppEntriesProvider);
  605. MacCppEntriesProvider.prototype.loadSymbols = function(libName) {
  606. this.parsePos = 0;
  607. libName = this.targetRootFS + libName;
  608. try {
  609. this.symbols = [os.system(this.nmExec, ['-n', '-f', libName], -1, -1), ''];
  610. } catch (e) {
  611. // If the library cannot be found on this system let's not panic.
  612. this.symbols = '';
  613. }
  614. };
  615. function WindowsCppEntriesProvider(_ignored_nmExec, targetRootFS) {
  616. this.targetRootFS = targetRootFS;
  617. this.symbols = '';
  618. this.parsePos = 0;
  619. };
  620. inherits(WindowsCppEntriesProvider, CppEntriesProvider);
  621. WindowsCppEntriesProvider.FILENAME_RE = /^(.*)\.([^.]+)$/;
  622. WindowsCppEntriesProvider.FUNC_RE =
  623. /^\s+0001:[0-9a-fA-F]{8}\s+([_\?@$0-9a-zA-Z]+)\s+([0-9a-fA-F]{8}).*$/;
  624. WindowsCppEntriesProvider.IMAGE_BASE_RE =
  625. /^\s+0000:00000000\s+___ImageBase\s+([0-9a-fA-F]{8}).*$/;
  626. // This is almost a constant on Windows.
  627. WindowsCppEntriesProvider.EXE_IMAGE_BASE = 0x00400000;
  628. WindowsCppEntriesProvider.prototype.loadSymbols = function(libName) {
  629. libName = this.targetRootFS + libName;
  630. var fileNameFields = libName.match(WindowsCppEntriesProvider.FILENAME_RE);
  631. if (!fileNameFields) return;
  632. var mapFileName = fileNameFields[1] + '.map';
  633. this.moduleType_ = fileNameFields[2].toLowerCase();
  634. try {
  635. this.symbols = read(mapFileName);
  636. } catch (e) {
  637. // If .map file cannot be found let's not panic.
  638. this.symbols = '';
  639. }
  640. };
  641. WindowsCppEntriesProvider.prototype.parseNextLine = function() {
  642. var lineEndPos = this.symbols.indexOf('\r\n', this.parsePos);
  643. if (lineEndPos == -1) {
  644. return false;
  645. }
  646. var line = this.symbols.substring(this.parsePos, lineEndPos);
  647. this.parsePos = lineEndPos + 2;
  648. // Image base entry is above all other symbols, so we can just
  649. // terminate parsing.
  650. var imageBaseFields = line.match(WindowsCppEntriesProvider.IMAGE_BASE_RE);
  651. if (imageBaseFields) {
  652. var imageBase = parseInt(imageBaseFields[1], 16);
  653. if ((this.moduleType_ == 'exe') !=
  654. (imageBase == WindowsCppEntriesProvider.EXE_IMAGE_BASE)) {
  655. return false;
  656. }
  657. }
  658. var fields = line.match(WindowsCppEntriesProvider.FUNC_RE);
  659. return fields ?
  660. { name: this.unmangleName(fields[1]), start: parseInt(fields[2], 16) } :
  661. null;
  662. };
  663. /**
  664. * Performs very simple unmangling of C++ names.
  665. *
  666. * Does not handle arguments and template arguments. The mangled names have
  667. * the form:
  668. *
  669. * ?LookupInDescriptor@JSObject@internal@v8@@...arguments info...
  670. */
  671. WindowsCppEntriesProvider.prototype.unmangleName = function(name) {
  672. // Empty or non-mangled name.
  673. if (name.length < 1 || name.charAt(0) != '?') return name;
  674. var nameEndPos = name.indexOf('@@');
  675. var components = name.substring(1, nameEndPos).split('@');
  676. components.reverse();
  677. return components.join('::');
  678. };
  679. function ArgumentsProcessor(args) {
  680. this.args_ = args;
  681. this.result_ = ArgumentsProcessor.DEFAULTS;
  682. this.argsDispatch_ = {
  683. '-j': ['stateFilter', TickProcessor.VmStates.JS,
  684. 'Show only ticks from JS VM state'],
  685. '-g': ['stateFilter', TickProcessor.VmStates.GC,
  686. 'Show only ticks from GC VM state'],
  687. '-c': ['stateFilter', TickProcessor.VmStates.COMPILER,
  688. 'Show only ticks from COMPILER VM state'],
  689. '-o': ['stateFilter', TickProcessor.VmStates.OTHER,
  690. 'Show only ticks from OTHER VM state'],
  691. '-e': ['stateFilter', TickProcessor.VmStates.EXTERNAL,
  692. 'Show only ticks from EXTERNAL VM state'],
  693. '--call-graph-size': ['callGraphSize', TickProcessor.CALL_GRAPH_SIZE,
  694. 'Set the call graph size'],
  695. '--ignore-unknown': ['ignoreUnknown', true,
  696. 'Exclude ticks of unknown code entries from processing'],
  697. '--separate-ic': ['separateIc', true,
  698. 'Separate IC entries'],
  699. '--unix': ['platform', 'unix',
  700. 'Specify that we are running on *nix platform'],
  701. '--windows': ['platform', 'windows',
  702. 'Specify that we are running on Windows platform'],
  703. '--mac': ['platform', 'mac',
  704. 'Specify that we are running on Mac OS X platform'],
  705. '--nm': ['nm', 'nm',
  706. 'Specify the \'nm\' executable to use (e.g. --nm=/my_dir/nm)'],
  707. '--target': ['targetRootFS', '',
  708. 'Specify the target root directory for cross environment'],
  709. '--snapshot-log': ['snapshotLogFileName', 'snapshot.log',
  710. 'Specify snapshot log file to use (e.g. --snapshot-log=snapshot.log)'],
  711. '--range': ['range', 'auto,auto',
  712. 'Specify the range limit as [start],[end]'],
  713. '--distortion': ['distortion', 0,
  714. 'Specify the logging overhead in picoseconds'],
  715. '--source-map': ['sourceMap', null,
  716. 'Specify the source map that should be used for output']
  717. };
  718. this.argsDispatch_['--js'] = this.argsDispatch_['-j'];
  719. this.argsDispatch_['--gc'] = this.argsDispatch_['-g'];
  720. this.argsDispatch_['--compiler'] = this.argsDispatch_['-c'];
  721. this.argsDispatch_['--other'] = this.argsDispatch_['-o'];
  722. this.argsDispatch_['--external'] = this.argsDispatch_['-e'];
  723. };
  724. ArgumentsProcessor.DEFAULTS = {
  725. logFileName: 'v8.log',
  726. snapshotLogFileName: null,
  727. platform: 'unix',
  728. stateFilter: null,
  729. callGraphSize: 5,
  730. ignoreUnknown: false,
  731. separateIc: false,
  732. targetRootFS: '',
  733. nm: 'nm',
  734. range: 'auto,auto',
  735. distortion: 0
  736. };
  737. ArgumentsProcessor.prototype.parse = function() {
  738. while (this.args_.length) {
  739. var arg = this.args_[0];
  740. if (arg.charAt(0) != '-') {
  741. break;
  742. }
  743. this.args_.shift();
  744. var userValue = null;
  745. var eqPos = arg.indexOf('=');
  746. if (eqPos != -1) {
  747. userValue = arg.substr(eqPos + 1);
  748. arg = arg.substr(0, eqPos);
  749. }
  750. if (arg in this.argsDispatch_) {
  751. var dispatch = this.argsDispatch_[arg];
  752. this.result_[dispatch[0]] = userValue == null ? dispatch[1] : userValue;
  753. } else {
  754. return false;
  755. }
  756. }
  757. if (this.args_.length >= 1) {
  758. this.result_.logFileName = this.args_.shift();
  759. }
  760. return true;
  761. };
  762. ArgumentsProcessor.prototype.result = function() {
  763. return this.result_;
  764. };
  765. ArgumentsProcessor.prototype.printUsageAndExit = function() {
  766. function padRight(s, len) {
  767. s = s.toString();
  768. if (s.length < len) {
  769. s = s + (new Array(len - s.length + 1).join(' '));
  770. }
  771. return s;
  772. }
  773. print('Cmdline args: [options] [log-file-name]\n' +
  774. 'Default log file name is "' +
  775. ArgumentsProcessor.DEFAULTS.logFileName + '".\n');
  776. print('Options:');
  777. for (var arg in this.argsDispatch_) {
  778. var synonims = [arg];
  779. var dispatch = this.argsDispatch_[arg];
  780. for (var synArg in this.argsDispatch_) {
  781. if (arg !== synArg && dispatch === this.argsDispatch_[synArg]) {
  782. synonims.push(synArg);
  783. delete this.argsDispatch_[synArg];
  784. }
  785. }
  786. print(' ' + padRight(synonims.join(', '), 20) + dispatch[2]);
  787. }
  788. quit(2);
  789. };