You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

176 lines
3.8 KiB

  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. defaultLLVMSymbolizer = "llvm-symbolizer"
  27. )
  28. // llvmSymbolizer is a connection to an llvm-symbolizer command for
  29. // obtaining address and line number information from a binary.
  30. type llvmSymbolizer struct {
  31. sync.Mutex
  32. filename string
  33. rw lineReaderWriter
  34. base uint64
  35. }
  36. type llvmSymbolizerJob struct {
  37. cmd *exec.Cmd
  38. in io.WriteCloser
  39. out *bufio.Reader
  40. }
  41. func (a *llvmSymbolizerJob) write(s string) error {
  42. _, err := fmt.Fprint(a.in, s+"\n")
  43. return err
  44. }
  45. func (a *llvmSymbolizerJob) readLine() (string, error) {
  46. return a.out.ReadString('\n')
  47. }
  48. // close releases any resources used by the llvmSymbolizer object.
  49. func (a *llvmSymbolizerJob) close() {
  50. a.in.Close()
  51. a.cmd.Wait()
  52. }
  53. // newLlvmSymbolizer starts the given llvmSymbolizer command reporting
  54. // information about the given executable file. If file is a shared
  55. // library, base should be the address at which it was mapped in the
  56. // program under consideration.
  57. func newLLVMSymbolizer(cmd, file string, base uint64) (*llvmSymbolizer, error) {
  58. if cmd == "" {
  59. cmd = defaultLLVMSymbolizer
  60. }
  61. j := &llvmSymbolizerJob{
  62. cmd: exec.Command(cmd, "-inlining", "-demangle=false"),
  63. }
  64. var err error
  65. if j.in, err = j.cmd.StdinPipe(); err != nil {
  66. return nil, err
  67. }
  68. outPipe, err := j.cmd.StdoutPipe()
  69. if err != nil {
  70. return nil, err
  71. }
  72. j.out = bufio.NewReader(outPipe)
  73. if err := j.cmd.Start(); err != nil {
  74. return nil, err
  75. }
  76. a := &llvmSymbolizer{
  77. filename: file,
  78. rw: j,
  79. base: base,
  80. }
  81. return a, nil
  82. }
  83. func (d *llvmSymbolizer) readString() (string, error) {
  84. s, err := d.rw.readLine()
  85. if err != nil {
  86. return "", err
  87. }
  88. return strings.TrimSpace(s), nil
  89. }
  90. // readFrame parses the llvm-symbolizer output for a single address. It
  91. // returns a populated plugin.Frame and whether it has reached the end of the
  92. // data.
  93. func (d *llvmSymbolizer) readFrame() (plugin.Frame, bool) {
  94. funcname, err := d.readString()
  95. if err != nil {
  96. return plugin.Frame{}, true
  97. }
  98. switch funcname {
  99. case "":
  100. return plugin.Frame{}, true
  101. case "??":
  102. funcname = ""
  103. }
  104. fileline, err := d.readString()
  105. if err != nil {
  106. return plugin.Frame{Func: funcname}, true
  107. }
  108. linenumber := 0
  109. if fileline == "??:0" {
  110. fileline = ""
  111. } else {
  112. switch split := strings.Split(fileline, ":"); len(split) {
  113. case 1:
  114. // filename
  115. fileline = split[0]
  116. case 2, 3:
  117. // filename:line , or
  118. // filename:line:disc , or
  119. fileline = split[0]
  120. if line, err := strconv.Atoi(split[1]); err == nil {
  121. linenumber = line
  122. }
  123. default:
  124. // Unrecognized, ignore
  125. }
  126. }
  127. return plugin.Frame{Func: funcname, File: fileline, Line: linenumber}, false
  128. }
  129. // addrInfo returns the stack frame information for a specific program
  130. // address. It returns nil if the address could not be identified.
  131. func (d *llvmSymbolizer) addrInfo(addr uint64) ([]plugin.Frame, error) {
  132. d.Lock()
  133. defer d.Unlock()
  134. if err := d.rw.write(fmt.Sprintf("%s 0x%x", d.filename, addr-d.base)); err != nil {
  135. return nil, err
  136. }
  137. var stack []plugin.Frame
  138. for {
  139. frame, end := d.readFrame()
  140. if end {
  141. break
  142. }
  143. if frame != (plugin.Frame{}) {
  144. stack = append(stack, frame)
  145. }
  146. }
  147. return stack, nil
  148. }