Skip to content

Commit 7381b14

Browse files
Copilotbrianrob
andauthored
Fix Histogram.AddMetric losing values after single-bucket to array transition (#2337)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: brianrob <6210322+brianrob@users.noreply.github.com> Co-authored-by: Brian Robbins <brianrob@microsoft.com>
1 parent bba3596 commit 7381b14

File tree

2 files changed

+141
-11
lines changed

2 files changed

+141
-11
lines changed

src/TraceEvent/Stacks/Histogram.cs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,28 @@ public void AddSample(StackSourceSample sample)
4747
/// <param name="bucket">The bucket to add to.</param>
4848
public void AddMetric(float metric, int bucket)
4949
{
50-
Debug.Assert(0 <= bucket && bucket < Count);
50+
Debug.Assert(0 <= bucket && bucket < Count, $"Bucket index is out of range. Bucket: {bucket}, Count: {Count}");
5151

52-
if (m_singleBucketNum < 0)
53-
{
54-
m_singleBucketNum = bucket;
55-
}
56-
57-
if (m_singleBucketNum == bucket)
58-
{
59-
m_singleBucketValue += metric;
60-
return;
61-
}
6252
if (m_buckets == null)
6353
{
54+
// We have not expanded to multiple buckets yet
55+
if (m_singleBucketNum < 0)
56+
{
57+
m_singleBucketNum = bucket;
58+
}
59+
60+
if (m_singleBucketNum == bucket)
61+
{
62+
m_singleBucketValue += metric;
63+
return;
64+
}
65+
66+
// Need to transition to array mode
6467
m_buckets = new float[Count];
6568
m_buckets[m_singleBucketNum] = m_singleBucketValue;
69+
// Clear the single bucket tracking since we're now using the array
70+
m_singleBucketNum = -1;
71+
m_singleBucketValue = 0;
6672
}
6773
m_buckets[bucket] += metric;
6874
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
using Microsoft.Diagnostics.Tracing.Stacks;
2+
using Xunit;
3+
4+
namespace TraceEventTests.Stacks
5+
{
6+
public class HistogramTests
7+
{
8+
/// <summary>
9+
/// Test for the bug where AddMetric doesn't properly transition from single-bucket to array mode.
10+
/// After creating the array, subsequent calls to AddMetric with the original bucket should update
11+
/// the array, not the m_singleBucketValue.
12+
/// </summary>
13+
[Fact]
14+
public void AddMetric_TransitionToArrayMode_OriginalBucketUpdatesArray()
15+
{
16+
// Create a simple histogram controller for testing
17+
var stackSource = new CopyStackSource();
18+
var callTree = new CallTree(ScalingPolicyKind.TimeMetric)
19+
{
20+
StackSource = stackSource
21+
};
22+
var controller = new TimeHistogramController(callTree, 0, 100);
23+
var histogram = new Histogram(controller);
24+
25+
// Step 1: Add metric to bucket 0 (single bucket mode)
26+
histogram.AddMetric(1.0f, 0);
27+
Assert.Equal(1.0f, histogram[0]);
28+
29+
// Step 2: Add metric to bucket 1 (should trigger array creation)
30+
histogram.AddMetric(2.0f, 1);
31+
Assert.Equal(1.0f, histogram[0]); // Bucket 0 should still have 1.0
32+
Assert.Equal(2.0f, histogram[1]); // Bucket 1 should have 2.0
33+
34+
// Step 3: Add metric to bucket 0 again (this is where the bug manifests)
35+
// The value should be added to the array, not to m_singleBucketValue
36+
histogram.AddMetric(3.0f, 0);
37+
Assert.Equal(4.0f, histogram[0]); // Bucket 0 should now have 1.0 + 3.0 = 4.0
38+
Assert.Equal(2.0f, histogram[1]); // Bucket 1 should still have 2.0
39+
}
40+
41+
/// <summary>
42+
/// Test that multiple buckets work correctly after transitioning to array mode.
43+
/// </summary>
44+
[Fact]
45+
public void AddMetric_MultipleBuckets_AllValuesCorrect()
46+
{
47+
var stackSource = new CopyStackSource();
48+
var callTree = new CallTree(ScalingPolicyKind.TimeMetric)
49+
{
50+
StackSource = stackSource
51+
};
52+
var controller = new TimeHistogramController(callTree, 0, 100);
53+
var histogram = new Histogram(controller);
54+
55+
// Add to multiple buckets in various order
56+
histogram.AddMetric(10.0f, 5);
57+
histogram.AddMetric(20.0f, 10);
58+
histogram.AddMetric(30.0f, 5); // Add more to bucket 5
59+
histogram.AddMetric(40.0f, 15);
60+
histogram.AddMetric(50.0f, 10); // Add more to bucket 10
61+
62+
// Verify all values are correct
63+
Assert.Equal(0.0f, histogram[0]);
64+
Assert.Equal(40.0f, histogram[5]); // 10 + 30
65+
Assert.Equal(70.0f, histogram[10]); // 20 + 50
66+
Assert.Equal(40.0f, histogram[15]);
67+
}
68+
69+
/// <summary>
70+
/// Test single bucket mode (when only one bucket is ever used).
71+
/// </summary>
72+
[Fact]
73+
public void AddMetric_SingleBucketOnly_ValuesCorrect()
74+
{
75+
var stackSource = new CopyStackSource();
76+
var callTree = new CallTree(ScalingPolicyKind.TimeMetric)
77+
{
78+
StackSource = stackSource
79+
};
80+
var controller = new TimeHistogramController(callTree, 0, 100);
81+
var histogram = new Histogram(controller);
82+
83+
// Add multiple values to the same bucket
84+
histogram.AddMetric(1.0f, 7);
85+
histogram.AddMetric(2.0f, 7);
86+
histogram.AddMetric(3.0f, 7);
87+
88+
// Verify the value accumulated correctly
89+
Assert.Equal(6.0f, histogram[7]);
90+
Assert.Equal(0.0f, histogram[0]);
91+
Assert.Equal(0.0f, histogram[10]);
92+
}
93+
94+
/// <summary>
95+
/// Test AddScaled works correctly after histogram transition to array mode.
96+
/// </summary>
97+
[Fact]
98+
public void AddScaled_AfterArrayTransition_ValuesCorrect()
99+
{
100+
var stackSource = new CopyStackSource();
101+
var callTree = new CallTree(ScalingPolicyKind.TimeMetric)
102+
{
103+
StackSource = stackSource
104+
};
105+
var controller = new TimeHistogramController(callTree, 0, 100);
106+
107+
// Create source histogram with multiple buckets
108+
var sourceHistogram = new Histogram(controller);
109+
sourceHistogram.AddMetric(10.0f, 5);
110+
sourceHistogram.AddMetric(20.0f, 10);
111+
112+
// Create target histogram that starts in single-bucket mode
113+
var targetHistogram = new Histogram(controller);
114+
targetHistogram.AddMetric(5.0f, 5);
115+
116+
// Add scaled source to target - this should transition target to array mode
117+
targetHistogram.AddScaled(sourceHistogram);
118+
119+
// Verify values
120+
Assert.Equal(15.0f, targetHistogram[5]); // 5 + 10
121+
Assert.Equal(20.0f, targetHistogram[10]); // 0 + 20
122+
}
123+
}
124+
}

0 commit comments

Comments
 (0)