|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 |
- // Copyright 2018 Google Inc. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- package dwarf
-
- import (
- "fmt"
- "sort"
- )
-
- // pcToFuncEntries maps PC ranges to function entries.
- //
- // Each element contains a *Entry for a function and its corresponding start PC.
- // If we know the address one past the last instruction of a function, and it is
- // not equal to the start address of the next function, we mark that with
- // another element containing that address and a nil entry. The elements are
- // sorted by PC. Among elements with the same PC, those with non-nil *Entry
- // are put earlier.
- type pcToFuncEntries []pcToFuncEntry
- type pcToFuncEntry struct {
- pc uint64
- entry *Entry
- }
-
- func (p pcToFuncEntries) Len() int { return len(p) }
- func (p pcToFuncEntries) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
- func (p pcToFuncEntries) Less(i, j int) bool {
- if p[i].pc != p[j].pc {
- return p[i].pc < p[j].pc
- }
- return p[i].entry != nil && p[j].entry == nil
- }
-
- // nameCache maps each symbol name to a linked list of the entries with that name.
- type nameCache map[string]*nameCacheEntry
- type nameCacheEntry struct {
- entry *Entry
- link *nameCacheEntry
- }
-
- // pcToLineEntries maps PCs to line numbers.
- //
- // It is a slice of (PC, line, file number) triples, sorted by PC. The file
- // number is an index into the source files slice.
- // If (PC1, line1, file1) and (PC2, line2, file2) are two consecutive elements,
- // then the span of addresses [PC1, PC2) belongs to (line1, file1). If an
- // element's file number is zero, it only marks the end of a span.
- //
- // TODO: could save memory by changing pcToLineEntries and lineToPCEntries to use
- // interval trees containing references into .debug_line.
- type pcToLineEntries []pcToLineEntry
- type pcToLineEntry struct {
- pc uint64
- line uint64
- file uint64
- }
-
- func (p pcToLineEntries) Len() int { return len(p) }
- func (p pcToLineEntries) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
- func (p pcToLineEntries) Less(i, j int) bool {
- if p[i].pc != p[j].pc {
- return p[i].pc < p[j].pc
- }
- return p[i].file > p[j].file
- }
-
- // byFileLine is used temporarily while building lineToPCEntries.
- type byFileLine []pcToLineEntry
-
- func (b byFileLine) Len() int { return len(b) }
- func (b byFileLine) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
- func (b byFileLine) Less(i, j int) bool {
- if b[i].file != b[j].file {
- return b[i].file < b[j].file
- }
- return b[i].line < b[j].line
- }
-
- // lineToPCEntries maps line numbers to breakpoint addresses.
- //
- // The slice contains, for each source file in Data, a slice of (line, PC)
- // pairs, sorted by line. Note that there may be more than one PC for a line.
- type lineToPCEntries [][]lineToPCEntry
- type lineToPCEntry struct {
- line uint64
- pc uint64
- }
-
- func (d *Data) buildLineToPCCache(pclfs pcToLineEntries) {
- // TODO: only include lines where is_stmt is true
- sort.Sort(byFileLine(pclfs))
- // Make a slice of (line, PC) pairs for each (non-zero) file.
- var (
- c = make(lineToPCEntries, len(d.sourceFiles))
- curSlice []lineToPCEntry
- )
- for i, pclf := range pclfs {
- if pclf.file == 0 {
- // This entry indicated the end of an instruction sequence, not a breakpoint.
- continue
- }
- curSlice = append(curSlice, lineToPCEntry{line: pclf.line, pc: pclf.pc})
- if i+1 == len(pclfs) || pclf.file != pclfs[i+1].file {
- // curSlice now contains all of the entries for pclf.file.
- if pclf.file > 0 && pclf.file < uint64(len(c)) {
- c[pclf.file] = curSlice
- }
- curSlice = nil
- }
- }
- d.lineToPCEntries = c
- }
-
- func (d *Data) buildPCToLineCache(cache pcToLineEntries) {
- // Sort cache by PC (in increasing order), then by file number (in decreasing order).
- sort.Sort(cache)
-
- // Build a copy without redundant entries.
- var out pcToLineEntries
- for i, pclf := range cache {
- if i > 0 && pclf.pc == cache[i-1].pc {
- // This entry is for the same PC as the previous entry.
- continue
- }
- if i > 0 && pclf.file == cache[i-1].file && pclf.line == cache[i-1].line {
- // This entry is for the same file and line as the previous entry.
- continue
- }
- out = append(out, pclf)
- }
- d.pcToLineEntries = out
- }
-
- // buildLineCaches constructs d.sourceFiles, d.lineToPCEntries, d.pcToLineEntries.
- func (d *Data) buildLineCaches() error {
- var cache pcToLineEntries
- r := d.Reader()
- for {
- entry, err := r.Next()
- if err != nil {
- return err
- }
- if entry == nil {
- break
- }
- if entry.Tag != TagCompileUnit {
- r.SkipChildren()
- continue
- }
- // Get the offset of this unit's line number program in .debug_line.
- offset, ok := entry.Val(AttrStmtList).(int64)
- if !ok {
- return fmt.Errorf("AttrStmtList not present or not int64 for unit %d", r.unit)
- }
- buf := makeBuf(d, &d.unit[r.unit], "line", Offset(offset), d.line[offset:])
- var m lineMachine
- if err := m.parseHeader(&buf); err != nil {
- return err
- }
- fileNumOffset := uint64(len(d.sourceFiles))
- for _, f := range m.header.file {
- d.sourceFiles = append(d.sourceFiles, f.name)
- }
- fn := func(m *lineMachine) bool {
- if m.endSequence {
- cache = append(cache, pcToLineEntry{
- pc: m.address,
- line: 0,
- file: 0,
- })
- } else {
- // m.file is a 1-based index into the files of this line number program's
- // header. Translate it to a 0-based index into d.sourceFiles.
- fnum := fileNumOffset + m.file - 1
- cache = append(cache, pcToLineEntry{
- pc: m.address,
- line: m.line,
- file: fnum,
- })
- }
- return true
- }
- if err := m.evalCompilationUnit(&buf, fn); err != nil {
- return err
- }
- }
- d.buildLineToPCCache(cache)
- d.buildPCToLineCache(cache)
- return nil
- }
-
- // buildInfoCaches initializes nameCache and pcToFuncEntries by walking the
- // top-level entries under each compile unit. It swallows any errors in parsing.
- func (d *Data) buildInfoCaches() {
- // TODO: record errors somewhere?
- d.nameCache = make(map[string]*nameCacheEntry)
-
- var pcToFuncEntries pcToFuncEntries
-
- r := d.Reader()
- loop:
- for {
- entry, err := r.Next()
- if entry == nil || err != nil {
- break loop
- }
- if entry.Tag != TagCompileUnit /* DW_TAG_compile_unit */ {
- r.SkipChildren()
- continue
- }
- for {
- entry, err := r.Next()
- if entry == nil || err != nil {
- break loop
- }
- if entry.Tag == 0 {
- // End of children of current compile unit.
- break
- }
- r.SkipChildren()
- // Update name-to-entry cache.
- if name, ok := entry.Val(AttrName).(string); ok {
- d.nameCache[name] = &nameCacheEntry{entry: entry, link: d.nameCache[name]}
- }
-
- // If this entry is a function, update PC-to-containing-function cache.
- if entry.Tag != TagSubprogram /* DW_TAG_subprogram */ {
- continue
- }
-
- // DW_AT_low_pc, if present, is the address of the first instruction of
- // the function.
- lowpc, ok := entry.Val(AttrLowpc).(uint64)
- if !ok {
- continue
- }
- pcToFuncEntries = append(pcToFuncEntries, pcToFuncEntry{lowpc, entry})
-
- // DW_AT_high_pc, if present (TODO: and of class address) is the address
- // one past the last instruction of the function.
- highpc, ok := entry.Val(AttrHighpc).(uint64)
- if !ok {
- continue
- }
- pcToFuncEntries = append(pcToFuncEntries, pcToFuncEntry{highpc, nil})
- }
- }
- // Sort elements by PC. If there are multiple elements with the same PC,
- // those with non-nil *Entry are placed earlier.
- sort.Sort(pcToFuncEntries)
-
- // Copy only the first element for each PC to out.
- n := 0
- for i, ce := range pcToFuncEntries {
- if i == 0 || ce.pc != pcToFuncEntries[i-1].pc {
- n++
- }
- }
- out := make([]pcToFuncEntry, 0, n)
- for i, ce := range pcToFuncEntries {
- if i == 0 || ce.pc != pcToFuncEntries[i-1].pc {
- out = append(out, ce)
- }
- }
- d.pcToFuncEntries = out
- }
|