...

Source file src/debug/dwarf/entry_test.go

Documentation: debug/dwarf

		 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  }
	 309  

View as plain text