Keine Beschreibung

addr2liner.go 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. // Copyright 2014 Google Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package binutils
  15. import (
  16. "bufio"
  17. "fmt"
  18. "io"
  19. "os/exec"
  20. "strconv"
  21. "strings"
  22. "sync"
  23. "github.com/google/pprof/internal/plugin"
  24. )
  25. const (
  26. defaultAddr2line = "addr2line"
  27. // addr2line may produce multiple lines of output. We
  28. // use this sentinel to identify the end of the output.
  29. sentinel = ^uint64(0)
  30. )
  31. // addr2Liner is a connection to an addr2line command for obtaining
  32. // address and line number information from a binary.
  33. type addr2Liner struct {
  34. mu sync.Mutex
  35. rw lineReaderWriter
  36. base uint64
  37. // nm holds an NM based addr2Liner which can provide
  38. // better full names compared to addr2line, which often drops
  39. // namespaces etc. from the names it returns.
  40. nm *addr2LinerNM
  41. }
  42. // lineReaderWriter is an interface to abstract the I/O to an addr2line
  43. // process. It writes a line of input to the job, and reads its output
  44. // one line at a time.
  45. type lineReaderWriter interface {
  46. write(string) error
  47. readLine() (string, error)
  48. close()
  49. }
  50. type addr2LinerJob struct {
  51. cmd *exec.Cmd
  52. in io.WriteCloser
  53. out *bufio.Reader
  54. }
  55. func (a *addr2LinerJob) write(s string) error {
  56. _, err := fmt.Fprint(a.in, s+"\n")
  57. return err
  58. }
  59. func (a *addr2LinerJob) readLine() (string, error) {
  60. return a.out.ReadString('\n')
  61. }
  62. // close releases any resources used by the addr2liner object.
  63. func (a *addr2LinerJob) close() {
  64. a.in.Close()
  65. a.cmd.Wait()
  66. }
  67. // newAddr2liner starts the given addr2liner command reporting
  68. // information about the given executable file. If file is a shared
  69. // library, base should be the address at which it was mapped in the
  70. // program under consideration.
  71. func newAddr2Liner(cmd, file string, base uint64) (*addr2Liner, error) {
  72. if cmd == "" {
  73. cmd = defaultAddr2line
  74. }
  75. j := &addr2LinerJob{
  76. cmd: exec.Command(cmd, "-aif", "-e", file),
  77. }
  78. var err error
  79. if j.in, err = j.cmd.StdinPipe(); err != nil {
  80. return nil, err
  81. }
  82. outPipe, err := j.cmd.StdoutPipe()
  83. if err != nil {
  84. return nil, err
  85. }
  86. j.out = bufio.NewReader(outPipe)
  87. if err := j.cmd.Start(); err != nil {
  88. return nil, err
  89. }
  90. a := &addr2Liner{
  91. rw: j,
  92. base: base,
  93. }
  94. return a, nil
  95. }
  96. func (d *addr2Liner) readString() (string, error) {
  97. s, err := d.rw.readLine()
  98. if err != nil {
  99. return "", err
  100. }
  101. return strings.TrimSpace(s), nil
  102. }
  103. // readFrame parses the addr2line output for a single address. It
  104. // returns a populated plugin.Frame and whether it has reached the end of the
  105. // data.
  106. func (d *addr2Liner) readFrame() (plugin.Frame, bool) {
  107. funcname, err := d.readString()
  108. if err != nil {
  109. return plugin.Frame{}, true
  110. }
  111. if strings.HasPrefix(funcname, "0x") {
  112. // If addr2line returns a hex address we can assume it is the
  113. // sentinel. Read and ignore next two lines of output from
  114. // addr2line
  115. d.readString()
  116. d.readString()
  117. return plugin.Frame{}, true
  118. }
  119. fileline, err := d.readString()
  120. if err != nil {
  121. return plugin.Frame{}, true
  122. }
  123. linenumber := 0
  124. if funcname == "??" {
  125. funcname = ""
  126. }
  127. if fileline == "??:0" {
  128. fileline = ""
  129. } else {
  130. if i := strings.LastIndex(fileline, ":"); i >= 0 {
  131. // Remove discriminator, if present
  132. if disc := strings.Index(fileline, " (discriminator"); disc > 0 {
  133. fileline = fileline[:disc]
  134. }
  135. // If we cannot parse a number after the last ":", keep it as
  136. // part of the filename.
  137. if line, err := strconv.Atoi(fileline[i+1:]); err == nil {
  138. linenumber = line
  139. fileline = fileline[:i]
  140. }
  141. }
  142. }
  143. return plugin.Frame{
  144. Func: funcname,
  145. File: fileline,
  146. Line: linenumber}, false
  147. }
  148. func (d *addr2Liner) rawAddrInfo(addr uint64) ([]plugin.Frame, error) {
  149. d.mu.Lock()
  150. defer d.mu.Unlock()
  151. if err := d.rw.write(fmt.Sprintf("%x", addr-d.base)); err != nil {
  152. return nil, err
  153. }
  154. if err := d.rw.write(fmt.Sprintf("%x", sentinel)); err != nil {
  155. return nil, err
  156. }
  157. resp, err := d.readString()
  158. if err != nil {
  159. return nil, err
  160. }
  161. if !strings.HasPrefix(resp, "0x") {
  162. return nil, fmt.Errorf("unexpected addr2line output: %s", resp)
  163. }
  164. var stack []plugin.Frame
  165. for {
  166. frame, end := d.readFrame()
  167. if end {
  168. break
  169. }
  170. if frame != (plugin.Frame{}) {
  171. stack = append(stack, frame)
  172. }
  173. }
  174. return stack, err
  175. }
  176. // addrInfo returns the stack frame information for a specific program
  177. // address. It returns nil if the address could not be identified.
  178. func (d *addr2Liner) addrInfo(addr uint64) ([]plugin.Frame, error) {
  179. stack, err := d.rawAddrInfo(addr)
  180. if err != nil {
  181. return nil, err
  182. }
  183. // Get better name from nm if possible.
  184. if len(stack) > 0 && d.nm != nil {
  185. nm, err := d.nm.addrInfo(addr)
  186. if err == nil && len(nm) > 0 {
  187. // Last entry in frame list should match since
  188. // it is non-inlined. As a simple heuristic,
  189. // we only switch to the nm-based name if it
  190. // is longer.
  191. nmName := nm[len(nm)-1].Func
  192. a2lName := stack[len(stack)-1].Func
  193. if len(nmName) > len(a2lName) {
  194. stack[len(stack)-1].Func = nmName
  195. }
  196. }
  197. }
  198. return stack, nil
  199. }