提交 5a947bce 编写于 作者: A aarzilli 提交者: Alessandro Arzilli

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
上级 f1a5e654
......@@ -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{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册