func runWithOptions(allOpts *options, remaining []string) error { pprofRawOutput, err := pprof.GetRaw(allOpts.PProfOptions, remaining) if err != nil { return fmt.Errorf("could not get raw output from pprof: %v", err) } callStacks, err := pprof.ParseRaw(pprofRawOutput) if err != nil { return fmt.Errorf("could not parse raw pprof output: %v", err) } flameInput, err := renderer.ToFlameInput(callStacks) if err != nil { return fmt.Errorf("could not convert stacks to flamegraph input: %v", err) } opts := allOpts.OutputOpts if opts.Raw { torchlog.Print("Printing raw flamegraph input to stdout") fmt.Printf("%s\n", flameInput) return nil } var flameGraphArgs = buildFlameGraphArgs(opts) flameGraph, err := renderer.GenerateFlameGraph(flameInput, flameGraphArgs...) if err != nil { return fmt.Errorf("could not generate flame graph: %v", err) } if opts.Print { torchlog.Print("Printing svg to stdout") fmt.Printf("%s\n", flameGraph) return nil } torchlog.Printf("Writing svg to %v", opts.File) if err := ioutil.WriteFile(opts.File, flameGraph, 0666); err != nil { return fmt.Errorf("could not write output file: %v", err) } return nil }
func runPProf(args ...string) ([]byte, error) { allArgs := []string{"tool", "pprof", "-raw"} allArgs = append(allArgs, args...) var buf bytes.Buffer torchlog.Printf("Run pprof command: go %v", strings.Join(allArgs, " ")) cmd := exec.Command("go", allArgs...) cmd.Stderr = &buf out, err := cmd.Output() if err != nil { return nil, fmt.Errorf("pprof error: %v\nSTDERR:\n%s", err, buf.Bytes()) } // @HACK because 'go tool pprof' doesn't exit on errors with nonzero status codes. // Ironically, this means that Go's own os/exec package does not detect its errors. // See issue here https://github.com/golang/go/issues/11510 if len(out) == 0 { return nil, fmt.Errorf("pprof error:\n%s", buf.Bytes()) } return out, nil }