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 {
364 if n.Heapaddr == nil {
365 base.Fatalf("invalid heap allocated var without Heapaddr")
366 }
367 debug := fn.DebugInfo.(*ssa.FuncDebug)
368 list := createHeapDerefLocationList(n, debug.EntryID)
369 dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
370 debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym))
371 }
372 }
373
374 fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
375 return dvar
376 }
377
378
379
380
381
382
383
384 func sortDeclsAndVars(fn *ir.Func, decls []*ir.Name, vars []*dwarf.Var) {
385 paramOrder := make(map[*ir.Name]int)
386 idx := 1
387 for _, f := range fn.Type().RecvParamsResults() {
388 if n, ok := f.Nname.(*ir.Name); ok {
389 paramOrder[n] = idx
390 idx++
391 }
392 }
393 sort.Stable(varsAndDecls{decls, vars, paramOrder})
394 }
395
396 type varsAndDecls struct {
397 decls []*ir.Name
398 vars []*dwarf.Var
399 paramOrder map[*ir.Name]int
400 }
401
402 func (v varsAndDecls) Len() int {
403 return len(v.decls)
404 }
405
406 func (v varsAndDecls) Less(i, j int) bool {
407 nameLT := func(ni, nj *ir.Name) bool {
408 oi, foundi := v.paramOrder[ni]
409 oj, foundj := v.paramOrder[nj]
410 if foundi {
411 if foundj {
412 return oi < oj
413 } else {
414 return true
415 }
416 }
417 return false
418 }
419 return nameLT(v.decls[i], v.decls[j])
420 }
421
422 func (v varsAndDecls) Swap(i, j int) {
423 v.vars[i], v.vars[j] = v.vars[j], v.vars[i]
424 v.decls[i], v.decls[j] = v.decls[j], v.decls[i]
425 }
426
427
428
429
430
431
432
433 func preInliningDcls(fnsym *obj.LSym) []*ir.Name {
434 fn := base.Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*ir.Func)
435 var rdcl []*ir.Name
436 for _, n := range fn.Inl.Dcl {
437 if n.Sym().Name[0] == '.' || !shouldEmitDwarfVarSafe(n) {
438 continue
439 }
440 rdcl = append(rdcl, n)
441 }
442 return rdcl
443 }
444
445 func createSimpleVar(fnsym *obj.LSym, n *ir.Name, closureVars map[*ir.Name]int64) *dwarf.Var {
446 var tag int
447 var offs int64
448
449 localAutoOffset := func() int64 {
450 offs = n.FrameOffset()
451 if base.Ctxt.Arch.FixedFrameSize == 0 {
452 offs -= int64(types.PtrSize)
453 }
454 if buildcfg.FramePointerEnabled {
455 offs -= int64(types.PtrSize)
456 }
457 return offs
458 }
459
460 switch n.Class {
461 case ir.PAUTO:
462 offs = localAutoOffset()
463 tag = dwarf.DW_TAG_variable
464 case ir.PPARAM, ir.PPARAMOUT:
465 tag = dwarf.DW_TAG_formal_parameter
466 if n.IsOutputParamInRegisters() {
467 offs = localAutoOffset()
468 } else {
469 offs = n.FrameOffset() + base.Ctxt.Arch.FixedFrameSize
470 }
471
472 default:
473 base.Fatalf("createSimpleVar unexpected class %v for node %v", n.Class, n)
474 }
475
476 typename := dwarf.InfoPrefix + types.TypeSymName(n.Type())
477 delete(fnsym.Func().Autot, reflectdata.TypeLinksym(n.Type()))
478 inlIndex := 0
479 if base.Flag.GenDwarfInl > 1 {
480 if n.InlFormal() || n.InlLocal() {
481 inlIndex = posInlIndex(n.Pos()) + 1
482 if n.InlFormal() {
483 tag = dwarf.DW_TAG_formal_parameter
484 }
485 }
486 }
487 declpos := base.Ctxt.InnermostPos(declPos(n))
488 return &dwarf.Var{
489 Name: n.Sym().Name,
490 IsReturnValue: n.Class == ir.PPARAMOUT,
491 IsInlFormal: n.InlFormal(),
492 Tag: tag,
493 StackOffset: int32(offs),
494 Type: base.Ctxt.Lookup(typename),
495 DeclFile: declpos.RelFilename(),
496 DeclLine: declpos.RelLine(),
497 DeclCol: declpos.RelCol(),
498 InlIndex: int32(inlIndex),
499 ChildIndex: -1,
500 DictIndex: n.DictIndex,
501 ClosureOffset: closureOffset(n, closureVars),
502 }
503 }
504
505
506 func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID, closureVars map[*ir.Name]int64) *dwarf.Var {
507 debug := fn.DebugInfo.(*ssa.FuncDebug)
508 n := debug.Vars[varID]
509
510 var tag int
511 switch n.Class {
512 case ir.PAUTO:
513 tag = dwarf.DW_TAG_variable
514 case ir.PPARAM, ir.PPARAMOUT:
515 tag = dwarf.DW_TAG_formal_parameter
516 default:
517 return nil
518 }
519
520 gotype := reflectdata.TypeLinksym(n.Type())
521 delete(fnsym.Func().Autot, gotype)
522 typename := dwarf.InfoPrefix + gotype.Name[len("type:"):]
523 inlIndex := 0
524 if base.Flag.GenDwarfInl > 1 {
525 if n.InlFormal() || n.InlLocal() {
526 inlIndex = posInlIndex(n.Pos()) + 1
527 if n.InlFormal() {
528 tag = dwarf.DW_TAG_formal_parameter
529 }
530 }
531 }
532 declpos := base.Ctxt.InnermostPos(n.Pos())
533 dvar := &dwarf.Var{
534 Name: n.Sym().Name,
535 IsReturnValue: n.Class == ir.PPARAMOUT,
536 IsInlFormal: n.InlFormal(),
537 Tag: tag,
538 WithLoclist: true,
539 Type: base.Ctxt.Lookup(typename),
540
541
542
543
544 StackOffset: ssagen.StackOffset(debug.Slots[debug.VarSlots[varID][0]]),
545 DeclFile: declpos.RelFilename(),
546 DeclLine: declpos.RelLine(),
547 DeclCol: declpos.RelCol(),
548 InlIndex: int32(inlIndex),
549 ChildIndex: -1,
550 DictIndex: n.DictIndex,
551 ClosureOffset: closureOffset(n, closureVars),
552 }
553 list := debug.LocationLists[varID]
554 if len(list) != 0 {
555 dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
556 debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym))
557 }
558 }
559 return dvar
560 }
561
562
563
564 func createHeapDerefLocationList(n *ir.Name, entryID ssa.ID) []ssa.LocListEntry {
565
566 heapPtrOffset := n.Heapaddr.FrameOffset()
567 if base.Ctxt.Arch.FixedFrameSize == 0 {
568 heapPtrOffset -= int64(types.PtrSize)
569 }
570 if buildcfg.FramePointerEnabled {
571 heapPtrOffset -= int64(types.PtrSize)
572 }
573
574
575 var expr []byte
576 expr = append(expr, dwarf.DW_OP_fbreg)
577 expr = dwarf.AppendSleb128(expr, heapPtrOffset)
578 expr = append(expr, dwarf.DW_OP_deref)
579
580 return []ssa.LocListEntry{{
581 StartBlock: entryID,
582 StartValue: ssa.BlockStart.ID,
583 EndBlock: entryID,
584 EndValue: ssa.FuncEnd.ID,
585 Expr: expr,
586 }}
587 }
588
589
590
591 func RecordFlags(flags ...string) {
592 if base.Ctxt.Pkgpath == "" {
593 base.Fatalf("missing pkgpath")
594 }
595
596 type BoolFlag interface {
597 IsBoolFlag() bool
598 }
599 type CountFlag interface {
600 IsCountFlag() bool
601 }
602 var cmd bytes.Buffer
603 for _, name := range flags {
604 f := flag.Lookup(name)
605 if f == nil {
606 continue
607 }
608 getter := f.Value.(flag.Getter)
609 if getter.String() == f.DefValue {
610
611 continue
612 }
613 if bf, ok := f.Value.(BoolFlag); ok && bf.IsBoolFlag() {
614 val, ok := getter.Get().(bool)
615 if ok && val {
616 fmt.Fprintf(&cmd, " -%s", f.Name)
617 continue
618 }
619 }
620 if cf, ok := f.Value.(CountFlag); ok && cf.IsCountFlag() {
621 val, ok := getter.Get().(int)
622 if ok && val == 1 {
623 fmt.Fprintf(&cmd, " -%s", f.Name)
624 continue
625 }
626 }
627 fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get())
628 }
629
630
631
632
633
634 if buildcfg.Experiment.RegabiArgs {
635 cmd.Write([]byte(" regabi"))
636 }
637
638 if cmd.Len() == 0 {
639 return
640 }
641 s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + base.Ctxt.Pkgpath)
642 s.Type = objabi.SDWARFCUINFO
643
644
645 s.Set(obj.AttrDuplicateOK, true)
646 base.Ctxt.Data = append(base.Ctxt.Data, s)
647 s.P = cmd.Bytes()[1:]
648 }
649
650
651
652 func RecordPackageName() {
653 s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + base.Ctxt.Pkgpath)
654 s.Type = objabi.SDWARFCUINFO
655
656
657 s.Set(obj.AttrDuplicateOK, true)
658 base.Ctxt.Data = append(base.Ctxt.Data, s)
659 s.P = []byte(types.LocalPkg.Name)
660 }
661
662
663
664
665 func shouldEmitDwarfVar(n *ir.Name) bool {
666 if ir.IsAutoTmp(n) {
667 return false
668 }
669 return shouldEmitDwarfVarSafe(n)
670 }
671
672
673
674
675
676
677 func shouldEmitDwarfVarSafe(n *ir.Name) bool {
678 if !ssa.IsVarWantedForDebug(n) {
679 return false
680 }
681 if n.Sym().Name == "_" {
682 return false
683 }
684 if n.Type().IsUntyped() {
685 return false
686 }
687 return true
688 }
689
690 func closureOffset(n *ir.Name, closureVars map[*ir.Name]int64) int64 {
691 return closureVars[n]
692 }
693
View as plain text