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  }