
Support for position independent executables (PIE) on the native linux backend, the gdbserver backend on linux and the core backend. Also implemented in the windows native backend, but it can't be tested because go doesn't support PIE on windows yet.
129 lines
2.7 KiB
Go
129 lines
2.7 KiB
Go
package line
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"debug/dwarf"
|
|
"debug/macho"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"runtime"
|
|
"testing"
|
|
)
|
|
|
|
func slurpGzip(path string) ([]byte, error) {
|
|
fh, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer fh.Close()
|
|
gzin, err := gzip.NewReader(fh)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer gzin.Close()
|
|
return ioutil.ReadAll(gzin)
|
|
}
|
|
|
|
const (
|
|
newCompileUnit = "NEW COMPILE UNIT"
|
|
debugLineEnd = "END"
|
|
)
|
|
|
|
func TestGrafana(t *testing.T) {
|
|
// Compares a full execution of our state machine on the debug_line section
|
|
// of grafana to the output generated using debug/dwarf.LineReader on the
|
|
// same section.
|
|
|
|
if runtime.GOOS == "windows" {
|
|
t.Skip("filepath.Join ruins this test on windows")
|
|
}
|
|
|
|
debugBytes, err := slurpGzip("_testdata/debug.grafana.debug.gz")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
exe, err := macho.NewFile(bytes.NewReader(debugBytes))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
sec := exe.Section("__debug_line")
|
|
debugLineBytes, err := sec.Data()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
data, err := exe.DWARF()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
debugLineBuffer := bytes.NewBuffer(debugLineBytes)
|
|
rdr := data.Reader()
|
|
for {
|
|
e, err := rdr.Next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if e == nil {
|
|
break
|
|
}
|
|
rdr.SkipChildren()
|
|
if e.Tag != dwarf.TagCompileUnit {
|
|
continue
|
|
}
|
|
cuname, _ := e.Val(dwarf.AttrName).(string)
|
|
|
|
lineInfo := Parse(e.Val(dwarf.AttrCompDir).(string), debugLineBuffer, t.Logf, 0)
|
|
sm := newStateMachine(lineInfo, lineInfo.Instructions)
|
|
|
|
lnrdr, err := data.LineReader(e)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
checkCompileUnit(t, cuname, lnrdr, sm)
|
|
}
|
|
}
|
|
|
|
func checkCompileUnit(t *testing.T, cuname string, lnrdr *dwarf.LineReader, sm *StateMachine) {
|
|
var lne dwarf.LineEntry
|
|
for {
|
|
if err := sm.next(); err != nil {
|
|
if err != io.EOF {
|
|
t.Fatalf("state machine next error: %v", err)
|
|
}
|
|
break
|
|
}
|
|
if !sm.valid {
|
|
continue
|
|
}
|
|
|
|
err := lnrdr.Next(&lne)
|
|
if err == io.EOF {
|
|
t.Fatalf("line reader ended before our state machine for compile unit %s", cuname)
|
|
}
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
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)
|
|
|
|
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)
|
|
if out != tgt {
|
|
t.Errorf("mismatch:\n")
|
|
t.Errorf("got:\t%s\n", out)
|
|
t.Errorf("expected:\t%s\n", tgt)
|
|
t.Fatal("previous error")
|
|
}
|
|
}
|
|
|
|
err := lnrdr.Next(&lne)
|
|
if err != io.EOF {
|
|
t.Fatalf("state machine ended before the line reader for compile unit %s", cuname)
|
|
}
|
|
}
|