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