github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/debug/dwarf/entry_test.go (about) 1 // Copyright 2009 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 dwarf_test 6 7 import ( 8 . "debug/dwarf" 9 "encoding/binary" 10 "path/filepath" 11 "reflect" 12 "testing" 13 ) 14 15 func TestSplit(t *testing.T) { 16 // debug/dwarf doesn't (currently) support split DWARF, but 17 // the attributes that pointed to the split DWARF used to 18 // cause loading the DWARF data to fail entirely (issue 19 // #12592). Test that we can at least read the DWARF data. 20 d := elfData(t, "testdata/split.elf") 21 r := d.Reader() 22 e, err := r.Next() 23 if err != nil { 24 t.Fatal(err) 25 } 26 if e.Tag != TagCompileUnit { 27 t.Fatalf("bad tag: have %s, want %s", e.Tag, TagCompileUnit) 28 } 29 // Check that we were able to parse the unknown section offset 30 // field, even if we can't figure out its DWARF class. 31 const AttrGNUAddrBase Attr = 0x2133 32 f := e.AttrField(AttrGNUAddrBase) 33 if _, ok := f.Val.(int64); !ok { 34 t.Fatalf("bad attribute value type: have %T, want int64", f.Val) 35 } 36 if f.Class != ClassUnknown { 37 t.Fatalf("bad class: have %s, want %s", f.Class, ClassUnknown) 38 } 39 } 40 41 // wantRange maps from a PC to the ranges of the compilation unit 42 // containing that PC. 43 type wantRange struct { 44 pc uint64 45 ranges [][2]uint64 46 } 47 48 func TestReaderSeek(t *testing.T) { 49 want := []wantRange{ 50 {0x40059d, [][2]uint64{{0x40059d, 0x400601}}}, 51 {0x400600, [][2]uint64{{0x40059d, 0x400601}}}, 52 {0x400601, [][2]uint64{{0x400601, 0x400611}}}, 53 {0x4005f0, [][2]uint64{{0x40059d, 0x400601}}}, // loop test 54 {0x10, nil}, 55 {0x400611, nil}, 56 } 57 testRanges(t, "testdata/line-gcc.elf", want) 58 59 want = []wantRange{ 60 {0x401122, [][2]uint64{{0x401122, 0x401166}}}, 61 {0x401165, [][2]uint64{{0x401122, 0x401166}}}, 62 {0x401166, [][2]uint64{{0x401166, 0x401179}}}, 63 } 64 testRanges(t, "testdata/line-gcc-dwarf5.elf", want) 65 66 want = []wantRange{ 67 {0x401130, [][2]uint64{{0x401130, 0x40117e}}}, 68 {0x40117d, [][2]uint64{{0x401130, 0x40117e}}}, 69 {0x40117e, nil}, 70 } 71 testRanges(t, "testdata/line-clang-dwarf5.elf", want) 72 } 73 74 func TestRangesSection(t *testing.T) { 75 want := []wantRange{ 76 {0x400500, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}}, 77 {0x400400, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}}, 78 {0x400548, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}}, 79 {0x400407, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}}, 80 {0x400408, nil}, 81 {0x400449, nil}, 82 {0x4003ff, nil}, 83 } 84 testRanges(t, "testdata/ranges.elf", want) 85 } 86 87 func testRanges(t *testing.T, name string, want []wantRange) { 88 d := elfData(t, name) 89 r := d.Reader() 90 for _, w := range want { 91 entry, err := r.SeekPC(w.pc) 92 if err != nil { 93 if w.ranges != nil { 94 t.Errorf("%s: missing Entry for %#x", name, w.pc) 95 } 96 if err != ErrUnknownPC { 97 t.Errorf("%s: expected ErrUnknownPC for %#x, got %v", name, w.pc, err) 98 } 99 continue 100 } 101 102 ranges, err := d.Ranges(entry) 103 if err != nil { 104 t.Errorf("%s: %v", name, err) 105 continue 106 } 107 if !reflect.DeepEqual(ranges, w.ranges) { 108 t.Errorf("%s: for %#x got %x, expected %x", name, w.pc, ranges, w.ranges) 109 } 110 } 111 } 112 113 func TestReaderRanges(t *testing.T) { 114 type subprograms []struct { 115 name string 116 ranges [][2]uint64 117 } 118 tests := []struct { 119 filename string 120 subprograms subprograms 121 }{ 122 { 123 "testdata/line-gcc.elf", 124 subprograms{ 125 {"f1", [][2]uint64{{0x40059d, 0x4005e7}}}, 126 {"main", [][2]uint64{{0x4005e7, 0x400601}}}, 127 {"f2", [][2]uint64{{0x400601, 0x400611}}}, 128 }, 129 }, 130 { 131 "testdata/line-gcc-dwarf5.elf", 132 subprograms{ 133 {"main", [][2]uint64{{0x401147, 0x401166}}}, 134 {"f1", [][2]uint64{{0x401122, 0x401147}}}, 135 {"f2", [][2]uint64{{0x401166, 0x401179}}}, 136 }, 137 }, 138 { 139 "testdata/line-clang-dwarf5.elf", 140 subprograms{ 141 {"main", [][2]uint64{{0x401130, 0x401144}}}, 142 {"f1", [][2]uint64{{0x401150, 0x40117e}}}, 143 {"f2", [][2]uint64{{0x401180, 0x401197}}}, 144 }, 145 }, 146 } 147 148 for _, test := range tests { 149 d := elfData(t, test.filename) 150 subprograms := test.subprograms 151 152 r := d.Reader() 153 i := 0 154 for entry, err := r.Next(); entry != nil && err == nil; entry, err = r.Next() { 155 if entry.Tag != TagSubprogram { 156 continue 157 } 158 159 if i > len(subprograms) { 160 t.Fatalf("%s: too many subprograms (expected at most %d)", test.filename, i) 161 } 162 163 if got := entry.Val(AttrName).(string); got != subprograms[i].name { 164 t.Errorf("%s: subprogram %d name is %s, expected %s", test.filename, i, got, subprograms[i].name) 165 } 166 ranges, err := d.Ranges(entry) 167 if err != nil { 168 t.Errorf("%s: subprogram %d: %v", test.filename, i, err) 169 continue 170 } 171 if !reflect.DeepEqual(ranges, subprograms[i].ranges) { 172 t.Errorf("%s: subprogram %d ranges are %x, expected %x", test.filename, i, ranges, subprograms[i].ranges) 173 } 174 i++ 175 } 176 177 if i < len(subprograms) { 178 t.Errorf("%s: saw only %d subprograms, expected %d", test.filename, i, len(subprograms)) 179 } 180 } 181 } 182 183 func Test64Bit(t *testing.T) { 184 // I don't know how to generate a 64-bit DWARF debug 185 // compilation unit except by using XCOFF, so this is 186 // hand-written. 187 tests := []struct { 188 name string 189 info []byte 190 addrSize int 191 byteOrder binary.ByteOrder 192 }{ 193 { 194 "32-bit little", 195 []byte{0x30, 0, 0, 0, // comp unit length 196 4, 0, // DWARF version 4 197 0, 0, 0, 0, // abbrev offset 198 8, // address size 199 0, 200 0, 0, 0, 0, 0, 0, 0, 0, 201 0, 0, 0, 0, 0, 0, 0, 0, 202 0, 0, 0, 0, 0, 0, 0, 0, 203 0, 0, 0, 0, 0, 0, 0, 0, 204 0, 0, 0, 0, 0, 0, 0, 0, 205 }, 206 8, binary.LittleEndian, 207 }, 208 { 209 "64-bit little", 210 []byte{0xff, 0xff, 0xff, 0xff, // 64-bit DWARF 211 0x30, 0, 0, 0, 0, 0, 0, 0, // comp unit length 212 4, 0, // DWARF version 4 213 0, 0, 0, 0, 0, 0, 0, 0, // abbrev offset 214 8, // address size 215 0, 0, 0, 0, 0, 216 0, 0, 0, 0, 0, 0, 0, 0, 217 0, 0, 0, 0, 0, 0, 0, 0, 218 0, 0, 0, 0, 0, 0, 0, 0, 219 0, 0, 0, 0, 0, 0, 0, 0, 220 }, 221 8, binary.LittleEndian, 222 }, 223 { 224 "64-bit big", 225 []byte{0xff, 0xff, 0xff, 0xff, // 64-bit DWARF 226 0, 0, 0, 0, 0, 0, 0, 0x30, // comp unit length 227 0, 4, // DWARF version 4 228 0, 0, 0, 0, 0, 0, 0, 0, // abbrev offset 229 8, // address size 230 0, 0, 0, 0, 0, 231 0, 0, 0, 0, 0, 0, 0, 0, 232 0, 0, 0, 0, 0, 0, 0, 0, 233 0, 0, 0, 0, 0, 0, 0, 0, 234 0, 0, 0, 0, 0, 0, 0, 0, 235 }, 236 8, binary.BigEndian, 237 }, 238 } 239 240 for _, test := range tests { 241 data, err := New(nil, nil, nil, test.info, nil, nil, nil, nil) 242 if err != nil { 243 t.Errorf("%s: %v", test.name, err) 244 } 245 246 r := data.Reader() 247 if r.AddressSize() != test.addrSize { 248 t.Errorf("%s: got address size %d, want %d", test.name, r.AddressSize(), test.addrSize) 249 } 250 if r.ByteOrder() != test.byteOrder { 251 t.Errorf("%s: got byte order %s, want %s", test.name, r.ByteOrder(), test.byteOrder) 252 } 253 } 254 } 255 256 func TestUnitIteration(t *testing.T) { 257 // Iterate over all ELF test files we have and ensure that 258 // we get the same set of compilation units skipping (method 0) 259 // and not skipping (method 1) CU children. 260 files, err := filepath.Glob(filepath.Join("testdata", "*.elf")) 261 if err != nil { 262 t.Fatal(err) 263 } 264 for _, file := range files { 265 t.Run(file, func(t *testing.T) { 266 d := elfData(t, file) 267 var units [2][]interface{} 268 for method := range units { 269 for r := d.Reader(); ; { 270 ent, err := r.Next() 271 if err != nil { 272 t.Fatal(err) 273 } 274 if ent == nil { 275 break 276 } 277 if ent.Tag == TagCompileUnit { 278 units[method] = append(units[method], ent.Val(AttrName)) 279 } 280 if method == 0 { 281 if ent.Tag != TagCompileUnit { 282 t.Fatalf("found unexpected tag %v on top level", ent.Tag) 283 } 284 r.SkipChildren() 285 } 286 } 287 } 288 t.Logf("skipping CUs: %v", units[0]) 289 t.Logf("not-skipping CUs: %v", units[1]) 290 if !reflect.DeepEqual(units[0], units[1]) { 291 t.Fatal("set of CUs differ") 292 } 293 }) 294 } 295 }