Data Structure

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:

minimal-cpu-profile

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:

minimal-cpu-profile-profile-length

Time deltas

Profile: minimal-cpu-profile-timing-data-time-deltas.cpuprofile

DevTools Performance Tab:

minimal-cpu-profile-timing-data-time-deltas

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:

minimal-cpu-profile-timing-data-samples

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:

minimal-cpu-profile-parent-children-ids

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:

minimal-cpu-profile-call-frame-source-location

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:

minimal-cpu-profile-nodes-call-frame-url

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:

minimal-cpu-profile-synthetic-frames

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.