Source file src/runtime/tracestring.go

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Trace string management.
     6  
     7  package runtime
     8  
     9  import "internal/trace/tracev2"
    10  
    11  // Trace strings.
    12  
    13  // traceStringTable is map of string -> unique ID that also manages
    14  // writing strings out into the trace.
    15  //
    16  // ID 0 is reserved for the empty string.
    17  type traceStringTable struct {
    18  	// lock protects buf.
    19  	lock mutex
    20  	buf  *traceBuf // string batches to write out to the trace.
    21  
    22  	// tab is a mapping of string -> unique ID.
    23  	tab traceMap
    24  }
    25  
    26  // put adds a string to the table, emits it, and returns a unique ID for it.
    27  func (t *traceStringTable) put(gen uintptr, s string) uint64 {
    28  	// Truncate the string now to avoid wasting space in the
    29  	// traceMap and to stay within traceRegionAlloc's block size limit.
    30  	if len(s) > tracev2.MaxEventTrailerDataSize {
    31  		s = s[:tracev2.MaxEventTrailerDataSize]
    32  	}
    33  	// Put the string in the table.
    34  	ss := stringStructOf(&s)
    35  	id, added := t.tab.put(ss.str, uintptr(ss.len))
    36  	if added {
    37  		// Write the string to the buffer.
    38  		systemstack(func() {
    39  			t.writeString(gen, id, s)
    40  		})
    41  	}
    42  	return id
    43  }
    44  
    45  // emit emits a string and creates an ID for it, but doesn't add it to the table. Returns the ID.
    46  func (t *traceStringTable) emit(gen uintptr, s string) uint64 {
    47  	if len(s) == 0 {
    48  		return 0 // Empty strings are implicitly assigned ID 0 already.
    49  	}
    50  	// Grab an ID and write the string to the buffer.
    51  	id := t.tab.stealID()
    52  	systemstack(func() {
    53  		t.writeString(gen, id, s)
    54  	})
    55  	return id
    56  }
    57  
    58  // writeString writes the string to t.buf.
    59  //
    60  // Must run on the systemstack because it acquires t.lock.
    61  //
    62  //go:systemstack
    63  func (t *traceStringTable) writeString(gen uintptr, id uint64, s string) {
    64  	// Truncate the string if necessary.
    65  	if len(s) > tracev2.MaxEventTrailerDataSize {
    66  		s = s[:tracev2.MaxEventTrailerDataSize]
    67  	}
    68  
    69  	lock(&t.lock)
    70  	w := unsafeTraceWriter(gen, t.buf)
    71  
    72  	// Ensure we have a place to write to.
    73  	var flushed bool
    74  	w, flushed = w.ensure(2 + 2*traceBytesPerNumber + len(s) /* tracev2.EvStrings + tracev2.EvString + ID + len + string data */)
    75  	if flushed {
    76  		// Annotate the batch as containing strings.
    77  		w.byte(byte(tracev2.EvStrings))
    78  	}
    79  
    80  	// Write out the string.
    81  	w.byte(byte(tracev2.EvString))
    82  	w.varint(id)
    83  	w.varint(uint64(len(s)))
    84  	w.stringData(s)
    85  
    86  	// Store back buf in case it was updated during ensure.
    87  	t.buf = w.traceBuf
    88  	unlock(&t.lock)
    89  }
    90  
    91  // reset clears the string table and flushes any buffers it has.
    92  //
    93  // Must be called only once the caller is certain nothing else will be
    94  // added to this table.
    95  func (t *traceStringTable) reset(gen uintptr) {
    96  	if t.buf != nil {
    97  		systemstack(func() {
    98  			lock(&trace.lock)
    99  			traceBufFlush(t.buf, gen)
   100  			unlock(&trace.lock)
   101  		})
   102  		t.buf = nil
   103  	}
   104  
   105  	// Reset the table.
   106  	t.tab.reset()
   107  }
   108  

View as plain text