delve/dwarf/line/line_parser_test.go
Alessandro Arzilli 8724b3fce7 Go 1.8 compatibility (part 2) (#667)
* dwarf/line: bugfix: not all values of the state machine can be used

According to DWARF Version 3 Section 6.2 "Line Number Information" not
all the values transversed by the line numbers state machine are valid
instructions, only the ones after a "special opcode", after the
standard opcode DW_LNS_copy and the extended opcode
DW_LINE_end_sequence.

DWARF3 describes this by specifying that only the opcodes listed above
"append a row to the matrix".

Additionally the implementation of DW_LNS_const_add_pc was wrong.

Fixes #664

* dwarf/line: fixed test failing with go1.8

* service/test: fix prologue detection tests

The conditions about which function prologue is emitted by the compiler
changed in go1.8, changed the test program so that callme2 will still
have a prologue under go1.8.

* service/test: fix step test

compilation units are linked in a different order under go1.8 so the
code of 'fmt' is no longer located after 'main' in the executable,
changed the tests so that they don't rely on this assumption anymore.

* proc: change runtime.Breakpoint support for go1.8

Before 1.8 it was sufficient to step twice to exit a
runtime.Breakpoint(), but go 1.8 added frame pointer tracking to small
functions making runtime.Breakpoint longer.
This changes runtime.Breakpoint handling in Continue to single step as
many times as are needed to exit runtime.Breakpoint.

* proc/test: fix TestIssue561 for go1.8
2017-02-07 13:07:18 -08:00

130 lines
2.8 KiB
Go

package line
import (
"debug/elf"
"debug/macho"
"debug/pe"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"github.com/davecheney/profile"
)
func grabDebugLineSection(p string, t *testing.T) []byte {
f, err := os.Open(p)
if err != nil {
t.Fatal(err)
}
defer f.Close()
ef, err := elf.NewFile(f)
if err == nil {
data, _ := ef.Section(".debug_line").Data()
return data
}
pf, err := pe.NewFile(f)
if err == nil {
data, _ := pf.Section(".debug_line").Data()
return data
}
mf, _ := macho.NewFile(f)
data, _ := mf.Section("__debug_line").Data()
return data
}
const (
lineBaseGo14 int8 = -1
lineBaseGo18 int8 = -4
lineRangeGo14 uint8 = 4
lineRangeGo18 uint8 = 10
)
func TestDebugLinePrologueParser(t *testing.T) {
// Test against known good values, from readelf --debug-dump=rawline _fixtures/testnextprog
p, err := filepath.Abs("../../_fixtures/testnextprog")
if err != nil {
t.Fatal(err)
}
err = exec.Command("go", "build", "-gcflags=-N -l", "-o", p, p+".go").Run()
if err != nil {
t.Fatal("Could not compile test file", p, err)
}
defer os.Remove(p)
data := grabDebugLineSection(p, t)
debugLines := Parse(data)
dbl := debugLines[0]
prologue := dbl.Prologue
if prologue.Version != uint16(2) {
t.Fatal("Version not parsed correctly", prologue.Version)
}
if prologue.MinInstrLength != uint8(1) {
t.Fatal("Minimun Instruction Length not parsed correctly", prologue.MinInstrLength)
}
if prologue.InitialIsStmt != uint8(1) {
t.Fatal("Initial value of 'is_stmt' not parsed correctly", prologue.InitialIsStmt)
}
if prologue.LineBase != lineBaseGo14 && prologue.LineBase != lineBaseGo18 {
// go < 1.8 uses -1
// go >= 1.8 uses -4
t.Fatal("Line base not parsed correctly", prologue.LineBase)
}
if prologue.LineRange != lineRangeGo14 && prologue.LineRange != lineRangeGo18 {
// go < 1.8 uses 4
// go >= 1.8 uses 10
t.Fatal("Line Range not parsed correctly", prologue.LineRange)
}
if prologue.OpcodeBase != uint8(10) {
t.Fatal("Opcode Base not parsed correctly", prologue.OpcodeBase)
}
lengths := []uint8{0, 1, 1, 1, 1, 0, 0, 0, 1}
for i, l := range prologue.StdOpLengths {
if l != lengths[i] {
t.Fatal("Length not parsed correctly", l)
}
}
if len(dbl.IncludeDirs) != 0 {
t.Fatal("Include dirs not parsed correctly")
}
ok := false
for _, n := range dbl.FileNames {
if strings.Contains(n.Name, "/delve/_fixtures/testnextprog.go") {
ok = true
break
}
}
if !ok {
t.Fatal("File names table not parsed correctly")
}
}
func BenchmarkLineParser(b *testing.B) {
defer profile.Start(profile.MemProfile).Stop()
p, err := filepath.Abs("../../_fixtures/testnextprog")
if err != nil {
b.Fatal(err)
}
data := grabDebugLineSection(p, nil)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = Parse(data)
}
}