Skip to content

Commit 66bad0d

Browse files
rogeralsingclaude
andcommitted
Inline timeline with call tree
Render timeline bars on the same line as call tree nodes instead of using a separate table. Properly truncates names and aligns timeline. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent db23801 commit 66bad0d

File tree

4 files changed

+354
-353
lines changed

4 files changed

+354
-353
lines changed

src/ProfileTool/Cpu/CallTreeNode.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
namespace Asynkron.Profiler;
44

5-
internal sealed class CallTreeNode
5+
public sealed class CallTreeNode
66
{
77
public CallTreeNode(int frameIdx, string name)
88
{
99
FrameIdx = frameIdx;
1010
Name = name;
11+
MinStart = double.MaxValue;
12+
MaxEnd = double.MinValue;
1113
}
1214

1315
public int FrameIdx { get; }
@@ -16,4 +18,28 @@ public CallTreeNode(int frameIdx, string name)
1618
public double Self { get; set; }
1719
public int Calls { get; set; }
1820
public Dictionary<int, CallTreeNode> Children { get; } = new();
21+
22+
/// <summary>
23+
/// Earliest start time of any invocation of this node (for timeline rendering).
24+
/// </summary>
25+
public double MinStart { get; set; }
26+
27+
/// <summary>
28+
/// Latest end time of any invocation of this node (for timeline rendering).
29+
/// </summary>
30+
public double MaxEnd { get; set; }
31+
32+
/// <summary>
33+
/// Updates timing bounds with a new span.
34+
/// </summary>
35+
public void UpdateTiming(double startMs, double endMs)
36+
{
37+
if (startMs < MinStart) MinStart = startMs;
38+
if (endMs > MaxEnd) MaxEnd = endMs;
39+
}
40+
41+
/// <summary>
42+
/// Whether this node has valid timing data.
43+
/// </summary>
44+
public bool HasTiming => MinStart < double.MaxValue && MaxEnd > double.MinValue;
1945
}

src/ProfileTool/Cpu/CpuTimelineFormatter.cs

Lines changed: 0 additions & 294 deletions
This file was deleted.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
namespace Asynkron.Profiler;
2+
3+
/// <summary>
4+
/// Context for timeline rendering with call tree.
5+
/// </summary>
6+
public sealed class TimelineContext
7+
{
8+
public double RootStart { get; init; }
9+
public double RootEnd { get; init; }
10+
public int BarWidth { get; init; }
11+
public int TextWidth { get; init; }
12+
public int MaxNameLength { get; init; }
13+
public int MaxDepth { get; init; }
14+
15+
public double RootDuration => RootEnd - RootStart;
16+
17+
/// <summary>
18+
/// Calculate padding needed to align timeline separator at a fixed X position.
19+
/// The tree guides add ~4 chars per level (branches can add more), so deeper nodes need less padding.
20+
/// </summary>
21+
public int GetPaddingForDepth(int depth, int visibleTextLength)
22+
{
23+
// Tree guides add ~4 chars per level (can be more with branches)
24+
var treeGuideWidth = depth * 4;
25+
// Target X position is TextWidth (which is the timeline X position)
26+
// Content so far = tree guides + visible text
27+
var contentWidth = treeGuideWidth + visibleTextLength;
28+
return Math.Max(0, TextWidth - contentWidth);
29+
}
30+
}

0 commit comments

Comments
 (0)