github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/src/debug/dwarf/line_test.go (about)

     1  // Copyright 2015 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  	"io"
    10  	"testing"
    11  )
    12  
    13  var (
    14  	file1C = &LineFile{Name: "/home/austin/go.dev/src/debug/dwarf/testdata/line1.c"}
    15  	file1H = &LineFile{Name: "/home/austin/go.dev/src/debug/dwarf/testdata/line1.h"}
    16  	file2C = &LineFile{Name: "/home/austin/go.dev/src/debug/dwarf/testdata/line2.c"}
    17  )
    18  
    19  func TestLineELFGCC(t *testing.T) {
    20  	// Generated by:
    21  	//   # gcc --version | head -n1
    22  	//   gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2
    23  	//   # gcc -g -o line-gcc.elf line*.c
    24  
    25  	// Line table based on readelf --debug-dump=rawline,decodedline
    26  	want := []LineEntry{
    27  		{Address: 0x40059d, File: file1H, Line: 2, IsStmt: true},
    28  		{Address: 0x4005a5, File: file1H, Line: 2, IsStmt: true},
    29  		{Address: 0x4005b4, File: file1H, Line: 5, IsStmt: true},
    30  		{Address: 0x4005bd, File: file1H, Line: 6, IsStmt: true, Discriminator: 2},
    31  		{Address: 0x4005c7, File: file1H, Line: 5, IsStmt: true, Discriminator: 2},
    32  		{Address: 0x4005cb, File: file1H, Line: 5, IsStmt: false, Discriminator: 1},
    33  		{Address: 0x4005d1, File: file1H, Line: 7, IsStmt: true},
    34  		{Address: 0x4005e7, File: file1C, Line: 6, IsStmt: true},
    35  		{Address: 0x4005eb, File: file1C, Line: 7, IsStmt: true},
    36  		{Address: 0x4005f5, File: file1C, Line: 8, IsStmt: true},
    37  		{Address: 0x4005ff, File: file1C, Line: 9, IsStmt: true},
    38  		{Address: 0x400601, EndSequence: true},
    39  
    40  		{Address: 0x400601, File: file2C, Line: 4, IsStmt: true},
    41  		{Address: 0x400605, File: file2C, Line: 5, IsStmt: true},
    42  		{Address: 0x40060f, File: file2C, Line: 6, IsStmt: true},
    43  		{Address: 0x400611, EndSequence: true},
    44  	}
    45  
    46  	testLineTable(t, want, elfData(t, "testdata/line-gcc.elf"))
    47  }
    48  
    49  func TestLineELFClang(t *testing.T) {
    50  	// Generated by:
    51  	//   # clang --version | head -n1
    52  	//   Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4)
    53  	//   # clang -g -o line-clang.elf line*.
    54  
    55  	want := []LineEntry{
    56  		{Address: 0x400530, File: file1C, Line: 6, IsStmt: true},
    57  		{Address: 0x400534, File: file1C, Line: 7, IsStmt: true, PrologueEnd: true},
    58  		{Address: 0x400539, File: file1C, Line: 8, IsStmt: true},
    59  		{Address: 0x400545, File: file1C, Line: 9, IsStmt: true},
    60  		{Address: 0x400550, File: file1H, Line: 2, IsStmt: true},
    61  		{Address: 0x400554, File: file1H, Line: 5, IsStmt: true, PrologueEnd: true},
    62  		{Address: 0x400568, File: file1H, Line: 6, IsStmt: true},
    63  		{Address: 0x400571, File: file1H, Line: 5, IsStmt: true},
    64  		{Address: 0x400581, File: file1H, Line: 7, IsStmt: true},
    65  		{Address: 0x400583, EndSequence: true},
    66  
    67  		{Address: 0x400590, File: file2C, Line: 4, IsStmt: true},
    68  		{Address: 0x4005a0, File: file2C, Line: 5, IsStmt: true, PrologueEnd: true},
    69  		{Address: 0x4005a7, File: file2C, Line: 6, IsStmt: true},
    70  		{Address: 0x4005b0, EndSequence: true},
    71  	}
    72  
    73  	testLineTable(t, want, elfData(t, "testdata/line-clang.elf"))
    74  }
    75  
    76  func TestLineSeek(t *testing.T) {
    77  	d := elfData(t, "testdata/line-gcc.elf")
    78  
    79  	// Get the line table for the first CU.
    80  	cu, err := d.Reader().Next()
    81  	if err != nil {
    82  		t.Fatal("d.Reader().Next:", err)
    83  	}
    84  	lr, err := d.LineReader(cu)
    85  	if err != nil {
    86  		t.Fatal("d.LineReader:", err)
    87  	}
    88  
    89  	// Read entries forward.
    90  	var line LineEntry
    91  	var posTable []LineReaderPos
    92  	var table []LineEntry
    93  	for {
    94  		posTable = append(posTable, lr.Tell())
    95  
    96  		err := lr.Next(&line)
    97  		if err != nil {
    98  			if err == io.EOF {
    99  				break
   100  			}
   101  			t.Fatal("lr.Next:", err)
   102  		}
   103  		table = append(table, line)
   104  	}
   105  
   106  	// Test that Reset returns to the first line.
   107  	lr.Reset()
   108  	if err := lr.Next(&line); err != nil {
   109  		t.Fatal("lr.Next after Reset failed:", err)
   110  	} else if line != table[0] {
   111  		t.Fatal("lr.Next after Reset returned", line, "instead of", table[0])
   112  	}
   113  
   114  	// Check that entries match when seeking backward.
   115  	for i := len(posTable) - 1; i >= 0; i-- {
   116  		lr.Seek(posTable[i])
   117  		err := lr.Next(&line)
   118  		if i == len(posTable)-1 {
   119  			if err != io.EOF {
   120  				t.Fatal("expected io.EOF after seek to end, got", err)
   121  			}
   122  		} else if err != nil {
   123  			t.Fatal("lr.Next after seek to", posTable[i], "failed:", err)
   124  		} else if line != table[i] {
   125  			t.Fatal("lr.Next after seek to", posTable[i], "returned", line, "instead of", table[i])
   126  		}
   127  	}
   128  
   129  	// Check that seeking to a PC returns the right line.
   130  	if err := lr.SeekPC(table[0].Address-1, &line); err != ErrUnknownPC {
   131  		t.Fatalf("lr.SeekPC to %#x returned %v instead of ErrUnknownPC", table[0].Address-1, err)
   132  	}
   133  	for i, testLine := range table {
   134  		if testLine.EndSequence {
   135  			if err := lr.SeekPC(testLine.Address, &line); err != ErrUnknownPC {
   136  				t.Fatalf("lr.SeekPC to %#x returned %v instead of ErrUnknownPC", testLine.Address, err)
   137  			}
   138  			continue
   139  		}
   140  
   141  		nextPC := table[i+1].Address
   142  		for pc := testLine.Address; pc < nextPC; pc++ {
   143  			if err := lr.SeekPC(pc, &line); err != nil {
   144  				t.Fatalf("lr.SeekPC to %#x failed: %v", pc, err)
   145  			} else if line != testLine {
   146  				t.Fatalf("lr.SeekPC to %#x returned %v instead of %v", pc, line, testLine)
   147  			}
   148  		}
   149  	}
   150  }
   151  
   152  func testLineTable(t *testing.T, want []LineEntry, d *Data) {
   153  	// Get line table from d.
   154  	var got []LineEntry
   155  	dr := d.Reader()
   156  	for {
   157  		ent, err := dr.Next()
   158  		if err != nil {
   159  			t.Fatal("dr.Next:", err)
   160  		} else if ent == nil {
   161  			break
   162  		}
   163  
   164  		if ent.Tag != TagCompileUnit {
   165  			dr.SkipChildren()
   166  			continue
   167  		}
   168  
   169  		// Decode CU's line table.
   170  		lr, err := d.LineReader(ent)
   171  		if err != nil {
   172  			t.Fatal("d.LineReader:", err)
   173  		} else if lr == nil {
   174  			continue
   175  		}
   176  
   177  		for {
   178  			var line LineEntry
   179  			err := lr.Next(&line)
   180  			if err != nil {
   181  				if err == io.EOF {
   182  					break
   183  				}
   184  				t.Fatal("lr.Next:", err)
   185  			}
   186  			got = append(got, line)
   187  		}
   188  	}
   189  
   190  	// Compare line tables.
   191  	if !compareLines(got, want) {
   192  		t.Log("Line tables do not match. Got:")
   193  		dumpLines(t, got)
   194  		t.Log("Want:")
   195  		dumpLines(t, want)
   196  		t.FailNow()
   197  	}
   198  }
   199  
   200  func compareLines(a, b []LineEntry) bool {
   201  	if len(a) != len(b) {
   202  		return false
   203  	}
   204  
   205  	for i := range a {
   206  		al, bl := a[i], b[i]
   207  		// If both are EndSequence, then the only other valid
   208  		// field is Address. Otherwise, test equality of all
   209  		// fields.
   210  		if al.EndSequence && bl.EndSequence && al.Address == bl.Address {
   211  			continue
   212  		}
   213  		if al.File.Name != bl.File.Name {
   214  			return false
   215  		}
   216  		al.File = nil
   217  		bl.File = nil
   218  		if al != bl {
   219  			return false
   220  		}
   221  	}
   222  	return true
   223  }
   224  
   225  func dumpLines(t *testing.T, lines []LineEntry) {
   226  	for _, l := range lines {
   227  		t.Logf("  %+v File:%+v", l, l.File)
   228  	}
   229  }