diff --git a/src/__tests__/__snapshots__/profiling-test.js.snap b/src/__tests__/__snapshots__/profiling-test.js.snap
deleted file mode 100644
index 314e152dc4..0000000000
--- a/src/__tests__/__snapshots__/profiling-test.js.snap
+++ /dev/null
@@ -1,1106 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`profiling CommitDetails should be collected for each commit: CommitDetails commitIndex: 0 1`] = `
-Object {
- "actualDurations": Map {
- 1 => 12,
- 2 => 12,
- 3 => 0,
- 4 => 1,
- 5 => 1,
- },
- "commitIndex": 0,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 1 => 0,
- 2 => 10,
- 3 => 0,
- 4 => 1,
- 5 => 1,
- },
-}
-`;
-
-exports[`profiling CommitDetails should be collected for each commit: CommitDetails commitIndex: 1 1`] = `
-Object {
- "actualDurations": Map {
- 3 => 0,
- 4 => 1,
- 6 => 2,
- 2 => 13,
- 1 => 13,
- },
- "commitIndex": 1,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 3 => 0,
- 4 => 1,
- 6 => 2,
- 2 => 10,
- 1 => 0,
- },
-}
-`;
-
-exports[`profiling CommitDetails should be collected for each commit: CommitDetails commitIndex: 2 1`] = `
-Object {
- "actualDurations": Map {
- 3 => 0,
- 2 => 10,
- 1 => 10,
- },
- "commitIndex": 2,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 3 => 0,
- 2 => 10,
- 1 => 0,
- },
-}
-`;
-
-exports[`profiling CommitDetails should be collected for each commit: CommitDetails commitIndex: 3 1`] = `
-Object {
- "actualDurations": Map {
- 2 => 10,
- 1 => 10,
- },
- "commitIndex": 3,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 2 => 10,
- 1 => 0,
- },
-}
-`;
-
-exports[`profiling CommitDetails should be collected for each commit: imported data 1`] = `
-Object {
- "commitDetails": Array [
- Object {
- "actualDurations": Map {
- 1 => 12,
- 2 => 12,
- 3 => 0,
- 4 => 1,
- 5 => 1,
- },
- "commitIndex": 0,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 1 => 0,
- 2 => 10,
- 3 => 0,
- 4 => 1,
- 5 => 1,
- },
- },
- Object {
- "actualDurations": Map {
- 3 => 0,
- 4 => 1,
- 6 => 2,
- 2 => 13,
- 1 => 13,
- },
- "commitIndex": 1,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 3 => 0,
- 4 => 1,
- 6 => 2,
- 2 => 10,
- 1 => 0,
- },
- },
- Object {
- "actualDurations": Map {
- 3 => 0,
- 2 => 10,
- 1 => 10,
- },
- "commitIndex": 2,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 3 => 0,
- 2 => 10,
- 1 => 0,
- },
- },
- Object {
- "actualDurations": Map {
- 2 => 10,
- 1 => 10,
- },
- "commitIndex": 3,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 2 => 10,
- 1 => 0,
- },
- },
- ],
- "interactions": Object {
- "interactions": Array [],
- "rootID": 1,
- },
- "profilingOperations": Map {
- 1 => Array [
- Uint32Array [
- 1,
- 1,
- 17,
- 6,
- 80,
- 97,
- 114,
- 101,
- 110,
- 116,
- 5,
- 67,
- 104,
- 105,
- 108,
- 100,
- 1,
- 48,
- 1,
- 49,
- 1,
- 1,
- 11,
- 1,
- 1,
- 4,
- 1,
- 12000,
- 1,
- 2,
- 5,
- 1,
- 0,
- 1,
- 0,
- 4,
- 2,
- 12000,
- 1,
- 3,
- 5,
- 2,
- 2,
- 2,
- 3,
- 4,
- 3,
- 0,
- 1,
- 4,
- 5,
- 2,
- 2,
- 2,
- 4,
- 4,
- 4,
- 1000,
- 1,
- 5,
- 8,
- 2,
- 2,
- 2,
- 0,
- 4,
- 5,
- 1000,
- ],
- Uint32Array [
- 1,
- 1,
- 8,
- 5,
- 67,
- 104,
- 105,
- 108,
- 100,
- 1,
- 50,
- 1,
- 6,
- 5,
- 2,
- 2,
- 1,
- 2,
- 4,
- 6,
- 2000,
- 4,
- 2,
- 14000,
- 3,
- 2,
- 4,
- 3,
- 4,
- 6,
- 5,
- 4,
- 1,
- 14000,
- ],
- Uint32Array [
- 1,
- 1,
- 0,
- 2,
- 2,
- 6,
- 4,
- 4,
- 2,
- 11000,
- 3,
- 2,
- 2,
- 3,
- 5,
- 4,
- 1,
- 11000,
- ],
- Uint32Array [
- 1,
- 1,
- 0,
- 2,
- 1,
- 3,
- ],
- ],
- },
- "profilingSnapshots": Map {
- 1 => Map {},
- },
- "profilingSummary": Object {
- "commitDurations": Array [
- 12,
- 13,
- 10,
- 10,
- ],
- "commitTimes": Array [
- 12,
- 25,
- 35,
- 45,
- ],
- "initialTreeBaseDurations": Map {},
- "interactionCount": 0,
- "rootID": 1,
- },
- "version": 3,
-}
-`;
-
-exports[`profiling CommitDetails should calculate a self duration based on actual children (not filtered children): CommitDetails with filtered self durations 1`] = `
-Object {
- "actualDurations": Map {
- 1 => 16,
- 2 => 16,
- 3 => 1,
- 5 => 1,
- },
- "commitIndex": 0,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 1 => 0,
- 2 => 10,
- 3 => 1,
- 5 => 1,
- },
-}
-`;
-
-exports[`profiling CommitDetails should calculate self duration correctly for suspended views: CommitDetails with filtered self durations 1`] = `
-Object {
- "actualDurations": Map {
- 1 => 15,
- 2 => 15,
- 3 => 5,
- 4 => 2,
- },
- "commitIndex": 0,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 1 => 0,
- 2 => 10,
- 3 => 3,
- 4 => 2,
- },
-}
-`;
-
-exports[`profiling CommitDetails should calculate self duration correctly for suspended views: CommitDetails with filtered self durations 2`] = `
-Object {
- "actualDurations": Map {
- 5 => 3,
- 3 => 3,
- },
- "commitIndex": 1,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 5 => 3,
- 3 => 0,
- },
-}
-`;
-
-exports[`profiling FiberCommits should be collected for each rendered fiber: FiberCommits: element 2 1`] = `
-Object {
- "commitDurations": Array [
- 0,
- 10,
- 1,
- 10,
- 2,
- 10,
- ],
- "fiberID": 2,
- "rootID": 1,
-}
-`;
-
-exports[`profiling FiberCommits should be collected for each rendered fiber: FiberCommits: element 3 1`] = `
-Object {
- "commitDurations": Array [
- 0,
- 0,
- 1,
- 0,
- 2,
- 0,
- ],
- "fiberID": 3,
- "rootID": 1,
-}
-`;
-
-exports[`profiling FiberCommits should be collected for each rendered fiber: FiberCommits: element 4 1`] = `
-Object {
- "commitDurations": Array [
- 0,
- 1,
- ],
- "fiberID": 4,
- "rootID": 1,
-}
-`;
-
-exports[`profiling FiberCommits should be collected for each rendered fiber: FiberCommits: element 5 1`] = `
-Object {
- "commitDurations": Array [
- 1,
- 1,
- 2,
- 1,
- ],
- "fiberID": 5,
- "rootID": 1,
-}
-`;
-
-exports[`profiling FiberCommits should be collected for each rendered fiber: FiberCommits: element 6 1`] = `
-Object {
- "commitDurations": Array [
- 2,
- 2,
- ],
- "fiberID": 6,
- "rootID": 1,
-}
-`;
-
-exports[`profiling FiberCommits should be collected for each rendered fiber: imported data 1`] = `
-Object {
- "commitDetails": Array [
- Object {
- "actualDurations": Map {
- 1 => 11,
- 2 => 11,
- 3 => 0,
- 4 => 1,
- },
- "commitIndex": 0,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 1 => 0,
- 2 => 10,
- 3 => 0,
- 4 => 1,
- },
- },
- Object {
- "actualDurations": Map {
- 3 => 0,
- 5 => 1,
- 2 => 11,
- 1 => 11,
- },
- "commitIndex": 1,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 3 => 0,
- 5 => 1,
- 2 => 10,
- 1 => 0,
- },
- },
- Object {
- "actualDurations": Map {
- 3 => 0,
- 5 => 1,
- 6 => 2,
- 2 => 13,
- 1 => 13,
- },
- "commitIndex": 2,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 3 => 0,
- 5 => 1,
- 6 => 2,
- 2 => 10,
- 1 => 0,
- },
- },
- ],
- "interactions": Object {
- "interactions": Array [],
- "rootID": 1,
- },
- "profilingOperations": Map {
- 1 => Array [
- Uint32Array [
- 1,
- 1,
- 15,
- 6,
- 80,
- 97,
- 114,
- 101,
- 110,
- 116,
- 5,
- 67,
- 104,
- 105,
- 108,
- 100,
- 1,
- 48,
- 1,
- 1,
- 11,
- 1,
- 1,
- 4,
- 1,
- 11000,
- 1,
- 2,
- 5,
- 1,
- 0,
- 1,
- 0,
- 4,
- 2,
- 11000,
- 1,
- 3,
- 5,
- 2,
- 2,
- 2,
- 3,
- 4,
- 3,
- 0,
- 1,
- 4,
- 8,
- 2,
- 2,
- 2,
- 0,
- 4,
- 4,
- 1000,
- ],
- Uint32Array [
- 1,
- 1,
- 8,
- 5,
- 67,
- 104,
- 105,
- 108,
- 100,
- 1,
- 49,
- 1,
- 5,
- 5,
- 2,
- 2,
- 1,
- 2,
- 4,
- 5,
- 1000,
- 4,
- 2,
- 12000,
- 3,
- 2,
- 3,
- 3,
- 5,
- 4,
- 4,
- 1,
- 12000,
- ],
- Uint32Array [
- 1,
- 1,
- 8,
- 5,
- 67,
- 104,
- 105,
- 108,
- 100,
- 1,
- 50,
- 1,
- 6,
- 5,
- 2,
- 2,
- 1,
- 2,
- 4,
- 6,
- 2000,
- 4,
- 2,
- 14000,
- 3,
- 2,
- 4,
- 3,
- 5,
- 6,
- 4,
- 4,
- 1,
- 14000,
- ],
- ],
- },
- "profilingSnapshots": Map {
- 1 => Map {},
- },
- "profilingSummary": Object {
- "commitDurations": Array [
- 11,
- 11,
- 13,
- ],
- "commitTimes": Array [
- 11,
- 22,
- 35,
- ],
- "initialTreeBaseDurations": Map {},
- "interactionCount": 0,
- "rootID": 1,
- },
- "version": 3,
-}
-`;
-
-exports[`profiling Interactions should be collected for every traced interaction: Interactions 1`] = `
-Object {
- "interactions": Array [
- Object {
- "__count": 1,
- "commits": Array [
- 0,
- ],
- "id": 0,
- "name": "mount: one child",
- "timestamp": 0,
- },
- Object {
- "__count": 0,
- "commits": Array [
- 1,
- ],
- "id": 1,
- "name": "update: two children",
- "timestamp": 11,
- },
- ],
- "rootID": 1,
-}
-`;
-
-exports[`profiling Interactions should be collected for every traced interaction: imported data 1`] = `
-Object {
- "commitDetails": Array [
- Object {
- "actualDurations": Map {
- 1 => 11,
- 2 => 11,
- 3 => 0,
- 4 => 1,
- },
- "commitIndex": 0,
- "interactions": Array [
- Object {
- "__count": 1,
- "id": 0,
- "name": "mount: one child",
- "timestamp": 0,
- },
- ],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 1 => 0,
- 2 => 10,
- 3 => 0,
- 4 => 1,
- },
- },
- Object {
- "actualDurations": Map {
- 3 => 0,
- 5 => 1,
- 2 => 11,
- 1 => 11,
- },
- "commitIndex": 1,
- "interactions": Array [
- Object {
- "__count": 0,
- "id": 1,
- "name": "update: two children",
- "timestamp": 11,
- },
- ],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 3 => 0,
- 5 => 1,
- 2 => 10,
- 1 => 0,
- },
- },
- ],
- "interactions": Object {
- "interactions": Array [
- Object {
- "__count": 1,
- "commits": Array [
- 0,
- ],
- "id": 0,
- "name": "mount: one child",
- "timestamp": 0,
- },
- Object {
- "__count": 0,
- "commits": Array [
- 1,
- ],
- "id": 1,
- "name": "update: two children",
- "timestamp": 11,
- },
- ],
- "rootID": 1,
- },
- "profilingOperations": Map {
- 1 => Array [
- Uint32Array [
- 1,
- 1,
- 15,
- 6,
- 80,
- 97,
- 114,
- 101,
- 110,
- 116,
- 5,
- 67,
- 104,
- 105,
- 108,
- 100,
- 1,
- 48,
- 1,
- 1,
- 11,
- 1,
- 1,
- 4,
- 1,
- 11000,
- 1,
- 2,
- 5,
- 1,
- 0,
- 1,
- 0,
- 4,
- 2,
- 11000,
- 1,
- 3,
- 5,
- 2,
- 2,
- 2,
- 3,
- 4,
- 3,
- 0,
- 1,
- 4,
- 8,
- 2,
- 2,
- 2,
- 0,
- 4,
- 4,
- 1000,
- ],
- Uint32Array [
- 1,
- 1,
- 8,
- 5,
- 67,
- 104,
- 105,
- 108,
- 100,
- 1,
- 49,
- 1,
- 5,
- 5,
- 2,
- 2,
- 1,
- 2,
- 4,
- 5,
- 1000,
- 4,
- 2,
- 12000,
- 3,
- 2,
- 3,
- 3,
- 5,
- 4,
- 4,
- 1,
- 12000,
- ],
- ],
- },
- "profilingSnapshots": Map {
- 1 => Map {},
- },
- "profilingSummary": Object {
- "commitDurations": Array [
- 11,
- 11,
- ],
- "commitTimes": Array [
- 11,
- 22,
- ],
- "initialTreeBaseDurations": Map {},
- "interactionCount": 2,
- "rootID": 1,
- },
- "version": 3,
-}
-`;
-
-exports[`profiling ProfilingSummary should be collected for each commit: ProfilingSummary 1`] = `
-Object {
- "commitDurations": Array [
- 13,
- 10,
- 10,
- ],
- "commitTimes": Array [
- 13,
- 23,
- 33,
- ],
- "initialTreeBaseDurations": Map {
- 1 => 12,
- 2 => 12,
- 3 => 0,
- 4 => 1,
- 5 => 1,
- },
- "interactionCount": 0,
- "rootID": 1,
-}
-`;
-
-exports[`profiling ProfilingSummary should be collected for each commit: imported data 1`] = `
-Object {
- "commitDetails": Array [
- Object {
- "actualDurations": Map {
- 3 => 0,
- 4 => 1,
- 6 => 2,
- 2 => 13,
- 1 => 13,
- },
- "commitIndex": 0,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 3 => 0,
- 4 => 1,
- 6 => 2,
- 2 => 10,
- 1 => 0,
- },
- },
- Object {
- "actualDurations": Map {
- 3 => 0,
- 2 => 10,
- 1 => 10,
- },
- "commitIndex": 1,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 3 => 0,
- 2 => 10,
- 1 => 0,
- },
- },
- Object {
- "actualDurations": Map {
- 2 => 10,
- 1 => 10,
- },
- "commitIndex": 2,
- "interactions": Array [],
- "priorityLevel": "Immediate",
- "rootID": 1,
- "selfDurations": Map {
- 2 => 10,
- 1 => 0,
- },
- },
- ],
- "interactions": Object {
- "interactions": Array [],
- "rootID": 1,
- },
- "profilingOperations": Map {
- 1 => Array [
- Uint32Array [
- 1,
- 1,
- 8,
- 5,
- 67,
- 104,
- 105,
- 108,
- 100,
- 1,
- 50,
- 1,
- 6,
- 5,
- 2,
- 2,
- 1,
- 2,
- 4,
- 6,
- 2000,
- 4,
- 2,
- 14000,
- 3,
- 2,
- 4,
- 3,
- 4,
- 6,
- 5,
- 4,
- 1,
- 14000,
- ],
- Uint32Array [
- 1,
- 1,
- 0,
- 2,
- 2,
- 6,
- 4,
- 4,
- 2,
- 11000,
- 3,
- 2,
- 2,
- 3,
- 5,
- 4,
- 1,
- 11000,
- ],
- Uint32Array [
- 1,
- 1,
- 0,
- 2,
- 1,
- 3,
- ],
- ],
- },
- "profilingSnapshots": Map {
- 1 => Map {
- 1 => Object {
- "children": Array [
- 2,
- ],
- "displayName": null,
- "id": 1,
- "key": null,
- "type": 11,
- },
- 2 => Object {
- "children": Array [
- 3,
- 4,
- 5,
- ],
- "displayName": "Parent",
- "id": 2,
- "key": null,
- "type": 5,
- },
- 3 => Object {
- "children": Array [],
- "displayName": "Child",
- "id": 3,
- "key": "0",
- "type": 5,
- },
- 4 => Object {
- "children": Array [],
- "displayName": "Child",
- "id": 4,
- "key": "1",
- "type": 5,
- },
- 5 => Object {
- "children": Array [],
- "displayName": "Child",
- "id": 5,
- "key": null,
- "type": 8,
- },
- },
- },
- "profilingSummary": Object {
- "commitDurations": Array [
- 13,
- 10,
- 10,
- ],
- "commitTimes": Array [
- 13,
- 23,
- 33,
- ],
- "initialTreeBaseDurations": Map {
- 1 => 12,
- 2 => 12,
- 3 => 0,
- 4 => 1,
- 5 => 1,
- },
- "interactionCount": 0,
- "rootID": 1,
- },
- "version": 3,
-}
-`;
diff --git a/src/__tests__/__snapshots__/profilingCache-test.js.snap b/src/__tests__/__snapshots__/profilingCache-test.js.snap
new file mode 100644
index 0000000000..a6f2d1c4d6
--- /dev/null
+++ b/src/__tests__/__snapshots__/profilingCache-test.js.snap
@@ -0,0 +1,1495 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ProfilingCache should calculate a self duration based on actual children (not filtered children): CommitDetails with filtered self durations 1`] = `
+Object {
+ "duration": 16,
+ "fiberActualDurations": Map {
+ 1 => 16,
+ 2 => 16,
+ 3 => 1,
+ 5 => 1,
+ },
+ "fiberSelfDurations": Map {
+ 1 => 0,
+ 2 => 10,
+ 3 => 1,
+ 5 => 1,
+ },
+ "interactionIDs": Array [],
+ "priorityLevel": "Immediate",
+ "screenshot": null,
+ "timestamp": 16,
+}
+`;
+
+exports[`ProfilingCache should calculate self duration correctly for suspended views: CommitDetails with filtered self durations 1`] = `
+Object {
+ "duration": 15,
+ "fiberActualDurations": Map {
+ 1 => 15,
+ 2 => 15,
+ 3 => 5,
+ 4 => 2,
+ },
+ "fiberSelfDurations": Map {
+ 1 => 0,
+ 2 => 10,
+ 3 => 3,
+ 4 => 2,
+ },
+ "interactionIDs": Array [],
+ "priorityLevel": "Immediate",
+ "screenshot": null,
+ "timestamp": 15,
+}
+`;
+
+exports[`ProfilingCache should calculate self duration correctly for suspended views: CommitDetails with filtered self durations 2`] = `
+Object {
+ "duration": 3,
+ "fiberActualDurations": Map {
+ 5 => 3,
+ 3 => 3,
+ },
+ "fiberSelfDurations": Map {
+ 5 => 3,
+ 3 => 0,
+ },
+ "interactionIDs": Array [],
+ "priorityLevel": "Immediate",
+ "screenshot": null,
+ "timestamp": 18,
+}
+`;
+
+exports[`ProfilingCache should collect data for each commit: CommitDetails commitIndex: 0 1`] = `
+Object {
+ "duration": 12,
+ "fiberActualDurations": Map {
+ 1 => 12,
+ 2 => 12,
+ 3 => 0,
+ 4 => 1,
+ 5 => 1,
+ },
+ "fiberSelfDurations": Map {
+ 1 => 0,
+ 2 => 10,
+ 3 => 0,
+ 4 => 1,
+ 5 => 1,
+ },
+ "interactionIDs": Array [],
+ "priorityLevel": "Immediate",
+ "screenshot": null,
+ "timestamp": 12,
+}
+`;
+
+exports[`ProfilingCache should collect data for each commit: CommitDetails commitIndex: 1 1`] = `
+Object {
+ "duration": 13,
+ "fiberActualDurations": Map {
+ 3 => 0,
+ 4 => 1,
+ 6 => 2,
+ 2 => 13,
+ 1 => 13,
+ },
+ "fiberSelfDurations": Map {
+ 3 => 0,
+ 4 => 1,
+ 6 => 2,
+ 2 => 10,
+ 1 => 0,
+ },
+ "interactionIDs": Array [],
+ "priorityLevel": "Immediate",
+ "screenshot": null,
+ "timestamp": 25,
+}
+`;
+
+exports[`ProfilingCache should collect data for each commit: CommitDetails commitIndex: 2 1`] = `
+Object {
+ "duration": 10,
+ "fiberActualDurations": Map {
+ 3 => 0,
+ 2 => 10,
+ 1 => 10,
+ },
+ "fiberSelfDurations": Map {
+ 3 => 0,
+ 2 => 10,
+ 1 => 0,
+ },
+ "interactionIDs": Array [],
+ "priorityLevel": "Immediate",
+ "screenshot": null,
+ "timestamp": 35,
+}
+`;
+
+exports[`ProfilingCache should collect data for each commit: CommitDetails commitIndex: 3 1`] = `
+Object {
+ "duration": 10,
+ "fiberActualDurations": Map {
+ 2 => 10,
+ 1 => 10,
+ },
+ "fiberSelfDurations": Map {
+ 2 => 10,
+ 1 => 0,
+ },
+ "interactionIDs": Array [],
+ "priorityLevel": "Immediate",
+ "screenshot": null,
+ "timestamp": 45,
+}
+`;
+
+exports[`ProfilingCache should collect data for each commit: imported data 1`] = `
+"{
+ \\"version\\": 5,
+ \\"dataForRoots\\": [
+ {
+ \\"commitData\\": [
+ {
+ \\"duration\\": 12,
+ \\"fiberActualDurations\\": [
+ [
+ 1,
+ 12
+ ],
+ [
+ 2,
+ 12
+ ],
+ [
+ 3,
+ 0
+ ],
+ [
+ 4,
+ 1
+ ],
+ [
+ 5,
+ 1
+ ]
+ ],
+ \\"fiberSelfDurations\\": [
+ [
+ 1,
+ 0
+ ],
+ [
+ 2,
+ 10
+ ],
+ [
+ 3,
+ 0
+ ],
+ [
+ 4,
+ 1
+ ],
+ [
+ 5,
+ 1
+ ]
+ ],
+ \\"interactionIDs\\": [],
+ \\"priorityLevel\\": \\"Immediate\\",
+ \\"screenshot\\": null,
+ \\"timestamp\\": 12
+ },
+ {
+ \\"duration\\": 13,
+ \\"fiberActualDurations\\": [
+ [
+ 3,
+ 0
+ ],
+ [
+ 4,
+ 1
+ ],
+ [
+ 6,
+ 2
+ ],
+ [
+ 2,
+ 13
+ ],
+ [
+ 1,
+ 13
+ ]
+ ],
+ \\"fiberSelfDurations\\": [
+ [
+ 3,
+ 0
+ ],
+ [
+ 4,
+ 1
+ ],
+ [
+ 6,
+ 2
+ ],
+ [
+ 2,
+ 10
+ ],
+ [
+ 1,
+ 0
+ ]
+ ],
+ \\"interactionIDs\\": [],
+ \\"priorityLevel\\": \\"Immediate\\",
+ \\"screenshot\\": null,
+ \\"timestamp\\": 25
+ },
+ {
+ \\"duration\\": 10,
+ \\"fiberActualDurations\\": [
+ [
+ 3,
+ 0
+ ],
+ [
+ 2,
+ 10
+ ],
+ [
+ 1,
+ 10
+ ]
+ ],
+ \\"fiberSelfDurations\\": [
+ [
+ 3,
+ 0
+ ],
+ [
+ 2,
+ 10
+ ],
+ [
+ 1,
+ 0
+ ]
+ ],
+ \\"interactionIDs\\": [],
+ \\"priorityLevel\\": \\"Immediate\\",
+ \\"screenshot\\": null,
+ \\"timestamp\\": 35
+ },
+ {
+ \\"duration\\": 10,
+ \\"fiberActualDurations\\": [
+ [
+ 2,
+ 10
+ ],
+ [
+ 1,
+ 10
+ ]
+ ],
+ \\"fiberSelfDurations\\": [
+ [
+ 2,
+ 10
+ ],
+ [
+ 1,
+ 0
+ ]
+ ],
+ \\"interactionIDs\\": [],
+ \\"priorityLevel\\": \\"Immediate\\",
+ \\"screenshot\\": null,
+ \\"timestamp\\": 45
+ }
+ ],
+ \\"displayName\\": \\"Parent\\",
+ \\"initialTreeBaseDurations\\": [],
+ \\"interactionCommits\\": [],
+ \\"interactions\\": [],
+ \\"operations\\": [
+ [
+ 1,
+ 1,
+ 17,
+ 6,
+ 80,
+ 97,
+ 114,
+ 101,
+ 110,
+ 116,
+ 5,
+ 67,
+ 104,
+ 105,
+ 108,
+ 100,
+ 1,
+ 48,
+ 1,
+ 49,
+ 1,
+ 1,
+ 11,
+ 1,
+ 1,
+ 4,
+ 1,
+ 12000,
+ 1,
+ 2,
+ 5,
+ 1,
+ 0,
+ 1,
+ 0,
+ 4,
+ 2,
+ 12000,
+ 1,
+ 3,
+ 5,
+ 2,
+ 2,
+ 2,
+ 3,
+ 4,
+ 3,
+ 0,
+ 1,
+ 4,
+ 5,
+ 2,
+ 2,
+ 2,
+ 4,
+ 4,
+ 4,
+ 1000,
+ 1,
+ 5,
+ 8,
+ 2,
+ 2,
+ 2,
+ 0,
+ 4,
+ 5,
+ 1000
+ ],
+ [
+ 1,
+ 1,
+ 8,
+ 5,
+ 67,
+ 104,
+ 105,
+ 108,
+ 100,
+ 1,
+ 50,
+ 1,
+ 6,
+ 5,
+ 2,
+ 2,
+ 1,
+ 2,
+ 4,
+ 6,
+ 2000,
+ 4,
+ 2,
+ 14000,
+ 3,
+ 2,
+ 4,
+ 3,
+ 4,
+ 6,
+ 5,
+ 4,
+ 1,
+ 14000
+ ],
+ [
+ 1,
+ 1,
+ 0,
+ 2,
+ 2,
+ 6,
+ 4,
+ 4,
+ 2,
+ 11000,
+ 3,
+ 2,
+ 2,
+ 3,
+ 5,
+ 4,
+ 1,
+ 11000
+ ],
+ [
+ 1,
+ 1,
+ 0,
+ 2,
+ 1,
+ 3
+ ]
+ ],
+ \\"rootID\\": 1,
+ \\"snapshots\\": []
+ }
+ ]
+}"
+`;
+
+exports[`ProfilingCache should collect data for each rendered fiber: FiberCommits: element 2 1`] = `
+Array [
+ 0,
+ 1,
+ 2,
+]
+`;
+
+exports[`ProfilingCache should collect data for each rendered fiber: FiberCommits: element 3 1`] = `
+Array [
+ 0,
+ 1,
+ 2,
+]
+`;
+
+exports[`ProfilingCache should collect data for each rendered fiber: FiberCommits: element 4 1`] = `
+Array [
+ 0,
+]
+`;
+
+exports[`ProfilingCache should collect data for each rendered fiber: FiberCommits: element 5 1`] = `
+Array [
+ 1,
+ 2,
+]
+`;
+
+exports[`ProfilingCache should collect data for each rendered fiber: FiberCommits: element 6 1`] = `
+Array [
+ 2,
+]
+`;
+
+exports[`ProfilingCache should collect data for each rendered fiber: imported data 1`] = `
+"{
+ \\"version\\": 5,
+ \\"dataForRoots\\": [
+ {
+ \\"commitData\\": [
+ {
+ \\"duration\\": 11,
+ \\"fiberActualDurations\\": [
+ [
+ 1,
+ 11
+ ],
+ [
+ 2,
+ 11
+ ],
+ [
+ 3,
+ 0
+ ],
+ [
+ 4,
+ 1
+ ]
+ ],
+ \\"fiberSelfDurations\\": [
+ [
+ 1,
+ 0
+ ],
+ [
+ 2,
+ 10
+ ],
+ [
+ 3,
+ 0
+ ],
+ [
+ 4,
+ 1
+ ]
+ ],
+ \\"interactionIDs\\": [],
+ \\"priorityLevel\\": \\"Immediate\\",
+ \\"screenshot\\": null,
+ \\"timestamp\\": 11
+ },
+ {
+ \\"duration\\": 11,
+ \\"fiberActualDurations\\": [
+ [
+ 3,
+ 0
+ ],
+ [
+ 5,
+ 1
+ ],
+ [
+ 2,
+ 11
+ ],
+ [
+ 1,
+ 11
+ ]
+ ],
+ \\"fiberSelfDurations\\": [
+ [
+ 3,
+ 0
+ ],
+ [
+ 5,
+ 1
+ ],
+ [
+ 2,
+ 10
+ ],
+ [
+ 1,
+ 0
+ ]
+ ],
+ \\"interactionIDs\\": [],
+ \\"priorityLevel\\": \\"Immediate\\",
+ \\"screenshot\\": null,
+ \\"timestamp\\": 22
+ },
+ {
+ \\"duration\\": 13,
+ \\"fiberActualDurations\\": [
+ [
+ 3,
+ 0
+ ],
+ [
+ 5,
+ 1
+ ],
+ [
+ 6,
+ 2
+ ],
+ [
+ 2,
+ 13
+ ],
+ [
+ 1,
+ 13
+ ]
+ ],
+ \\"fiberSelfDurations\\": [
+ [
+ 3,
+ 0
+ ],
+ [
+ 5,
+ 1
+ ],
+ [
+ 6,
+ 2
+ ],
+ [
+ 2,
+ 10
+ ],
+ [
+ 1,
+ 0
+ ]
+ ],
+ \\"interactionIDs\\": [],
+ \\"priorityLevel\\": \\"Immediate\\",
+ \\"screenshot\\": null,
+ \\"timestamp\\": 35
+ }
+ ],
+ \\"displayName\\": \\"Parent\\",
+ \\"initialTreeBaseDurations\\": [],
+ \\"interactionCommits\\": [],
+ \\"interactions\\": [],
+ \\"operations\\": [
+ [
+ 1,
+ 1,
+ 15,
+ 6,
+ 80,
+ 97,
+ 114,
+ 101,
+ 110,
+ 116,
+ 5,
+ 67,
+ 104,
+ 105,
+ 108,
+ 100,
+ 1,
+ 48,
+ 1,
+ 1,
+ 11,
+ 1,
+ 1,
+ 4,
+ 1,
+ 11000,
+ 1,
+ 2,
+ 5,
+ 1,
+ 0,
+ 1,
+ 0,
+ 4,
+ 2,
+ 11000,
+ 1,
+ 3,
+ 5,
+ 2,
+ 2,
+ 2,
+ 3,
+ 4,
+ 3,
+ 0,
+ 1,
+ 4,
+ 8,
+ 2,
+ 2,
+ 2,
+ 0,
+ 4,
+ 4,
+ 1000
+ ],
+ [
+ 1,
+ 1,
+ 8,
+ 5,
+ 67,
+ 104,
+ 105,
+ 108,
+ 100,
+ 1,
+ 49,
+ 1,
+ 5,
+ 5,
+ 2,
+ 2,
+ 1,
+ 2,
+ 4,
+ 5,
+ 1000,
+ 4,
+ 2,
+ 12000,
+ 3,
+ 2,
+ 3,
+ 3,
+ 5,
+ 4,
+ 4,
+ 1,
+ 12000
+ ],
+ [
+ 1,
+ 1,
+ 8,
+ 5,
+ 67,
+ 104,
+ 105,
+ 108,
+ 100,
+ 1,
+ 50,
+ 1,
+ 6,
+ 5,
+ 2,
+ 2,
+ 1,
+ 2,
+ 4,
+ 6,
+ 2000,
+ 4,
+ 2,
+ 14000,
+ 3,
+ 2,
+ 4,
+ 3,
+ 5,
+ 6,
+ 4,
+ 4,
+ 1,
+ 14000
+ ]
+ ],
+ \\"rootID\\": 1,
+ \\"snapshots\\": []
+ }
+ ]
+}"
+`;
+
+exports[`ProfilingCache should collect data for each root: ProfilingSummary 1`] = `
+Object {
+ "commitData": Array [
+ Object {
+ "duration": 13,
+ "fiberActualDurations": Map {
+ 3 => 0,
+ 4 => 1,
+ 6 => 2,
+ 2 => 13,
+ 1 => 13,
+ },
+ "fiberSelfDurations": Map {
+ 3 => 0,
+ 4 => 1,
+ 6 => 2,
+ 2 => 10,
+ 1 => 0,
+ },
+ "interactionIDs": Array [],
+ "priorityLevel": "Immediate",
+ "screenshot": null,
+ "timestamp": 13,
+ },
+ Object {
+ "duration": 10,
+ "fiberActualDurations": Map {
+ 3 => 0,
+ 2 => 10,
+ 1 => 10,
+ },
+ "fiberSelfDurations": Map {
+ 3 => 0,
+ 2 => 10,
+ 1 => 0,
+ },
+ "interactionIDs": Array [],
+ "priorityLevel": "Immediate",
+ "screenshot": null,
+ "timestamp": 23,
+ },
+ Object {
+ "duration": 10,
+ "fiberActualDurations": Map {
+ 2 => 10,
+ 1 => 10,
+ },
+ "fiberSelfDurations": Map {
+ 2 => 10,
+ 1 => 0,
+ },
+ "interactionIDs": Array [],
+ "priorityLevel": "Immediate",
+ "screenshot": null,
+ "timestamp": 33,
+ },
+ ],
+ "displayName": "Parent",
+ "initialTreeBaseDurations": Map {
+ 1 => 12,
+ 2 => 12,
+ 3 => 0,
+ 4 => 1,
+ 5 => 1,
+ },
+ "interactionCommits": Map {},
+ "interactions": Map {},
+ "operations": Array [
+ Uint32Array [
+ 1,
+ 1,
+ 8,
+ 5,
+ 67,
+ 104,
+ 105,
+ 108,
+ 100,
+ 1,
+ 50,
+ 1,
+ 6,
+ 5,
+ 2,
+ 2,
+ 1,
+ 2,
+ 4,
+ 6,
+ 2000,
+ 4,
+ 2,
+ 14000,
+ 3,
+ 2,
+ 4,
+ 3,
+ 4,
+ 6,
+ 5,
+ 4,
+ 1,
+ 14000,
+ ],
+ Uint32Array [
+ 1,
+ 1,
+ 0,
+ 2,
+ 2,
+ 6,
+ 4,
+ 4,
+ 2,
+ 11000,
+ 3,
+ 2,
+ 2,
+ 3,
+ 5,
+ 4,
+ 1,
+ 11000,
+ ],
+ Uint32Array [
+ 1,
+ 1,
+ 0,
+ 2,
+ 1,
+ 3,
+ ],
+ ],
+ "rootID": 1,
+ "snapshots": Map {
+ 1 => Object {
+ "children": Array [
+ 2,
+ ],
+ "displayName": null,
+ "id": 1,
+ "key": null,
+ "type": 11,
+ },
+ 2 => Object {
+ "children": Array [
+ 3,
+ 4,
+ 5,
+ ],
+ "displayName": "Parent",
+ "id": 2,
+ "key": null,
+ "type": 5,
+ },
+ 3 => Object {
+ "children": Array [],
+ "displayName": "Child",
+ "id": 3,
+ "key": "0",
+ "type": 5,
+ },
+ 4 => Object {
+ "children": Array [],
+ "displayName": "Child",
+ "id": 4,
+ "key": "1",
+ "type": 5,
+ },
+ 5 => Object {
+ "children": Array [],
+ "displayName": "Child",
+ "id": 5,
+ "key": null,
+ "type": 8,
+ },
+ },
+}
+`;
+
+exports[`ProfilingCache should collect data for each root: imported data 1`] = `
+"{
+ \\"version\\": 5,
+ \\"dataForRoots\\": [
+ {
+ \\"commitData\\": [
+ {
+ \\"duration\\": 13,
+ \\"fiberActualDurations\\": [
+ [
+ 3,
+ 0
+ ],
+ [
+ 4,
+ 1
+ ],
+ [
+ 6,
+ 2
+ ],
+ [
+ 2,
+ 13
+ ],
+ [
+ 1,
+ 13
+ ]
+ ],
+ \\"fiberSelfDurations\\": [
+ [
+ 3,
+ 0
+ ],
+ [
+ 4,
+ 1
+ ],
+ [
+ 6,
+ 2
+ ],
+ [
+ 2,
+ 10
+ ],
+ [
+ 1,
+ 0
+ ]
+ ],
+ \\"interactionIDs\\": [],
+ \\"priorityLevel\\": \\"Immediate\\",
+ \\"screenshot\\": null,
+ \\"timestamp\\": 13
+ },
+ {
+ \\"duration\\": 10,
+ \\"fiberActualDurations\\": [
+ [
+ 3,
+ 0
+ ],
+ [
+ 2,
+ 10
+ ],
+ [
+ 1,
+ 10
+ ]
+ ],
+ \\"fiberSelfDurations\\": [
+ [
+ 3,
+ 0
+ ],
+ [
+ 2,
+ 10
+ ],
+ [
+ 1,
+ 0
+ ]
+ ],
+ \\"interactionIDs\\": [],
+ \\"priorityLevel\\": \\"Immediate\\",
+ \\"screenshot\\": null,
+ \\"timestamp\\": 23
+ },
+ {
+ \\"duration\\": 10,
+ \\"fiberActualDurations\\": [
+ [
+ 2,
+ 10
+ ],
+ [
+ 1,
+ 10
+ ]
+ ],
+ \\"fiberSelfDurations\\": [
+ [
+ 2,
+ 10
+ ],
+ [
+ 1,
+ 0
+ ]
+ ],
+ \\"interactionIDs\\": [],
+ \\"priorityLevel\\": \\"Immediate\\",
+ \\"screenshot\\": null,
+ \\"timestamp\\": 33
+ }
+ ],
+ \\"displayName\\": \\"Parent\\",
+ \\"initialTreeBaseDurations\\": [
+ [
+ 1,
+ 12
+ ],
+ [
+ 2,
+ 12
+ ],
+ [
+ 3,
+ 0
+ ],
+ [
+ 4,
+ 1
+ ],
+ [
+ 5,
+ 1
+ ]
+ ],
+ \\"interactionCommits\\": [],
+ \\"interactions\\": [],
+ \\"operations\\": [
+ [
+ 1,
+ 1,
+ 8,
+ 5,
+ 67,
+ 104,
+ 105,
+ 108,
+ 100,
+ 1,
+ 50,
+ 1,
+ 6,
+ 5,
+ 2,
+ 2,
+ 1,
+ 2,
+ 4,
+ 6,
+ 2000,
+ 4,
+ 2,
+ 14000,
+ 3,
+ 2,
+ 4,
+ 3,
+ 4,
+ 6,
+ 5,
+ 4,
+ 1,
+ 14000
+ ],
+ [
+ 1,
+ 1,
+ 0,
+ 2,
+ 2,
+ 6,
+ 4,
+ 4,
+ 2,
+ 11000,
+ 3,
+ 2,
+ 2,
+ 3,
+ 5,
+ 4,
+ 1,
+ 11000
+ ],
+ [
+ 1,
+ 1,
+ 0,
+ 2,
+ 1,
+ 3
+ ]
+ ],
+ \\"rootID\\": 1,
+ \\"snapshots\\": [
+ [
+ 1,
+ {
+ \\"id\\": 1,
+ \\"children\\": [
+ 2
+ ],
+ \\"displayName\\": null,
+ \\"key\\": null,
+ \\"type\\": 11
+ }
+ ],
+ [
+ 2,
+ {
+ \\"id\\": 2,
+ \\"children\\": [
+ 3,
+ 4,
+ 5
+ ],
+ \\"displayName\\": \\"Parent\\",
+ \\"key\\": null,
+ \\"type\\": 5
+ }
+ ],
+ [
+ 3,
+ {
+ \\"id\\": 3,
+ \\"children\\": [],
+ \\"displayName\\": \\"Child\\",
+ \\"key\\": \\"0\\",
+ \\"type\\": 5
+ }
+ ],
+ [
+ 4,
+ {
+ \\"id\\": 4,
+ \\"children\\": [],
+ \\"displayName\\": \\"Child\\",
+ \\"key\\": \\"1\\",
+ \\"type\\": 5
+ }
+ ],
+ [
+ 5,
+ {
+ \\"id\\": 5,
+ \\"children\\": [],
+ \\"displayName\\": \\"Child\\",
+ \\"key\\": null,
+ \\"type\\": 8
+ }
+ ]
+ ]
+ }
+ ]
+}"
+`;
+
+exports[`ProfilingCache should report every traced interaction: Interactions 1`] = `
+Array [
+ Object {
+ "__count": 1,
+ "id": 0,
+ "name": "mount: one child",
+ "timestamp": 0,
+ },
+ Object {
+ "__count": 0,
+ "id": 1,
+ "name": "update: two children",
+ "timestamp": 11,
+ },
+]
+`;
+
+exports[`ProfilingCache should report every traced interaction: imported data 1`] = `
+"{
+ \\"version\\": 5,
+ \\"dataForRoots\\": [
+ {
+ \\"commitData\\": [
+ {
+ \\"duration\\": 11,
+ \\"fiberActualDurations\\": [
+ [
+ 1,
+ 11
+ ],
+ [
+ 2,
+ 11
+ ],
+ [
+ 3,
+ 0
+ ],
+ [
+ 4,
+ 1
+ ]
+ ],
+ \\"fiberSelfDurations\\": [
+ [
+ 1,
+ 0
+ ],
+ [
+ 2,
+ 10
+ ],
+ [
+ 3,
+ 0
+ ],
+ [
+ 4,
+ 1
+ ]
+ ],
+ \\"interactionIDs\\": [
+ 0
+ ],
+ \\"priorityLevel\\": \\"Immediate\\",
+ \\"screenshot\\": null,
+ \\"timestamp\\": 11
+ },
+ {
+ \\"duration\\": 11,
+ \\"fiberActualDurations\\": [
+ [
+ 3,
+ 0
+ ],
+ [
+ 5,
+ 1
+ ],
+ [
+ 2,
+ 11
+ ],
+ [
+ 1,
+ 11
+ ]
+ ],
+ \\"fiberSelfDurations\\": [
+ [
+ 3,
+ 0
+ ],
+ [
+ 5,
+ 1
+ ],
+ [
+ 2,
+ 10
+ ],
+ [
+ 1,
+ 0
+ ]
+ ],
+ \\"interactionIDs\\": [
+ 1
+ ],
+ \\"priorityLevel\\": \\"Immediate\\",
+ \\"screenshot\\": null,
+ \\"timestamp\\": 22
+ }
+ ],
+ \\"displayName\\": \\"Parent\\",
+ \\"initialTreeBaseDurations\\": [],
+ \\"interactionCommits\\": [
+ [
+ 0,
+ [
+ 0
+ ]
+ ],
+ [
+ 1,
+ [
+ 1
+ ]
+ ]
+ ],
+ \\"interactions\\": [
+ [
+ 0,
+ {
+ \\"__count\\": 1,
+ \\"id\\": 0,
+ \\"name\\": \\"mount: one child\\",
+ \\"timestamp\\": 0
+ }
+ ],
+ [
+ 1,
+ {
+ \\"__count\\": 0,
+ \\"id\\": 1,
+ \\"name\\": \\"update: two children\\",
+ \\"timestamp\\": 11
+ }
+ ]
+ ],
+ \\"operations\\": [
+ [
+ 1,
+ 1,
+ 15,
+ 6,
+ 80,
+ 97,
+ 114,
+ 101,
+ 110,
+ 116,
+ 5,
+ 67,
+ 104,
+ 105,
+ 108,
+ 100,
+ 1,
+ 48,
+ 1,
+ 1,
+ 11,
+ 1,
+ 1,
+ 4,
+ 1,
+ 11000,
+ 1,
+ 2,
+ 5,
+ 1,
+ 0,
+ 1,
+ 0,
+ 4,
+ 2,
+ 11000,
+ 1,
+ 3,
+ 5,
+ 2,
+ 2,
+ 2,
+ 3,
+ 4,
+ 3,
+ 0,
+ 1,
+ 4,
+ 8,
+ 2,
+ 2,
+ 2,
+ 0,
+ 4,
+ 4,
+ 1000
+ ],
+ [
+ 1,
+ 1,
+ 8,
+ 5,
+ 67,
+ 104,
+ 105,
+ 108,
+ 100,
+ 1,
+ 49,
+ 1,
+ 5,
+ 5,
+ 2,
+ 2,
+ 1,
+ 2,
+ 4,
+ 5,
+ 1000,
+ 4,
+ 2,
+ 12000,
+ 3,
+ 2,
+ 3,
+ 3,
+ 5,
+ 4,
+ 4,
+ 1,
+ 12000
+ ]
+ ],
+ \\"rootID\\": 1,
+ \\"snapshots\\": []
+ }
+ ]
+}"
+`;
diff --git a/src/__tests__/__snapshots__/profilingCharts-test.js.snap b/src/__tests__/__snapshots__/profilingCharts-test.js.snap
index fb8003ce23..8399be81c6 100644
--- a/src/__tests__/__snapshots__/profilingCharts-test.js.snap
+++ b/src/__tests__/__snapshots__/profilingCharts-test.js.snap
@@ -247,6 +247,20 @@ Object {
exports[`profiling charts interactions should contain valid data: Interactions 1`] = `
Object {
+ "interactions": Array [
+ Object {
+ "__count": 1,
+ "id": 0,
+ "name": "mount",
+ "timestamp": 0,
+ },
+ Object {
+ "__count": 0,
+ "id": 1,
+ "name": "update",
+ "timestamp": 15,
+ },
+ ],
"lastInteractionTime": 25,
"maxCommitDuration": 15,
}
@@ -254,6 +268,20 @@ Object {
exports[`profiling charts interactions should contain valid data: Interactions 2`] = `
Object {
+ "interactions": Array [
+ Object {
+ "__count": 1,
+ "id": 0,
+ "name": "mount",
+ "timestamp": 0,
+ },
+ Object {
+ "__count": 0,
+ "id": 1,
+ "name": "update",
+ "timestamp": 15,
+ },
+ ],
"lastInteractionTime": 25,
"maxCommitDuration": 15,
}
diff --git a/src/__tests__/profilerStore-test.js b/src/__tests__/profilerStore-test.js
new file mode 100644
index 0000000000..d2f7eefef7
--- /dev/null
+++ b/src/__tests__/profilerStore-test.js
@@ -0,0 +1,57 @@
+// @flow
+
+import type Store from 'src/devtools/store';
+
+describe('ProfilerStore', () => {
+ let React;
+ let ReactDOM;
+ let store: Store;
+ let utils;
+
+ beforeEach(() => {
+ utils = require('./utils');
+ utils.beforeEachProfiling();
+
+ store = global.store;
+ store.collapseNodesByDefault = false;
+
+ React = require('react');
+ ReactDOM = require('react-dom');
+ });
+
+ it('should not remove profiling data when roots are unmounted', async () => {
+ const Parent = ({ count }) =>
+ new Array(count)
+ .fill(true)
+ .map((_, index) => );
+ const Child = () =>
Hi!
;
+
+ const containerA = document.createElement('div');
+ const containerB = document.createElement('div');
+
+ utils.act(() => {
+ ReactDOM.render(, containerA);
+ ReactDOM.render(, containerB);
+ });
+
+ utils.act(() => store.startProfiling());
+
+ utils.act(() => {
+ ReactDOM.render(, containerA);
+ ReactDOM.render(, containerB);
+ });
+
+ utils.act(() => store.stopProfiling());
+
+ const rootA = store.roots[0];
+ const rootB = store.roots[1];
+
+ utils.act(() => ReactDOM.unmountComponentAtNode(containerB));
+
+ expect(store.profilerStore.getDataForRoot(rootA)).not.toBeNull();
+
+ utils.act(() => ReactDOM.unmountComponentAtNode(containerA));
+
+ expect(store.profilerStore.getDataForRoot(rootB)).not.toBeNull();
+ });
+});
diff --git a/src/__tests__/profiling-test.js b/src/__tests__/profiling-test.js
deleted file mode 100644
index d992e72a88..0000000000
--- a/src/__tests__/profiling-test.js
+++ /dev/null
@@ -1,574 +0,0 @@
-// @flow
-
-import typeof ReactTestRenderer from 'react-test-renderer';
-import type Bridge from 'src/bridge';
-import type Store from 'src/devtools/store';
-
-describe('profiling', () => {
- let React;
- let ReactDOM;
- let Scheduler;
- let SchedulerTracing;
- let TestRenderer: ReactTestRenderer;
- let bridge: Bridge;
- let store: Store;
- let utils;
-
- beforeEach(() => {
- utils = require('./utils');
- utils.beforeEachProfiling();
-
- bridge = global.bridge;
- store = global.store;
- store.collapseNodesByDefault = false;
-
- React = require('react');
- ReactDOM = require('react-dom');
- Scheduler = require('scheduler');
- SchedulerTracing = require('scheduler/tracing');
- TestRenderer = utils.requireTestRenderer();
- });
-
- it('should throw if importing older/unsupported data', () => {
- const {
- prepareImportedProfilingData,
- } = require('src/devtools/views/Profiler/utils');
- expect(() =>
- prepareImportedProfilingData(
- JSON.stringify({
- version: 0,
- })
- )
- ).toThrow('Unsupported profiler export version "0"');
- });
-
- describe('ProfilingSummary', () => {
- it('should be collected for each commit', async done => {
- const Parent = ({ count }) => {
- Scheduler.advanceTime(10);
- const children = new Array(count)
- .fill(true)
- .map((_, index) => );
- return (
-
- {children}
-
-
- );
- };
- const Child = ({ duration }) => {
- Scheduler.advanceTime(duration);
- return null;
- };
- const MemoizedChild = React.memo(Child);
-
- const container = document.createElement('div');
-
- utils.act(() => ReactDOM.render(, container));
- utils.act(() => store.startProfiling());
- utils.act(() => ReactDOM.render(, container));
- utils.act(() => ReactDOM.render(, container));
- utils.act(() => ReactDOM.render(, container));
- utils.act(() => store.stopProfiling());
-
- let profilingSummary = null;
-
- function Suspender({ previousPofilingSummary, rendererID, rootID }) {
- profilingSummary = store.profilingCache.ProfilingSummary.read({
- rendererID,
- rootID,
- });
- if (previousPofilingSummary != null) {
- expect(profilingSummary).toEqual(previousPofilingSummary);
- } else {
- expect(profilingSummary).toMatchSnapshot('ProfilingSummary');
- }
- return null;
- }
-
- const rendererID = utils.getRendererID();
- const rootID = store.roots[0];
-
- await utils.actAsync(() =>
- TestRenderer.create(
-
-
-
- )
- );
-
- expect(profilingSummary).not.toBeNull();
-
- utils.exportImportHelper(bridge, store, rendererID, rootID);
-
- await utils.actAsync(() =>
- TestRenderer.create(
-
-
-
- )
- );
-
- done();
- });
- });
-
- describe('CommitDetails', () => {
- it('should be collected for each commit', async done => {
- const Parent = ({ count }) => {
- Scheduler.advanceTime(10);
- const children = new Array(count)
- .fill(true)
- .map((_, index) => );
- return (
-
- {children}
-
-
- );
- };
- const Child = ({ duration }) => {
- Scheduler.advanceTime(duration);
- return null;
- };
- const MemoizedChild = React.memo(Child);
-
- const container = document.createElement('div');
-
- utils.act(() => store.startProfiling());
- utils.act(() => ReactDOM.render(, container));
- utils.act(() => ReactDOM.render(, container));
- utils.act(() => ReactDOM.render(, container));
- utils.act(() => ReactDOM.render(, container));
- utils.act(() => store.stopProfiling());
-
- const allCommitDetails = [];
-
- function Suspender({
- commitIndex,
- previousCommitDetails,
- rendererID,
- rootID,
- }) {
- const commitDetails = store.profilingCache.CommitDetails.read({
- commitIndex,
- rendererID,
- rootID,
- });
- if (previousCommitDetails != null) {
- expect(commitDetails).toEqual(previousCommitDetails);
- } else {
- allCommitDetails.push(commitDetails);
- expect(commitDetails).toMatchSnapshot(
- `CommitDetails commitIndex: ${commitIndex}`
- );
- }
- return null;
- }
-
- const rendererID = utils.getRendererID();
- const rootID = store.roots[0];
-
- for (let commitIndex = 0; commitIndex < 4; commitIndex++) {
- await utils.actAsync(() => {
- TestRenderer.create(
-
-
-
- );
- });
- }
-
- expect(allCommitDetails).toHaveLength(4);
-
- utils.exportImportHelper(bridge, store, rendererID, rootID);
-
- for (let commitIndex = 0; commitIndex < 4; commitIndex++) {
- await utils.actAsync(() => {
- TestRenderer.create(
-
-
-
- );
- });
- }
-
- done();
- });
-
- it('should calculate a self duration based on actual children (not filtered children)', async done => {
- store.componentFilters = [utils.createDisplayNameFilter('^Parent$')];
-
- const Grandparent = () => {
- Scheduler.advanceTime(10);
- return (
-
-
-
-
- );
- };
- const Parent = () => {
- Scheduler.advanceTime(2);
- return ;
- };
- const Child = () => {
- Scheduler.advanceTime(1);
- return null;
- };
-
- utils.act(() => store.startProfiling());
- utils.act(() =>
- ReactDOM.render(, document.createElement('div'))
- );
- utils.act(() => store.stopProfiling());
-
- let commitDetails = null;
-
- function Suspender({ commitIndex, rendererID, rootID }) {
- commitDetails = store.profilingCache.CommitDetails.read({
- commitIndex,
- rendererID,
- rootID,
- });
- expect(commitDetails).toMatchSnapshot(
- `CommitDetails with filtered self durations`
- );
- return null;
- }
-
- const rendererID = utils.getRendererID();
- const rootID = store.roots[0];
-
- await utils.actAsync(() => {
- TestRenderer.create(
-
-
-
- );
- });
-
- expect(commitDetails).not.toBeNull();
-
- done();
- });
-
- it('should calculate self duration correctly for suspended views', async done => {
- let data;
- const getData = () => {
- if (data) {
- return data;
- } else {
- throw new Promise(resolve => {
- data = 'abc';
- resolve(data);
- });
- }
- };
-
- const Parent = () => {
- Scheduler.advanceTime(10);
- return (
- }>
-
-
- );
- };
- const Fallback = () => {
- Scheduler.advanceTime(2);
- return 'Fallback...';
- };
- const Async = () => {
- Scheduler.advanceTime(3);
- const data = getData();
- return data;
- };
-
- utils.act(() => store.startProfiling());
- await utils.actAsync(() =>
- ReactDOM.render(, document.createElement('div'))
- );
- utils.act(() => store.stopProfiling());
-
- const allCommitDetails = [];
-
- function Suspender({ commitIndex, rendererID, rootID }) {
- const commitDetails = store.profilingCache.CommitDetails.read({
- commitIndex,
- rendererID,
- rootID,
- });
- allCommitDetails.push(commitDetails);
- expect(commitDetails).toMatchSnapshot(
- `CommitDetails with filtered self durations`
- );
- return null;
- }
-
- const rendererID = utils.getRendererID();
- const rootID = store.roots[0];
-
- for (let commitIndex = 0; commitIndex < 2; commitIndex++) {
- await utils.actAsync(() => {
- TestRenderer.create(
-
-
-
- );
- });
- }
-
- expect(allCommitDetails).toHaveLength(2);
-
- done();
- });
- });
-
- describe('FiberCommits', () => {
- it('should be collected for each rendered fiber', async done => {
- const Parent = ({ count }) => {
- Scheduler.advanceTime(10);
- const children = new Array(count)
- .fill(true)
- .map((_, index) => );
- return (
-
- {children}
-
-
- );
- };
- const Child = ({ duration }) => {
- Scheduler.advanceTime(duration);
- return null;
- };
- const MemoizedChild = React.memo(Child);
-
- const container = document.createElement('div');
-
- utils.act(() => store.startProfiling());
- utils.act(() => ReactDOM.render(, container));
- utils.act(() => ReactDOM.render(, container));
- utils.act(() => ReactDOM.render(, container));
- utils.act(() => store.stopProfiling());
-
- const allFiberCommits = [];
-
- function Suspender({
- fiberID,
- previousFiberCommits,
- rendererID,
- rootID,
- }) {
- const fiberCommits = store.profilingCache.FiberCommits.read({
- fiberID,
- rendererID,
- rootID,
- });
- if (previousFiberCommits != null) {
- expect(fiberCommits).toEqual(previousFiberCommits);
- } else {
- allFiberCommits.push(fiberCommits);
- expect(fiberCommits).toMatchSnapshot(
- `FiberCommits: element ${fiberID}`
- );
- }
- return null;
- }
-
- const rendererID = utils.getRendererID();
- const rootID = store.roots[0];
-
- for (let index = 0; index < store.numElements; index++) {
- await utils.actAsync(() => {
- const fiberID = store.getElementIDAtIndex(index);
- if (fiberID == null) {
- throw Error(`Unexpected null ID for element at index ${index}`);
- }
- TestRenderer.create(
-
-
-
- );
- });
- }
-
- expect(allFiberCommits).toHaveLength(store.numElements);
-
- utils.exportImportHelper(bridge, store, rendererID, rootID);
-
- for (let index = 0; index < store.numElements; index++) {
- await utils.actAsync(() => {
- const fiberID = store.getElementIDAtIndex(index);
- if (fiberID == null) {
- throw Error(`Unexpected null ID for element at index ${index}`);
- }
- TestRenderer.create(
-
-
-
- );
- });
- }
-
- done();
- });
- });
-
- describe('Interactions', () => {
- it('should be collected for every traced interaction', async done => {
- const Parent = ({ count }) => {
- Scheduler.advanceTime(10);
- const children = new Array(count)
- .fill(true)
- .map((_, index) => );
- return (
-
- {children}
-
-
- );
- };
- const Child = ({ duration }) => {
- Scheduler.advanceTime(duration);
- return null;
- };
- const MemoizedChild = React.memo(Child);
-
- const container = document.createElement('div');
-
- utils.act(() => store.startProfiling());
- utils.act(() =>
- SchedulerTracing.unstable_trace(
- 'mount: one child',
- Scheduler.unstable_now(),
- () => ReactDOM.render(, container)
- )
- );
- utils.act(() =>
- SchedulerTracing.unstable_trace(
- 'update: two children',
- Scheduler.unstable_now(),
- () => ReactDOM.render(, container)
- )
- );
- utils.act(() => store.stopProfiling());
-
- let interactions = null;
-
- function Suspender({ previousInteractions, rendererID, rootID }) {
- interactions = store.profilingCache.Interactions.read({
- rendererID,
- rootID,
- });
- if (previousInteractions != null) {
- expect(interactions).toEqual(previousInteractions);
- } else {
- expect(interactions).toMatchSnapshot('Interactions');
- }
- return null;
- }
-
- const rendererID = utils.getRendererID();
- const rootID = store.roots[0];
-
- await utils.actAsync(() =>
- TestRenderer.create(
-
-
-
- )
- );
-
- expect(interactions).not.toBeNull();
-
- utils.exportImportHelper(bridge, store, rendererID, rootID);
-
- await utils.actAsync(() =>
- TestRenderer.create(
-
-
-
- )
- );
-
- done();
- });
- });
-
- it('should remove profiling data when roots are unmounted', async () => {
- const Parent = ({ count }) =>
- new Array(count)
- .fill(true)
- .map((_, index) => );
- const Child = () => Hi!
;
-
- const containerA = document.createElement('div');
- const containerB = document.createElement('div');
-
- utils.act(() => {
- ReactDOM.render(, containerA);
- ReactDOM.render(, containerB);
- });
-
- utils.act(() => store.startProfiling());
-
- utils.act(() => {
- ReactDOM.render(, containerA);
- ReactDOM.render(, containerB);
- });
-
- utils.act(() => ReactDOM.unmountComponentAtNode(containerB));
-
- utils.act(() => ReactDOM.unmountComponentAtNode(containerA));
-
- utils.act(() => store.stopProfiling());
-
- // Assert all maps are empty
- store.assertExpectedRootMapSizes();
- });
-});
diff --git a/src/__tests__/profilingCache-test.js b/src/__tests__/profilingCache-test.js
new file mode 100644
index 0000000000..04e4a9fdda
--- /dev/null
+++ b/src/__tests__/profilingCache-test.js
@@ -0,0 +1,465 @@
+// @flow
+
+import typeof ReactTestRenderer from 'react-test-renderer';
+import type Bridge from 'src/bridge';
+import type Store from 'src/devtools/store';
+
+describe('ProfilingCache', () => {
+ let React;
+ let ReactDOM;
+ let Scheduler;
+ let SchedulerTracing;
+ let TestRenderer: ReactTestRenderer;
+ let bridge: Bridge;
+ let store: Store;
+ let utils;
+
+ beforeEach(() => {
+ utils = require('./utils');
+ utils.beforeEachProfiling();
+
+ bridge = global.bridge;
+ store = global.store;
+ store.collapseNodesByDefault = false;
+
+ React = require('react');
+ ReactDOM = require('react-dom');
+ Scheduler = require('scheduler');
+ SchedulerTracing = require('scheduler/tracing');
+ TestRenderer = utils.requireTestRenderer();
+ });
+
+ it('should collect data for each root', async done => {
+ const Parent = ({ count }) => {
+ Scheduler.advanceTime(10);
+ const children = new Array(count)
+ .fill(true)
+ .map((_, index) => );
+ return (
+
+ {children}
+
+
+ );
+ };
+ const Child = ({ duration }) => {
+ Scheduler.advanceTime(duration);
+ return null;
+ };
+ const MemoizedChild = React.memo(Child);
+
+ const container = document.createElement('div');
+
+ utils.act(() => ReactDOM.render(, container));
+ utils.act(() => store.startProfiling());
+ utils.act(() => ReactDOM.render(, container));
+ utils.act(() => ReactDOM.render(, container));
+ utils.act(() => ReactDOM.render(, container));
+ utils.act(() => store.stopProfiling());
+
+ let profilingDataForRoot = null;
+
+ // TODO (profarc) Add multi roots
+
+ function Suspender({ previousProfilingDataForRoot, rootID }) {
+ profilingDataForRoot = store.profilerStore.getDataForRoot(rootID);
+ if (previousProfilingDataForRoot != null) {
+ expect(profilingDataForRoot).toEqual(previousProfilingDataForRoot);
+ } else {
+ expect(profilingDataForRoot).toMatchSnapshot('ProfilingSummary');
+ }
+ return null;
+ }
+
+ const rootID = store.roots[0];
+
+ await utils.actAsync(() =>
+ TestRenderer.create(
+
+
+
+ )
+ );
+
+ expect(profilingDataForRoot).not.toBeNull();
+
+ utils.exportImportHelper(bridge, store, rootID);
+
+ await utils.actAsync(() =>
+ TestRenderer.create(
+
+
+
+ )
+ );
+
+ done();
+ });
+
+ it('should collect data for each commit', async done => {
+ const Parent = ({ count }) => {
+ Scheduler.advanceTime(10);
+ const children = new Array(count)
+ .fill(true)
+ .map((_, index) => );
+ return (
+
+ {children}
+
+
+ );
+ };
+ const Child = ({ duration }) => {
+ Scheduler.advanceTime(duration);
+ return null;
+ };
+ const MemoizedChild = React.memo(Child);
+
+ const container = document.createElement('div');
+
+ utils.act(() => store.startProfiling());
+ utils.act(() => ReactDOM.render(, container));
+ utils.act(() => ReactDOM.render(, container));
+ utils.act(() => ReactDOM.render(, container));
+ utils.act(() => ReactDOM.render(, container));
+ utils.act(() => store.stopProfiling());
+
+ const allCommitData = [];
+
+ function Suspender({ commitIndex, previousCommitDetails, rootID }) {
+ const commitData = store.profilerStore.getCommitData(rootID, commitIndex);
+ if (previousCommitDetails != null) {
+ expect(commitData).toEqual(previousCommitDetails);
+ } else {
+ allCommitData.push(commitData);
+ expect(commitData).toMatchSnapshot(
+ `CommitDetails commitIndex: ${commitIndex}`
+ );
+ }
+ return null;
+ }
+
+ const rootID = store.roots[0];
+
+ for (let commitIndex = 0; commitIndex < 4; commitIndex++) {
+ await utils.actAsync(() => {
+ TestRenderer.create(
+
+
+
+ );
+ });
+ }
+
+ expect(allCommitData).toHaveLength(4);
+
+ utils.exportImportHelper(bridge, store, rootID);
+
+ for (let commitIndex = 0; commitIndex < 4; commitIndex++) {
+ await utils.actAsync(() => {
+ TestRenderer.create(
+
+
+
+ );
+ });
+ }
+
+ done();
+ });
+
+ it('should calculate a self duration based on actual children (not filtered children)', async done => {
+ store.componentFilters = [utils.createDisplayNameFilter('^Parent$')];
+
+ const Grandparent = () => {
+ Scheduler.advanceTime(10);
+ return (
+
+
+
+
+ );
+ };
+ const Parent = () => {
+ Scheduler.advanceTime(2);
+ return ;
+ };
+ const Child = () => {
+ Scheduler.advanceTime(1);
+ return null;
+ };
+
+ utils.act(() => store.startProfiling());
+ utils.act(() =>
+ ReactDOM.render(, document.createElement('div'))
+ );
+ utils.act(() => store.stopProfiling());
+
+ let commitData = null;
+
+ function Suspender({ commitIndex, rootID }) {
+ commitData = store.profilerStore.getCommitData(rootID, commitIndex);
+ expect(commitData).toMatchSnapshot(
+ `CommitDetails with filtered self durations`
+ );
+ return null;
+ }
+
+ const rootID = store.roots[0];
+
+ await utils.actAsync(() => {
+ TestRenderer.create(
+
+
+
+ );
+ });
+
+ expect(commitData).not.toBeNull();
+
+ done();
+ });
+
+ it('should calculate self duration correctly for suspended views', async done => {
+ let data;
+ const getData = () => {
+ if (data) {
+ return data;
+ } else {
+ throw new Promise(resolve => {
+ data = 'abc';
+ resolve(data);
+ });
+ }
+ };
+
+ const Parent = () => {
+ Scheduler.advanceTime(10);
+ return (
+ }>
+
+
+ );
+ };
+ const Fallback = () => {
+ Scheduler.advanceTime(2);
+ return 'Fallback...';
+ };
+ const Async = () => {
+ Scheduler.advanceTime(3);
+ const data = getData();
+ return data;
+ };
+
+ utils.act(() => store.startProfiling());
+ await utils.actAsync(() =>
+ ReactDOM.render(, document.createElement('div'))
+ );
+ utils.act(() => store.stopProfiling());
+
+ const allCommitData = [];
+
+ function Suspender({ commitIndex, rootID }) {
+ const commitData = store.profilerStore.getCommitData(rootID, commitIndex);
+ allCommitData.push(commitData);
+ expect(commitData).toMatchSnapshot(
+ `CommitDetails with filtered self durations`
+ );
+ return null;
+ }
+
+ const rootID = store.roots[0];
+
+ for (let commitIndex = 0; commitIndex < 2; commitIndex++) {
+ await utils.actAsync(() => {
+ TestRenderer.create(
+
+
+
+ );
+ });
+ }
+
+ expect(allCommitData).toHaveLength(2);
+
+ done();
+ });
+
+ it('should collect data for each rendered fiber', async done => {
+ const Parent = ({ count }) => {
+ Scheduler.advanceTime(10);
+ const children = new Array(count)
+ .fill(true)
+ .map((_, index) => );
+ return (
+
+ {children}
+
+
+ );
+ };
+ const Child = ({ duration }) => {
+ Scheduler.advanceTime(duration);
+ return null;
+ };
+ const MemoizedChild = React.memo(Child);
+
+ const container = document.createElement('div');
+
+ utils.act(() => store.startProfiling());
+ utils.act(() => ReactDOM.render(, container));
+ utils.act(() => ReactDOM.render(, container));
+ utils.act(() => ReactDOM.render(, container));
+ utils.act(() => store.stopProfiling());
+
+ const allFiberCommits = [];
+
+ function Suspender({ fiberID, previousFiberCommits, rootID }) {
+ const fiberCommits = store.profilingCache.getFiberCommits({
+ fiberID,
+ rootID,
+ });
+ if (previousFiberCommits != null) {
+ expect(fiberCommits).toEqual(previousFiberCommits);
+ } else {
+ allFiberCommits.push(fiberCommits);
+ expect(fiberCommits).toMatchSnapshot(
+ `FiberCommits: element ${fiberID}`
+ );
+ }
+ return null;
+ }
+
+ const rootID = store.roots[0];
+
+ for (let index = 0; index < store.numElements; index++) {
+ await utils.actAsync(() => {
+ const fiberID = store.getElementIDAtIndex(index);
+ if (fiberID == null) {
+ throw Error(`Unexpected null ID for element at index ${index}`);
+ }
+ TestRenderer.create(
+
+
+
+ );
+ });
+ }
+
+ expect(allFiberCommits).toHaveLength(store.numElements);
+
+ utils.exportImportHelper(bridge, store, rootID);
+
+ for (let index = 0; index < store.numElements; index++) {
+ await utils.actAsync(() => {
+ const fiberID = store.getElementIDAtIndex(index);
+ if (fiberID == null) {
+ throw Error(`Unexpected null ID for element at index ${index}`);
+ }
+ TestRenderer.create(
+
+
+
+ );
+ });
+ }
+
+ done();
+ });
+
+ it('should report every traced interaction', async done => {
+ const Parent = ({ count }) => {
+ Scheduler.advanceTime(10);
+ const children = new Array(count)
+ .fill(true)
+ .map((_, index) => );
+ return (
+
+ {children}
+
+
+ );
+ };
+ const Child = ({ duration }) => {
+ Scheduler.advanceTime(duration);
+ return null;
+ };
+ const MemoizedChild = React.memo(Child);
+
+ const container = document.createElement('div');
+
+ utils.act(() => store.startProfiling());
+ utils.act(() =>
+ SchedulerTracing.unstable_trace(
+ 'mount: one child',
+ Scheduler.unstable_now(),
+ () => ReactDOM.render(, container)
+ )
+ );
+ utils.act(() =>
+ SchedulerTracing.unstable_trace(
+ 'update: two children',
+ Scheduler.unstable_now(),
+ () => ReactDOM.render(, container)
+ )
+ );
+ utils.act(() => store.stopProfiling());
+
+ let interactions = null;
+
+ function Suspender({ previousInteractions, rootID }) {
+ interactions = store.profilingCache.getInteractionsChartData({
+ rootID,
+ }).interactions;
+ if (previousInteractions != null) {
+ expect(interactions).toEqual(previousInteractions);
+ } else {
+ expect(interactions).toMatchSnapshot('Interactions');
+ }
+ return null;
+ }
+
+ const rootID = store.roots[0];
+
+ await utils.actAsync(() =>
+ TestRenderer.create(
+
+
+
+ )
+ );
+
+ expect(interactions).not.toBeNull();
+
+ utils.exportImportHelper(bridge, store, rootID);
+
+ await utils.actAsync(() =>
+ TestRenderer.create(
+
+
+
+ )
+ );
+
+ done();
+ });
+});
diff --git a/src/__tests__/profilingCharts-test.js b/src/__tests__/profilingCharts-test.js
index 62e2c40b30..33d698600b 100644
--- a/src/__tests__/profilingCharts-test.js
+++ b/src/__tests__/profilingCharts-test.js
@@ -62,59 +62,45 @@ describe('profiling charts', () => {
);
utils.act(() => store.stopProfiling());
- let suspenseResolved = false;
+ let renderFinished = false;
- function Suspender({ commitIndex, rendererID, rootID }) {
- const profilingSummary = store.profilingCache.ProfilingSummary.read({
- rendererID,
- rootID,
- });
- const commitDetails = store.profilingCache.CommitDetails.read({
- commitIndex,
- rendererID,
- rootID,
- });
- suspenseResolved = true;
+ function Suspender({ commitIndex, rootID }) {
const commitTree = store.profilingCache.getCommitTree({
commitIndex,
- profilingSummary,
+ rootID,
});
const chartData = store.profilingCache.getFlamegraphChartData({
- commitDetails,
commitIndex,
commitTree,
+ rootID,
});
expect(commitTree).toMatchSnapshot(`${commitIndex}: CommitTree`);
expect(chartData).toMatchSnapshot(
`${commitIndex}: FlamegraphChartData`
);
+ renderFinished = true;
return null;
}
- const rendererID = utils.getRendererID();
const rootID = store.roots[0];
for (let commitIndex = 0; commitIndex < 2; commitIndex++) {
- suspenseResolved = false;
+ renderFinished = false;
await utils.actAsync(
() =>
TestRenderer.create(
-
+
),
3
);
- expect(suspenseResolved).toBe(true);
+ expect(renderFinished).toBe(true);
}
- expect(suspenseResolved).toBe(true);
+ expect(renderFinished).toBe(true);
done();
});
@@ -156,54 +142,40 @@ describe('profiling charts', () => {
);
utils.act(() => store.stopProfiling());
- let suspenseResolved = false;
+ let renderFinished = false;
- function Suspender({ commitIndex, rendererID, rootID }) {
- const profilingSummary = store.profilingCache.ProfilingSummary.read({
- rendererID,
- rootID,
- });
- const commitDetails = store.profilingCache.CommitDetails.read({
- commitIndex,
- rendererID,
- rootID,
- });
- suspenseResolved = true;
+ function Suspender({ commitIndex, rootID }) {
const commitTree = store.profilingCache.getCommitTree({
commitIndex,
- profilingSummary,
+ rootID,
});
const chartData = store.profilingCache.getRankedChartData({
- commitDetails,
commitIndex,
commitTree,
+ rootID,
});
expect(commitTree).toMatchSnapshot(`${commitIndex}: CommitTree`);
expect(chartData).toMatchSnapshot(`${commitIndex}: RankedChartData`);
+ renderFinished = true;
return null;
}
- const rendererID = utils.getRendererID();
const rootID = store.roots[0];
for (let commitIndex = 0; commitIndex < 2; commitIndex++) {
- suspenseResolved = false;
+ renderFinished = false;
await utils.actAsync(
() =>
TestRenderer.create(
-
+
),
3
);
- expect(suspenseResolved).toBe(true);
+ expect(renderFinished).toBe(true);
}
done();
@@ -246,47 +218,33 @@ describe('profiling charts', () => {
);
utils.act(() => store.stopProfiling());
- let suspenseResolved = false;
+ let renderFinished = false;
- function Suspender({ commitIndex, rendererID, rootID }) {
- const profilingSummary = store.profilingCache.ProfilingSummary.read({
- rendererID,
- rootID,
- });
- const { interactions } = store.profilingCache.Interactions.read({
- rendererID,
- rootID,
- });
- suspenseResolved = true;
+ function Suspender({ commitIndex, rootID }) {
const chartData = store.profilingCache.getInteractionsChartData({
- interactions,
- profilingSummary,
+ rootID,
});
expect(chartData).toMatchSnapshot('Interactions');
+ renderFinished = true;
return null;
}
- const rendererID = utils.getRendererID();
const rootID = store.roots[0];
for (let commitIndex = 0; commitIndex < 2; commitIndex++) {
- suspenseResolved = false;
+ renderFinished = false;
await utils.actAsync(
() =>
TestRenderer.create(
-
+
),
3
);
- expect(suspenseResolved).toBe(true);
+ expect(renderFinished).toBe(true);
}
done();
diff --git a/src/__tests__/profilingCommitTreeBuilder-test.js b/src/__tests__/profilingCommitTreeBuilder-test.js
index 8356a0fd4a..31d7acafcf 100644
--- a/src/__tests__/profilingCommitTreeBuilder-test.js
+++ b/src/__tests__/profilingCommitTreeBuilder-test.js
@@ -45,43 +45,34 @@ describe('commit tree', () => {
utils.act(() => ReactDOM.render(, container));
utils.act(() => store.stopProfiling());
- let suspenseResolved = false;
+ let renderFinished = false;
- function Suspender({ commitIndex, rendererID, rootID }) {
- const profilingSummary = store.profilingCache.ProfilingSummary.read({
- rendererID,
- rootID,
- });
- suspenseResolved = true;
+ function Suspender({ commitIndex, rootID }) {
const commitTree = store.profilingCache.getCommitTree({
commitIndex,
- profilingSummary,
+ rootID,
});
expect(commitTree).toMatchSnapshot(`${commitIndex}: CommitTree`);
+ renderFinished = true;
return null;
}
- const rendererID = utils.getRendererID();
const rootID = store.roots[0];
for (let commitIndex = 0; commitIndex < 4; commitIndex++) {
- suspenseResolved = false;
+ renderFinished = false;
await utils.actAsync(
() =>
TestRenderer.create(
-
+
),
3
);
- expect(suspenseResolved).toBe(true);
+ expect(renderFinished).toBe(true);
}
done();
diff --git a/src/__tests__/profilingUtils-test.js b/src/__tests__/profilingUtils-test.js
new file mode 100644
index 0000000000..66029a2568
--- /dev/null
+++ b/src/__tests__/profilingUtils-test.js
@@ -0,0 +1,20 @@
+// @flow
+
+describe('profiling utils', () => {
+ let utils;
+
+ beforeEach(() => {
+ utils = require('src/devtools/views/Profiler/utils');
+ });
+
+ it('should throw if importing older/unsupported data', () => {
+ expect(() =>
+ utils.prepareProfilingDataFrontendFromExport(
+ ({
+ version: 0,
+ dataForRoots: [],
+ }: any)
+ )
+ ).toThrow('Unsupported profiler export version "0"');
+ });
+});
diff --git a/src/__tests__/utils.js b/src/__tests__/utils.js
index dd5b4b4646..934e1a593b 100644
--- a/src/__tests__/utils.js
+++ b/src/__tests__/utils.js
@@ -2,10 +2,10 @@
import typeof ReactTestRenderer from 'react-test-renderer';
-import type { ElementType } from 'src/types';
-
import type Bridge from 'src/bridge';
import type Store from 'src/devtools/store';
+import type { ProfilingDataFrontend } from 'src/devtools/views/Profiler/types';
+import type { ElementType } from 'src/types';
export function act(callback: Function): void {
const TestUtils = require('react-dom/test-utils');
@@ -136,54 +136,44 @@ export function requireTestRenderer(): ReactTestRenderer {
export function exportImportHelper(
bridge: Bridge,
store: Store,
- rendererID: number,
rootID: number
): void {
const { act } = require('./utils');
const {
- prepareExportedProfilingSummary,
- prepareImportedProfilingData,
+ prepareProfilingDataExport,
+ prepareProfilingDataFrontendFromExport,
} = require('src/devtools/views/Profiler/utils');
- let exportedProfilingDataJsonString = '';
- const onExportFile = ({ contents }) => {
- if (typeof contents === 'string') {
- exportedProfilingDataJsonString = (contents: string);
- }
- };
- bridge.addListener('exportFile', onExportFile);
+ const { profilerStore } = store;
- act(() => {
- const exportProfilingSummary = prepareExportedProfilingSummary(
- store.profilingOperations,
- store.profilingSnapshots,
- rendererID,
- rootID
- );
- bridge.send('exportProfilingSummary', exportProfilingSummary);
- });
+ expect(profilerStore.profilingData).not.toBeNull();
- // Cleanup to be able to call this again on the same bridge without memory leaks.
- bridge.removeListener('exportFile', onExportFile);
+ const profilingDataFrontendInitial = ((profilerStore.profilingData: any): ProfilingDataFrontend);
- expect(typeof exportedProfilingDataJsonString).toBe('string');
- expect(exportedProfilingDataJsonString).not.toBe('');
-
- const profilingData = prepareImportedProfilingData(
- exportedProfilingDataJsonString
+ const profilingDataExport = prepareProfilingDataExport(
+ profilingDataFrontendInitial
);
+
+ // Simulate writing/reading to disk.
+ const serializedProfilingDataExport = JSON.stringify(
+ profilingDataExport,
+ null,
+ 2
+ );
+ const parsedProfilingDataExport = JSON.parse(serializedProfilingDataExport);
+
+ const profilingDataFrontend = prepareProfilingDataFrontendFromExport(
+ (parsedProfilingDataExport: any)
+ );
+
// Sanity check that profiling snapshots are serialized correctly.
- expect(store.profilingSnapshots.get(rootID)).toEqual(
- profilingData.profilingSnapshots.get(rootID)
- );
- expect(store.profilingOperations.get(rootID)).toEqual(
- profilingData.profilingOperations.get(rootID)
- );
+ expect(profilingDataFrontendInitial).toEqual(profilingDataFrontend);
// Snapshot the JSON-parsed object, rather than the raw string, because Jest formats the diff nicer.
- expect(profilingData).toMatchSnapshot('imported data');
+ expect(serializedProfilingDataExport).toMatchSnapshot('imported data');
act(() => {
- store.profilingData = profilingData;
+ // Apply the new exported-then-reimported data so tests can re-run assertions.
+ profilerStore.profilingData = profilingDataFrontend;
});
}
diff --git a/src/backend/agent.js b/src/backend/agent.js
index 097ad00827..08469d4d47 100644
--- a/src/backend/agent.js
+++ b/src/backend/agent.js
@@ -10,7 +10,6 @@ import {
} from '../constants';
import { hideOverlay, showOverlay } from './views/Highlighter';
-import type { ExportedProfilingSummaryFromFrontend } from 'src/devtools/views/Profiler/types';
import type {
PathFrame,
PathMatch,
@@ -20,8 +19,6 @@ import type {
import type { OwnersList } from 'src/devtools/views/Components/types';
import type { Bridge, ComponentFilter } from '../types';
-import { prepareExportedProfilingData } from 'src/devtools/views/Profiler/utils';
-
const debug = (methodName, ...args) => {
if (__DEBUG__) {
console.log(
@@ -96,12 +93,8 @@ export default class Agent extends EventEmitter {
'clearHighlightedElementInDOM',
this.clearHighlightedElementInDOM
);
- bridge.addListener('exportProfilingSummary', this.exportProfilingSummary);
- bridge.addListener('getCommitDetails', this.getCommitDetails);
- bridge.addListener('getFiberCommits', this.getFiberCommits);
- bridge.addListener('getInteractions', this.getInteractions);
+ bridge.addListener('getProfilingData', this.getProfilingData);
bridge.addListener('getProfilingStatus', this.getProfilingStatus);
- bridge.addListener('getProfilingSummary', this.getProfilingSummary);
bridge.addListener('highlightElementInDOM', this.highlightElementInDOM);
bridge.addListener('getOwnersList', this.getOwnersList);
bridge.addListener('inspectElement', this.inspectElement);
@@ -154,109 +147,19 @@ export default class Agent extends EventEmitter {
return null;
}
- exportProfilingSummary = (
- exportedProfilingSummary: ExportedProfilingSummaryFromFrontend
- ): void => {
- const { rendererID, rootID } = exportedProfilingSummary;
+ getProfilingData = ({ rendererID }: {| rendererID: RendererID |}) => {
const renderer = this._rendererInterfaces[rendererID];
if (renderer == null) {
console.warn(`Invalid renderer id "${rendererID}"`);
- return;
}
- try {
- const exportedProfilingDataFromRenderer = renderer.getExportedProfilingData(
- rootID
- );
- const exportedProfilingData = prepareExportedProfilingData(
- exportedProfilingDataFromRenderer,
- exportedProfilingSummary
- );
- this._bridge.send('exportFile', {
- contents: JSON.stringify(exportedProfilingData, null, 2),
- filename: 'profile-data.json',
- });
- } catch (error) {
- console.warn(`Unable to export file: ${error.stack}`);
- }
- };
- getCommitDetails = ({
- commitIndex,
- rendererID,
- rootID,
- }: {
- commitIndex: number,
- rendererID: number,
- rootID: number,
- }) => {
- const renderer = this._rendererInterfaces[rendererID];
- if (renderer == null) {
- console.warn(`Invalid renderer id "${rendererID}"`);
- } else {
- this._bridge.send(
- 'commitDetails',
- renderer.getCommitDetails(rootID, commitIndex)
- );
- }
- };
-
- getFiberCommits = ({
- fiberID,
- rendererID,
- rootID,
- }: {
- fiberID: number,
- rendererID: number,
- rootID: number,
- }) => {
- const renderer = this._rendererInterfaces[rendererID];
- if (renderer == null) {
- console.warn(`Invalid renderer id "${rendererID}"`);
- } else {
- this._bridge.send(
- 'fiberCommits',
- renderer.getFiberCommits(rootID, fiberID)
- );
- }
- };
-
- getInteractions = ({
- rendererID,
- rootID,
- }: {
- rendererID: number,
- rootID: number,
- }) => {
- const renderer = this._rendererInterfaces[rendererID];
- if (renderer == null) {
- console.warn(`Invalid renderer id "${rendererID}"`);
- } else {
- this._bridge.send('interactions', renderer.getInteractions(rootID));
- }
+ this._bridge.send('profilingData', renderer.getProfilingData());
};
getProfilingStatus = () => {
this._bridge.send('profilingStatus', this._isProfiling);
};
- getProfilingSummary = ({
- rendererID,
- rootID,
- }: {
- rendererID: number,
- rootID: number,
- }) => {
- const renderer = this._rendererInterfaces[rendererID];
- if (renderer == null) {
- console.warn(`Invalid renderer id "${rendererID}"`);
- } else {
- this._bridge.send(
- 'profilingSummary',
- renderer.getProfilingSummary(rootID)
- );
- }
- };
-
clearHighlightedElementInDOM = () => {
hideOverlay();
};
diff --git a/src/backend/renderer.js b/src/backend/renderer.js
index b0532eefae..f4e8a6ffe9 100644
--- a/src/backend/renderer.js
+++ b/src/backend/renderer.js
@@ -18,7 +18,6 @@ import {
ElementTypeRoot,
ElementTypeSuspense,
} from 'src/types';
-import { PROFILER_EXPORT_VERSION } from 'src/constants';
import {
getDisplayName,
getDefaultComponentFilters,
@@ -37,17 +36,13 @@ import {
import { inspectHooksOfFiber } from './ReactDebugHooks';
import type {
- CommitDetailsBackend,
+ CommitDataBackend,
DevToolsHook,
- ExportedProfilingDataFromRenderer,
Fiber,
- FiberCommitsBackend,
- InteractionBackend,
- InteractionsBackend,
- InteractionWithCommitsBackend,
PathFrame,
PathMatch,
- ProfilingSummaryBackend,
+ ProfilingDataBackend,
+ ProfilingDataForRootBackend,
ReactRenderer,
RendererInterface,
} from './types';
@@ -55,6 +50,7 @@ import type {
InspectedElement,
Owner,
} from 'src/devtools/views/Components/types';
+import type { Interaction } from 'src/devtools/views/Profiler/types';
import type { ComponentFilter, ElementType } from 'src/types';
function getInternalReactConstants(version) {
@@ -820,6 +816,12 @@ export function attach(
pushOperation(ElementTypeRoot);
pushOperation(isProfilingSupported ? 1 : 0);
pushOperation(hasOwnerMetadata ? 1 : 0);
+
+ if (isProfiling) {
+ if (displayNamesByRootID !== null) {
+ displayNamesByRootID.set(id, getDisplayNameForRoot(fiber));
+ }
+ }
} else {
const { key } = fiber;
const displayName = getDisplayNameForFiber(fiber);
@@ -1286,7 +1288,7 @@ export function attach(
durations: [],
commitTime: performance.now() - profilingStartTime,
interactions: Array.from(root.memoizedInteractions).map(
- (interaction: InteractionBackend) => ({
+ (interaction: Interaction) => ({
...interaction,
timestamp: interaction.timestamp - profilingStartTime,
})
@@ -1329,7 +1331,7 @@ export function attach(
durations: [],
commitTime: performance.now() - profilingStartTime,
interactions: Array.from(root.memoizedInteractions).map(
- (interaction: InteractionBackend) => ({
+ (interaction: Interaction) => ({
...interaction,
timestamp: interaction.timestamp - profilingStartTime,
})
@@ -1981,188 +1983,113 @@ export function attach(
type CommitProfilingData = {|
commitTime: number,
durations: Array,
- interactions: Array,
+ interactions: Array,
maxActualDuration: number,
priorityLevel: string | null,
|};
type CommitProfilingMetadataMap = Map>;
+ type DisplayNamesByRootID = Map;
let currentCommitProfilingMetadata: CommitProfilingData | null = null;
+ let displayNamesByRootID: DisplayNamesByRootID | null = null;
let initialTreeBaseDurationsMap: Map | null = null;
let initialIDToRootMap: Map | null = null;
let isProfiling: boolean = false;
let profilingStartTime: number = 0;
let rootToCommitProfilingMetadataMap: CommitProfilingMetadataMap | null = null;
- function getCommitDetails(
- rootID: number,
- commitIndex: number
- ): CommitDetailsBackend {
- const commitProfilingMetadata = ((rootToCommitProfilingMetadataMap: any): CommitProfilingMetadataMap).get(
- rootID
- );
- if (commitProfilingMetadata != null) {
- const commitProfilingData = commitProfilingMetadata[commitIndex];
- if (commitProfilingData != null) {
- return {
- commitIndex,
- durations: commitProfilingData.durations,
- interactions: commitProfilingData.interactions,
- priorityLevel: commitProfilingData.priorityLevel,
+ function getProfilingData(): ProfilingDataBackend {
+ const dataForRoots: Array = [];
+
+ if (rootToCommitProfilingMetadataMap === null) {
+ throw Error(
+ 'getProfilingData() called before any profiling data was recorded'
+ );
+ }
+
+ rootToCommitProfilingMetadataMap.forEach(
+ (commitProfilingMetadata, rootID) => {
+ const commitData: Array = [];
+ const initialTreeBaseDurations: Array<[number, number]> = [];
+ const allInteractions: Map = new Map();
+ const interactionCommits: Map> = new Map();
+
+ const displayName =
+ (displayNamesByRootID !== null && displayNamesByRootID.get(rootID)) ||
+ 'Unknown';
+
+ if (initialTreeBaseDurationsMap != null) {
+ initialTreeBaseDurationsMap.forEach((treeBaseDuration, id) => {
+ if (
+ initialIDToRootMap != null &&
+ initialIDToRootMap.get(id) === rootID
+ ) {
+ // We don't need to convert milliseconds to microseconds in this case,
+ // because the profiling summary is JSON serialized.
+ initialTreeBaseDurations.push([id, treeBaseDuration]);
+ }
+ });
+ }
+
+ commitProfilingMetadata.forEach((commitProfilingData, commitIndex) => {
+ const {
+ durations,
+ interactions,
+ maxActualDuration,
+ priorityLevel,
+ commitTime,
+ } = commitProfilingData;
+
+ const interactionIDs: Array = [];
+
+ interactions.forEach(interaction => {
+ if (!allInteractions.has(interaction.id)) {
+ allInteractions.set(interaction.id, interaction);
+ }
+
+ interactionIDs.push(interaction.id);
+
+ const commitIndices = interactionCommits.get(interaction.id);
+ if (commitIndices != null) {
+ commitIndices.push(commitIndex);
+ } else {
+ interactionCommits.set(interaction.id, [commitIndex]);
+ }
+ });
+
+ const fiberActualDurations: Array<[number, number]> = [];
+ const fiberSelfDurations: Array<[number, number]> = [];
+ for (let i = 0; i < durations.length; i += 3) {
+ const fiberID = durations[i];
+ fiberActualDurations.push([fiberID, durations[i + 1]]);
+ fiberSelfDurations.push([fiberID, durations[i + 2]]);
+ }
+
+ commitData.push({
+ duration: maxActualDuration,
+ fiberActualDurations,
+ fiberSelfDurations,
+ interactionIDs,
+ priorityLevel,
+ timestamp: commitTime,
+ });
+ });
+
+ dataForRoots.push({
+ commitData,
+ displayName,
+ initialTreeBaseDurations,
+ interactionCommits: Array.from(interactionCommits.entries()),
+ interactions: Array.from(allInteractions.entries()),
rootID,
- };
- }
- }
-
- console.warn(
- `getCommitDetails(): No profiling info recorded for root "${rootID}" and commit ${commitIndex}`
- );
-
- return {
- commitIndex,
- durations: [],
- interactions: [],
- priorityLevel: null,
- rootID,
- };
- }
-
- function getFiberCommits(
- rootID: number,
- fiberID: number
- ): FiberCommitsBackend {
- const commitProfilingMetadata = ((rootToCommitProfilingMetadataMap: any): CommitProfilingMetadataMap).get(
- rootID
- );
- if (commitProfilingMetadata != null) {
- const commitDurations = [];
- commitProfilingMetadata.forEach(({ durations }, commitIndex) => {
- for (let i = 0; i < durations.length; i += 3) {
- if (durations[i] === fiberID) {
- commitDurations.push(commitIndex, durations[i + 2]);
- break;
- }
- }
- });
-
- return {
- commitDurations,
- fiberID,
- rootID,
- };
- }
-
- console.warn(
- `getFiberCommits(): No profiling info recorded for root "${rootID}"`
- );
-
- return {
- commitDurations: [],
- fiberID,
- rootID,
- };
- }
-
- function getInteractions(rootID: number): InteractionsBackend {
- const commitProfilingMetadata = ((rootToCommitProfilingMetadataMap: any): CommitProfilingMetadataMap).get(
- rootID
- );
- if (commitProfilingMetadata != null) {
- const interactionsMap: Map<
- number,
- InteractionWithCommitsBackend
- > = new Map();
-
- commitProfilingMetadata.forEach((commitProfilingData, commitIndex) => {
- commitProfilingData.interactions.forEach(interaction => {
- const interactionWithCommits = interactionsMap.get(interaction.id);
- if (interactionWithCommits != null) {
- interactionWithCommits.commits.push(commitIndex);
- } else {
- interactionsMap.set(interaction.id, {
- ...interaction,
- commits: [commitIndex],
- });
- }
});
- });
-
- return {
- interactions: Array.from(interactionsMap.values()),
- rootID,
- };
- }
-
- console.warn(
- `getInteractions(): No interactions recorded for root "${rootID}"`
- );
-
- return {
- interactions: [],
- rootID,
- };
- }
-
- function getExportedProfilingData(
- rootID: number
- ): ExportedProfilingDataFromRenderer {
- const commitDetailsForEachCommit = [];
- const commitProfilingMetadata = ((rootToCommitProfilingMetadataMap: any): CommitProfilingMetadataMap).get(
- rootID
- );
- if (commitProfilingMetadata != null) {
- for (let index = 0; index < commitProfilingMetadata.length; index++) {
- commitDetailsForEachCommit.push(getCommitDetails(rootID, index));
}
- }
-
- return {
- version: PROFILER_EXPORT_VERSION,
- profilingSummary: getProfilingSummary(rootID),
- commitDetails: commitDetailsForEachCommit,
- interactions: getInteractions(rootID),
- };
- }
-
- function getProfilingSummary(rootID: number): ProfilingSummaryBackend {
- const interactions = new Set();
- const commitDurations = [];
- const commitTimes = [];
-
- const commitProfilingMetadata = ((rootToCommitProfilingMetadataMap: any): CommitProfilingMetadataMap).get(
- rootID
);
- if (commitProfilingMetadata != null) {
- commitProfilingMetadata.forEach(metadata => {
- commitDurations.push(metadata.maxActualDuration);
- commitTimes.push(metadata.commitTime);
- metadata.interactions.forEach(({ name, timestamp }) => {
- interactions.add(`${timestamp}:${name}`);
- });
- });
- }
-
- const initialTreeBaseDurations = [];
- if (initialTreeBaseDurationsMap != null) {
- initialTreeBaseDurationsMap.forEach((treeBaseDuration, id) => {
- if (
- initialIDToRootMap != null &&
- initialIDToRootMap.get(id) === rootID
- ) {
- // We don't need to convert milliseconds to microseconds in this case,
- // because the profiling summary is JSON serialized.
- initialTreeBaseDurations.push(id, treeBaseDuration);
- }
- });
- }
return {
- commitDurations,
- commitTimes,
- initialTreeBaseDurations,
- interactionCount: interactions.size,
- rootID,
+ dataForRoots,
+ rendererID,
};
}
@@ -2175,9 +2102,18 @@ export function attach(
// It's important we snapshot both the durations and the id-to-root map,
// since either of these may change during the profiling session
// (e.g. when a fiber is re-rendered or when a fiber gets removed).
+ displayNamesByRootID = new Map();
initialTreeBaseDurationsMap = new Map(idToTreeBaseDurationMap);
initialIDToRootMap = new Map(idToRootMap);
+ hook.getFiberRoots(rendererID).forEach(root => {
+ const rootID = getFiberID(getPrimaryFiber(root.current));
+ ((displayNamesByRootID: any): DisplayNamesByRootID).set(
+ rootID,
+ getDisplayNameForRoot(root.current)
+ );
+ });
+
isProfiling = true;
profilingStartTime = performance.now();
rootToCommitProfilingMetadataMap = new Map();
@@ -2313,6 +2249,32 @@ export function attach(
const rootDisplayNameCounter: Map = new Map();
function setRootPseudoKey(id: number, fiber: Fiber) {
+ const name = getDisplayNameForRoot(fiber);
+ const counter = rootDisplayNameCounter.get(name) || 0;
+ rootDisplayNameCounter.set(name, counter + 1);
+ const pseudoKey = `${name}:${counter}`;
+ rootPseudoKeys.set(id, pseudoKey);
+ }
+
+ function removeRootPseudoKey(id: number) {
+ const pseudoKey = rootPseudoKeys.get(id);
+ if (pseudoKey === undefined) {
+ throw new Error('Expected root pseudo key to be known.');
+ }
+ const name = pseudoKey.substring(0, pseudoKey.lastIndexOf(':'));
+ const counter = rootDisplayNameCounter.get(name);
+ if (counter === undefined) {
+ throw new Error('Expected counter to be known.');
+ }
+ if (counter > 1) {
+ rootDisplayNameCounter.set(name, counter - 1);
+ } else {
+ rootDisplayNameCounter.delete(name);
+ }
+ rootPseudoKeys.delete(id);
+ }
+
+ function getDisplayNameForRoot(fiber: Fiber): string {
let preferredDisplayName = null;
let fallbackDisplayName = null;
let child = fiber.child;
@@ -2339,29 +2301,7 @@ export function attach(
}
child = child.child;
}
- const name = preferredDisplayName || fallbackDisplayName || 'Anonymous';
- const counter = rootDisplayNameCounter.get(name) || 0;
- rootDisplayNameCounter.set(name, counter + 1);
- const pseudoKey = `${name}:${counter}`;
- rootPseudoKeys.set(id, pseudoKey);
- }
-
- function removeRootPseudoKey(id: number) {
- const pseudoKey = rootPseudoKeys.get(id);
- if (pseudoKey === undefined) {
- throw new Error('Expected root pseudo key to be known.');
- }
- const name = pseudoKey.substring(0, pseudoKey.lastIndexOf(':'));
- const counter = rootDisplayNameCounter.get(name);
- if (counter === undefined) {
- throw new Error('Expected counter to be known.');
- }
- if (counter > 1) {
- rootDisplayNameCounter.set(name, counter - 1);
- } else {
- rootDisplayNameCounter.delete(name);
- }
- rootPseudoKeys.delete(id);
+ return preferredDisplayName || fallbackDisplayName || 'Anonymous';
}
function getPathFrame(fiber: Fiber): PathFrame {
@@ -2459,15 +2399,11 @@ export function attach(
cleanup,
flushInitialOperations,
getBestMatchForTrackedPath,
- getCommitDetails,
getFiberIDFromNative,
- getFiberCommits,
- getInteractions,
findNativeByFiberID,
getOwnersList,
getPathForElement,
- getExportedProfilingData,
- getProfilingSummary,
+ getProfilingData,
handleCommitFiberRoot,
handleCommitFiberUnmount,
inspectElement,
diff --git a/src/backend/types.js b/src/backend/types.js
index 431619db15..a2a17dd5a1 100644
--- a/src/backend/types.js
+++ b/src/backend/types.js
@@ -5,6 +5,7 @@ import type {
InspectedElement,
Owner,
} from 'src/devtools/views/Components/types';
+import type { Interaction } from 'src/devtools/views/Profiler/types';
type BundleType =
| 0 // PROD
@@ -115,51 +116,33 @@ export type ReactRenderer = {
currentDispatcherRef?: {| current: null | Dispatcher |},
};
-export type InteractionBackend = {|
- id: number,
- name: string,
+export type CommitDataBackend = {|
+ duration: number,
+ // Tuple of fiber ID and actual duration
+ fiberActualDurations: Array<[number, number]>,
+ // Tuple of fiber ID and computed "self" duration
+ fiberSelfDurations: Array<[number, number]>,
+ interactionIDs: Array,
+ priorityLevel: string | null,
timestamp: number,
|};
-export type CommitDetailsBackend = {|
- commitIndex: number,
- // An interleaved array: fiberID at [i], actualDuration at [i + 1], computed selfDuration at [i + 2].
- durations: Array,
- interactions: Array,
- priorityLevel: string | null,
+export type ProfilingDataForRootBackend = {|
+ commitData: Array,
+ displayName: string,
+ // Tuple of Fiber ID and base duration
+ initialTreeBaseDurations: Array<[number, number]>,
+ // Tuple of Interaction ID and commit indices
+ interactionCommits: Array<[number, Array]>,
+ interactions: Array<[number, Interaction]>,
rootID: number,
|};
-export type FiberCommitsBackend = {|
- commitDurations: Array,
- fiberID: number,
- rootID: number,
-|};
-
-export type InteractionWithCommitsBackend = {|
- ...InteractionBackend,
- commits: Array,
-|};
-
-export type InteractionsBackend = {|
- interactions: Array,
- rootID: number,
-|};
-
-export type ProfilingSummaryBackend = {|
- commitDurations: Array,
- commitTimes: Array,
- // An interleaved array: fiberID at [i], initialTreeBaseDuration at [i + 1].
- initialTreeBaseDurations: Array,
- interactionCount: number,
- rootID: number,
-|};
-
-export type ExportedProfilingDataFromRenderer = {|
- version: 3,
- profilingSummary: ProfilingSummaryBackend,
- commitDetails: Array,
- interactions: InteractionsBackend,
+// Profiling data collected by the renderer interface.
+// This information will be passed to the frontend and combined with info it collects.
+export type ProfilingDataBackend = {|
+ dataForRoots: Array,
+ rendererID: number,
|};
export type PathFrame = {|
@@ -178,21 +161,12 @@ export type RendererInterface = {
findNativeByFiberID: (id: number) => ?Array,
flushInitialOperations: () => void,
getBestMatchForTrackedPath: () => PathMatch | null,
- getCommitDetails: (
- rootID: number,
- commitIndex: number
- ) => CommitDetailsBackend,
getFiberIDFromNative: (
component: NativeType,
findNearestUnfilteredAncestor?: boolean
) => number | null,
- getFiberCommits: (rootID: number, fiberID: number) => FiberCommitsBackend,
- getInteractions: (rootID: number) => InteractionsBackend,
+ getProfilingData(): ProfilingDataBackend,
getOwnersList: (id: number) => Array | null,
- getExportedProfilingData: (
- rootID: number
- ) => ExportedProfilingDataFromRenderer,
- getProfilingSummary: (rootID: number) => ProfilingSummaryBackend,
getPathForElement: (id: number) => Array | null,
handleCommitFiberRoot: (fiber: Object, commitPriority?: number) => void,
handleCommitFiberUnmount: (fiber: Object) => void,
diff --git a/src/constants.js b/src/constants.js
index 66341ac584..5d43b93b5c 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -16,4 +16,4 @@ export const SESSION_STORAGE_LAST_SELECTION_KEY =
export const __DEBUG__ = false;
-export const PROFILER_EXPORT_VERSION = 3;
+export const PROFILER_EXPORT_VERSION = 5;
diff --git a/src/devtools/ProfilerStore.js b/src/devtools/ProfilerStore.js
new file mode 100644
index 0000000000..41b547682a
--- /dev/null
+++ b/src/devtools/ProfilerStore.js
@@ -0,0 +1,370 @@
+// @flow
+
+import EventEmitter from 'events';
+import memoize from 'memoize-one';
+import throttle from 'lodash.throttle';
+import { prepareProfilingDataFrontendFromBackendAndStore } from './views/Profiler/utils';
+import ProfilingCache from './ProfilingCache';
+import Store from './store';
+
+import type { ProfilingDataBackend } from 'src/backend/types';
+import type {
+ CommitDataFrontend,
+ ProfilingDataForRootFrontend,
+ ProfilingDataFrontend,
+ SnapshotNode,
+} from './views/Profiler/types';
+import type { Bridge } from '../types';
+
+const THROTTLE_CAPTURE_SCREENSHOT_DURATION = 500;
+
+export default class ProfilerStore extends EventEmitter {
+ _bridge: Bridge;
+
+ // Suspense cache for lazily calculating derived profiling data.
+ _cache: ProfilingCache;
+
+ // Temporary store of profiling data from the backend renderer(s).
+ // This data will be converted to the ProfilingDataFrontend format after being collected from all renderers.
+ _dataBackends: Array = [];
+
+ // Data from the most recently completed profiling session,
+ // or data that has been imported from a previously exported session.
+ // This object contains all necessary data to drive the Profiler UI interface,
+ // even though some of it is lazily parsed/derived via the ProfilingCache.
+ _dataFrontend: ProfilingDataFrontend | null = null;
+
+ // Snapshot of the state of the main Store (including all roots) when profiling started.
+ // Once profiling is finished, this snapshot can be used along with "operations" messages emitted during profiling,
+ // to reconstruct the state of each root for each commit.
+ // It's okay to use a single root to store this information because node IDs are unique across all roots.
+ //
+ // This map is only updated while profiling is in progress;
+ // Upon completion, it is converted into the exportable ProfilingDataFrontend format.
+ _initialSnapshotsByRootID: Map> = new Map();
+
+ // Map of root (id) to a list of tree mutation that occur during profiling.
+ // Once profiling is finished, these mutations can be used, along with the initial tree snapshots,
+ // to reconstruct the state of each root for each commit.
+ //
+ // This map is only updated while profiling is in progress;
+ // Upon completion, it is converted into the exportable ProfilingDataFrontend format.
+ _inProgressOperationsByRootID: Map> = new Map();
+
+ // Map of root (id) to a Map of screenshots by commit ID.
+ // Stores screenshots for each commit (when profiling).
+ //
+ // This map is only updated while profiling is in progress;
+ // Upon completion, it is converted into the exportable ProfilingDataFrontend format.
+ _inProgressScreenshotsByRootID: Map> = new Map();
+
+ // The backend is currently profiling.
+ // When profiling is in progress, operations are stored so that we can later reconstruct past commit trees.
+ _isProfiling: boolean = false;
+
+ // After profiling, data is requested from each attached renderer using this queue.
+ // So long as this queue is not empty, the store is retrieving and processing profiling data from the backend.
+ _rendererQueue: Set = new Set();
+
+ _store: Store;
+
+ constructor(bridge: Bridge, store: Store, defaultIsProfiling: boolean) {
+ super();
+
+ this._bridge = bridge;
+ this._isProfiling = defaultIsProfiling;
+ this._store = store;
+
+ bridge.addListener('operations', this.onBridgeOperations);
+ bridge.addListener('profilingData', this.onBridgeProfilingData);
+ bridge.addListener('profilingStatus', this.onProfilingStatus);
+ bridge.addListener('shutdown', this.onBridgeShutdown);
+
+ // It's possible that profiling has already started (e.g. "reload and start profiling")
+ // so the frontend needs to ask the backend for its status after mounting.
+ bridge.send('getProfilingStatus');
+
+ this._cache = new ProfilingCache(this);
+ }
+
+ getCommitData(rootID: number, commitIndex: number): CommitDataFrontend {
+ if (this._dataFrontend !== null) {
+ const dataForRoot = this._dataFrontend.dataForRoots.get(rootID);
+ if (dataForRoot != null) {
+ const commitDatum = dataForRoot.commitData[commitIndex];
+ if (commitDatum != null) {
+ return commitDatum;
+ }
+ }
+ }
+
+ throw Error(
+ `Could not find commit data for root "${rootID}" and commit ${commitIndex}`
+ );
+ }
+
+ getDataForRoot(rootID: number): ProfilingDataForRootFrontend {
+ if (this._dataFrontend !== null) {
+ const dataForRoot = this._dataFrontend.dataForRoots.get(rootID);
+ if (dataForRoot != null) {
+ return dataForRoot;
+ }
+ }
+
+ throw Error(`Could not find commit data for root "${rootID}"`);
+ }
+
+ get cache(): ProfilingCache {
+ return this._cache;
+ }
+
+ // Profiling data has been recorded for at least one root.
+ get hasProfilingData(): boolean {
+ return (
+ this._dataFrontend !== null && this._dataFrontend.dataForRoots.size > 0
+ );
+ }
+
+ // TODO (profarc) Remove this getter
+ get initialSnapshotsByRootID(): Map> {
+ return this._initialSnapshotsByRootID;
+ }
+
+ // TODO (profarc) Remove this getter
+ get inProgressOperationsByRootID(): Map> {
+ return this._inProgressOperationsByRootID;
+ }
+
+ // TODO (profarc) Remove this getter
+ get inProgressScreenshotsByRootID(): Map> {
+ return this._inProgressScreenshotsByRootID;
+ }
+
+ get isProcessingData(): boolean {
+ return this._rendererQueue.size > 0 || this._dataBackends.length > 0;
+ }
+
+ get isProfiling(): boolean {
+ return this._isProfiling;
+ }
+
+ get profilingData(): ProfilingDataFrontend | null {
+ return this._dataFrontend;
+ }
+ set profilingData(value: ProfilingDataFrontend | null): void {
+ this._dataBackends.splice(0);
+ this._dataFrontend = value;
+ this._initialSnapshotsByRootID.clear();
+ this._inProgressOperationsByRootID.clear();
+ this._inProgressScreenshotsByRootID.clear();
+ this._cache.invalidate();
+
+ // TODO (profarc) Remove subscriptions to Store for this
+ this._store.emit('profilingData');
+ this.emit('profilingData');
+ }
+
+ clear(): void {
+ this._dataBackends.splice(0);
+ this._dataFrontend = null;
+ this._initialSnapshotsByRootID.clear();
+ this._inProgressOperationsByRootID.clear();
+ this._inProgressScreenshotsByRootID.clear();
+
+ // Invalidate suspense cache if profiling data is being (re-)recorded.
+ // Note that we clear now because any existing data is "stale".
+ this._cache.invalidate();
+
+ // TODO (profarc) Remove subscriptions to Store for this
+ this._store.emit('isProfiling');
+ this.emit('isProfiling');
+ }
+
+ startProfiling(): void {
+ this._bridge.send('startProfiling');
+
+ // Don't actually update the local profiling boolean yet!
+ // Wait for onProfilingStatus() to confirm the status has changed.
+ // This ensures the frontend and backend are in sync wrt which commits were profiled.
+ // We do this to avoid mismatches on e.g. CommitTreeBuilder that would cause errors.
+ }
+
+ stopProfiling(): void {
+ this._bridge.send('stopProfiling');
+
+ // Don't actually update the local profiling boolean yet!
+ // Wait for onProfilingStatus() to confirm the status has changed.
+ // This ensures the frontend and backend are in sync wrt which commits were profiled.
+ // We do this to avoid mismatches on e.g. CommitTreeBuilder that would cause errors.
+ }
+
+ _captureScreenshot = throttle(
+ memoize((rootID: number, commitIndex: number) => {
+ this._bridge.send('captureScreenshot', { commitIndex, rootID });
+ }),
+ THROTTLE_CAPTURE_SCREENSHOT_DURATION
+ );
+
+ _takeProfilingSnapshotRecursive = (
+ elementID: number,
+ profilingSnapshots: Map
+ ) => {
+ const element = this._store.getElementByID(elementID);
+ if (element !== null) {
+ const snapshotNode: SnapshotNode = {
+ id: elementID,
+ children: element.children.slice(0),
+ displayName: element.displayName,
+ key: element.key,
+ type: element.type,
+ };
+ profilingSnapshots.set(elementID, snapshotNode);
+
+ element.children.forEach(childID =>
+ this._takeProfilingSnapshotRecursive(childID, profilingSnapshots)
+ );
+ }
+ };
+
+ onBridgeOperations = (operations: Uint32Array) => {
+ if (!(operations instanceof Uint32Array)) {
+ // $FlowFixMe TODO HACK Temporary workaround for the fact that Chrome is not transferring the typed array.
+ operations = Uint32Array.from(Object.values(operations));
+ }
+
+ // The first two values are always rendererID and rootID
+ const rootID = operations[1];
+
+ if (this._isProfiling) {
+ let profilingOperations = this._inProgressOperationsByRootID.get(rootID);
+ if (profilingOperations == null) {
+ profilingOperations = [operations];
+ this._inProgressOperationsByRootID.set(rootID, profilingOperations);
+ } else {
+ profilingOperations.push(operations);
+ }
+
+ if (!this._initialSnapshotsByRootID.has(rootID)) {
+ this._initialSnapshotsByRootID.set(rootID, new Map());
+ }
+
+ if (this._store.captureScreenshots) {
+ const commitIndex = profilingOperations.length - 1;
+ this._captureScreenshot(rootID, commitIndex);
+ }
+ }
+ };
+
+ onBridgeProfilingData = (dataBackend: ProfilingDataBackend) => {
+ if (this._isProfiling) {
+ // This should never happen, but if it does- ignore previous profiling data.
+ return;
+ }
+
+ const { rendererID } = dataBackend;
+
+ if (!this._rendererQueue.has(rendererID)) {
+ throw Error(
+ `Unexpected profiling data update from renderer "${rendererID}"`
+ );
+ }
+
+ this._dataBackends.push(dataBackend);
+ this._rendererQueue.delete(rendererID);
+
+ if (this._rendererQueue.size === 0) {
+ this._dataFrontend = prepareProfilingDataFrontendFromBackendAndStore(
+ this._dataBackends,
+ this._inProgressOperationsByRootID,
+ this._inProgressScreenshotsByRootID,
+ this._initialSnapshotsByRootID
+ );
+
+ this._dataBackends.splice(0);
+
+ // TODO (profarc) Remove subscriptions to Store for this
+ this._store.emit('isProcessingData');
+ this.emit('isProcessingData');
+ }
+ };
+
+ onBridgeShutdown = () => {
+ this._bridge.removeListener('operations', this.onBridgeOperations);
+ this._bridge.removeListener('profilingStatus', this.onProfilingStatus);
+ this._bridge.removeListener('shutdown', this.onBridgeShutdown);
+ };
+
+ onProfilingStatus = (isProfiling: boolean) => {
+ if (isProfiling) {
+ this._dataBackends.splice(0);
+ this._dataFrontend = null;
+ this._initialSnapshotsByRootID.clear();
+ this._inProgressOperationsByRootID.clear();
+ this._inProgressScreenshotsByRootID.clear();
+ this._rendererQueue.clear();
+
+ // Record snapshot of tree at the time profiling is started.
+ // This info is required to handle cases of e.g. nodes being removed during profiling.
+ this._store.roots.forEach(rootID => {
+ const profilingSnapshots = new Map();
+ this._initialSnapshotsByRootID.set(rootID, profilingSnapshots);
+ this._takeProfilingSnapshotRecursive(rootID, profilingSnapshots);
+ });
+ }
+
+ if (this._isProfiling !== isProfiling) {
+ this._isProfiling = isProfiling;
+
+ // Invalidate suspense cache if profiling data is being (re-)recorded.
+ // Note that we clear again, in case any views read from the cache while profiling.
+ // (That would have resolved a now-stale value without any profiling data.)
+ this._cache.invalidate();
+
+ // TODO (profarc) Remove subscriptions to Store for this
+ this._store.emit('isProfiling');
+ this.emit('isProfiling');
+
+ // If we've just finished a profiling session, we need to fetch data stored in each renderer interface
+ // and re-assemble it on the front-end into a format (ProfilingDataFrontend) that can power the Profiler UI.
+ // During this time, DevTools UI should probably not be interactive.
+ if (!isProfiling) {
+ this._dataBackends.splice(0);
+ this._rendererQueue.clear();
+
+ for (let rendererID of this._store.rootIDToRendererID.values()) {
+ if (!this._rendererQueue.has(rendererID)) {
+ this._rendererQueue.add(rendererID);
+
+ this._bridge.send('getProfilingData', { rendererID });
+ }
+ }
+
+ // TODO (profarc) Remove subscriptions to Store for this
+ this._store.emit('isProcessingData');
+ this.emit('isProcessingData');
+ }
+ }
+ };
+
+ onScreenshotCaptured = ({
+ commitIndex,
+ dataURL,
+ rootID,
+ }: {|
+ commitIndex: number,
+ dataURL: string,
+ rootID: number,
+ |}) => {
+ let screenshotsForRootByCommitIndex = this._inProgressScreenshotsByRootID.get(
+ rootID
+ );
+ if (!screenshotsForRootByCommitIndex) {
+ screenshotsForRootByCommitIndex = new Map();
+ this._inProgressScreenshotsByRootID.set(
+ rootID,
+ screenshotsForRootByCommitIndex
+ );
+ }
+ screenshotsForRootByCommitIndex.set(commitIndex, dataURL);
+ };
+}
diff --git a/src/devtools/ProfilingCache.js b/src/devtools/ProfilingCache.js
index a334edd4c8..d8f1819ddb 100644
--- a/src/devtools/ProfilingCache.js
+++ b/src/devtools/ProfilingCache.js
@@ -1,7 +1,6 @@
// @flow
-import { createResource } from './cache';
-import Store from './store';
+import ProfilerStore from './ProfilerStore';
import {
getCommitTree,
invalidateCommitTrees,
@@ -19,415 +18,105 @@ import {
invalidateChartData as invalidateRankedChartData,
} from 'src/devtools/views/Profiler/RankedChartBuilder';
-import type { Resource } from './cache';
-import type {
- CommitDetailsBackend,
- FiberCommitsBackend,
- InteractionsBackend,
- ProfilingSummaryBackend,
-} from 'src/backend/types';
-import type {
- CommitDetailsFrontend,
- FiberCommitsFrontend,
- InteractionsFrontend,
- InteractionWithCommitsFrontend,
- CommitTreeFrontend,
- ProfilingSummaryFrontend,
-} from 'src/devtools/views/Profiler/types';
+import type { CommitTree } from 'src/devtools/views/Profiler/types';
import type { ChartData as FlamegraphChartData } from 'src/devtools/views/Profiler/FlamegraphChartBuilder';
import type { ChartData as InteractionsChartData } from 'src/devtools/views/Profiler/InteractionsChartBuilder';
import type { ChartData as RankedChartData } from 'src/devtools/views/Profiler/RankedChartBuilder';
-import type { Bridge } from 'src/types';
-
-type CommitDetailsParams = {|
- commitIndex: number,
- rendererID: number,
- rootID: number,
-|};
-
-type FiberCommitsParams = {|
- fiberID: number,
- rendererID: number,
- rootID: number,
-|};
-
-type InteractionsParams = {|
- rendererID: number,
- rootID: number,
-|};
-
-type GetCommitTreeParams = {|
- commitIndex: number,
- profilingSummary: ProfilingSummaryFrontend,
-|};
-
-type ProfilingSummaryParams = {|
- rendererID: number,
- rootID: number,
-|};
export default class ProfilingCache {
- _bridge: Bridge;
- _store: Store;
+ _fiberCommits: Map> = new Map();
+ _profilerStore: ProfilerStore;
- _pendingCommitDetailsMap: Map<
- string,
- (commitDetails: CommitDetailsFrontend) => void
- > = new Map();
-
- _pendingFiberCommitsMap: Map<
- string,
- (fiberCommits: FiberCommitsFrontend) => void
- > = new Map();
-
- _pendingInteractionsMap: Map<
- number,
- (interactions: InteractionsFrontend) => void
- > = new Map();
-
- _pendingProfileSummaryMap: Map<
- number,
- (profilingSummary: ProfilingSummaryFrontend) => void
- > = new Map();
-
- CommitDetails: Resource<
- CommitDetailsParams,
- string,
- CommitDetailsFrontend
- > = createResource(
- ({ commitIndex, rendererID, rootID }: CommitDetailsParams) => {
- return new Promise(resolve => {
- const pendingKey = `${rootID}-${commitIndex}`;
- const profilingData = this._store.profilingData;
- if (profilingData !== null) {
- const commitDetailsByCommitIndex = profilingData.commitDetails;
- if (
- commitDetailsByCommitIndex != null &&
- commitIndex < commitDetailsByCommitIndex.length
- ) {
- const commitDetails = commitDetailsByCommitIndex[commitIndex];
- if (commitDetails != null) {
- this._pendingCommitDetailsMap.delete(pendingKey);
- resolve(commitDetails);
- return;
- }
- }
- } else if (this._store.profilingOperations.has(rootID)) {
- this._pendingCommitDetailsMap.set(pendingKey, resolve);
- this._bridge.send('getCommitDetails', {
- commitIndex,
- rendererID,
- rootID,
- });
- return;
- }
-
- this._pendingCommitDetailsMap.delete(pendingKey);
-
- // If no profiling data was recorded for this root, skip the round trip.
- resolve({
- rootID,
- commitIndex,
- actualDurations: new Map(),
- priorityLevel: null,
- interactions: [],
- selfDurations: new Map(),
- });
- });
- },
- ({ commitIndex, rendererID, rootID }: CommitDetailsParams) =>
- `${rootID}-${commitIndex}`
- );
-
- FiberCommits: Resource<
- FiberCommitsParams,
- string,
- FiberCommitsFrontend
- > = createResource(
- ({ fiberID, rendererID, rootID }: FiberCommitsParams) => {
- return new Promise(resolve => {
- const pendingKey = `${rootID}-${fiberID}`;
- const profilingData = this._store.profilingData;
- if (profilingData !== null) {
- const { commitDetails } = profilingData;
- const commitDurations = [];
- commitDetails.forEach(({ selfDurations }, commitIndex) => {
- const selfDuration = selfDurations.get(fiberID);
- if (selfDuration != null) {
- commitDurations.push(commitIndex, selfDuration);
- }
- });
- this._pendingFiberCommitsMap.delete(pendingKey);
- resolve({
- commitDurations,
- fiberID,
- rootID,
- });
- return;
- } else if (this._store.profilingOperations.has(rootID)) {
- this._pendingFiberCommitsMap.set(pendingKey, resolve);
- this._bridge.send('getFiberCommits', {
- fiberID,
- rendererID,
- rootID,
- });
- return;
- }
-
- this._pendingFiberCommitsMap.delete(pendingKey);
-
- // If no profiling data was recorded for this root, skip the round trip.
- resolve({
- commitDurations: [],
- fiberID,
- rootID,
- });
- });
- },
- ({ fiberID, rendererID, rootID }: FiberCommitsParams) =>
- `${rootID}-${fiberID}`
- );
-
- Interactions: Resource<
- InteractionsParams,
- number,
- InteractionsFrontend
- > = createResource(
- ({ rendererID, rootID }: InteractionsParams) => {
- return new Promise(resolve => {
- const pendingKey = rootID;
- const profilingData = this._store.profilingData;
- if (profilingData !== null) {
- const interactionsFrontend: InteractionsFrontend =
- profilingData.interactions;
- if (interactionsFrontend != null) {
- this._pendingInteractionsMap.delete(pendingKey);
- resolve(interactionsFrontend);
- return;
- }
- } else if (this._store.profilingOperations.has(rootID)) {
- this._pendingInteractionsMap.set(pendingKey, resolve);
- this._bridge.send('getInteractions', {
- rendererID,
- rootID,
- });
- return;
- }
-
- this._pendingInteractionsMap.delete(pendingKey);
-
- // If no profiling data was recorded for this root, skip the round trip.
- resolve({
- interactions: [],
- rootID,
- });
- });
- },
- ({ rendererID, rootID }: ProfilingSummaryParams) => rootID
- );
-
- ProfilingSummary: Resource<
- ProfilingSummaryParams,
- number,
- ProfilingSummaryFrontend
- > = createResource(
- ({ rendererID, rootID }: ProfilingSummaryParams) => {
- return new Promise(resolve => {
- const pendingKey = rootID;
- const profilingData = this._store.profilingData;
- if (profilingData !== null) {
- const profilingSummaryFrontend: ProfilingSummaryFrontend =
- profilingData.profilingSummary;
- if (profilingSummaryFrontend != null) {
- this._pendingProfileSummaryMap.delete(pendingKey);
- resolve(profilingSummaryFrontend);
- return;
- }
- } else if (this._store.profilingOperations.has(rootID)) {
- this._pendingProfileSummaryMap.set(pendingKey, resolve);
- this._bridge.send('getProfilingSummary', { rendererID, rootID });
- return;
- }
-
- this._pendingProfileSummaryMap.delete(pendingKey);
-
- // If no profiling data was recorded for this root, skip the round trip.
- resolve({
- rootID,
- commitDurations: [],
- commitTimes: [],
- initialTreeBaseDurations: new Map(),
- interactionCount: 0,
- });
- });
- },
- ({ rendererID, rootID }: ProfilingSummaryParams) => rootID
- );
-
- constructor(bridge: Bridge, store: Store) {
- this._bridge = bridge;
- this._store = store;
-
- bridge.addListener('commitDetails', this.onCommitDetails);
- bridge.addListener('fiberCommits', this.onFiberCommits);
- bridge.addListener('interactions', this.onInteractions);
- bridge.addListener('profilingSummary', this.onProfileSummary);
+ constructor(profilerStore: ProfilerStore) {
+ this._profilerStore = profilerStore;
}
- getCommitTree = ({ commitIndex, profilingSummary }: GetCommitTreeParams) =>
+ getCommitTree = ({
+ commitIndex,
+ rootID,
+ }: {|
+ commitIndex: number,
+ rootID: number,
+ |}) =>
getCommitTree({
commitIndex,
- profilingSummary,
- store: this._store,
+ profilerStore: this._profilerStore,
+ rootID,
});
+ getFiberCommits = ({
+ fiberID,
+ rootID,
+ }: {|
+ fiberID: number,
+ rootID: number,
+ |}): Array => {
+ const cachedFiberCommits = this._fiberCommits.get(fiberID);
+ if (cachedFiberCommits != null) {
+ return cachedFiberCommits;
+ }
+
+ const fiberCommits = [];
+ const dataForRoot = this._profilerStore.getDataForRoot(rootID);
+ dataForRoot.commitData.forEach((commitDatum, commitIndex) => {
+ if (commitDatum.fiberActualDurations.has(fiberID)) {
+ fiberCommits.push(commitIndex);
+ }
+ });
+
+ this._fiberCommits.set(fiberID, fiberCommits);
+
+ return fiberCommits;
+ };
+
getFlamegraphChartData = ({
- commitDetails,
commitIndex,
commitTree,
+ rootID,
}: {|
- commitDetails: CommitDetailsFrontend,
commitIndex: number,
- commitTree: CommitTreeFrontend,
+ commitTree: CommitTree,
+ rootID: number,
|}): FlamegraphChartData =>
getFlamegraphChartData({
- commitDetails,
commitIndex,
commitTree,
+ profilerStore: this._profilerStore,
+ rootID,
});
getInteractionsChartData = ({
- interactions,
- profilingSummary,
+ rootID,
}: {|
- interactions: Array,
- profilingSummary: ProfilingSummaryFrontend,
+ rootID: number,
|}): InteractionsChartData =>
getInteractionsChartData({
- interactions,
- profilingSummary,
+ profilerStore: this._profilerStore,
+ rootID,
});
getRankedChartData = ({
- commitDetails,
commitIndex,
commitTree,
+ rootID,
}: {|
- commitDetails: CommitDetailsFrontend,
commitIndex: number,
- commitTree: CommitTreeFrontend,
+ commitTree: CommitTree,
+ rootID: number,
|}): RankedChartData =>
getRankedChartData({
- commitDetails,
commitIndex,
commitTree,
+ profilerStore: this._profilerStore,
+ rootID,
});
invalidate() {
- // Invalidate Suspense caches.
- this.CommitDetails.clear();
- this.FiberCommits.clear();
- this.Interactions.clear();
- this.ProfilingSummary.clear();
+ this._fiberCommits.clear();
- // Invalidate non-Suspense caches too.
invalidateCommitTrees();
invalidateFlamegraphChartData();
invalidateInteractionsChartData();
invalidateRankedChartData();
-
- this._pendingCommitDetailsMap.clear();
- this._pendingFiberCommitsMap.clear();
- this._pendingInteractionsMap.clear();
- this._pendingProfileSummaryMap.clear();
}
-
- onCommitDetails = ({
- commitIndex,
- durations,
- interactions,
- priorityLevel,
- rootID,
- }: CommitDetailsBackend) => {
- const key = `${rootID}-${commitIndex}`;
- const resolve = this._pendingCommitDetailsMap.get(key);
- if (resolve != null) {
- this._pendingCommitDetailsMap.delete(key);
-
- const actualDurationsMap = new Map();
- const selfDurationsMap = new Map();
- for (let i = 0; i < durations.length; i += 3) {
- const fiberID = durations[i];
- actualDurationsMap.set(fiberID, durations[i + 1]);
- selfDurationsMap.set(fiberID, durations[i + 2]);
- }
-
- resolve({
- actualDurations: actualDurationsMap,
- commitIndex,
- interactions,
- priorityLevel,
- rootID,
- selfDurations: selfDurationsMap,
- });
- }
- };
-
- onFiberCommits = ({
- commitDurations,
- fiberID,
- rootID,
- }: FiberCommitsBackend) => {
- const key = `${rootID}-${fiberID}`;
- const resolve = this._pendingFiberCommitsMap.get(key);
- if (resolve != null) {
- this._pendingFiberCommitsMap.delete(key);
-
- resolve({
- commitDurations,
- fiberID,
- rootID,
- });
- }
- };
-
- onInteractions = ({ interactions, rootID }: InteractionsBackend) => {
- const resolve = this._pendingInteractionsMap.get(rootID);
- if (resolve != null) {
- this._pendingInteractionsMap.delete(rootID);
-
- resolve({
- interactions,
- rootID,
- });
- }
- };
-
- onProfileSummary = ({
- commitDurations,
- commitTimes,
- initialTreeBaseDurations,
- interactionCount,
- rootID,
- }: ProfilingSummaryBackend) => {
- const resolve = this._pendingProfileSummaryMap.get(rootID);
- if (resolve != null) {
- this._pendingProfileSummaryMap.delete(rootID);
-
- const initialTreeBaseDurationsMap = new Map();
- for (let i = 0; i < initialTreeBaseDurations.length; i += 2) {
- const fiberID = initialTreeBaseDurations[i];
- const initialTreeBaseDuration = initialTreeBaseDurations[i + 1];
- initialTreeBaseDurationsMap.set(fiberID, initialTreeBaseDuration);
- }
-
- resolve({
- commitDurations,
- commitTimes,
- initialTreeBaseDurations: initialTreeBaseDurationsMap,
- interactionCount,
- rootID,
- });
- }
- };
}
diff --git a/src/devtools/store.js b/src/devtools/store.js
index 21ae3255d0..1385c07431 100644
--- a/src/devtools/store.js
+++ b/src/devtools/store.js
@@ -1,8 +1,6 @@
// @flow
import EventEmitter from 'events';
-import memoize from 'memoize-one';
-import throttle from 'lodash.throttle';
import { inspect } from 'util';
import {
TREE_OPERATION_ADD,
@@ -19,11 +17,12 @@ import {
import { __DEBUG__ } from '../constants';
import ProfilingCache from './ProfilingCache';
import { printStore } from 'src/__tests__/storeSerializer';
+import ProfilerStore from './ProfilerStore';
import type { Element } from './views/Components/types';
import type {
- ImportedProfilingData,
- ProfilingSnapshotNode,
+ ProfilingDataFrontend,
+ SnapshotNode,
} from './views/Profiler/types';
import type { Bridge, ComponentFilter, ElementType } from '../types';
@@ -43,8 +42,6 @@ const LOCAL_STORAGE_CAPTURE_SCREENSHOTS_KEY =
const LOCAL_STORAGE_COLLAPSE_ROOTS_BY_DEFAULT_KEY =
'React::DevTools::collapseNodesByDefault';
-const THROTTLE_CAPTURE_SCREENSHOT_DURATION = 500;
-
type Config = {|
isProfiling?: boolean,
supportsCaptureScreenshots?: boolean,
@@ -80,37 +77,11 @@ export default class Store extends EventEmitter {
// The InspectedElementContext also relies on this mutability for its WeakMap usage.
_idToElement: Map = new Map();
- // The backend is currently profiling.
- // When profiling is in progress, operations are stored so that we can later reconstruct past commit trees.
- _isProfiling: boolean = false;
-
// Map of element (id) to the set of elements (ids) it owns.
// This map enables getOwnersListForElement() to avoid traversing the entire tree.
_ownersMap: Map> = new Map();
- // Suspense cache for reading profiling data.
- _profilingCache: ProfilingCache;
-
- // The user has imported a previously exported profiling session.
- _profilingData: ImportedProfilingData | null = null;
-
- // Map of root (id) to a list of tree mutation that occur during profiling.
- // Once profiling is finished, these mutations can be used, along with the initial tree snapshots,
- // to reconstruct the state of each root for each commit.
- _profilingOperationsByRootID: Map> = new Map();
-
- // Map of root (id) to a Map of screenshots by commit ID.
- // Stores screenshots for each commit (when profiling).
- _profilingScreenshotsByRootID: Map> = new Map();
-
- // Snapshot of the state of the main Store (including all roots) when profiling started.
- // Once profiling is finished, this snapshot can be used along with "operations" messages emitted during profiling,
- // to reconstruct the state of each root for each commit.
- // It's okay to use a single root to store this information because node IDs are unique across all roots.
- _profilingSnapshotsByRootID: Map<
- number,
- Map
- > = new Map();
+ _profilerStore: ProfilerStore;
// Incremented each time the store is mutated.
// This enables a passive effect to detect a mutation between render and commit phase.
@@ -150,17 +121,16 @@ export default class Store extends EventEmitter {
this._componentFilters = getSavedComponentFilters();
+ let isProfiling = false;
if (config != null) {
+ isProfiling = config.isProfiling === true;
+
const {
- isProfiling,
supportsCaptureScreenshots,
supportsFileDownloads,
supportsProfiling,
supportsReloadAndProfile,
} = config;
- if (isProfiling) {
- this._isProfiling = true;
- }
if (supportsCaptureScreenshots) {
this._supportsCaptureScreenshots = true;
this._captureScreenshots =
@@ -180,15 +150,9 @@ export default class Store extends EventEmitter {
this._bridge = bridge;
bridge.addListener('operations', this.onBridgeOperations);
- bridge.addListener('profilingStatus', this.onProfilingStatus);
- bridge.addListener('screenshotCaptured', this.onScreenshotCaptured);
bridge.addListener('shutdown', this.onBridgeShutdown);
- // It's possible that profiling has already started (e.g. "reload and start profiling")
- // so the frontend needs to ask the backend for its status after mounting.
- bridge.send('getProfilingStatus');
-
- this._profilingCache = new ProfilingCache(bridge, this);
+ this._profilerStore = new ProfilerStore(bridge, this, isProfiling);
}
// This is only used in tests to avoid memory leaks.
@@ -197,23 +161,6 @@ export default class Store extends EventEmitter {
// The only safe time to assert these maps are empty is when the store is empty.
this.assertMapSizeMatchesRootCount(this._idToElement, '_idToElement');
this.assertMapSizeMatchesRootCount(this._ownersMap, '_ownersMap');
-
- // These maps will be empty unless profiling mode has been started.
- // After this, their size should always match the number of roots,
- // but unless we want to track additional metadata about profiling history,
- // the only safe time to assert this is when the store is empty.
- this.assertMapSizeMatchesRootCount(
- this._profilingOperationsByRootID,
- '_profilingOperationsByRootID'
- );
- this.assertMapSizeMatchesRootCount(
- this._profilingScreenshotsByRootID,
- '_profilingScreenshotsByRootID'
- );
- this.assertMapSizeMatchesRootCount(
- this._profilingSnapshotsByRootID,
- '_profilingSnapshotsByRootID'
- );
}
// These maps should always be the same size as the number of roots
@@ -273,7 +220,7 @@ export default class Store extends EventEmitter {
return this._componentFilters;
}
set componentFilters(value: Array): void {
- if (this._isProfiling) {
+ if (this._profilerStore.isProfiling) {
// Re-mounting a tree while profiling is in progress might break a lot of assumptions.
// If necessary, we could support this- but it doesn't seem like a necessary use case.
throw Error('Cannot modify filter preferences while profiling');
@@ -295,54 +242,65 @@ export default class Store extends EventEmitter {
return this._hasOwnerMetadata;
}
- // Profiling data has been recorded for at least one root.
+ // TODO (profarc) Update views to use ProfilerStore directly to access this value.
get hasProfilingData(): boolean {
- return (
- this._profilingData !== null || this._profilingOperationsByRootID.size > 0
- );
+ return this._profilerStore.hasProfilingData;
}
+ // TODO (profarc) Update views to use ProfilerStore directly to access this value.
+ get isProcessingProfilingData(): boolean {
+ return this._profilerStore.isProcessingData;
+ }
+
+ // TODO (profarc) Update views to use ProfilerStore directly to access this value.
get isProfiling(): boolean {
- return this._isProfiling;
+ return this._profilerStore.isProfiling;
}
get numElements(): number {
return this._weightAcrossRoots;
}
+ // TODO (profarc) Update views to use ProfilerStore directly to access this value.
get profilingCache(): ProfilingCache {
- return this._profilingCache;
+ return this._profilerStore.cache;
}
- get profilingData(): ImportedProfilingData | null {
- return this._profilingData;
+ // TODO (profarc) Update views to use ProfilerStore directly to access this value.
+ get profilingData(): ProfilingDataFrontend | null {
+ return this._profilerStore.profilingData;
}
- set profilingData(value: ImportedProfilingData | null): void {
- this._profilingData = value;
- this._profilingOperationsByRootID = new Map();
- this._profilingScreenshotsByRootID = new Map();
- this._profilingSnapshotsByRootID = new Map();
- this._profilingCache.invalidate();
-
- this.emit('profilingData');
+ set profilingData(value: ProfilingDataFrontend | null): void {
+ this._profilerStore.profilingData = value;
}
- get profilingOperations(): Map> {
- return this._profilingOperationsByRootID;
+ // TODO (profarc) Update views to use ProfilerStore directly to access this value.
+ get profilingOperationsByRootID(): Map> {
+ return this._profilerStore.inProgressOperationsByRootID;
}
- get profilingScreenshots(): Map> {
- return this._profilingScreenshotsByRootID;
+ // TODO (profarc) Update views to use ProfilerStore directly to access this value.
+ get profilingScreenshotsByRootID(): Map> {
+ return this._profilerStore.inProgressScreenshotsByRootID;
}
- get profilingSnapshots(): Map> {
- return this._profilingSnapshotsByRootID;
+ // TODO (profarc) Update views to use ProfilerStore directly to access this value.
+ get profilingSnapshotsByRootID(): Map> {
+ return this._profilerStore.initialSnapshotsByRootID;
+ }
+
+ get profilerStore(): ProfilerStore {
+ return this._profilerStore;
}
get revision(): number {
return this._revision;
}
+ get rootIDToRendererID(): Map {
+ return this._rootIDToRendererID;
+ }
+
get roots(): $ReadOnlyArray {
return this._roots;
}
@@ -363,17 +321,9 @@ export default class Store extends EventEmitter {
return this._supportsReloadAndProfile;
}
+ // TODO (profarc) Update views to use ProfilerStore directly to access this method.
clearProfilingData(): void {
- this._profilingData = null;
- this._profilingOperationsByRootID = new Map();
- this._profilingScreenshotsByRootID = new Map();
- this._profilingSnapshotsByRootID = new Map();
-
- // Invalidate suspense cache if profiling data is being (re-)recorded.
- // Note that we clear now because any existing data is "stale".
- this._profilingCache.invalidate();
-
- this.emit('isProfiling');
+ this._profilerStore.clear();
}
containsElement(id: number): boolean {
@@ -601,22 +551,14 @@ export default class Store extends EventEmitter {
return false;
}
+ // TODO (profarc) Update views to use ProfilerStore directly to access this method.
startProfiling(): void {
- this._bridge.send('startProfiling');
-
- // Don't actually update the local profiling boolean yet!
- // Wait for onProfilingStatus() to confirm the status has changed.
- // This ensures the frontend and backend are in sync wrt which commits were profiled.
- // We do this to avoid mismatches on e.g. CommitTreeBuilder that would cause errors.
+ this._profilerStore.startProfiling();
}
+ // TODO (profarc) Update views to use ProfilerStore directly to access this method.
stopProfiling(): void {
- this._bridge.send('stopProfiling');
-
- // Don't actually update the local profiling boolean yet!
- // Wait for onProfilingStatus() to confirm the status has changed.
- // This ensures the frontend and backend are in sync wrt which commits were profiled.
- // We do this to avoid mismatches on e.g. CommitTreeBuilder that would cause errors.
+ this._profilerStore.stopProfiling();
}
// TODO Maybe split this into two methods: expand() and collapse()
@@ -701,34 +643,6 @@ export default class Store extends EventEmitter {
}
}
- _captureScreenshot = throttle(
- memoize((rootID: number, commitIndex: number) => {
- this._bridge.send('captureScreenshot', { commitIndex, rootID });
- }),
- THROTTLE_CAPTURE_SCREENSHOT_DURATION
- );
-
- _takeProfilingSnapshotRecursive = (
- elementID: number,
- profilingSnapshots: Map
- ) => {
- const element = this.getElementByID(elementID);
- if (element !== null) {
- const snapshotNode: ProfilingSnapshotNode = {
- id: elementID,
- children: element.children.slice(0),
- displayName: element.displayName,
- key: element.key,
- type: element.type,
- };
- profilingSnapshots.set(elementID, snapshotNode);
-
- element.children.forEach(childID =>
- this._takeProfilingSnapshotRecursive(childID, profilingSnapshots)
- );
- }
- };
-
_adjustParentTreeWeight = (
parentElement: Element | null,
weightDelta: number
@@ -769,23 +683,8 @@ export default class Store extends EventEmitter {
let haveRootsChanged = false;
+ // The first two values are always rendererID and rootID
const rendererID = operations[0];
- const rootID = operations[1];
-
- if (this._isProfiling) {
- let profilingOperations = this._profilingOperationsByRootID.get(rootID);
- if (profilingOperations == null) {
- profilingOperations = [operations];
- this._profilingOperationsByRootID.set(rootID, profilingOperations);
- } else {
- profilingOperations.push(operations);
- }
-
- if (this._captureScreenshots) {
- const commitIndex = profilingOperations.length - 1;
- this._captureScreenshot(rootID, commitIndex);
- }
- }
const addedElementIDs: Array = [];
// This is a mapping of removed ID -> parent ID:
@@ -857,10 +756,6 @@ export default class Store extends EventEmitter {
weight: 0,
});
- if (this._isProfiling) {
- this._profilingSnapshotsByRootID.set(id, new Map());
- }
-
haveRootsChanged = true;
} else {
parentID = ((operations[i]: any): number);
@@ -956,10 +851,6 @@ export default class Store extends EventEmitter {
this._rootIDToRendererID.delete(id);
this._rootIDToCapabilities.delete(id);
- this._profilingOperationsByRootID.delete(id);
- this._profilingScreenshotsByRootID.delete(id);
- this._profilingSnapshotsByRootID.delete(id);
-
haveRootsChanged = true;
} else {
if (__DEBUG__) {
@@ -1065,60 +956,12 @@ export default class Store extends EventEmitter {
this.emit('mutated', [addedElementIDs, removedElementIDs]);
};
- onProfilingStatus = (isProfiling: boolean) => {
- if (isProfiling) {
- this._profilingData = null;
- this._profilingOperationsByRootID = new Map();
- this._profilingScreenshotsByRootID = new Map();
- this._profilingSnapshotsByRootID = new Map();
- this.roots.forEach(rootID => {
- const profilingSnapshots = new Map();
- this._profilingSnapshotsByRootID.set(rootID, profilingSnapshots);
- this._takeProfilingSnapshotRecursive(rootID, profilingSnapshots);
- });
- }
-
- if (this._isProfiling !== isProfiling) {
- this._isProfiling = isProfiling;
-
- // Invalidate suspense cache if profiling data is being (re-)recorded.
- // Note that we clear again, in case any views read from the cache while profiling.
- // (That would have resolved a now-stale value without any profiling data.)
- this._profilingCache.invalidate();
-
- this.emit('isProfiling');
- }
- };
-
- onScreenshotCaptured = ({
- commitIndex,
- dataURL,
- rootID,
- }: {|
- commitIndex: number,
- dataURL: string,
- rootID: number,
- |}) => {
- let profilingScreenshotsForRootByCommitIndex = this._profilingScreenshotsByRootID.get(
- rootID
- );
- if (!profilingScreenshotsForRootByCommitIndex) {
- profilingScreenshotsForRootByCommitIndex = new Map();
- this._profilingScreenshotsByRootID.set(
- rootID,
- profilingScreenshotsForRootByCommitIndex
- );
- }
- profilingScreenshotsForRootByCommitIndex.set(commitIndex, dataURL);
- };
-
onBridgeShutdown = () => {
if (__DEBUG__) {
debug('onBridgeShutdown', 'unsubscribing from Bridge');
}
this._bridge.removeListener('operations', this.onBridgeOperations);
- this._bridge.removeListener('profilingStatus', this.onProfilingStatus);
this._bridge.removeListener('shutdown', this.onBridgeShutdown);
};
}
diff --git a/src/devtools/views/Profiler/CommitFlamegraph.js b/src/devtools/views/Profiler/CommitFlamegraph.js
index 091fd96b61..58c714b523 100644
--- a/src/devtools/views/Profiler/CommitFlamegraph.js
+++ b/src/devtools/views/Profiler/CommitFlamegraph.js
@@ -13,7 +13,7 @@ import { StoreContext } from '../context';
import styles from './CommitFlamegraph.css';
import type { ChartData, ChartNode } from './FlamegraphChartBuilder';
-import type { CommitDetailsFrontend, CommitTreeFrontend } from './types';
+import type { CommitTree } from './types';
export type ItemData = {|
chartData: ChartData,
@@ -26,7 +26,7 @@ export type ItemData = {|
export default function CommitFlamegraphAutoSizer(_: {||}) {
const { profilingCache } = useContext(StoreContext);
- const { rendererID, rootID, selectedCommitIndex, selectFiber } = useContext(
+ const { rootID, selectedCommitIndex, selectFiber } = useContext(
ProfilerContext
);
@@ -38,39 +38,22 @@ export default function CommitFlamegraphAutoSizer(_: {||}) {
[selectFiber]
);
- const profilingSummary = profilingCache.ProfilingSummary.read({
- rendererID: ((rendererID: any): number),
- rootID: ((rootID: any): number),
- });
-
- let commitDetails: CommitDetailsFrontend | null = null;
- let commitTree: CommitTreeFrontend | null = null;
+ let commitTree: CommitTree | null = null;
let chartData: ChartData | null = null;
if (selectedCommitIndex !== null) {
- commitDetails = profilingCache.CommitDetails.read({
+ commitTree = profilingCache.getCommitTree({
commitIndex: selectedCommitIndex,
- rendererID: ((rendererID: any): number),
rootID: ((rootID: any): number),
});
- commitTree = profilingCache.getCommitTree({
- commitIndex: selectedCommitIndex,
- profilingSummary,
- });
-
chartData = profilingCache.getFlamegraphChartData({
- commitDetails,
commitIndex: selectedCommitIndex,
commitTree,
+ rootID: ((rootID: any): number),
});
}
- if (
- commitDetails != null &&
- commitTree != null &&
- chartData != null &&
- chartData.depth > 0
- ) {
+ if (commitTree != null && chartData != null && chartData.depth > 0) {
return (
@@ -79,8 +62,7 @@ export default function CommitFlamegraphAutoSizer(_: {||}) {
// by the time this render prop function is called, the values of the `let` variables have not changed.
@@ -95,19 +77,12 @@ export default function CommitFlamegraphAutoSizer(_: {||}) {
type Props = {|
chartData: ChartData,
- commitDetails: CommitDetailsFrontend,
- commitTree: CommitTreeFrontend,
+ commitTree: CommitTree,
height: number,
width: number,
|};
-function CommitFlamegraph({
- chartData,
- commitDetails,
- commitTree,
- height,
- width,
-}: Props) {
+function CommitFlamegraph({ chartData, commitTree, height, width }: Props) {
const { selectFiber, selectedFiberID } = useContext(ProfilerContext);
const selectedChartNodeIndex = useMemo(() => {
diff --git a/src/devtools/views/Profiler/CommitRanked.js b/src/devtools/views/Profiler/CommitRanked.js
index 63200f37ba..466475c16b 100644
--- a/src/devtools/views/Profiler/CommitRanked.js
+++ b/src/devtools/views/Profiler/CommitRanked.js
@@ -13,7 +13,7 @@ import { StoreContext } from '../context';
import styles from './CommitRanked.css';
import type { ChartData } from './RankedChartBuilder';
-import type { CommitDetailsFrontend, CommitTreeFrontend } from './types';
+import type { CommitTree } from './types';
export type ItemData = {|
chartData: ChartData,
@@ -26,7 +26,7 @@ export type ItemData = {|
export default function CommitRankedAutoSizer(_: {||}) {
const { profilingCache } = useContext(StoreContext);
- const { rendererID, rootID, selectedCommitIndex, selectFiber } = useContext(
+ const { rootID, selectedCommitIndex, selectFiber } = useContext(
ProfilerContext
);
@@ -38,47 +38,29 @@ export default function CommitRankedAutoSizer(_: {||}) {
[selectFiber]
);
- const profilingSummary = profilingCache.ProfilingSummary.read({
- rendererID: ((rendererID: any): number),
- rootID: ((rootID: any): number),
- });
-
- let commitDetails: CommitDetailsFrontend | null = null;
- let commitTree: CommitTreeFrontend | null = null;
+ let commitTree: CommitTree | null = null;
let chartData: ChartData | null = null;
if (selectedCommitIndex !== null) {
- commitDetails = profilingCache.CommitDetails.read({
+ commitTree = profilingCache.getCommitTree({
commitIndex: selectedCommitIndex,
- rendererID: ((rendererID: any): number),
rootID: ((rootID: any): number),
});
- commitTree = profilingCache.getCommitTree({
- commitIndex: selectedCommitIndex,
- profilingSummary,
- });
-
chartData = profilingCache.getRankedChartData({
- commitDetails,
commitIndex: selectedCommitIndex,
commitTree,
+ rootID: ((rootID: any): number),
});
}
- if (
- commitDetails != null &&
- commitTree != null &&
- chartData != null &&
- chartData.nodes.length > 0
- ) {
+ if (commitTree != null && chartData != null && chartData.nodes.length > 0) {
return (
{({ height, width }) => (
@@ -93,19 +75,12 @@ export default function CommitRankedAutoSizer(_: {||}) {
type Props = {|
chartData: ChartData,
- commitDetails: CommitDetailsFrontend,
- commitTree: CommitTreeFrontend,
+ commitTree: CommitTree,
height: number,
width: number,
|};
-function CommitRanked({
- chartData,
- commitDetails,
- commitTree,
- height,
- width,
-}: Props) {
+function CommitRanked({ chartData, commitTree, height, width }: Props) {
const { selectedFiberID, selectFiber } = useContext(ProfilerContext);
const selectedFiberIndex = useMemo(
diff --git a/src/devtools/views/Profiler/CommitTreeBuilder.js b/src/devtools/views/Profiler/CommitTreeBuilder.js
index eff090f4bd..ea18a53812 100644
--- a/src/devtools/views/Profiler/CommitTreeBuilder.js
+++ b/src/devtools/views/Profiler/CommitTreeBuilder.js
@@ -9,14 +9,13 @@ import {
} from 'src/constants';
import { utfDecodeString } from 'src/utils';
import { ElementTypeRoot } from 'src/types';
-import Store from 'src/devtools/store';
+import ProfilerStore from 'src/devtools/ProfilerStore';
import type { ElementType } from 'src/types';
import type {
- CommitTreeFrontend,
- CommitTreeNodeFrontend,
- ProfilingSnapshotNode,
- ProfilingSummaryFrontend,
+ CommitTree,
+ CommitTreeNode,
+ ProfilingDataForRootFrontend,
} from 'src/devtools/views/Profiler/types';
const debug = (methodName, ...args) => {
@@ -30,36 +29,40 @@ const debug = (methodName, ...args) => {
}
};
-const rootToCommitTreeMap: Map> = new Map();
+const rootToCommitTreeMap: Map> = new Map();
export function getCommitTree({
commitIndex,
- profilingSummary,
- store,
+ profilerStore,
+ rootID,
}: {|
commitIndex: number,
- profilingSummary: ProfilingSummaryFrontend,
- store: Store,
-|}): CommitTreeFrontend {
- const { rootID } = profilingSummary;
-
+ profilerStore: ProfilerStore,
+ rootID: number,
+|}): CommitTree {
if (!rootToCommitTreeMap.has(rootID)) {
rootToCommitTreeMap.set(rootID, []);
}
const commitTrees = ((rootToCommitTreeMap.get(
rootID
- ): any): Array);
+ ): any): Array);
if (commitIndex < commitTrees.length) {
return commitTrees[commitIndex];
}
- const { profilingData } = store;
- const profilingOperations =
- profilingData != null
- ? profilingData.profilingOperations
- : store.profilingOperations;
+ const { profilingData } = profilerStore;
+ if (profilingData === null) {
+ throw Error(`No profiling data available`);
+ }
+
+ const dataForRoot = profilingData.dataForRoots.get(rootID);
+ if (dataForRoot == null) {
+ throw Error(`Could not find profiling data for root "${rootID}"`);
+ }
+
+ const { operations } = dataForRoot;
// Commits are generated sequentially and cached.
// If this is the very first commit, start with the cached snapshot and apply the first mutation.
@@ -67,32 +70,12 @@ export function getCommitTree({
if (commitIndex === 0) {
const nodes = new Map();
- const { profilingData } = store;
- const profilingSnapshots =
- profilingData != null
- ? profilingData.profilingSnapshots.get(rootID)
- : store.profilingSnapshots.get(rootID);
-
- if (profilingSnapshots == null) {
- throw Error(`Could not find profiling snapshot for root "${rootID}"`);
- }
-
// Construct the initial tree.
- recursivelyInitializeTree(
- rootID,
- 0,
- nodes,
- profilingSummary.initialTreeBaseDurations,
- profilingSnapshots
- );
+ recursivelyInitializeTree(rootID, 0, nodes, dataForRoot);
// Mutate the tree
- const commitOperations = profilingOperations.get(rootID);
- if (commitOperations != null && commitIndex < commitOperations.length) {
- const commitTree = updateTree(
- { nodes, rootID },
- commitOperations[commitIndex]
- );
+ if (operations != null && commitIndex < operations.length) {
+ const commitTree = updateTree({ nodes, rootID }, operations[commitIndex]);
if (__DEBUG__) {
__printTree(commitTree);
@@ -104,14 +87,14 @@ export function getCommitTree({
} else {
const previousCommitTree = getCommitTree({
commitIndex: commitIndex - 1,
- profilingSummary,
- store,
+ profilerStore,
+ rootID,
});
- const commitOperations = profilingOperations.get(rootID);
- if (commitOperations != null && commitIndex < commitOperations.length) {
+
+ if (operations != null && commitIndex < operations.length) {
const commitTree = updateTree(
previousCommitTree,
- commitOperations[commitIndex]
+ operations[commitIndex]
);
if (__DEBUG__) {
@@ -131,11 +114,10 @@ export function getCommitTree({
function recursivelyInitializeTree(
id: number,
parentID: number,
- nodes: Map,
- initialTreeBaseDurations: Map,
- profilingSnapshots: Map
+ nodes: Map,
+ dataForRoot: ProfilingDataForRootFrontend
): void {
- const node = profilingSnapshots.get(id);
+ const node = dataForRoot.snapshots.get(id);
if (node != null) {
nodes.set(id, {
id,
@@ -143,35 +125,31 @@ function recursivelyInitializeTree(
displayName: node.displayName,
key: node.key,
parentID,
- treeBaseDuration: ((initialTreeBaseDurations.get(id): any): number),
+ treeBaseDuration: ((dataForRoot.initialTreeBaseDurations.get(
+ id
+ ): any): number),
type: node.type,
});
node.children.forEach(childID =>
- recursivelyInitializeTree(
- childID,
- id,
- nodes,
- initialTreeBaseDurations,
- profilingSnapshots
- )
+ recursivelyInitializeTree(childID, id, nodes, dataForRoot)
);
}
}
function updateTree(
- commitTree: CommitTreeFrontend,
+ commitTree: CommitTree,
operations: Uint32Array
-): CommitTreeFrontend {
+): CommitTree {
// Clone the original tree so edits don't affect it.
const nodes = new Map(commitTree.nodes);
// Clone nodes before mutating them so edits don't affect them.
- const getClonedNode = (id: number): CommitTreeNodeFrontend => {
+ const getClonedNode = (id: number): CommitTreeNode => {
const clonedNode = ((Object.assign(
{},
nodes.get(id)
- ): any): CommitTreeNodeFrontend);
+ ): any): CommitTreeNode);
nodes.set(id, clonedNode);
return clonedNode;
};
@@ -219,7 +197,7 @@ function updateTree(
debug('Add', `new root fiber ${id}`);
}
- const node: CommitTreeNodeFrontend = {
+ const node: CommitTreeNode = {
children: [],
displayName: null,
id,
@@ -254,7 +232,7 @@ function updateTree(
const parentNode = getClonedNode(parentID);
parentNode.children = parentNode.children.concat(id);
- const node: CommitTreeNodeFrontend = {
+ const node: CommitTreeNode = {
children: [],
displayName,
id,
@@ -355,7 +333,7 @@ export function invalidateCommitTrees(): void {
}
// DEBUG
-const __printTree = (commitTree: CommitTreeFrontend) => {
+const __printTree = (commitTree: CommitTree) => {
if (__DEBUG__) {
const { nodes, rootID } = commitTree;
console.group('__printTree()');
diff --git a/src/devtools/views/Profiler/FlamegraphChartBuilder.js b/src/devtools/views/Profiler/FlamegraphChartBuilder.js
index 994a08c777..7287d09189 100644
--- a/src/devtools/views/Profiler/FlamegraphChartBuilder.js
+++ b/src/devtools/views/Profiler/FlamegraphChartBuilder.js
@@ -2,8 +2,9 @@
import { ElementTypeForwardRef, ElementTypeMemo } from 'src/types';
import { formatDuration } from './utils';
+import ProfilerStore from 'src/devtools/ProfilerStore';
-import type { CommitDetailsFrontend, CommitTreeFrontend } from './types';
+import type { CommitTree } from './types';
export type ChartNode = {|
actualDuration: number,
@@ -28,15 +29,19 @@ export type ChartData = {|
const cachedChartData: Map = new Map();
export function getChartData({
- commitDetails,
commitIndex,
commitTree,
+ profilerStore,
+ rootID,
}: {|
- commitDetails: CommitDetailsFrontend,
commitIndex: number,
- commitTree: CommitTreeFrontend,
+ commitTree: CommitTree,
+ profilerStore: ProfilerStore,
+ rootID: number,
|}): ChartData {
- const { actualDurations, rootID, selfDurations } = commitDetails;
+ const commitDatum = profilerStore.getCommitData(rootID, commitIndex);
+
+ const { fiberActualDurations, fiberSelfDurations } = commitDatum;
const { nodes } = commitTree;
const key = `${rootID}-${commitIndex}`;
@@ -62,9 +67,9 @@ export function getChartData({
const { children, displayName, key, treeBaseDuration, type } = node;
- const actualDuration = actualDurations.get(id) || 0;
- const selfDuration = selfDurations.get(id) || 0;
- const didRender = actualDurations.has(id);
+ const actualDuration = fiberActualDurations.get(id) || 0;
+ const selfDuration = fiberSelfDurations.get(id) || 0;
+ const didRender = fiberActualDurations.has(id);
const name = displayName || 'Anonymous';
const maybeKey = key !== null ? ` key="${key}"` : '';
@@ -131,7 +136,7 @@ export function getChartData({
walkTree(id, baseDuration, 1);
}
- actualDurations.forEach((duration, id) => {
+ fiberActualDurations.forEach((duration, id) => {
const node = nodes.get(id);
if (node != null) {
let currentID = node.parentID;
diff --git a/src/devtools/views/Profiler/InteractionListItem.js b/src/devtools/views/Profiler/InteractionListItem.js
index a4e3cd7054..4e6d8c746b 100644
--- a/src/devtools/views/Profiler/InteractionListItem.js
+++ b/src/devtools/views/Profiler/InteractionListItem.js
@@ -17,9 +17,8 @@ type Props = {
function InteractionListItem({ data: itemData, index, style }: Props) {
const {
chartData,
- interactions,
+ dataForRoot,
labelWidth,
- profilingSummary,
scaleX,
selectedInteractionID,
selectCommitIndex,
@@ -27,20 +26,22 @@ function InteractionListItem({ data: itemData, index, style }: Props) {
selectTab,
} = itemData;
- const { maxCommitDuration } = chartData;
- const { commitDurations, commitTimes } = profilingSummary;
+ const { commitData, interactionCommits } = dataForRoot;
+ const { interactions, lastInteractionTime, maxCommitDuration } = chartData;
const interaction = interactions[index];
+ if (interaction == null) {
+ throw Error(`Could not find interaction #${index}`);
+ }
const handleClick = useCallback(() => {
selectInteraction(interaction.id);
}, [interaction, selectInteraction]);
+ const commits = interactionCommits.get(interaction.id) || [];
+
const startTime = interaction.timestamp;
- const stopTime =
- interaction.commits.length > 0
- ? commitTimes[interaction.commits[interaction.commits.length - 1]]
- : interaction.timestamp;
+ const stopTime = lastInteractionTime;
const viewCommit = (commitIndex: number) => {
selectTab('flame-chart');
@@ -71,7 +72,7 @@ function InteractionListItem({ data: itemData, index, style }: Props) {
width: scaleX(stopTime - startTime, 0),
}}
/>
- {interaction.commits.map(commitIndex => (
+ {commits.map(commitIndex => (
))}
diff --git a/src/devtools/views/Profiler/Interactions.js b/src/devtools/views/Profiler/Interactions.js
index c9c5d3f14f..6c560f7ed9 100644
--- a/src/devtools/views/Profiler/Interactions.js
+++ b/src/devtools/views/Profiler/Interactions.js
@@ -11,18 +11,14 @@ import { scale } from './utils';
import styles from './Interactions.css';
+import type { ProfilingDataForRootFrontend } from './types';
import type { ChartData } from './InteractionsChartBuilder';
import type { TabID } from './ProfilerContext';
-import type {
- InteractionWithCommitsFrontend,
- ProfilingSummaryFrontend,
-} from './types';
export type ItemData = {|
chartData: ChartData,
- interactions: Array,
+ dataForRoot: ProfilingDataForRootFrontend,
labelWidth: number,
- profilingSummary: ProfilingSummaryFrontend,
scaleX: (value: number, fallbackValue: number) => number,
selectedInteractionID: number | null,
selectCommitIndex: (id: number | null) => void,
@@ -42,29 +38,21 @@ export default function InteractionsAutoSizer(_: {||}) {
function Interactions({ height, width }: {| height: number, width: number |}) {
const {
- rendererID,
rootID,
selectedInteractionID,
selectInteraction,
selectCommitIndex,
selectTab,
} = useContext(ProfilerContext);
- const { profilingCache } = useContext(StoreContext);
+ const { profilerStore } = useContext(StoreContext);
- const { interactions } = profilingCache.Interactions.read({
- rendererID: ((rendererID: any): number),
+ const dataForRoot = profilerStore.getDataForRoot(((rootID: any): number));
+
+ const chartData = profilerStore.cache.getInteractionsChartData({
rootID: ((rootID: any): number),
});
- const profilingSummary = profilingCache.ProfilingSummary.read({
- rendererID: ((rendererID: any): number),
- rootID: ((rootID: any): number),
- });
-
- const chartData = profilingCache.getInteractionsChartData({
- interactions,
- profilingSummary,
- });
+ const { interactions } = chartData;
const handleKeyDown = useCallback(
event => {
@@ -110,9 +98,8 @@ function Interactions({ height, width }: {| height: number, width: number |}) {
return {
chartData,
- interactions,
+ dataForRoot,
labelWidth,
- profilingSummary,
scaleX: scale(0, chartData.lastInteractionTime, 0, timelineWidth),
selectedInteractionID,
selectCommitIndex,
@@ -121,8 +108,7 @@ function Interactions({ height, width }: {| height: number, width: number |}) {
};
}, [
chartData,
- interactions,
- profilingSummary,
+ dataForRoot,
selectedInteractionID,
selectCommitIndex,
selectInteraction,
diff --git a/src/devtools/views/Profiler/InteractionsChartBuilder.js b/src/devtools/views/Profiler/InteractionsChartBuilder.js
index 08bf71be00..6171749ef0 100644
--- a/src/devtools/views/Profiler/InteractionsChartBuilder.js
+++ b/src/devtools/views/Profiler/InteractionsChartBuilder.js
@@ -1,11 +1,11 @@
// @flow
-import type {
- InteractionWithCommitsFrontend,
- ProfilingSummaryFrontend,
-} from './types';
+import ProfilerStore from 'src/devtools/ProfilerStore';
+
+import type { Interaction } from './types';
export type ChartData = {|
+ interactions: Array,
lastInteractionTime: number,
maxCommitDuration: number,
|};
@@ -13,30 +13,37 @@ export type ChartData = {|
const cachedChartData: Map = new Map();
export function getChartData({
- interactions,
- profilingSummary,
+ profilerStore,
+ rootID,
}: {|
- interactions: Array,
- profilingSummary: ProfilingSummaryFrontend,
+ profilerStore: ProfilerStore,
+ rootID: number,
|}): ChartData {
- const { rootID } = profilingSummary;
-
if (cachedChartData.has(rootID)) {
return ((cachedChartData.get(rootID): any): ChartData);
}
- const { commitDurations, commitTimes } = profilingSummary;
+ const dataForRoot = profilerStore.getDataForRoot(rootID);
+ if (dataForRoot == null) {
+ throw Error(`Could not find profiling data for root "${rootID}"`);
+ }
+
+ const { commitData, interactions } = dataForRoot;
const lastInteractionTime =
- commitTimes.length > 0 ? commitTimes[commitTimes.length - 1] : 0;
+ commitData.length > 0 ? commitData[commitData.length - 1].timestamp : 0;
let maxCommitDuration = 0;
- commitDurations.forEach(commitDuration => {
- maxCommitDuration = Math.max(maxCommitDuration, commitDuration);
+ commitData.forEach(commitDatum => {
+ maxCommitDuration = Math.max(maxCommitDuration, commitDatum.duration);
});
- const chartData = { lastInteractionTime, maxCommitDuration };
+ const chartData = {
+ interactions: Array.from(interactions.values()),
+ lastInteractionTime,
+ maxCommitDuration,
+ };
cachedChartData.set(rootID, chartData);
diff --git a/src/devtools/views/Profiler/ProfilerContext.js b/src/devtools/views/Profiler/ProfilerContext.js
index 1658e52430..c829516a14 100644
--- a/src/devtools/views/Profiler/ProfilerContext.js
+++ b/src/devtools/views/Profiler/ProfilerContext.js
@@ -16,7 +16,7 @@ import {
import { StoreContext } from '../context';
import Store from '../../store';
-import type { ImportedProfilingData } from './types';
+import type { ProfilingDataFrontend } from './types';
export type TabID = 'flame-chart' | 'ranked-chart' | 'interactions';
@@ -72,7 +72,7 @@ ProfilerContext.displayName = 'ProfilerContext';
type StoreProfilingState = {|
hasProfilingData: boolean,
- profilingData: ImportedProfilingData | null,
+ profilingData: ProfilingDataFrontend | null,
isProfiling: boolean,
|};
@@ -117,11 +117,11 @@ function ProfilerContextController({ children }: Props) {
rendererID = store.getRendererIDForElement(selectedElementID);
rootID = store.getRootIDForElement(selectedElementID);
rootHasProfilingData =
- rootID === null ? false : store.profilingOperations.has(rootID);
+ rootID === null ? false : store.profilingOperationsByRootID.has(rootID);
} else if (store.roots.length > 0) {
// If no root is selected, assume the first root; many React apps are single root anyway.
rootID = store.roots[0];
- rootHasProfilingData = store.profilingOperations.has(rootID);
+ rootHasProfilingData = store.profilingOperationsByRootID.has(rootID);
rendererID = store.getRendererIDForElement(rootID);
}
diff --git a/src/devtools/views/Profiler/ProfilingImportExportButtons.js b/src/devtools/views/Profiler/ProfilingImportExportButtons.js
index 14bf451d29..c196e6d6b2 100644
--- a/src/devtools/views/Profiler/ProfilingImportExportButtons.js
+++ b/src/devtools/views/Profiler/ProfilingImportExportButtons.js
@@ -5,20 +5,20 @@ import { ProfilerContext } from './ProfilerContext';
import { ModalDialogContext } from '../ModalDialog';
import Button from '../Button';
import ButtonIcon from '../ButtonIcon';
-import { BridgeContext, StoreContext } from '../context';
+import { StoreContext } from '../context';
import {
- prepareExportedProfilingSummary,
- prepareImportedProfilingData,
+ prepareProfilingDataExport,
+ prepareProfilingDataFrontendFromExport,
} from './utils';
import styles from './ProfilingImportExportButtons.css';
+import type { ProfilingDataExport } from './types';
+
export default function ProfilingImportExportButtons() {
- const bridge = useContext(BridgeContext);
- const { isProfiling, rendererID, rootHasProfilingData, rootID } = useContext(
- ProfilerContext
- );
+ const { isProfiling, rendererID, rootID } = useContext(ProfilerContext);
const store = useContext(StoreContext);
+ const { profilerStore } = store;
const inputRef = useRef(null);
@@ -29,20 +29,15 @@ export default function ProfilingImportExportButtons() {
return;
}
- const exportedProfilingSummary = prepareExportedProfilingSummary(
- store.profilingOperations,
- store.profilingSnapshots,
- rootID,
- rendererID
- );
- bridge.send('exportProfilingSummary', exportedProfilingSummary);
- }, [
- bridge,
- rendererID,
- rootID,
- store.profilingOperations,
- store.profilingSnapshots,
- ]);
+ if (profilerStore.profilingData !== null) {
+ const profilingDataExport = prepareProfilingDataExport(
+ profilerStore.profilingData
+ );
+
+ // TODO (profarc) Generate anchor "download" tag and click it
+ console.log('profilingDataExport:', profilingDataExport);
+ }
+ }, [rendererID, rootID, profilerStore.profilingData]);
const uploadData = useCallback(() => {
if (inputRef.current !== null) {
@@ -57,7 +52,12 @@ export default function ProfilingImportExportButtons() {
fileReader.addEventListener('load', () => {
try {
const raw = ((fileReader.result: any): string);
- store.profilingData = prepareImportedProfilingData(raw);
+ const profilingDataExport = ((JSON.parse(
+ raw
+ ): any): ProfilingDataExport);
+ store.profilingData = prepareProfilingDataFrontendFromExport(
+ profilingDataExport
+ );
} catch (error) {
modalDialogDispatch({
type: 'SHOW',
@@ -97,7 +97,7 @@ export default function ProfilingImportExportButtons() {
{store.supportsFileDownloads && (