暂无描述

addr2liner_llvm.go 3.8KB

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