
CPU profiles in Chrome DevTools can reveal a lot about how your application runs, but making sense of the underlying data structures isn’t always straightforward. Even experienced developers often find the structure of these JSON files less than intuitive. In this article, we break down the main components that make up CPU profile data and examine how they’re represented in DevTools. With a clearer grasp of these building blocks, you will be better equipped to investigate performance and spot optimization opportunities.
Data Structure
CPU profiles are JSON files that contain structured data representing the execution timeline and call hierarchy of your Node.js application. The profile consists of nodes representing function calls, timing information, and sampling data that can be visualized in DevTools.
The example below shows a minimal CPU profile.
Profile: minimal-cpu-profile.cpuprofile
Profile content:
DevTools Performance Tab:

Dimensions and Timing Data
CPU profiles represent execution data across two primary dimensions: time (horizontal axis) and call-tree depth (vertical axis). Understanding these dimensions is crucial for interpreting flame charts and performance data.
Dimensions
startTime - the microsecond timestamp when profiling began
endTime - equals startTime + Σ timeDeltas, marking the profile's visible end
Timing Data
timeDeltas - an array of intervals (μs) between each sample tick. Time deltas overflow the visible end of the measure.
samples - an array of node IDs indicating which nodes were active during the profile.
Time (horizontal axis) represents the execution timeline with startTime marking when profiling began, endTime marking the profile's visible end, and timeDeltas providing intervals between samples.
Call-tree depth (vertical axis) shows the function call hierarchy where the root node is at depth 0, and each level represents nested function calls. The samples array contains the "leaf frames" - the deepest executing functions at each time interval, with their parent chain automatically reconstructed for visualization.
The example below shows a profile with one node (ID 2) centered in the middle of the chart. Its position in the timeline is determined by the timeDeltas, while endTime marks the end of the profile.
Profile: minimal-cpu-profile-timing-data.cpuprofile
DevTools Performance Tab:

Time deltas
Profile: minimal-cpu-profile-timing-data-time-deltas.cpuprofile
DevTools Performance Tab:

Samples
Samples are the list of "visible" nodes when viewed from the bottom of the chart. Listing a node at a given timeDelta (its position in the samples array) also includes all of its parent nodes. In other words, the samples array represents the leaf frames in the chart.
Profile: minimal-cpu-profile-timing-data-samples.cpuprofile
This example draws the same node sequence (1->2->3) twice.
The first time, it draws them as a tower, where each frame has the same width (takes the same time).
"samples": [1, 3, 3, 1], "timeDeltas": [0, 100, 100, 100]
(visualized as ▀▀)
The second time, it draws them as a flame, where each frame is slightly narrower and nested inside its parent.
"samples": [1, 2, 3, 2, 1], "timeDeltas": [0, 100, 100, 100, 100]
(visualized as ▔▀▔)
DevTools Performance Tab:

Nodes
Parent and child nodes
Node relationships define the call hierarchy through parent-child references that mirror the JavaScript call stack. These relationships enable reconstruction of the complete call tree for flame chart visualization and performance analysis.
parent contains the optional ID of the immediate caller in the call tree (omitted on the root node).
children lists the IDs of frames this node directly invokes, mirroring the inverted parent relationships.
Traversal starts at the root (no parent) and recursively follows its children to rebuild the full call hierarchy, useful for flame chart rendering and aggregating inclusive vs. exclusive hit counts.
Profile: minimal-cpu-profile-nodes-parent-child.cpuprofile
DevTools Performance Tab:

This view shows only part of the node tree. In the samples array, node 6 appears as the last visible node. From that point, the full tree can be reconstructed using the parent and children properties. Leaf nodes from 3 onward are not visible in the chart because walking up from node 6 does not reach them.
CallFrame
A callFrame objects contains source location details, such as file, function name, line, column that DevTools displays. In a flame chart, the call depth determines vertical stacking, with deeper calls shown lower in the visualization.
Source Location and label
The callFrame object provides details about the source code location and helps identify functions in the DevTools profiler.
functionName: The name of the executed function.
url: The file path or URL of the script. This also determines the color-coding of frames in the flame chart (same URL means same color).
scriptId: A unique identifier for the script.
lineNumber: The line number within the script.
columnNumber: The column number within the script.
Profile: minimal-cpu-profile-nodes-call-frame-source-location.cpuprofile
DevTools Performance Tab:

URL and coloring
The url property in the callFrame object is crucial for visualizing a profile in DevTools. It determines the color-coding of frames in the flame chart, with frames from the same URL sharing the same color. This helps quickly identify which parts of your code are executing and how they relate to each other.
Profile: minimal-cpu-profile-nodes-call-frame-url.cpuprofile
DevTools Performance Tab:

Synthetic and Internal Frames
Synthetic frames are not actual lines from your code but markers added by the V8 engine (the JavaScript engine in Chrome and Node.js). These frames provide important context about the execution environment and system-level operations.
A synthetic frame has functionName in parentheses to represent entry points, top-level script evaluation, idle time, or garbage collection cycles. Its scriptId is always "0", the url is an empty string (""), and lineNumber and columnNumber are typically -1. These frames help distinguish between user code execution and runtime overhead in performance analysis.
What they represent:
Frame Name | What It Means |
|---|
(root)
| The very start of the profile session (entry point) |
(program)
|
Your top-level script (code not inside any function) |
(idle)
| Time when nothing is happening — app is waiting |
(garbage collector)
| Time spent cleaning memory — V8 is reclaiming RAM |
Profile: minimal-cpu-profile-nodes-call-frame-synthetic-frames.cpuprofile
DevTools Performance Tab:

Conclusion
Demystifying the core data structures behind CPU profiles is an important step toward effective performance analysis in Chrome DevTools. With this knowledge, you will find it easier to read and use actual profiling output in your daily work. The next article will show these concepts in action with real-world profiling examples and hands-on analysis.
If you need expert help with profiling or a full performance audit, our team is here to assist.