Файловый менеджер - Редактировать - /var/www/html/dwarfgen.zip
Ðазад
PK ! �g�d d scope.gonu �[��� // Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package dwarfgen import ( "sort" "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/internal/dwarf" "cmd/internal/obj" "cmd/internal/src" ) // See golang.org/issue/20390. func xposBefore(p, q src.XPos) bool { return base.Ctxt.PosTable.Pos(p).Before(base.Ctxt.PosTable.Pos(q)) } func findScope(marks []ir.Mark, pos src.XPos) ir.ScopeID { i := sort.Search(len(marks), func(i int) bool { return xposBefore(pos, marks[i].Pos) }) if i == 0 { return 0 } return marks[i-1].Scope } func assembleScopes(fnsym *obj.LSym, fn *ir.Func, dwarfVars []*dwarf.Var, varScopes []ir.ScopeID) []dwarf.Scope { // Initialize the DWARF scope tree based on lexical scopes. dwarfScopes := make([]dwarf.Scope, 1+len(fn.Parents)) for i, parent := range fn.Parents { dwarfScopes[i+1].Parent = int32(parent) } scopeVariables(dwarfVars, varScopes, dwarfScopes, fnsym.ABI() != obj.ABI0) if fnsym.Func().Text != nil { scopePCs(fnsym, fn.Marks, dwarfScopes) } return compactScopes(dwarfScopes) } // scopeVariables assigns DWARF variable records to their scopes. func scopeVariables(dwarfVars []*dwarf.Var, varScopes []ir.ScopeID, dwarfScopes []dwarf.Scope, regabi bool) { if regabi { sort.Stable(varsByScope{dwarfVars, varScopes}) } else { sort.Stable(varsByScopeAndOffset{dwarfVars, varScopes}) } i0 := 0 for i := range dwarfVars { if varScopes[i] == varScopes[i0] { continue } dwarfScopes[varScopes[i0]].Vars = dwarfVars[i0:i] i0 = i } if i0 < len(dwarfVars) { dwarfScopes[varScopes[i0]].Vars = dwarfVars[i0:] } } // scopePCs assigns PC ranges to their scopes. func scopePCs(fnsym *obj.LSym, marks []ir.Mark, dwarfScopes []dwarf.Scope) { // If there aren't any child scopes (in particular, when scope // tracking is disabled), we can skip a whole lot of work. if len(marks) == 0 { return } p0 := fnsym.Func().Text scope := findScope(marks, p0.Pos) for p := p0; p != nil; p = p.Link { if p.Pos == p0.Pos { continue } dwarfScopes[scope].AppendRange(dwarf.Range{Start: p0.Pc, End: p.Pc}) p0 = p scope = findScope(marks, p0.Pos) } if p0.Pc < fnsym.Size { dwarfScopes[scope].AppendRange(dwarf.Range{Start: p0.Pc, End: fnsym.Size}) } } func compactScopes(dwarfScopes []dwarf.Scope) []dwarf.Scope { // Reverse pass to propagate PC ranges to parent scopes. for i := len(dwarfScopes) - 1; i > 0; i-- { s := &dwarfScopes[i] dwarfScopes[s.Parent].UnifyRanges(s) } return dwarfScopes } type varsByScopeAndOffset struct { vars []*dwarf.Var scopes []ir.ScopeID } func (v varsByScopeAndOffset) Len() int { return len(v.vars) } func (v varsByScopeAndOffset) Less(i, j int) bool { if v.scopes[i] != v.scopes[j] { return v.scopes[i] < v.scopes[j] } return v.vars[i].StackOffset < v.vars[j].StackOffset } func (v varsByScopeAndOffset) Swap(i, j int) { v.vars[i], v.vars[j] = v.vars[j], v.vars[i] v.scopes[i], v.scopes[j] = v.scopes[j], v.scopes[i] } type varsByScope struct { vars []*dwarf.Var scopes []ir.ScopeID } func (v varsByScope) Len() int { return len(v.vars) } func (v varsByScope) Less(i, j int) bool { return v.scopes[i] < v.scopes[j] } func (v varsByScope) Swap(i, j int) { v.vars[i], v.vars[j] = v.vars[j], v.vars[i] v.scopes[i], v.scopes[j] = v.scopes[j], v.scopes[i] } PK ! B� $x; x; scope_test.gonu �[��� // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package dwarfgen import ( "debug/dwarf" "fmt" "internal/platform" "internal/testenv" "os" "path/filepath" "runtime" "sort" "strconv" "strings" "testing" "cmd/internal/objfile" ) type testline struct { // line is one line of go source line string // scopes is a list of scope IDs of all the lexical scopes that this line // of code belongs to. // Scope IDs are assigned by traversing the tree of lexical blocks of a // function in pre-order // Scope IDs are function specific, i.e. scope 0 is always the root scope // of the function that this line belongs to. Empty scopes are not assigned // an ID (because they are not saved in debug_info). // Scope 0 is always omitted from this list since all lines always belong // to it. scopes []int // vars is the list of variables that belong in scopes[len(scopes)-1]. // Local variables are prefixed with "var ", formal parameters with "arg ". // Must be ordered alphabetically. // Set to nil to skip the check. vars []string // decl is the list of variables declared at this line. decl []string // declBefore is the list of variables declared at or before this line. declBefore []string } var testfile = []testline{ {line: "package main"}, {line: "var sink any"}, {line: "func f1(x int) { }"}, {line: "func f2(x int) { }"}, {line: "func f3(x int) { }"}, {line: "func f4(x int) { }"}, {line: "func f5(x int) { }"}, {line: "func f6(x int) { }"}, {line: "func leak(x interface{}) { sink = x }"}, {line: "func gret1() int { return 2 }"}, {line: "func gretbool() bool { return true }"}, {line: "func gret3() (int, int, int) { return 0, 1, 2 }"}, {line: "var v = []int{ 0, 1, 2 }"}, {line: "var ch = make(chan int)"}, {line: "var floatch = make(chan float64)"}, {line: "var iface interface{}"}, {line: "func TestNestedFor() {", vars: []string{"var a int"}}, {line: " a := 0", decl: []string{"a"}}, {line: " f1(a)"}, {line: " for i := 0; i < 5; i++ {", scopes: []int{1}, vars: []string{"var i int"}, decl: []string{"i"}}, {line: " f2(i)", scopes: []int{1}}, {line: " for i := 0; i < 5; i++ {", scopes: []int{1, 2}, vars: []string{"var i int"}, decl: []string{"i"}}, {line: " f3(i)", scopes: []int{1, 2}}, {line: " }"}, {line: " f4(i)", scopes: []int{1}}, {line: " }"}, {line: " f5(a)"}, {line: "}"}, {line: "func TestOas2() {", vars: []string{}}, {line: " if a, b, c := gret3(); a != 1 {", scopes: []int{1}, vars: []string{"var a int", "var b int", "var c int"}}, {line: " f1(a)", scopes: []int{1}}, {line: " f1(b)", scopes: []int{1}}, {line: " f1(c)", scopes: []int{1}}, {line: " }"}, {line: " for i, x := range v {", scopes: []int{2}, vars: []string{"var i int", "var x int"}}, {line: " f1(i)", scopes: []int{2}}, {line: " f1(x)", scopes: []int{2}}, {line: " }"}, {line: " if a, ok := <- ch; ok {", scopes: []int{3}, vars: []string{"var a int", "var ok bool"}}, {line: " f1(a)", scopes: []int{3}}, {line: " }"}, {line: " if a, ok := iface.(int); ok {", scopes: []int{4}, vars: []string{"var a int", "var ok bool"}}, {line: " f1(a)", scopes: []int{4}}, {line: " }"}, {line: "}"}, {line: "func TestIfElse() {"}, {line: " if x := gret1(); x != 0 {", scopes: []int{1}, vars: []string{"var x int"}}, {line: " a := 0", scopes: []int{1, 2}, vars: []string{"var a int"}}, {line: " f1(a); f1(x)", scopes: []int{1, 2}}, {line: " } else {"}, {line: " b := 1", scopes: []int{1, 3}, vars: []string{"var b int"}}, {line: " f1(b); f1(x+1)", scopes: []int{1, 3}}, {line: " }"}, {line: "}"}, {line: "func TestSwitch() {", vars: []string{}}, {line: " switch x := gret1(); x {", scopes: []int{1}, vars: []string{"var x int"}}, {line: " case 0:", scopes: []int{1, 2}}, {line: " i := x + 5", scopes: []int{1, 2}, vars: []string{"var i int"}}, {line: " f1(x); f1(i)", scopes: []int{1, 2}}, {line: " case 1:", scopes: []int{1, 3}}, {line: " j := x + 10", scopes: []int{1, 3}, vars: []string{"var j int"}}, {line: " f1(x); f1(j)", scopes: []int{1, 3}}, {line: " case 2:", scopes: []int{1, 4}}, {line: " k := x + 2", scopes: []int{1, 4}, vars: []string{"var k int"}}, {line: " f1(x); f1(k)", scopes: []int{1, 4}}, {line: " }"}, {line: "}"}, {line: "func TestTypeSwitch() {", vars: []string{}}, {line: " switch x := iface.(type) {"}, {line: " case int:", scopes: []int{1}}, {line: " f1(x)", scopes: []int{1}, vars: []string{"var x int"}}, {line: " case uint8:", scopes: []int{2}}, {line: " f1(int(x))", scopes: []int{2}, vars: []string{"var x uint8"}}, {line: " case float64:", scopes: []int{3}}, {line: " f1(int(x)+1)", scopes: []int{3}, vars: []string{"var x float64"}}, {line: " }"}, {line: "}"}, {line: "func TestSelectScope() {"}, {line: " select {"}, {line: " case i := <- ch:", scopes: []int{1}}, {line: " f1(i)", scopes: []int{1}, vars: []string{"var i int"}}, {line: " case f := <- floatch:", scopes: []int{2}}, {line: " f1(int(f))", scopes: []int{2}, vars: []string{"var f float64"}}, {line: " }"}, {line: "}"}, {line: "func TestBlock() {", vars: []string{"var a int"}}, {line: " a := 1"}, {line: " {"}, {line: " b := 2", scopes: []int{1}, vars: []string{"var b int"}}, {line: " f1(b)", scopes: []int{1}}, {line: " f1(a)", scopes: []int{1}}, {line: " }"}, {line: "}"}, {line: "func TestDiscontiguousRanges() {", vars: []string{"var a int"}}, {line: " a := 0"}, {line: " f1(a)"}, {line: " {"}, {line: " b := 0", scopes: []int{1}, vars: []string{"var b int"}}, {line: " f2(b)", scopes: []int{1}}, {line: " if gretbool() {", scopes: []int{1}}, {line: " c := 0", scopes: []int{1, 2}, vars: []string{"var c int"}}, {line: " f3(c)", scopes: []int{1, 2}}, {line: " } else {"}, {line: " c := 1.1", scopes: []int{1, 3}, vars: []string{"var c float64"}}, {line: " f4(int(c))", scopes: []int{1, 3}}, {line: " }"}, {line: " f5(b)", scopes: []int{1}}, {line: " }"}, {line: " f6(a)"}, {line: "}"}, {line: "func TestClosureScope() {", vars: []string{"var a int", "var b int", "var f func(int)"}}, {line: " a := 1; b := 1"}, {line: " f := func(c int) {", scopes: []int{0}, vars: []string{"arg c int", "var &b *int", "var a int", "var d int"}, declBefore: []string{"&b", "a"}}, {line: " d := 3"}, {line: " f1(c); f1(d)"}, {line: " if e := 3; e != 0 {", scopes: []int{1}, vars: []string{"var e int"}}, {line: " f1(e)", scopes: []int{1}}, {line: " f1(a)", scopes: []int{1}}, {line: " b = 2", scopes: []int{1}}, {line: " }"}, {line: " }"}, {line: " f(3); f1(b)"}, {line: "}"}, {line: "func TestEscape() {"}, {line: " a := 1", vars: []string{"var a int"}}, {line: " {"}, {line: " b := 2", scopes: []int{1}, vars: []string{"var &b *int", "var p *int"}}, {line: " p := &b", scopes: []int{1}}, {line: " f1(a)", scopes: []int{1}}, {line: " leak(p)", scopes: []int{1}}, {line: " }"}, {line: "}"}, {line: "var fglob func() int"}, {line: "func TestCaptureVar(flag bool) {"}, {line: " a := 1", vars: []string{"arg flag bool", "var a int"}}, // TODO(register args) restore "arg ~r1 func() int", {line: " if flag {"}, {line: " b := 2", scopes: []int{1}, vars: []string{"var b int", "var f func() int"}}, {line: " f := func() int {", scopes: []int{1, 0}}, {line: " return b + 1"}, {line: " }"}, {line: " fglob = f", scopes: []int{1}}, {line: " }"}, {line: " f1(a)"}, {line: "}"}, {line: "func main() {"}, {line: " TestNestedFor()"}, {line: " TestOas2()"}, {line: " TestIfElse()"}, {line: " TestSwitch()"}, {line: " TestTypeSwitch()"}, {line: " TestSelectScope()"}, {line: " TestBlock()"}, {line: " TestDiscontiguousRanges()"}, {line: " TestClosureScope()"}, {line: " TestEscape()"}, {line: " TestCaptureVar(true)"}, {line: "}"}, } const detailOutput = false // Compiles testfile checks that the description of lexical blocks emitted // by the linker in debug_info, for each function in the main package, // corresponds to what we expect it to be. func TestScopeRanges(t *testing.T) { testenv.MustHaveGoBuild(t) t.Parallel() if !platform.ExecutableHasDWARF(runtime.GOOS, runtime.GOARCH) { t.Skipf("skipping on %s/%s: no DWARF symbol table in executables", runtime.GOOS, runtime.GOARCH) } src, f := gobuild(t, t.TempDir(), false, testfile) defer f.Close() // the compiler uses forward slashes for paths even on windows src = strings.Replace(src, "\\", "/", -1) pcln, err := f.PCLineTable() if err != nil { t.Fatal(err) } dwarfData, err := f.DWARF() if err != nil { t.Fatal(err) } dwarfReader := dwarfData.Reader() lines := make(map[line][]*lexblock) for { entry, err := dwarfReader.Next() if err != nil { t.Fatal(err) } if entry == nil { break } if entry.Tag != dwarf.TagSubprogram { continue } name, ok := entry.Val(dwarf.AttrName).(string) if !ok || !strings.HasPrefix(name, "main.Test") { continue } var scope lexblock ctxt := scopexplainContext{ dwarfData: dwarfData, dwarfReader: dwarfReader, scopegen: 1, } readScope(&ctxt, &scope, entry) scope.markLines(pcln, lines) } anyerror := false for i := range testfile { tgt := testfile[i].scopes out := lines[line{src, i + 1}] if detailOutput { t.Logf("%s // %v", testfile[i].line, out) } scopesok := checkScopes(tgt, out) if !scopesok { t.Logf("mismatch at line %d %q: expected: %v got: %v\n", i, testfile[i].line, tgt, scopesToString(out)) } varsok := true if testfile[i].vars != nil { if len(out) > 0 { varsok = checkVars(testfile[i].vars, out[len(out)-1].vars) if !varsok { t.Logf("variable mismatch at line %d %q for scope %d: expected: %v got: %v\n", i+1, testfile[i].line, out[len(out)-1].id, testfile[i].vars, out[len(out)-1].vars) } for j := range testfile[i].decl { if line := declLineForVar(out[len(out)-1].vars, testfile[i].decl[j]); line != i+1 { t.Errorf("wrong declaration line for variable %s, expected %d got: %d", testfile[i].decl[j], i+1, line) } } for j := range testfile[i].declBefore { if line := declLineForVar(out[len(out)-1].vars, testfile[i].declBefore[j]); line > i+1 { t.Errorf("wrong declaration line for variable %s, expected %d (or less) got: %d", testfile[i].declBefore[j], i+1, line) } } } } anyerror = anyerror || !scopesok || !varsok } if anyerror { t.Fatalf("mismatched output") } } func scopesToString(v []*lexblock) string { r := make([]string, len(v)) for i, s := range v { r[i] = strconv.Itoa(s.id) } return "[ " + strings.Join(r, ", ") + " ]" } func checkScopes(tgt []int, out []*lexblock) bool { if len(out) > 0 { // omit scope 0 out = out[1:] } if len(tgt) != len(out) { return false } for i := range tgt { if tgt[i] != out[i].id { return false } } return true } func checkVars(tgt []string, out []variable) bool { if len(tgt) != len(out) { return false } for i := range tgt { if tgt[i] != out[i].expr { return false } } return true } func declLineForVar(scope []variable, name string) int { for i := range scope { if scope[i].name() == name { return scope[i].declLine } } return -1 } type lexblock struct { id int ranges [][2]uint64 vars []variable scopes []lexblock } type variable struct { expr string declLine int } func (v *variable) name() string { return strings.Split(v.expr, " ")[1] } type line struct { file string lineno int } type scopexplainContext struct { dwarfData *dwarf.Data dwarfReader *dwarf.Reader scopegen int } // readScope reads the DW_TAG_lexical_block or the DW_TAG_subprogram in // entry and writes a description in scope. // Nested DW_TAG_lexical_block entries are read recursively. func readScope(ctxt *scopexplainContext, scope *lexblock, entry *dwarf.Entry) { var err error scope.ranges, err = ctxt.dwarfData.Ranges(entry) if err != nil { panic(err) } for { e, err := ctxt.dwarfReader.Next() if err != nil { panic(err) } switch e.Tag { case 0: sort.Slice(scope.vars, func(i, j int) bool { return scope.vars[i].expr < scope.vars[j].expr }) return case dwarf.TagFormalParameter: typ, err := ctxt.dwarfData.Type(e.Val(dwarf.AttrType).(dwarf.Offset)) if err != nil { panic(err) } scope.vars = append(scope.vars, entryToVar(e, "arg", typ)) case dwarf.TagVariable: typ, err := ctxt.dwarfData.Type(e.Val(dwarf.AttrType).(dwarf.Offset)) if err != nil { panic(err) } scope.vars = append(scope.vars, entryToVar(e, "var", typ)) case dwarf.TagLexDwarfBlock: scope.scopes = append(scope.scopes, lexblock{id: ctxt.scopegen}) ctxt.scopegen++ readScope(ctxt, &scope.scopes[len(scope.scopes)-1], e) } } } func entryToVar(e *dwarf.Entry, kind string, typ dwarf.Type) variable { return variable{ fmt.Sprintf("%s %s %s", kind, e.Val(dwarf.AttrName).(string), typ.String()), int(e.Val(dwarf.AttrDeclLine).(int64)), } } // markLines marks all lines that belong to this scope with this scope // Recursively calls markLines for all children scopes. func (scope *lexblock) markLines(pcln objfile.Liner, lines map[line][]*lexblock) { for _, r := range scope.ranges { for pc := r[0]; pc < r[1]; pc++ { file, lineno, _ := pcln.PCToLine(pc) l := line{file, lineno} if len(lines[l]) == 0 || lines[l][len(lines[l])-1] != scope { lines[l] = append(lines[l], scope) } } } for i := range scope.scopes { scope.scopes[i].markLines(pcln, lines) } } func gobuild(t *testing.T, dir string, optimized bool, testfile []testline) (string, *objfile.File) { src := filepath.Join(dir, "test.go") dst := filepath.Join(dir, "out.o") f, err := os.Create(src) if err != nil { t.Fatal(err) } for i := range testfile { f.Write([]byte(testfile[i].line)) f.Write([]byte{'\n'}) } f.Close() args := []string{"build"} if !optimized { args = append(args, "-gcflags=-N -l") } args = append(args, "-o", dst, src) cmd := testenv.Command(t, testenv.GoToolPath(t), args...) if b, err := cmd.CombinedOutput(); err != nil { t.Logf("build: %s\n", string(b)) t.Fatal(err) } pkg, err := objfile.Open(dst) if err != nil { t.Fatal(err) } return src, pkg } // TestEmptyDwarfRanges tests that no list entry in debug_ranges has start == end. // See issue #23928. func TestEmptyDwarfRanges(t *testing.T) { testenv.MustHaveGoRun(t) t.Parallel() if !platform.ExecutableHasDWARF(runtime.GOOS, runtime.GOARCH) { t.Skipf("skipping on %s/%s: no DWARF symbol table in executables", runtime.GOOS, runtime.GOARCH) } _, f := gobuild(t, t.TempDir(), true, []testline{{line: "package main"}, {line: "func main(){ println(\"hello\") }"}}) defer f.Close() dwarfData, err := f.DWARF() if err != nil { t.Fatal(err) } dwarfReader := dwarfData.Reader() for { entry, err := dwarfReader.Next() if err != nil { t.Fatal(err) } if entry == nil { break } ranges, err := dwarfData.Ranges(entry) if err != nil { t.Fatal(err) } if ranges == nil { continue } for _, rng := range ranges { if rng[0] == rng[1] { t.Errorf("range entry with start == end: %v", rng) } } } } PK ! GGQ�� � marker.gonu �[��� // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package dwarfgen import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/internal/src" ) // A ScopeMarker tracks scope nesting and boundaries for later use // during DWARF generation. type ScopeMarker struct { parents []ir.ScopeID marks []ir.Mark } // checkPos validates the given position and returns the current scope. func (m *ScopeMarker) checkPos(pos src.XPos) ir.ScopeID { if !pos.IsKnown() { base.Fatalf("unknown scope position") } if len(m.marks) == 0 { return 0 } last := &m.marks[len(m.marks)-1] if xposBefore(pos, last.Pos) { base.FatalfAt(pos, "non-monotonic scope positions\n\t%v: previous scope position", base.FmtPos(last.Pos)) } return last.Scope } // Push records a transition to a new child scope of the current scope. func (m *ScopeMarker) Push(pos src.XPos) { current := m.checkPos(pos) m.parents = append(m.parents, current) child := ir.ScopeID(len(m.parents)) m.marks = append(m.marks, ir.Mark{Pos: pos, Scope: child}) } // Pop records a transition back to the current scope's parent. func (m *ScopeMarker) Pop(pos src.XPos) { current := m.checkPos(pos) parent := m.parents[current-1] m.marks = append(m.marks, ir.Mark{Pos: pos, Scope: parent}) } // Unpush removes the current scope, which must be empty. func (m *ScopeMarker) Unpush() { i := len(m.marks) - 1 current := m.marks[i].Scope if current != ir.ScopeID(len(m.parents)) { base.FatalfAt(m.marks[i].Pos, "current scope is not empty") } m.parents = m.parents[:current-1] m.marks = m.marks[:i] } // WriteTo writes the recorded scope marks to the given function, // and resets the marker for reuse. func (m *ScopeMarker) WriteTo(fn *ir.Func) { m.compactMarks() fn.Parents = make([]ir.ScopeID, len(m.parents)) copy(fn.Parents, m.parents) m.parents = m.parents[:0] fn.Marks = make([]ir.Mark, len(m.marks)) copy(fn.Marks, m.marks) m.marks = m.marks[:0] } func (m *ScopeMarker) compactMarks() { n := 0 for _, next := range m.marks { if n > 0 && next.Pos == m.marks[n-1].Pos { m.marks[n-1].Scope = next.Scope continue } m.marks[n] = next n++ } m.marks = m.marks[:n] } PK ! D�}Q3 Q3 dwinl.gonu �[��� // Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package dwarfgen import ( "fmt" "strings" "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/internal/dwarf" "cmd/internal/obj" "cmd/internal/src" ) // To identify variables by original source position. type varPos struct { DeclName string DeclFile string DeclLine uint DeclCol uint } // This is the main entry point for collection of raw material to // drive generation of DWARF "inlined subroutine" DIEs. See proposal // 22080 for more details and background info. func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls { var inlcalls dwarf.InlCalls if base.Debug.DwarfInl != 0 { base.Ctxt.Logf("assembling DWARF inlined routine info for %v\n", fnsym.Name) } // This maps inline index (from Ctxt.InlTree) to index in inlcalls.Calls imap := make(map[int]int) // Walk progs to build up the InlCalls data structure var prevpos src.XPos for p := fnsym.Func().Text; p != nil; p = p.Link { if p.Pos == prevpos { continue } ii := posInlIndex(p.Pos) if ii >= 0 { insertInlCall(&inlcalls, ii, imap) } prevpos = p.Pos } // This is used to partition DWARF vars by inline index. Vars not // produced by the inliner will wind up in the vmap[0] entry. vmap := make(map[int32][]*dwarf.Var) // Now walk the dwarf vars and partition them based on whether they // were produced by the inliner (dwv.InlIndex > 0) or were original // vars/params from the function (dwv.InlIndex == 0). for _, dwv := range dwVars { vmap[dwv.InlIndex] = append(vmap[dwv.InlIndex], dwv) // Zero index => var was not produced by an inline if dwv.InlIndex == 0 { continue } // Look up index in our map, then tack the var in question // onto the vars list for the correct inlined call. ii := int(dwv.InlIndex) - 1 idx, ok := imap[ii] if !ok { // We can occasionally encounter a var produced by the // inliner for which there is no remaining prog; add a new // entry to the call list in this scenario. idx = insertInlCall(&inlcalls, ii, imap) } inlcalls.Calls[idx].InlVars = append(inlcalls.Calls[idx].InlVars, dwv) } // Post process the map above to assign child indices to vars. // // A given variable is treated differently depending on whether it // is part of the top-level function (ii == 0) or if it was // produced as a result of an inline (ii != 0). // // If a variable was not produced by an inline and its containing // function was not inlined, then we just assign an ordering of // based on variable name. // // If a variable was not produced by an inline and its containing // function was inlined, then we need to assign a child index // based on the order of vars in the abstract function (in // addition, those vars that don't appear in the abstract // function, such as "~r1", are flagged as such). // // If a variable was produced by an inline, then we locate it in // the pre-inlining decls for the target function and assign child // index accordingly. for ii, sl := range vmap { var m map[varPos]int if ii == 0 { if !fnsym.WasInlined() { for j, v := range sl { v.ChildIndex = int32(j) } continue } m = makePreinlineDclMap(fnsym) } else { ifnlsym := base.Ctxt.InlTree.InlinedFunction(int(ii - 1)) m = makePreinlineDclMap(ifnlsym) } // Here we assign child indices to variables based on // pre-inlined decls, and set the "IsInAbstract" flag // appropriately. In addition: parameter and local variable // names are given "middle dot" version numbers as part of the // writing them out to export data (see issue 4326). If DWARF // inlined routine generation is turned on, we want to undo // this versioning, since DWARF variables in question will be // parented by the inlined routine and not the top-level // caller. synthCount := len(m) for _, v := range sl { vp := varPos{ DeclName: v.Name, DeclFile: v.DeclFile, DeclLine: v.DeclLine, DeclCol: v.DeclCol, } synthesized := strings.HasPrefix(v.Name, "~") || v.Name == "_" if idx, found := m[vp]; found { v.ChildIndex = int32(idx) v.IsInAbstract = !synthesized } else { // Variable can't be found in the pre-inline dcl list. // In the top-level case (ii=0) this can happen // because a composite variable was split into pieces, // and we're looking at a piece. We can also see // return temps (~r%d) that were created during // lowering, or unnamed params ("_"). v.ChildIndex = int32(synthCount) synthCount++ } } } // Make a second pass through the progs to compute PC ranges for // the various inlined calls. start := int64(-1) curii := -1 var prevp *obj.Prog for p := fnsym.Func().Text; p != nil; prevp, p = p, p.Link { if prevp != nil && p.Pos == prevp.Pos { continue } ii := posInlIndex(p.Pos) if ii == curii { continue } // Close out the current range if start != -1 { addRange(inlcalls.Calls, start, p.Pc, curii, imap) } // Begin new range start = p.Pc curii = ii } if start != -1 { addRange(inlcalls.Calls, start, fnsym.Size, curii, imap) } // Issue 33188: if II foo is a child of II bar, then ensure that // bar's ranges include the ranges of foo (the loop above will produce // disjoint ranges). for k, c := range inlcalls.Calls { if c.Root { unifyCallRanges(inlcalls, k) } } // Debugging if base.Debug.DwarfInl != 0 { dumpInlCalls(inlcalls) dumpInlVars(dwVars) } // Perform a consistency check on inlined routine PC ranges // produced by unifyCallRanges above. In particular, complain in // cases where you have A -> B -> C (e.g. C is inlined into B, and // B is inlined into A) and the ranges for B are not enclosed // within the ranges for A, or C within B. for k, c := range inlcalls.Calls { if c.Root { checkInlCall(fnsym.Name, inlcalls, fnsym.Size, k, -1) } } return inlcalls } // Secondary hook for DWARF inlined subroutine generation. This is called // late in the compilation when it is determined that we need an // abstract function DIE for an inlined routine imported from a // previously compiled package. func AbstractFunc(fn *obj.LSym) { ifn := base.Ctxt.DwFixups.GetPrecursorFunc(fn) if ifn == nil { base.Ctxt.Diag("failed to locate precursor fn for %v", fn) return } _ = ifn.(*ir.Func) if base.Debug.DwarfInl != 0 { base.Ctxt.Logf("DwarfAbstractFunc(%v)\n", fn.Name) } base.Ctxt.DwarfAbstractFunc(ifn, fn) } // Given a function that was inlined as part of the compilation, dig // up the pre-inlining DCL list for the function and create a map that // supports lookup of pre-inline dcl index, based on variable // position/name. NB: the recipe for computing variable pos/file/line // needs to be kept in sync with the similar code in gc.createSimpleVars // and related functions. func makePreinlineDclMap(fnsym *obj.LSym) map[varPos]int { dcl := preInliningDcls(fnsym) m := make(map[varPos]int) for i, n := range dcl { pos := base.Ctxt.InnermostPos(n.Pos()) vp := varPos{ DeclName: n.Sym().Name, DeclFile: pos.RelFilename(), DeclLine: pos.RelLine(), DeclCol: pos.RelCol(), } if _, found := m[vp]; found { // We can see collisions (variables with the same name/file/line/col) in obfuscated or machine-generated code -- see issue 44378 for an example. Skip duplicates in such cases, since it is unlikely that a human will be debugging such code. continue } m[vp] = i } return m } func insertInlCall(dwcalls *dwarf.InlCalls, inlIdx int, imap map[int]int) int { callIdx, found := imap[inlIdx] if found { return callIdx } // Haven't seen this inline yet. Visit parent of inline if there // is one. We do this first so that parents appear before their // children in the resulting table. parCallIdx := -1 parInlIdx := base.Ctxt.InlTree.Parent(inlIdx) if parInlIdx >= 0 { parCallIdx = insertInlCall(dwcalls, parInlIdx, imap) } // Create new entry for this inline inlinedFn := base.Ctxt.InlTree.InlinedFunction(inlIdx) callXPos := base.Ctxt.InlTree.CallPos(inlIdx) callPos := base.Ctxt.InnermostPos(callXPos) absFnSym := base.Ctxt.DwFixups.AbsFuncDwarfSym(inlinedFn) ic := dwarf.InlCall{ InlIndex: inlIdx, CallPos: callPos, AbsFunSym: absFnSym, Root: parCallIdx == -1, } dwcalls.Calls = append(dwcalls.Calls, ic) callIdx = len(dwcalls.Calls) - 1 imap[inlIdx] = callIdx if parCallIdx != -1 { // Add this inline to parent's child list dwcalls.Calls[parCallIdx].Children = append(dwcalls.Calls[parCallIdx].Children, callIdx) } return callIdx } // Given a src.XPos, return its associated inlining index if it // corresponds to something created as a result of an inline, or -1 if // there is no inline info. Note that the index returned will refer to // the deepest call in the inlined stack, e.g. if you have "A calls B // calls C calls D" and all three callees are inlined (B, C, and D), // the index for a node from the inlined body of D will refer to the // call to D from C. Whew. func posInlIndex(xpos src.XPos) int { pos := base.Ctxt.PosTable.Pos(xpos) if b := pos.Base(); b != nil { ii := b.InliningIndex() if ii >= 0 { return ii } } return -1 } func addRange(calls []dwarf.InlCall, start, end int64, ii int, imap map[int]int) { if start == -1 { panic("bad range start") } if end == -1 { panic("bad range end") } if ii == -1 { return } if start == end { return } // Append range to correct inlined call callIdx, found := imap[ii] if !found { base.Fatalf("can't find inlIndex %d in imap for prog at %d\n", ii, start) } call := &calls[callIdx] call.Ranges = append(call.Ranges, dwarf.Range{Start: start, End: end}) } func dumpInlCall(inlcalls dwarf.InlCalls, idx, ilevel int) { for i := 0; i < ilevel; i++ { base.Ctxt.Logf(" ") } ic := inlcalls.Calls[idx] callee := base.Ctxt.InlTree.InlinedFunction(ic.InlIndex) base.Ctxt.Logf(" %d: II:%d (%s) V: (", idx, ic.InlIndex, callee.Name) for _, f := range ic.InlVars { base.Ctxt.Logf(" %v", f.Name) } base.Ctxt.Logf(" ) C: (") for _, k := range ic.Children { base.Ctxt.Logf(" %v", k) } base.Ctxt.Logf(" ) R:") for _, r := range ic.Ranges { base.Ctxt.Logf(" [%d,%d)", r.Start, r.End) } base.Ctxt.Logf("\n") for _, k := range ic.Children { dumpInlCall(inlcalls, k, ilevel+1) } } func dumpInlCalls(inlcalls dwarf.InlCalls) { for k, c := range inlcalls.Calls { if c.Root { dumpInlCall(inlcalls, k, 0) } } } func dumpInlVars(dwvars []*dwarf.Var) { for i, dwv := range dwvars { typ := "local" if dwv.Abbrev == dwarf.DW_ABRV_PARAM_LOCLIST || dwv.Abbrev == dwarf.DW_ABRV_PARAM { typ = "param" } ia := 0 if dwv.IsInAbstract { ia = 1 } base.Ctxt.Logf("V%d: %s CI:%d II:%d IA:%d %s\n", i, dwv.Name, dwv.ChildIndex, dwv.InlIndex-1, ia, typ) } } func rangesContains(par []dwarf.Range, rng dwarf.Range) (bool, string) { for _, r := range par { if rng.Start >= r.Start && rng.End <= r.End { return true, "" } } msg := fmt.Sprintf("range [%d,%d) not contained in {", rng.Start, rng.End) for _, r := range par { msg += fmt.Sprintf(" [%d,%d)", r.Start, r.End) } msg += " }" return false, msg } func rangesContainsAll(parent, child []dwarf.Range) (bool, string) { for _, r := range child { c, m := rangesContains(parent, r) if !c { return false, m } } return true, "" } // checkInlCall verifies that the PC ranges for inline info 'idx' are // enclosed/contained within the ranges of its parent inline (or if // this is a root/toplevel inline, checks that the ranges fall within // the extent of the top level function). A panic is issued if a // malformed range is found. func checkInlCall(funcName string, inlCalls dwarf.InlCalls, funcSize int64, idx, parentIdx int) { // Callee ic := inlCalls.Calls[idx] callee := base.Ctxt.InlTree.InlinedFunction(ic.InlIndex).Name calleeRanges := ic.Ranges // Caller caller := funcName parentRanges := []dwarf.Range{dwarf.Range{Start: int64(0), End: funcSize}} if parentIdx != -1 { pic := inlCalls.Calls[parentIdx] caller = base.Ctxt.InlTree.InlinedFunction(pic.InlIndex).Name parentRanges = pic.Ranges } // Callee ranges contained in caller ranges? c, m := rangesContainsAll(parentRanges, calleeRanges) if !c { base.Fatalf("** malformed inlined routine range in %s: caller %s callee %s II=%d %s\n", funcName, caller, callee, idx, m) } // Now visit kids for _, k := range ic.Children { checkInlCall(funcName, inlCalls, funcSize, k, idx) } } // unifyCallRanges ensures that the ranges for a given inline // transitively include all of the ranges for its child inlines. func unifyCallRanges(inlcalls dwarf.InlCalls, idx int) { ic := &inlcalls.Calls[idx] for _, childIdx := range ic.Children { // First make sure child ranges are unified. unifyCallRanges(inlcalls, childIdx) // Then merge child ranges into ranges for this inline. cic := inlcalls.Calls[childIdx] ic.Ranges = dwarf.MergeRanges(ic.Ranges, cic.Ranges) } } PK ! �CH��H �H dwarf.gonu �[��� // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package dwarfgen import ( "bytes" "flag" "fmt" "internal/buildcfg" "sort" "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/reflectdata" "cmd/compile/internal/ssa" "cmd/compile/internal/ssagen" "cmd/compile/internal/types" "cmd/internal/dwarf" "cmd/internal/obj" "cmd/internal/objabi" "cmd/internal/src" ) func Info(fnsym *obj.LSym, infosym *obj.LSym, curfn obj.Func) (scopes []dwarf.Scope, inlcalls dwarf.InlCalls) { fn := curfn.(*ir.Func) if fn.Nname != nil { expect := fn.Linksym() if fnsym.ABI() == obj.ABI0 { expect = fn.LinksymABI(obj.ABI0) } if fnsym != expect { base.Fatalf("unexpected fnsym: %v != %v", fnsym, expect) } } // Back when there were two different *Funcs for a function, this code // was not consistent about whether a particular *Node being processed // was an ODCLFUNC or ONAME node. Partly this is because inlined function // bodies have no ODCLFUNC node, which was it's own inconsistency. // In any event, the handling of the two different nodes for DWARF purposes // was subtly different, likely in unintended ways. CL 272253 merged the // two nodes' Func fields, so that code sees the same *Func whether it is // holding the ODCLFUNC or the ONAME. This resulted in changes in the // DWARF output. To preserve the existing DWARF output and leave an // intentional change for a future CL, this code does the following when // fn.Op == ONAME: // // 1. Disallow use of createComplexVars in createDwarfVars. // It was not possible to reach that code for an ONAME before, // because the DebugInfo was set only on the ODCLFUNC Func. // Calling into it in the ONAME case causes an index out of bounds panic. // // 2. Do not populate apdecls. fn.Func.Dcl was in the ODCLFUNC Func, // not the ONAME Func. Populating apdecls for the ONAME case results // in selected being populated after createSimpleVars is called in // createDwarfVars, and then that causes the loop to skip all the entries // in dcl, meaning that the RecordAutoType calls don't happen. // // These two adjustments keep toolstash -cmp working for now. // Deciding the right answer is, as they say, future work. // // We can tell the difference between the old ODCLFUNC and ONAME // cases by looking at the infosym.Name. If it's empty, DebugInfo is // being called from (*obj.Link).populateDWARF, which used to use // the ODCLFUNC. If it's non-empty (the name will end in $abstract), // DebugInfo is being called from (*obj.Link).DwarfAbstractFunc, // which used to use the ONAME form. isODCLFUNC := infosym.Name == "" var apdecls []*ir.Name // Populate decls for fn. if isODCLFUNC { for _, n := range fn.Dcl { if n.Op() != ir.ONAME { // might be OTYPE or OLITERAL continue } switch n.Class { case ir.PAUTO: if !n.Used() { // Text == nil -> generating abstract function if fnsym.Func().Text != nil { base.Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)") } continue } case ir.PPARAM, ir.PPARAMOUT: default: continue } apdecls = append(apdecls, n) if n.Type().Kind() == types.TSSA { // Can happen for TypeInt128 types. This only happens for // spill locations, so not a huge deal. continue } fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) } } decls, dwarfVars := createDwarfVars(fnsym, isODCLFUNC, fn, apdecls) // For each type referenced by the functions auto vars but not // already referenced by a dwarf var, attach an R_USETYPE relocation to // the function symbol to insure that the type included in DWARF // processing during linking. typesyms := []*obj.LSym{} for t := range fnsym.Func().Autot { typesyms = append(typesyms, t) } sort.Sort(obj.BySymName(typesyms)) for _, sym := range typesyms { r := obj.Addrel(infosym) r.Sym = sym r.Type = objabi.R_USETYPE } fnsym.Func().Autot = nil var varScopes []ir.ScopeID for _, decl := range decls { pos := declPos(decl) varScopes = append(varScopes, findScope(fn.Marks, pos)) } scopes = assembleScopes(fnsym, fn, dwarfVars, varScopes) if base.Flag.GenDwarfInl > 0 { inlcalls = assembleInlines(fnsym, dwarfVars) } return scopes, inlcalls } func declPos(decl *ir.Name) src.XPos { return decl.Canonical().Pos() } // createDwarfVars process fn, returning a list of DWARF variables and the // Nodes they represent. func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var) { // Collect a raw list of DWARF vars. var vars []*dwarf.Var var decls []*ir.Name var selected ir.NameSet if base.Ctxt.Flag_locationlists && base.Ctxt.Flag_optimize && fn.DebugInfo != nil && complexOK { decls, vars, selected = createComplexVars(fnsym, fn) } else if fn.ABI == obj.ABIInternal && base.Flag.N != 0 && complexOK { decls, vars, selected = createABIVars(fnsym, fn, apDecls) } else { decls, vars, selected = createSimpleVars(fnsym, apDecls) } if fn.DebugInfo != nil { // Recover zero sized variables eliminated by the stackframe pass for _, n := range fn.DebugInfo.(*ssa.FuncDebug).OptDcl { if n.Class != ir.PAUTO { continue } types.CalcSize(n.Type()) if n.Type().Size() == 0 { decls = append(decls, n) vars = append(vars, createSimpleVar(fnsym, n)) vars[len(vars)-1].StackOffset = 0 fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) } } } dcl := apDecls if fnsym.WasInlined() { dcl = preInliningDcls(fnsym) } else { // The backend's stackframe pass prunes away entries from the // fn's Dcl list, including PARAMOUT nodes that correspond to // output params passed in registers. Add back in these // entries here so that we can process them properly during // DWARF-gen. See issue 48573 for more details. debugInfo := fn.DebugInfo.(*ssa.FuncDebug) for _, n := range debugInfo.RegOutputParams { if n.Class != ir.PPARAMOUT || !n.IsOutputParamInRegisters() { panic("invalid ir.Name on debugInfo.RegOutputParams list") } dcl = append(dcl, n) } } // If optimization is enabled, the list above will typically be // missing some of the original pre-optimization variables in the // function (they may have been promoted to registers, folded into // constants, dead-coded away, etc). Input arguments not eligible // for SSA optimization are also missing. Here we add back in entries // for selected missing vars. Note that the recipe below creates a // conservative location. The idea here is that we want to // communicate to the user that "yes, there is a variable named X // in this function, but no, I don't have enough information to // reliably report its contents." // For non-SSA-able arguments, however, the correct information // is known -- they have a single home on the stack. for _, n := range dcl { if selected.Has(n) { continue } c := n.Sym().Name[0] if c == '.' || n.Type().IsUntyped() { continue } if n.Class == ir.PPARAM && !ssa.CanSSA(n.Type()) { // SSA-able args get location lists, and may move in and // out of registers, so those are handled elsewhere. // Autos and named output params seem to get handled // with VARDEF, which creates location lists. // Args not of SSA-able type are treated here; they // are homed on the stack in a single place for the // entire call. vars = append(vars, createSimpleVar(fnsym, n)) decls = append(decls, n) continue } typename := dwarf.InfoPrefix + types.TypeSymName(n.Type()) decls = append(decls, n) abbrev := dwarf.DW_ABRV_AUTO_LOCLIST isReturnValue := (n.Class == ir.PPARAMOUT) if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT { abbrev = dwarf.DW_ABRV_PARAM_LOCLIST } if n.Esc() == ir.EscHeap { // The variable in question has been promoted to the heap. // Its address is in n.Heapaddr. // TODO(thanm): generate a better location expression } inlIndex := 0 if base.Flag.GenDwarfInl > 1 { if n.InlFormal() || n.InlLocal() { inlIndex = posInlIndex(n.Pos()) + 1 if n.InlFormal() { abbrev = dwarf.DW_ABRV_PARAM_LOCLIST } } } declpos := base.Ctxt.InnermostPos(n.Pos()) vars = append(vars, &dwarf.Var{ Name: n.Sym().Name, IsReturnValue: isReturnValue, Abbrev: abbrev, StackOffset: int32(n.FrameOffset()), Type: base.Ctxt.Lookup(typename), DeclFile: declpos.RelFilename(), DeclLine: declpos.RelLine(), DeclCol: declpos.RelCol(), InlIndex: int32(inlIndex), ChildIndex: -1, DictIndex: n.DictIndex, }) // Record go type of to insure that it gets emitted by the linker. fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) } // Sort decls and vars. sortDeclsAndVars(fn, decls, vars) return decls, vars } // sortDeclsAndVars sorts the decl and dwarf var lists according to // parameter declaration order, so as to insure that when a subprogram // DIE is emitted, its parameter children appear in declaration order. // Prior to the advent of the register ABI, sorting by frame offset // would achieve this; with the register we now need to go back to the // original function signature. func sortDeclsAndVars(fn *ir.Func, decls []*ir.Name, vars []*dwarf.Var) { paramOrder := make(map[*ir.Name]int) idx := 1 for _, f := range fn.Type().RecvParamsResults() { if n, ok := f.Nname.(*ir.Name); ok { paramOrder[n] = idx idx++ } } sort.Stable(varsAndDecls{decls, vars, paramOrder}) } type varsAndDecls struct { decls []*ir.Name vars []*dwarf.Var paramOrder map[*ir.Name]int } func (v varsAndDecls) Len() int { return len(v.decls) } func (v varsAndDecls) Less(i, j int) bool { nameLT := func(ni, nj *ir.Name) bool { oi, foundi := v.paramOrder[ni] oj, foundj := v.paramOrder[nj] if foundi { if foundj { return oi < oj } else { return true } } return false } return nameLT(v.decls[i], v.decls[j]) } func (v varsAndDecls) Swap(i, j int) { v.vars[i], v.vars[j] = v.vars[j], v.vars[i] v.decls[i], v.decls[j] = v.decls[j], v.decls[i] } // Given a function that was inlined at some point during the // compilation, return a sorted list of nodes corresponding to the // autos/locals in that function prior to inlining. If this is a // function that is not local to the package being compiled, then the // names of the variables may have been "versioned" to avoid conflicts // with local vars; disregard this versioning when sorting. func preInliningDcls(fnsym *obj.LSym) []*ir.Name { fn := base.Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*ir.Func) var rdcl []*ir.Name for _, n := range fn.Inl.Dcl { c := n.Sym().Name[0] // Avoid reporting "_" parameters, since if there are more than // one, it can result in a collision later on, as in #23179. if n.Sym().Name == "_" || c == '.' || n.Type().IsUntyped() { continue } rdcl = append(rdcl, n) } return rdcl } // createSimpleVars creates a DWARF entry for every variable declared in the // function, claiming that they are permanently on the stack. func createSimpleVars(fnsym *obj.LSym, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { var vars []*dwarf.Var var decls []*ir.Name var selected ir.NameSet for _, n := range apDecls { if ir.IsAutoTmp(n) { continue } decls = append(decls, n) vars = append(vars, createSimpleVar(fnsym, n)) selected.Add(n) } return decls, vars, selected } func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var { var abbrev int var offs int64 localAutoOffset := func() int64 { offs = n.FrameOffset() if base.Ctxt.Arch.FixedFrameSize == 0 { offs -= int64(types.PtrSize) } if buildcfg.FramePointerEnabled { offs -= int64(types.PtrSize) } return offs } switch n.Class { case ir.PAUTO: offs = localAutoOffset() abbrev = dwarf.DW_ABRV_AUTO case ir.PPARAM, ir.PPARAMOUT: abbrev = dwarf.DW_ABRV_PARAM if n.IsOutputParamInRegisters() { offs = localAutoOffset() } else { offs = n.FrameOffset() + base.Ctxt.Arch.FixedFrameSize } default: base.Fatalf("createSimpleVar unexpected class %v for node %v", n.Class, n) } typename := dwarf.InfoPrefix + types.TypeSymName(n.Type()) delete(fnsym.Func().Autot, reflectdata.TypeLinksym(n.Type())) inlIndex := 0 if base.Flag.GenDwarfInl > 1 { if n.InlFormal() || n.InlLocal() { inlIndex = posInlIndex(n.Pos()) + 1 if n.InlFormal() { abbrev = dwarf.DW_ABRV_PARAM } } } declpos := base.Ctxt.InnermostPos(declPos(n)) return &dwarf.Var{ Name: n.Sym().Name, IsReturnValue: n.Class == ir.PPARAMOUT, IsInlFormal: n.InlFormal(), Abbrev: abbrev, StackOffset: int32(offs), Type: base.Ctxt.Lookup(typename), DeclFile: declpos.RelFilename(), DeclLine: declpos.RelLine(), DeclCol: declpos.RelCol(), InlIndex: int32(inlIndex), ChildIndex: -1, DictIndex: n.DictIndex, } } // createABIVars creates DWARF variables for functions in which the // register ABI is enabled but optimization is turned off. It uses a // hybrid approach in which register-resident input params are // captured with location lists, and all other vars use the "simple" // strategy. func createABIVars(fnsym *obj.LSym, fn *ir.Func, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { // Invoke createComplexVars to generate dwarf vars for input parameters // that are register-allocated according to the ABI rules. decls, vars, selected := createComplexVars(fnsym, fn) // Now fill in the remainder of the variables: input parameters // that are not register-resident, output parameters, and local // variables. for _, n := range apDecls { if ir.IsAutoTmp(n) { continue } if _, ok := selected[n]; ok { // already handled continue } decls = append(decls, n) vars = append(vars, createSimpleVar(fnsym, n)) selected.Add(n) } return decls, vars, selected } // createComplexVars creates recomposed DWARF vars with location lists, // suitable for describing optimized code. func createComplexVars(fnsym *obj.LSym, fn *ir.Func) ([]*ir.Name, []*dwarf.Var, ir.NameSet) { debugInfo := fn.DebugInfo.(*ssa.FuncDebug) // Produce a DWARF variable entry for each user variable. var decls []*ir.Name var vars []*dwarf.Var var ssaVars ir.NameSet for varID, dvar := range debugInfo.Vars { n := dvar ssaVars.Add(n) for _, slot := range debugInfo.VarSlots[varID] { ssaVars.Add(debugInfo.Slots[slot].N) } if dvar := createComplexVar(fnsym, fn, ssa.VarID(varID)); dvar != nil { decls = append(decls, n) vars = append(vars, dvar) } } return decls, vars, ssaVars } // createComplexVar builds a single DWARF variable entry and location list. func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var { debug := fn.DebugInfo.(*ssa.FuncDebug) n := debug.Vars[varID] var abbrev int switch n.Class { case ir.PAUTO: abbrev = dwarf.DW_ABRV_AUTO_LOCLIST case ir.PPARAM, ir.PPARAMOUT: abbrev = dwarf.DW_ABRV_PARAM_LOCLIST default: return nil } gotype := reflectdata.TypeLinksym(n.Type()) delete(fnsym.Func().Autot, gotype) typename := dwarf.InfoPrefix + gotype.Name[len("type:"):] inlIndex := 0 if base.Flag.GenDwarfInl > 1 { if n.InlFormal() || n.InlLocal() { inlIndex = posInlIndex(n.Pos()) + 1 if n.InlFormal() { abbrev = dwarf.DW_ABRV_PARAM_LOCLIST } } } declpos := base.Ctxt.InnermostPos(n.Pos()) dvar := &dwarf.Var{ Name: n.Sym().Name, IsReturnValue: n.Class == ir.PPARAMOUT, IsInlFormal: n.InlFormal(), Abbrev: abbrev, Type: base.Ctxt.Lookup(typename), // The stack offset is used as a sorting key, so for decomposed // variables just give it the first one. It's not used otherwise. // This won't work well if the first slot hasn't been assigned a stack // location, but it's not obvious how to do better. StackOffset: ssagen.StackOffset(debug.Slots[debug.VarSlots[varID][0]]), DeclFile: declpos.RelFilename(), DeclLine: declpos.RelLine(), DeclCol: declpos.RelCol(), InlIndex: int32(inlIndex), ChildIndex: -1, DictIndex: n.DictIndex, } list := debug.LocationLists[varID] if len(list) != 0 { dvar.PutLocationList = func(listSym, startPC dwarf.Sym) { debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym)) } } return dvar } // RecordFlags records the specified command-line flags to be placed // in the DWARF info. func RecordFlags(flags ...string) { if base.Ctxt.Pkgpath == "" { panic("missing pkgpath") } type BoolFlag interface { IsBoolFlag() bool } type CountFlag interface { IsCountFlag() bool } var cmd bytes.Buffer for _, name := range flags { f := flag.Lookup(name) if f == nil { continue } getter := f.Value.(flag.Getter) if getter.String() == f.DefValue { // Flag has default value, so omit it. continue } if bf, ok := f.Value.(BoolFlag); ok && bf.IsBoolFlag() { val, ok := getter.Get().(bool) if ok && val { fmt.Fprintf(&cmd, " -%s", f.Name) continue } } if cf, ok := f.Value.(CountFlag); ok && cf.IsCountFlag() { val, ok := getter.Get().(int) if ok && val == 1 { fmt.Fprintf(&cmd, " -%s", f.Name) continue } } fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get()) } // Adds flag to producer string signaling whether regabi is turned on or // off. // Once regabi is turned on across the board and the relative GOEXPERIMENT // knobs no longer exist this code should be removed. if buildcfg.Experiment.RegabiArgs { cmd.Write([]byte(" regabi")) } if cmd.Len() == 0 { return } s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + 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 = cmd.Bytes()[1:] } // 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) } PK ! �g�d d scope.gonu �[��� PK ! B� $x; x; � scope_test.gonu �[��� PK ! GGQ�� � QI marker.gonu �[��� PK ! D�}Q3 Q3 |R dwinl.gonu �[��� PK ! �CH��H �H � dwarf.gonu �[��� PK n #�
| ver. 1.1 | |
.
| PHP 8.4.18 | Ð“ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ñ Ñтраницы: 0 |
proxy
|
phpinfo
|
ÐаÑтройка