A four-state profiling flame graph — bar width is time, rows are stack depth, click to zoom with a breadcrumb back, and a search that highlights matches across the whole profile with a matched-time readout.
Preview in your theme
Loading preview…
This component is part of Pro
The install command, prompt and concept map are included with a Pro license — one purchase unlocks every Pro component, forever.
status="ready" · 1,380 ms over 30 frames and 10 levels; 29 bars are painted. The 2 ms formatNumber inside Card is 0.14% of the profile — under the 0.2% width floor — so it is the one frame the footnote reports as not drawn, and Card carries the marked edge that says where to zoom. Type "pg." to light 4 frames worth 37.6% of the profile. Last zoom: nothing yet.
GET /dashboard · p95 sampleClick a bar to zoom · arrows walk the rows · Backspace goes back · Escape resets
1.38 s in view · 30 frames · 10 of 10 levels
Flame graph of All frames: 1.38 s across 30 frames, 10 of 10 levels drawn. Heaviest by self time: pg.awaitResult, 271 ms, called from db.query widgets. 1 frame is too narrow or too deep to draw; zoom in to reach them. Left and right walk a row, up and down change level, Enter zooms into a frame, Backspace goes back one level, Escape resets the zoom. The table that follows lists the heaviest frames by self time.
Width is time, never rank: a frame sits on the frame that called it, and the gap left at its top edge is its own self time. Click or press Enter to zoom in, Backspace to step back, Escape to reset.
1 frame is not drawn — narrower than 0.2% of this view, or past the 10 rows shown. Zoom into their caller and they widen back into existence.
1 frame keeps callees behind a marked edge.
The 20 heaviest frames inside All frames by self time, with the frame that called them.
Frame
Called from
Depth
Self
Total
Share of All frames
pg.awaitResult
db.query widgets
5
271 ms
271 ms
19.6%
pg.awaitResult
db.query metrics
5
209 ms
209 ms
15.1%
gc: scavenge
All frames
1
96 ms
96 ms
7%
redis.mget
loadDashboard
4
88 ms
88 ms
6.4%
Array.map
path.build
9
61 ms
61 ms
4.4%
Card
Grid
6
55 ms
198 ms
14.3%
jwt.verify
authenticate
4
54 ms
54 ms
3.9%
Grid
Dashboard
5
48 ms
246 ms
17.8%
JSON.stringify
serialize
5
44 ms
44 ms
3.2%
NavItem x12
Sidebar
6
44 ms
44 ms
3.2%
metrics.flush
GET /dashboard
2
41 ms
41 ms
3%
Dashboard
renderToPipeableStream
4
38 ms
352 ms
25.5%
d3.scaleLinear
Chart
8
33 ms
33 ms
2.4%
renderToPipeableStream
router.handle
3
29 ms
381 ms
27.6%
path.build
Chart
8
28 ms
89 ms
6.4%
GET /dashboard
All frames
1
27 ms
1.28 s
93%
db.query users
authenticate
4
27 ms
27 ms
2%
Sidebar
Dashboard
5
24 ms
68 ms
4.9%
compress
router.handle
3
24 ms
24 ms
1.7%
pg.parse
db.query widgets
5
21 ms
21 ms
1.5%
Deep and wide: 12,440 samples, 81 frames, 29 levels — 12 rows drawn, 45 frames too deep or too narrow to paint, and the recursion frame on the last row carries the marked edge. unit="samples", so values print compactly with the noun instead of on the ms ladder.
12.4K samples in view · 81 frames · 12 of 29 levels
11 frames match “expect” · 12.9% of All samples · 5 outside this zoom
Flame graph of All samples: 12.4K samples across 81 frames, 12 of 29 levels drawn. Heaviest by self time: peekToken, 1.4K samples, called from parseExpression. 45 frames are too narrow or too deep to draw; zoom in to reach them. Left and right walk a row, up and down change level, Enter zooms into a frame, Backspace goes back one level, Escape resets the zoom. The table that follows lists the heaviest frames by self time.
Width is time, never rank: a frame sits on the frame that called it, and the gap left at its top edge is its own self time. Click or press Enter to zoom in, Backspace to step back, Escape to reset.
45 frames are not drawn — narrower than 0.2% of this view, or past the 12 rows shown. Zoom into their caller and they widen back into existence.
1 frame keeps callees behind a marked edge.
The 20 heaviest frames inside All samples by self time, with the frame that called them.
Frame
Called from
Depth
Self
Total
Share of All samples
peekToken
parseExpression
2
1.4K samples
1.4K samples
11.6%
parseExpression
All samples
1
898 samples
11.6K samples
93.2%
tokenize
All samples
1
840 samples
840 samples
6.8%
parseBinary
parseExpression
2
770 samples
8.6K samples
68.9%
recordSpan
parsePrimary
5
607 samples
607 samples
4.9%
parseUnary
parseBinary
3
537 samples
7.3K samples
58.5%
parsePrimary
parseUnary
4
524 samples
6.1K samples
49.4%
expect
parseBinary
3
521 samples
521 samples
4.2%
peekToken
parsePrimary
5
518 samples
518 samples
4.2%
expect
parseExpression
2
449 samples
449 samples
3.6%
parseCall
parsePrimary
5
431 samples
4.5K samples
36.1%
makeNode
parseUnary
4
394 samples
394 samples
3.2%
parseExpression
parseCall
6
301 samples
3.7K samples
29.7%
parseUnary
parseBinary
8
299 samples
2.3K samples
18.4%
makeNode
parseExpression
2
246 samples
246 samples
2%
peekToken
parseCall
6
218 samples
218 samples
1.8%
recordSpan
parseUnary
4
200 samples
200 samples
1.6%
recordSpan
parseBinary
8
198 samples
198 samples
1.6%
expect
parseExpression
7
195 samples
195 samples
1.6%
parseBinary
parseExpression
7
183 samples
2.9K samples
23.7%
status="empty" · the profiler ran and recorded nothing — a different sentence from "still sampling".
GET /dashboard · p95 sampleNo samples in this window
Nothing was sampled
A flame graph needs at least one frame with time on it.
status="loading" · a pyramid of pulsing bars on the same row grid as the real thing, aria-hidden, plus one sr-only status line. No timers.
GET /dashboard · p95 sampleCollecting samples
Loading profile
status="error" with onRetry · press Try again to swap to ready. Omit onRetry and the button never renders.
GET /dashboard · p95 sampleProfiler socket timed out
Couldn't load this profile
The profiler didn't answer.
Degenerate: two queries summing to 640 ms under a 520 ms job (normalised onto it, so nothing paints outside its caller), a reported self time that disagrees with the picture, a zero-time step, a negative clock correction, and a 53-character name that has to truncate. The footnote names every one of them.
Nightly batch · trace is inconsistentExport includes retired and unmeasured steps
940 ms in view · 6 frames · 3 of 3 levels
Flame graph of Batch window: 940 ms across 6 frames, 3 of 3 levels drawn. Heaviest by self time: db.query invoices, 380 ms, called from nightly-invoice-rollup. Left and right walk a row, up and down change level, Enter zooms into a frame, Backspace goes back one level, Escape resets the zoom. The table that follows lists the heaviest frames by self time.
Width is time, never rank: a frame sits on the frame that called it, and the gap left at its top edge is its own self time. Click or press Enter to zoom in, Backspace to step back, Escape to reset.
Inconsistent in the trace: 2 frames with no positive time, 1 whose callees sum past it (normalised to fit), 1 whose reported self time disagrees with the picture.
The 5 heaviest frames inside Batch window by self time, with the frame that called them.
Degenerate: two sibling frames ship the same id, so one of them could never be addressed. The contract names the collision instead of drawing a branch nobody can open.
Checkout handler · ids collide
This profile can't be drawn
Two frames under "handleRequest" share the id "step". A frame is addressed by the path of ids that reaches it, so one of the two could never be opened.
Degenerate: one frame, no callees — every millisecond is self time and there is nothing to zoom into. Two rows tall, and still a real graph.
Checksum worker · flat profileA tight loop has no hierarchy to show
1 s in view · 2 frames · 2 of 2 levels
Flame graph of Worker tick: 1 s across 2 frames, 2 of 2 levels drawn. Heaviest by self time: computeChecksum, 1 s, called from Worker tick. Left and right walk a row, up and down change level, Enter zooms into a frame, Backspace goes back one level, Escape resets the zoom. The table that follows lists the heaviest frames by self time.
Width is time, never rank: a frame sits on the frame that called it, and the gap left at its top edge is its own self time. Click or press Enter to zoom in, Backspace to step back, Escape to reset.
The 1 heaviest frames inside Worker tick by self time, with the frame that called them.
Frame
Called from
Depth
Self
Total
Share of Worker tick
computeChecksum
Worker tick
1
1 s
1 s
100%
maxDepth={6} · the same request capped at six rows: 7 frames drop out of the picture and the 2 frames holding them pick up the marked edge, which still opens on click.
GET /dashboard · p95 sampleSix rows, the rest behind a marked edge
1.38 s in view · 30 frames · 6 of 10 levels
Flame graph of All frames: 1.38 s across 30 frames, 6 of 10 levels drawn. Heaviest by self time: pg.awaitResult, 271 ms, called from db.query widgets. 7 frames are too narrow or too deep to draw; zoom in to reach them. Left and right walk a row, up and down change level, Enter zooms into a frame, Backspace goes back one level, Escape resets the zoom. The table that follows lists the heaviest frames by self time.
Width is time, never rank: a frame sits on the frame that called it, and the gap left at its top edge is its own self time. Click or press Enter to zoom in, Backspace to step back, Escape to reset.
7 frames are not drawn — narrower than 0.2% of this view, or past the 6 rows shown. Zoom into their caller and they widen back into existence.
2 frames keep callees behind a marked edge.
The 20 heaviest frames inside All frames by self time, with the frame that called them.
Frame
Called from
Depth
Self
Total
Share of All frames
pg.awaitResult
db.query widgets
5
271 ms
271 ms
19.6%
pg.awaitResult
db.query metrics
5
209 ms
209 ms
15.1%
gc: scavenge
All frames
1
96 ms
96 ms
7%
redis.mget
loadDashboard
4
88 ms
88 ms
6.4%
Array.map
path.build
9
61 ms
61 ms
4.4%
Card
Grid
6
55 ms
198 ms
14.3%
jwt.verify
authenticate
4
54 ms
54 ms
3.9%
Grid
Dashboard
5
48 ms
246 ms
17.8%
JSON.stringify
serialize
5
44 ms
44 ms
3.2%
NavItem x12
Sidebar
6
44 ms
44 ms
3.2%
metrics.flush
GET /dashboard
2
41 ms
41 ms
3%
Dashboard
renderToPipeableStream
4
38 ms
352 ms
25.5%
d3.scaleLinear
Chart
8
33 ms
33 ms
2.4%
renderToPipeableStream
router.handle
3
29 ms
381 ms
27.6%
path.build
Chart
8
28 ms
89 ms
6.4%
GET /dashboard
All frames
1
27 ms
1.28 s
93%
db.query users
authenticate
4
27 ms
27 ms
2%
Sidebar
Dashboard
5
24 ms
68 ms
4.9%
compress
router.handle
3
24 ms
24 ms
1.7%
pg.parse
db.query widgets
5
21 ms
21 ms
1.5%
orientation="icicle" with rowHeight={18} · the base bar hangs from the top and stacks grow down, the way a DevTools panel reads. The arrows follow the picture, so up still means "toward the caller" here.
GET /dashboard · p95 sampleIcicle orientation, tighter rows
1.38 s in view · 30 frames · 10 of 10 levels
Flame graph of All frames: 1.38 s across 30 frames, 10 of 10 levels drawn. Heaviest by self time: pg.awaitResult, 271 ms, called from db.query widgets. 1 frame is too narrow or too deep to draw; zoom in to reach them. Left and right walk a row, up and down change level, Enter zooms into a frame, Backspace goes back one level, Escape resets the zoom. The table that follows lists the heaviest frames by self time.
Width is time, never rank: a frame hangs under its caller, and the gap left at its bottom edge is its own self time. Click or press Enter to zoom in, Backspace to step back, Escape to reset.
1 frame is not drawn — narrower than 0.2% of this view, or past the 10 rows shown. Zoom into their caller and they widen back into existence.
1 frame keeps callees behind a marked edge.
The 20 heaviest frames inside All frames by self time, with the frame that called them.