func TestNextConcurrentVariant2(t *testing.T) { // Just like TestNextConcurrent but instead of removing the initial breakpoint we check that when it happens is for other goroutines testcases := []nextTest{ {8, 9}, {9, 10}, {10, 11}, } withTestProcess("parallel_next", t, func(p *Process, fixture protest.Fixture) { _, err := setFunctionBreakpoint(p, "main.sayhi") assertNoError(err, t, "SetBreakpoint") assertNoError(p.Continue(), t, "Continue") f, ln := currentLineNumber(p, t) initV, err := evalVariable(p, "n") initVval, _ := constant.Int64Val(initV.Value) assertNoError(err, t, "EvalVariable") for _, tc := range testcases { g, err := p.CurrentThread.GetG() assertNoError(err, t, "GetG()") if p.SelectedGoroutine.ID != g.ID { t.Fatalf("SelectedGoroutine not CurrentThread's goroutine: %d %d", g.ID, p.SelectedGoroutine.ID) } if ln != tc.begin { t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, filepath.Base(f), ln) } assertNoError(p.Next(), t, "Next() returned an error") var vval int64 for { v, err := evalVariable(p, "n") assertNoError(err, t, "EvalVariable") vval, _ = constant.Int64Val(v.Value) if p.CurrentThread.CurrentBreakpoint == nil { if vval != initVval { t.Fatal("Did not end up on same goroutine") } break } else { if vval == initVval { t.Fatal("Initial breakpoint triggered twice for the same goroutine") } assertNoError(p.Continue(), t, "Continue 2") } } f, ln = currentLineNumber(p, t) if ln != tc.end { t.Fatalf("Program did not continue to correct next location expected %d was %s:%d", tc.end, filepath.Base(f), ln) } } }) }
// goVal returns the Go value for val, or nil. func goVal(val constant.Value) interface{} { // val should exist, but be conservative and check if val == nil { return nil } // Match implementation restriction of other compilers. // gc only checks duplicates for integer, floating-point // and string values, so only create Go values for these // types. switch val.Kind() { case constant.Int: if x, ok := constant.Int64Val(val); ok { return x } if x, ok := constant.Uint64Val(val); ok { return x } case constant.Float: if x, ok := constant.Float64Val(val); ok { return x } case constant.String: return constant.StringVal(val) } return nil }
func (g *javaGen) genConst(o *types.Const) { // TODO(hyangah): should const names use upper cases + "_"? // TODO(hyangah): check invalid names. jType := g.javaType(o.Type()) val := o.Val().String() switch b := o.Type().(*types.Basic); b.Kind() { case types.Int64, types.UntypedInt: i, exact := constant.Int64Val(o.Val()) if !exact { g.errorf("const value %s for %s cannot be represented as %s", val, o.Name(), jType) return } val = fmt.Sprintf("%dL", i) case types.Float32: f, _ := constant.Float32Val(o.Val()) val = fmt.Sprintf("%gf", f) case types.Float64, types.UntypedFloat: f, _ := constant.Float64Val(o.Val()) if math.IsInf(f, 0) || math.Abs(f) > math.MaxFloat64 { g.errorf("const value %s for %s cannot be represented as %s", val, o.Name(), jType) return } val = fmt.Sprintf("%g", f) } g.Printf("public static final %s %s = %s;\n", g.javaType(o.Type()), o.Name(), val) }
func TestPointerSetting(t *testing.T) { withTestProcess("testvariables3", t, func(p *Process, fixture protest.Fixture) { assertNoError(p.Continue(), t, "Continue() returned an error") pval := func(n int64) { variable, err := evalVariable(p, "p1") assertNoError(err, t, "EvalVariable()") c0val, _ := constant.Int64Val(variable.Children[0].Value) if c0val != n { t.Fatalf("Wrong value of p1, *%d expected *%d", c0val, n) } } pval(1) // change p1 to point to i2 scope, err := p.CurrentThread.Scope() assertNoError(err, t, "Scope()") i2addr, err := scope.EvalExpression("i2") assertNoError(err, t, "EvalExpression()") assertNoError(setVariable(p, "p1", fmt.Sprintf("(*int)(0x%x)", i2addr.Addr)), t, "SetVariable()") pval(2) // change the value of i2 check that p1 also changes assertNoError(setVariable(p, "i2", "5"), t, "SetVariable()") pval(5) }) }
func (p *exporter) value(x constant.Value) { if trace { p.tracef("value { ") defer p.tracef("} ") } switch kind := x.Kind(); kind { case constant.Bool: tag := falseTag if constant.BoolVal(x) { tag = trueTag } p.int(tag) case constant.Int: if i, ok := constant.Int64Val(x); ok { p.int(int64Tag) p.int64(i) return } p.int(floatTag) p.float(x) case constant.Float: p.int(fractionTag) p.fraction(x) case constant.Complex: p.int(complexTag) p.fraction(constant.Real(x)) p.fraction(constant.Imag(x)) case constant.String: p.int(stringTag) p.string(constant.StringVal(x)) default: panic(fmt.Sprintf("unexpected value kind %d", kind)) } }
func (n *Node) Val() Val { var expr ast.Expr var ok bool if expr, ok = n.Node.(ast.Expr); ok { var typeAndValue types.TypeAndValue if typeAndValue, ok = n.Ctx.fn.Types[expr]; ok { if typeAndValue.Value == nil { panic("internal compiler error") } if typeAndValue.Value.Kind() != constant.Int { panic("unimplemented") } if i64, exact := constant.Int64Val(typeAndValue.Value); exact { mpInt := Mpint{} mpInt.Val = *big.NewInt(i64) val := Val{} val.U = &mpInt return val } else { panic("internal compiler error") } } else { panic("internal compiler error") } } else if _, ok = n.Node.(*ast.BasicLit); ok { panic("unimplemented") } else { // TODO: other ast.* return Val{} } }
func (v *Variable) setValue(y *Variable) error { var err error switch v.Kind { case reflect.Float32, reflect.Float64: f, _ := constant.Float64Val(y.Value) err = v.writeFloatRaw(f, v.RealType.Size()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: n, _ := constant.Int64Val(y.Value) err = v.writeUint(uint64(n), v.RealType.Size()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: n, _ := constant.Uint64Val(y.Value) err = v.writeUint(n, v.RealType.Size()) case reflect.Bool: err = v.writeBool(constant.BoolVal(y.Value)) case reflect.Complex64, reflect.Complex128: real, _ := constant.Float64Val(constant.Real(y.Value)) imag, _ := constant.Float64Val(constant.Imag(y.Value)) err = v.writeComplex(real, imag, v.RealType.Size()) default: fmt.Printf("default\n") if t, isptr := v.RealType.(*dwarf.PtrType); isptr { err = v.writeUint(uint64(y.Children[0].Addr), int64(t.ByteSize)) } else { return fmt.Errorf("can not set variables of type %s (not implemented)", v.Kind.String()) } } return err }
func (v *Variable) loadSliceInfo(t *dwarf.SliceType) { v.mem = cacheMemory(v.mem, v.Addr, int(t.Size())) var err error for _, f := range t.Field { switch f.Name { case "array": var base uint64 base, err = readUintRaw(v.mem, uintptr(int64(v.Addr)+f.ByteOffset), f.Type.Size()) if err == nil { v.Base = uintptr(base) // Dereference array type to get value type ptrType, ok := f.Type.(*dwarf.PtrType) if !ok { v.Unreadable = fmt.Errorf("Invalid type %s in slice array", f.Type) return } v.fieldType = ptrType.Type } case "len": lstrAddr, _ := v.toField(f) lstrAddr.loadValue(loadSingleValue) err = lstrAddr.Unreadable if err == nil { v.Len, _ = constant.Int64Val(lstrAddr.Value) } case "cap": cstrAddr, _ := v.toField(f) cstrAddr.loadValue(loadSingleValue) err = cstrAddr.Unreadable if err == nil { v.Cap, _ = constant.Int64Val(cstrAddr.Value) } } if err != nil { v.Unreadable = err return } } v.stride = v.fieldType.Size() if t, ok := v.fieldType.(*dwarf.PtrType); ok { v.stride = t.ByteSize } return }
func (v *Variable) loadSliceInfo(t *dwarf.StructType) { var err error for _, f := range t.Field { switch f.Name { case "array": var base uint64 base, err = v.thread.readUintRaw(uintptr(int64(v.Addr)+f.ByteOffset), int64(v.thread.dbp.arch.PtrSize())) if err == nil { v.base = uintptr(base) // Dereference array type to get value type ptrType, ok := f.Type.(*dwarf.PtrType) if !ok { v.Unreadable = fmt.Errorf("Invalid type %s in slice array", f.Type) return } v.fieldType = ptrType.Type } case "len": lstrAddr, _ := v.toField(f) lstrAddr.loadValue() err = lstrAddr.Unreadable if err == nil { v.Len, _ = constant.Int64Val(lstrAddr.Value) } case "cap": cstrAddr, _ := v.toField(f) cstrAddr.loadValue() err = cstrAddr.Unreadable if err == nil { v.Cap, _ = constant.Int64Val(cstrAddr.Value) } } if err != nil { v.Unreadable = err return } } v.stride = v.fieldType.Size() if _, ok := v.fieldType.(*dwarf.PtrType); ok { v.stride = int64(v.thread.dbp.arch.PtrSize()) } return }
// asInt64 returns the value as a 64-bit integer if possible, or returns an // error if not possible. func (expr *NumVal) asInt64() (int64, error) { intVal, ok := expr.asConstantInt() if !ok { return 0, fmt.Errorf("cannot represent %v as an int", expr.Value) } i, exact := constant.Int64Val(intVal) if !exact { return 0, fmt.Errorf("representing %v as an int would overflow", intVal) } return i, nil }
// asInt64 returns the value as a 64-bit integer if possible, or returns an // error if not possible. The method will set expr.resInt to the value of // this int64 if it is successful, avoiding the need to call the method again. func (expr *NumVal) asInt64() (int64, error) { intVal, ok := expr.asConstantInt() if !ok { return 0, errConstNotInt } i, exact := constant.Int64Val(intVal) if !exact { return 0, errConstOutOfRange } expr.resInt = DInt(i) return i, nil }
// Conversion type-checks the conversion T(x). // The result is in x. func (check *Checker) conversion(x *operand, T Type) { constArg := x.mode == constant_ var ok bool switch { case constArg && isConstType(T): // constant conversion switch t := T.Underlying().(*Basic); { case representableConst(x.val, check.conf, t.kind, &x.val): ok = true case isInteger(x.typ) && isString(t): codepoint := int64(-1) if i, ok := constant.Int64Val(x.val); ok { codepoint = i } // If codepoint < 0 the absolute value is too large (or unknown) for // conversion. This is the same as converting any other out-of-range // value - let string(codepoint) do the work. x.val = constant.MakeString(string(codepoint)) ok = true } case x.convertibleTo(check.conf, T): // non-constant conversion x.mode = value ok = true } if !ok { check.errorf(x.pos(), "cannot convert %s to %s", x, T) x.mode = invalid return } // The conversion argument types are final. For untyped values the // conversion provides the type, per the spec: "A constant may be // given a type explicitly by a constant declaration or conversion,...". final := x.typ if isUntyped(x.typ) { final = T // - For conversions to interfaces, use the argument's default type. // - For conversions of untyped constants to non-constant types, also // use the default type (e.g., []byte("foo") should report string // not []byte as type for the constant "foo"). // - Keep untyped nil for untyped nil arguments. if IsInterface(T) || constArg && !isConstType(T) { final = defaultType(x.typ) } check.updateExprType(x.expr, final, true) } x.typ = T }
func (gvar *Variable) parseG() (*G, error) { mem := gvar.mem dbp := gvar.dbp gaddr := uint64(gvar.Addr) _, deref := gvar.RealType.(*dwarf.PtrType) initialInstructions := make([]byte, dbp.arch.PtrSize()+1) initialInstructions[0] = op.DW_OP_addr binary.LittleEndian.PutUint64(initialInstructions[1:], gaddr) if deref { gaddrbytes, err := mem.readMemory(uintptr(gaddr), dbp.arch.PtrSize()) if err != nil { return nil, fmt.Errorf("error derefing *G %s", err) } initialInstructions = append([]byte{op.DW_OP_addr}, gaddrbytes...) gaddr = binary.LittleEndian.Uint64(gaddrbytes) if gaddr == 0 { id := 0 if thread, ok := mem.(*Thread); ok { id = thread.ID } return nil, NoGError{tid: id} } } if gaddr == 0 { return nil, NoGError{} } gvar.loadValue() if gvar.Unreadable != nil { return nil, gvar.Unreadable } schedVar := gvar.toFieldNamed("sched") pc, _ := constant.Int64Val(schedVar.toFieldNamed("pc").Value) sp, _ := constant.Int64Val(schedVar.toFieldNamed("sp").Value) id, _ := constant.Int64Val(gvar.toFieldNamed("goid").Value) gopc, _ := constant.Int64Val(gvar.toFieldNamed("gopc").Value) waitReason := constant.StringVal(gvar.toFieldNamed("waitreason").Value) d := gvar.toFieldNamed("_defer") deferPC := int64(0) fnvar := d.toFieldNamed("fn") if fnvar != nil { fnvalvar := fnvar.toFieldNamed("fn") deferPC, _ = constant.Int64Val(fnvalvar.Value) } status, _ := constant.Int64Val(gvar.toFieldNamed("atomicstatus").Value) f, l, fn := gvar.dbp.goSymTable.PCToLine(uint64(pc)) g := &G{ ID: int(id), GoPC: uint64(gopc), PC: uint64(pc), SP: uint64(sp), WaitReason: waitReason, DeferPC: uint64(deferPC), Status: uint64(status), CurrentLoc: Location{PC: uint64(pc), File: f, Line: l, Fn: fn}, dbp: gvar.dbp, } return g, nil }
func TestNextConcurrent(t *testing.T) { testcases := []nextTest{ {8, 9}, {9, 10}, {10, 11}, } withTestProcess("parallel_next", t, func(p *Process, fixture protest.Fixture) { bp, err := setFunctionBreakpoint(p, "main.sayhi") assertNoError(err, t, "SetBreakpoint") assertNoError(p.Continue(), t, "Continue") f, ln := currentLineNumber(p, t) initV, err := evalVariable(p, "n") initVval, _ := constant.Int64Val(initV.Value) assertNoError(err, t, "EvalVariable") _, err = p.ClearBreakpoint(bp.Addr) assertNoError(err, t, "ClearBreakpoint()") for _, tc := range testcases { g, err := p.CurrentThread.GetG() assertNoError(err, t, "GetG()") if p.SelectedGoroutine.ID != g.ID { t.Fatalf("SelectedGoroutine not CurrentThread's goroutine: %d %d", g.ID, p.SelectedGoroutine.ID) } if ln != tc.begin { t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, filepath.Base(f), ln) } assertNoError(p.Next(), t, "Next() returned an error") f, ln = currentLineNumber(p, t) if ln != tc.end { t.Fatalf("Program did not continue to correct next location expected %d was %s:%d", tc.end, filepath.Base(f), ln) } v, err := evalVariable(p, "n") assertNoError(err, t, "EvalVariable") vval, _ := constant.Int64Val(v.Value) if vval != initVval { t.Fatal("Did not end up on same goroutine") } } }) }
// Int64 returns the numeric value of this constant truncated to fit // a signed 64-bit integer. // func (c *Const) Int64() int64 { switch x := c.Value; x.Kind() { case exact.Int: if i, ok := exact.Int64Val(x); ok { return i } return 0 case exact.Float: f, _ := exact.Float64Val(x) return int64(f) } panic(fmt.Sprintf("unexpected constant value: %T", c.Value)) }
func (g *ObjcGen) genConstM(o *types.Const) { if _, ok := o.Type().(*types.Basic); !ok { g.Printf("// skipped const %s with unsupported type: %T\n\n", o.Name(), o) return } cName := fmt.Sprintf("%s%s", g.namePrefix, o.Name()) objcType := g.objcType(o.Type()) switch b := o.Type().(*types.Basic); b.Kind() { case types.Bool, types.UntypedBool: v := "NO" if constant.BoolVal(o.Val()) { v = "YES" } g.Printf("const BOOL %s = %s;\n", cName, v) case types.String, types.UntypedString: g.Printf("NSString* const %s = @%s;\n", cName, constExactString(o)) case types.Int, types.Int8, types.Int16, types.Int32: g.Printf("const %s %s = %s;\n", objcType, cName, o.Val()) case types.Int64, types.UntypedInt: i, exact := constant.Int64Val(o.Val()) if !exact { g.errorf("const value %s for %s cannot be represented as %s", o.Val(), o.Name(), objcType) return } if i == math.MinInt64 { // -9223372036854775808LL does not work because 922337203685477508 is // larger than max int64. g.Printf("const int64_t %s = %dLL-1;\n", cName, i+1) } else { g.Printf("const int64_t %s = %dLL;\n", cName, i) } case types.Float32, types.Float64, types.UntypedFloat: f, _ := constant.Float64Val(o.Val()) if math.IsInf(f, 0) || math.Abs(f) > math.MaxFloat64 { g.errorf("const value %s for %s cannot be represented as double", o.Val(), o.Name()) return } g.Printf("const %s %s = %g;\n", objcType, cName, f) default: g.errorf("unsupported const type %s for %s", b, o.Name()) } }
func (v *Variable) asInt() (int64, error) { if v.DwarfType == nil { if v.Value.Kind() != constant.Int { return 0, fmt.Errorf("can not convert constant %s to int", v.Value) } } else { v.loadValue() if v.Unreadable != nil { return 0, v.Unreadable } if _, ok := v.DwarfType.(*dwarf.IntType); !ok { return 0, fmt.Errorf("can not convert value of type %s to int", v.DwarfType.String()) } } n, _ := constant.Int64Val(v.Value) return n, nil }
// checkLongShift checks if shift or shift-assign operations shift by more than // the length of the underlying variable. func checkLongShift(f *File, node ast.Node, x, y ast.Expr) { if f.pkg.types[x].Value != nil { // Ignore shifts of constants. // These are frequently used for bit-twiddling tricks // like ^uint(0) >> 63 for 32/64 bit detection and compatibility. return } v := f.pkg.types[y].Value if v == nil { return } amt, ok := constant.Int64Val(v) if !ok { return } t := f.pkg.types[x].Type if t == nil { return } b, ok := t.Underlying().(*types.Basic) if !ok { return } var size int64 var msg string switch b.Kind() { case types.Uint8, types.Int8: size = 8 case types.Uint16, types.Int16: size = 16 case types.Uint32, types.Int32: size = 32 case types.Uint64, types.Int64: size = 64 case types.Int, types.Uint, types.Uintptr: // These types may be as small as 32 bits, but no smaller. size = 32 msg = "might be " default: return } if amt >= size { ident := f.gofmt(x) f.Badf(node.Pos(), "%s %stoo small for shift of %d", ident, msg, amt) } }
func (p *exporter) value(x constant.Value) { if trace { p.tracef("= ") } switch x.Kind() { case constant.Bool: tag := falseTag if constant.BoolVal(x) { tag = trueTag } p.tag(tag) case constant.Int: if v, exact := constant.Int64Val(x); exact { // common case: x fits into an int64 - use compact encoding p.tag(int64Tag) p.int64(v) return } // uncommon case: large x - use float encoding // (powers of 2 will be encoded efficiently with exponent) p.tag(floatTag) p.float(constant.ToFloat(x)) case constant.Float: p.tag(floatTag) p.float(x) case constant.Complex: p.tag(complexTag) p.float(constant.Real(x)) p.float(constant.Imag(x)) case constant.String: p.tag(stringTag) p.string(constant.StringVal(x)) case constant.Unknown: // package contains type errors p.tag(unknownTag) default: log.Fatalf("gcimporter: unexpected value %v (%T)", x, x) } }
func TestCondBreakpointError(t *testing.T) { withTestProcess("parallel_next", t, func(p *Process, fixture protest.Fixture) { addr, _, err := p.goSymTable.LineToPC(fixture.Source, 9) assertNoError(err, t, "LineToPC") bp, err := p.SetBreakpoint(addr) assertNoError(err, t, "SetBreakpoint()") bp.Cond = &ast.BinaryExpr{ Op: token.EQL, X: &ast.Ident{Name: "nonexistentvariable"}, Y: &ast.BasicLit{Kind: token.INT, Value: "7"}, } err = p.Continue() if err == nil { t.Fatalf("No error on first Continue()") } if err.Error() != "error evaluating expression: could not find symbol value for nonexistentvariable" && err.Error() != "multiple errors evaluating conditions" { t.Fatalf("Unexpected error on first Continue(): %v", err) } bp.Cond = &ast.BinaryExpr{ Op: token.EQL, X: &ast.Ident{Name: "n"}, Y: &ast.BasicLit{Kind: token.INT, Value: "7"}, } err = p.Continue() if err != nil { if _, exited := err.(ProcessExitedError); !exited { t.Fatalf("Unexpected error on second Continue(): %v", err) } } else { nvar, err := evalVariable(p, "n") assertNoError(err, t, "EvalVariable()") n, _ := constant.Int64Val(nvar.Value) if n != 7 { t.Fatalf("Stoppend on wrong goroutine %d\n", n) } } }) }
func (check *Checker) arrayLength(e ast.Expr) int64 { var x operand check.expr(&x, e) if x.mode != constant { if x.mode != invalid { check.errorf(x.pos(), "array length %s must be constant", &x) } return 0 } if !x.isInteger() { check.errorf(x.pos(), "array length %s must be integer", &x) return 0 } n, ok := exact.Int64Val(x.val) if !ok || n < 0 { check.errorf(x.pos(), "invalid array length %s", &x) return 0 } return n }
// checkLongShift checks if shift or shift-assign operations shift by more than // the length of the underlying variable. func checkLongShift(f *File, node ast.Node, x, y ast.Expr) { v := f.pkg.types[y].Value if v == nil { return } amt, ok := exact.Int64Val(v) if !ok { return } t := f.pkg.types[x].Type if t == nil { return } b, ok := t.Underlying().(*types.Basic) if !ok { return } var size int64 var msg string switch b.Kind() { case types.Uint8, types.Int8: size = 8 case types.Uint16, types.Int16: size = 16 case types.Uint32, types.Int32: size = 32 case types.Uint64, types.Int64: size = 64 case types.Int, types.Uint, types.Uintptr: // These types may be as small as 32 bits, but no smaller. size = 32 msg = "might be " default: return } if amt >= size { ident := f.gofmt(x) f.Badf(node.Pos(), "%s %stoo small for shift of %d", ident, msg, amt) } }
// ResolveAsType implements the Constant interface. func (expr *NumVal) ResolveAsType(typ Datum) (Datum, error) { switch { case typ.TypeEqual(TypeInt): i, exact := constant.Int64Val(constant.ToInt(expr.Value)) if !exact { return nil, fmt.Errorf("integer value out of range: %v", expr.Value) } return NewDInt(DInt(i)), nil case typ.TypeEqual(TypeFloat): f, _ := constant.Float64Val(constant.ToFloat(expr.Value)) return NewDFloat(DFloat(f)), nil case typ.TypeEqual(TypeDecimal): dd := &DDecimal{} s := expr.ExactString() if idx := strings.IndexRune(s, '/'); idx != -1 { // Handle constant.ratVal, which will return a rational string // like 6/7. If only we could call big.Rat.FloatString() on it... num, den := s[:idx], s[idx+1:] if _, ok := dd.SetString(num); !ok { return nil, fmt.Errorf("could not evaluate numerator of %v as Datum type DDecimal "+ "from string %q", expr, num) } denDec := new(inf.Dec) if _, ok := denDec.SetString(den); !ok { return nil, fmt.Errorf("could not evaluate denominator %v as Datum type DDecimal "+ "from string %q", expr, den) } dd.QuoRound(&dd.Dec, denDec, decimal.Precision, inf.RoundHalfUp) } else { if _, ok := dd.SetString(s); !ok { return nil, fmt.Errorf("could not evaluate %v as Datum type DDecimal from "+ "string %q", expr, s) } } return dd, nil default: return nil, fmt.Errorf("could not resolve %T %v into a %T", expr, expr, typ) } }
func TestCondBreakpoint(t *testing.T) { withTestProcess("parallel_next", t, func(p *Process, fixture protest.Fixture) { addr, _, err := p.goSymTable.LineToPC(fixture.Source, 9) assertNoError(err, t, "LineToPC") bp, err := p.SetBreakpoint(addr) assertNoError(err, t, "SetBreakpoint()") bp.Cond = &ast.BinaryExpr{ Op: token.EQL, X: &ast.Ident{Name: "n"}, Y: &ast.BasicLit{Kind: token.INT, Value: "7"}, } assertNoError(p.Continue(), t, "Continue()") nvar, err := evalVariable(p, "n") assertNoError(err, t, "EvalVariable()") n, _ := constant.Int64Val(nvar.Value) if n != 7 { t.Fatalf("Stoppend on wrong goroutine %d\n", n) } }) }
func (check *Checker) arrayLength(e ast.Expr) int64 { var x operand check.expr(&x, e) if x.mode != constant_ { if x.mode != invalid { check.errorf(x.pos(), "array length %s must be constant", &x) } return 0 } if isUntyped(x.typ) || isInteger(x.typ) { if val := constant.ToInt(x.val); val.Kind() == constant.Int { if representableConst(val, check.conf, Typ[Int], nil) { if n, ok := constant.Int64Val(val); ok && n >= 0 { return n } check.errorf(x.pos(), "invalid array length %s", &x) return 0 } } } check.errorf(x.pos(), "array length %s must be integer", &x) return 0 }
// index checks an index expression for validity. // If max >= 0, it is the upper bound for index. // If index is valid and the result i >= 0, then i is the constant value of index. func (check *Checker) index(index ast.Expr, max int64) (i int64, valid bool) { var x operand check.expr(&x, index) if x.mode == invalid { return } // an untyped constant must be representable as Int check.convertUntyped(&x, Typ[Int]) if x.mode == invalid { return } // the index must be of integer type if !isInteger(x.typ) { check.invalidArg(x.pos(), "index %s must be integer", &x) return } // a constant index i must be in bounds if x.mode == constant_ { if constant.Sign(x.val) < 0 { check.invalidArg(x.pos(), "index %s must not be negative", &x) return } i, valid = constant.Int64Val(constant.ToInt(x.val)) if !valid || max >= 0 && i >= max { check.errorf(x.pos(), "index %s is out of bounds", &x) return i, false } // 0 <= i [ && i < max ] return i, true } return -1, true }
// genDecl processes one declaration clause. func (f *File) genDecl(node ast.Node) bool { decl, ok := node.(*ast.GenDecl) if !ok || decl.Tok != token.CONST { // We only care about const declarations. return true } // The name of the type of the constants we are declaring. // Can change if this is a multi-element declaration. typ := "" // Loop over the elements of the declaration. Each element is a ValueSpec: // a list of names possibly followed by a type, possibly followed by values. // If the type and value are both missing, we carry down the type (and value, // but the "go/types" package takes care of that). for _, spec := range decl.Specs { vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST. if vspec.Type == nil && len(vspec.Values) > 0 { // "X = 1". With no type but a value, the constant is untyped. // Skip this vspec and reset the remembered type. typ = "" continue } if vspec.Type != nil { // "X T". We have a type. Remember it. ident, ok := vspec.Type.(*ast.Ident) if !ok { continue } typ = ident.Name } if typ != f.typeName { // This is not the type we're looking for. continue } // We now have a list of names (from one line of source code) all being // declared with the desired type. // Grab their names and actual values and store them in f.values. for _, name := range vspec.Names { if name.Name == "_" { continue } // This dance lets the type checker find the values for us. It's a // bit tricky: look up the object declared by the name, find its // types.Const, and extract its value. obj, ok := f.pkg.defs[name] if !ok { log.Fatalf("no value for constant %s", name) } info := obj.Type().Underlying().(*types.Basic).Info() if info&types.IsInteger == 0 { log.Fatalf("can't handle non-integer constant type %s", typ) } value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST. if value.Kind() != exact.Int { log.Fatalf("can't happen: constant is not an integer %s", name) } i64, isInt := exact.Int64Val(value) u64, isUint := exact.Uint64Val(value) if !isInt && !isUint { log.Fatalf("internal error: value of %s is not an integer: %s", name, value.String()) } if !isInt { u64 = uint64(i64) } v := Value{ name: name.Name, value: u64, signed: info&types.IsUnsigned == 0, str: value.String(), } f.values = append(f.values, v) } } return false }
func TestVariableEvaluation(t *testing.T) { testcases := []struct { name string st reflect.Kind value interface{} length, cap int64 childrenlen int }{ {"a1", reflect.String, "foofoofoofoofoofoo", 18, 0, 0}, {"a11", reflect.Array, nil, 3, -1, 3}, {"a12", reflect.Slice, nil, 2, 2, 2}, {"a13", reflect.Slice, nil, 3, 3, 3}, {"a2", reflect.Int, int64(6), 0, 0, 0}, {"a3", reflect.Float64, float64(7.23), 0, 0, 0}, {"a4", reflect.Array, nil, 2, -1, 2}, {"a5", reflect.Slice, nil, 5, 5, 5}, {"a6", reflect.Struct, nil, 2, 0, 2}, {"a7", reflect.Ptr, nil, 1, 0, 1}, {"a8", reflect.Struct, nil, 2, 0, 2}, {"a9", reflect.Ptr, nil, 1, 0, 1}, {"baz", reflect.String, "bazburzum", 9, 0, 0}, {"neg", reflect.Int, int64(-1), 0, 0, 0}, {"f32", reflect.Float32, float64(float32(1.2)), 0, 0, 0}, {"c64", reflect.Complex64, complex128(complex64(1 + 2i)), 0, 0, 0}, {"c128", reflect.Complex128, complex128(2 + 3i), 0, 0, 0}, {"a6.Baz", reflect.Int, int64(8), 0, 0, 0}, {"a7.Baz", reflect.Int, int64(5), 0, 0, 0}, {"a8.Baz", reflect.String, "feh", 3, 0, 0}, {"a8", reflect.Struct, nil, 2, 0, 2}, {"i32", reflect.Array, nil, 2, -1, 2}, {"b1", reflect.Bool, true, 0, 0, 0}, {"b2", reflect.Bool, false, 0, 0, 0}, {"f", reflect.Func, "main.barfoo", 0, 0, 0}, {"ba", reflect.Slice, nil, 200, 200, 64}, } withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) { assertNoError(p.Continue(), t, "Continue() returned an error") for _, tc := range testcases { v, err := evalVariable(p, tc.name) assertNoError(err, t, fmt.Sprintf("EvalVariable(%s)", tc.name)) if v.Kind != tc.st { t.Fatalf("%s simple type: expected: %s got: %s", tc.name, tc.st, v.Kind.String()) } if v.Value == nil && tc.value != nil { t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) } else { switch v.Kind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: x, _ := constant.Int64Val(v.Value) if y, ok := tc.value.(int64); !ok || x != y { t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) } case reflect.Float32, reflect.Float64: x, _ := constant.Float64Val(v.Value) if y, ok := tc.value.(float64); !ok || x != y { t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) } case reflect.Complex64, reflect.Complex128: xr, _ := constant.Float64Val(constant.Real(v.Value)) xi, _ := constant.Float64Val(constant.Imag(v.Value)) if y, ok := tc.value.(complex128); !ok || complex(xr, xi) != y { t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) } case reflect.String: if y, ok := tc.value.(string); !ok || constant.StringVal(v.Value) != y { t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) } } } if v.Len != tc.length { t.Fatalf("%s len: expected: %d got: %d", tc.name, tc.length, v.Len) } if v.Cap != tc.cap { t.Fatalf("%s cap: expected: %d got: %d", tc.name, tc.cap, v.Cap) } if len(v.Children) != tc.childrenlen { t.Fatalf("%s children len: expected %d got: %d", tc.name, tc.childrenlen, len(v.Children)) } } }) }
func TestBreakpointCountsWithDetection(t *testing.T) { if !doTestBreakpointCountsWithDetection { return } m := map[int64]int64{} withTestProcess("bpcountstest", t, func(p *Process, fixture protest.Fixture) { addr, _, err := p.goSymTable.LineToPC(fixture.Source, 12) assertNoError(err, t, "LineToPC") bp, err := p.SetBreakpoint(addr) assertNoError(err, t, "SetBreakpoint()") for { if err := p.Continue(); err != nil { if _, exited := err.(ProcessExitedError); exited { break } assertNoError(err, t, "Continue()") } fmt.Printf("Continue returned %d\n", bp.TotalHitCount) for _, th := range p.Threads { if th.CurrentBreakpoint == nil { continue } scope, err := th.Scope() assertNoError(err, t, "Scope()") v, err := scope.EvalVariable("i") assertNoError(err, t, "evalVariable") i, _ := constant.Int64Val(v.Value) v, err = scope.EvalVariable("id") assertNoError(err, t, "evalVariable") id, _ := constant.Int64Val(v.Value) m[id] = i fmt.Printf("\tgoroutine (%d) %d: %d\n", th.ID, id, i) } total := int64(0) for i := range m { total += m[i] + 1 } if uint64(total) != bp.TotalHitCount { t.Fatalf("Mismatched total count %d %d\n", total, bp.TotalHitCount) } } t.Logf("TotalHitCount: %d", bp.TotalHitCount) if bp.TotalHitCount != 200 { t.Fatalf("Wrong TotalHitCount for the breakpoint (%d)", bp.TotalHitCount) } if len(bp.HitCount) != 2 { t.Fatalf("Wrong number of goroutines for breakpoint (%d)", len(bp.HitCount)) } for _, v := range bp.HitCount { if v != 100 { t.Fatalf("Wrong HitCount for breakpoint (%v)", bp.HitCount) } } }) }
func TestFrameEvaluation(t *testing.T) { withTestProcess("goroutinestackprog", t, func(p *Process, fixture protest.Fixture) { _, err := setFunctionBreakpoint(p, "main.stacktraceme") assertNoError(err, t, "setFunctionBreakpoint") assertNoError(p.Continue(), t, "Continue()") // Testing evaluation on goroutines gs, err := p.GoroutinesInfo() assertNoError(err, t, "GoroutinesInfo") found := make([]bool, 10) for _, g := range gs { frame := -1 frames, err := p.GoroutineStacktrace(g, 10) assertNoError(err, t, "GoroutineStacktrace()") for i := range frames { if frames[i].Call.Fn != nil && frames[i].Call.Fn.Name == "main.agoroutine" { frame = i break } } if frame < 0 { t.Logf("Goroutine %d: could not find correct frame", g.ID) continue } scope, err := p.ConvertEvalScope(g.ID, frame) assertNoError(err, t, "ConvertEvalScope()") t.Logf("scope = %v", scope) v, err := scope.EvalVariable("i") t.Logf("v = %v", v) if err != nil { t.Logf("Goroutine %d: %v\n", g.ID, err) continue } vval, _ := constant.Int64Val(v.Value) found[vval] = true } for i := range found { if !found[i] { t.Fatalf("Goroutine %d not found\n", i) } } // Testing evaluation on frames assertNoError(p.Continue(), t, "Continue() 2") g, err := p.CurrentThread.GetG() assertNoError(err, t, "GetG()") for i := 0; i <= 3; i++ { scope, err := p.ConvertEvalScope(g.ID, i+1) assertNoError(err, t, fmt.Sprintf("ConvertEvalScope() on frame %d", i+1)) v, err := scope.EvalVariable("n") assertNoError(err, t, fmt.Sprintf("EvalVariable() on frame %d", i+1)) n, _ := constant.Int64Val(v.Value) t.Logf("frame %d n %d\n", i+1, n) if n != int64(3-i) { t.Fatalf("On frame %d value of n is %d (not %d)", i+1, n, 3-i) } } }) }