Няма описание

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