dwarf,proc: various fixes to support DWARFv5 (#3893)

Miscellaneous fixes to our DWARFv5 implementation, several contributed by @thanm.
This commit is contained in:
Alessandro Arzilli
2025-03-03 17:42:44 +01:00
committed by GitHub
parent 1df310a2e3
commit e6e7aeb667
7 changed files with 34 additions and 12 deletions

View File

@ -116,7 +116,11 @@ func Parse(compdir string, buf *bytes.Buffer, debugLineStr []byte, logfn func(st
// - dbl.Prologue.UnitLength is the length of the entire unit, not including the 4 bytes to represent that length.
// - dbl.Prologue.Length is the length of the prologue not including unit length, version or prologue length itself.
// - So you have UnitLength - PrologueLength - (version_length_bytes(2) + prologue_length_bytes(4)).
dbl.Instructions = buf.Next(int(dbl.Prologue.UnitLength - dbl.Prologue.Length - 6))
verDelta := uint32(6)
if dbl.Prologue.Version >= 5 {
verDelta = uint32(8)
}
dbl.Instructions = buf.Next(int(dbl.Prologue.UnitLength - dbl.Prologue.Length - verDelta))
return dbl
}

View File

@ -63,6 +63,7 @@ const (
lineRangeGo18 uint8 = 10
versionGo14 uint16 = 2
versionGo111 uint16 = 3
versionGo125 uint16 = 5
opcodeBaseGo14 uint8 = 10
opcodeBaseGo111 uint8 = 11
)
@ -79,7 +80,7 @@ func testDebugLinePrologueParser(p string, t *testing.T) {
for _, dbl := range debugLines {
prologue := dbl.Prologue
if prologue.Version != versionGo14 && prologue.Version != versionGo111 {
if prologue.Version != versionGo14 && prologue.Version != versionGo111 && prologue.Version != versionGo125 {
t.Fatal("Version not parsed correctly", prologue.Version)
}

View File

@ -106,7 +106,7 @@ func newStateMachine(dbl *DebugLineInfo, instructions []byte, ptrSize int) *Stat
}
var file string
if len(dbl.FileNames) > 0 {
file = dbl.FileNames[0].Path
file = dbl.defaultFile()
}
dbl.endSeqIsValid = true
sm := &StateMachine{
@ -123,6 +123,22 @@ func newStateMachine(dbl *DebugLineInfo, instructions []byte, ptrSize int) *Stat
return sm
}
func (dbl *DebugLineInfo) defaultFile() string {
// The default file is always file 1, however DWARFv5 starts numbering the
// entries of the files table from 0 but prior versions started with 1
// which means that for DWARFv4 and earlier the default file is the first
// entry of the table where in DWARFv5 the default is the second entry.
// See DWARFv4 and DWARFv5 section 6.2.4 at the end.
if dbl.Prologue.Version < 5 {
return dbl.FileNames[0].Path
}
if len(dbl.FileNames) == 1 {
// DWARFv5 doesn't say what should happen in this case.
return dbl.FileNames[0].Path
}
return dbl.FileNames[1].Path
}
// AllPCsForFileLines Adds all PCs for a given file and set (domain of map) of lines
// to the map value corresponding to each line.
func (lineInfo *DebugLineInfo) AllPCsForFileLines(f string, m map[int][]uint64) {
@ -390,7 +406,7 @@ func (sm *StateMachine) next() error {
}
if sm.endSeq {
sm.endSeq = false
sm.file = sm.dbl.FileNames[0].Path
sm.file = sm.dbl.defaultFile()
sm.line = 1
sm.column = 0
sm.isa = 0

View File

@ -131,7 +131,7 @@ func ReadUnitVersions(data []byte) map[dwarf.Offset]uint8 {
switch unitType {
case _DW_UT_compile, _DW_UT_partial:
headerSize = 5 + secoffsz
headerSize = 4 + secoffsz
case _DW_UT_skeleton, _DW_UT_split_compile:
headerSize = 4 + secoffsz + 8

View File

@ -42,7 +42,7 @@ import (
const (
dwarfGoLanguage = 22 // DW_LANG_Go (from DWARF v5, section 7.12, page 231)
dwarfAttrAddrBase = 0x74 // debug/dwarf.AttrAddrBase in Go 1.14, defined here for compatibility with Go < 1.14
dwarfAttrAddrBase = 0x73 // debug/dwarf.AttrAddrBase in Go 1.14, defined here for compatibility with Go < 1.14
dwarfTreeCacheSize = 512 // size of the dwarfTree cache of each image
)

View File

@ -136,7 +136,7 @@ func TestDwarfVersion(t *testing.T) {
const fakeEntryPoint = 1
assertNoError(bi.LoadBinaryInfo(fixture.Path, fakeEntryPoint, nil), t, "LoadBinaryInfo")
for _, cu := range bi.Images[0].compileUnits {
if cu.Version != 4 {
if cu.Version != 4 && cu.Version != 5 {
t.Errorf("compile unit %q at %#x has bad version %d", cu.name, cu.entry.Offset, cu.Version)
}
}

View File

@ -720,13 +720,13 @@ func TestStacktrace(t *testing.T) {
locations, err := proc.ThreadStacktrace(p, p.CurrentThread(), 40)
assertNoError(err, t, "Stacktrace()")
if len(locations) != len(stacks[i])+2 {
t.Fatalf("Wrong stack trace size %d %d\n", len(locations), len(stacks[i])+2)
}
t.Logf("Stacktrace %d:\n", i)
for i := range locations {
t.Logf("\t%s:%d\n", locations[i].Call.File, locations[i].Call.Line)
t.Logf("\t%s (%#x) %s:%d\n", locations[i].Call.Fn.Name, locations[i].Call.PC, locations[i].Call.File, locations[i].Call.Line)
}
if len(locations) != len(stacks[i])+2 {
t.Fatalf("Wrong stack trace size %d %d\n", len(locations), len(stacks[i])+2)
}
for j := range stacks[i] {
@ -3193,6 +3193,7 @@ func testDeclLineCount(t *testing.T, p *proc.Target, lineno int, tgtvars []strin
assertLineNumber(p, t, lineno, "Program did not continue to correct next location")
scope, err := proc.GoroutineScope(p, p.CurrentThread())
t.Logf("scope PC: %#x\n", scope.PC)
assertNoError(err, t, fmt.Sprintf("GoroutineScope (:%d)", lineno))
vars, err := scope.Locals(0, "")
assertNoError(err, t, fmt.Sprintf("Locals (:%d)", lineno))