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.
This commit is contained in:
@@ -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": {
|
||||
|
||||
7
sample/profiles/trace-event/simple-object-partial.json
Normal file
7
sample/profiles/trace-event/simple-object-partial.json
Normal file
@@ -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},
|
||||
7
sample/profiles/trace-event/simple-partial.json
Normal file
7
sample/profiles/trace-event/simple-partial.json
Normal file
@@ -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},
|
||||
@@ -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"`;
|
||||
|
||||
@@ -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<ProfileGroup | null> {
|
||||
const fileName = await dataSource.name()
|
||||
|
||||
@@ -115,6 +125,7 @@ async function _importProfileGroup(dataSource: ProfileDataSource): Promise<Profi
|
||||
|
||||
// Second pass: Try to guess what file format it is based on structure
|
||||
let parsed: any
|
||||
let profileGroup: ProfileGroup | null
|
||||
try {
|
||||
parsed = JSON.parse(contents)
|
||||
} catch (e) {}
|
||||
@@ -131,9 +142,8 @@ async function _importProfileGroup(dataSource: ProfileDataSource): Promise<Profi
|
||||
} else if ('nodes' in parsed && 'samples' in parsed && 'timeDeltas' in parsed) {
|
||||
console.log('Importing as Chrome CPU Profile')
|
||||
return toGroup(importFromChromeCPUProfile(parsed))
|
||||
} else if (isTraceEventFormatted(parsed)) {
|
||||
console.log('Importing as Trace Event Format profile')
|
||||
return importTraceEvents(parsed)
|
||||
} else if ((profileGroup = tryImportTraceEvents(parsed))) {
|
||||
return profileGroup
|
||||
} else if ('head' in parsed && 'samples' in parsed && 'timestamps' in parsed) {
|
||||
console.log('Importing as Chrome CPU Profile (old format)')
|
||||
return toGroup(importFromOldV8CPUProfile(parsed))
|
||||
@@ -175,6 +185,17 @@ async function _importProfileGroup(dataSource: ProfileDataSource): Promise<Profi
|
||||
}
|
||||
}
|
||||
|
||||
// Third pass: try parsing partial JSON file, as some of the formats,
|
||||
// e.g. Trace Event Format, allow partial files.
|
||||
try {
|
||||
parsed = partialParse(contents)
|
||||
} catch {}
|
||||
if (parsed) {
|
||||
if ((profileGroup = tryImportTraceEvents(parsed))) {
|
||||
return profileGroup
|
||||
}
|
||||
}
|
||||
|
||||
// Unrecognized format
|
||||
return null
|
||||
}
|
||||
|
||||
1
src/import/partial-json-parser.d.ts
vendored
Normal file
1
src/import/partial-json-parser.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
declare module 'partial-json-parser'
|
||||
@@ -11,3 +11,11 @@ test('importTraceEvents simple object', async () => {
|
||||
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')
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user