This repository has been archived by the owner on Dec 18, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
flamegraph.go
89 lines (78 loc) · 2.09 KB
/
flamegraph.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package pprofserver
import (
"fmt"
"io"
"net/url"
"os/exec"
"path"
"strings"
"github.com/segmentio/events"
"github.com/uber/go-torch/pprof"
"github.com/uber/go-torch/renderer"
)
func supportsFlamegraph(params string) bool {
if strings.HasPrefix(params, "?") {
params = params[1:]
}
query, err := url.ParseQuery(params)
if err != nil {
events.Log("flamegraph support check: params=%{params}q: %{error}s", params, err)
return false
}
pprofUrl := query.Get("url")
pprofParsed, err := url.Parse(pprofUrl)
if err != nil {
return false
}
switch path.Base(pprofParsed.Path) {
case "profile", "heap", "block", "mutex":
return true
case "goroutine":
return pprofParsed.Query().Get("debug") == "1"
}
return false
}
func renderFlamegraph(w io.Writer, url, sampleType string) error {
// Get the raw pprof data
c := exec.Command("go", "tool", "pprof", "-raw", url)
raw, err := c.Output()
if err != nil {
return fmt.Errorf("get raw pprof data: %v", err)
}
profile, err := pprof.ParseRaw(raw)
if err != nil {
return fmt.Errorf("parse raw pprof output: %v", err)
}
// Select a sample type from the profile (bytes allocated, objects allocated, etc.)
var args []string
if sampleType != "" {
args = append(args, "-"+sampleType)
}
sampleIndex := pprof.SelectSample(args, profile.SampleNames)
flameInput, err := renderer.ToFlameInput(profile, sampleIndex)
if err != nil {
return fmt.Errorf("convert stacks to flamegraph input: %v", err)
}
// Construct graph title
title := url
if sampleType != "" {
title = fmt.Sprintf("%s (%s)", url, sampleType)
}
// Try to find reasonable units
unit := "samples"
if strings.Contains(sampleType, "space") {
unit = "bytes"
} else if strings.Contains(sampleType, "objects") {
unit = "objects"
}
// Render the graph
flameGraph, err := renderer.GenerateFlameGraph(flameInput, "--title", title, "--countname", unit)
if err != nil {
return fmt.Errorf("generate flame graph: %v", err)
}
// Write the graph to the response
if _, err := w.Write(flameGraph); err != nil {
return fmt.Errorf("write flamegraph SVG: %v", err)
}
return nil
}