go tool compile: DWARF调试信息生成
gc.Main()→dwarfgen.RecordFlags()
记录当前构建信息到dwarf调试信息中去,方便调试器调试时查看tracee的构建细节
这个函数的主要目的是将编译器的命令行参数记录到 DWARF 调试信息中。DWARF 是一种调试信息格式,用于帮助调试器理解程序的内部结构。具体来说:
1. 函数接收一系列标志名称作为参数,这些标志是编译器命令行参数
2. 对于每个标志,函数会:
- 检查标志是否存在
- 检查标志值是否与默认值不同(如果相同则跳过)
- 根据标志类型(布尔型、计数型或普通型)以不同格式记录到缓冲区中
3. 特殊处理:
- 对于布尔型标志(如 -race),如果值为 true,只记录标志名
- 对于计数型标志(如 -v),如果值为 1,只记录标志名
- 对于其他标志,记录标志名和值(如 -gcflags="-N -l")
4. 最后,这些参数会被存储在一个特殊的符号中:
- 符号名格式为 dwarf.CUInfoPrefix + "producer." + base.Ctxt.Pkgpath
- 符号类型设置为 objabi.SDWARFCUINFO(表示这是编译单元信息)
- 允许重复(因为测试时可能会链接多个 main 包)
- 将参数信息存储在符号的数据中
这样做的目的是让调试器能够知道程序是如何被编译的,这对于调试和问题诊断很有帮助。比如,如果程序是用 -race 编译的,调试器就能知道这是一个竞态检测版本的程序。
这个信息会被写入到最终的可执行文件中,作为 DWARF 调试信息的一部分。当使用调试器(如 GDB)时,这些信息可以帮助开发者更好地理解程序的编译环境和配置。
gc.Main()→dwarf flags设置
根据命令行参数设置对应的dwarf设置
if base.Flag.Dwarf {
base.Ctxt.DebugInfo = dwarfgen.Info
base.Ctxt.GenAbstractFunc = dwarfgen.AbstractFunc
base.Ctxt.DwFixups = obj.NewDwarfFixupTable(base.Ctxt)
} else {
// turn off inline generation if no dwarf at all
base.Flag.GenDwarfInl = 0
base.Ctxt.Flag_locationlists = false
}
if base.Ctxt.Flag_locationlists && len(base.Ctxt.Arch.DWARFRegisters) == 0 {
log.Fatalf("location lists requested but register mapping not available on %v", base.Ctxt.Arch.Name)
}
if base.Flag.Dwarf {
dwarf.EnableLogging(base.Debug.DwarfInl != 0)
}
gc.Main()→dwarfgen.RecordPackageName()
记录下当前编译单元的PackageName,记录在哪呢?生成一个符号表符号,类型为SDWARFCUINFO
// RecordPackageName records the name of the package being
// compiled, so that the linker can save it in the compile unit's DIE.
func RecordPackageName() {
s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + base.Ctxt.Pkgpath)
s.Type = objabi.SDWARFCUINFO
// Sometimes (for example when building tests) we can link
// together two package main archives. So allow dups.
s.Set(obj.AttrDuplicateOK, true)
base.Ctxt.Data = append(base.Ctxt.Data, s)
s.P = []byte(types.LocalPkg.Name)
}
gc.Main()→dumpGlobal(n)/dumpGlobalConst(n)
将当前localpackage中的全局变量、常量生成到dwarf调试信息
for nextFunc, nextExtern := 0, 0; ; {
...
if nextExtern < len(typecheck.Target.Externs) {
switch n := typecheck.Target.Externs[nextExtern]; n.Op() {
case ir.ONAME:
dumpGlobal(n)
case ir.OLITERAL:
dumpGlobalConst(n)
...
}
nextExtern++
continue
}
...
}
func dumpGlobal(n *ir.Name) {
...
if n.Class == ir.PFUNC { return }
if n.Sym().Pkg != types.LocalPkg { return }
...
base.Ctxt.DwarfGlobal(types.TypeSymName(n.Type()), n.Linksym())
}
// DwarfGlobal creates a link symbol containing a DWARF entry for
// a global variable.
func (ctxt *Link) DwarfGlobal(typename string, varSym *LSym) {
myimportpath := ctxt.Pkgpath
if myimportpath == "" || varSym.Local() {
return
}
varname := varSym.Name
dieSym := &LSym{
Type: objabi.SDWARFVAR,
}
varSym.NewVarInfo().dwarfInfoSym = dieSym
ctxt.Data = append(ctxt.Data, dieSym)
typeSym := ctxt.Lookup(dwarf.InfoPrefix + typename)
dwarf.PutGlobal(dwCtxt{ctxt}, dieSym, typeSym, varSym, varname)
}
// PutGlobal writes a DIE for a global variable.
func PutGlobal(ctxt Context, info, typ, gvar Sym, name string) {
Uleb128put(ctxt, info, DW_ABRV_VARIABLE)
putattr(ctxt, info, DW_ABRV_VARIABLE, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
putattr(ctxt, info, DW_ABRV_VARIABLE, DW_FORM_block1, DW_CLS_ADDRESS, 0, gvar)
putattr(ctxt, info, DW_ABRV_VARIABLE, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, typ)
putattr(ctxt, info, DW_ABRV_VARIABLE, DW_FORM_flag, DW_CLS_FLAG, 1, nil)
}
func dumpGlobalConst(n *ir.Name) {
...
base.Ctxt.DwarfIntConst(n.Sym().Name, types.TypeSymName(t), ir.IntVal(t, v))
}
// DwarfIntConst creates a link symbol for an integer constant with the
// given name, type and value.
func (ctxt *Link) DwarfIntConst(name, typename string, val int64) {
myimportpath := ctxt.Pkgpath
if myimportpath == "" {
return
}
s := ctxt.LookupInit(dwarf.ConstInfoPrefix+myimportpath, func(s *LSym) {
s.Type = objabi.SDWARFCONST
ctxt.Data = append(ctxt.Data, s)
})
dwarf.PutIntConst(dwCtxt{ctxt}, s, ctxt.Lookup(dwarf.InfoPrefix+typename), myimportpath+"."+name, val)
}
// PutIntConst writes a DIE for an integer constant
func PutIntConst(ctxt Context, info, typ Sym, name string, val int64) {
Uleb128put(ctxt, info, DW_ABRV_INT_CONSTANT)
putattr(ctxt, info, DW_ABRV_INT_CONSTANT, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
putattr(ctxt, info, DW_ABRV_INT_CONSTANT, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, typ)
putattr(ctxt, info, DW_ABRV_INT_CONSTANT, DW_FORM_sdata, DW_CLS_CONSTANT, val, nil)
}
gc.Main()→enqueueFunc(f)+compilequeue
gc.Main()
\-> enqueueFunc
\-> compileFunctions
\-> compile
\-> (*Progs).FLush
\-> (*Progs).Flushplist
\-> (*Link).populateDWARF
OK,详细展开看看:
func gc.Main(...) {
...
for nextFunc, nextExtern := 0, 0; ; {
if nextFunc < len(typecheck.Target.Funcs) {
enqueueFunc(typecheck.Target.Funcs[nextFunc])
nextFunc++
continue
}
// The SSA backend supports using multiple goroutines, so keep it
// as late as possible to maximize how much work we can batch and
// process concurrently.
if len(compilequeue) != 0 {
compileFunctions(profile)
continue
}
...
// Finalize DWARF inline routine DIEs, then explicitly turn off
// further DWARF inlining generation to avoid problems with
// generated method wrappers.
//
// Note: The DWARF fixup code for inlined calls currently doesn't
// allow multiple invocations, so we intentionally run it just
// once after everything else. Worst case, some generated
// functions have slightly larger DWARF DIEs.
if base.Ctxt.DwFixups != nil {
base.Ctxt.DwFixups.Finalize(base.Ctxt.Pkgpath, base.Debug.DwarfInl != 0)
base.Ctxt.DwFixups = nil
base.Flag.GenDwarfInl = 0
continue // may have called reflectdata.TypeLinksym (#62156)
}
...
}
}
// 递归地分析fn中的body,如果内部还有新创建的fn将继续假如compilequeue
func enqueueFunc(fn *ir.Func) {
todo := []*ir.Func{fn}
for len(todo) > 0 {
next := todo[len(todo)-1]
todo = todo[:len(todo)-1]
prepareFunc(next)
todo = append(todo, next.Closures...)
}
...
// Enqueue just fn itself. compileFunctions will handle
// scheduling compilation of its closures after it's done.
compilequeue = append(compilequeue, fn)
}
// compileFunctions compiles all functions in compilequeue.
// It fans out nBackendWorkers to do the work
// and waits for them to complete.
func compileFunctions(profile *pgoir.Profile) {
...
var compile func([]*ir.Func)
compile = func(fns []*ir.Func) {
for _, fn := range fns {
fn := fn
queue(func(worker int) {
ssagen.Compile(fn, worker, profile)
compile(fn.Closures)
})
}
}
...
compile(compilequeue)
}
// Compile builds an SSA backend function,
// uses it to generate a plist,
// and flushes that plist to machine code.
// worker indicates which of the backend workers is doing the processing.
func Compile(fn *ir.Func, worker int, profile *pgoir.Profile) {
f := buildssa(fn, worker, inline.IsPgoHotFunc(fn, profile) || inline.HasPgoHotInline(fn))
...
pp := objw.NewProgs(fn, worker)
defer pp.Free()
genssa(f, pp)
...
pp.Flush() // assemble, fill in boilerplate, etc.
...
}
// Flush converts from pp to machine code.
func (pp *Progs) Flush() {
plist := &obj.Plist{Firstpc: pp.Text, Curfn: pp.CurFunc}
obj.Flushplist(base.Ctxt, plist, pp.NewProg)
}
func Flushplist(ctxt *Link, plist *Plist, newprog ProgAlloc) {
...
ctxt.populateDWARF(plist.Curfn, s)
...
}
// populateDWARF fills in the DWARF Debugging Information Entries for
// TEXT symbol 's'. The various DWARF symbols must already have been
// initialized in InitTextSym.
func (ctxt *Link) populateDWARF(curfn Func, s *LSym) {
// see more details below
...
}
(Link).populateDWARF(Func, LSym)
// populateDWARF fills in the DWARF Debugging Information Entries for
// TEXT symbol 's'. The various DWARF symbols must already have been
// initialized in InitTextSym.
func (ctxt *Link) populateDWARF(curfn Func, s *LSym) {
...
info, loc, ranges, absfunc, lines := ctxt.dwarfSym(s)
...
var scopes []dwarf.Scope
var inlcalls dwarf.InlCalls
if ctxt.DebugInfo != nil {
scopes, inlcalls = ctxt.DebugInfo(s, info, curfn)
}
var err error
dwctxt := dwCtxt{ctxt}
startPos := ctxt.InnermostPos(textPos(s))
...
fnstate := &dwarf.FnState{
Name: s.Name,
Info: info,
Loc: loc,
Ranges: ranges,
Absfn: absfunc,
StartPC: s,
Size: s.Size,
StartPos: startPos,
External: !s.Static(),
Scopes: scopes,
InlCalls: inlcalls,
UseBASEntries: ctxt.UseBASEntries,
}
if absfunc != nil {
err = dwarf.PutAbstractFunc(dwctxt, fnstate)
if err != nil {
ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
}
err = dwarf.PutConcreteFunc(dwctxt, fnstate, s.Wrapper())
} else {
err = dwarf.PutDefaultFunc(dwctxt, fnstate, s.Wrapper())
}
if err != nil {
ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
}
// Fill in the debug lines symbol.
ctxt.generateDebugLinesSymbol(s, lines)
}
func PutAbstractFunc(ctxt Context, s *FnState) error {...}
func putAbstractVar(...)
func putAbstractVarAbbrev(...)
func putattr(...)
...
// 将函数声明记录到dwarf信息中
func PutConcreteFunc(ctxt Context, s *FnState, isWrapper bool) error {...}
func putattr(...)
func concreteVar(...)
func inlinedVarTable(...)
func putparamtypes(...)
...
// 这些函数是将函数体中不同作用域的变量给记录到dwarf信息中
func putPrunedScopes(...)
func putscope(...)
func putparamtypes(...)
...
func putInlinedFunc(...)
...
func Uleb128put(...)
...
// 将函数体中的语句的pc值变化、行号值变化记录到dwarf行号信息表中
func generateDebugLinesSymbol(...)
func putpclcdelta(...) // pc< delta <-> ln delta
gc.Main()→ foreach func → (*DwarfFixupTable).Finalize()
貌似是有引用某些内联函数中定义的局部变量,此时可能需要这里处理下
// Called after all functions have been compiled; the main job of this
// function is to identify cases where there are outstanding fixups.
// This scenario crops up when we have references to variables of an
// inlined routine, but that routine is defined in some other package.
// This helper walks through and locate these fixups, then invokes a
// helper to create an abstract subprogram DIE for each one.
func (ft *DwarfFixupTable) Finalize(myimportpath string, trace bool) {
...
// Collect up the keys from the precursor map, then sort the
// resulting list (don't want to rely on map ordering here).
fns := make([]*LSym, len(ft.precursor))
idx := 0
for fn := range ft.precursor {
fns[idx] = fn
idx++
}
sort.Sort(BySymName(fns))
...
// Generate any missing abstract functions.
for _, s := range fns {
absfn := ft.AbsFuncDwarfSym(s)
slot, found := ft.symtab[absfn]
if !found || !ft.svec[slot].defseen {
ft.ctxt.GenAbstractFunc(s)
}
}
// Apply fixups.
for _, s := range fns {
absfn := ft.AbsFuncDwarfSym(s)
slot, found := ft.symtab[absfn]
if !found {
ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize orphan abstract function for %v", s)
} else {
ft.processFixups(slot, s)
}
}
}