[Perf Panel Node] (Part 2/2) Don't skip the system nodes when build the callFrames
Currently, when we build the fake jsFrame events for cpuProfile, the system nodes ( idle node, garbage collector node & program node) are skipped. This CL just let them be handled like normal, so they will be displayed. The special case is garbage collector node, which has no parent, so we use the precious system node as its parent, which is same as JS profiler and other cpuProfile viewer.
This also solve the time calculation wrong problem, because in Perf panel frontend, it will auto fill the empty gap, so for example when there is no '(idle)' node, the previous function will fill the time period of '(idle)', and Perf panel also will also add this time to the previous event, so the previous function's time is amplified.
Screenshot: https://imgur.com/a/QMFyWUr
Fixed: 1382733, 1080918
Change-Id: I2f741aed7a32ab3971213605168a81857e27ede8
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/4118545
Reviewed-by: Jack Franklin <jacktfranklin@chromium.org>
Commit-Queue: Nancy Li <nancyly@chromium.org>
diff --git a/front_end/models/timeline_model/TimelineJSProfile.ts b/front_end/models/timeline_model/TimelineJSProfile.ts
index d8d750b..cadce29 100644
--- a/front_end/models/timeline_model/TimelineJSProfile.ts
+++ b/front_end/models/timeline_model/TimelineJSProfile.ts
@@ -23,29 +23,33 @@
static generateTracingEventsFromCpuProfile(
jsProfileModel: SDK.CPUProfileDataModel.CPUProfileDataModel,
thread: SDK.TracingModel.Thread): SDK.TracingModel.Event[] {
- const idleNode = jsProfileModel.idleNode;
- const programNode = jsProfileModel.programNode || null;
- const gcNode = jsProfileModel.gcNode;
const samples = jsProfileModel.samples || [];
const timestamps = jsProfileModel.timestamps;
const jsEvents = [];
const nodeToStackMap = new Map<SDK.ProfileTreeModel.ProfileNode|null, Protocol.Runtime.CallFrame[]>();
- nodeToStackMap.set(programNode, []);
+
+ let prevNode: SDK.ProfileTreeModel.ProfileNode = jsProfileModel.root;
for (let i = 0; i < samples.length; ++i) {
let node: SDK.ProfileTreeModel.ProfileNode|null = jsProfileModel.nodeByIndex(i);
if (!node) {
console.error(`Node with unknown id ${samples[i]} at index ${i}`);
continue;
}
- if (node === gcNode || node === idleNode) {
- continue;
- }
let callFrames = nodeToStackMap.get(node);
if (!callFrames) {
- callFrames = (new Array(node.depth + 1) as Protocol.Runtime.CallFrame[]);
- nodeToStackMap.set(node, callFrames);
- for (let j = 0; node.parent; node = node.parent) {
- callFrames[j++] = (node as Protocol.Runtime.CallFrame);
+ if (node === jsProfileModel.gcNode) {
+ // GC samples have no stack, so we just put GC node on top of the last recorded sample.
+ // The depth is always 0, so set the callFrames size to 2 to save the GC and last recorded sample.
+ callFrames = (new Array(2) as Protocol.Runtime.CallFrame[]);
+ callFrames[0] = (node as Protocol.Runtime.CallFrame);
+ callFrames[1] = (prevNode as Protocol.Runtime.CallFrame);
+ nodeToStackMap.set(node, callFrames);
+ } else {
+ callFrames = (new Array(node.depth + 1) as Protocol.Runtime.CallFrame[]);
+ nodeToStackMap.set(node, callFrames);
+ for (let j = 0; node.parent; node = node.parent) {
+ callFrames[j++] = (node as Protocol.Runtime.CallFrame);
+ }
}
}
const jsSampleEvent = new SDK.TracingModel.Event(
@@ -53,6 +57,8 @@
timestamps[i], thread);
jsSampleEvent.args['data'] = {stackTrace: callFrames};
jsEvents.push(jsSampleEvent);
+
+ prevNode = node;
}
return jsEvents;
}