Skip to content

Commit

Permalink
Add a folded stacks output format
Browse files Browse the repository at this point in the history
Fixes #658
  • Loading branch information
mhansen committed Nov 8, 2021
1 parent 9288912 commit 3afac6a
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 1 deletion.
2 changes: 2 additions & 0 deletions doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ pprof text reports show the location hierarchy in text format.
* **-peek= _regex_:** Print the location entry with all its predecessors and
successors, without trimming any entries.
* **-traces:** Prints each sample with a location per line.
* **-folded:** Prints each sample in [Brendan Gregg's Folded Stacks
format](https://queue.acm.org/detail.cfm?id=2927301#:~:text=The%20folded%20stack%2Dtrace%20format,trace%2C%20followed%20by%20a%20semicolon.).

## Graphical reports

Expand Down
1 change: 1 addition & 0 deletions internal/driver/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ var pprofCommands = commands{
"top": {report.Text, nil, nil, false, "Outputs top entries in text form", reportHelp("top", true, true)},
"traces": {report.Traces, nil, nil, false, "Outputs all profile samples in text form", ""},
"tree": {report.Tree, nil, nil, false, "Outputs a text rendering of call graph", reportHelp("tree", true, true)},
"folded": {report.Folded, nil, nil, false, "Outputs entries in folded stack form", reportHelp("folded", false, true)},

// Save binary formats to a file
"callgrind": {report.Callgrind, nil, awayFromTTY("callgraph.out"), false, "Outputs a graph in callgrind format", reportHelp("callgrind", false, true)},
Expand Down
52 changes: 52 additions & 0 deletions internal/report/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const (
Traces
Tree
WebList
Folded
)

// Options are the formatting and filtering options used to generate a
Expand Down Expand Up @@ -94,6 +95,8 @@ func Generate(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
return printDOT(w, rpt)
case Tree:
return printTree(w, rpt)
case Folded:
return printFolded(w, rpt)
case Text:
return printText(w, rpt)
case Traces:
Expand Down Expand Up @@ -828,6 +831,55 @@ func printText(w io.Writer, rpt *Report) error {
return nil
}

// printFolded prints a profile in Brendan Gregg's Folded Stacks format.
func printFolded(w io.Writer, rpt *Report) error {
prof := rpt.prof
o := rpt.options

_, locations := graph.CreateNodes(prof, &graph.Options{})
for _, sample := range prof.Sample {
var stack []*graph.NodeInfo
for _, loc := range sample.Location {
nodes := locations[loc.ID]
for _, n := range nodes {
stack = append(stack, &n.Info)
}
}

if len(stack) == 0 {
continue
}

var d, v int64
v = o.SampleValue(sample.Value)
if o.SampleMeanDivisor != nil {
d = o.SampleMeanDivisor(sample.Value)
}
if d != 0 {
v = v / d
}
// Print call stack.
for i := range stack {
// Folded stack convention: start with root frame, end
// with leaves.
s := stack[len(stack)-i-1]
if i > 0 {
fmt.Fprint(w, ";")
}
// TODO: should we print more than just s.Name?
// NodeInfo.PrintableName() has a lot more.

// Remove semicolons and newlines.
name := strings.ReplaceAll(s.Name, ";", "")
name = strings.ReplaceAll(s.Name, "\n", "")
fmt.Fprint(w, name)
}
// We just want a raw number, so don't use rpt.formatValue().
fmt.Fprintf(w, " %d\n", v)
}
return nil
}

// printTraces prints all traces from a profile.
func printTraces(w io.Writer, rpt *Report) error {
fmt.Fprintln(w, strings.Join(ProfileLabels(rpt), "\n"))
Expand Down
42 changes: 41 additions & 1 deletion internal/report/report_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ type testcase struct {
func TestTextReports(t *testing.T) {
const path = "testdata/"

sampleValue0 := func(v []int64) int64 {
return v[0]
}
sampleValue1 := func(v []int64) int64 {
return v[1]
}
Expand Down Expand Up @@ -70,6 +73,43 @@ func TestTextReports(t *testing.T) {
),
want: path + "report.dot",
},
{
rpt: New(
testProfile.Copy(),
&Options{
OutputFormat: Folded,

SampleValue: sampleValue1,
SampleUnit: testProfile.SampleType[1].Unit,
},
),
want: path + "report.folded",
},
{
rpt: New(
testProfile.Copy(),
&Options{
OutputFormat: Folded,

SampleValue: sampleValue0,
SampleUnit: testProfile.SampleType[0].Unit,
},
),
want: path + "report0.folded",
},
{
rpt: New(
testProfile.Copy(),
&Options{
OutputFormat: Folded,

SampleValue: sampleValue1,
SampleUnit: testProfile.SampleType[1].Unit,
SampleMeanDivisor: sampleValue0,
},
),
want: path + "report_mean.folded",
},
} {
var b bytes.Buffer
if err := Generate(&b, tc.rpt, &binutils.Binutils{}); err != nil {
Expand Down Expand Up @@ -259,7 +299,7 @@ var testProfile = &profile.Profile{
},
{
Location: []*profile.Location{testL[4], testL[3], testL[0]},
Value: []int64{1, 10000},
Value: []int64{2, 10000},
},
},
Location: testL,
Expand Down
5 changes: 5 additions & 0 deletions internal/report/testdata/report.folded
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
main 1
main;foo;bar 10
main;bar;tee 100
main;tee 1000
main;tee;tee 10000
5 changes: 5 additions & 0 deletions internal/report/testdata/report0.folded
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
main 1
main;foo;bar 1
main;bar;tee 1
main;tee 1
main;tee;tee 2
5 changes: 5 additions & 0 deletions internal/report/testdata/report_mean.folded
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
main 1
main;foo;bar 10
main;bar;tee 100
main;tee 1000
main;tee;tee 5000

0 comments on commit 3afac6a

Please sign in to comment.