proc: always resolve array types even if they don't appear in the
program When evaluating type casts always resolve array types. Instead of resolving them by looking up the string in debug_info construct a fake array type so that a type cast to an array type always works as long as the element type exists. We already did this for byte arrays, this commit extends this to any array type. The reason is that we return a fake array type (that doesn't exist in the target program) for the array of a channel type. Fixes #1736
This commit is contained in:
parent
f1a5e654ff
commit
5a947bceff
@ -125,7 +125,7 @@ func main() {
|
||||
var fn2 functype = nil
|
||||
var nilslice []int = nil
|
||||
var nilptr *int = nil
|
||||
ch1 := make(chan int, 10)
|
||||
ch1 := make(chan int, 11)
|
||||
ch1 <- 1
|
||||
ch1 <- 4
|
||||
ch1 <- 3
|
||||
|
@ -15,7 +15,6 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -1177,6 +1176,18 @@ func (bi *BinaryInfo) findTypeExpr(expr ast.Expr) (godwarf.Type, error) {
|
||||
// string. Useful as a catch-all workaround for cases where we don't
|
||||
// parse/serialize types correctly or can not resolve package paths.
|
||||
typn, _ := strconv.Unquote(lit.Value)
|
||||
|
||||
// Check if the type in question is an array type, in which case we try to
|
||||
// fake it.
|
||||
if len(typn) > 0 && typn[0] == '[' {
|
||||
closedBrace := strings.Index(typn, "]")
|
||||
if closedBrace > 1 {
|
||||
n, err := strconv.Atoi(typn[1:closedBrace])
|
||||
if err == nil {
|
||||
return bi.findArrayType(n, typn[closedBrace+1:])
|
||||
}
|
||||
}
|
||||
}
|
||||
return bi.findType(typn)
|
||||
}
|
||||
bi.expandPackagesInType(expr)
|
||||
@ -1192,34 +1203,37 @@ func (bi *BinaryInfo) findTypeExpr(expr ast.Expr) (godwarf.Type, error) {
|
||||
return pointerTo(ptyp, bi.Arch), nil
|
||||
}
|
||||
if anode, ok := expr.(*ast.ArrayType); ok {
|
||||
// Byte array types (i.e. [N]byte) are only present in DWARF if they are
|
||||
// Array types (for example [N]byte) are only present in DWARF if they are
|
||||
// used by the program, but it's convenient to make all of them available
|
||||
// to the user so that they can be used to read arbitrary memory, byte by
|
||||
// byte.
|
||||
// to the user for two reasons:
|
||||
// 1. to allow reading arbitrary memory byte-by-byte (by casting an
|
||||
// address to an array of bytes).
|
||||
// 2. to read the contents of a channel's buffer (we create fake array
|
||||
// types for them)
|
||||
|
||||
alen, litlen := anode.Len.(*ast.BasicLit)
|
||||
if litlen && alen.Kind == token.INT {
|
||||
n, _ := strconv.Atoi(alen.Value)
|
||||
switch exprToString(anode.Elt) {
|
||||
case "byte", "uint8":
|
||||
btyp, err := bi.findType("uint8")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &godwarf.ArrayType{
|
||||
CommonType: godwarf.CommonType{
|
||||
ReflectKind: reflect.Array,
|
||||
ByteSize: int64(n),
|
||||
Name: fmt.Sprintf("[%d]uint8", n)},
|
||||
Type: btyp,
|
||||
StrideBitSize: 8,
|
||||
Count: int64(n)}, nil
|
||||
}
|
||||
return bi.findArrayType(n, exprToString(anode.Elt))
|
||||
}
|
||||
}
|
||||
return bi.findType(exprToString(expr))
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) findArrayType(n int, etyp string) (godwarf.Type, error) {
|
||||
switch etyp {
|
||||
case "byte", "uint8":
|
||||
etyp = "uint8"
|
||||
fallthrough
|
||||
default:
|
||||
btyp, err := bi.findType(etyp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fakeArrayType(uint64(n), btyp), nil
|
||||
}
|
||||
}
|
||||
|
||||
func complexType(typename string) bool {
|
||||
for _, ch := range typename {
|
||||
switch ch {
|
||||
|
@ -1903,6 +1903,18 @@ func fakeSliceType(fieldType godwarf.Type) godwarf.Type {
|
||||
}
|
||||
}
|
||||
|
||||
func fakeArrayType(n uint64, fieldType godwarf.Type) godwarf.Type {
|
||||
stride := alignAddr(fieldType.Common().ByteSize, fieldType.Align())
|
||||
return &godwarf.ArrayType{
|
||||
CommonType: godwarf.CommonType{
|
||||
ReflectKind: reflect.Array,
|
||||
ByteSize: int64(n) * stride,
|
||||
Name: fmt.Sprintf("[%d]%s", n, fieldType.String())},
|
||||
Type: fieldType,
|
||||
StrideBitSize: stride * 8,
|
||||
Count: int64(n)}
|
||||
}
|
||||
|
||||
var errMethodEvalUnsupported = errors.New("evaluating methods not supported on this version of Go")
|
||||
|
||||
func (fn *Function) fakeType(bi *BinaryInfo, removeReceiver bool) (*godwarf.FuncType, error) {
|
||||
|
@ -4407,3 +4407,16 @@ func TestBreakpointConfusionOnResume(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestIssue1736(t *testing.T) {
|
||||
withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
|
||||
assertNoError(proc.Continue(p), t, "Continue()")
|
||||
ch1BufVar := evalVariable(p, t, "*(ch1.buf)")
|
||||
q := fmt.Sprintf("*(*%q)(%d)", ch1BufVar.DwarfType.Common().Name, ch1BufVar.Addr)
|
||||
t.Logf("%s", q)
|
||||
ch1BufVar2 := evalVariable(p, t, q)
|
||||
if ch1BufVar2.Unreadable != nil {
|
||||
t.Fatal(ch1BufVar2.Unreadable)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1160,17 +1160,7 @@ func (v *Variable) loadChanInfo() {
|
||||
field := &godwarf.StructField{}
|
||||
*field = *structType.Field[i]
|
||||
if field.Name == "buf" {
|
||||
stride := chanType.ElemType.Common().ByteSize
|
||||
atyp := &godwarf.ArrayType{
|
||||
CommonType: godwarf.CommonType{
|
||||
ReflectKind: reflect.Array,
|
||||
ByteSize: int64(chanLen) * stride,
|
||||
Name: fmt.Sprintf("[%d]%s", chanLen, chanType.ElemType.String())},
|
||||
Type: chanType.ElemType,
|
||||
StrideBitSize: stride * 8,
|
||||
Count: int64(chanLen)}
|
||||
|
||||
field.Type = pointerTo(atyp, v.bi.Arch)
|
||||
field.Type = pointerTo(fakeArrayType(chanLen, chanType.ElemType), v.bi.Arch)
|
||||
}
|
||||
newStructType.Field[i] = field
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ func TestSetVariable(t *testing.T) {
|
||||
{"b.ptr", "*main.A", "*main.A {val: 1337}", "nil", "*main.A nil"},
|
||||
{"m2", "map[int]*main.astruct", "map[int]*main.astruct [1: *{A: 10, B: 11}, ]", "nil", "map[int]*main.astruct nil"},
|
||||
{"fn1", "main.functype", "main.afunc", "nil", "nil"},
|
||||
{"ch1", "chan int", "chan int 4/10", "nil", "chan int nil"},
|
||||
{"ch1", "chan int", "chan int 4/11", "nil", "chan int nil"},
|
||||
{"s2", "[]main.astruct", "[]main.astruct len: 8, cap: 8, [{A: 1, B: 2},{A: 3, B: 4},{A: 5, B: 6},{A: 7, B: 8},{A: 9, B: 10},{A: 11, B: 12},{A: 13, B: 14},{A: 15, B: 16}]", "nil", "[]main.astruct len: 0, cap: 0, nil"},
|
||||
{"err1", "error", "error(*main.astruct) *{A: 1, B: 2}", "nil", "error nil"},
|
||||
{"s1[0]", "string", `"one"`, `""`, `""`},
|
||||
@ -587,7 +587,7 @@ func TestEvalExpression(t *testing.T) {
|
||||
{"*p3", false, "", "", "int", fmt.Errorf("nil pointer dereference")},
|
||||
|
||||
// channels
|
||||
{"ch1", true, "chan int 4/10", "chan int 4/10", "chan int", nil},
|
||||
{"ch1", true, "chan int 4/11", "chan int 4/11", "chan int", nil},
|
||||
{"chnil", true, "chan int nil", "chan int nil", "chan int", nil},
|
||||
{"ch1+1", false, "", "", "", fmt.Errorf("can not convert 1 constant to chan int")},
|
||||
|
||||
@ -682,7 +682,7 @@ func TestEvalExpression(t *testing.T) {
|
||||
{"len(s3)", false, "0", "0", "", nil},
|
||||
{"cap(nilslice)", false, "0", "0", "", nil},
|
||||
{"len(nilslice)", false, "0", "0", "", nil},
|
||||
{"cap(ch1)", false, "10", "10", "", nil},
|
||||
{"cap(ch1)", false, "11", "11", "", nil},
|
||||
{"len(ch1)", false, "4", "4", "", nil},
|
||||
{"cap(chnil)", false, "0", "0", "", nil},
|
||||
{"len(chnil)", false, "0", "0", "", nil},
|
||||
@ -790,8 +790,8 @@ func TestEvalExpression(t *testing.T) {
|
||||
|
||||
// access to channel field members
|
||||
{"ch1.qcount", false, "4", "4", "uint", nil},
|
||||
{"ch1.dataqsiz", false, "10", "10", "uint", nil},
|
||||
{"ch1.buf", false, `*[10]int [1,4,3,2,0,0,0,0,0,0]`, `(*[10]int)(…`, "*[10]int", nil},
|
||||
{"ch1.dataqsiz", false, "11", "11", "uint", nil},
|
||||
{"ch1.buf", false, `*[11]int [1,4,3,2,0,0,0,0,0,0,0]`, `(*[11]int)(…`, "*[11]int", nil},
|
||||
{"ch1.buf[0]", false, "1", "1", "int", nil},
|
||||
|
||||
// shortcircuited logical operators
|
||||
@ -1185,8 +1185,8 @@ func TestCallFunction(t *testing.T) {
|
||||
{`strings.Join(stringslice, ",")`, []string{`:string:"one,two,three"`}, nil},
|
||||
{`strings.LastIndexByte(stringslice[1], 'w')`, []string{":int:1"}, nil},
|
||||
{`strings.LastIndexByte(stringslice[1], 'o')`, []string{":int:2"}, nil},
|
||||
{`d.Base.Method()`, []string{ `:int:4` }, nil },
|
||||
{`d.Method()`, []string{ `:int:4` }, nil },
|
||||
{`d.Base.Method()`, []string{`:int:4`}, nil},
|
||||
{`d.Method()`, []string{`:int:4`}, nil},
|
||||
}
|
||||
|
||||
var testcases113 = []testCaseCallFunction{
|
||||
|
Loading…
Reference in New Issue
Block a user