diff --git a/_fixtures/testvariables3.go b/_fixtures/testvariables3.go index 0d23b608..4bcfb297 100644 --- a/_fixtures/testvariables3.go +++ b/_fixtures/testvariables3.go @@ -48,6 +48,11 @@ type dstruct struct { type maptype map[string]interface{} +type benchstruct struct { + a [64]byte + b [64]byte +} + func main() { i1 := 1 i2 := 2 @@ -138,6 +143,12 @@ func main() { var iface2fn2 interface{} = afunc2 var mapinf maptype = map[string]interface{}{} mapinf["inf"] = mapinf + var bencharr [64]benchstruct + var benchparr [64]*benchstruct + + for i := range benchparr { + benchparr[i] = &benchstruct{} + } var amb1 = 1 runtime.Breakpoint() @@ -145,5 +156,5 @@ func main() { fmt.Println(amb1) } runtime.Breakpoint() - fmt.Println(i1, i2, i3, p1, amb1, s1, s3, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, mapinf) + fmt.Println(i1, i2, i3, p1, amb1, s1, s3, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf) } diff --git a/proc/eval.go b/proc/eval.go index 8a0cab32..e690f68f 100644 --- a/proc/eval.go +++ b/proc/eval.go @@ -135,7 +135,7 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) { converr := fmt.Errorf("can not convert %q to %s", exprToString(node.Args[0]), typ.String()) - v := newVariable("", 0, styp, scope.Thread) + v := newVariable("", 0, styp, scope.Thread.dbp, scope.Thread) v.loaded = true switch ttyp := typ.(type) { @@ -154,7 +154,7 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) { n, _ := constant.Int64Val(argv.Value) - v.Children = []Variable{*newVariable("", uintptr(n), ttyp.Type, scope.Thread)} + v.Children = []Variable{*(scope.newVariable("", uintptr(n), ttyp.Type))} return v, nil case *dwarf.UintType: @@ -274,18 +274,18 @@ func capBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { } fallthrough case reflect.Array: - return newConstant(constant.MakeInt64(arg.Len), arg.thread), nil + return newConstant(constant.MakeInt64(arg.Len), arg.mem), nil case reflect.Slice: - return newConstant(constant.MakeInt64(arg.Cap), arg.thread), nil + return newConstant(constant.MakeInt64(arg.Cap), arg.mem), nil case reflect.Chan: arg.loadValue() if arg.Unreadable != nil { return nil, arg.Unreadable } if arg.base == 0 { - return newConstant(constant.MakeInt64(0), arg.thread), nil + return newConstant(constant.MakeInt64(0), arg.mem), nil } - return newConstant(arg.Children[1].Value, arg.thread), nil + return newConstant(arg.Children[1].Value, arg.mem), nil default: return nil, invalidArgErr } @@ -309,25 +309,25 @@ func lenBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { if arg.Unreadable != nil { return nil, arg.Unreadable } - return newConstant(constant.MakeInt64(arg.Len), arg.thread), nil + return newConstant(constant.MakeInt64(arg.Len), arg.mem), nil case reflect.Chan: arg.loadValue() if arg.Unreadable != nil { return nil, arg.Unreadable } if arg.base == 0 { - return newConstant(constant.MakeInt64(0), arg.thread), nil + return newConstant(constant.MakeInt64(0), arg.mem), nil } - return newConstant(arg.Children[0].Value, arg.thread), nil + return newConstant(arg.Children[0].Value, arg.mem), nil case reflect.Map: it := arg.mapIterator() if arg.Unreadable != nil { return nil, arg.Unreadable } if it == nil { - return newConstant(constant.MakeInt64(0), arg.thread), nil + return newConstant(constant.MakeInt64(0), arg.mem), nil } - return newConstant(constant.MakeInt64(arg.Len), arg.thread), nil + return newConstant(constant.MakeInt64(arg.Len), arg.mem), nil default: return nil, invalidArgErr } @@ -377,7 +377,7 @@ func complexBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { typ := &dwarf.ComplexType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: int64(sz / 8), Name: fmt.Sprintf("complex%d", sz)}, BitSize: sz, BitOffset: 0}} - r := newVariable("", 0, typ, realev.thread) + r := realev.newVariable("", 0, typ) r.Value = constant.BinaryOp(realev.Value, token.ADD, constant.MakeImag(imagev.Value)) return r, nil } @@ -398,7 +398,7 @@ func imagBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { return nil, fmt.Errorf("invalid argument %s (type %s) to imag", exprToString(nodeargs[0]), arg.TypeString()) } - return newConstant(constant.Imag(arg.Value), arg.thread), nil + return newConstant(constant.Imag(arg.Value), arg.mem), nil } func realBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { @@ -417,7 +417,7 @@ func realBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { return nil, fmt.Errorf("invalid argument %s (type %s) to real", exprToString(nodeargs[0]), arg.TypeString()) } - return newConstant(constant.Real(arg.Value), arg.thread), nil + return newConstant(constant.Real(arg.Value), arg.mem), nil } // Evaluates identifier expressions @@ -625,7 +625,7 @@ func (scope *EvalScope) evalAddrOf(node *ast.UnaryExpr) (*Variable, error) { xev.OnlyAddr = true typename := "*" + xev.DwarfType.String() - rv := newVariable("", 0, &dwarf.PtrType{CommonType: dwarf.CommonType{ByteSize: int64(scope.Thread.dbp.arch.PtrSize()), Name: typename}, Type: xev.DwarfType}, scope.Thread) + rv := scope.newVariable("", 0, &dwarf.PtrType{CommonType: dwarf.CommonType{ByteSize: int64(scope.Thread.dbp.arch.PtrSize()), Name: typename}, Type: xev.DwarfType}) rv.Children = []Variable{*xev} rv.loaded = true @@ -687,11 +687,11 @@ func (scope *EvalScope) evalUnary(node *ast.UnaryExpr) (*Variable, error) { return nil, err } if xv.DwarfType != nil { - r := newVariable("", 0, xv.DwarfType, xv.thread) + r := xv.newVariable("", 0, xv.DwarfType) r.Value = rc return r, nil } - return newConstant(rc, xv.thread), nil + return newConstant(rc, xv.mem), nil } func negotiateType(op token.Token, xv, yv *Variable) (dwarf.Type, error) { @@ -806,7 +806,7 @@ func (scope *EvalScope) evalBinary(node *ast.BinaryExpr) (*Variable, error) { if err != nil { return nil, err } - return newConstant(constant.MakeBool(v), xv.thread), nil + return newConstant(constant.MakeBool(v), xv.mem), nil default: if xv.Value == nil { @@ -823,10 +823,10 @@ func (scope *EvalScope) evalBinary(node *ast.BinaryExpr) (*Variable, error) { } if typ == nil { - return newConstant(rc, xv.thread), nil + return newConstant(rc, xv.mem), nil } - r := newVariable("", 0, typ, xv.thread) + r := xv.newVariable("", 0, typ) r.Value = rc return r, nil } @@ -1039,7 +1039,7 @@ func (v *Variable) sliceAccess(idx int) (*Variable, error) { if idx < 0 || int64(idx) >= v.Len { return nil, fmt.Errorf("index out of bounds") } - return newVariable("", v.base+uintptr(int64(idx)*v.stride), v.fieldType, v.thread), nil + return v.newVariable("", v.base+uintptr(int64(idx)*v.stride), v.fieldType), nil } func (v *Variable) mapAccess(idx *Variable) (*Variable, error) { @@ -1101,7 +1101,7 @@ func (v *Variable) reslice(low int64, high int64) (*Variable, error) { } } - r := newVariable("", 0, typ, v.thread) + r := v.newVariable("", 0, typ) r.Cap = len r.Len = len r.base = base diff --git a/proc/mem.go b/proc/mem.go new file mode 100644 index 00000000..83cb14be --- /dev/null +++ b/proc/mem.go @@ -0,0 +1,54 @@ +package proc + +const cacheEnabled = true + +type memoryReadWriter interface { + readMemory(addr uintptr, size int) (data []byte, err error) + writeMemory(addr uintptr, data []byte) (written int, err error) +} + +type memCache struct { + cacheAddr uintptr + cache []byte + mem memoryReadWriter +} + +func (m *memCache) contains(addr uintptr, size int) bool { + return addr >= m.cacheAddr && (addr+uintptr(size)) <= (m.cacheAddr+uintptr(len(m.cache))) +} + +func (m *memCache) readMemory(addr uintptr, size int) (data []byte, err error) { + if m.contains(addr, size) { + d := make([]byte, size) + copy(d, m.cache[addr-m.cacheAddr:]) + return d, nil + } + + return m.mem.readMemory(addr, size) +} + +func (m *memCache) writeMemory(addr uintptr, data []byte) (written int, err error) { + return m.mem.writeMemory(addr, data) +} + +func cacheMemory(mem memoryReadWriter, addr uintptr, size int) memoryReadWriter { + if !cacheEnabled { + return mem + } + if cacheMem, isCache := mem.(*memCache); isCache { + if cacheMem.contains(addr, size) { + return mem + } else { + cache, err := cacheMem.mem.readMemory(addr, size) + if err != nil { + return mem + } + return &memCache{addr, cache, mem} + } + } + cache, err := mem.readMemory(addr, size) + if err != nil { + return mem + } + return &memCache{addr, cache, mem} +} diff --git a/proc/proc_test.go b/proc/proc_test.go index d2099e72..0009b7b4 100644 --- a/proc/proc_test.go +++ b/proc/proc_test.go @@ -26,7 +26,7 @@ func TestMain(m *testing.M) { os.Exit(protest.RunTestsWithFixtures(m)) } -func withTestProcess(name string, t *testing.T, fn func(p *Process, fixture protest.Fixture)) { +func withTestProcess(name string, t testing.TB, fn func(p *Process, fixture protest.Fixture)) { fixture := protest.BuildFixture(name) p, err := Launch([]string{fixture.Path}) if err != nil { @@ -54,7 +54,7 @@ func dataAtAddr(thread *Thread, addr uint64) ([]byte, error) { return thread.readMemory(uintptr(addr), 1) } -func assertNoError(err error, t *testing.T, s string) { +func assertNoError(err error, t testing.TB, s string) { if err != nil { _, file, line, _ := runtime.Caller(1) fname := filepath.Base(file) @@ -1181,6 +1181,18 @@ func TestBreakpointCounts(t *testing.T) { }) } +func BenchmarkArray(b *testing.B) { + // each bencharr struct is 128 bytes, bencharr is 64 elements long + b.SetBytes(int64(64 * 128)) + withTestProcess("testvariables3", b, func(p *Process, fixture protest.Fixture) { + assertNoError(p.Continue(), b, "Continue()") + for i := 0; i < b.N; i++ { + _, err := evalVariable(p, "bencharr") + assertNoError(err, b, "EvalVariable()") + } + }) +} + const doTestBreakpointCountsWithDetection = false func TestBreakpointCountsWithDetection(t *testing.T) { @@ -1245,6 +1257,44 @@ func TestBreakpointCountsWithDetection(t *testing.T) { }) } +func BenchmarkArrayPointer(b *testing.B) { + // each bencharr struct is 128 bytes, benchparr is an array of 64 pointers to bencharr + // each read will read 64 bencharr structs plus the 64 pointers of benchparr + b.SetBytes(int64(64*128 + 64*8)) + withTestProcess("testvariables3", b, func(p *Process, fixture protest.Fixture) { + assertNoError(p.Continue(), b, "Continue()") + for i := 0; i < b.N; i++ { + _, err := evalVariable(p, "bencharr") + assertNoError(err, b, "EvalVariable()") + } + }) +} + +func BenchmarkMap(b *testing.B) { + // m1 contains 41 entries, each one has a value that's 2 int values (2* 8 bytes) and a string key + // each string key has an average of 9 character + // reading strings and the map structure imposes a overhead that we ignore here + b.SetBytes(int64(41 * (2*8 + 9))) + withTestProcess("testvariables3", b, func(p *Process, fixture protest.Fixture) { + assertNoError(p.Continue(), b, "Continue()") + for i := 0; i < b.N; i++ { + _, err := evalVariable(p, "m1") + assertNoError(err, b, "EvalVariable()") + } + }) +} + +func BenchmarkGoroutinesInfo(b *testing.B) { + withTestProcess("testvariables3", b, func(p *Process, fixture protest.Fixture) { + assertNoError(p.Continue(), b, "Continue()") + for i := 0; i < b.N; i++ { + p.allGCache = nil + _, err := p.GoroutinesInfo() + assertNoError(err, b, "GoroutinesInfo") + } + }) +} + func TestIssue262(t *testing.T) { // Continue does not work when the current breakpoint is set on a NOP instruction withTestProcess("issue262", t, func(p *Process, fixture protest.Fixture) { @@ -1275,3 +1325,15 @@ func TestIssue341(t *testing.T) { t.Logf("mapinf: %v\n", mapinf) }) } + +func BenchmarkLocalVariables(b *testing.B) { + withTestProcess("testvariables", b, func(p *Process, fixture protest.Fixture) { + assertNoError(p.Continue(), b, "Continue() returned an error") + scope, err := p.CurrentThread.Scope() + assertNoError(err, b, "Scope()") + for i := 0; i < b.N; i++ { + _, err := scope.LocalVariables() + assertNoError(err, b, "LocalVariables()") + } + }) +} diff --git a/proc/variables.go b/proc/variables.go index 1515095d..5c6fdc41 100644 --- a/proc/variables.go +++ b/proc/variables.go @@ -20,6 +20,8 @@ const ( maxVariableRecurse = 1 // How far to recurse when evaluating nested types. maxArrayValues = 64 // Max value for reading large arrays. maxErrCount = 3 // Max number of read errors to accept while evaluating slices, arrays and structs + + maxArrayStridePrefetch = 1024 // Maximum size of array stride for which we will prefetch the array contents chanRecv = "chan receive" chanSend = "chan send" @@ -39,7 +41,8 @@ type Variable struct { DwarfType dwarf.Type RealType dwarf.Type Kind reflect.Kind - thread *Thread + mem memoryReadWriter + dbp *Process Value constant.Value @@ -136,12 +139,21 @@ func ptrTypeKind(t *dwarf.PtrType) reflect.Kind { return reflect.Ptr } -func newVariable(name string, addr uintptr, dwarfType dwarf.Type, thread *Thread) *Variable { +func (scope *EvalScope) newVariable(name string, addr uintptr, dwarfType dwarf.Type) *Variable { + return newVariable(name, addr, dwarfType, scope.Thread.dbp, scope.Thread) +} + +func (v *Variable) newVariable(name string, addr uintptr, dwarfType dwarf.Type) *Variable { + return newVariable(name, addr, dwarfType, v.dbp, v.mem) +} + +func newVariable(name string, addr uintptr, dwarfType dwarf.Type, dbp *Process, mem memoryReadWriter) *Variable { v := &Variable{ Name: name, Addr: addr, DwarfType: dwarfType, - thread: thread, + mem: mem, + dbp: dbp, } v.RealType = resolveTypedef(v.DwarfType) @@ -156,7 +168,7 @@ func newVariable(name string, addr uintptr, dwarfType dwarf.Type, thread *Thread v.stride = 1 v.fieldType = &dwarf.UintType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 1, Name: "byte"}, BitSize: 8, BitOffset: 0}} if v.Addr != 0 { - v.base, v.Len, v.Unreadable = v.thread.readStringInfo(v.Addr) + v.base, v.Len, v.Unreadable = readStringInfo(v.mem, v.dbp.arch, v.Addr) } case t.StructName == "runtime.iface" || t.StructName == "runtime.eface": v.Kind = reflect.Interface @@ -222,8 +234,8 @@ func resolveTypedef(typ dwarf.Type) dwarf.Type { } } -func newConstant(val constant.Value, thread *Thread) *Variable { - v := &Variable{Value: val, thread: thread, loaded: true} +func newConstant(val constant.Value, mem memoryReadWriter) *Variable { + v := &Variable{Value: val, mem: mem, loaded: true} switch val.Kind() { case constant.Int: v.Kind = reflect.Int @@ -281,7 +293,7 @@ func (v *Variable) toField(field *dwarf.StructField) (*Variable, error) { name = fmt.Sprintf("%s.%s", v.Name, field.Name) } } - return newVariable(name, uintptr(int64(v.Addr)+field.ByteOffset), field.Type, v.thread), nil + return v.newVariable(name, uintptr(int64(v.Addr)+field.ByteOffset), field.Type), nil } // DwarfReader returns the DwarfReader containing the @@ -349,6 +361,11 @@ func parseG(thread *Thread, gaddr uint64, deref bool) (*G, error) { return nil, err } + var mem memoryReadWriter = thread + if gtype, err := thread.dbp.dwarf.Type(entry.Offset); err == nil { + mem = cacheMemory(thread, uintptr(gaddr), int(gtype.Size())) + } + // Parse defer deferAddr, err := rdr.AddrForMember("_defer", initialInstructions) if err != nil { @@ -356,7 +373,7 @@ func parseG(thread *Thread, gaddr uint64, deref bool) (*G, error) { } var deferPC uint64 // Dereference *defer pointer - deferAddrBytes, err := thread.readMemory(uintptr(deferAddr), thread.dbp.arch.PtrSize()) + deferAddrBytes, err := mem.readMemory(uintptr(deferAddr), thread.dbp.arch.PtrSize()) if err != nil { return nil, fmt.Errorf("error derefing defer %s", err) } @@ -367,11 +384,11 @@ func parseG(thread *Thread, gaddr uint64, deref bool) (*G, error) { return nil, err } deferPCAddr, err := rdr.AddrForMember("fn", initialDeferInstructions) - deferPC, err = thread.readUintRaw(uintptr(deferPCAddr), 8) + deferPC, err = readUintRaw(mem, uintptr(deferPCAddr), 8) if err != nil { return nil, err } - deferPC, err = thread.readUintRaw(uintptr(deferPC), 8) + deferPC, err = readUintRaw(mem, uintptr(deferPC), 8) if err != nil { return nil, err } @@ -391,11 +408,11 @@ func parseG(thread *Thread, gaddr uint64, deref bool) (*G, error) { return nil, err } // From sched, let's parse PC and SP. - sp, err := thread.readUintRaw(uintptr(schedAddr), 8) + sp, err := readUintRaw(mem, uintptr(schedAddr), 8) if err != nil { return nil, err } - pc, err := thread.readUintRaw(uintptr(schedAddr+uint64(thread.dbp.arch.PtrSize())), 8) + pc, err := readUintRaw(mem, uintptr(schedAddr+uint64(thread.dbp.arch.PtrSize())), 8) if err != nil { return nil, err } @@ -404,13 +421,13 @@ func parseG(thread *Thread, gaddr uint64, deref bool) (*G, error) { if err != nil { return nil, err } - atomicStatus, err := thread.readUintRaw(uintptr(atomicStatusAddr), 4) + atomicStatus, err := readUintRaw(mem, uintptr(atomicStatusAddr), 4) // Parse goid goidAddr, err := rdr.AddrForMember("goid", initialInstructions) if err != nil { return nil, err } - goid, err := thread.readIntRaw(uintptr(goidAddr), 8) + goid, err := readIntRaw(mem, uintptr(goidAddr), 8) if err != nil { return nil, err } @@ -419,7 +436,7 @@ func parseG(thread *Thread, gaddr uint64, deref bool) (*G, error) { if err != nil { return nil, err } - waitreason, _, err := thread.readString(uintptr(waitReasonAddr)) + waitreason, _, err := readString(mem, thread.dbp.arch, uintptr(waitReasonAddr)) if err != nil { return nil, err } @@ -428,7 +445,7 @@ func parseG(thread *Thread, gaddr uint64, deref bool) (*G, error) { if err != nil { return nil, err } - gopc, err := thread.readUintRaw(uintptr(gopcAddr), 8) + gopc, err := readUintRaw(mem, uintptr(gopcAddr), 8) if err != nil { return nil, err } @@ -725,7 +742,7 @@ func (scope *EvalScope) extractVarInfoFromEntry(entry *dwarf.Entry, rdr *reader. return nil, err } - return newVariable(n, uintptr(addr), t, scope.Thread), nil + return scope.newVariable(n, uintptr(addr), t), nil } // If v is a pointer a new variable is returned containing the value pointed by v. @@ -736,8 +753,8 @@ func (v *Variable) maybeDereference() *Variable { switch t := v.RealType.(type) { case *dwarf.PtrType: - ptrval, err := v.thread.readUintRaw(uintptr(v.Addr), int64(v.thread.dbp.arch.PtrSize())) - r := newVariable("", uintptr(ptrval), t.Type, v.thread) + ptrval, err := readUintRaw(v.mem, uintptr(v.Addr), t.ByteSize) + r := v.newVariable("", uintptr(ptrval), t.Type) if err != nil { r.Unreadable = err } @@ -757,6 +774,7 @@ func (v *Variable) loadValueInternal(recurseLevel int) { if v.Unreadable != nil || v.loaded || (v.Addr == 0 && v.base == 0) { return } + v.loaded = true switch v.Kind { case reflect.Ptr, reflect.UnsafePointer: @@ -777,13 +795,14 @@ func (v *Variable) loadValueInternal(recurseLevel int) { case reflect.String: var val string - val, v.Unreadable = v.thread.readStringValue(v.base, v.Len) + val, v.Unreadable = readStringValue(v.mem, v.base, v.Len) v.Value = constant.MakeString(val) case reflect.Slice, reflect.Array: v.loadArrayValues(recurseLevel) case reflect.Struct: + v.mem = cacheMemory(v.mem, v.Addr, int(v.RealType.Size())) t := v.RealType.(*dwarf.StructType) v.Len = int64(len(t.Field)) // Recursively call extractValue to grab @@ -805,15 +824,15 @@ func (v *Variable) loadValueInternal(recurseLevel int) { v.readComplex(v.RealType.(*dwarf.ComplexType).ByteSize) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: var val int64 - val, v.Unreadable = v.thread.readIntRaw(v.Addr, v.RealType.(*dwarf.IntType).ByteSize) + val, v.Unreadable = readIntRaw(v.mem, v.Addr, v.RealType.(*dwarf.IntType).ByteSize) v.Value = constant.MakeInt64(val) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: var val uint64 - val, v.Unreadable = v.thread.readUintRaw(v.Addr, v.RealType.(*dwarf.UintType).ByteSize) + val, v.Unreadable = readUintRaw(v.mem, v.Addr, v.RealType.(*dwarf.UintType).ByteSize) v.Value = constant.MakeUint64(val) case reflect.Bool: - val, err := v.thread.readMemory(v.Addr, 1) + val, err := v.mem.readMemory(v.Addr, 1) v.Unreadable = err if err == nil { v.Value = constant.MakeBool(val[0] != 0) @@ -849,8 +868,8 @@ func (v *Variable) setValue(y *Variable) error { err = v.writeComplex(real, imag, v.RealType.Size()) default: fmt.Printf("default\n") - if _, isptr := v.RealType.(*dwarf.PtrType); isptr { - err = v.writeUint(uint64(y.Children[0].Addr), int64(v.thread.dbp.arch.PtrSize())) + 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()) } @@ -859,12 +878,14 @@ func (v *Variable) setValue(y *Variable) error { return err } -func (thread *Thread) readStringInfo(addr uintptr) (uintptr, int64, error) { +func readStringInfo(mem memoryReadWriter, arch Arch, addr uintptr) (uintptr, int64, error) { // string data structure is always two ptrs in size. Addr, followed by len // http://research.swtch.com/godata + mem = cacheMemory(mem, addr, arch.PtrSize()*2) + // read len - val, err := thread.readMemory(addr+uintptr(thread.dbp.arch.PtrSize()), thread.dbp.arch.PtrSize()) + val, err := mem.readMemory(addr+uintptr(arch.PtrSize()), arch.PtrSize()) if err != nil { return 0, 0, fmt.Errorf("could not read string len %s", err) } @@ -874,7 +895,7 @@ func (thread *Thread) readStringInfo(addr uintptr) (uintptr, int64, error) { } // read addr - val, err = thread.readMemory(addr, thread.dbp.arch.PtrSize()) + val, err = mem.readMemory(addr, arch.PtrSize()) if err != nil { return 0, 0, fmt.Errorf("could not read string pointer %s", err) } @@ -886,13 +907,13 @@ func (thread *Thread) readStringInfo(addr uintptr) (uintptr, int64, error) { return addr, strlen, nil } -func (thread *Thread) readStringValue(addr uintptr, strlen int64) (string, error) { +func readStringValue(mem memoryReadWriter, addr uintptr, strlen int64) (string, error) { count := strlen if count > maxArrayValues { count = maxArrayValues } - val, err := thread.readMemory(addr, int(count)) + val, err := mem.readMemory(addr, int(count)) if err != nil { return "", fmt.Errorf("could not read string at %#v due to %s", addr, err) } @@ -902,23 +923,25 @@ func (thread *Thread) readStringValue(addr uintptr, strlen int64) (string, error return retstr, nil } -func (thread *Thread) readString(addr uintptr) (string, int64, error) { - addr, strlen, err := thread.readStringInfo(addr) +func readString(mem memoryReadWriter, arch Arch, addr uintptr) (string, int64, error) { + addr, strlen, err := readStringInfo(mem, arch, addr) if err != nil { return "", 0, err } - retstr, err := thread.readStringValue(addr, strlen) + retstr, err := readStringValue(mem, addr, strlen) return retstr, strlen, err } func (v *Variable) loadSliceInfo(t *dwarf.StructType) { + 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 = v.thread.readUintRaw(uintptr(int64(v.Addr)+f.ByteOffset), int64(v.thread.dbp.arch.PtrSize())) + 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 @@ -951,8 +974,8 @@ func (v *Variable) loadSliceInfo(t *dwarf.StructType) { } v.stride = v.fieldType.Size() - if _, ok := v.fieldType.(*dwarf.PtrType); ok { - v.stride = int64(v.thread.dbp.arch.PtrSize()) + if t, ok := v.fieldType.(*dwarf.PtrType); ok { + v.stride = t.ByteSize } return @@ -963,15 +986,20 @@ func (v *Variable) loadArrayValues(recurseLevel int) { return } + count := v.Len + // Cap number of elements + if count > maxArrayValues { + count = maxArrayValues + } + + if v.stride < maxArrayStridePrefetch { + v.mem = cacheMemory(v.mem, v.base, int(v.stride*count)) + } + errcount := 0 - for i := int64(0); i < v.Len; i++ { - // Cap number of elements - if i >= maxArrayValues { - break - } - - fieldvar := newVariable("", uintptr(int64(v.base)+(i*v.stride)), v.fieldType, v.thread) + for i := int64(0); i < count; i++ { + fieldvar := v.newVariable("", uintptr(int64(v.base)+(i*v.stride)), v.fieldType) fieldvar.loadValueInternal(recurseLevel + 1) if fieldvar.Unreadable != nil { @@ -999,8 +1027,8 @@ func (v *Variable) readComplex(size int64) { ftyp := &dwarf.FloatType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: fs, Name: fmt.Sprintf("float%d", fs)}, BitSize: fs * 8, BitOffset: 0}} - realvar := newVariable("real", v.Addr, ftyp, v.thread) - imagvar := newVariable("imaginary", v.Addr+uintptr(fs), ftyp, v.thread) + realvar := v.newVariable("real", v.Addr, ftyp) + imagvar := v.newVariable("imaginary", v.Addr+uintptr(fs), ftyp) realvar.loadValue() imagvar.loadValue() v.Value = constant.BinaryOp(realvar.Value, token.ADD, constant.MakeImag(imagvar.Value)) @@ -1016,10 +1044,10 @@ func (v *Variable) writeComplex(real, imag float64, size int64) error { return imagaddr.writeFloatRaw(imag, int64(size/2)) } -func (thread *Thread) readIntRaw(addr uintptr, size int64) (int64, error) { +func readIntRaw(mem memoryReadWriter, addr uintptr, size int64) (int64, error) { var n int64 - val, err := thread.readMemory(addr, int(size)) + val, err := mem.readMemory(addr, int(size)) if err != nil { return 0, err } @@ -1052,14 +1080,14 @@ func (v *Variable) writeUint(value uint64, size int64) error { binary.LittleEndian.PutUint64(val, uint64(value)) } - _, err := v.thread.writeMemory(v.Addr, val) + _, err := v.mem.writeMemory(v.Addr, val) return err } -func (thread *Thread) readUintRaw(addr uintptr, size int64) (uint64, error) { +func readUintRaw(mem memoryReadWriter, addr uintptr, size int64) (uint64, error) { var n uint64 - val, err := thread.readMemory(addr, int(size)) + val, err := mem.readMemory(addr, int(size)) if err != nil { return 0, err } @@ -1079,7 +1107,7 @@ func (thread *Thread) readUintRaw(addr uintptr, size int64) (uint64, error) { } func (v *Variable) readFloatRaw(size int64) (float64, error) { - val, err := v.thread.readMemory(v.Addr, int(size)) + val, err := v.mem.readMemory(v.Addr, int(size)) if err != nil { return 0.0, err } @@ -1111,19 +1139,19 @@ func (v *Variable) writeFloatRaw(f float64, size int64) error { binary.Write(buf, binary.LittleEndian, n) } - _, err := v.thread.writeMemory(v.Addr, buf.Bytes()) + _, err := v.mem.writeMemory(v.Addr, buf.Bytes()) return err } func (v *Variable) writeBool(value bool) error { val := []byte{0} val[0] = *(*byte)(unsafe.Pointer(&value)) - _, err := v.thread.writeMemory(v.Addr, val) + _, err := v.mem.writeMemory(v.Addr, val) return err } func (v *Variable) readFunctionPtr() { - val, err := v.thread.readMemory(v.Addr, v.thread.dbp.arch.PtrSize()) + val, err := v.mem.readMemory(v.Addr, v.dbp.arch.PtrSize()) if err != nil { v.Unreadable = err return @@ -1137,14 +1165,14 @@ func (v *Variable) readFunctionPtr() { return } - val, err = v.thread.readMemory(fnaddr, v.thread.dbp.arch.PtrSize()) + val, err = v.mem.readMemory(fnaddr, v.dbp.arch.PtrSize()) if err != nil { v.Unreadable = err return } v.base = uintptr(binary.LittleEndian.Uint64(val)) - fn := v.thread.dbp.goSymTable.PCToFunc(uint64(v.base)) + fn := v.dbp.goSymTable.PCToFunc(uint64(v.base)) if fn == nil { v.Unreadable = fmt.Errorf("could not find function for %#v", v.base) return @@ -1225,6 +1253,8 @@ func (v *Variable) mapIterator() *mapIterator { return it } + v.mem = cacheMemory(v.mem, v.base, int(v.RealType.Size())) + for _, f := range maptype.Field { var err error field, _ := sv.toField(f) @@ -1301,6 +1331,8 @@ func (it *mapIterator) nextBucket() bool { return false } + it.b.mem = cacheMemory(it.b.mem, it.b.Addr, int(it.b.RealType.Size())) + it.tophashes = nil it.keys = nil it.values = nil @@ -1403,6 +1435,8 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool) { var typestring, data *Variable isnil := false + v.mem = cacheMemory(v.mem, v.Addr, int(v.RealType.Size())) + for _, f := range v.RealType.(*dwarf.StructType).Field { switch f.Name { case "tab": // for runtime.iface @@ -1464,7 +1498,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool) { return } - typ, err := v.thread.dbp.findTypeExpr(t) + typ, err := v.dbp.findTypeExpr(t) if err != nil { v.Unreadable = fmt.Errorf("interface type \"%s\" not found for 0x%x: %v", constant.StringVal(typestring.Value), data.Addr, err) return @@ -1473,10 +1507,10 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool) { realtyp := resolveTypedef(typ) if _, isptr := realtyp.(*dwarf.PtrType); !isptr { // interface to non-pointer types are pointers even if the type says otherwise - typ = v.thread.dbp.pointerTo(typ) + typ = v.dbp.pointerTo(typ) } - data = newVariable("data", data.Addr, typ, data.thread) + data = data.newVariable("data", data.Addr, typ) v.Children = []Variable{*data} if loadData {