Commit 6550945c authored by Alexei Filippov's avatar Alexei Filippov Committed by Commit Bot

DevTools: Report modules in Heap snapshot text view.

Module names and address offsets within the module can be used by
symbolizers to determine symbol names.

BUG=803276

Change-Id: Id3cca2b5f39a3e4f6bb5eb9ddd4c2bb8ec80ed68
Reviewed-on: https://chromium-review.googlesource.com/1159363
Commit-Queue: Alexei Filippov <alph@chromium.org>
Reviewed-by: default avatarAleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#581376}
parent 41532413
...@@ -57,8 +57,24 @@ Profiler.HeapProfileView = class extends Profiler.ProfileView { ...@@ -57,8 +57,24 @@ Profiler.HeapProfileView = class extends Profiler.ProfileView {
let text = `Sampling memory profile.\n\nDate/Time: ${new Date()}\n` + let text = `Sampling memory profile.\n\nDate/Time: ${new Date()}\n` +
`Report Version: 7\nNode weight: 1 KiB\n----\n\nCall graph:\n`; `Report Version: 7\nNode weight: 1 KiB\n----\n\nCall graph:\n`;
const sortedChildren = this.profile.root.children.sort((a, b) => b.total - a.total); const sortedChildren = this.profile.root.children.sort((a, b) => b.total - a.total);
const modules = this.profile.modules.map(
m => Object.assign({address: BigInt(m.baseAddress), endAddress: BigInt(m.baseAddress) + BigInt(m.size)}, m));
modules.sort((m1, m2) => m1.address - m2.address);
for (const child of sortedChildren) for (const child of sortedChildren)
printTree(' ', child !== sortedChildren.peekLast(), child); printTree(' ', child !== sortedChildren.peekLast(), child);
text += '\nBinary Images:\n';
for (const module of modules) {
const fileName = /[^/\\]*$/.exec(module.name)[0];
const version = '1.0';
const formattedUuid = module.uuid.includes('-') ?
module.uuid :
module.uuid.replace(/(.{8})(.{4})(.{4})(.{4})(.{12}).*/, '$1-$2-$3-$4-$5');
text += `${('0x' + module.address.toString(16)).padStart(18)} - `;
text += `${('0x' + (module.endAddress - BigInt(1)).toString(16)).padStart(18)}`;
text += ` ${fileName} (${version}) <${formattedUuid}> ${module.name}\n`;
}
view.contentElement.createChild('pre', 'profile-text-view monospace').textContent = text; view.contentElement.createChild('pre', 'profile-text-view monospace').textContent = text;
/** /**
...@@ -67,10 +83,27 @@ Profiler.HeapProfileView = class extends Profiler.ProfileView { ...@@ -67,10 +83,27 @@ Profiler.HeapProfileView = class extends Profiler.ProfileView {
* @param {!SDK.ProfileNode} node * @param {!SDK.ProfileNode} node
*/ */
function printTree(padding, drawGuide, node) { function printTree(padding, drawGuide, node) {
const isAddress = node.functionName.startsWith('0x'); const addressText = /0x[0-9a-f]*|[0-9]*/.exec(node.functionName)[0] || '';
const functionName = isAddress ? '???' : node.functionName; let module;
const address = isAddress ? node.functionName : '???'; if (addressText) {
text += `${padding}${Math.round(node.total / 1024)} ${functionName} [${address}]\n`; const address = BigInt(addressText);
const pos = modules.upperBound(address, (address, module) => address - module.address);
if (pos > 0 && address < modules[pos - 1].endAddress)
module = modules[pos - 1];
}
const functionName =
(addressText ? node.functionName.substr(addressText.length + 1) : node.functionName) || '???';
text += `${padding}${Math.round(node.total / 1024)} ${functionName} `;
if (module) {
const fileName = /[^/\\]*$/.exec(module.name);
if (fileName)
text += `(in ${fileName}) `;
const offset = BigInt(addressText) - module.address;
text += `load address ${module.baseAddress} + 0x${offset.toString(16)} `;
}
if (addressText)
text += `[${addressText}]`;
text += '\n';
const guideChar = drawGuide ? guides[padding.length / 2 % guides.length] : ' '; const guideChar = drawGuide ? guides[padding.length / 2 % guides.length] : ' ';
const nextPadding = padding + guideChar + ' '; const nextPadding = padding + guideChar + ' ';
const sortedChildren = node.children.sort((a, b) => b.total - a.total); const sortedChildren = node.children.sort((a, b) => b.total - a.total);
...@@ -351,8 +384,8 @@ Profiler.SamplingNativeHeapSnapshotBrowserType = class extends Profiler.Sampling ...@@ -351,8 +384,8 @@ Profiler.SamplingNativeHeapSnapshotBrowserType = class extends Profiler.Sampling
* @param {!SDK.HeapProfilerModel} heapProfilerModel * @param {!SDK.HeapProfilerModel} heapProfilerModel
* @return {!Promise<!Protocol.HeapProfiler.SamplingHeapProfile>} * @return {!Promise<!Protocol.HeapProfiler.SamplingHeapProfile>}
*/ */
_takeNativeSnapshot(heapProfilerModel) { async _takeNativeSnapshot(heapProfilerModel) {
return heapProfilerModel.takeNativeBrowserSnapshot(); return await heapProfilerModel.takeNativeBrowserSnapshot();
} }
}; };
...@@ -442,6 +475,7 @@ Profiler.SamplingHeapProfileModel = class extends SDK.ProfileTreeModel { ...@@ -442,6 +475,7 @@ Profiler.SamplingHeapProfileModel = class extends SDK.ProfileTreeModel {
constructor(profile) { constructor(profile) {
super(); super();
this.initialize(translateProfileTree(profile.head)); this.initialize(translateProfileTree(profile.head));
this.modules = profile.modules || [];
/** /**
* @param {!Protocol.HeapProfiler.SamplingHeapProfileNode} root * @param {!Protocol.HeapProfiler.SamplingHeapProfileNode} root
......
...@@ -55,7 +55,7 @@ SDK.HeapProfilerModel = class extends SDK.SDKModel { ...@@ -55,7 +55,7 @@ SDK.HeapProfilerModel = class extends SDK.SDKModel {
} }
/** /**
* @return {!Promise<!Protocol.HeapProfiler.SamplingHeapProfile>} * @return {!Promise<!SDK.HeapProfilerModel.NativeHeapProfile>}
*/ */
async stopNativeSampling() { async stopNativeSampling() {
const rawProfile = await this._memoryAgent.getSamplingProfile(); const rawProfile = await this._memoryAgent.getSamplingProfile();
...@@ -64,7 +64,7 @@ SDK.HeapProfilerModel = class extends SDK.SDKModel { ...@@ -64,7 +64,7 @@ SDK.HeapProfilerModel = class extends SDK.SDKModel {
} }
/** /**
* @return {!Promise<!Protocol.HeapProfiler.SamplingHeapProfile>} * @return {!Promise<!SDK.HeapProfilerModel.NativeHeapProfile>}
*/ */
async takeNativeSnapshot() { async takeNativeSnapshot() {
const rawProfile = await this._memoryAgent.getAllTimeSamplingProfile(); const rawProfile = await this._memoryAgent.getAllTimeSamplingProfile();
...@@ -72,7 +72,7 @@ SDK.HeapProfilerModel = class extends SDK.SDKModel { ...@@ -72,7 +72,7 @@ SDK.HeapProfilerModel = class extends SDK.SDKModel {
} }
/** /**
* @return {!Promise<!Protocol.HeapProfiler.SamplingHeapProfile>} * @return {!Promise<!SDK.HeapProfilerModel.NativeHeapProfile>}
*/ */
async takeNativeBrowserSnapshot() { async takeNativeBrowserSnapshot() {
const rawProfile = await this._memoryAgent.getBrowserSamplingProfile(); const rawProfile = await this._memoryAgent.getBrowserSamplingProfile();
...@@ -81,10 +81,11 @@ SDK.HeapProfilerModel = class extends SDK.SDKModel { ...@@ -81,10 +81,11 @@ SDK.HeapProfilerModel = class extends SDK.SDKModel {
/** /**
* @param {!Protocol.Memory.SamplingProfile} rawProfile * @param {!Protocol.Memory.SamplingProfile} rawProfile
* @return {!Protocol.HeapProfiler.SamplingHeapProfile} * @return {!SDK.HeapProfilerModel.NativeHeapProfile}
*/ */
_convertNativeProfile(rawProfile) { _convertNativeProfile(rawProfile) {
const head = {children: new Map(), selfSize: 0, callFrame: {functionName: '(root)', url: ''}}; const head = /** @type {!Protocol.HeapProfiler.SamplingHeapProfileNode} */
({children: new Map(), selfSize: 0, callFrame: {functionName: '(root)', url: ''}});
for (const sample of rawProfile.samples) { for (const sample of rawProfile.samples) {
const node = sample.stack.reverse().reduce((node, name) => { const node = sample.stack.reverse().reduce((node, name) => {
let child = node.children.get(name); let child = node.children.get(name);
...@@ -108,7 +109,7 @@ SDK.HeapProfilerModel = class extends SDK.SDKModel { ...@@ -108,7 +109,7 @@ SDK.HeapProfilerModel = class extends SDK.SDKModel {
} }
convertChildren(head); convertChildren(head);
return /** @type {!Protocol.HeapProfiler.SamplingHeapProfile} */ ({head}); return new SDK.HeapProfilerModel.NativeHeapProfile(head, rawProfile.modules);
} }
/** /**
...@@ -217,6 +218,20 @@ SDK.HeapProfilerModel.Events = { ...@@ -217,6 +218,20 @@ SDK.HeapProfilerModel.Events = {
ResetProfiles: Symbol('ResetProfiles') ResetProfiles: Symbol('ResetProfiles')
}; };
/**
* @extends {Protocol.HeapProfiler.SamplingHeapProfile}
*/
SDK.HeapProfilerModel.NativeHeapProfile = class {
/**
* @param {!Protocol.HeapProfiler.SamplingHeapProfileNode} head
* @param {!Array<!Protocol.Memory.Module>} modules
*/
constructor(head, modules) {
this.head = head;
this.modules = modules;
}
};
/** /**
* @extends {Protocol.HeapProfilerDispatcher} * @extends {Protocol.HeapProfilerDispatcher}
* @unrestricted * @unrestricted
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment