mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 10:47:27 +08:00 
			
		
		
		
	proc/variables: bugfix: ifaces with types in user defined packages
The concrete type of an interface only contains the abbreviated package name, we must construct a map from package names to package paths to be able to resolve the concrete type of an interface.
This commit is contained in:
		| @ -2,6 +2,7 @@ package main | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"go/constant" | ||||||
| 	"runtime" | 	"runtime" | ||||||
| 	"unsafe" | 	"unsafe" | ||||||
| ) | ) | ||||||
| @ -109,15 +110,20 @@ func main() { | |||||||
| 	var err2 error = c1.pb | 	var err2 error = c1.pb | ||||||
| 	var errnil error = nil | 	var errnil error = nil | ||||||
| 	var iface1 interface{} = c1.sa[0] | 	var iface1 interface{} = c1.sa[0] | ||||||
|  | 	var iface2 interface{} = "test" | ||||||
|  | 	var iface3 interface{} = map[string]constant.Value{} | ||||||
|  | 	var iface4 interface{} = []constant.Value{constant.MakeInt64(4)} | ||||||
| 	var ifacenil interface{} = nil | 	var ifacenil interface{} = nil | ||||||
| 	arr1 := [4]int{0, 1, 2, 3} | 	arr1 := [4]int{0, 1, 2, 3} | ||||||
| 	parr := &arr1 | 	parr := &arr1 | ||||||
| 	cpx1 := complex(1, 2) | 	cpx1 := complex(1, 2) | ||||||
|  | 	const1 := constant.MakeInt64(3) | ||||||
|  |  | ||||||
| 	var amb1 = 1 | 	var amb1 = 1 | ||||||
| 	runtime.Breakpoint() | 	runtime.Breakpoint() | ||||||
| 	for amb1 := 0; amb1 < 10; amb1++ { | 	for amb1 := 0; amb1 < 10; amb1++ { | ||||||
| 		fmt.Println(amb1) | 		fmt.Println(amb1) | ||||||
| 	} | 	} | ||||||
| 	fmt.Println(i1, i2, i3, p1, amb1, s1, s3, 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, arr1, parr, cpx1) | 	runtime.Breakpoint() | ||||||
|  | 	fmt.Println(i1, i2, i3, p1, amb1, s1, s3, 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, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4) | ||||||
| } | } | ||||||
|  | |||||||
| @ -932,15 +932,6 @@ func equalChildren(xv, yv *Variable, shortcircuit bool) (bool, error) { | |||||||
| 	return r, nil | 	return r, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (dbp *Process) findType(name string) (dwarf.Type, error) { |  | ||||||
| 	reader := dbp.DwarfReader() |  | ||||||
| 	typentry, err := reader.SeekToTypeNamed(name) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	return dbp.dwarf.Type(typentry.Offset) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (v *Variable) asInt() (int64, error) { | func (v *Variable) asInt() (int64, error) { | ||||||
| 	if v.DwarfType == nil { | 	if v.DwarfType == nil { | ||||||
| 		if v.Value.Kind() != constant.Int { | 		if v.Value.Kind() != constant.Int { | ||||||
|  | |||||||
| @ -39,6 +39,9 @@ type Process struct { | |||||||
| 	// Normally SelectedGoroutine is CurrentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread | 	// Normally SelectedGoroutine is CurrentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread | ||||||
| 	SelectedGoroutine *G | 	SelectedGoroutine *G | ||||||
|  |  | ||||||
|  | 	// Maps package names to package paths, needed to lookup types inside DWARF info | ||||||
|  | 	packageMap map[string]string | ||||||
|  |  | ||||||
| 	allGCache               []*G | 	allGCache               []*G | ||||||
| 	dwarf                   *dwarf.Data | 	dwarf                   *dwarf.Data | ||||||
| 	goSymTable              *gosym.Table | 	goSymTable              *gosym.Table | ||||||
|  | |||||||
							
								
								
									
										123
									
								
								proc/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								proc/types.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,123 @@ | |||||||
|  | package proc | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"debug/dwarf" | ||||||
|  | 	"go/ast" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Do not call this function directly it isn't able to deal correctly with package paths | ||||||
|  | func (dbp *Process) findType(name string) (dwarf.Type, error) { | ||||||
|  | 	reader := dbp.DwarfReader() | ||||||
|  | 	typentry, err := reader.SeekToTypeNamed(name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return dbp.dwarf.Type(typentry.Offset) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dbp *Process) pointerTo(typ dwarf.Type) dwarf.Type { | ||||||
|  | 	return &dwarf.PtrType{dwarf.CommonType{int64(dbp.arch.PtrSize()), ""}, typ} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dbp *Process) findTypeExpr(expr ast.Expr) (dwarf.Type, error) { | ||||||
|  | 	dbp.loadPackageMap() | ||||||
|  | 	dbp.expandPackagesInType(expr) | ||||||
|  | 	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 dbp.pointerTo(ptyp), nil | ||||||
|  | 	} | ||||||
|  | 	return dbp.findType(exprToString(expr)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func complexType(typename string) bool { | ||||||
|  | 	dot := 0 | ||||||
|  | 	for _, ch := range typename { | ||||||
|  | 		switch ch { | ||||||
|  | 		case '*', '[', '<', '{', '(', ' ': | ||||||
|  | 			return true | ||||||
|  | 		case '.': | ||||||
|  | 			dot++ | ||||||
|  | 			if dot > 1 { | ||||||
|  | 				return true | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dbp *Process) loadPackageMap() error { | ||||||
|  | 	if dbp.packageMap != nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	dbp.packageMap = map[string]string{} | ||||||
|  | 	reader := dbp.DwarfReader() | ||||||
|  | 	for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if entry.Tag != dwarf.TagTypedef && entry.Tag != dwarf.TagBaseType && entry.Tag != dwarf.TagClassType && entry.Tag != dwarf.TagStructType { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		typename, ok := entry.Val(dwarf.AttrName).(string) | ||||||
|  | 		if !ok || complexType(typename) { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		dot := strings.Index(typename, ".") | ||||||
|  | 		if dot < 0 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		path := typename[:dot] | ||||||
|  | 		slash := strings.LastIndex(path, "/") | ||||||
|  | 		if slash < 0 || slash+1 >= len(path) { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		name := path[slash+1:] | ||||||
|  | 		dbp.packageMap[name] = path | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (dbp *Process) expandPackagesInType(expr ast.Expr) { | ||||||
|  | 	switch e := expr.(type) { | ||||||
|  | 	case *ast.ArrayType: | ||||||
|  | 		dbp.expandPackagesInType(e.Elt) | ||||||
|  | 	case *ast.ChanType: | ||||||
|  | 		dbp.expandPackagesInType(e.Value) | ||||||
|  | 	case *ast.FuncType: | ||||||
|  | 		for i := range e.Params.List { | ||||||
|  | 			dbp.expandPackagesInType(e.Params.List[i].Type) | ||||||
|  | 		} | ||||||
|  | 		for i := range e.Results.List { | ||||||
|  | 			dbp.expandPackagesInType(e.Results.List[i].Type) | ||||||
|  | 		} | ||||||
|  | 	case *ast.MapType: | ||||||
|  | 		dbp.expandPackagesInType(e.Key) | ||||||
|  | 		dbp.expandPackagesInType(e.Value) | ||||||
|  | 	case *ast.ParenExpr: | ||||||
|  | 		dbp.expandPackagesInType(e.X) | ||||||
|  | 	case *ast.SelectorExpr: | ||||||
|  | 		switch x := e.X.(type) { | ||||||
|  | 		case *ast.Ident: | ||||||
|  | 			if path, ok := dbp.packageMap[x.Name]; ok { | ||||||
|  | 				x.Name = path | ||||||
|  | 			} | ||||||
|  | 		default: | ||||||
|  | 			dbp.expandPackagesInType(e.X) | ||||||
|  | 		} | ||||||
|  | 	case *ast.StarExpr: | ||||||
|  | 		dbp.expandPackagesInType(e.X) | ||||||
|  | 	default: | ||||||
|  | 		// nothing to do | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -5,7 +5,6 @@ import ( | |||||||
| 	"debug/dwarf" | 	"debug/dwarf" | ||||||
| 	"encoding/binary" | 	"encoding/binary" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"go/ast" |  | ||||||
| 	"go/constant" | 	"go/constant" | ||||||
| 	"go/parser" | 	"go/parser" | ||||||
| 	"go/token" | 	"go/token" | ||||||
| @ -1451,10 +1450,16 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool) { | |||||||
|  |  | ||||||
| 	typ, err := v.thread.dbp.findTypeExpr(t) | 	typ, err := v.thread.dbp.findTypeExpr(t) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		v.Unreadable = fmt.Errorf("invalid interface type: %v", err) | 		v.Unreadable = fmt.Errorf("interface type \"%s\" not found for 0x%x: %v", constant.StringVal(typestring.Value), data.Addr, err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	realtyp := resolveTypedef(typ) | ||||||
|  | 	if _, isptr := realtyp.(*dwarf.PtrType); !isptr { | ||||||
|  | 		// interface to non-pointer types are pointers even if the type says otherwise | ||||||
|  | 		typ = v.thread.dbp.pointerTo(typ) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	data = newVariable("data", data.Addr, typ, data.thread) | 	data = newVariable("data", data.Addr, typ, data.thread) | ||||||
|  |  | ||||||
| 	v.Children = []Variable{*data} | 	v.Children = []Variable{*data} | ||||||
| @ -1464,21 +1469,6 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool) { | |||||||
| 	return | 	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 | // Fetches all variables of a specific type in the current function scope | ||||||
| func (scope *EvalScope) variablesByTag(tag dwarf.Tag) ([]*Variable, error) { | func (scope *EvalScope) variablesByTag(tag dwarf.Tag) ([]*Variable, error) { | ||||||
| 	reader := scope.DwarfReader() | 	reader := scope.DwarfReader() | ||||||
|  | |||||||
| @ -404,6 +404,9 @@ func TestEvalExpression(t *testing.T) { | |||||||
| 		{"err2", true, "error(*struct main.bstruct) *{a: 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}, | 		{"errnil", true, "error nil", "", "error", nil}, | ||||||
| 		{"iface1", true, "interface {}(*struct main.astruct) *{A: 1, B: 2}", "", "interface {}", nil}, | 		{"iface1", true, "interface {}(*struct main.astruct) *{A: 1, B: 2}", "", "interface {}", nil}, | ||||||
|  | 		{"iface2", true, "interface {}(*struct string) *\"test\"", "", "interface {}", nil}, | ||||||
|  | 		{"iface3", true, "interface {}(map[string]go/constant.Value) []", "", "interface {}", nil}, | ||||||
|  | 		{"iface4", true, "interface {}(*struct []go/constant.Value) *[*4]", "", "interface {}", nil}, | ||||||
| 		{"ifacenil", true, "interface {} nil", "", "interface {}", nil}, | 		{"ifacenil", true, "interface {} nil", "", "interface {}", nil}, | ||||||
| 		{"err1 == err2", false, "false", "", "", nil}, | 		{"err1 == err2", false, "false", "", "", nil}, | ||||||
| 		{"err1 == iface1", false, "", "", "", fmt.Errorf("mismatched types \"error\" and \"interface {}\"")}, | 		{"err1 == iface1", false, "", "", "", fmt.Errorf("mismatched types \"error\" and \"interface {}\"")}, | ||||||
| @ -412,6 +415,7 @@ func TestEvalExpression(t *testing.T) { | |||||||
| 		{"err1.(*main.astruct)", false, "*struct main.astruct {A: 1, B: 2}", "", "*struct main.astruct", 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")}, | 		{"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")}, | 		{"errnil.(*main.astruct)", false, "", "", "", fmt.Errorf("interface conversion: error is nil, not *main.astruct")}, | ||||||
|  | 		{"const1", true, "go/constant.Value(*go/constant.int64Val) *3", "", "go/constant.Value", nil}, | ||||||
|  |  | ||||||
| 		// combined expressions | 		// combined expressions | ||||||
| 		{"c1.pb.a.A", true, "1", "", "int", nil}, | 		{"c1.pb.a.A", true, "1", "", "int", nil}, | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 aarzilli
					aarzilli