Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6)

Unified Diff: impl/memory/gkvlite_tracing_utils.go

Issue 1911263002: Fix memory corruption bug in impl/memory (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/gae@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: impl/memory/gkvlite_tracing_utils.go
diff --git a/impl/memory/gkvlite_tracing_utils.go b/impl/memory/gkvlite_tracing_utils.go
new file mode 100644
index 0000000000000000000000000000000000000000..e766e432d5eb17cad0474dd681c86e390219b681
--- /dev/null
+++ b/impl/memory/gkvlite_tracing_utils.go
@@ -0,0 +1,188 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package memory
+
+import (
+ "encoding/hex"
+ "flag"
+ "fmt"
+ "os"
+ "runtime"
+ "sync"
+ "sync/atomic"
+
+ "github.com/luci/gkvlite"
+)
+
+var logMemCollectionFolder = flag.String(
dnj 2016/04/22 06:09:44 Note this will be compiled into every binary. It m
iannucci 2016/04/22 06:56:25 I thought about that, but I think it'll be worse t
+ "luci.gae.gkvlite_trace_folder", "",
+ "Set to a folder path to enable debugging traces to be dumped there. Set to '-' to dump to stdout.")
+var logMemCollectionOnce sync.Once
+var logMemCounter uint32
+var logMemNameKey = "holds a string indicating the GKVLiteDebuggingTraceName"
+var stdoutLock sync.Mutex
+
+func wrapTracingMemStore(store memStore) memStore {
+ var writer traceWriter
+ logNum := atomic.AddUint32(&logMemCounter, 1) - 1
+ collName := fmt.Sprintf("coll%d", logNum)
+
+ if *logMemCollectionFolder == "-" {
+ writer = func(format string, a ...interface{}) {
+ stdoutLock.Lock()
+ defer stdoutLock.Unlock()
+ fmt.Printf(format+"\n", a...)
+ }
+ } else {
+ logMemCollectionOnce.Do(func() {
+ fmt.Fprintln(os.Stderr, "REMOVING LOG COLLECTION FOLDER:", *logMemCollectionFolder)
dnj 2016/04/22 06:09:44 The possibility of me doing something stupid here
iannucci 2016/04/22 06:56:24 Good point, changed to use a tempdir at the design
+ if err := os.RemoveAll(*logMemCollectionFolder); !(err == os.ErrNotExist || err == nil) {
+ panic(err)
+ }
+ if err := os.MkdirAll(*logMemCollectionFolder, 0755); err != nil {
dnj 2016/04/22 06:09:44 I would say the user should ensure that the direct
iannucci 2016/04/22 06:56:25 Acknowledged.
+ panic(err)
+ }
+ })
+
+ lck := sync.Mutex{}
+ fname := fmt.Sprintf("%s/%d.trace", *logMemCollectionFolder, logNum)
dnj 2016/04/22 06:09:44 Use filepath.Join: https://golang.org/pkg/path/fil
iannucci 2016/04/22 06:56:25 Done.
+ fil, err := os.Create(fname)
+ if err != nil {
+ panic(err)
+ }
+ writer = func(format string, a ...interface{}) {
+ lck.Lock()
+ defer lck.Unlock()
+ fmt.Fprintf(fil, format+"\n", a...)
+ }
+ runtime.SetFinalizer(&writer, func(_ *traceWriter) { fil.Close() })
+ }
+ writer("%s := newMemStore()", collName)
+ return &tracingMemStoreImpl{store, writer, collName, 0, false}
+}
+
+type traceWriter func(format string, a ...interface{})
+
+type tracingMemStoreImpl struct {
+ i memStore
+ w traceWriter
+
+ collName string
+ // for the mutable store, this is a counter that increments for every
+ // Snapshot, and for snapshots, this is the number of the snapshot.
+ snapNum uint32
+ isSnap bool
+}
+
+var _ memStore = (*tracingMemStoreImpl)(nil)
+
+func (t *tracingMemStoreImpl) ImATestingSnapshot() {}
+
+func (t *tracingMemStoreImpl) colWriter(action, name string) traceWriter {
+ ident := t.ident()
+ hexname := hex.EncodeToString([]byte(name))
+ writer := func(format string, a ...interface{}) {
+ if format[0] == '/' { // comment
dnj 2016/04/22 06:09:44 Might as well just do a strings.HasPrefix("//") to
iannucci 2016/04/22 06:56:25 Done.
+ t.w(format, a...)
+ } else {
+ t.w(fmt.Sprintf("%s_%s%s", ident, hexname, format), a...)
+ }
+ }
+ writer(" := %s.%s(%q)", ident, action, name)
+ return writer
+}
+
+func (t *tracingMemStoreImpl) ident() string {
+ if t.isSnap {
+ return fmt.Sprintf("%s_snap%d", t.collName, t.snapNum)
+ }
+ return t.collName
+}
+
+func (t *tracingMemStoreImpl) GetCollection(name string) memCollection {
+ coll := t.i.GetCollection(name)
+ if coll == nil {
+ t.w("// %s.GetCollection(%q) -> nil", t.ident(), name)
+ return nil
+ }
+ writer := t.colWriter("GetCollection", name)
+ return &tracingMemCollectionImpl{coll, writer, 0}
+}
+
+func (t *tracingMemStoreImpl) GetCollectionNames() []string {
+ t.w("%s.GetCollectionNames()", t.ident())
+ return t.i.GetCollectionNames()
+}
+
+func (t *tracingMemStoreImpl) GetOrCreateCollection(name string) memCollection {
+ writer := t.colWriter("GetOrCreateCollection", name)
+ return &tracingMemCollectionImpl{t.i.GetOrCreateCollection(name), writer, 0}
+}
+
+func (t *tracingMemStoreImpl) Snapshot() memStore {
+ snap := t.i.Snapshot()
+ if snap == t.i {
+ t.w("// %s.Snapshot() -> self", t.ident())
+ return t
+ }
+ ret := &tracingMemStoreImpl{snap, t.w, t.collName, t.snapNum, true}
+ t.w("%s := %s.Snapshot()", ret.ident(), t.ident())
+ t.snapNum++
dnj 2016/04/22 06:09:44 Non-atomic? Is Snapshot not goroutine-safe? I supp
iannucci 2016/04/22 06:56:24 not safe, snapshot already needs a lock.
+ return ret
+}
+
+func (t *tracingMemStoreImpl) IsReadOnly() bool {
+ return t.i.IsReadOnly()
+}
+
+type tracingMemCollectionImpl struct {
+ i memCollection
+ w traceWriter
+ visitNumber uint32
+}
+
+var _ memCollection = (*tracingMemCollectionImpl)(nil)
+
+func (t *tracingMemCollectionImpl) Name() string {
+ return t.i.Name()
+}
+
+func (t *tracingMemCollectionImpl) Delete(k []byte) bool {
+ t.w(".Delete(%#v)", k)
+ return t.i.Delete(k)
+}
+
+func (t *tracingMemCollectionImpl) Get(k []byte) []byte {
+ t.w(".Get(%#v)", k)
+ return t.i.Get(k)
+}
+
+func (t *tracingMemCollectionImpl) GetTotals() (numItems, numBytes uint64) {
+ t.w(".GetTotals()")
+ return t.i.GetTotals()
+}
+
+func (t *tracingMemCollectionImpl) MinItem(withValue bool) *gkvlite.Item {
+ t.w(".MinItem(%t)", withValue)
+ return t.i.MinItem(withValue)
+}
+
+func (t *tracingMemCollectionImpl) Set(k, v []byte) {
+ t.w(".Set(%#v, %#v)", k, v)
+ t.i.Set(k, v)
+}
+
+func (t *tracingMemCollectionImpl) VisitItemsAscend(target []byte, withValue bool, visitor gkvlite.ItemVisitor) {
+ vnum := t.visitNumber
dnj 2016/04/22 06:09:44 Same, non-atomic check? If not, just use an "int"
iannucci 2016/04/22 06:56:24 changed to uint
+ t.visitNumber++
+
+ t.w(".VisitItemsAscend(%#v, %t, func(i *gkvlite.Item) bool{ return true }) // BEGIN VisitItemsAscend(%d)", target, withValue, vnum)
+ defer t.w("// END VisitItemsAscend(%d)", vnum)
+ t.i.VisitItemsAscend(target, withValue, visitor)
+}
+
+func (t *tracingMemCollectionImpl) IsReadOnly() bool {
+ return t.i.IsReadOnly()
+}

Powered by Google App Engine
This is Rietveld 408576698