go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/impl/memory/memstore_tracing_utils.go (about) 1 // Copyright 2015 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package memory 16 17 import ( 18 "encoding/hex" 19 "flag" 20 "fmt" 21 "io/ioutil" 22 "os" 23 "path/filepath" 24 "runtime" 25 "strings" 26 "sync" 27 "sync/atomic" 28 ) 29 30 var logMemCollectionFolder = flag.String( 31 "luci.gae.store_trace_folder", "", 32 "Set to a folder path to enable debugging traces to be dumped there. Set to '-' to dump to stdout.") 33 var logMemCollectionFolderTmp string 34 var logMemCollectionOnce sync.Once 35 var logMemCounter uint32 36 var stdoutLock sync.Mutex 37 38 func wrapTracingMemStore(store memStore) memStore { 39 var writer traceWriter 40 logNum := atomic.AddUint32(&logMemCounter, 1) - 1 41 collName := fmt.Sprintf("coll%d", logNum) 42 43 if *logMemCollectionFolder == "-" { 44 writer = func(format string, a ...any) { 45 stdoutLock.Lock() 46 defer stdoutLock.Unlock() 47 fmt.Printf(format+"\n", a...) 48 } 49 } else { 50 logMemCollectionOnce.Do(func() { 51 var err error 52 logMemCollectionFolderTmp, err = ioutil.TempDir(*logMemCollectionFolder, "luci-gae-store_trace") 53 if err != nil { 54 panic(err) 55 } 56 logMemCollectionFolderTmp, err = filepath.Abs(logMemCollectionFolderTmp) 57 if err != nil { 58 panic(err) 59 } 60 fmt.Fprintf(os.Stderr, "Saving store trace files to %q\n", logMemCollectionFolderTmp) 61 }) 62 if logMemCollectionFolderTmp == "" { 63 panic("unable to create folder for tracefiles") 64 } 65 66 lck := sync.Mutex{} 67 fname := fmt.Sprintf(filepath.Join(logMemCollectionFolderTmp, fmt.Sprintf("%d.trace", logNum))) 68 fil, err := os.Create(fname) 69 if err != nil { 70 panic(err) 71 } 72 writer = func(format string, a ...any) { 73 lck.Lock() 74 defer lck.Unlock() 75 fmt.Fprintf(fil, format+"\n", a...) 76 } 77 runtime.SetFinalizer(&writer, func(_ *traceWriter) { fil.Close() }) 78 } 79 writer("%s := newMemStore()", collName) 80 return &tracingMemStoreImpl{store, writer, collName, 0, false} 81 } 82 83 type traceWriter func(format string, a ...any) 84 85 type tracingMemStoreImpl struct { 86 i memStore 87 w traceWriter 88 89 collName string 90 // for the mutable store, this is a counter that increments for every 91 // Snapshot, and for snapshots, this is the number of the snapshot. 92 snapNum uint 93 isSnap bool 94 } 95 96 var _ memStore = (*tracingMemStoreImpl)(nil) 97 98 func (t *tracingMemStoreImpl) ImATestingSnapshot() {} 99 100 func (t *tracingMemStoreImpl) colWriter(action, name string) traceWriter { 101 ident := t.ident() 102 hexname := hex.EncodeToString([]byte(name)) 103 writer := func(format string, a ...any) { 104 if strings.HasPrefix(format, "//") { // comment 105 t.w(format, a...) 106 } else { 107 t.w(fmt.Sprintf("%s_%s%s", ident, hexname, format), a...) 108 } 109 } 110 writer(" := %s.%s(%q)", ident, action, name) 111 return writer 112 } 113 114 func (t *tracingMemStoreImpl) ident() string { 115 if t.isSnap { 116 return fmt.Sprintf("%s_snap%d", t.collName, t.snapNum) 117 } 118 return t.collName 119 } 120 121 func (t *tracingMemStoreImpl) GetCollection(name string) memCollection { 122 coll := t.i.GetCollection(name) 123 if coll == nil { 124 t.w("// %s.GetCollection(%q) -> nil", t.ident(), name) 125 return nil 126 } 127 writer := t.colWriter("GetCollection", name) 128 return &tracingMemCollectionImpl{coll, writer, 0, 0} 129 } 130 131 func (t *tracingMemStoreImpl) GetCollectionNames() []string { 132 t.w("%s.GetCollectionNames()", t.ident()) 133 return t.i.GetCollectionNames() 134 } 135 136 func (t *tracingMemStoreImpl) GetOrCreateCollection(name string) memCollection { 137 writer := t.colWriter("GetOrCreateCollection", name) 138 return &tracingMemCollectionImpl{t.i.GetOrCreateCollection(name), writer, 0, 0} 139 } 140 141 func (t *tracingMemStoreImpl) Snapshot() memStore { 142 snap := t.i.Snapshot() 143 if snap == t.i { 144 t.w("// %s.Snapshot() -> self", t.ident()) 145 return t 146 } 147 ret := &tracingMemStoreImpl{snap, t.w, t.collName, t.snapNum, true} 148 t.w("%s := %s.Snapshot()", ret.ident(), t.ident()) 149 t.snapNum++ 150 return ret 151 } 152 153 func (t *tracingMemStoreImpl) IsReadOnly() bool { 154 return t.i.IsReadOnly() 155 } 156 157 type tracingMemCollectionImpl struct { 158 i memCollection 159 w traceWriter 160 161 iterNumber uint 162 forEachNumber uint 163 } 164 165 var _ memCollection = (*tracingMemCollectionImpl)(nil) 166 167 func (t *tracingMemCollectionImpl) Name() string { 168 return t.i.Name() 169 } 170 171 func (t *tracingMemCollectionImpl) Delete(k []byte) { 172 t.w(".Delete(%#v)", k) 173 t.i.Delete(k) 174 } 175 176 func (t *tracingMemCollectionImpl) Get(k []byte) []byte { 177 t.w(".Get(%#v)", k) 178 return t.i.Get(k) 179 } 180 181 func (t *tracingMemCollectionImpl) MinItem() *storeEntry { 182 t.w(".MinItem()") 183 return t.i.MinItem() 184 } 185 186 func (t *tracingMemCollectionImpl) Set(k, v []byte) { 187 t.w(".Set(%#v, %#v)", k, v) 188 t.i.Set(k, v) 189 } 190 191 func (t *tracingMemCollectionImpl) Iterator(pivot []byte) memIterator { 192 inum := t.iterNumber 193 t.iterNumber++ 194 195 t.w(".Iterator(%#v) // Created Iterator #%d", pivot, inum) 196 return &tracingMemIteratorImpl{t.i.Iterator(pivot), t.w, inum} 197 } 198 199 func (t *tracingMemCollectionImpl) ForEachItem(fn memVisitor) { 200 vnum := t.forEachNumber 201 t.forEachNumber++ 202 203 t.w(".ForEachItem(func(k, v []byte) bool{ return true }) // BEGIN ForEachItem(%d)", vnum) 204 defer t.w("// END ForEachItem(%d)", vnum) 205 t.i.ForEachItem(fn) 206 } 207 208 func (t *tracingMemCollectionImpl) IsReadOnly() bool { 209 return t.i.IsReadOnly() 210 } 211 212 type tracingMemIteratorImpl struct { 213 i memIterator 214 w traceWriter 215 num uint 216 } 217 218 func (t *tracingMemIteratorImpl) Next() *storeEntry { 219 t.w(".Next() // Iterator #%d", t.num) 220 return t.i.Next() 221 }