| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | package binutils |
| |
|
| | import ( |
| | "bufio" |
| | "fmt" |
| | "io" |
| | "os/exec" |
| | "strconv" |
| | "strings" |
| | "sync" |
| |
|
| | "github.com/google/pprof/internal/plugin" |
| | ) |
| |
|
| | const ( |
| | defaultAddr2line = "addr2line" |
| |
|
| | |
| | |
| | sentinel = ^uint64(0) |
| | ) |
| |
|
| | |
| | |
| | type addr2Liner struct { |
| | mu sync.Mutex |
| | rw lineReaderWriter |
| | base uint64 |
| |
|
| | |
| | |
| | |
| | |
| | |
| | nm *addr2LinerNM |
| | } |
| |
|
| | |
| | |
| | |
| | type lineReaderWriter interface { |
| | write(string) error |
| | readLine() (string, error) |
| | close() |
| | } |
| |
|
| | type addr2LinerJob struct { |
| | cmd *exec.Cmd |
| | in io.WriteCloser |
| | out *bufio.Reader |
| | } |
| |
|
| | func (a *addr2LinerJob) write(s string) error { |
| | _, err := fmt.Fprint(a.in, s+"\n") |
| | return err |
| | } |
| |
|
| | func (a *addr2LinerJob) readLine() (string, error) { |
| | s, err := a.out.ReadString('\n') |
| | if err != nil { |
| | return "", err |
| | } |
| | return strings.TrimSpace(s), nil |
| | } |
| |
|
| | |
| | func (a *addr2LinerJob) close() { |
| | a.in.Close() |
| | a.cmd.Wait() |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func newAddr2Liner(cmd, file string, base uint64) (*addr2Liner, error) { |
| | if cmd == "" { |
| | cmd = defaultAddr2line |
| | } |
| |
|
| | j := &addr2LinerJob{ |
| | cmd: exec.Command(cmd, "-aif", "-e", file), |
| | } |
| |
|
| | var err error |
| | if j.in, err = j.cmd.StdinPipe(); err != nil { |
| | return nil, err |
| | } |
| |
|
| | outPipe, err := j.cmd.StdoutPipe() |
| | if err != nil { |
| | return nil, err |
| | } |
| |
|
| | j.out = bufio.NewReader(outPipe) |
| | if err := j.cmd.Start(); err != nil { |
| | return nil, err |
| | } |
| |
|
| | a := &addr2Liner{ |
| | rw: j, |
| | base: base, |
| | } |
| |
|
| | return a, nil |
| | } |
| |
|
| | |
| | |
| | |
| | func (d *addr2Liner) readFrame() (plugin.Frame, bool) { |
| | funcname, err := d.rw.readLine() |
| | if err != nil { |
| | return plugin.Frame{}, true |
| | } |
| | if strings.HasPrefix(funcname, "0x") { |
| | |
| | |
| | |
| | d.rw.readLine() |
| | d.rw.readLine() |
| | return plugin.Frame{}, true |
| | } |
| |
|
| | fileline, err := d.rw.readLine() |
| | if err != nil { |
| | return plugin.Frame{}, true |
| | } |
| |
|
| | linenumber := 0 |
| |
|
| | if funcname == "??" { |
| | funcname = "" |
| | } |
| |
|
| | if fileline == "??:0" { |
| | fileline = "" |
| | } else { |
| | if i := strings.LastIndex(fileline, ":"); i >= 0 { |
| | |
| | if disc := strings.Index(fileline, " (discriminator"); disc > 0 { |
| | fileline = fileline[:disc] |
| | } |
| | |
| | |
| | if line, err := strconv.Atoi(fileline[i+1:]); err == nil { |
| | linenumber = line |
| | fileline = fileline[:i] |
| | } |
| | } |
| | } |
| |
|
| | return plugin.Frame{ |
| | Func: funcname, |
| | File: fileline, |
| | Line: linenumber}, false |
| | } |
| |
|
| | func (d *addr2Liner) rawAddrInfo(addr uint64) ([]plugin.Frame, error) { |
| | d.mu.Lock() |
| | defer d.mu.Unlock() |
| |
|
| | if err := d.rw.write(fmt.Sprintf("%x", addr-d.base)); err != nil { |
| | return nil, err |
| | } |
| |
|
| | if err := d.rw.write(fmt.Sprintf("%x", sentinel)); err != nil { |
| | return nil, err |
| | } |
| |
|
| | resp, err := d.rw.readLine() |
| | if err != nil { |
| | return nil, err |
| | } |
| |
|
| | if !strings.HasPrefix(resp, "0x") { |
| | return nil, fmt.Errorf("unexpected addr2line output: %s", resp) |
| | } |
| |
|
| | var stack []plugin.Frame |
| | for { |
| | frame, end := d.readFrame() |
| | if end { |
| | break |
| | } |
| |
|
| | if frame != (plugin.Frame{}) { |
| | stack = append(stack, frame) |
| | } |
| | } |
| | return stack, err |
| | } |
| |
|
| | |
| | |
| | func (d *addr2Liner) addrInfo(addr uint64) ([]plugin.Frame, error) { |
| | stack, err := d.rawAddrInfo(addr) |
| | if err != nil { |
| | return nil, err |
| | } |
| |
|
| | |
| | |
| | |
| | if len(stack) > 0 && d.nm != nil { |
| | nm, err := d.nm.addrInfo(addr) |
| | if err == nil && len(nm) > 0 { |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | nmName := nm[len(nm)-1].Func |
| | a2lName := stack[len(stack)-1].Func |
| | if len(nmName) > len(a2lName)+1 { |
| | stack[len(stack)-1].Func = nmName |
| | } |
| | } |
| | } |
| |
|
| | return stack, nil |
| | } |
| |
|