pkg/proc: use DW_AT_decl_line to determine var visibility

Fixes #186, #83
This commit is contained in:
aarzilli 2017-09-08 12:31:03 +02:00 committed by Derek Parker
parent b3246296d7
commit 85669434f6
4 changed files with 60 additions and 4 deletions

15
_fixtures/decllinetest.go Normal file

@ -0,0 +1,15 @@
package main
import (
"fmt"
"runtime"
)
func main() {
a := 0
runtime.Breakpoint()
a++
b := 0
runtime.Breakpoint()
fmt.Println(a, b)
}

@ -15,16 +15,17 @@ type VariableReader struct {
depth int
onlyVisible bool
pc uint64
line int
err error
}
// Variables returns a VariableReader for the function or lexical block at off.
// If onlyVisible is true only variables visible at pc will be returned by
// the VariableReader.
func Variables(dwarf *dwarf.Data, off dwarf.Offset, pc uint64, onlyVisible bool) *VariableReader {
func Variables(dwarf *dwarf.Data, off dwarf.Offset, pc uint64, line int, onlyVisible bool) *VariableReader {
reader := dwarf.Reader()
reader.Seek(off)
return &VariableReader{dwarf: dwarf, reader: reader, entry: nil, depth: 0, onlyVisible: onlyVisible, pc: pc, err: nil}
return &VariableReader{dwarf: dwarf, reader: reader, entry: nil, depth: 0, onlyVisible: onlyVisible, pc: pc, line: line, err: nil}
}
// Next reads the next variable entry, returns false if there aren't any.
@ -69,7 +70,9 @@ func (vrdr *VariableReader) Next() bool {
vrdr.err = errors.New("offset was not lexical block or subprogram")
return false
}
return true
if declLine, ok := vrdr.entry.Val(dwarf.AttrDeclLine).(int64); !ok || vrdr.line >= int(declLine) {
return true
}
}
}
}

@ -1681,6 +1681,14 @@ func TestStepIntoFunction(t *testing.T) {
func TestIssue384(t *testing.T) {
// Crash related to reading uninitialized memory, introduced by the memory prefetching optimization
ver, _ := goversion.Parse(runtime.Version())
if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{1, 10, -1, 0, 0, ""}) {
// go 1.10 emits DW_AT_decl_line and we won't be able to evaluate 'st'
// which is declared after line 13.
return
}
protest.AllowRecording(t)
withTestProcess("issue384", t, func(p proc.Process, fixture protest.Fixture) {
start, _, err := p.BinInfo().LineToPC(fixture.Source, 13)
@ -3374,3 +3382,31 @@ func TestIssue1008(t *testing.T) {
}
})
}
func TestDeclLine(t *testing.T) {
ver, _ := goversion.Parse(runtime.Version())
if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 10, -1, 0, 0, ""}) {
t.Skip("go 1.9 and prior versions do not emit DW_AT_decl_line")
}
withTestProcess("decllinetest", t, func(p proc.Process, fixture protest.Fixture) {
assertNoError(proc.Continue(p), t, "Continue")
scope, err := proc.GoroutineScope(p.CurrentThread())
assertNoError(err, t, "GoroutineScope (1)")
vars, err := scope.LocalVariables(normalLoadConfig)
assertNoError(err, t, "LocalVariables (1)")
if len(vars) != 1 {
t.Fatalf("wrong number of variables %d", len(vars))
}
assertNoError(proc.Continue(p), t, "Continue")
scope, err = proc.GoroutineScope(p.CurrentThread())
assertNoError(err, t, "GoroutineScope (2)")
scope.LocalVariables(normalLoadConfig)
vars, err = scope.LocalVariables(normalLoadConfig)
assertNoError(err, t, "LocalVariables (2)")
if len(vars) != 2 {
t.Fatalf("wrong number of variables %d", len(vars))
}
})
}

@ -1745,9 +1745,11 @@ func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg *LoadConfig) ([]*Varia
return nil, errors.New("unable to find function context")
}
_, line, _ := scope.BinInfo.PCToLine(scope.PC)
var vars []*Variable
var depths []int
varReader := reader.Variables(scope.BinInfo.dwarf, fn.offset, scope.PC, tag == dwarf.TagVariable)
varReader := reader.Variables(scope.BinInfo.dwarf, fn.offset, scope.PC, line, tag == dwarf.TagVariable)
hasScopes := false
for varReader.Next() {
entry := varReader.Entry()