Go 1.7 compatibility (#524)
* tests: update to cope with go1.7 SSA compiler * de-vendored golang.org/x/debug/dwarf We need our own tweaked version * dwarf/debug/dwarf: always use the entry's name attribute Using the name attribute leads to better type names as well as fixes inconsistencies between 1.5, 1.6 and 1.7. * proc: Updated loadInterface to work with go1.7 go1.7 changed the internal representation of types, removing the string field from runtime._type. Updated loadInterface to use the new str field.
This commit is contained in:
parent
5933a0f48f
commit
9bc6ad4f46
12
Godeps/Godeps.json
generated
12
Godeps/Godeps.json
generated
@ -69,18 +69,6 @@
|
|||||||
"ImportPath": "github.com/spf13/pflag",
|
"ImportPath": "github.com/spf13/pflag",
|
||||||
"Rev": "b084184666e02084b8ccb9b704bf0d79c466eb1d"
|
"Rev": "b084184666e02084b8ccb9b704bf0d79c466eb1d"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/debug/dwarf",
|
|
||||||
"Rev": "450bc3a73495e77763c92c336e3bc040b3f34c14"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/debug/elf",
|
|
||||||
"Rev": "450bc3a73495e77763c92c336e3bc040b3f34c14"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "golang.org/x/debug/macho",
|
|
||||||
"Rev": "450bc3a73495e77763c92c336e3bc040b3f34c14"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/sys/unix",
|
"ImportPath": "golang.org/x/sys/unix",
|
||||||
"Rev": "eb2c74142fd19a79b3f237334c7384d5167b1b46"
|
"Rev": "eb2c74142fd19a79b3f237334c7384d5167b1b46"
|
||||||
|
@ -30,7 +30,8 @@ func anotherFunction() {
|
|||||||
func main() {
|
func main() {
|
||||||
var a SomeType
|
var a SomeType
|
||||||
var b OtherType
|
var b OtherType
|
||||||
fmt.Printf("%s %s\n", a.String(), b.String())
|
i := 10
|
||||||
|
fmt.Printf("%s %s %v\n", a.String(), b.String(), i)
|
||||||
a.SomeFunction()
|
a.SomeFunction()
|
||||||
anotherFunction()
|
anotherFunction()
|
||||||
ioutil.ReadFile("nonexistent.file.txt")
|
ioutil.ReadFile("nonexistent.file.txt")
|
||||||
|
@ -5,6 +5,8 @@ import "math"
|
|||||||
var f = 1.5
|
var f = 1.5
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
_ = math.Floor(f)
|
floatvar1 := math.Floor(f)
|
||||||
_ = float64(int(f))
|
floatvar2 := float64(int(f))
|
||||||
|
_ = floatvar1
|
||||||
|
_ = floatvar2
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,12 @@ type PtrType struct {
|
|||||||
Type Type
|
Type Type
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *PtrType) String() string { return "*" + t.Type.String() }
|
func (t *PtrType) String() string {
|
||||||
|
if t.Name != "" {
|
||||||
|
return t.Name
|
||||||
|
}
|
||||||
|
return "*" + t.Type.String()
|
||||||
|
}
|
||||||
|
|
||||||
// A StructType represents a struct, union, or C++ class type.
|
// A StructType represents a struct, union, or C++ class type.
|
||||||
type StructType struct {
|
type StructType struct {
|
||||||
@ -160,6 +165,9 @@ type StructField struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *StructType) String() string {
|
func (t *StructType) String() string {
|
||||||
|
if t.Name != "" {
|
||||||
|
return t.Name
|
||||||
|
}
|
||||||
if t.StructName != "" {
|
if t.StructName != "" {
|
||||||
return t.Kind + " " + t.StructName
|
return t.Kind + " " + t.StructName
|
||||||
}
|
}
|
||||||
@ -591,6 +599,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
|
|||||||
case TagUnionType:
|
case TagUnionType:
|
||||||
t.Kind = "union"
|
t.Kind = "union"
|
||||||
}
|
}
|
||||||
|
t.Name, _ = e.Val(AttrName).(string)
|
||||||
t.StructName, _ = e.Val(AttrName).(string)
|
t.StructName, _ = e.Val(AttrName).(string)
|
||||||
t.Incomplete = e.Val(AttrDeclaration) != nil
|
t.Incomplete = e.Val(AttrDeclaration) != nil
|
||||||
t.Field = make([]*StructField, 0, 8)
|
t.Field = make([]*StructField, 0, 8)
|
||||||
@ -705,6 +714,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
|
|||||||
// AttrType: subtype [not required! void* has no AttrType]
|
// AttrType: subtype [not required! void* has no AttrType]
|
||||||
// AttrAddrClass: address class [ignored]
|
// AttrAddrClass: address class [ignored]
|
||||||
t := new(PtrType)
|
t := new(PtrType)
|
||||||
|
t.Name, _ = e.Val(AttrName).(string)
|
||||||
t.ReflectKind = getKind(e)
|
t.ReflectKind = getKind(e)
|
||||||
typ = t
|
typ = t
|
||||||
typeCache[off] = t
|
typeCache[off] = t
|
@ -13,7 +13,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"golang.org/x/debug/dwarf"
|
"github.com/derekparker/delve/dwarf/debug/dwarf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: error reporting detail
|
// TODO: error reporting detail
|
@ -14,7 +14,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"golang.org/x/debug/dwarf"
|
"github.com/derekparker/delve/dwarf/debug/dwarf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A File represents an open Mach-O file.
|
// A File represents an open Mach-O file.
|
@ -3,8 +3,8 @@ package reader
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/debug/dwarf"
|
|
||||||
|
|
||||||
|
"github.com/derekparker/delve/dwarf/debug/dwarf"
|
||||||
"github.com/derekparker/delve/dwarf/op"
|
"github.com/derekparker/delve/dwarf/op"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,10 +24,10 @@ type Breakpoint struct {
|
|||||||
Temp bool // Whether this is a temp breakpoint (for next'ing).
|
Temp bool // Whether this is a temp breakpoint (for next'ing).
|
||||||
|
|
||||||
// Breakpoint information
|
// Breakpoint information
|
||||||
Tracepoint bool // Tracepoint flag
|
Tracepoint bool // Tracepoint flag
|
||||||
Goroutine bool // Retrieve goroutine information
|
Goroutine bool // Retrieve goroutine information
|
||||||
Stacktrace int // Number of stack frames to retrieve
|
Stacktrace int // Number of stack frames to retrieve
|
||||||
Variables []string // Variables to evaluate
|
Variables []string // Variables to evaluate
|
||||||
LoadArgs *LoadConfig
|
LoadArgs *LoadConfig
|
||||||
LoadLocals *LoadConfig
|
LoadLocals *LoadConfig
|
||||||
HitCount map[int]uint64 // Number of times a breakpoint has been reached in a certain goroutine
|
HitCount map[int]uint64 // Number of times a breakpoint has been reached in a certain goroutine
|
||||||
|
@ -11,8 +11,8 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/derekparker/delve/dwarf/debug/dwarf"
|
||||||
"github.com/derekparker/delve/dwarf/reader"
|
"github.com/derekparker/delve/dwarf/reader"
|
||||||
"golang.org/x/debug/dwarf"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// EvalExpression returns the value of the given expression.
|
// EvalExpression returns the value of the given expression.
|
||||||
|
87
proc/moduledata.go
Normal file
87
proc/moduledata.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package proc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/constant"
|
||||||
|
)
|
||||||
|
|
||||||
|
// delve counterpart to runtime.moduledata
|
||||||
|
type moduleData struct {
|
||||||
|
types, etypes uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbp *Process) loadModuleData() (err error) {
|
||||||
|
dbp.loadModuleDataOnce.Do(func() {
|
||||||
|
scope := &EvalScope{Thread: dbp.CurrentThread, PC: 0, CFA: 0}
|
||||||
|
var md *Variable
|
||||||
|
md, err = scope.packageVarAddr("runtime.firstmoduledata")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for md.Addr != 0 {
|
||||||
|
var typesVar, etypesVar, nextVar *Variable
|
||||||
|
var types, etypes uint64
|
||||||
|
|
||||||
|
if typesVar, err = md.structMember("types"); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if etypesVar, err = md.structMember("etypes"); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if nextVar, err = md.structMember("next"); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if types, err = typesVar.asUint(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if etypes, err = etypesVar.asUint(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dbp.moduleData = append(dbp.moduleData, moduleData{uintptr(types), uintptr(etypes)})
|
||||||
|
|
||||||
|
md = nextVar.maybeDereference()
|
||||||
|
if md.Unreadable != nil {
|
||||||
|
err = md.Unreadable
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbp *Process) resolveNameOff(typeAddr uintptr, off uintptr) (uintptr, error) {
|
||||||
|
// See runtime.resolveNameOff in $GOROOT/src/runtime/type.go
|
||||||
|
if err := dbp.loadModuleData(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
for _, md := range dbp.moduleData {
|
||||||
|
if typeAddr >= md.types && typeAddr < md.etypes {
|
||||||
|
return md.types + off, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scope := &EvalScope{Thread: dbp.CurrentThread, PC: 0, CFA: 0}
|
||||||
|
reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reflectOffsm, err := reflectOffs.structMember("m")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), dbp.CurrentThread))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resv := v.maybeDereference()
|
||||||
|
if resv.Unreadable != nil {
|
||||||
|
return 0, resv.Unreadable
|
||||||
|
}
|
||||||
|
|
||||||
|
return resv.Addr, nil
|
||||||
|
}
|
@ -14,8 +14,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/debug/dwarf"
|
"github.com/derekparker/delve/dwarf/debug/dwarf"
|
||||||
|
|
||||||
"github.com/derekparker/delve/dwarf/frame"
|
"github.com/derekparker/delve/dwarf/frame"
|
||||||
"github.com/derekparker/delve/dwarf/line"
|
"github.com/derekparker/delve/dwarf/line"
|
||||||
"github.com/derekparker/delve/dwarf/reader"
|
"github.com/derekparker/delve/dwarf/reader"
|
||||||
@ -59,6 +58,9 @@ type Process struct {
|
|||||||
ptraceChan chan func()
|
ptraceChan chan func()
|
||||||
ptraceDoneChan chan interface{}
|
ptraceDoneChan chan interface{}
|
||||||
types map[string]dwarf.Offset
|
types map[string]dwarf.Offset
|
||||||
|
|
||||||
|
loadModuleDataOnce sync.Once
|
||||||
|
moduleData []moduleData
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns an initialized Process struct. Before returning,
|
// New returns an initialized Process struct. Before returning,
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/debug/macho"
|
"github.com/derekparker/delve/dwarf/debug/macho"
|
||||||
|
|
||||||
"github.com/derekparker/delve/dwarf/frame"
|
"github.com/derekparker/delve/dwarf/frame"
|
||||||
"github.com/derekparker/delve/dwarf/line"
|
"github.com/derekparker/delve/dwarf/line"
|
||||||
|
@ -14,10 +14,9 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/debug/elf"
|
|
||||||
|
|
||||||
sys "golang.org/x/sys/unix"
|
sys "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"github.com/derekparker/delve/dwarf/debug/elf"
|
||||||
"github.com/derekparker/delve/dwarf/frame"
|
"github.com/derekparker/delve/dwarf/frame"
|
||||||
"github.com/derekparker/delve/dwarf/line"
|
"github.com/derekparker/delve/dwarf/line"
|
||||||
)
|
)
|
||||||
|
@ -13,10 +13,9 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/debug/dwarf"
|
|
||||||
|
|
||||||
sys "golang.org/x/sys/windows"
|
sys "golang.org/x/sys/windows"
|
||||||
|
|
||||||
|
"github.com/derekparker/delve/dwarf/debug/dwarf"
|
||||||
"github.com/derekparker/delve/dwarf/frame"
|
"github.com/derekparker/delve/dwarf/frame"
|
||||||
"github.com/derekparker/delve/dwarf/line"
|
"github.com/derekparker/delve/dwarf/line"
|
||||||
)
|
)
|
||||||
|
@ -118,7 +118,7 @@ func (it *stackIterator) Next() bool {
|
|||||||
it.frame, it.err = it.dbp.frameInfo(it.pc, it.sp, it.top)
|
it.frame, it.err = it.dbp.frameInfo(it.pc, it.sp, it.top)
|
||||||
if it.err != nil {
|
if it.err != nil {
|
||||||
if _, nofde := it.err.(*frame.NoFDEForPCError); nofde && !it.top {
|
if _, nofde := it.err.(*frame.NoFDEForPCError); nofde && !it.top {
|
||||||
it.frame = Stackframe{ Current: Location{ PC: it.pc, File: "?", Line: -1 }, Call: Location{ PC: it.pc, File: "?", Line: -1 }, CFA: 0, Ret: 0 }
|
it.frame = Stackframe{Current: Location{PC: it.pc, File: "?", Line: -1}, Call: Location{PC: it.pc, File: "?", Line: -1}, CFA: 0, Ret: 0}
|
||||||
it.atend = true
|
it.atend = true
|
||||||
it.err = nil
|
it.err = nil
|
||||||
return true
|
return true
|
||||||
|
@ -9,8 +9,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"golang.org/x/debug/dwarf"
|
"github.com/derekparker/delve/dwarf/debug/dwarf"
|
||||||
|
|
||||||
"github.com/derekparker/delve/dwarf/frame"
|
"github.com/derekparker/delve/dwarf/frame"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -4,11 +4,12 @@ import (
|
|||||||
"github.com/derekparker/delve/dwarf/reader"
|
"github.com/derekparker/delve/dwarf/reader"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/token"
|
"go/token"
|
||||||
"golang.org/x/debug/dwarf"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/derekparker/delve/dwarf/debug/dwarf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Do not call this function directly it isn't able to deal correctly with package paths
|
// Do not call this function directly it isn't able to deal correctly with package paths
|
||||||
|
@ -12,8 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/debug/dwarf"
|
"github.com/derekparker/delve/dwarf/debug/dwarf"
|
||||||
|
|
||||||
"github.com/derekparker/delve/dwarf/op"
|
"github.com/derekparker/delve/dwarf/op"
|
||||||
"github.com/derekparker/delve/dwarf/reader"
|
"github.com/derekparker/delve/dwarf/reader"
|
||||||
)
|
)
|
||||||
@ -368,7 +367,7 @@ func (gvar *Variable) parseG() (*G, error) {
|
|||||||
if thread, ok := mem.(*Thread); ok {
|
if thread, ok := mem.(*Thread); ok {
|
||||||
id = thread.ID
|
id = thread.ID
|
||||||
}
|
}
|
||||||
return nil, NoGError{ tid: id }
|
return nil, NoGError{tid: id}
|
||||||
}
|
}
|
||||||
gvar.loadValue(loadFullValue)
|
gvar.loadValue(loadFullValue)
|
||||||
if gvar.Unreadable != nil {
|
if gvar.Unreadable != nil {
|
||||||
@ -627,10 +626,10 @@ func (v *Variable) structMember(memberName string) (*Variable, error) {
|
|||||||
// not a regular struct member
|
// not a regular struct member
|
||||||
for _, field := range t.Field {
|
for _, field := range t.Field {
|
||||||
isEmbeddedStructMember :=
|
isEmbeddedStructMember :=
|
||||||
(field.Type.String() == ("struct " + field.Name)) ||
|
(field.Type.String() == field.Name) ||
|
||||||
(len(field.Name) > 1 &&
|
(len(field.Name) > 1 &&
|
||||||
field.Name[0] == '*' &&
|
field.Name[0] == '*' &&
|
||||||
field.Type.String()[1:] == ("struct "+field.Name[1:]))
|
field.Type.String()[1:] == field.Name[1:])
|
||||||
if !isEmbeddedStructMember {
|
if !isEmbeddedStructMember {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -1394,9 +1393,38 @@ func mapEvacuated(b *Variable) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig) {
|
func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig) {
|
||||||
var typestring, data *Variable
|
var _type, str, typestring, data *Variable
|
||||||
|
var typename string
|
||||||
|
var err error
|
||||||
isnil := false
|
isnil := false
|
||||||
|
|
||||||
|
// An interface variable is implemented either by a runtime.iface
|
||||||
|
// struct or a runtime.eface struct. The difference being that empty
|
||||||
|
// interfaces (i.e. "interface {}") are represented by runtime.eface
|
||||||
|
// and non-empty interfaces by runtime.iface.
|
||||||
|
//
|
||||||
|
// For both runtime.ifaces and runtime.efaces the data is stored in v.data
|
||||||
|
//
|
||||||
|
// The concrete type however is stored in v.tab._type for non-empty
|
||||||
|
// interfaces and in v._type for empty interfaces.
|
||||||
|
//
|
||||||
|
// For nil empty interface variables _type will be nil, for nil
|
||||||
|
// non-empty interface variables tab will be nil
|
||||||
|
//
|
||||||
|
// In either case the _type field is a pointer to a runtime._type struct.
|
||||||
|
//
|
||||||
|
// Before go1.7 _type used to have a field named 'string' containing
|
||||||
|
// the name of the type. Since go1.7 the field has been replaced by a
|
||||||
|
// str field that contains an offset in the module data, the concrete
|
||||||
|
// type must be calculated using the str address along with the value
|
||||||
|
// of v.tab._type (v._type for empty interfaces).
|
||||||
|
//
|
||||||
|
// The following code works for both runtime.iface and runtime.eface
|
||||||
|
// and sets the go17 flag when the 'string' field can not be found
|
||||||
|
// but the str field was found
|
||||||
|
|
||||||
|
go17 := false
|
||||||
|
|
||||||
v.mem = cacheMemory(v.mem, v.Addr, int(v.RealType.Size()))
|
v.mem = cacheMemory(v.mem, v.Addr, int(v.RealType.Size()))
|
||||||
|
|
||||||
ityp := resolveTypedef(&v.RealType.(*dwarf.InterfaceType).TypedefType).(*dwarf.StructType)
|
ityp := resolveTypedef(&v.RealType.(*dwarf.InterfaceType).TypedefType).(*dwarf.StructType)
|
||||||
@ -1405,33 +1433,42 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
|
|||||||
switch f.Name {
|
switch f.Name {
|
||||||
case "tab": // for runtime.iface
|
case "tab": // for runtime.iface
|
||||||
tab, _ := v.toField(f)
|
tab, _ := v.toField(f)
|
||||||
_type, err := tab.structMember("_type")
|
tab = tab.maybeDereference()
|
||||||
if err != nil {
|
isnil = tab.Addr == 0
|
||||||
_, isnil = err.(*IsNilErr)
|
if !isnil {
|
||||||
if !isnil {
|
_type, err = tab.structMember("_type")
|
||||||
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
typestring, err = _type.structMember("_string")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
typestring = typestring.maybeDereference()
|
typestring, err = _type.structMember("_string")
|
||||||
|
if err == nil {
|
||||||
|
typestring = typestring.maybeDereference()
|
||||||
|
} else {
|
||||||
|
go17 = true
|
||||||
|
str, err = _type.structMember("str")
|
||||||
|
if err != nil {
|
||||||
|
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case "_type": // for runtime.eface
|
case "_type": // for runtime.eface
|
||||||
var err error
|
_type, _ = v.toField(f)
|
||||||
_type, _ := v.toField(f)
|
_type = _type.maybeDereference()
|
||||||
typestring, err = _type.structMember("_string")
|
isnil = _type.Addr == 0
|
||||||
if err != nil {
|
if !isnil {
|
||||||
_, isnil = err.(*IsNilErr)
|
typestring, err = _type.structMember("_string")
|
||||||
if !isnil {
|
if err == nil {
|
||||||
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
typestring = typestring.maybeDereference()
|
||||||
return
|
} else {
|
||||||
|
go17 = true
|
||||||
|
str, err = _type.structMember("str")
|
||||||
|
if err != nil {
|
||||||
|
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
typestring = typestring.maybeDereference()
|
|
||||||
}
|
}
|
||||||
case "data":
|
case "data":
|
||||||
data, _ = v.toField(f)
|
data, _ = v.toField(f)
|
||||||
@ -1448,17 +1485,56 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if typestring == nil || data == nil || typestring.Addr == 0 || typestring.Kind != reflect.String {
|
if data == nil {
|
||||||
v.Unreadable = fmt.Errorf("invalid interface type")
|
v.Unreadable = fmt.Errorf("invalid interface type")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
typestring.loadValue(LoadConfig{false, 0, 512, 0, 0})
|
|
||||||
if typestring.Unreadable != nil {
|
if go17 {
|
||||||
v.Unreadable = fmt.Errorf("invalid interface type: %v", typestring.Unreadable)
|
// No 'string' field use 'str' and 'runtime.firstmoduledata' to
|
||||||
return
|
// find out what the concrete type is
|
||||||
|
|
||||||
|
typeAddr := _type.maybeDereference().Addr
|
||||||
|
strOff, err := str.asInt()
|
||||||
|
if err != nil {
|
||||||
|
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := v.dbp.resolveNameOff(typeAddr, uintptr(strOff))
|
||||||
|
if err != nil {
|
||||||
|
v.Unreadable = fmt.Errorf("could not resolve concrete type (data: %#x): %v", data.Addr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// For a description of how memory is organized for type names read
|
||||||
|
// the comment to 'type name struct' in $GOROOT/src/reflect/type.go
|
||||||
|
|
||||||
|
typdata, err := v.dbp.CurrentThread.readMemory(res, 3+v.dbp.arch.PtrSize())
|
||||||
|
if err != nil {
|
||||||
|
v.Unreadable = fmt.Errorf("could not read concrete type (data: %#v): %v", data.Addr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nl := int(typdata[1]<<8 | typdata[2])
|
||||||
|
|
||||||
|
rawstr, err := v.dbp.CurrentThread.readMemory(res+3, nl)
|
||||||
|
|
||||||
|
typename = string(rawstr)
|
||||||
|
} else {
|
||||||
|
if typestring == nil || typestring.Addr == 0 || typestring.Kind != reflect.String {
|
||||||
|
v.Unreadable = fmt.Errorf("invalid interface type")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
typestring.loadValue(LoadConfig{false, 0, 512, 0, 0})
|
||||||
|
if typestring.Unreadable != nil {
|
||||||
|
v.Unreadable = fmt.Errorf("invalid interface type: %v", typestring.Unreadable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
typename = constant.StringVal(typestring.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := parser.ParseExpr(constant.StringVal(typestring.Value))
|
t, err := parser.ParseExpr(typename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.Unreadable = fmt.Errorf("invalid interface type, unparsable data type: %v", err)
|
v.Unreadable = fmt.Errorf("invalid interface type, unparsable data type: %v", err)
|
||||||
return
|
return
|
||||||
@ -1466,7 +1542,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
|
|||||||
|
|
||||||
typ, err := v.dbp.findTypeExpr(t)
|
typ, err := v.dbp.findTypeExpr(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.Unreadable = fmt.Errorf("interface type \"%s\" not found for 0x%x: %v", constant.StringVal(typestring.Value), data.Addr, err)
|
v.Unreadable = fmt.Errorf("interface type \"%s\" not found for 0x%x: %v", typename, data.Addr, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,10 +6,10 @@ import (
|
|||||||
"go/constant"
|
"go/constant"
|
||||||
"go/printer"
|
"go/printer"
|
||||||
"go/token"
|
"go/token"
|
||||||
"golang.org/x/debug/dwarf"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/derekparker/delve/dwarf/debug/dwarf"
|
||||||
"github.com/derekparker/delve/proc"
|
"github.com/derekparker/delve/proc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -581,18 +581,18 @@ func Test1ClientServer_FindLocations(t *testing.T) {
|
|||||||
t.Fatalf("Wrong locations returned for \"/.*Type.*String/\", got: %v expected: %v and %v\n", stringAddrs, someTypeStringFuncAddr, otherTypeStringFuncAddr)
|
t.Fatalf("Wrong locations returned for \"/.*Type.*String/\", got: %v expected: %v and %v\n", stringAddrs, someTypeStringFuncAddr, otherTypeStringFuncAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 3, Tracepoint: false})
|
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 4, Tracepoint: false})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("CreateBreakpoint(): %v\n", err)
|
t.Fatalf("CreateBreakpoint(): %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
<-c.Continue()
|
<-c.Continue()
|
||||||
|
|
||||||
locationsprog34Addr := findLocationHelper(t, c, "locationsprog.go:34", false, 1, 0)[0]
|
locationsprog35Addr := findLocationHelper(t, c, "locationsprog.go:35", false, 1, 0)[0]
|
||||||
findLocationHelper(t, c, fmt.Sprintf("%s:34", testProgPath(t, "locationsprog")), false, 1, locationsprog34Addr)
|
findLocationHelper(t, c, fmt.Sprintf("%s:35", testProgPath(t, "locationsprog")), false, 1, locationsprog35Addr)
|
||||||
findLocationHelper(t, c, "+1", false, 1, locationsprog34Addr)
|
findLocationHelper(t, c, "+1", false, 1, locationsprog35Addr)
|
||||||
findLocationHelper(t, c, "34", false, 1, locationsprog34Addr)
|
findLocationHelper(t, c, "35", false, 1, locationsprog35Addr)
|
||||||
findLocationHelper(t, c, "-1", false, 1, findLocationHelper(t, c, "locationsprog.go:32", false, 1, 0)[0])
|
findLocationHelper(t, c, "-1", false, 1, findLocationHelper(t, c, "locationsprog.go:33", false, 1, 0)[0])
|
||||||
})
|
})
|
||||||
|
|
||||||
withTestClient1("testnextdefer", t, func(c *rpc1.RPCClient) {
|
withTestClient1("testnextdefer", t, func(c *rpc1.RPCClient) {
|
||||||
|
@ -588,18 +588,18 @@ func TestClientServer_FindLocations(t *testing.T) {
|
|||||||
t.Fatalf("Wrong locations returned for \"/.*Type.*String/\", got: %v expected: %v and %v\n", stringAddrs, someTypeStringFuncAddr, otherTypeStringFuncAddr)
|
t.Fatalf("Wrong locations returned for \"/.*Type.*String/\", got: %v expected: %v and %v\n", stringAddrs, someTypeStringFuncAddr, otherTypeStringFuncAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 3, Tracepoint: false})
|
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 4, Tracepoint: false})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("CreateBreakpoint(): %v\n", err)
|
t.Fatalf("CreateBreakpoint(): %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
<-c.Continue()
|
<-c.Continue()
|
||||||
|
|
||||||
locationsprog34Addr := findLocationHelper(t, c, "locationsprog.go:34", false, 1, 0)[0]
|
locationsprog35Addr := findLocationHelper(t, c, "locationsprog.go:35", false, 1, 0)[0]
|
||||||
findLocationHelper(t, c, fmt.Sprintf("%s:34", testProgPath(t, "locationsprog")), false, 1, locationsprog34Addr)
|
findLocationHelper(t, c, fmt.Sprintf("%s:35", testProgPath(t, "locationsprog")), false, 1, locationsprog35Addr)
|
||||||
findLocationHelper(t, c, "+1", false, 1, locationsprog34Addr)
|
findLocationHelper(t, c, "+1", false, 1, locationsprog35Addr)
|
||||||
findLocationHelper(t, c, "34", false, 1, locationsprog34Addr)
|
findLocationHelper(t, c, "35", false, 1, locationsprog35Addr)
|
||||||
findLocationHelper(t, c, "-1", false, 1, findLocationHelper(t, c, "locationsprog.go:32", false, 1, 0)[0])
|
findLocationHelper(t, c, "-1", false, 1, findLocationHelper(t, c, "locationsprog.go:33", false, 1, 0)[0])
|
||||||
})
|
})
|
||||||
|
|
||||||
withTestClient2("testnextdefer", t, func(c service.Client) {
|
withTestClient2("testnextdefer", t, func(c service.Client) {
|
||||||
|
@ -374,7 +374,7 @@ func TestEmbeddedStruct(t *testing.T) {
|
|||||||
{"b.ptr.val", true, "1337", "1337", "int", nil},
|
{"b.ptr.val", true, "1337", "1337", "int", nil},
|
||||||
{"b.C.s", true, "\"hello\"", "\"hello\"", "string", nil},
|
{"b.C.s", true, "\"hello\"", "\"hello\"", "string", nil},
|
||||||
{"b.s", true, "\"hello\"", "\"hello\"", "string", nil},
|
{"b.s", true, "\"hello\"", "\"hello\"", "string", nil},
|
||||||
{"b2", true, "main.B {main.A: struct main.A {val: 42}, *main.C: *struct main.C nil, a: main.A {val: 47}, ptr: *main.A nil}", "main.B {main.A: (*struct main.A)(0x…", "main.B", nil},
|
{"b2", true, "main.B {main.A: main.A {val: 42}, *main.C: *main.C nil, a: main.A {val: 47}, ptr: *main.A nil}", "main.B {main.A: (*main.A)(0x…", "main.B", nil},
|
||||||
}
|
}
|
||||||
assertNoError(p.Continue(), t, "Continue()")
|
assertNoError(p.Continue(), t, "Continue()")
|
||||||
|
|
||||||
@ -459,7 +459,7 @@ func TestEvalExpression(t *testing.T) {
|
|||||||
{"ch1+1", false, "", "", "", fmt.Errorf("can not convert 1 constant to chan int")},
|
{"ch1+1", false, "", "", "", fmt.Errorf("can not convert 1 constant to chan int")},
|
||||||
|
|
||||||
// maps
|
// maps
|
||||||
{"m1[\"Malone\"]", false, "struct main.astruct {A: 2, B: 3}", "struct main.astruct {A: 2, B: 3}", "struct main.astruct", nil},
|
{"m1[\"Malone\"]", false, "main.astruct {A: 2, B: 3}", "main.astruct {A: 2, B: 3}", "main.astruct", nil},
|
||||||
{"m2[1].B", false, "11", "11", "int", nil},
|
{"m2[1].B", false, "11", "11", "int", nil},
|
||||||
{"m2[c1.sa[2].B-4].A", false, "10", "10", "int", nil},
|
{"m2[c1.sa[2].B-4].A", false, "10", "10", "int", nil},
|
||||||
{"m2[*p1].B", false, "11", "11", "int", nil},
|
{"m2[*p1].B", false, "11", "11", "int", nil},
|
||||||
@ -468,10 +468,10 @@ func TestEvalExpression(t *testing.T) {
|
|||||||
{"m1[80:]", false, "", "", "", fmt.Errorf("map index out of bounds")},
|
{"m1[80:]", false, "", "", "", fmt.Errorf("map index out of bounds")},
|
||||||
|
|
||||||
// interfaces
|
// interfaces
|
||||||
{"err1", true, "error(*struct main.astruct) *{A: 1, B: 2}", "error(*struct main.astruct) 0x…", "error", nil},
|
{"err1", true, "error(*main.astruct) *{A: 1, B: 2}", "error(*main.astruct) 0x…", "error", nil},
|
||||||
{"err2", true, "error(*struct main.bstruct) *{a: main.astruct {A: 1, B: 2}}", "error(*struct main.bstruct) 0x…", "error", nil},
|
{"err2", true, "error(*main.bstruct) *{a: main.astruct {A: 1, B: 2}}", "error(*main.bstruct) 0x…", "error", nil},
|
||||||
{"errnil", true, "error nil", "error nil", "error", nil},
|
{"errnil", true, "error nil", "error nil", "error", nil},
|
||||||
{"iface1", true, "interface {}(*struct main.astruct) *{A: 1, B: 2}", "interface {}(*struct main.astruct) 0x…", "interface {}", nil},
|
{"iface1", true, "interface {}(*main.astruct) *{A: 1, B: 2}", "interface {}(*main.astruct) 0x…", "interface {}", nil},
|
||||||
{"iface2", true, "interface {}(*string) *\"test\"", "interface {}(*string) 0x…", "interface {}", nil},
|
{"iface2", true, "interface {}(*string) *\"test\"", "interface {}(*string) 0x…", "interface {}", nil},
|
||||||
{"iface3", true, "interface {}(*map[string]go/constant.Value) *[]", "interface {}(*map[string]go/constant.Value) 0x…", "interface {}", nil},
|
{"iface3", true, "interface {}(*map[string]go/constant.Value) *[]", "interface {}(*map[string]go/constant.Value) 0x…", "interface {}", nil},
|
||||||
{"iface4", true, "interface {}(*[]go/constant.Value) *[*4]", "interface {}(*[]go/constant.Value) 0x…", "interface {}", nil},
|
{"iface4", true, "interface {}(*[]go/constant.Value) *[*4]", "interface {}(*[]go/constant.Value) 0x…", "interface {}", nil},
|
||||||
@ -480,8 +480,8 @@ func TestEvalExpression(t *testing.T) {
|
|||||||
{"err1 == iface1", false, "", "", "", fmt.Errorf("mismatched types \"error\" and \"interface {}\"")},
|
{"err1 == iface1", false, "", "", "", fmt.Errorf("mismatched types \"error\" and \"interface {}\"")},
|
||||||
{"errnil == nil", false, "false", "false", "", nil},
|
{"errnil == nil", false, "false", "false", "", nil},
|
||||||
{"nil == errnil", false, "false", "false", "", nil},
|
{"nil == errnil", false, "false", "false", "", nil},
|
||||||
{"err1.(*main.astruct)", false, "*struct main.astruct {A: 1, B: 2}", "(*struct main.astruct)(0x…", "*struct main.astruct", nil},
|
{"err1.(*main.astruct)", false, "*main.astruct {A: 1, B: 2}", "(*main.astruct)(0x…", "*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 *main.astruct, not *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(*go/constant.int64Val) 0x…", "go/constant.Value", nil},
|
{"const1", true, "go/constant.Value(*go/constant.int64Val) *3", "go/constant.Value(*go/constant.int64Val) 0x…", "go/constant.Value", nil},
|
||||||
|
|
||||||
@ -669,7 +669,7 @@ func TestEvalAddrAndCast(t *testing.T) {
|
|||||||
a, err := evalVariable(p, "*"+aaddrstr, pnormalLoadConfig)
|
a, err := evalVariable(p, "*"+aaddrstr, pnormalLoadConfig)
|
||||||
assertNoError(err, t, fmt.Sprintf("EvalExpression(*%s)", aaddrstr))
|
assertNoError(err, t, fmt.Sprintf("EvalExpression(*%s)", aaddrstr))
|
||||||
t.Logf("*%s → %s", aaddrstr, api.ConvertVar(a).SinglelineString())
|
t.Logf("*%s → %s", aaddrstr, api.ConvertVar(a).SinglelineString())
|
||||||
assertVariable(t, a, varTest{aaddrstr, false, "struct main.astruct {A: 1, B: 2}", "", "struct main.astruct", nil})
|
assertVariable(t, a, varTest{aaddrstr, false, "main.astruct {A: 1, B: 2}", "", "main.astruct", nil})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user