cmd/compile,cmd/link: set DW_AT_decl_line for function declarations

DW_AT_decl_line provides the line number of function declarations (the
line containing the func keyword). This is the equivalent to CL 429638,
but provided via DWARF.

Note that the file of declarations (DW_AT_decl_file) is already provided
for non-inlined functions. It is omitted for inlined functions because
those DWARF subprograms may be generated outside of their source
compilation unit, where referencing the file table is difficult.

Fixes #57308.

Change-Id: I3ad12e1f366c4465c2a588297988a5825ef7efec
Reviewed-on: https://go-review.googlesource.com/c/go/+/458195
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Michael Pratt <mpratt@google.com>
Run-TryBot: Michael Pratt <mpratt@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
This commit is contained in:
Michael Pratt 2022-12-14 13:36:19 -05:00 committed by Gopher Robot
parent ee5ce77c62
commit 9e45b1d53e
5 changed files with 41 additions and 14 deletions

View file

@ -23,7 +23,7 @@ import (
"cmd/internal/src" "cmd/internal/src"
) )
func Info(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls) { func Info(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) (scopes []dwarf.Scope, inlcalls dwarf.InlCalls, startPos src.XPos) {
fn := curfn.(*ir.Func) fn := curfn.(*ir.Func)
if fn.Nname != nil { if fn.Nname != nil {
@ -124,12 +124,11 @@ func Info(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.Scope,
varScopes = append(varScopes, findScope(fn.Marks, pos)) varScopes = append(varScopes, findScope(fn.Marks, pos))
} }
scopes := assembleScopes(fnsym, fn, dwarfVars, varScopes) scopes = assembleScopes(fnsym, fn, dwarfVars, varScopes)
var inlcalls dwarf.InlCalls
if base.Flag.GenDwarfInl > 0 { if base.Flag.GenDwarfInl > 0 {
inlcalls = assembleInlines(fnsym, dwarfVars) inlcalls = assembleInlines(fnsym, dwarfVars)
} }
return scopes, inlcalls return scopes, inlcalls, fn.Pos()
} }
func declPos(decl *ir.Name) src.XPos { func declPos(decl *ir.Name) src.XPos {

View file

@ -94,6 +94,7 @@ type FnState struct {
Absfn Sym Absfn Sym
StartPC Sym StartPC Sym
Size int64 Size int64
StartLine int32
External bool External bool
Scopes []Scope Scopes []Scope
InlCalls InlCalls InlCalls InlCalls
@ -458,6 +459,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{
{DW_AT_high_pc, DW_FORM_addr}, {DW_AT_high_pc, DW_FORM_addr},
{DW_AT_frame_base, DW_FORM_block1}, {DW_AT_frame_base, DW_FORM_block1},
{DW_AT_decl_file, DW_FORM_data4}, {DW_AT_decl_file, DW_FORM_data4},
{DW_AT_decl_line, DW_FORM_udata},
{DW_AT_external, DW_FORM_flag}, {DW_AT_external, DW_FORM_flag},
}, },
}, },
@ -482,6 +484,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{
[]dwAttrForm{ []dwAttrForm{
{DW_AT_name, DW_FORM_string}, {DW_AT_name, DW_FORM_string},
{DW_AT_inline, DW_FORM_data1}, {DW_AT_inline, DW_FORM_data1},
{DW_AT_decl_line, DW_FORM_udata},
{DW_AT_external, DW_FORM_flag}, {DW_AT_external, DW_FORM_flag},
}, },
}, },
@ -1254,6 +1257,8 @@ func PutAbstractFunc(ctxt Context, s *FnState) error {
// DW_AT_inlined value // DW_AT_inlined value
putattr(ctxt, s.Absfn, abbrev, DW_FORM_data1, DW_CLS_CONSTANT, int64(DW_INL_inlined), nil) putattr(ctxt, s.Absfn, abbrev, DW_FORM_data1, DW_CLS_CONSTANT, int64(DW_INL_inlined), nil)
putattr(ctxt, s.Absfn, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, int64(s.StartLine), nil)
var ev int64 var ev int64
if s.External { if s.External {
ev = 1 ev = 1
@ -1446,6 +1451,8 @@ func PutDefaultFunc(ctxt Context, s *FnState, isWrapper bool) error {
putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, int64(1), 0) putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, int64(1), 0)
} else { } else {
ctxt.AddFileRef(s.Info, s.Filesym) ctxt.AddFileRef(s.Info, s.Filesym)
putattr(ctxt, s.Info, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, int64(s.StartLine), nil)
var ev int64 var ev int64
if s.External { if s.External {
ev = 1 ev = 1

View file

@ -353,7 +353,9 @@ func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym, myimportpath string)
var scopes []dwarf.Scope var scopes []dwarf.Scope
var inlcalls dwarf.InlCalls var inlcalls dwarf.InlCalls
if ctxt.DebugInfo != nil { if ctxt.DebugInfo != nil {
scopes, inlcalls = ctxt.DebugInfo(s, info, curfn) // Don't need startPos because s.Func().StartLine is populated,
// as s is in this package.
scopes, inlcalls, _ = ctxt.DebugInfo(s, info, curfn)
} }
var err error var err error
dwctxt := dwCtxt{ctxt} dwctxt := dwCtxt{ctxt}
@ -368,6 +370,7 @@ func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym, myimportpath string)
Absfn: absfunc, Absfn: absfunc,
StartPC: s, StartPC: s,
Size: s.Size, Size: s.Size,
StartLine: s.Func().StartLine,
External: !s.Static(), External: !s.Static(),
Scopes: scopes, Scopes: scopes,
InlCalls: inlcalls, InlCalls: inlcalls,
@ -427,8 +430,12 @@ func (ctxt *Link) DwarfAbstractFunc(curfn interface{}, s *LSym, myimportpath str
if s.Func() == nil { if s.Func() == nil {
s.NewFuncInfo() s.NewFuncInfo()
} }
scopes, _ := ctxt.DebugInfo(s, absfn, curfn) scopes, _, startPos := ctxt.DebugInfo(s, absfn, curfn)
_, startLine := ctxt.getFileSymbolAndLine(startPos)
dwctxt := dwCtxt{ctxt} dwctxt := dwCtxt{ctxt}
// TODO(prattmic): this returns nil for symbols outside of the current
// package because s.Func() is empty. This doesn't matter because
// PutAbstractFunc doesn't use Filesym. Use startPos or remove.
filesym := ctxt.fileSymbol(s) filesym := ctxt.fileSymbol(s)
fnstate := dwarf.FnState{ fnstate := dwarf.FnState{
Name: s.Name, Name: s.Name,
@ -436,6 +443,7 @@ func (ctxt *Link) DwarfAbstractFunc(curfn interface{}, s *LSym, myimportpath str
Info: absfn, Info: absfn,
Filesym: filesym, Filesym: filesym,
Absfn: absfn, Absfn: absfn,
StartLine: startLine,
External: !s.Static(), External: !s.Static(),
Scopes: scopes, Scopes: scopes,
UseBASEntries: ctxt.UseBASEntries, UseBASEntries: ctxt.UseBASEntries,

View file

@ -916,7 +916,7 @@ type Link struct {
Imports []goobj.ImportedPkg Imports []goobj.ImportedPkg
DiagFunc func(string, ...interface{}) DiagFunc func(string, ...interface{})
DiagFlush func() DiagFlush func()
DebugInfo func(fn *LSym, info *LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls) // if non-nil, curfn is a *gc.Node DebugInfo func(fn *LSym, info *LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls, src.XPos) // if non-nil, curfn is a *ir.Func
GenAbstractFunc func(fn *LSym) GenAbstractFunc func(fn *LSym)
Errors int Errors int

View file

@ -334,10 +334,12 @@ func main() {
} }
} }
func varDeclCoordsAndSubrogramDeclFile(t *testing.T, testpoint string, expectFile string, expectLine int, directive string) { // expectLine is the expected line for main.
func varDeclCoordsAndSubprogramDeclFile(t *testing.T, testpoint string, expectFile string, expectLine int, directive string) {
t.Parallel() t.Parallel()
prog := fmt.Sprintf("package main\n%s\nfunc main() {\n\nvar i int\ni = i\n}\n", directive) prog := fmt.Sprintf("package main\n%s\nfunc main() {\n\nvar i int\ni = i\n}\n", directive)
const iLineOffset = 2
dir := t.TempDir() dir := t.TempDir()
@ -385,9 +387,12 @@ func varDeclCoordsAndSubrogramDeclFile(t *testing.T, testpoint string, expectFil
} }
// Verify line/file attributes. // Verify line/file attributes.
line := iEntry.Val(dwarf.AttrDeclLine) line, lineOK := iEntry.Val(dwarf.AttrDeclLine).(int64)
if line == nil || line.(int64) != int64(expectLine) { if !lineOK {
t.Errorf("DW_AT_decl_line for i is %v, want %d", line, expectLine) t.Errorf("missing or invalid DW_AT_decl_line for i")
}
if line != int64(expectLine+iLineOffset) {
t.Errorf("DW_AT_decl_line for i is %v, want %d", line, expectLine+iLineOffset)
} }
fileIdx, fileIdxOK := maindie.Val(dwarf.AttrDeclFile).(int64) fileIdx, fileIdxOK := maindie.Val(dwarf.AttrDeclFile).(int64)
@ -402,6 +407,14 @@ func varDeclCoordsAndSubrogramDeclFile(t *testing.T, testpoint string, expectFil
if base != expectFile { if base != expectFile {
t.Errorf("DW_AT_decl_file for main is %v, want %v", base, expectFile) t.Errorf("DW_AT_decl_file for main is %v, want %v", base, expectFile)
} }
line, lineOK = maindie.Val(dwarf.AttrDeclLine).(int64)
if !lineOK {
t.Errorf("missing or invalid DW_AT_decl_line for main")
}
if line != int64(expectLine) {
t.Errorf("DW_AT_decl_line for main is %v, want %d", line, expectLine)
}
} }
func TestVarDeclCoordsAndSubrogramDeclFile(t *testing.T) { func TestVarDeclCoordsAndSubrogramDeclFile(t *testing.T) {
@ -411,7 +424,7 @@ func TestVarDeclCoordsAndSubrogramDeclFile(t *testing.T) {
t.Skip("skipping on plan9; no DWARF symbol table in executables") t.Skip("skipping on plan9; no DWARF symbol table in executables")
} }
varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoords", "test.go", 5, "") varDeclCoordsAndSubprogramDeclFile(t, "TestVarDeclCoords", "test.go", 3, "")
} }
func TestVarDeclCoordsWithLineDirective(t *testing.T) { func TestVarDeclCoordsWithLineDirective(t *testing.T) {
@ -421,8 +434,8 @@ func TestVarDeclCoordsWithLineDirective(t *testing.T) {
t.Skip("skipping on plan9; no DWARF symbol table in executables") t.Skip("skipping on plan9; no DWARF symbol table in executables")
} }
varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoordsWithLineDirective", varDeclCoordsAndSubprogramDeclFile(t, "TestVarDeclCoordsWithLineDirective",
"foobar.go", 202, "//line /foobar.go:200") "foobar.go", 200, "//line /foobar.go:200")
} }
func TestInlinedRoutineRecords(t *testing.T) { func TestInlinedRoutineRecords(t *testing.T) {