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.
 
 
 

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