Skip to content

Commit 2534581

Browse files
authored
Fix root cumulative value in flamegraph trimming (#6028)
* Fix root cumulative value in flamegraph trimming Updates the flamegraph trimming logic to correctly set the root row's cumulative value as the sum of its direct children's cumulative values. * fix failing test * use dynamic sizing for value type * implement feedback on using largest type value
1 parent 629879e commit 2534581

File tree

2 files changed

+99
-17
lines changed

2 files changed

+99
-17
lines changed

pkg/query/flamegraph_arrow.go

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ func generateFlamegraphArrowRecord(ctx context.Context, mem memory.Allocator, tr
357357
fb.trimmedLocationLine.AppendNull()
358358
fb.trimmedFunctionStartLine = array.NewUint8Builder(fb.pool)
359359
fb.trimmedFunctionStartLine.AppendNull()
360-
fb.trimmedCumulative = array.NewUint8Builder(fb.pool)
360+
fb.trimmedCumulative = array.NewUint64Builder(fb.pool)
361361
fb.trimmedCumulative.AppendNull()
362362
fb.trimmedFlat = array.NewUint8Builder(fb.pool)
363363
fb.trimmedFlat.AppendNull()
@@ -1646,8 +1646,7 @@ func (fb *flamegraphBuilder) trim(ctx context.Context, tracer trace.Tracer, thre
16461646
trimmedFunctionNameIndices := array.NewInt32Builder(fb.pool)
16471647
trimmedFunctionSystemNameIndices := array.NewInt32Builder(fb.pool)
16481648
trimmedFunctionFilenameIndices := array.NewInt32Builder(fb.pool)
1649-
trimmedCumulativeType := smallestUnsignedTypeFor(largestCumulativeValue)
1650-
trimmedCumulative := array.NewBuilder(fb.pool, trimmedCumulativeType)
1649+
trimmedCumulative := builder.NewOptInt64Builder(arrow.PrimitiveTypes.Int64)
16511650
trimmedFlatType := smallestUnsignedTypeFor(largestFlatValue)
16521651
trimmedFlat := array.NewBuilder(fb.pool, trimmedFlatType)
16531652
trimmedDiffType := smallestSignedTypeFor(smallestDiffValue, largestDiffValue)
@@ -1660,7 +1659,8 @@ func (fb *flamegraphBuilder) trim(ctx context.Context, tracer trace.Tracer, thre
16601659
// Parent indices use int32 to allow for -1 (null parent)
16611660
trimmedParent := array.NewInt32Builder(fb.pool)
16621661

1663-
valueOffset := array.NewBuilder(fb.pool, trimmedCumulativeType)
1662+
valueOffsetType := smallestUnsignedTypeFor(largestCumulativeValue)
1663+
valueOffset := array.NewBuilder(fb.pool, valueOffsetType)
16641664

16651665
releasers = append(releasers,
16661666
trimmedMappingFileIndices,
@@ -1705,6 +1705,7 @@ func (fb *flamegraphBuilder) trim(ctx context.Context, tracer trace.Tracer, thre
17051705
trimmingQueue.elements = trimmingQueue.elements[:0]
17061706
trimmingQueue.push(trimmingElement{row: 0})
17071707

1708+
rootCumulativeTrimmedValue := int64(0)
17081709
// keep processing new elements until the queue is empty
17091710
for trimmingQueue.len() > 0 {
17101711
// pop the first item from the queue
@@ -1728,18 +1729,7 @@ func (fb *flamegraphBuilder) trim(ctx context.Context, tracer trace.Tracer, thre
17281729

17291730
// The following two will never be null.
17301731
cum := fb.builderCumulative.Value(te.row)
1731-
switch b := trimmedCumulative.(type) {
1732-
case *array.Uint64Builder:
1733-
b.Append(uint64(cum))
1734-
case *array.Uint32Builder:
1735-
b.Append(uint32(cum))
1736-
case *array.Uint16Builder:
1737-
b.Append(uint16(cum))
1738-
case *array.Uint8Builder:
1739-
b.Append(uint8(cum))
1740-
default:
1741-
panic(fmt.Errorf("unsupported type %T", b))
1742-
}
1732+
trimmedCumulative.Append(cum)
17431733

17441734
switch b := trimmedFlat.(type) {
17451735
case *array.Uint64Builder:
@@ -1812,6 +1802,10 @@ func (fb *flamegraphBuilder) trim(ctx context.Context, tracer trace.Tracer, thre
18121802
} else {
18131803
trimmedChildren[te.parent] = append(trimmedChildren[te.parent], row)
18141804
}
1805+
// Only accumulate direct children of root (parent row 0) for root's cumulative value
1806+
if te.parent == 0 {
1807+
rootCumulativeTrimmedValue += cum
1808+
}
18151809
}
18161810

18171811
cumThreshold := float32(cum) * threshold
@@ -1921,6 +1915,14 @@ func (fb *flamegraphBuilder) trim(ctx context.Context, tracer trace.Tracer, thre
19211915
}
19221916
fb.labels = trimmedLabels
19231917

1918+
trimmedCumulative.Set(0, rootCumulativeTrimmedValue)
1919+
tempTrimmedCumulative := array.NewBuilder(fb.pool, smallestUnsignedTypeFor(uint64(rootCumulativeTrimmedValue)))
1920+
for i := 0; i < trimmedCumulative.Len(); i++ {
1921+
copyInt64BuilderValueToUnknownUnsigned(trimmedCumulative, tempTrimmedCumulative, i)
1922+
}
1923+
1924+
trimmedCumulative.Release()
1925+
19241926
release(
19251927
fb.builderLabelsOnly,
19261928
fb.builderLabelsExist,
@@ -1939,7 +1941,7 @@ func (fb *flamegraphBuilder) trim(ctx context.Context, tracer trace.Tracer, thre
19391941
fb.builderInlined = trimmedInlined
19401942
fb.trimmedLocationLine = trimmedLocationLine
19411943
fb.trimmedFunctionStartLine = trimmedFunctionStartLine
1942-
fb.trimmedCumulative = trimmedCumulative
1944+
fb.trimmedCumulative = tempTrimmedCumulative
19431945
fb.trimmedFlat = trimmedFlat
19441946
fb.trimmedDiff = trimmedDiff
19451947
fb.trimmedChildren = trimmedChildren

pkg/query/flamegraph_arrow_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,86 @@ func TestGenerateFlamegraphArrowTrimming(t *testing.T) {
931931
fc.compare(expectedColumns)
932932
}
933933

934+
func TestGenerateFlamegraphArrowTrimmingRootCumulative(t *testing.T) {
935+
ctx := context.Background()
936+
mem := memory.NewGoAllocator()
937+
var err error
938+
939+
mappings := []*pprofprofile.Mapping{{
940+
ID: 1,
941+
File: "a",
942+
}}
943+
944+
functions := []*pprofprofile.Function{{
945+
ID: 1,
946+
Name: "func1",
947+
}, {
948+
ID: 2,
949+
Name: "func2",
950+
}, {
951+
ID: 3,
952+
Name: "func3",
953+
}, {
954+
ID: 4,
955+
Name: "func4",
956+
}}
957+
958+
locations := []*pprofprofile.Location{{
959+
ID: 1,
960+
Mapping: mappings[0],
961+
Line: []pprofprofile.Line{{Function: functions[0]}},
962+
}, {
963+
ID: 2,
964+
Mapping: mappings[0],
965+
Line: []pprofprofile.Line{{Function: functions[1]}},
966+
}, {
967+
ID: 3,
968+
Mapping: mappings[0],
969+
Line: []pprofprofile.Line{{Function: functions[2]}},
970+
}, {
971+
ID: 4,
972+
Mapping: mappings[0],
973+
Line: []pprofprofile.Line{{Function: functions[3]}},
974+
}}
975+
976+
// Create a profile with:
977+
// root -> func1 (100) -> func2 (80) -> func3 (60)
978+
// -> func4 (10) [trimmed]
979+
p, err := PprofToSymbolizedProfile(
980+
profile.Meta{Duration: (10 * time.Second).Nanoseconds()},
981+
&pprofprofile.Profile{
982+
Sample: []*pprofprofile.Sample{{
983+
Location: []*pprofprofile.Location{locations[2], locations[1], locations[0]},
984+
Value: []int64{60},
985+
}, {
986+
Location: []*pprofprofile.Location{locations[3], locations[1], locations[0]},
987+
Value: []int64{10},
988+
}, {
989+
Location: []*pprofprofile.Location{locations[1], locations[0]},
990+
Value: []int64{20},
991+
}, {
992+
Location: []*pprofprofile.Location{locations[0]},
993+
Value: []int64{10},
994+
}},
995+
},
996+
0, []string{},
997+
)
998+
require.NoError(t, err)
999+
1000+
tracer := noop.NewTracerProvider().Tracer("")
1001+
1002+
fa, cumulative, _, _, err := generateFlamegraphArrowRecord(ctx, mem, tracer, p, []string{FlamegraphFieldFunctionName}, float32(0.5))
1003+
require.NoError(t, err)
1004+
defer fa.Release()
1005+
1006+
cumulativeCol := fa.Column(fa.Schema().FieldIndices("cumulative")[0]).(*array.Uint8)
1007+
rootCumulative := int64(cumulativeCol.Value(0))
1008+
1009+
require.Equal(t, cumulative, rootCumulative, "Returned cumulative should match root row cumulative value")
1010+
1011+
require.Equal(t, int64(100), rootCumulative, "Root cumulative should be 100 (sum of direct child func1)")
1012+
}
1013+
9341014
func TestParents(t *testing.T) {
9351015
p := parent(-1)
9361016
require.Equal(t, -1, p.Get())

0 commit comments

Comments
 (0)