proc/variables: Support for interface types
This commit is contained in:
parent
2deb7fba20
commit
48e13a9045
@ -26,6 +26,14 @@ func afunc(x int) int {
|
||||
|
||||
type functype func(int) int
|
||||
|
||||
func (a *astruct) Error() string {
|
||||
return "not an error"
|
||||
}
|
||||
|
||||
func (b *bstruct) Error() string {
|
||||
return "not an error"
|
||||
}
|
||||
|
||||
func main() {
|
||||
i1 := 1
|
||||
i2 := 2
|
||||
@ -96,11 +104,16 @@ func main() {
|
||||
i4 := 800
|
||||
i5 := -3
|
||||
i6 := -500
|
||||
var err1 error = c1.sa[0]
|
||||
var err2 error = c1.pb
|
||||
var errnil error = nil
|
||||
var iface1 interface{} = c1.sa[0]
|
||||
var ifacenil interface{} = nil
|
||||
|
||||
var amb1 = 1
|
||||
runtime.Breakpoint()
|
||||
for amb1 := 0; amb1 < 10; amb1++ {
|
||||
fmt.Println(amb1)
|
||||
}
|
||||
fmt.Println(i1, i2, i3, p1, amb1, s1, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6)
|
||||
fmt.Println(i1, i2, i3, p1, amb1, s1, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6, err1, err2, errnil, iface1, ifacenil)
|
||||
}
|
||||
|
61
proc/eval.go
61
proc/eval.go
@ -55,6 +55,9 @@ func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
|
||||
// if it's not a package variable then it must be a struct member access
|
||||
return scope.evalStructSelector(node)
|
||||
|
||||
case *ast.TypeAssertExpr: // <expression>.(<type>)
|
||||
return scope.evalTypeAssert(node)
|
||||
|
||||
case *ast.IndexExpr:
|
||||
return scope.evalIndex(node)
|
||||
|
||||
@ -166,26 +169,11 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
|
||||
fnnode = p.X
|
||||
}
|
||||
|
||||
var styp, typ dwarf.Type
|
||||
|
||||
if snode, ok := fnnode.(*ast.StarExpr); ok {
|
||||
// Pointer types only appear in the dwarf informations when
|
||||
// a pointer to the type is used in the target program, here
|
||||
// we create a pointer type on the fly so that the user can
|
||||
// specify a pointer to any variable used in the target program
|
||||
ptyp, err := scope.findType(exprToString(snode.X))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typ = &dwarf.PtrType{dwarf.CommonType{int64(scope.Thread.dbp.arch.PtrSize()), exprToString(fnnode)}, ptyp}
|
||||
styp = typ
|
||||
} else {
|
||||
styp, err = scope.findType(exprToString(fnnode))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typ = resolveTypedef(styp)
|
||||
styp, err := scope.Thread.dbp.findTypeExpr(fnnode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typ := resolveTypedef(styp)
|
||||
|
||||
converr := fmt.Errorf("can not convert \"%s\" to %s", exprToString(node.Args[0]), typ.String())
|
||||
|
||||
@ -312,6 +300,35 @@ func (scope *EvalScope) evalStructSelector(node *ast.SelectorExpr) (*Variable, e
|
||||
return xv.structMember(node.Sel.Name)
|
||||
}
|
||||
|
||||
// Evaluates expressions <subexpr>.(<type>)
|
||||
func (scope *EvalScope) evalTypeAssert(node *ast.TypeAssertExpr) (*Variable, error) {
|
||||
xv, err := scope.evalAST(node.X)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if xv.Kind != reflect.Interface {
|
||||
return nil, fmt.Errorf("expression \"%s\" not an interface", exprToString(node.X))
|
||||
}
|
||||
xv.loadInterface(0, false)
|
||||
if xv.Unreadable != nil {
|
||||
return nil, xv.Unreadable
|
||||
}
|
||||
if xv.Children[0].Unreadable != nil {
|
||||
return nil, xv.Children[0].Unreadable
|
||||
}
|
||||
if xv.Children[0].Addr == 0 {
|
||||
return nil, fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), exprToString(node.Type))
|
||||
}
|
||||
typ, err := scope.Thread.dbp.findTypeExpr(node.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if xv.Children[0].DwarfType.String() != typ.String() {
|
||||
return nil, fmt.Errorf("interface conversion: %s is %s, not %s", xv.DwarfType.String(), xv.Children[0].TypeString(), typ)
|
||||
}
|
||||
return &xv.Children[0], nil
|
||||
}
|
||||
|
||||
// Evaluates expressions <subexpr>[<subexpr>] (subscript access to arrays, slices and maps)
|
||||
func (scope *EvalScope) evalIndex(node *ast.IndexExpr) (*Variable, error) {
|
||||
xev, err := scope.evalAST(node.X)
|
||||
@ -761,13 +778,13 @@ func equalChildren(xv, yv *Variable, shortcircuit bool) (bool, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (scope *EvalScope) findType(name string) (dwarf.Type, error) {
|
||||
reader := scope.DwarfReader()
|
||||
func (dbp *Process) findType(name string) (dwarf.Type, error) {
|
||||
reader := dbp.DwarfReader()
|
||||
typentry, err := reader.SeekToTypeNamed(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return scope.Thread.dbp.dwarf.Type(typentry.Offset)
|
||||
return dbp.dwarf.Type(typentry.Offset)
|
||||
}
|
||||
|
||||
func (v *Variable) asInt() (int64, error) {
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"debug/dwarf"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
@ -110,6 +111,14 @@ type EvalScope struct {
|
||||
CFA int64
|
||||
}
|
||||
|
||||
type IsNilErr struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (err *IsNilErr) Error() string {
|
||||
return fmt.Sprintf("%s is nil", err.name)
|
||||
}
|
||||
|
||||
func newVariable(name string, addr uintptr, dwarfType dwarf.Type, thread *Thread) *Variable {
|
||||
v := &Variable{
|
||||
Name: name,
|
||||
@ -142,6 +151,8 @@ func newVariable(name string, addr uintptr, dwarfType dwarf.Type, thread *Thread
|
||||
if v.Addr != 0 {
|
||||
v.base, v.Len, v.Unreadable = v.thread.readStringInfo(v.Addr)
|
||||
}
|
||||
case t.StructName == "runtime.iface" || t.StructName == "runtime.eface":
|
||||
v.Kind = reflect.Interface
|
||||
case strings.HasPrefix(t.StructName, "[]"):
|
||||
v.Kind = reflect.Slice
|
||||
if v.Addr != 0 {
|
||||
@ -249,7 +260,7 @@ func (v *Variable) toField(field *dwarf.StructField) (*Variable, error) {
|
||||
return v.clone(), nil
|
||||
}
|
||||
if v.Addr == 0 {
|
||||
return nil, fmt.Errorf("%s is nil", v.Name)
|
||||
return nil, &IsNilErr{v.Name}
|
||||
}
|
||||
|
||||
name := ""
|
||||
@ -770,6 +781,9 @@ func (v *Variable) loadValueInternal(recurseLevel int) {
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Interface:
|
||||
v.loadInterface(recurseLevel, true)
|
||||
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
v.readComplex(v.RealType.(*dwarf.ComplexType).ByteSize)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
@ -1366,6 +1380,101 @@ func mapEvacuated(b *Variable) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *Variable) loadInterface(recurseLevel int, loadData bool) {
|
||||
var typestring, data *Variable
|
||||
isnil := false
|
||||
|
||||
for _, f := range v.RealType.(*dwarf.StructType).Field {
|
||||
switch f.Name {
|
||||
case "tab": // for runtime.iface
|
||||
tab, _ := v.toField(f)
|
||||
_type, err := tab.structMember("_type")
|
||||
if err != nil {
|
||||
_, isnil = err.(*IsNilErr)
|
||||
if !isnil {
|
||||
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
typestring, err = _type.structMember("_string")
|
||||
if err != nil {
|
||||
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
||||
return
|
||||
}
|
||||
typestring = typestring.maybeDereference()
|
||||
}
|
||||
case "_type": // for runtime.eface
|
||||
var err error
|
||||
_type, _ := v.toField(f)
|
||||
typestring, err = _type.structMember("_string")
|
||||
if err != nil {
|
||||
_, isnil = err.(*IsNilErr)
|
||||
if !isnil {
|
||||
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
typestring = typestring.maybeDereference()
|
||||
}
|
||||
case "data":
|
||||
data, _ = v.toField(f)
|
||||
}
|
||||
}
|
||||
|
||||
if isnil {
|
||||
// interface to nil
|
||||
data = data.maybeDereference()
|
||||
v.Children = []Variable{*data}
|
||||
v.Children[0].loadValue()
|
||||
return
|
||||
}
|
||||
|
||||
if typestring == nil || data == nil || typestring.Addr == 0 || typestring.Kind != reflect.String {
|
||||
v.Unreadable = fmt.Errorf("invalid interface type")
|
||||
return
|
||||
}
|
||||
typestring.loadValue()
|
||||
if typestring.Unreadable != nil {
|
||||
v.Unreadable = fmt.Errorf("invalid interface type: %v", typestring.Unreadable)
|
||||
return
|
||||
}
|
||||
|
||||
t, err := parser.ParseExpr(constant.StringVal(typestring.Value))
|
||||
if err != nil {
|
||||
v.Unreadable = fmt.Errorf("invalid interface type, unparsable data type: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
typ, err := v.thread.dbp.findTypeExpr(t)
|
||||
if err != nil {
|
||||
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
data = newVariable("data", data.Addr, typ, data.thread)
|
||||
|
||||
v.Children = []Variable{*data}
|
||||
if loadData {
|
||||
v.Children[0].loadValue()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (dbp *Process) findTypeExpr(expr ast.Expr) (dwarf.Type, error) {
|
||||
if snode, ok := expr.(*ast.StarExpr); ok {
|
||||
// Pointer types only appear in the dwarf informations when
|
||||
// a pointer to the type is used in the target program, here
|
||||
// we create a pointer type on the fly so that the user can
|
||||
// specify a pointer to any variable used in the target program
|
||||
ptyp, err := dbp.findType(exprToString(snode.X))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &dwarf.PtrType{dwarf.CommonType{int64(dbp.arch.PtrSize()), exprToString(expr)}, ptyp}, nil
|
||||
}
|
||||
return dbp.findType(exprToString(expr))
|
||||
}
|
||||
|
||||
// Fetches all variables of a specific type in the current function scope
|
||||
func (scope *EvalScope) variablesByTag(tag dwarf.Tag) ([]*Variable, error) {
|
||||
reader := scope.DwarfReader()
|
||||
|
@ -68,10 +68,16 @@ func ConvertVar(v *proc.Variable) *Variable {
|
||||
|
||||
if v.DwarfType != nil {
|
||||
r.Type = v.DwarfType.String()
|
||||
if r.Type == "*void" {
|
||||
r.Type = "unsafe.Pointer"
|
||||
}
|
||||
}
|
||||
|
||||
if v.RealType != nil {
|
||||
r.RealType = v.RealType.String()
|
||||
if r.RealType == "*void" {
|
||||
r.Type = "unsafe.Pointer"
|
||||
}
|
||||
}
|
||||
|
||||
if v.Unreadable != nil {
|
||||
|
@ -69,6 +69,19 @@ func (v *Variable) writeTo(buf *bytes.Buffer, top, newlines, includeType bool, i
|
||||
}
|
||||
case reflect.Struct:
|
||||
v.writeStructTo(buf, newlines, includeType, indent)
|
||||
case reflect.Interface:
|
||||
if includeType {
|
||||
if v.Children[0].Kind == reflect.Invalid {
|
||||
fmt.Fprintf(buf, "%s ", v.Type)
|
||||
if v.Children[0].Addr == 0 {
|
||||
fmt.Fprintf(buf, "nil")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(buf, "%s(%s) ", v.Type, v.Children[0].Type)
|
||||
}
|
||||
}
|
||||
v.Children[0].writeTo(buf, false, newlines, false, indent)
|
||||
case reflect.Map:
|
||||
v.writeMapTo(buf, newlines, includeType, indent)
|
||||
case reflect.Func:
|
||||
@ -192,7 +205,7 @@ func (v *Variable) shouldNewlineArray(newlines bool) bool {
|
||||
kind, hasptr := (&v.Children[0]).recursiveKind()
|
||||
|
||||
switch kind {
|
||||
case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map:
|
||||
case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map, reflect.Interface:
|
||||
return true
|
||||
case reflect.String:
|
||||
if hasptr {
|
||||
@ -233,7 +246,7 @@ func (v *Variable) shouldNewlineStruct(newlines bool) bool {
|
||||
kind, hasptr := (&v.Children[i]).recursiveKind()
|
||||
|
||||
switch kind {
|
||||
case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map:
|
||||
case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map, reflect.Interface:
|
||||
return true
|
||||
case reflect.String:
|
||||
if hasptr {
|
||||
|
@ -399,6 +399,20 @@ func TestEvalExpression(t *testing.T) {
|
||||
{"mnil[\"Malone\"]", false, "", "", "", fmt.Errorf("key not found")},
|
||||
{"m1[80:]", false, "", "", "", fmt.Errorf("map index out of bounds")},
|
||||
|
||||
// interfaces
|
||||
{"err1", true, "error(*struct main.astruct) *{A: 1, B: 2}", "", "error", nil},
|
||||
{"err2", true, "error(*struct main.bstruct) *{a: main.astruct {A: 1, B: 2}}", "", "error", nil},
|
||||
{"errnil", true, "error nil", "", "error", nil},
|
||||
{"iface1", true, "interface {}(*struct main.astruct) *{A: 1, B: 2}", "", "interface {}", nil},
|
||||
{"ifacenil", true, "interface {} nil", "", "interface {}", nil},
|
||||
{"err1 == err2", false, "false", "", "", nil},
|
||||
{"err1 == iface1", false, "", "", "", fmt.Errorf("mismatched types \"error\" and \"interface {}\"")},
|
||||
{"errnil == nil", false, "false", "", "", nil},
|
||||
{"nil == errnil", false, "false", "", "", nil},
|
||||
{"err1.(*main.astruct)", false, "*struct main.astruct {A: 1, B: 2}", "", "*struct main.astruct", nil},
|
||||
{"err1.(*main.bstruct)", false, "", "", "", fmt.Errorf("interface conversion: error is *struct main.astruct, not *struct main.bstruct")},
|
||||
{"errnil.(*main.astruct)", false, "", "", "", fmt.Errorf("interface conversion: error is nil, not *main.astruct")},
|
||||
|
||||
// combined expressions
|
||||
{"c1.pb.a.A", true, "1", "", "int", nil},
|
||||
{"c1.sa[1].B", false, "3", "", "int", nil},
|
||||
|
Loading…
Reference in New Issue
Block a user