From cfc8fe8f6e995be00129b7a43101dd6ed16e8c81 Mon Sep 17 00:00:00 2001 From: Marcin Kolny Date: Sat, 9 Feb 2019 02:08:51 +0000 Subject: [PATCH] Support importing partial JSON files (#202) Partial files are allowed in many specs, e.g. Trace Event Format, so the viewer should be able to load partial files as well. --- package.json | 3 +- .../trace-event/simple-object-partial.json | 7 ++ .../profiles/trace-event/simple-partial.json | 7 ++ .../__snapshots__/trace-event.test.ts.snap | 102 ++++++++++++++++++ src/import/index.ts | 27 ++++- src/import/partial-json-parser.d.ts | 1 + src/import/trace-event.test.ts | 8 ++ src/import/trace-event.ts | 4 - 8 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 sample/profiles/trace-event/simple-object-partial.json create mode 100644 sample/profiles/trace-event/simple-partial.json create mode 100644 src/import/partial-json-parser.d.ts diff --git a/package.json b/package.json index 7dd4241..23f1e6e 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,8 @@ "ts-jest": "22.4.6", "typescript": "2.8.1", "typescript-eslint-parser": "17.0.1", - "uglify-es": "3.2.2" + "uglify-es": "3.2.2", + "partial-json-parser": "1.0.3" }, "jest": { "transform": { diff --git a/sample/profiles/trace-event/simple-object-partial.json b/sample/profiles/trace-event/simple-object-partial.json new file mode 100644 index 0000000..b4fc409 --- /dev/null +++ b/sample/profiles/trace-event/simple-object-partial.json @@ -0,0 +1,7 @@ +{ + "traceEvents": [ + {"pid": 0, "tid": 0, "ph": "B", "name": "alpha", "ts": 0}, + {"pid": 0, "tid": 0, "ph": "B", "name": "beta", "ts": 1}, + {"pid": 0, "tid": 0, "ph": "X", "ts": 7, "tdur": 4}, + {"pid": 0, "tid": 0, "ph": "E", "name": "beta", "ts": 13}, + {"pid": 0, "tid": 0, "ph": "E", "name": "alpha", "ts": 14}, diff --git a/sample/profiles/trace-event/simple-partial.json b/sample/profiles/trace-event/simple-partial.json new file mode 100644 index 0000000..023e454 --- /dev/null +++ b/sample/profiles/trace-event/simple-partial.json @@ -0,0 +1,7 @@ +[ + {"pid": 0, "tid": 0, "ph": "B", "name": "alpha", "ts": 0}, + {"pid": 0, "tid": 0, "ph": "B", "name": "beta", "ts": 1}, + {"pid": 0, "tid": 0, "ph": "X", "name": "gamma", "ts": 2, "dur": 5, "args": {"detail": "foobar"}}, + {"pid": 0, "tid": 0, "ph": "X", "name": "epsilon", "ts": 7, "tdur": 4}, + {"pid": 0, "tid": 0, "ph": "E", "name": "beta", "ts": 13}, + {"pid": 0, "tid": 0, "ph": "E", "name": "alpha", "ts": 14}, diff --git a/src/import/__snapshots__/trace-event.test.ts.snap b/src/import/__snapshots__/trace-event.test.ts.snap index b208437..8efcd1c 100644 --- a/src/import/__snapshots__/trace-event.test.ts.snap +++ b/src/import/__snapshots__/trace-event.test.ts.snap @@ -258,10 +258,112 @@ Object { } `; +exports[`importTraceEvents simple object partial 1`] = ` +Object { + "frames": Array [ + Frame { + "col": undefined, + "file": undefined, + "key": "alpha", + "line": undefined, + "name": "alpha", + "selfWeight": 2, + "totalWeight": 14, + }, + Frame { + "col": undefined, + "file": undefined, + "key": "beta", + "line": undefined, + "name": "beta", + "selfWeight": 8, + "totalWeight": 12, + }, + Frame { + "col": undefined, + "file": undefined, + "key": "(unnamed)", + "line": undefined, + "name": "(unnamed)", + "selfWeight": 4, + "totalWeight": 4, + }, + ], + "name": "pid 0, tid 0", + "stacks": Array [ + "alpha 1.00µs", + "alpha;beta 6.00µs", + "alpha;beta;(unnamed) 4.00µs", + "alpha;beta 2.00µs", + "alpha 1.00µs", + ], +} +`; + +exports[`importTraceEvents simple object partial: indexToView 1`] = `0`; + +exports[`importTraceEvents simple object partial: profileGroup.name 1`] = `"simple-object-partial.json"`; + exports[`importTraceEvents simple object: indexToView 1`] = `0`; exports[`importTraceEvents simple object: profileGroup.name 1`] = `"simple-object.json"`; +exports[`importTraceEvents simple partial 1`] = ` +Object { + "frames": Array [ + Frame { + "col": undefined, + "file": undefined, + "key": "alpha", + "line": undefined, + "name": "alpha", + "selfWeight": 2, + "totalWeight": 14, + }, + Frame { + "col": undefined, + "file": undefined, + "key": "beta", + "line": undefined, + "name": "beta", + "selfWeight": 3, + "totalWeight": 12, + }, + Frame { + "col": undefined, + "file": undefined, + "key": "gamma {\\"detail\\":\\"foobar\\"}", + "line": undefined, + "name": "gamma {\\"detail\\":\\"foobar\\"}", + "selfWeight": 5, + "totalWeight": 5, + }, + Frame { + "col": undefined, + "file": undefined, + "key": "epsilon", + "line": undefined, + "name": "epsilon", + "selfWeight": 4, + "totalWeight": 4, + }, + ], + "name": "pid 0, tid 0", + "stacks": Array [ + "alpha 1.00µs", + "alpha;beta 1.00µs", + "alpha;beta;gamma {\\"detail\\":\\"foobar\\"} 5.00µs", + "alpha;beta;epsilon 4.00µs", + "alpha;beta 2.00µs", + "alpha 1.00µs", + ], +} +`; + +exports[`importTraceEvents simple partial: indexToView 1`] = `0`; + +exports[`importTraceEvents simple partial: profileGroup.name 1`] = `"simple-partial.json"`; + exports[`importTraceEvents simple: indexToView 1`] = `0`; exports[`importTraceEvents simple: profileGroup.name 1`] = `"simple.json"`; diff --git a/src/import/index.ts b/src/import/index.ts index 77aadff..a257633 100644 --- a/src/import/index.ts +++ b/src/import/index.ts @@ -1,6 +1,8 @@ import {Profile, ProfileGroup} from '../lib/profile' import {FileSystemDirectoryEntry} from './file-system-entry' +import {partialParse} from 'partial-json-parser' + import { importFromChromeCPUProfile, importFromChromeTimeline, @@ -71,6 +73,14 @@ function toGroup(profile: Profile | null): ProfileGroup | null { return {name: profile.getName(), indexToView: 0, profiles: [profile]} } +function tryImportTraceEvents(parsed: any): ProfileGroup | null { + if (isTraceEventFormatted(parsed)) { + console.log('Importing as Trace Event Format profile') + return importTraceEvents(parsed) + } + return null +} + async function _importProfileGroup(dataSource: ProfileDataSource): Promise { const fileName = await dataSource.name() @@ -115,6 +125,7 @@ async function _importProfileGroup(dataSource: ProfileDataSource): Promise { test('importTraceEvents multiprocess', async () => { await checkProfileSnapshot('./sample/profiles/trace-event/multiprocess.json') }) + +test('importTraceEvents simple partial', async () => { + await checkProfileSnapshot('./sample/profiles/trace-event/simple-partial.json') +}) + +test('importTraceEvents simple object partial', async () => { + await checkProfileSnapshot('./sample/profiles/trace-event/simple-object-partial.json') +}) diff --git a/src/import/trace-event.ts b/src/import/trace-event.ts index eb24f7d..c262902 100644 --- a/src/import/trace-event.ts +++ b/src/import/trace-event.ts @@ -288,10 +288,6 @@ export function isTraceEventFormatted( // We're only going to suppor the JSON formatted profiles for now. // The spec also discusses support for data embedded in ftrace supported data: https://lwn.net/Articles/365835/. - // TODO(jlfwong): The spec also specifies that it's valid for the trace to not contain a terminating `]`. - // That complicates things a bit for us, so let's just ignore that for now until someone writes in with a - // bug report from real data. - return isTraceEventObject(rawProfile) || isTraceEventList(rawProfile) }