Pyroscope: Fix incorrect rate calculation from flamegraph totals (#110470)

* fix(pyroscope): remove incorrect rate calculation from flamegraph totals

* update CHANGELOG.md
This commit is contained in:
Marc Sanmiquel
2025-09-03 08:15:06 +01:00
committed by GitHub
parent cb3fc369fe
commit cb77e97996
3 changed files with 15 additions and 34 deletions
+1
View File
@@ -13,6 +13,7 @@
- **Alerting:** Fix active time intervals when time interval is renamed [#108547](https://github.com/grafana/grafana/pull/108547), [@yuri-tceretian](https://github.com/yuri-tceretian)
- **Alerting:** Fix subpath handling in the alerting package [#109505](https://github.com/grafana/grafana/pull/109505), [@konrad147](https://github.com/konrad147)
- **Config:** Fix date_formats options being moved to a different section [#109366](https://github.com/grafana/grafana/pull/109366), [@joshhunt](https://github.com/joshhunt)
- **Pyroscope:** Fix flamegraph totals showing incorrect values after rate aggregation changes [#110470](https://github.com/grafana/grafana/pull/110470), [@marcsanmiquel](https://github.com/marcsanmiquel)
<!-- 12.1.1 END -->
<!-- 12.0.4 START -->
+4 -20
View File
@@ -357,15 +357,7 @@ type CustomMeta struct {
// dataFrame to again basically walking depth first over the tree/profile.
func treeToNestedSetDataFrame(tree *ProfileTree, unit string, stepDurationSec float64, profileTypeID string) *data.Frame {
frame := data.NewFrame("response")
frameMeta := &data.FrameMeta{PreferredVisualization: "flamegraph"}
// Add metadata when rate calculation is applied
if isCumulativeProfile(profileTypeID) && stepDurationSec > 0 {
frameMeta.Custom = map[string]interface{}{
"rateCalculated": true,
}
}
frame.Meta = frameMeta
frame.Meta = &data.FrameMeta{PreferredVisualization: "flamegraph"}
levelField := data.NewField("level", nil, []int64{})
valueField := data.NewField("value", nil, []int64{})
@@ -382,17 +374,9 @@ func treeToNestedSetDataFrame(tree *ProfileTree, unit string, stepDurationSec fl
if tree != nil {
walkTree(tree, func(tree *ProfileTree) {
levelField.Append(int64(tree.Level))
// Apply rate calculation for cumulative profiles
value := tree.Value
self := tree.Self
if isCumulativeProfile(profileTypeID) && stepDurationSec > 0 {
value = int64(float64(value) / stepDurationSec)
self = int64(float64(self) / stepDurationSec)
}
valueField.Append(value)
selfField.Append(self)
// Flamegraphs show cumulative values without rate calculation
valueField.Append(tree.Value)
selfField.Append(tree.Self)
labelField.Append(tree.Name)
})
}
@@ -229,15 +229,13 @@ func Test_treeToNestedDataFrame(t *testing.T) {
require.Equal(t, 0, frame.Fields[0].Len())
})
t.Run("rateCalculated metadata for cumulative profile", func(t *testing.T) {
t.Run("no rateCalculated metadata for flamegraph", func(t *testing.T) {
tree := &ProfileTree{
Value: 100, Level: 0, Self: 1, Name: "root",
}
frame := treeToNestedSetDataFrame(tree, "short", 15.0, "process_cpu:cpu:nanoseconds:cpu:nanoseconds")
require.NotNil(t, frame.Meta)
require.NotNil(t, frame.Meta.Custom)
custom := frame.Meta.Custom.(map[string]interface{})
require.Equal(t, true, custom["rateCalculated"])
require.Nil(t, frame.Meta.Custom)
})
t.Run("no rateCalculated metadata for instant profile", func(t *testing.T) {
@@ -249,26 +247,24 @@ func Test_treeToNestedDataFrame(t *testing.T) {
require.Nil(t, frame.Meta.Custom)
})
t.Run("CPU time keeps original units for tree data", func(t *testing.T) {
t.Run("CPU time keeps original values and units for flamegraph", func(t *testing.T) {
tree := &ProfileTree{
Value: 3000000000, Level: 0, Self: 1500000000, Name: "root", // 3s total, 1.5s self in nanoseconds
}
// Test CPU profile (should keep nanoseconds for flamegraph, no unit conversion)
// Test CPU profile flamegraph - should keep original cumulative values and units
frame := treeToNestedSetDataFrame(tree, "ns", 15.0, "process_cpu:cpu:nanoseconds:cpu:nanoseconds")
// Check unit remains as nanoseconds (no conversion for flamegraphs)
// Check unit remains as nanoseconds
require.Equal(t, "ns", frame.Fields[1].Config.Unit)
require.Equal(t, "ns", frame.Fields[2].Config.Unit)
// Check values were rate calculated but not unit converted: 3000000000/15 = 200000000, 1500000000/15 = 100000000
require.Equal(t, int64(200000000), frame.Fields[1].At(0))
require.Equal(t, int64(100000000), frame.Fields[2].At(0))
// Check values are NOT rate calculated - flamegraphs show cumulative totals
require.Equal(t, int64(3000000000), frame.Fields[1].At(0))
require.Equal(t, int64(1500000000), frame.Fields[2].At(0))
// Check metadata shows rate was calculated
// Check metadata shows rate was NOT calculated for flamegraphs
require.NotNil(t, frame.Meta)
require.NotNil(t, frame.Meta.Custom)
custom := frame.Meta.Custom.(map[string]interface{})
require.Equal(t, true, custom["rateCalculated"])
require.Nil(t, frame.Meta.Custom)
})
}