github.com/comwrg/go/src@v0.0.0-20220319063731-c238d0440370/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 TestRangesRnglistx(t *testing.T) { 88 want := []wantRange{ 89 {0x401000, [][2]uint64{{0x401020, 0x40102c}, {0x401000, 0x40101d}}}, 90 {0x40101c, [][2]uint64{{0x401020, 0x40102c}, {0x401000, 0x40101d}}}, 91 {0x40101d, nil}, 92 {0x40101f, nil}, 93 {0x401020, [][2]uint64{{0x401020, 0x40102c}, {0x401000, 0x40101d}}}, 94 {0x40102b, [][2]uint64{{0x401020, 0x40102c}, {0x401000, 0x40101d}}}, 95 {0x40102c, nil}, 96 } 97 testRanges(t, "testdata/rnglistx.elf", want) 98 } 99 100 func testRanges(t *testing.T, name string, want []wantRange) { 101 d := elfData(t, name) 102 r := d.Reader() 103 for _, w := range want { 104 entry, err := r.SeekPC(w.pc) 105 if err != nil { 106 if w.ranges != nil { 107 t.Errorf("%s: missing Entry for %#x", name, w.pc) 108 } 109 if err != ErrUnknownPC { 110 t.Errorf("%s: expected ErrUnknownPC for %#x, got %v", name, w.pc, err) 111 } 112 continue 113 } 114 115 ranges, err := d.Ranges(entry) 116 if err != nil { 117 t.Errorf("%s: %v", name, err) 118 continue 119 } 120 if !reflect.DeepEqual(ranges, w.ranges) { 121 t.Errorf("%s: for %#x got %x, expected %x", name, w.pc, ranges, w.ranges) 122 } 123 } 124 } 125 126 func TestReaderRanges(t *testing.T) { 127 type subprograms []struct { 128 name string 129 ranges [][2]uint64 130 } 131 tests := []struct { 132 filename string 133 subprograms subprograms 134 }{ 135 { 136 "testdata/line-gcc.elf", 137 subprograms{ 138 {"f1", [][2]uint64{{0x40059d, 0x4005e7}}}, 139 {"main", [][2]uint64{{0x4005e7, 0x400601}}}, 140 {"f2", [][2]uint64{{0x400601, 0x400611}}}, 141 }, 142 }, 143 { 144 "testdata/line-gcc-dwarf5.elf", 145 subprograms{ 146 {"main", [][2]uint64{{0x401147, 0x401166}}}, 147 {"f1", [][2]uint64{{0x401122, 0x401147}}}, 148 {"f2", [][2]uint64{{0x401166, 0x401179}}}, 149 }, 150 }, 151 { 152 "testdata/line-clang-dwarf5.elf", 153 subprograms{ 154 {"main", [][2]uint64{{0x401130, 0x401144}}}, 155 {"f1", [][2]uint64{{0x401150, 0x40117e}}}, 156 {"f2", [][2]uint64{{0x401180, 0x401197}}}, 157 }, 158 }, 159 } 160 161 for _, test := range tests { 162 d := elfData(t, test.filename) 163 subprograms := test.subprograms 164 165 r := d.Reader() 166 i := 0 167 for entry, err := r.Next(); entry != nil && err == nil; entry, err = r.Next() { 168 if entry.Tag != TagSubprogram { 169 continue 170 } 171 172 if i > len(subprograms) { 173 t.Fatalf("%s: too many subprograms (expected at most %d)", test.filename, i) 174 } 175 176 if got := entry.Val(AttrName).(string); got != subprograms[i].name { 177 t.Errorf("%s: subprogram %d name is %s, expected %s", test.filename, i, got, subprograms[i].name) 178 } 179 ranges, err := d.Ranges(entry) 180 if err != nil { 181 t.Errorf("%s: subprogram %d: %v", test.filename, i, err) 182 continue 183 } 184 if !reflect.DeepEqual(ranges, subprograms[i].ranges) { 185 t.Errorf("%s: subprogram %d ranges are %x, expected %x", test.filename, i, ranges, subprograms[i].ranges) 186 } 187 i++ 188 } 189 190 if i < len(subprograms) { 191 t.Errorf("%s: saw only %d subprograms, expected %d", test.filename, i, len(subprograms)) 192 } 193 } 194 } 195 196 func Test64Bit(t *testing.T) { 197 // I don't know how to generate a 64-bit DWARF debug 198 // compilation unit except by using XCOFF, so this is 199 // hand-written. 200 tests := []struct { 201 name string 202 info []byte 203 addrSize int 204 byteOrder binary.ByteOrder 205 }{ 206 { 207 "32-bit little", 208 []byte{0x30, 0, 0, 0, // comp unit length 209 4, 0, // DWARF version 4 210 0, 0, 0, 0, // abbrev offset 211 8, // address size 212 0, 213 0, 0, 0, 0, 0, 0, 0, 0, 214 0, 0, 0, 0, 0, 0, 0, 0, 215 0, 0, 0, 0, 0, 0, 0, 0, 216 0, 0, 0, 0, 0, 0, 0, 0, 217 0, 0, 0, 0, 0, 0, 0, 0, 218 }, 219 8, binary.LittleEndian, 220 }, 221 { 222 "64-bit little", 223 []byte{0xff, 0xff, 0xff, 0xff, // 64-bit DWARF 224 0x30, 0, 0, 0, 0, 0, 0, 0, // comp unit length 225 4, 0, // DWARF version 4 226 0, 0, 0, 0, 0, 0, 0, 0, // abbrev offset 227 8, // address size 228 0, 0, 0, 0, 0, 229 0, 0, 0, 0, 0, 0, 0, 0, 230 0, 0, 0, 0, 0, 0, 0, 0, 231 0, 0, 0, 0, 0, 0, 0, 0, 232 0, 0, 0, 0, 0, 0, 0, 0, 233 }, 234 8, binary.LittleEndian, 235 }, 236 { 237 "64-bit big", 238 []byte{0xff, 0xff, 0xff, 0xff, // 64-bit DWARF 239 0, 0, 0, 0, 0, 0, 0, 0x30, // comp unit length 240 0, 4, // DWARF version 4 241 0, 0, 0, 0, 0, 0, 0, 0, // abbrev offset 242 8, // address size 243 0, 0, 0, 0, 0, 244 0, 0, 0, 0, 0, 0, 0, 0, 245 0, 0, 0, 0, 0, 0, 0, 0, 246 0, 0, 0, 0, 0, 0, 0, 0, 247 0, 0, 0, 0, 0, 0, 0, 0, 248 }, 249 8, binary.BigEndian, 250 }, 251 } 252 253 for _, test := range tests { 254 data, err := New(nil, nil, nil, test.info, nil, nil, nil, nil) 255 if err != nil { 256 t.Errorf("%s: %v", test.name, err) 257 } 258 259 r := data.Reader() 260 if r.AddressSize() != test.addrSize { 261 t.Errorf("%s: got address size %d, want %d", test.name, r.AddressSize(), test.addrSize) 262 } 263 if r.ByteOrder() != test.byteOrder { 264 t.Errorf("%s: got byte order %s, want %s", test.name, r.ByteOrder(), test.byteOrder) 265 } 266 } 267 } 268 269 func TestUnitIteration(t *testing.T) { 270 // Iterate over all ELF test files we have and ensure that 271 // we get the same set of compilation units skipping (method 0) 272 // and not skipping (method 1) CU children. 273 files, err := filepath.Glob(filepath.Join("testdata", "*.elf")) 274 if err != nil { 275 t.Fatal(err) 276 } 277 for _, file := range files { 278 t.Run(file, func(t *testing.T) { 279 d := elfData(t, file) 280 var units [2][]interface{} 281 for method := range units { 282 for r := d.Reader(); ; { 283 ent, err := r.Next() 284 if err != nil { 285 t.Fatal(err) 286 } 287 if ent == nil { 288 break 289 } 290 if ent.Tag == TagCompileUnit { 291 units[method] = append(units[method], ent.Val(AttrName)) 292 } 293 if method == 0 { 294 if ent.Tag != TagCompileUnit { 295 t.Fatalf("found unexpected tag %v on top level", ent.Tag) 296 } 297 r.SkipChildren() 298 } 299 } 300 } 301 t.Logf("skipping CUs: %v", units[0]) 302 t.Logf("not-skipping CUs: %v", units[1]) 303 if !reflect.DeepEqual(units[0], units[1]) { 304 t.Fatal("set of CUs differ") 305 } 306 }) 307 } 308 }