github.com/bir3/gocompiler@v0.9.2202/src/cmd/link/internal/dwtest/dwtest.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package dwtest 6 7 import ( 8 "debug/dwarf" 9 "errors" 10 "fmt" 11 "os" 12 ) 13 14 // Helper type for supporting queries on DIEs within a DWARF 15 // .debug_info section. Invoke the populate() method below passing in 16 // a dwarf.Reader, which will read in all DIEs and keep track of 17 // parent/child relationships. Queries can then be made to ask for 18 // DIEs by name or by offset. This will hopefully reduce boilerplate 19 // for future test writing. 20 21 type Examiner struct { 22 dies []*dwarf.Entry 23 idxByOffset map[dwarf.Offset]int 24 kids map[int][]int 25 parent map[int]int 26 byname map[string][]int 27 } 28 29 // Populate the Examiner using the DIEs read from rdr. 30 func (ex *Examiner) Populate(rdr *dwarf.Reader) error { 31 ex.idxByOffset = make(map[dwarf.Offset]int) 32 ex.kids = make(map[int][]int) 33 ex.parent = make(map[int]int) 34 ex.byname = make(map[string][]int) 35 var nesting []int 36 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { 37 if err != nil { 38 return err 39 } 40 if entry.Tag == 0 { 41 // terminator 42 if len(nesting) == 0 { 43 return errors.New("nesting stack underflow") 44 } 45 nesting = nesting[:len(nesting)-1] 46 continue 47 } 48 idx := len(ex.dies) 49 ex.dies = append(ex.dies, entry) 50 if _, found := ex.idxByOffset[entry.Offset]; found { 51 return errors.New("DIE clash on offset") 52 } 53 ex.idxByOffset[entry.Offset] = idx 54 if name, ok := entry.Val(dwarf.AttrName).(string); ok { 55 ex.byname[name] = append(ex.byname[name], idx) 56 } 57 if len(nesting) > 0 { 58 parent := nesting[len(nesting)-1] 59 ex.kids[parent] = append(ex.kids[parent], idx) 60 ex.parent[idx] = parent 61 } 62 if entry.Children { 63 nesting = append(nesting, idx) 64 } 65 } 66 if len(nesting) > 0 { 67 return errors.New("unterminated child sequence") 68 } 69 return nil 70 } 71 72 func (e *Examiner) DIEs() []*dwarf.Entry { 73 return e.dies 74 } 75 76 func indent(ilevel int) { 77 for i := 0; i < ilevel; i++ { 78 fmt.Printf(" ") 79 } 80 } 81 82 // For debugging new tests 83 func (ex *Examiner) DumpEntry(idx int, dumpKids bool, ilevel int) { 84 if idx >= len(ex.dies) { 85 fmt.Fprintf(os.Stderr, "DumpEntry: bad DIE %d: index out of range\n", idx) 86 return 87 } 88 entry := ex.dies[idx] 89 indent(ilevel) 90 fmt.Printf("0x%x: %v\n", idx, entry.Tag) 91 for _, f := range entry.Field { 92 indent(ilevel) 93 fmt.Printf("at=%v val=%v\n", f.Attr, f.Val) 94 } 95 if dumpKids { 96 ksl := ex.kids[idx] 97 for _, k := range ksl { 98 ex.DumpEntry(k, true, ilevel+2) 99 } 100 } 101 } 102 103 // Given a DIE offset, return the previously read dwarf.Entry, or nil 104 func (ex *Examiner) EntryFromOffset(off dwarf.Offset) *dwarf.Entry { 105 if idx, found := ex.idxByOffset[off]; found && idx != -1 { 106 return ex.entryFromIdx(idx) 107 } 108 return nil 109 } 110 111 // Return the ID that Examiner uses to refer to the DIE at offset off 112 func (ex *Examiner) IdxFromOffset(off dwarf.Offset) int { 113 if idx, found := ex.idxByOffset[off]; found { 114 return idx 115 } 116 return -1 117 } 118 119 // Return the dwarf.Entry pointer for the DIE with id 'idx' 120 func (ex *Examiner) entryFromIdx(idx int) *dwarf.Entry { 121 if idx >= len(ex.dies) || idx < 0 { 122 return nil 123 } 124 return ex.dies[idx] 125 } 126 127 // Returns a list of child entries for a die with ID 'idx' 128 func (ex *Examiner) Children(idx int) []*dwarf.Entry { 129 sl := ex.kids[idx] 130 ret := make([]*dwarf.Entry, len(sl)) 131 for i, k := range sl { 132 ret[i] = ex.entryFromIdx(k) 133 } 134 return ret 135 } 136 137 // Returns parent DIE for DIE 'idx', or nil if the DIE is top level 138 func (ex *Examiner) Parent(idx int) *dwarf.Entry { 139 p, found := ex.parent[idx] 140 if !found { 141 return nil 142 } 143 return ex.entryFromIdx(p) 144 } 145 146 // ParentCU returns the enclosing compilation unit DIE for the DIE 147 // with a given index, or nil if for some reason we can't establish a 148 // parent. 149 func (ex *Examiner) ParentCU(idx int) *dwarf.Entry { 150 for { 151 parentDie := ex.Parent(idx) 152 if parentDie == nil { 153 return nil 154 } 155 if parentDie.Tag == dwarf.TagCompileUnit { 156 return parentDie 157 } 158 idx = ex.IdxFromOffset(parentDie.Offset) 159 } 160 } 161 162 // FileRef takes a given DIE by index and a numeric file reference 163 // (presumably from a decl_file or call_file attribute), looks up the 164 // reference in the .debug_line file table, and returns the proper 165 // string for it. We need to know which DIE is making the reference 166 // so as to find the right compilation unit. 167 func (ex *Examiner) FileRef(dw *dwarf.Data, dieIdx int, fileRef int64) (string, error) { 168 169 // Find the parent compilation unit DIE for the specified DIE. 170 cuDie := ex.ParentCU(dieIdx) 171 if cuDie == nil { 172 return "", fmt.Errorf("no parent CU DIE for DIE with idx %d?", dieIdx) 173 } 174 // Construct a line reader and then use it to get the file string. 175 lr, lrerr := dw.LineReader(cuDie) 176 if lrerr != nil { 177 return "", fmt.Errorf("d.LineReader: %v", lrerr) 178 } 179 files := lr.Files() 180 if fileRef < 0 || int(fileRef) > len(files)-1 { 181 return "", fmt.Errorf("Examiner.FileRef: malformed file reference %d", fileRef) 182 } 183 return files[fileRef].Name, nil 184 } 185 186 // Return a list of all DIEs with name 'name'. When searching for DIEs 187 // by name, keep in mind that the returned results will include child 188 // DIEs such as params/variables. For example, asking for all DIEs named 189 // "p" for even a small program will give you 400-500 entries. 190 func (ex *Examiner) Named(name string) []*dwarf.Entry { 191 sl := ex.byname[name] 192 ret := make([]*dwarf.Entry, len(sl)) 193 for i, k := range sl { 194 ret[i] = ex.entryFromIdx(k) 195 } 196 return ret 197 }