github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/pkg/dwarf/line/state_machine_test.go (about)

     1  package line
     2  
     3  import (
     4  	"bytes"
     5  	"compress/gzip"
     6  	"debug/dwarf"
     7  	"debug/macho"
     8  	"encoding/binary"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"os"
    13  	"testing"
    14  
    15  	pdwarf "github.com/go-delve/delve/pkg/dwarf"
    16  	"github.com/go-delve/delve/pkg/dwarf/leb128"
    17  )
    18  
    19  func slurpGzip(path string) ([]byte, error) {
    20  	fh, err := os.Open(path)
    21  	if err != nil {
    22  		return nil, err
    23  	}
    24  	defer fh.Close()
    25  	gzin, err := gzip.NewReader(fh)
    26  	if err != nil {
    27  		return nil, err
    28  	}
    29  	defer gzin.Close()
    30  	return ioutil.ReadAll(gzin)
    31  }
    32  
    33  func TestGrafana(t *testing.T) {
    34  	// Compares a full execution of our state machine on the debug_line section
    35  	// of grafana to the output generated using debug/dwarf.LineReader on the
    36  	// same section.
    37  
    38  	debugBytes, err := slurpGzip("_testdata/debug.grafana.debug.gz")
    39  	if err != nil {
    40  		t.Fatal(err)
    41  	}
    42  	exe, err := macho.NewFile(bytes.NewReader(debugBytes))
    43  	if err != nil {
    44  		t.Fatal(err)
    45  	}
    46  
    47  	sec := exe.Section("__debug_line")
    48  	debugLineBytes, err := sec.Data()
    49  	if err != nil {
    50  		t.Fatal(err)
    51  	}
    52  
    53  	data, err := exe.DWARF()
    54  	if err != nil {
    55  		t.Fatal(err)
    56  	}
    57  
    58  	debugLineBuffer := bytes.NewBuffer(debugLineBytes)
    59  	rdr := data.Reader()
    60  	for {
    61  		e, err := rdr.Next()
    62  		if err != nil {
    63  			t.Fatal(err)
    64  		}
    65  		if e == nil {
    66  			break
    67  		}
    68  		rdr.SkipChildren()
    69  		if e.Tag != dwarf.TagCompileUnit {
    70  			continue
    71  		}
    72  		cuname, _ := e.Val(dwarf.AttrName).(string)
    73  
    74  		lineInfo := Parse(e.Val(dwarf.AttrCompDir).(string), debugLineBuffer, nil, t.Logf, 0, false, 8)
    75  		lineInfo.endSeqIsValid = true
    76  		sm := newStateMachine(lineInfo, lineInfo.Instructions, 8)
    77  
    78  		lnrdr, err := data.LineReader(e)
    79  		if err != nil {
    80  			t.Fatal(err)
    81  		}
    82  
    83  		checkCompileUnit(t, cuname, lnrdr, sm)
    84  	}
    85  }
    86  
    87  func checkCompileUnit(t *testing.T, cuname string, lnrdr *dwarf.LineReader, sm *StateMachine) {
    88  	var lne dwarf.LineEntry
    89  	for {
    90  		if err := sm.next(); err != nil {
    91  			if err != io.EOF {
    92  				t.Fatalf("state machine next error: %v", err)
    93  			}
    94  			break
    95  		}
    96  		if !sm.valid {
    97  			continue
    98  		}
    99  
   100  		err := lnrdr.Next(&lne)
   101  		if err == io.EOF {
   102  			t.Fatalf("line reader ended before our state machine for compile unit %s", cuname)
   103  		}
   104  		if err != nil {
   105  			t.Fatal(err)
   106  		}
   107  
   108  		tgt := fmt.Sprintf("%#x %s:%d isstmt:%v prologue_end:%v epilogue_begin:%v", lne.Address, lne.File.Name, lne.Line, lne.IsStmt, lne.PrologueEnd, lne.EpilogueBegin)
   109  
   110  		out := fmt.Sprintf("%#x %s:%d isstmt:%v prologue_end:%v epilogue_begin:%v", sm.address, sm.file, sm.line, sm.isStmt, sm.prologueEnd, sm.epilogueBegin)
   111  		if out != tgt {
   112  			t.Errorf("mismatch:\n")
   113  			t.Errorf("got:\t%s\n", out)
   114  			t.Errorf("expected:\t%s\n", tgt)
   115  			t.Fatal("previous error")
   116  		}
   117  	}
   118  
   119  	err := lnrdr.Next(&lne)
   120  	if err != io.EOF {
   121  		t.Fatalf("state machine ended before the line reader for compile unit %s", cuname)
   122  	}
   123  }
   124  
   125  func TestMultipleSequences(t *testing.T) {
   126  	// Check that our state machine (specifically PCToLine and AllPCsBetween)
   127  	// are correct when dealing with units containing more than one sequence.
   128  
   129  	const thefile = "thefile.go"
   130  
   131  	instr := bytes.NewBuffer(nil)
   132  	ptrSize := ptrSizeByRuntimeArch()
   133  
   134  	write_DW_LNE_set_address := func(addr uint64) {
   135  		instr.WriteByte(0)
   136  		leb128.EncodeUnsigned(instr, 9) // 1 + ptr_size
   137  		instr.WriteByte(DW_LINE_set_address)
   138  		pdwarf.WriteUint(instr, binary.LittleEndian, ptrSize, addr)
   139  	}
   140  
   141  	write_DW_LNS_copy := func() {
   142  		instr.WriteByte(DW_LNS_copy)
   143  	}
   144  
   145  	write_DW_LNS_advance_pc := func(off uint64) {
   146  		instr.WriteByte(DW_LNS_advance_pc)
   147  		leb128.EncodeUnsigned(instr, off)
   148  	}
   149  
   150  	write_DW_LNS_advance_line := func(off int64) {
   151  		instr.WriteByte(DW_LNS_advance_line)
   152  		leb128.EncodeSigned(instr, off)
   153  	}
   154  
   155  	write_DW_LNE_end_sequence := func() {
   156  		instr.WriteByte(0)
   157  		leb128.EncodeUnsigned(instr, 1)
   158  		instr.WriteByte(DW_LINE_end_sequence)
   159  	}
   160  
   161  	write_DW_LNE_set_address(0x400000)
   162  	write_DW_LNS_copy() // thefile.go:1 0x400000
   163  	write_DW_LNS_advance_pc(0x2)
   164  	write_DW_LNS_advance_line(1)
   165  	write_DW_LNS_copy() // thefile.go:2 0x400002
   166  	write_DW_LNS_advance_pc(0x2)
   167  	write_DW_LNS_advance_line(1)
   168  	write_DW_LNS_copy() // thefile.go:3 0x400004
   169  	write_DW_LNS_advance_pc(0x2)
   170  	write_DW_LNE_end_sequence() // thefile.go:3 ends the byte before 0x400006
   171  
   172  	write_DW_LNE_set_address(0x600000)
   173  	write_DW_LNS_advance_line(10)
   174  	write_DW_LNS_copy() // thefile.go:11 0x600000
   175  	write_DW_LNS_advance_pc(0x2)
   176  	write_DW_LNS_advance_line(1)
   177  	write_DW_LNS_copy() // thefile.go:12 0x600002
   178  	write_DW_LNS_advance_pc(0x2)
   179  	write_DW_LNS_advance_line(1)
   180  	write_DW_LNS_copy() // thefile.go:13 0x600004
   181  	write_DW_LNS_advance_pc(0x2)
   182  	write_DW_LNE_end_sequence() // thefile.go:13 ends the byte before 0x600006
   183  
   184  	write_DW_LNE_set_address(0x500000)
   185  	write_DW_LNS_advance_line(20)
   186  	write_DW_LNS_copy() // thefile.go:21 0x500000
   187  	write_DW_LNS_advance_pc(0x2)
   188  	write_DW_LNS_advance_line(1)
   189  	write_DW_LNS_copy() // thefile.go:22 0x500002
   190  	write_DW_LNS_advance_pc(0x2)
   191  	write_DW_LNS_advance_line(1)
   192  	write_DW_LNS_copy() // thefile.go:23 0x500004
   193  	write_DW_LNS_advance_pc(0x2)
   194  	write_DW_LNE_end_sequence() // thefile.go:23 ends the byte before 0x500006
   195  
   196  	lines := &DebugLineInfo{
   197  		Prologue: &DebugLinePrologue{
   198  			UnitLength:     1,
   199  			Version:        2,
   200  			MinInstrLength: 1,
   201  			InitialIsStmt:  1,
   202  			LineBase:       -3,
   203  			LineRange:      12,
   204  			OpcodeBase:     13,
   205  			StdOpLengths:   []uint8{0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1},
   206  		},
   207  		IncludeDirs:  []string{},
   208  		FileNames:    []*FileEntry{&FileEntry{Path: thefile}},
   209  		Instructions: instr.Bytes(),
   210  		ptrSize:      ptrSize,
   211  	}
   212  
   213  	// Test that PCToLine is correct for all three sequences
   214  
   215  	for _, testCase := range []struct {
   216  		pc   uint64
   217  		line int
   218  	}{
   219  		{0x400000, 1},
   220  		{0x400002, 2},
   221  		{0x400004, 3},
   222  
   223  		{0x500000, 21},
   224  		{0x500002, 22},
   225  		{0x500004, 23},
   226  
   227  		{0x600000, 11},
   228  		{0x600002, 12},
   229  		{0x600004, 13},
   230  	} {
   231  		sm := newStateMachine(lines, lines.Instructions, lines.ptrSize)
   232  		file, curline, ok := sm.PCToLine(testCase.pc)
   233  		if !ok {
   234  			t.Fatalf("Could not find %#x", testCase.pc)
   235  		}
   236  		if file != thefile {
   237  			t.Fatalf("Wrong file returned for %#x %q", testCase.pc, file)
   238  		}
   239  		if curline != testCase.line {
   240  			t.Errorf("Wrong line returned for %#x: got %d expected %d", testCase.pc, curline, testCase.line)
   241  		}
   242  
   243  	}
   244  
   245  	// Test that AllPCsBetween is correct for all three sequences
   246  	for _, testCase := range []struct {
   247  		start, end uint64
   248  		tgt        []uint64
   249  	}{
   250  		{0x400000, 0x400005, []uint64{0x400000, 0x400002, 0x400004}},
   251  		{0x500000, 0x500005, []uint64{0x500000, 0x500002, 0x500004}},
   252  		{0x600000, 0x600005, []uint64{0x600000, 0x600002, 0x600004}},
   253  	} {
   254  		out, err := lines.AllPCsBetween(testCase.start, testCase.end, "", -1)
   255  		if err != nil {
   256  			t.Fatalf("AllPCsBetween(%#x, %#x): %v", testCase.start, testCase.end, err)
   257  		}
   258  
   259  		if len(out) != len(testCase.tgt) {
   260  			t.Errorf("AllPCsBetween(%#x, %#x): expected: %#x got: %#x", testCase.start, testCase.end, testCase.tgt, out)
   261  		}
   262  	}
   263  }