Skip to content

Commit

Permalink
Make heap-snapshot command produce V8 snapshot by default (#200)
Browse files Browse the repository at this point in the history
Signed-off-by: Liam McLoughlin <lmcloughlin@google.com>
  • Loading branch information
Hexxeh committed Aug 5, 2022
1 parent ebed2be commit 457b521
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 33 deletions.
14 changes: 4 additions & 10 deletions packages/memory-profiler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,15 @@ enough. The following resources are helpful in understanding it:

## V8 heap snapshot

First, make a heap snapshot with the hidden sdk-cli command `heap-snapshot`. Then,
With your device connected (`connect device`) and FBA loaded (`set-app-package` or `install`), simply run the `heap-snapshot` command. A `.heapsnapshot` file will be produced in the current directory.

```
$ node lib/cli.js v8 /path/to/fitbit-project/js-heap.bin /path/to/fitbit-project/build/app.fba /path/to/fitbit.heapsnapshot atlas
```

(replacing the paths and `atlas` with your device type).

Then load the file `fitbit.heapsnapshot` in Chrome Dev Tools under the Memory tab. You can also take
Then load the `.heapsnapshot` file in Chrome Dev Tools under the Memory tab. You can also take
successive snapshots and load them in Chrome Dev Tools and compare them to see how the heap has
changed.
changed, though note that IDs are not stable currently which limits the usefulness of this.

## Explore in a REPL

If you want to manually explore the heap graph to really dig into things, you can load a repl
If you want to manually explore the heap graph to really dig into things, you can load a REPL
with the graph of the jerryscript heap snapshot already loaded. Run:

```
Expand Down
2 changes: 1 addition & 1 deletion packages/memory-profiler/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { generateGraph as convert } from './convert';
export { generateGraph, generateV8HeapSnapshot } from './convert';
2 changes: 1 addition & 1 deletion packages/sdk-cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ cli.history('Fitbit-Command-Line-SDK');
cli.use(build);
cli.use(buildAndInstall({ hostConnections, appContext }));
cli.use(connect({ hostConnections }));
cli.use(heapSnapshot({ hostConnections }));
cli.use(heapSnapshot({ hostConnections, appContext }));
cli.use(install({ hostConnections, appContext }));
cli.use(screenshot({ hostConnections }));
cli.use(setAppPackage({ appContext }));
Expand Down
70 changes: 51 additions & 19 deletions packages/sdk-cli/src/commands/heapSnapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,51 @@ import dateformat from 'dateformat';
import untildify from 'untildify';
import vorpal from '@moleculer/vorpal';

import HostConnections from '../models/HostConnections';
import AppContext from '../models/AppContext';
import captureHeapSnapshot from '../models/captureHeapSnapshot';
import * as compatibility from '../models/compatibility';
import HostConnections from '../models/HostConnections';
import { ComponentSourceMaps } from '@fitbit/app-package';

type HeapSnapshotArgs = vorpal.Args & {
uuid?: string;
};

export default function install(stores: { hostConnections: HostConnections }) {
export default function heapSnapshot(stores: {
hostConnections: HostConnections;
appContext: AppContext;
}) {
return (cli: vorpal) => {
cli
.command(
'heap-snapshot [path] [uuid]',
'heap-snapshot [path]',
// tslint:disable-next-line:max-line-length
'Capture a JS heap snapshot from the connected device and write the raw data to a file (experimental)',
'Capture a JS heap snapshot from the connected device and write the data to a file (experimental)',
)
.hidden()
.option('-f, --format <fmt>', 'heap snapshot format to request', () => {
if (!stores.hostConnections.appHost) return [];
return stores.hostConnections.appHost.host.getHeapSnapshotSupport()
.formats;
return [
...stores.hostConnections.appHost.host.getHeapSnapshotSupport()
.formats,
'v8',
];
})
.types({ string: ['f', 'format', 'path', 'uuid'] })
.action(async (args: HeapSnapshotArgs & { path?: string }) => {
.types({ string: ['f', 'format', 'path'] })
.action(async (args: vorpal.Args & { path?: string }) => {
const { appHost } = stores.hostConnections;
if (!appHost) {
cli.activeCommand.log('Not connected to a device');
return false;
}

const { appPackage } = stores.appContext;
if (!appPackage) {
cli.activeCommand.log(
'App package not loaded, use `install` or `set-app-package` commands first',
);
return false;
}

const { supported, formats } = appHost.host.getHeapSnapshotSupport();

const snapshotFormats = ['v8', ...formats];

if (!supported) {
cli.activeCommand.log(
'Device does not support capturing JS heap snapshots',
Expand All @@ -43,15 +57,14 @@ export default function install(stores: { hostConnections: HostConnections }) {
}

let { format } = args.options;

if (!format) {
if (formats.length === 0) {
if (snapshotFormats.length === 0) {
cli.activeCommand.log(
'Device does not support any heap snapshot formats',
);
return false;
}
if (formats.length === 1) {
if (snapshotFormats.length === 1) {
format = formats[0];
cli.activeCommand.log(
`Requesting a JS heap snapshot in ${JSON.stringify(
Expand All @@ -65,20 +78,39 @@ export default function install(stores: { hostConnections: HostConnections }) {
name: 'format',
message:
'Which format would you like the JS heap snapshot to be in?',
choices: formats,
choices: snapshotFormats,
})
).format;
}
}

const extension = format === 'v8' ? 'heapsnapshot' : 'bin';
const destPath = path.resolve(
args.path
? untildify(args.path)
: dateformat('"js-heap." yyyy-mm-dd.H.MM.ss."bin"'),
: dateformat(`"js-heap." yyyy-mm-dd.H.MM.ss."${extension}"`),
);

let sourceMaps: ComponentSourceMaps | undefined;
if (
appPackage.sourceMaps !== undefined &&
appPackage.sourceMaps.device
) {
const deviceFamily = compatibility.findCompatibleAppComponent(
appPackage,
appHost.host.info,
);
sourceMaps = appPackage.sourceMaps.device[deviceFamily];
}

try {
await captureHeapSnapshot(appHost.host, format, destPath, args.uuid);
await captureHeapSnapshot(
appHost.host,
format,
destPath,
appPackage.uuid,
sourceMaps,
);
cli.activeCommand.log(`JS heap snapshot saved to ${destPath}`);
return true;
} catch (ex) {
Expand Down
21 changes: 19 additions & 2 deletions packages/sdk-cli/src/models/captureHeapSnapshot.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
import { ComponentSourceMaps } from '@fitbit/app-package';
import { RemoteHost } from '@fitbit/fdb-debugger';
import { generateGraph, generateV8HeapSnapshot } from '@fitbit/memory-profiler';
import fsExtra from 'fs-extra';

export default async function captureHeapSnapshot(
host: RemoteHost,
format: string,
destPath: string,
uuid?: string,
sourceMaps?: ComponentSourceMaps,
) {
let v8Requested = false;
let actualFormat = format;

if (format === 'v8') {
v8Requested = true;
actualFormat = 'jerryscript-1';
}

if (!host.getHeapSnapshotSupport().supported) {
throw new Error('Connected device does not support heap snapshots');
}

const snapshot = await host.captureHeapSnapshot(format, uuid);
return fsExtra.writeFile(destPath, snapshot);
const snapshot = await host.captureHeapSnapshot(actualFormat, uuid);
if (!v8Requested) {
return fsExtra.writeFile(destPath, snapshot);
}

const graph = await generateGraph(snapshot, actualFormat, sourceMaps || {});
const v8Snapshot = generateV8HeapSnapshot(graph);
return fsExtra.writeFile(destPath, JSON.stringify(v8Snapshot));
}

0 comments on commit 457b521

Please sign in to comment.