proc: added *BinaryInfo.AllPCsForFileLine for faster bulk queries (#1592)

Support for bulk queries makes the DWARF quality checker
(github.com/dr2chase/dwarf-goodness/cmd/dwarf-goodness)
run much more efficiently (replace quadratic cost with
linear).
This commit is contained in:
David Chase 2019-06-27 22:39:15 -04:00 committed by Derek Parker
parent 7afda8dbe0
commit a25d2a2b24
3 changed files with 66 additions and 1 deletions

@ -136,6 +136,36 @@ func (lineInfo *DebugLineInfo) AllPCsForFileLine(f string, l int) (pcs []uint64)
return
}
// AllPCsForFileLines Adds all PCs for a given file and set (domain of map) of lines
// to the map value corresponding to each line.
func (lineInfo *DebugLineInfo) AllPCsForFileLines(f string, m map[int][]uint64) {
if lineInfo == nil {
return
}
var (
lastAddr uint64
sm = newStateMachine(lineInfo, lineInfo.Instructions)
)
for {
if err := sm.next(); err != nil {
if lineInfo.Logf != nil {
lineInfo.Logf("AllPCsForFileLine error: %v", err)
}
break
}
if sm.address != lastAddr && sm.isStmt && sm.valid && sm.file == f {
if pcs, ok := m[sm.line]; ok {
pcs = append(pcs, sm.address)
m[sm.line] = pcs
lastAddr = sm.address
}
}
}
return
}
var NoSourceError = errors.New("no source available")
// AllPCsBetween returns all PC addresses between begin and end (including both begin and end) that have the is_stmt flag set and do not belong to excludeFile:excludeLine

@ -406,6 +406,20 @@ func (bi *BinaryInfo) AllPCsForFileLine(filename string, lineno int) []uint64 {
return r
}
// AllPCsForFileLines returns a map providing all PC addresses for filename and each line in linenos
func (bi *BinaryInfo) AllPCsForFileLines(filename string, linenos []int) map[int][]uint64 {
r := make(map[int][]uint64)
for _, line := range linenos {
r[line] = make([]uint64, 0, 1)
}
for _, cu := range bi.compileUnits {
if cu.lineInfo.Lookup[filename] != nil {
cu.lineInfo.AllPCsForFileLines(filename, r)
}
}
return r
}
// PCToFunc returns the function containing the given PC address
func (bi *BinaryInfo) PCToFunc(pc uint64) *Function {
i := sort.Search(len(bi.Functions), func(i int) bool {

@ -3629,6 +3629,27 @@ func checkFrame(frame proc.Stackframe, fnname, file string, line int, inlined bo
return nil
}
func TestAllPCsForFileLines(t *testing.T) {
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 10, -1, 0, 0, ""}) {
// Versions of go before 1.10 do not have DWARF information for inlined calls
t.Skip("inlining not supported")
}
withTestProcessArgs("testinline", t, ".", []string{}, protest.EnableInlining, func(p proc.Process, fixture protest.Fixture) {
l2pcs := p.BinInfo().AllPCsForFileLines(fixture.Source, []int{7, 20})
if len(l2pcs) != 2 {
t.Fatalf("expected two map entries for %s:{%d,%d} (got %d: %v)", fixture.Source, 7, 20, len(l2pcs), l2pcs)
}
pcs := l2pcs[20]
if len(pcs) < 1 {
t.Fatalf("expected at least one location for %s:%d (got %d: %#x)", fixture.Source, 20, len(pcs), pcs)
}
pcs = l2pcs[7]
if len(pcs) < 2 {
t.Fatalf("expected at least two locations for %s:%d (got %d: %#x)", fixture.Source, 7, len(pcs), pcs)
}
})
}
func TestInlinedStacktraceAndVariables(t *testing.T) {
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 10, -1, 0, 0, ""}) {
// Versions of go before 1.10 do not have DWARF information for inlined calls
@ -3680,7 +3701,7 @@ func TestInlinedStacktraceAndVariables(t *testing.T) {
withTestProcessArgs("testinline", t, ".", []string{}, protest.EnableInlining, func(p proc.Process, fixture protest.Fixture) {
pcs := p.BinInfo().AllPCsForFileLine(fixture.Source, 7)
if len(pcs) < 2 {
t.Fatalf("expected at least two locations for %s:%d (got %d: %#x)", fixture.Source, 6, len(pcs), pcs)
t.Fatalf("expected at least two locations for %s:%d (got %d: %#x)", fixture.Source, 7, len(pcs), pcs)
}
for _, pc := range pcs {
t.Logf("setting breakpoint at %#x\n", pc)