1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package binutils
16
17 import (
18 "bufio"
19 "encoding/json"
20 "fmt"
21 "io"
22 "os/exec"
23 "strconv"
24 "strings"
25 "sync"
26
27 "github.com/google/pprof/internal/plugin"
28 )
29
30 const (
31 defaultLLVMSymbolizer = "llvm-symbolizer"
32 )
33
34
35
36 type llvmSymbolizer struct {
37 sync.Mutex
38 filename string
39 rw lineReaderWriter
40 base uint64
41 isData bool
42 }
43
44 type llvmSymbolizerJob struct {
45 cmd *exec.Cmd
46 in io.WriteCloser
47 out *bufio.Reader
48
49 symType string
50 }
51
52 func (a *llvmSymbolizerJob) write(s string) error {
53 _, err := fmt.Fprintln(a.in, a.symType, s)
54 return err
55 }
56
57 func (a *llvmSymbolizerJob) readLine() (string, error) {
58 s, err := a.out.ReadString('\n')
59 if err != nil {
60 return "", err
61 }
62 return strings.TrimSpace(s), nil
63 }
64
65
66 func (a *llvmSymbolizerJob) close() {
67 a.in.Close()
68 a.cmd.Wait()
69 }
70
71
72
73
74
75 func newLLVMSymbolizer(cmd, file string, base uint64, isData bool) (*llvmSymbolizer, error) {
76 if cmd == "" {
77 cmd = defaultLLVMSymbolizer
78 }
79
80 j := &llvmSymbolizerJob{
81 cmd: exec.Command(cmd, "--inlining", "-demangle=false", "--output-style=JSON"),
82 symType: "CODE",
83 }
84 if isData {
85 j.symType = "DATA"
86 }
87
88 var err error
89 if j.in, err = j.cmd.StdinPipe(); err != nil {
90 return nil, err
91 }
92
93 outPipe, err := j.cmd.StdoutPipe()
94 if err != nil {
95 return nil, err
96 }
97
98 j.out = bufio.NewReader(outPipe)
99 if err := j.cmd.Start(); err != nil {
100 return nil, err
101 }
102
103 a := &llvmSymbolizer{
104 filename: file,
105 rw: j,
106 base: base,
107 isData: isData,
108 }
109
110 return a, nil
111 }
112
113
114
115 func (d *llvmSymbolizer) readDataFrames() ([]plugin.Frame, error) {
116 line, err := d.rw.readLine()
117 if err != nil {
118 return nil, err
119 }
120 var frame struct {
121 Address string `json:"Address"`
122 ModuleName string `json:"ModuleName"`
123 Data struct {
124 Start string `json:"Start"`
125 Size string `json:"Size"`
126 Name string `json:"Name"`
127 } `json:"Data"`
128 }
129 if err := json.Unmarshal([]byte(line), &frame); err != nil {
130 return nil, err
131 }
132
133
134 size, err := strconv.ParseInt(frame.Data.Size, 0, 0)
135 if err != nil {
136 return nil, err
137 }
138 var stack []plugin.Frame
139 stack = append(stack, plugin.Frame{Func: frame.Data.Name, File: fmt.Sprintf("%s %d", frame.Data.Start, size)})
140 return stack, nil
141 }
142
143
144
145 func (d *llvmSymbolizer) readCodeFrames() ([]plugin.Frame, error) {
146 line, err := d.rw.readLine()
147 if err != nil {
148 return nil, err
149 }
150 var frame struct {
151 Address string `json:"Address"`
152 ModuleName string `json:"ModuleName"`
153 Symbol []struct {
154 Line int `json:"Line"`
155 Column int `json:"Column"`
156 FunctionName string `json:"FunctionName"`
157 FileName string `json:"FileName"`
158 StartLine int `json:"StartLine"`
159 } `json:"Symbol"`
160 }
161 if err := json.Unmarshal([]byte(line), &frame); err != nil {
162 return nil, err
163 }
164 var stack []plugin.Frame
165 for _, s := range frame.Symbol {
166 stack = append(stack, plugin.Frame{Func: s.FunctionName, File: s.FileName, Line: s.Line, Column: s.Column, StartLine: s.StartLine})
167 }
168 return stack, nil
169 }
170
171
172
173 func (d *llvmSymbolizer) addrInfo(addr uint64) ([]plugin.Frame, error) {
174 d.Lock()
175 defer d.Unlock()
176
177 if err := d.rw.write(fmt.Sprintf("%s 0x%x", d.filename, addr-d.base)); err != nil {
178 return nil, err
179 }
180 if d.isData {
181 return d.readDataFrames()
182 }
183 return d.readCodeFrames()
184 }
185
View as plain text