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.
 
 
 

278 lines
8.0 KiB

  1. // Copyright 2018 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 dwarf
  15. import (
  16. "fmt"
  17. "sort"
  18. )
  19. // pcToFuncEntries maps PC ranges to function entries.
  20. //
  21. // Each element contains a *Entry for a function and its corresponding start PC.
  22. // If we know the address one past the last instruction of a function, and it is
  23. // not equal to the start address of the next function, we mark that with
  24. // another element containing that address and a nil entry. The elements are
  25. // sorted by PC. Among elements with the same PC, those with non-nil *Entry
  26. // are put earlier.
  27. type pcToFuncEntries []pcToFuncEntry
  28. type pcToFuncEntry struct {
  29. pc uint64
  30. entry *Entry
  31. }
  32. func (p pcToFuncEntries) Len() int { return len(p) }
  33. func (p pcToFuncEntries) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
  34. func (p pcToFuncEntries) Less(i, j int) bool {
  35. if p[i].pc != p[j].pc {
  36. return p[i].pc < p[j].pc
  37. }
  38. return p[i].entry != nil && p[j].entry == nil
  39. }
  40. // nameCache maps each symbol name to a linked list of the entries with that name.
  41. type nameCache map[string]*nameCacheEntry
  42. type nameCacheEntry struct {
  43. entry *Entry
  44. link *nameCacheEntry
  45. }
  46. // pcToLineEntries maps PCs to line numbers.
  47. //
  48. // It is a slice of (PC, line, file number) triples, sorted by PC. The file
  49. // number is an index into the source files slice.
  50. // If (PC1, line1, file1) and (PC2, line2, file2) are two consecutive elements,
  51. // then the span of addresses [PC1, PC2) belongs to (line1, file1). If an
  52. // element's file number is zero, it only marks the end of a span.
  53. //
  54. // TODO: could save memory by changing pcToLineEntries and lineToPCEntries to use
  55. // interval trees containing references into .debug_line.
  56. type pcToLineEntries []pcToLineEntry
  57. type pcToLineEntry struct {
  58. pc uint64
  59. line uint64
  60. file uint64
  61. }
  62. func (p pcToLineEntries) Len() int { return len(p) }
  63. func (p pcToLineEntries) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
  64. func (p pcToLineEntries) Less(i, j int) bool {
  65. if p[i].pc != p[j].pc {
  66. return p[i].pc < p[j].pc
  67. }
  68. return p[i].file > p[j].file
  69. }
  70. // byFileLine is used temporarily while building lineToPCEntries.
  71. type byFileLine []pcToLineEntry
  72. func (b byFileLine) Len() int { return len(b) }
  73. func (b byFileLine) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
  74. func (b byFileLine) Less(i, j int) bool {
  75. if b[i].file != b[j].file {
  76. return b[i].file < b[j].file
  77. }
  78. return b[i].line < b[j].line
  79. }
  80. // lineToPCEntries maps line numbers to breakpoint addresses.
  81. //
  82. // The slice contains, for each source file in Data, a slice of (line, PC)
  83. // pairs, sorted by line. Note that there may be more than one PC for a line.
  84. type lineToPCEntries [][]lineToPCEntry
  85. type lineToPCEntry struct {
  86. line uint64
  87. pc uint64
  88. }
  89. func (d *Data) buildLineToPCCache(pclfs pcToLineEntries) {
  90. // TODO: only include lines where is_stmt is true
  91. sort.Sort(byFileLine(pclfs))
  92. // Make a slice of (line, PC) pairs for each (non-zero) file.
  93. var (
  94. c = make(lineToPCEntries, len(d.sourceFiles))
  95. curSlice []lineToPCEntry
  96. )
  97. for i, pclf := range pclfs {
  98. if pclf.file == 0 {
  99. // This entry indicated the end of an instruction sequence, not a breakpoint.
  100. continue
  101. }
  102. curSlice = append(curSlice, lineToPCEntry{line: pclf.line, pc: pclf.pc})
  103. if i+1 == len(pclfs) || pclf.file != pclfs[i+1].file {
  104. // curSlice now contains all of the entries for pclf.file.
  105. if pclf.file > 0 && pclf.file < uint64(len(c)) {
  106. c[pclf.file] = curSlice
  107. }
  108. curSlice = nil
  109. }
  110. }
  111. d.lineToPCEntries = c
  112. }
  113. func (d *Data) buildPCToLineCache(cache pcToLineEntries) {
  114. // Sort cache by PC (in increasing order), then by file number (in decreasing order).
  115. sort.Sort(cache)
  116. // Build a copy without redundant entries.
  117. var out pcToLineEntries
  118. for i, pclf := range cache {
  119. if i > 0 && pclf.pc == cache[i-1].pc {
  120. // This entry is for the same PC as the previous entry.
  121. continue
  122. }
  123. if i > 0 && pclf.file == cache[i-1].file && pclf.line == cache[i-1].line {
  124. // This entry is for the same file and line as the previous entry.
  125. continue
  126. }
  127. out = append(out, pclf)
  128. }
  129. d.pcToLineEntries = out
  130. }
  131. // buildLineCaches constructs d.sourceFiles, d.lineToPCEntries, d.pcToLineEntries.
  132. func (d *Data) buildLineCaches() error {
  133. var cache pcToLineEntries
  134. r := d.Reader()
  135. for {
  136. entry, err := r.Next()
  137. if err != nil {
  138. return err
  139. }
  140. if entry == nil {
  141. break
  142. }
  143. if entry.Tag != TagCompileUnit {
  144. r.SkipChildren()
  145. continue
  146. }
  147. // Get the offset of this unit's line number program in .debug_line.
  148. offset, ok := entry.Val(AttrStmtList).(int64)
  149. if !ok {
  150. return fmt.Errorf("AttrStmtList not present or not int64 for unit %d", r.unit)
  151. }
  152. buf := makeBuf(d, &d.unit[r.unit], "line", Offset(offset), d.line[offset:])
  153. var m lineMachine
  154. if err := m.parseHeader(&buf); err != nil {
  155. return err
  156. }
  157. fileNumOffset := uint64(len(d.sourceFiles))
  158. for _, f := range m.header.file {
  159. d.sourceFiles = append(d.sourceFiles, f.name)
  160. }
  161. fn := func(m *lineMachine) bool {
  162. if m.endSequence {
  163. cache = append(cache, pcToLineEntry{
  164. pc: m.address,
  165. line: 0,
  166. file: 0,
  167. })
  168. } else {
  169. // m.file is a 1-based index into the files of this line number program's
  170. // header. Translate it to a 0-based index into d.sourceFiles.
  171. fnum := fileNumOffset + m.file - 1
  172. cache = append(cache, pcToLineEntry{
  173. pc: m.address,
  174. line: m.line,
  175. file: fnum,
  176. })
  177. }
  178. return true
  179. }
  180. if err := m.evalCompilationUnit(&buf, fn); err != nil {
  181. return err
  182. }
  183. }
  184. d.buildLineToPCCache(cache)
  185. d.buildPCToLineCache(cache)
  186. return nil
  187. }
  188. // buildInfoCaches initializes nameCache and pcToFuncEntries by walking the
  189. // top-level entries under each compile unit. It swallows any errors in parsing.
  190. func (d *Data) buildInfoCaches() {
  191. // TODO: record errors somewhere?
  192. d.nameCache = make(map[string]*nameCacheEntry)
  193. var pcToFuncEntries pcToFuncEntries
  194. r := d.Reader()
  195. loop:
  196. for {
  197. entry, err := r.Next()
  198. if entry == nil || err != nil {
  199. break loop
  200. }
  201. if entry.Tag != TagCompileUnit /* DW_TAG_compile_unit */ {
  202. r.SkipChildren()
  203. continue
  204. }
  205. for {
  206. entry, err := r.Next()
  207. if entry == nil || err != nil {
  208. break loop
  209. }
  210. if entry.Tag == 0 {
  211. // End of children of current compile unit.
  212. break
  213. }
  214. r.SkipChildren()
  215. // Update name-to-entry cache.
  216. if name, ok := entry.Val(AttrName).(string); ok {
  217. d.nameCache[name] = &nameCacheEntry{entry: entry, link: d.nameCache[name]}
  218. }
  219. // If this entry is a function, update PC-to-containing-function cache.
  220. if entry.Tag != TagSubprogram /* DW_TAG_subprogram */ {
  221. continue
  222. }
  223. // DW_AT_low_pc, if present, is the address of the first instruction of
  224. // the function.
  225. lowpc, ok := entry.Val(AttrLowpc).(uint64)
  226. if !ok {
  227. continue
  228. }
  229. pcToFuncEntries = append(pcToFuncEntries, pcToFuncEntry{lowpc, entry})
  230. // DW_AT_high_pc, if present (TODO: and of class address) is the address
  231. // one past the last instruction of the function.
  232. highpc, ok := entry.Val(AttrHighpc).(uint64)
  233. if !ok {
  234. continue
  235. }
  236. pcToFuncEntries = append(pcToFuncEntries, pcToFuncEntry{highpc, nil})
  237. }
  238. }
  239. // Sort elements by PC. If there are multiple elements with the same PC,
  240. // those with non-nil *Entry are placed earlier.
  241. sort.Sort(pcToFuncEntries)
  242. // Copy only the first element for each PC to out.
  243. n := 0
  244. for i, ce := range pcToFuncEntries {
  245. if i == 0 || ce.pc != pcToFuncEntries[i-1].pc {
  246. n++
  247. }
  248. }
  249. out := make([]pcToFuncEntry, 0, n)
  250. for i, ce := range pcToFuncEntries {
  251. if i == 0 || ce.pc != pcToFuncEntries[i-1].pc {
  252. out = append(out, ce)
  253. }
  254. }
  255. d.pcToFuncEntries = out
  256. }