Skip to content
This repository has been archived by the owner on Feb 1, 2022. It is now read-only.

Commit

Permalink
feat: Support CPU & heap profiles
Browse files Browse the repository at this point in the history
  • Loading branch information
Jan Krems committed Nov 30, 2016
1 parent fe1af64 commit 3e1a66a
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 2 deletions.
94 changes: 92 additions & 2 deletions lib/_inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const Buffer = require('buffer').Buffer;
const { spawn } = require('child_process');
const crypto = require('crypto');
const { EventEmitter } = require('events');
const FS = require('fs');
const http = require('http');
const Path = require('path');
const Repl = require('repl');
Expand Down Expand Up @@ -585,7 +586,7 @@ function createRepl(inspector) {
});
}

const { Debugger, Runtime } = inspector;
const { Debugger, HeapProfiler, Profiler, Runtime } = inspector;

let repl; // eslint-disable-line prefer-const

Expand Down Expand Up @@ -676,6 +677,31 @@ function createRepl(inspector) {
}
}

const profiles = [];
class Profile {
constructor(data) {
this.data = data;
}

static createAndRegister({ profile }) {
const p = new Profile(profile);
profiles.push(p);
return p;
}

[util.inspect.custom](depth, { stylize }) {
const { startTime, endTime } = this.data;
return stylize(`[Profile ${endTime - startTime}μs]`, 'special');
}

save(filename = 'node.cpuprofile') {
const absoluteFile = Path.resolve(filename);
const json = JSON.stringify(this.data);
FS.writeFileSync(absoluteFile, json);
print('Saved profile to ' + absoluteFile);
}
}

class SourceSnippet {
constructor(location, delta, scriptSource) {
Object.assign(this, location);
Expand Down Expand Up @@ -1113,6 +1139,14 @@ function createRepl(inspector) {
}
});

Profiler.on('consoleProfileFinished', ({ profile }) => {
Profile.createAndRegister({ profile });
print([
'Captured new CPU profile.',
`Access it with profiles[${profiles.length - 1}]`
].join('\n'));
});

function initializeContext(context) {
inspector.domainNames.forEach((domain) => {
Object.defineProperty(context, domain, {
Expand Down Expand Up @@ -1176,6 +1210,62 @@ function createRepl(inspector) {
return evalInCurrentContext(expr);
},

get profile() {
return Profiler.start();
},

get profileEnd() {
return Profiler.stop()
.then(Profile.createAndRegister);
},

get profiles() {
return profiles;
},

takeHeapSnapshot(filename = 'node.heapsnapshot') {
return new Promise((resolve, reject) => {
const absoluteFile = Path.resolve(filename);
const writer = FS.createWriteStream(absoluteFile);
let totalSize;
let sizeWritten = 0;
function onProgress({ done, total, finished }) {
totalSize = total;
if (finished) {
print('Heap snaphost prepared.');
} else {
print(`Heap snapshot: ${done}/${total}`, true);
}
}
function onChunk({ chunk }) {
sizeWritten += chunk.length;
writer.write(chunk);
print(`Writing snapshot: ${sizeWritten}/${totalSize}`, true);
if (sizeWritten >= totalSize) {
writer.end();
teardown();
print(`Wrote snapshot: ${absoluteFile}`);
resolve();
}
}
function teardown() {
HeapProfiler.removeListener(
'reportHeapSnapshotProgress', onProgress);
HeapProfiler.removeListener('addHeapSnapshotChunk', onChunk);
}

HeapProfiler.on('reportHeapSnapshotProgress', onProgress);
HeapProfiler.on('addHeapSnapshotChunk', onChunk);

print('Heap snapshot: 0/0', true);
HeapProfiler.takeHeapSnapshot({ reportProgress: true })
.then(null, (error) => {
teardown();
reject(error);
});
});
},

get watchers() {
return watchers();
},
Expand Down Expand Up @@ -1374,7 +1464,7 @@ class NodeInspector {

this.client = new ProtocolClient(options.port, options.host);

this.domainNames = ['Debugger', 'Runtime'];
this.domainNames = ['Debugger', 'HeapProfiler', 'Profiler', 'Runtime'];
this.domainNames.forEach((domain) => {
this[domain] = createAgentProxy(domain, this.client);
});
Expand Down
6 changes: 6 additions & 0 deletions test/cli/profile.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ const { test } = require('tap');

const startCLI = require('./start-cli');

function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

test('profiles', (t) => {
const cli = startCLI(['examples/empty.js']);

Expand All @@ -18,8 +22,10 @@ test('profiles', (t) => {
t.match(cli.output, 'undefined');
})
.then(() => cli.command('exec console.profileEnd()'))
.then(() => delay(250))
.then(() => {
t.match(cli.output, 'undefined');
t.match(cli.output, 'Captured new CPU profile.');
})
.then(() => cli.quit())
.then(null, onFatal);
Expand Down

0 comments on commit 3e1a66a

Please sign in to comment.