Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/vroom/flamegraph.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (env *environment) postFlamegraph(w http.ResponseWriter, r *http.Request) {
s = sentry.StartSpan(ctx, "processing")
var ma *metrics.Aggregator
if body.GenerateMetrics {
agg := metrics.NewAggregator(maxUniqueFunctionsPerProfile, 5, minDepth)
agg := metrics.NewAggregator(maxUniqueFunctionsPerProfile, 5, int(minDepth), false)
ma = &agg
}
speedscope, err := flamegraph.GetFlamegraphFromCandidates(
Expand Down
40 changes: 23 additions & 17 deletions internal/flamegraph/flamegraph.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,27 +49,34 @@ func sumNodesSampleCount(nodes []*nodetree.Node) int {
return c
}

func annotateWithProfileExample(example examples.ExampleMetadata) func(n *nodetree.Node) {
return func(n *nodetree.Node) {
func annotateWithProfileExample(example examples.ExampleMetadata) func(n, m *nodetree.Node) {
return func(n, m *nodetree.Node) {
n.Profiles[example] = void
if n.WorstSelfTime < m.SelfTimeNS {
n.WorstSelfTime = m.SelfTimeNS
n.WorstProfile = example
}
}
}

func addCallTreeToFlamegraph(flamegraphTree *[]*nodetree.Node, callTree []*nodetree.Node, annotate func(n *nodetree.Node)) {
func addCallTreeToFlamegraph(flamegraphTree *[]*nodetree.Node, callTree []*nodetree.Node, annotate func(n, m *nodetree.Node)) {
for _, node := range callTree {
var currentNode *nodetree.Node
if existingNode := getMatchingNode(flamegraphTree, node); existingNode != nil {
currentNode = existingNode
currentNode.Occurrence += node.Occurrence
currentNode.SampleCount += node.SampleCount
currentNode.DurationNS += node.DurationNS
currentNode.SelfTimeNS += node.SelfTimeNS
currentNode.DurationsNS = append(currentNode.DurationsNS, node.DurationNS)
} else {
currentNode = node.ShallowCopyWithoutChildren()
currentNode.DurationsNS = []uint64{node.DurationNS}
*flamegraphTree = append(*flamegraphTree, currentNode)
}
addCallTreeToFlamegraph(&currentNode.Children, node.Children, annotate)
if node.SampleCount > sumNodesSampleCount(node.Children) {
annotate(currentNode)
annotate(currentNode, node)
}
}
}
Expand Down Expand Up @@ -436,12 +443,6 @@ func GetFlamegraphFromCandidates(
for _, callTree := range result.CallTrees {
addCallTreeToFlamegraph(&flamegraphTree, callTree, annotate)
}
// if metrics aggregator is not null, while we're at it,
// compute the metrics as well
if ma != nil {
functions := metrics.CapAndFilterFunctions(metrics.ExtractFunctionsFromCallTrees(result.CallTrees, ma.MinDepth), int(ma.MaxUniqueFunctions), true)
ma.AddFunctions(functions, example)
}

transactionProfileSpan.Finish()
} else if result, ok := res.(chunk.CallTreesReadJobResult); ok {
Expand Down Expand Up @@ -469,13 +470,6 @@ func GetFlamegraphFromCandidates(
annotate := annotateWithProfileExample(example)

addCallTreeToFlamegraph(&flamegraphTree, callTree, annotate)

// if metrics aggregator is not null, while we're at it,
// compute the metrics as well
if ma != nil {
functions := metrics.CapAndFilterFunctions(metrics.ExtractFunctionsFromCallTreesForThread(callTree, ma.MinDepth), int(ma.MaxUniqueFunctions), true)
ma.AddFunctions(functions, example)
}
}
chunkProfileSpan.Finish()
} else {
Expand All @@ -489,10 +483,22 @@ func GetFlamegraphFromCandidates(
serializeSpan := span.StartChild("serialize")
defer serializeSpan.Finish()

speedscopeSpan := span.StartChild("processing speedscope")
sp := toSpeedscope(ctx, flamegraphTree, 1000, 0)
speedscopeSpan.Finish()

// if metrics aggregator is not null, while we're at it,
// compute the metrics as well
if ma != nil {
metricsSpan := span.StartChild("processing metrics")
for _, tree := range flamegraphTree {
// tree.RecursiveComputeSelfTime()
tree.Visit(ma.AddFunction)
}
fm := ma.ToMetrics()
sp.Metrics = &fm
metricsSpan.Finish()
}

return sp, nil
}
170 changes: 100 additions & 70 deletions internal/flamegraph/flamegraph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ func TestFlamegraphAggregation(t *testing.T) {
ElapsedSinceStartNS: 20,
StackID: 2,
},
{
ElapsedSinceStartNS: 30,
StackID: 2,
},
}, // end Samples
}, // end Trace
},
Expand All @@ -140,7 +144,7 @@ func TestFlamegraphAggregation(t *testing.T) {
},
Profiles: []interface{}{
speedscope.SampledProfile{
EndValue: 6,
EndValue: 7,
IsMainThread: true,
Samples: [][]int{
{2},
Expand All @@ -152,13 +156,13 @@ func TestFlamegraphAggregation(t *testing.T) {
{0},
{1},
{1},
{0},
{0, 1},
},
Type: "sampled",
Unit: "count",
Weights: []uint64{1, 1, 1, 3},
SampleCounts: []uint64{1, 1, 1, 3},
SampleDurationsNs: []uint64{10, 10, 10, 30},
Weights: []uint64{1, 1, 1, 4},
SampleCounts: []uint64{1, 1, 1, 4},
SampleDurationsNs: []uint64{10, 10, 10, 40},
},
},
Shared: speedscope.SharedData{
Expand All @@ -169,8 +173,8 @@ func TestFlamegraphAggregation(t *testing.T) {
{Image: "test.package", Name: "e", Fingerprint: 2430275448},
},
FrameInfos: []speedscope.FrameInfo{
{Count: 4, Weight: 50},
{Count: 3, Weight: 40},
{Count: 2, Weight: 30},
{Count: 2, Weight: 20},
{Count: 1, Weight: 10},
},
Expand All @@ -181,17 +185,62 @@ func TestFlamegraphAggregation(t *testing.T) {
},
},
metrics: []examples.FunctionMetrics{
{
Name: "a",
Package: "test.package",
Fingerprint: 2430275452,
P75: 10,
P95: 20,
P99: 20,
Avg: float64(50) / float64(4),
Sum: 50,
SumSelfTime: 10,
Count: 5,
Worst: examples.ExampleMetadata{ProfileID: "cd2"},
Examples: []examples.ExampleMetadata{{ProfileID: "cd2"}},
},
{
Name: "b",
Package: "test.package",
Fingerprint: 2430275455,
P75: 20,
P95: 20,
P99: 20,
Avg: 15,
Sum: 30,
SumSelfTime: 30,
Count: 3,
Avg: float64(40) / float64(3),
Sum: 40,
SumSelfTime: 40,
Count: 4,
Worst: examples.ExampleMetadata{ProfileID: "ab1"},
Examples: []examples.ExampleMetadata{{ProfileID: "ab1"}, {ProfileID: "cd2"}},
},
{
Name: "c",
Package: "test.package",
Fingerprint: 2430275454,
InApp: true,
P75: 10,
P95: 10,
P99: 10,
Avg: 10,
Sum: 20,
SumSelfTime: 20,
Count: 2,
Worst: examples.ExampleMetadata{ProfileID: "ab1"},
Examples: []examples.ExampleMetadata{{ProfileID: "ab1"}},
},
{
Name: "e",
Package: "test.package",
Fingerprint: 2430275448,
P75: 10,
P95: 10,
P99: 10,
Avg: 10,
Sum: 10,
SumSelfTime: 10,
Count: 1,
Worst: examples.ExampleMetadata{ProfileID: "cd2"},
Examples: []examples.ExampleMetadata{{ProfileID: "cd2"}},
},
},
},
Expand Down Expand Up @@ -316,19 +365,6 @@ func TestFlamegraphAggregation(t *testing.T) {
},
},
metrics: []examples.FunctionMetrics{
{
Name: "c",
Package: "test.package",
Fingerprint: 2430275454,
InApp: true,
P75: 30,
P95: 30,
P99: 30,
Avg: 25,
Sum: 50,
SumSelfTime: 50,
Count: 5,
},
{
Name: "a",
Package: "test.package",
Expand All @@ -341,6 +377,8 @@ func TestFlamegraphAggregation(t *testing.T) {
Sum: 90,
SumSelfTime: 20,
Count: 9,
Worst: examples.ExampleMetadata{ProfileID: "ab1"},
Examples: []examples.ExampleMetadata{{ProfileID: "ab1"}},
},
{
Name: "b",
Expand All @@ -354,6 +392,23 @@ func TestFlamegraphAggregation(t *testing.T) {
Sum: 70,
SumSelfTime: 20,
Count: 7,
Worst: examples.ExampleMetadata{ProfileID: "ab1"},
Examples: []examples.ExampleMetadata{{ProfileID: "ab1"}},
},
{
Name: "c",
Package: "test.package",
Fingerprint: 2430275454,
InApp: true,
P75: 30,
P95: 30,
P99: 30,
Avg: 25,
Sum: 50,
SumSelfTime: 50,
Count: 5,
Worst: examples.ExampleMetadata{ProfileID: "ab1"},
Examples: []examples.ExampleMetadata{{ProfileID: "ab1"}},
},
},
},
Expand Down Expand Up @@ -460,30 +515,34 @@ func TestFlamegraphAggregation(t *testing.T) {
},
metrics: []examples.FunctionMetrics{
{
Name: "c",
Name: "b",
Package: "test.package",
Fingerprint: 2430275454,
Fingerprint: 2430275455,
InApp: true,
P75: 30,
P95: 30,
P99: 30,
Avg: 30,
Sum: 30,
SumSelfTime: 30,
Count: 3,
Avg: 25,
Sum: 50,
SumSelfTime: 20,
Count: 5,
Worst: examples.ExampleMetadata{ProfileID: "ab1"},
Examples: []examples.ExampleMetadata{{ProfileID: "ab1"}},
},
{
Name: "b",
Name: "c",
Package: "test.package",
Fingerprint: 2430275455,
Fingerprint: 2430275454,
InApp: true,
P75: 30,
P95: 30,
P99: 30,
Avg: 25,
Sum: 50,
SumSelfTime: 20,
Count: 5,
Avg: 30,
Sum: 30,
SumSelfTime: 30,
Count: 3,
Worst: examples.ExampleMetadata{ProfileID: "ab1"},
Examples: []examples.ExampleMetadata{{ProfileID: "ab1"}},
},
},
},
Expand All @@ -492,6 +551,7 @@ func TestFlamegraphAggregation(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var ft []*nodetree.Node

for _, sp := range test.profiles {
p := profile.New(&sp)
callTrees, err := p.CallTrees()
Expand All @@ -508,44 +568,14 @@ func TestFlamegraphAggregation(t *testing.T) {
t.Fatalf("Result mismatch: got - want +\n%s", diff)
}

ma := metrics.NewAggregator(
100,
5,
0,
)

for _, sp := range test.profiles {
p := profile.New(&sp)
callTrees, err := p.CallTrees()
if err != nil {
t.Fatalf("error when generating calltrees: %v", err)
}

start, end := p.StartAndEndEpoch()
example := examples.NewExampleFromProfileID(
p.ProjectID(),
p.ID(),
start,
end,
)

functions := metrics.CapAndFilterFunctions(
metrics.ExtractFunctionsFromCallTrees(
callTrees,
ma.MinDepth,
),
int(ma.MaxUniqueFunctions),
false,
)
ma.AddFunctions(functions, example)
ma := metrics.NewAggregator(100, 5, 0, false)
for _, tree := range ft {
// tree.RecursiveComputeSelfTime()
tree.Visit(ma.AddFunction)
}
m := ma.ToMetrics()

options := cmp.Options{
cmpopts.IgnoreFields(examples.FunctionMetrics{}, "Worst"),
cmpopts.IgnoreFields(examples.FunctionMetrics{}, "Examples"),
}
if diff := testutil.Diff(m, test.metrics, options); diff != "" {
if diff := testutil.Diff(m, test.metrics); diff != "" {
t.Fatalf("Result mismatch: got - want +\n%s", diff)
}
})
Expand Down
Loading
Loading