proc: Flag shadowed arguments as shadowed

Fixes #951
This commit is contained in:
aarzilli 2018-04-15 14:11:27 +02:00 committed by Derek Parker
parent 21be59469a
commit 7fd47749ef
6 changed files with 112 additions and 22 deletions

21
_fixtures/issue951.go Normal file

@ -0,0 +1,21 @@
package main
import (
"fmt"
"runtime"
)
func main() {
shadow(42)
}
func shadow(i int) {
for i := 0; i < 5; i++ {
for i := 20; i < 25; i++ {
runtime.Breakpoint()
fmt.Println("another shadow", i)
}
fmt.Println("shadow", i)
}
fmt.Println("arg", i)
}

@ -2,7 +2,6 @@ package proc
import ( import (
"bytes" "bytes"
"debug/dwarf"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
@ -564,7 +563,7 @@ func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) {
return nilVariable, nil return nilVariable, nil
} }
vars, err := scope.variablesByTag(dwarf.TagVariable, nil) vars, err := scope.Locals()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -573,15 +572,6 @@ func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) {
return vars[i], nil return vars[i], nil
} }
} }
args, err := scope.variablesByTag(dwarf.TagFormalParameter, nil)
if err != nil {
return nil, err
}
for i := range args {
if args[i].Name == node.Name {
return args[i], nil
}
}
// if it's not a local variable then it could be a package variable w/o explicit package name // if it's not a local variable then it could be a package variable w/o explicit package name
if scope.Fn != nil { if scope.Fn != nil {

@ -524,7 +524,7 @@ func FrameToScope(bi *BinaryInfo, thread MemoryReadWriter, g *G, frames ...Stack
// Creates a cacheMem that will preload the entire stack frame the first // Creates a cacheMem that will preload the entire stack frame the first
// time any local variable is read. // time any local variable is read.
// Remember that the stack grows from downward in memory. // Remember that the stack grows downward in memory.
minaddr := frames[0].Regs.SP() minaddr := frames[0].Regs.SP()
var maxaddr uint64 var maxaddr uint64
if len(frames) > 1 && frames[0].SystemStack == frames[1].SystemStack { if len(frames) > 1 && frames[0].SystemStack == frames[1].SystemStack {

@ -3665,3 +3665,35 @@ func TestInlineStepOut(t *testing.T) {
{contStepout, 18}, {contStepout, 18},
}) })
} }
func TestIssue951(t *testing.T) {
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) {
t.Skip("scopes not implemented in <=go1.8")
}
withTestProcess("issue951", t, func(p proc.Process, fixture protest.Fixture) {
assertNoError(proc.Continue(p), t, "Continue()")
scope, err := proc.GoroutineScope(p.CurrentThread())
assertNoError(err, t, "GoroutineScope")
args, err := scope.FunctionArguments(normalLoadConfig)
assertNoError(err, t, "FunctionArguments")
t.Logf("%#v", args[0])
if args[0].Flags&proc.VariableShadowed == 0 {
t.Error("argument is not shadowed")
}
vars, err := scope.LocalVariables(normalLoadConfig)
assertNoError(err, t, "LocalVariables")
shadowed, notShadowed := 0, 0
for i := range vars {
t.Logf("var %d: %#v\n", i, vars[i])
if vars[i].Flags&proc.VariableShadowed != 0 {
shadowed++
} else {
notShadowed++
}
}
if shadowed != 1 || notShadowed != 1 {
t.Errorf("Wrong number of shadowed/non-shadowed local variables: %d %d", shadowed, notShadowed)
}
})
}

@ -58,6 +58,10 @@ const (
VariableShadowed VariableShadowed
// VariableConstant means this variable is a constant value // VariableConstant means this variable is a constant value
VariableConstant VariableConstant
// VariableArgument means this variable is a function argument
VariableArgument
// VariableReturnArgument means this variable is a function return value
VariableReturnArgument
) )
// Variable represents a variable. It contains the address, name, // Variable represents a variable. It contains the address, name,
@ -616,12 +620,38 @@ func (scope *EvalScope) SetVariable(name, value string) error {
// LocalVariables returns all local variables from the current function scope. // LocalVariables returns all local variables from the current function scope.
func (scope *EvalScope) LocalVariables(cfg LoadConfig) ([]*Variable, error) { func (scope *EvalScope) LocalVariables(cfg LoadConfig) ([]*Variable, error) {
return scope.variablesByTag(dwarf.TagVariable, &cfg) vars, err := scope.Locals()
if err != nil {
return nil, err
}
vars = filterVariables(vars, func(v *Variable) bool {
return (v.Flags & (VariableArgument | VariableReturnArgument)) == 0
})
loadValues(vars, cfg)
return vars, nil
} }
// FunctionArguments returns the name, value, and type of all current function arguments. // FunctionArguments returns the name, value, and type of all current function arguments.
func (scope *EvalScope) FunctionArguments(cfg LoadConfig) ([]*Variable, error) { func (scope *EvalScope) FunctionArguments(cfg LoadConfig) ([]*Variable, error) {
return scope.variablesByTag(dwarf.TagFormalParameter, &cfg) vars, err := scope.Locals()
if err != nil {
return nil, err
}
vars = filterVariables(vars, func(v *Variable) bool {
return (v.Flags & (VariableArgument | VariableReturnArgument)) != 0
})
loadValues(vars, cfg)
return vars, nil
}
func filterVariables(vars []*Variable, pred func(v *Variable) bool) []*Variable {
r := make([]*Variable, 0, len(vars))
for i := range vars {
if pred(vars[i]) {
r = append(r, vars[i])
}
}
return r
} }
// PackageVariables returns the name, value, and type of all package variables in the application. // PackageVariables returns the name, value, and type of all package variables in the application.
@ -825,6 +855,12 @@ func (v *Variable) maybeDereference() *Variable {
} }
} }
func loadValues(vars []*Variable, cfg LoadConfig) {
for i := range vars {
vars[i].loadValueInternal(0, cfg)
}
}
// Extracts the value of the variable at the given address. // Extracts the value of the variable at the given address.
func (v *Variable) loadValue(cfg LoadConfig) { func (v *Variable) loadValue(cfg LoadConfig) {
v.loadValueInternal(0, cfg) v.loadValueInternal(0, cfg)
@ -1876,20 +1912,17 @@ func (v *variablesByDepth) Swap(i int, j int) {
} }
// Fetches all variables of a specific type in the current function scope // Fetches all variables of a specific type in the current function scope
func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg *LoadConfig) ([]*Variable, error) { func (scope *EvalScope) Locals() ([]*Variable, error) {
if scope.Fn == nil { if scope.Fn == nil {
return nil, errors.New("unable to find function context") return nil, errors.New("unable to find function context")
} }
var vars []*Variable var vars []*Variable
var depths []int var depths []int
varReader := reader.Variables(scope.BinInfo.dwarf, scope.Fn.offset, scope.PC, scope.Line, tag == dwarf.TagVariable) varReader := reader.Variables(scope.BinInfo.dwarf, scope.Fn.offset, scope.PC, scope.Line, true)
hasScopes := false hasScopes := false
for varReader.Next() { for varReader.Next() {
entry := varReader.Entry() entry := varReader.Entry()
if entry.Tag != tag {
continue
}
val, err := scope.extractVarInfoFromEntry(entry) val, err := scope.extractVarInfoFromEntry(entry)
if err != nil { if err != nil {
// skip variables that we can't parse yet // skip variables that we can't parse yet
@ -1897,6 +1930,17 @@ func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg *LoadConfig) ([]*Varia
} }
vars = append(vars, val) vars = append(vars, val)
depth := varReader.Depth() depth := varReader.Depth()
if entry.Tag == dwarf.TagFormalParameter {
if depth <= 1 {
depth = 0
}
isret, _ := entry.Val(dwarf.AttrVarParam).(bool)
if isret {
val.Flags |= VariableReturnArgument
} else {
val.Flags |= VariableArgument
}
}
depths = append(depths, depth) depths = append(depths, depth)
if depth > 1 { if depth > 1 {
hasScopes = true hasScopes = true
@ -1933,9 +1977,6 @@ func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg *LoadConfig) ([]*Varia
} }
lvn[v.Name] = v lvn[v.Name] = v
} }
if cfg != nil {
v.loadValue(*cfg)
}
} }
return vars, nil return vars, nil

@ -170,6 +170,12 @@ const (
// VariableConstant means this variable is a constant value // VariableConstant means this variable is a constant value
VariableConstant VariableConstant
// VariableArgument means this variable is a function argument
VariableArgument
// VariableReturnArgument means this variable is a function return value
VariableReturnArgument
) )
// Variable describes a variable. // Variable describes a variable.