diff --git a/_fixtures/testvariables2.go b/_fixtures/testvariables2.go index e227fea8..a27fb97e 100644 --- a/_fixtures/testvariables2.go +++ b/_fixtures/testvariables2.go @@ -108,7 +108,11 @@ func main() { var fn2 functype = nil var nilslice []int = nil var nilptr *int = nil - ch1 := make(chan int, 2) + ch1 := make(chan int, 10) + ch1 <- 1 + ch1 <- 4 + ch1 <- 3 + ch1 <- 2 var chnil chan int = nil m1 := map[string]astruct{ "Malone": astruct{2, 3}, diff --git a/pkg/proc/eval.go b/pkg/proc/eval.go index c111962c..643ddf0c 100644 --- a/pkg/proc/eval.go +++ b/pkg/proc/eval.go @@ -647,12 +647,27 @@ func (scope *EvalScope) evalIndex(node *ast.IndexExpr) (*Variable, error) { return nil, xev.Unreadable } + xev = xev.maybeDereference() + idxev, err := scope.evalAST(node.Index) if err != nil { return nil, err } + cantindex := fmt.Errorf("expression \"%s\" (%s) does not support indexing", exprToString(node.X), xev.TypeString()) + switch xev.Kind { + case reflect.Ptr: + if xev == nilVariable { + return nil, cantindex + } + _, isarrptr := xev.RealType.(*godwarf.PtrType).Type.(*godwarf.ArrayType) + if !isarrptr { + return nil, cantindex + } + xev = xev.maybeDereference() + fallthrough + case reflect.Slice, reflect.Array, reflect.String: if xev.Base == 0 { return nil, fmt.Errorf("can not index \"%s\"", exprToString(node.X)) @@ -670,8 +685,7 @@ func (scope *EvalScope) evalIndex(node *ast.IndexExpr) (*Variable, error) { } return xev.mapAccess(idxev) default: - return nil, fmt.Errorf("expression \"%s\" (%s) does not support indexing", exprToString(node.X), xev.TypeString()) - + return nil, cantindex } } diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index 7d47c9f2..aec9b4cb 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -198,6 +198,9 @@ func newVariable(name string, addr uintptr, dwarfType godwarf.Type, bi *BinaryIn } case *godwarf.ChanType: v.Kind = reflect.Chan + if v.Addr != 0 { + v.loadChanInfo() + } case *godwarf.MapType: v.Kind = reflect.Map case *godwarf.StringType: @@ -663,6 +666,11 @@ func (v *Variable) structMember(memberName string) (*Variable, error) { if v.Unreadable != nil { return v.clone(), nil } + switch v.Kind { + case reflect.Chan: + v = v.clone() + v.RealType = resolveTypedef(&(v.RealType.(*godwarf.ChanType).TypedefType)) + } structVar := v.maybeDereference() structVar.Name = v.Name if structVar.Unreadable != nil { @@ -1010,6 +1018,66 @@ func (v *Variable) loadSliceInfo(t *godwarf.SliceType) { } } +// loadChanInfo loads the buffer size of the channel and changes the type of +// the buf field from unsafe.Pointer to an array of the correct type. +func (v *Variable) loadChanInfo() { + chanType, ok := v.RealType.(*godwarf.ChanType) + if !ok { + v.Unreadable = errors.New("bad channel type") + return + } + sv := v.clone() + sv.RealType = resolveTypedef(&(chanType.TypedefType)) + sv = sv.maybeDereference() + if sv.Unreadable != nil || sv.Addr == 0 { + return + } + structType, ok := sv.DwarfType.(*godwarf.StructType) + if !ok { + v.Unreadable = errors.New("bad channel type") + return + } + + lenAddr, _ := sv.toField(structType.Field[1]) + lenAddr.loadValue(loadSingleValue) + if lenAddr.Unreadable != nil { + v.Unreadable = fmt.Errorf("unreadable length: %v", lenAddr.Unreadable) + return + } + chanLen, _ := constant.Uint64Val(lenAddr.Value) + + newStructType := &godwarf.StructType{} + *newStructType = *structType + newStructType.Field = make([]*godwarf.StructField, len(structType.Field)) + + for i := range structType.Field { + 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) + } + newStructType.Field[i] = field + } + + v.RealType = &godwarf.ChanType{ + TypedefType: godwarf.TypedefType{ + CommonType: chanType.TypedefType.CommonType, + Type: pointerTo(newStructType, v.bi.Arch), + }, + ElemType: chanType.ElemType, + } +} + func (v *Variable) loadArrayValues(recurseLevel int, cfg LoadConfig) { if v.Unreadable != nil { return diff --git a/service/test/variables_test.go b/service/test/variables_test.go index beabe157..b90973b3 100644 --- a/service/test/variables_test.go +++ b/service/test/variables_test.go @@ -534,7 +534,7 @@ func TestEvalExpression(t *testing.T) { {"*p3", false, "", "", "int", fmt.Errorf("nil pointer dereference")}, // channels - {"ch1", true, "chan int 0/2", "chan int 0/2", "chan int", nil}, + {"ch1", true, "chan int 4/10", "chan int 4/10", "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")}, @@ -626,8 +626,8 @@ 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, "2", "2", "", nil}, - {"len(ch1)", false, "0", "0", "", nil}, + {"cap(ch1)", false, "10", "10", "", nil}, + {"len(ch1)", false, "4", "4", "", nil}, {"cap(chnil)", false, "0", "0", "", nil}, {"len(chnil)", false, "0", "0", "", nil}, {"len(m1)", false, "41", "41", "", nil}, @@ -727,6 +727,12 @@ func TestEvalExpression(t *testing.T) { {"string(runeslice)", false, `"tèst"`, `""`, "string", nil}, {"[]byte(string(runeslice))", false, `[]uint8 len: 5, cap: 5, [116,195,168,115,116]`, `[]uint8 len: 0, cap: 0, nil`, "[]uint8", nil}, {"*(*[5]byte)(uintptr(&byteslice[0]))", false, `[5]uint8 [116,195,168,115,116]`, `[5]uint8 [...]`, "[5]uint8", nil}, + + // 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.buf[0]", false, "1", "1", "int", nil}, } ver, _ := goversion.Parse(runtime.Version())