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:
Alessandro Arzilli 2016-05-29 21:20:09 +02:00 committed by Derek Parker
parent 5933a0f48f
commit 9bc6ad4f46
35 changed files with 253 additions and 89 deletions

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

@ -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})
}) })
} }