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.
 
 
 

172 lines
4.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. "bytes"
  17. "io"
  18. "regexp"
  19. "strconv"
  20. "github.com/google/pprof/internal/plugin"
  21. "github.com/ianlancetaylor/demangle"
  22. )
  23. var (
  24. nmOutputRE = regexp.MustCompile(`^\s*([[:xdigit:]]+)\s+(.)\s+(.*)`)
  25. objdumpAsmOutputRE = regexp.MustCompile(`^\s*([[:xdigit:]]+):\s+(.*)`)
  26. objdumpOutputFileLine = regexp.MustCompile(`^(.*):([0-9]+)`)
  27. objdumpOutputFunction = regexp.MustCompile(`^(\S.*)\(\):`)
  28. )
  29. func findSymbols(syms []byte, file string, r *regexp.Regexp, address uint64) ([]*plugin.Sym, error) {
  30. // Collect all symbols from the nm output, grouping names mapped to
  31. // the same address into a single symbol.
  32. // The symbols to return.
  33. var symbols []*plugin.Sym
  34. // The current group of symbol names, and the address they are all at.
  35. names, start := []string{}, uint64(0)
  36. buf := bytes.NewBuffer(syms)
  37. for {
  38. symAddr, name, err := nextSymbol(buf)
  39. if err == io.EOF {
  40. // Done. If there was an unfinished group, append it.
  41. if len(names) != 0 {
  42. if match := matchSymbol(names, start, symAddr-1, r, address); match != nil {
  43. symbols = append(symbols, &plugin.Sym{Name: match, File: file, Start: start, End: symAddr - 1})
  44. }
  45. }
  46. // And return the symbols.
  47. return symbols, nil
  48. }
  49. if err != nil {
  50. // There was some kind of serious error reading nm's output.
  51. return nil, err
  52. }
  53. // If this symbol is at the same address as the current group, add it to the group.
  54. if symAddr == start {
  55. names = append(names, name)
  56. continue
  57. }
  58. // Otherwise append the current group to the list of symbols.
  59. if match := matchSymbol(names, start, symAddr-1, r, address); match != nil {
  60. symbols = append(symbols, &plugin.Sym{Name: match, File: file, Start: start, End: symAddr - 1})
  61. }
  62. // And start a new group.
  63. names, start = []string{name}, symAddr
  64. }
  65. }
  66. // matchSymbol checks if a symbol is to be selected by checking its
  67. // name to the regexp and optionally its address. It returns the name(s)
  68. // to be used for the matched symbol, or nil if no match
  69. func matchSymbol(names []string, start, end uint64, r *regexp.Regexp, address uint64) []string {
  70. if address != 0 && address >= start && address <= end {
  71. return names
  72. }
  73. for _, name := range names {
  74. if r == nil || r.MatchString(name) {
  75. return []string{name}
  76. }
  77. // Match all possible demangled versions of the name.
  78. for _, o := range [][]demangle.Option{
  79. {demangle.NoClones},
  80. {demangle.NoParams},
  81. {demangle.NoParams, demangle.NoTemplateParams},
  82. } {
  83. if demangled, err := demangle.ToString(name, o...); err == nil && r.MatchString(demangled) {
  84. return []string{demangled}
  85. }
  86. }
  87. }
  88. return nil
  89. }
  90. // disassemble parses the output of the objdump command and returns
  91. // the assembly instructions in a slice.
  92. func disassemble(asm []byte) ([]plugin.Inst, error) {
  93. buf := bytes.NewBuffer(asm)
  94. function, file, line := "", "", 0
  95. var assembly []plugin.Inst
  96. for {
  97. input, err := buf.ReadString('\n')
  98. if err != nil {
  99. if err != io.EOF {
  100. return nil, err
  101. }
  102. if input == "" {
  103. break
  104. }
  105. }
  106. if fields := objdumpAsmOutputRE.FindStringSubmatch(input); len(fields) == 3 {
  107. if address, err := strconv.ParseUint(fields[1], 16, 64); err == nil {
  108. assembly = append(assembly,
  109. plugin.Inst{
  110. Addr: address,
  111. Text: fields[2],
  112. Function: function,
  113. File: file,
  114. Line: line,
  115. })
  116. continue
  117. }
  118. }
  119. if fields := objdumpOutputFileLine.FindStringSubmatch(input); len(fields) == 3 {
  120. if l, err := strconv.ParseUint(fields[2], 10, 32); err == nil {
  121. file, line = fields[1], int(l)
  122. }
  123. continue
  124. }
  125. if fields := objdumpOutputFunction.FindStringSubmatch(input); len(fields) == 2 {
  126. function = fields[1]
  127. continue
  128. }
  129. // Reset on unrecognized lines.
  130. function, file, line = "", "", 0
  131. }
  132. return assembly, nil
  133. }
  134. // nextSymbol parses the nm output to find the next symbol listed.
  135. // Skips over any output it cannot recognize.
  136. func nextSymbol(buf *bytes.Buffer) (uint64, string, error) {
  137. for {
  138. line, err := buf.ReadString('\n')
  139. if err != nil {
  140. if err != io.EOF || line == "" {
  141. return 0, "", err
  142. }
  143. }
  144. if fields := nmOutputRE.FindStringSubmatch(line); len(fields) == 4 {
  145. if address, err := strconv.ParseUint(fields[1], 16, 64); err == nil {
  146. return address, fields[3], nil
  147. }
  148. }
  149. }
  150. }