Skip to content
This repository has been archived by the owner on Mar 7, 2019. It is now read-only.

Flamegraph options #50

Merged
merged 6 commits into from
Nov 10, 2016
Merged
Show file tree
Hide file tree
Changes from 5 commits
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
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ Output Options:
-p, --print Print the generated svg to stdout instead of writing to file
-r, --raw Print the raw call graph output to stdout instead of creating a flame graph; use with Brendan Gregg's flame graph perl script (see https://github.com/brendangregg/FlameGraph)
--title= Graph title to display in the output file (default: Flame Graph)

--width= Generated graph width (default: 1200)
--hash Colors are keyed by function name hash
--colors= Set color palette. Valid choices are: hot (default), mem, io, wakeup, chain, java,
js, perl, red, green, blue, aqua, yellow, purple, orange
--hash Graph colors are keyed by function name hash
--cp Graph use consistent palette (palette.map)
--inverted Icicle graph
Help Options:
-h, --help Show this help message
```
Expand Down
75 changes: 70 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"

"github.com/uber/go-torch/pprof"
Expand All @@ -42,10 +43,16 @@ type options struct {
}

type outputOptions struct {
File string `short:"f" long:"file" default:"torch.svg" description:"Output file name (must be .svg)"`
Print bool `short:"p" long:"print" description:"Print the generated svg to stdout instead of writing to file"`
Raw bool `short:"r" long:"raw" description:"Print the raw call graph output to stdout instead of creating a flame graph; use with Brendan Gregg's flame graph perl script (see https://github.com/brendangregg/FlameGraph)"`
Title string `long:"title" default:"Flame Graph" description:"Graph title to display in the output file"`
File string `short:"f" long:"file" default:"torch.svg" description:"Output file name (must be .svg)"`
Print bool `short:"p" long:"print" description:"Print the generated svg to stdout instead of writing to file"`
Raw bool `short:"r" long:"raw" description:"Print the raw call graph output to stdout instead of creating a flame graph; use with Brendan Gregg's flame graph perl script (see https://github.com/brendangregg/FlameGraph)"`
Title string `long:"title" default:"Flame Graph" description:"Graph title to display in the output file"`
Width int64 `long:"width" default:"1200" description:"Generated graph width"`
Hash bool `long:"hash" description:"Colors are keyed by function name hash"`
Colors string `long:"colors" default:"" description:"set color palette. choices are: hot (default), mem, io, wakeup, chain, java, js, perl, red, green, blue, aqua, yellow, purple, orange"`
ConsistentPalette bool `long:"cp" description:"Use consistent palette (palette.map)"`
Reverse bool `long:"reverse" description:"Generate stack-reversed flame graph"`
Inverted bool `long:"inverted" description:"icicle graph"`
}

// main is the entry point of the application
Expand Down Expand Up @@ -98,7 +105,8 @@ func runWithOptions(allOpts *options, remaining []string) error {
return nil
}

flameGraph, err := renderer.GenerateFlameGraph(flameInput, "--title", opts.Title)
var flameGraphArgs = buildFlameGraphArgs(opts)
flameGraph, err := renderer.GenerateFlameGraph(flameInput, flameGraphArgs...)
if err != nil {
return fmt.Errorf("could not generate flame graph: %v", err)
}
Expand All @@ -125,5 +133,62 @@ func validateOptions(opts *options) error {
if opts.PProfOptions.TimeSeconds < 1 {
return fmt.Errorf("seconds must be an integer greater than 0")
}

// extra FlameGraph options
if opts.OutputOpts.Title == "" {
return fmt.Errorf("FlameGraph title should not be empty")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: errors should be lowercase,
https://github.com/golang/go/wiki/CodeReviewComments#error-strings

same for all errors added

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK my idea here was to use name of the utility called. I am going to change all occurrences to lower-case flame graph.

}
if opts.OutputOpts.Width < 1200 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this enforced by the flame graph script?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NO, my bad... Thanks for pointing out.

return fmt.Errorf("FlameGraph miminal graph width is 1200 pixels")
}
if opts.OutputOpts.Colors != "" {
var validColors = []string{"hot", "mem", "io", "wakeup", "chain", "java", "js", "perl", "red",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this becomes a lot simpler with a switch, and as a bonus, it's also faster.

switch opts.OutputOpts.Colors {
case "hot", "mem", "io", [...]:
  // valid
default:
  // return error
}

"green", "blue", "aqua", "yellow", "purple", "orange"}
var isValidColors = false
for _, color := range validColors {
if opts.OutputOpts.Colors == color {
isValidColors = true
break
}
}
if !isValidColors {
return fmt.Errorf("FlameGraph unknown colors %q. Valid ones are: %s", opts.OutputOpts.Colors, strings.Join(validColors, ", "))
}
}

return nil
}

func buildFlameGraphArgs(opts outputOptions) []string {
var args []string

if opts.Title != "" {
args = append(args, "--title", opts.Title)
}

if opts.Width > 0 {
args = append(args, "--width", strconv.FormatInt(opts.Width, 10))
}

if opts.Colors != "" {
args = append(args, "--colors", opts.Colors)
}

if opts.Hash {
args = append(args, "--hash")
}

if opts.ConsistentPalette {
args = append(args, "--cp")
}

if opts.Reverse {
args = append(args, "--reverse")
}

if opts.Inverted {
args = append(args, "--inverted")
}

return args
}
35 changes: 35 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
"testing"

Expand Down Expand Up @@ -84,6 +85,18 @@ func TestInvalidOptions(t *testing.T) {
args: []string{"-t", "0"},
errorMessage: "seconds must be an integer greater than 0",
},
{
args: []string{"--title", ""},
errorMessage: "FlameGraph title should not be empty",
},
{
args: []string{"--width", "10"},
errorMessage: "FlameGraph miminal graph width is 1200 pixels",
},
{
args: []string{"--colors", "foo"},
errorMessage: "FlameGraph unknown colors \"foo\". Valid ones are: hot, mem, io, wakeup, chain, java, js, perl, red, green, blue, aqua, yellow, purple, orange",
},
}

for _, tt := range tests {
Expand All @@ -108,6 +121,28 @@ func TestRunRaw(t *testing.T) {
}
}

func TestFlameGraphArgs(t *testing.T) {
opts := getDefaultOptions()
opts.OutputOpts.Raw = true

opts.OutputOpts.Hash = true
opts.OutputOpts.Colors = "perl"
opts.OutputOpts.ConsistentPalette = true
opts.OutputOpts.Reverse = true
opts.OutputOpts.Inverted = true

expectedCommandWithArgs := []string{"--title", "Flame Graph", "--width", "1200", "--colors", "perl",
"--hash", "--cp", "--reverse", "--inverted"}

if !reflect.DeepEqual(expectedCommandWithArgs, buildFlameGraphArgs(opts.OutputOpts)) {
t.Fatalf("Invalid extra FlameGraph arguments!")
}

if err := runWithOptions(opts, nil); err != nil {
t.Fatalf("Run with extra FlameGraph arguments failed: %v", err)
}
}

func getTempFilename(t *testing.T, suffix string) string {
f, err := ioutil.TempFile("", "")
if err != nil {
Expand Down