1
2
3
4
5 package dwarfgen
6
7 import (
8 "bytes"
9 "flag"
10 "fmt"
11 "internal/buildcfg"
12 "slices"
13 "sort"
14 "strings"
15
16 "cmd/compile/internal/base"
17 "cmd/compile/internal/ir"
18 "cmd/compile/internal/reflectdata"
19 "cmd/compile/internal/ssa"
20 "cmd/compile/internal/ssagen"
21 "cmd/compile/internal/typecheck"
22 "cmd/compile/internal/types"
23 "cmd/internal/dwarf"
24 "cmd/internal/obj"
25 "cmd/internal/objabi"
26 "cmd/internal/src"
27 )
28
29 func Info(ctxt *obj.Link, fnsym *obj.LSym, infosym *obj.LSym, curfn obj.Func) (scopes []dwarf.Scope, inlcalls dwarf.InlCalls) {
30 fn := curfn.(*ir.Func)
31
32 if fn.Nname != nil {
33 expect := fn.Linksym()
34 if fnsym.ABI() == obj.ABI0 {
35 expect = fn.LinksymABI(obj.ABI0)
36 }
37 if fnsym != expect {
38 base.Fatalf("unexpected fnsym: %v != %v", fnsym, expect)
39 }
40 }
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 isODCLFUNC := infosym.Name == ""
75
76 var apdecls []*ir.Name
77
78 if isODCLFUNC {
79 for _, n := range fn.Dcl {
80 if n.Op() != ir.ONAME {
81 continue
82 }
83 switch n.Class {
84 case ir.PAUTO:
85 if !n.Used() {
86
87 if fnsym.Func().Text != nil {
88 base.Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)")
89 }
90 continue
91 }
92 case ir.PPARAM, ir.PPARAMOUT:
93 default:
94 continue
95 }
96 if !shouldEmitDwarfVar(n) {
97 continue
98 }
99 apdecls = append(apdecls, n)
100 if n.Type().Kind() == types.TSSA {
101
102
103 continue
104 }
105 fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
106 }
107 }
108
109 var closureVars map[*ir.Name]int64
110 if fn.Needctxt() {
111 closureVars = make(map[*ir.Name]int64)
112 csiter := typecheck.NewClosureStructIter(fn.ClosureVars)
113 for {
114 n, _, offset := csiter.Next()
115 if n == nil {
116 break
117 }
118 closureVars[n] = offset
119 if n.Heapaddr != nil {
120 closureVars[n.Heapaddr] = offset
121 }
122 }
123 }
124
125 decls, dwarfVars := createDwarfVars(fnsym, isODCLFUNC, fn, apdecls, closureVars)
126
127
128
129
130
131
132
133
134
135 typesyms := []*obj.LSym{}
136 for t := range fnsym.Func().Autot {
137 typesyms = append(typesyms, t)
138 }
139 for i := range fnsym.R {
140 if fnsym.R[i].Type == objabi.R_USEIFACE && !strings.HasPrefix(fnsym.R[i].Sym.Name, "go:itab.") {
141
142 typesyms = append(typesyms, fnsym.R[i].Sym)
143 }
144 }
145 slices.SortFunc(typesyms, func(a, b *obj.LSym) int {
146 return strings.Compare(a.Name, b.Name)
147 })
148 var lastsym *obj.LSym
149 for _, sym := range typesyms {
150 if sym == lastsym {
151 continue
152 }
153 lastsym = sym
154 infosym.AddRel(ctxt, obj.Reloc{Type: objabi.R_USETYPE, Sym: sym})
155 }
156 fnsym.Func().Autot = nil
157
158 var varScopes []ir.ScopeID
159 for _, decl := range decls {
160 pos := declPos(decl)
161 varScopes = append(varScopes, findScope(fn.Marks, pos))
162 }
163
164 scopes = assembleScopes(fnsym, fn, dwarfVars, varScopes)
165 if base.Flag.GenDwarfInl > 0 {
166 inlcalls = assembleInlines(fnsym, dwarfVars)
167 }
168 return scopes, inlcalls
169 }
170
171 func declPos(decl *ir.Name) src.XPos {
172 return decl.Canonical().Pos()
173 }
174
175
176
177 func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir.Name, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var) {
178
179 var vars []*dwarf.Var
180 var decls []*ir.Name
181
182
183 var debug *ssa.FuncDebug
184 var varIDMap map[*ir.Name]ssa.VarID
185 if fn.DebugInfo != nil {
186 debug = fn.DebugInfo.(*ssa.FuncDebug)
187 varIDMap = make(map[*ir.Name]ssa.VarID, len(debug.Vars))
188 for i, n := range debug.Vars {
189 varIDMap[n] = ssa.VarID(i)
190 }
191 }
192 canUseComplex := complexOK && debug != nil
193
194
195
196
197
198
199 seen := make(map[*ir.Name]bool)
200 markVarSeen := func(n *ir.Name, varID ssa.VarID) {
201 seen[n] = true
202 if debug != nil && int(varID) < len(debug.VarSlots) {
203 for _, slot := range debug.VarSlots[varID] {
204 seen[debug.Slots[slot].N] = true
205 }
206 }
207 }
208
209
210
211 for _, n := range apDecls {
212 if !shouldEmitDwarfVar(n) {
213 continue
214 }
215 if canUseComplex {
216 if vid, ok := varIDMap[n]; ok {
217 if dvar := createComplexVar(fnsym, fn, vid, closureVars); dvar != nil {
218 decls = append(decls, n)
219 vars = append(vars, dvar)
220 markVarSeen(n, vid)
221 continue
222 }
223 }
224 }
225 seen[n] = true
226 decls = append(decls, n)
227 vars = append(vars, createSimpleVar(fnsym, n, closureVars))
228 }
229
230
231 if canUseComplex {
232 for i, n := range debug.Vars {
233 if seen[n] {
234 continue
235 }
236 if !shouldEmitDwarfVar(n) {
237 continue
238 }
239 if dvar := createComplexVar(fnsym, fn, ssa.VarID(i), closureVars); dvar != nil {
240 decls = append(decls, n)
241 vars = append(vars, dvar)
242 markVarSeen(n, ssa.VarID(i))
243 }
244 }
245 }
246
247
248 if debug != nil {
249 for _, n := range debug.OptDcl {
250 if seen[n] {
251 continue
252 }
253 if n.Class != ir.PAUTO {
254 continue
255 }
256 types.CalcSize(n.Type())
257 if n.Type().Size() == 0 {
258 decls = append(decls, n)
259 vars = append(vars, createSimpleVar(fnsym, n, closureVars))
260 vars[len(vars)-1].StackOffset = 0
261 fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
262 seen[n] = true
263 }
264 }
265 }
266
267
268
269 dcl := apDecls
270 if fnsym.WasInlined() {
271 dcl = preInliningDcls(fnsym)
272 } else if debug != nil {
273
274
275
276
277
278 for _, n := range debug.RegOutputParams {
279 if !ssa.IsVarWantedForDebug(n) {
280 continue
281 }
282 if n.Class != ir.PPARAMOUT || !n.IsOutputParamInRegisters() {
283 base.Fatalf("invalid ir.Name on debugInfo.RegOutputParams list")
284 }
285 dcl = append(dcl, n)
286 }
287 }
288
289
290
291
292 for _, n := range dcl {
293 if seen[n] {
294 continue
295 }
296 if !shouldEmitDwarfVar(n) {
297 continue
298 }
299 seen[n] = true
300 if canUseComplex {
301 if vid, ok := varIDMap[n]; ok {
302 if dvar := createComplexVar(fnsym, fn, vid, closureVars); dvar != nil {
303 decls = append(decls, n)
304 vars = append(vars, dvar)
305 continue
306 }
307 }
308 }
309 if n.Class == ir.PPARAM && !ssa.CanSSA(n.Type()) {
310 decls = append(decls, n)
311 vars = append(vars, createSimpleVar(fnsym, n, closureVars))
312 continue
313 }
314 decls = append(decls, n)
315 vars = append(vars, createConservativeVar(fnsym, fn, n, closureVars))
316 }
317
318
319 sortDeclsAndVars(fn, decls, vars)
320
321 return decls, vars
322 }
323
324
325
326
327
328
329
330
331 func createConservativeVar(fnsym *obj.LSym, fn *ir.Func, n *ir.Name, closureVars map[*ir.Name]int64) *dwarf.Var {
332 typename := dwarf.InfoPrefix + types.TypeSymName(n.Type())
333 tag := dwarf.DW_TAG_variable
334 isReturnValue := (n.Class == ir.PPARAMOUT)
335 if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
336 tag = dwarf.DW_TAG_formal_parameter
337 }
338 inlIndex := 0
339 if base.Flag.GenDwarfInl > 1 {
340 if n.InlFormal() || n.InlLocal() {
341 inlIndex = posInlIndex(n.Pos()) + 1
342 if n.InlFormal() {
343 tag = dwarf.DW_TAG_formal_parameter
344 }
345 }
346 }
347 declpos := base.Ctxt.InnermostPos(n.Pos())
348 dvar := &dwarf.Var{
349 Name: n.Sym().Name,
350 IsReturnValue: isReturnValue,
351 Tag: tag,
352 WithLoclist: true,
353 StackOffset: int32(n.FrameOffset()),
354 Type: base.Ctxt.Lookup(typename),
355 DeclFile: declpos.RelFilename(),
356 DeclLine: declpos.RelLine(),
357 DeclCol: declpos.RelCol(),
358 InlIndex: int32(inlIndex),
359 ChildIndex: -1,
360 DictIndex: n.DictIndex,
361 ClosureOffset: closureOffset(n, closureVars),
362 }
363 if n.Esc() == ir.EscHeap && n.Heapaddr != nil {
364
365
366
367
368
369
370
371 debug := fn.DebugInfo.(*ssa.FuncDebug)
372 list := createHeapDerefLocationList(n, debug.EntryID)
373 dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
374 debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym))
375 }
376 }
377
378 fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
379 return dvar
380 }
381
382
383
384
385
386
387
388 func sortDeclsAndVars(fn *ir.Func, decls []*ir.Name, vars []*dwarf.Var) {
389 paramOrder := make(map[*ir.Name]int)
390 idx := 1
391 for _, f := range fn.Type().RecvParamsResults() {
392 if n, ok := f.Nname.(*ir.Name); ok {
393 paramOrder[n] = idx
394 idx++
395 }
396 }
397 sort.Stable(varsAndDecls{decls, vars, paramOrder})
398 }
399
400 type varsAndDecls struct {
401 decls []*ir.Name
402 vars []*dwarf.Var
403 paramOrder map[*ir.Name]int
404 }
405
406 func (v varsAndDecls) Len() int {
407 return len(v.decls)
408 }
409
410 func (v varsAndDecls) Less(i, j int) bool {
411 nameLT := func(ni, nj *ir.Name) bool {
412 oi, foundi := v.paramOrder[ni]
413 oj, foundj := v.paramOrder[nj]
414 if foundi {
415 if foundj {
416 return oi < oj
417 } else {
418 return true
419 }
420 }
421 return false
422 }
423 return nameLT(v.decls[i], v.decls[j])
424 }
425
426 func (v varsAndDecls) Swap(i, j int) {
427 v.vars[i], v.vars[j] = v.vars[j], v.vars[i]
428 v.decls[i], v.decls[j] = v.decls[j], v.decls[i]
429 }
430
431
432
433
434
435
436
437 func preInliningDcls(fnsym *obj.LSym) []*ir.Name {
438 fn := base.Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*ir.Func)
439 var rdcl []*ir.Name
440 for _, n := range fn.Inl.Dcl {
441 if n.Sym().Name[0] == '.' || !shouldEmitDwarfVarSafe(n) {
442 continue
443 }
444 rdcl = append(rdcl, n)
445 }
446 return rdcl
447 }
448
449 func createSimpleVar(fnsym *obj.LSym, n *ir.Name, closureVars map[*ir.Name]int64) *dwarf.Var {
450 var tag int
451 var offs int64
452
453 localAutoOffset := func() int64 {
454 offs = n.FrameOffset()
455 if base.Ctxt.Arch.FixedFrameSize == 0 {
456 offs -= int64(types.PtrSize)
457 }
458 if buildcfg.FramePointerEnabled {
459 offs -= int64(types.PtrSize)
460 }
461 return offs
462 }
463
464 switch n.Class {
465 case ir.PAUTO:
466 offs = localAutoOffset()
467 tag = dwarf.DW_TAG_variable
468 case ir.PPARAM, ir.PPARAMOUT:
469 tag = dwarf.DW_TAG_formal_parameter
470 if n.IsOutputParamInRegisters() {
471 offs = localAutoOffset()
472 } else {
473 offs = n.FrameOffset() + base.Ctxt.Arch.FixedFrameSize
474 }
475
476 default:
477 base.Fatalf("createSimpleVar unexpected class %v for node %v", n.Class, n)
478 }
479
480 typename := dwarf.InfoPrefix + types.TypeSymName(n.Type())
481 delete(fnsym.Func().Autot, reflectdata.TypeLinksym(n.Type()))
482 inlIndex := 0
483 if base.Flag.GenDwarfInl > 1 {
484 if n.InlFormal() || n.InlLocal() {
485 inlIndex = posInlIndex(n.Pos()) + 1
486 if n.InlFormal() {
487 tag = dwarf.DW_TAG_formal_parameter
488 }
489 }
490 }
491 declpos := base.Ctxt.InnermostPos(declPos(n))
492 return &dwarf.Var{
493 Name: n.Sym().Name,
494 IsReturnValue: n.Class == ir.PPARAMOUT,
495 IsInlFormal: n.InlFormal(),
496 Tag: tag,
497 StackOffset: int32(offs),
498 Type: base.Ctxt.Lookup(typename),
499 DeclFile: declpos.RelFilename(),
500 DeclLine: declpos.RelLine(),
501 DeclCol: declpos.RelCol(),
502 InlIndex: int32(inlIndex),
503 ChildIndex: -1,
504 DictIndex: n.DictIndex,
505 ClosureOffset: closureOffset(n, closureVars),
506 }
507 }
508
509
510 func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID, closureVars map[*ir.Name]int64) *dwarf.Var {
511 debug := fn.DebugInfo.(*ssa.FuncDebug)
512 n := debug.Vars[varID]
513
514 var tag int
515 switch n.Class {
516 case ir.PAUTO:
517 tag = dwarf.DW_TAG_variable
518 case ir.PPARAM, ir.PPARAMOUT:
519 tag = dwarf.DW_TAG_formal_parameter
520 default:
521 return nil
522 }
523
524 gotype := reflectdata.TypeLinksym(n.Type())
525 delete(fnsym.Func().Autot, gotype)
526 typename := dwarf.InfoPrefix + gotype.Name[len("type:"):]
527 inlIndex := 0
528 if base.Flag.GenDwarfInl > 1 {
529 if n.InlFormal() || n.InlLocal() {
530 inlIndex = posInlIndex(n.Pos()) + 1
531 if n.InlFormal() {
532 tag = dwarf.DW_TAG_formal_parameter
533 }
534 }
535 }
536 declpos := base.Ctxt.InnermostPos(n.Pos())
537 dvar := &dwarf.Var{
538 Name: n.Sym().Name,
539 IsReturnValue: n.Class == ir.PPARAMOUT,
540 IsInlFormal: n.InlFormal(),
541 Tag: tag,
542 WithLoclist: true,
543 Type: base.Ctxt.Lookup(typename),
544
545
546
547
548 StackOffset: ssagen.StackOffset(debug.Slots[debug.VarSlots[varID][0]]),
549 DeclFile: declpos.RelFilename(),
550 DeclLine: declpos.RelLine(),
551 DeclCol: declpos.RelCol(),
552 InlIndex: int32(inlIndex),
553 ChildIndex: -1,
554 DictIndex: n.DictIndex,
555 ClosureOffset: closureOffset(n, closureVars),
556 }
557 list := debug.LocationLists[varID]
558 if len(list) != 0 {
559 dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
560 debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym))
561 }
562 }
563 return dvar
564 }
565
566
567
568 func createHeapDerefLocationList(n *ir.Name, entryID ssa.ID) []ssa.LocListEntry {
569
570 heapPtrOffset := n.Heapaddr.FrameOffset()
571 if base.Ctxt.Arch.FixedFrameSize == 0 {
572 heapPtrOffset -= int64(types.PtrSize)
573 }
574 if buildcfg.FramePointerEnabled {
575 heapPtrOffset -= int64(types.PtrSize)
576 }
577
578
579 var expr []byte
580 expr = append(expr, dwarf.DW_OP_fbreg)
581 expr = dwarf.AppendSleb128(expr, heapPtrOffset)
582 expr = append(expr, dwarf.DW_OP_deref)
583
584 return []ssa.LocListEntry{{
585 StartBlock: entryID,
586 StartValue: ssa.BlockStart.ID,
587 EndBlock: entryID,
588 EndValue: ssa.FuncEnd.ID,
589 Expr: expr,
590 }}
591 }
592
593
594
595 func RecordFlags(flags ...string) {
596 if base.Ctxt.Pkgpath == "" {
597 base.Fatalf("missing pkgpath")
598 }
599
600 type BoolFlag interface {
601 IsBoolFlag() bool
602 }
603 type CountFlag interface {
604 IsCountFlag() bool
605 }
606 var cmd bytes.Buffer
607 for _, name := range flags {
608 f := flag.Lookup(name)
609 if f == nil {
610 continue
611 }
612 getter := f.Value.(flag.Getter)
613 if getter.String() == f.DefValue {
614
615 continue
616 }
617 if bf, ok := f.Value.(BoolFlag); ok && bf.IsBoolFlag() {
618 val, ok := getter.Get().(bool)
619 if ok && val {
620 fmt.Fprintf(&cmd, " -%s", f.Name)
621 continue
622 }
623 }
624 if cf, ok := f.Value.(CountFlag); ok && cf.IsCountFlag() {
625 val, ok := getter.Get().(int)
626 if ok && val == 1 {
627 fmt.Fprintf(&cmd, " -%s", f.Name)
628 continue
629 }
630 }
631 fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get())
632 }
633
634
635
636
637
638 if buildcfg.Experiment.RegabiArgs {
639 cmd.Write([]byte(" regabi"))
640 }
641
642 if cmd.Len() == 0 {
643 return
644 }
645 s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + base.Ctxt.Pkgpath)
646 s.Type = objabi.SDWARFCUINFO
647
648
649 s.Set(obj.AttrDuplicateOK, true)
650 base.Ctxt.Data = append(base.Ctxt.Data, s)
651 s.P = cmd.Bytes()[1:]
652 }
653
654
655
656 func RecordPackageName() {
657 s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + base.Ctxt.Pkgpath)
658 s.Type = objabi.SDWARFCUINFO
659
660
661 s.Set(obj.AttrDuplicateOK, true)
662 base.Ctxt.Data = append(base.Ctxt.Data, s)
663 s.P = []byte(types.LocalPkg.Name)
664 }
665
666
667
668
669 func shouldEmitDwarfVar(n *ir.Name) bool {
670 if ir.IsAutoTmp(n) {
671 return false
672 }
673 return shouldEmitDwarfVarSafe(n)
674 }
675
676
677
678
679
680
681 func shouldEmitDwarfVarSafe(n *ir.Name) bool {
682 if !ssa.IsVarWantedForDebug(n) {
683 return false
684 }
685 if n.Sym().Name == "_" {
686 return false
687 }
688 if n.Type().IsUntyped() {
689 return false
690 }
691 return true
692 }
693
694 func closureOffset(n *ir.Name, closureVars map[*ir.Name]int64) int64 {
695 return closureVars[n]
696 }
697
View as plain text