]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: clean up buggy DWARF inlined info PC ranges
authorThan McIntosh <thanm@google.com>
Mon, 17 Aug 2020 18:17:07 +0000 (14:17 -0400)
committerThan McIntosh <thanm@google.com>
Tue, 17 Nov 2020 21:53:08 +0000 (21:53 +0000)
Repair the code that generates PC ranges for DWARF inlined routine
instances to insure that if II Y is a child of II X within the inline
tree, X's ranges include the ranges from Y. This is similar to what
we're already doing for DWARF scopes.

Updates #33188.

Change-Id: I9bb552777fcd1ae93dc01872707667ad092b1dd9
Reviewed-on: https://go-review.googlesource.com/c/go/+/248724
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: David Chase <drchase@google.com>
Trust: Than McIntosh <thanm@google.com>

src/cmd/compile/internal/gc/dwinl.go
src/cmd/internal/dwarf/dwarf.go

index 5120fa1166c262c7eb4c05975ca69950b38ebaed..bb5ae61cbb3b40bd48e39704fc2683697666048a 100644 (file)
@@ -8,6 +8,7 @@ import (
        "cmd/internal/dwarf"
        "cmd/internal/obj"
        "cmd/internal/src"
+       "fmt"
        "strings"
 )
 
@@ -170,12 +171,32 @@ func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls {
                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 Debug_gendwarfinl != 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
 }
 
@@ -355,3 +376,74 @@ func dumpInlVars(dwvars []*dwarf.Var) {
                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 := 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 = Ctxt.InlTree.InlinedFunction(pic.InlIndex).Name
+               parentRanges = pic.Ranges
+       }
+
+       // Callee ranges contained in caller ranges?
+       c, m := rangesContainsAll(parentRanges, calleeRanges)
+       if !c {
+               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)
+       }
+}
index b2fd5262bb02bbc0824f9696ca6590817e6590ac..e1a70ef853e7c06d53d306f18e167f78e0b4bfcd 100644 (file)
@@ -101,26 +101,26 @@ func EnableLogging(doit bool) {
        logDwarf = doit
 }
 
-// UnifyRanges merges the list of ranges of c into the list of ranges of s
-func (s *Scope) UnifyRanges(c *Scope) {
-       out := make([]Range, 0, len(s.Ranges)+len(c.Ranges))
-
+// MergeRanges creates a new range list by merging the ranges from
+// its two arguments, then returns the new list.
+func MergeRanges(in1, in2 []Range) []Range {
+       out := make([]Range, 0, len(in1)+len(in2))
        i, j := 0, 0
        for {
                var cur Range
-               if i < len(s.Ranges) && j < len(c.Ranges) {
-                       if s.Ranges[i].Start < c.Ranges[j].Start {
-                               cur = s.Ranges[i]
+               if i < len(in2) && j < len(in1) {
+                       if in2[i].Start < in1[j].Start {
+                               cur = in2[i]
                                i++
                        } else {
-                               cur = c.Ranges[j]
+                               cur = in1[j]
                                j++
                        }
-               } else if i < len(s.Ranges) {
-                       cur = s.Ranges[i]
+               } else if i < len(in2) {
+                       cur = in2[i]
                        i++
-               } else if j < len(c.Ranges) {
-                       cur = c.Ranges[j]
+               } else if j < len(in1) {
+                       cur = in1[j]
                        j++
                } else {
                        break
@@ -133,7 +133,12 @@ func (s *Scope) UnifyRanges(c *Scope) {
                }
        }
 
-       s.Ranges = out
+       return out
+}
+
+// UnifyRanges merges the ranges from 'c' into the list of ranges for 's'.
+func (s *Scope) UnifyRanges(c *Scope) {
+       s.Ranges = MergeRanges(s.Ranges, c.Ranges)
 }
 
 // AppendRange adds r to s, if r is non-empty.