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  	// Put the string in the table.
    29  	ss := stringStructOf(&s)
    30  	id, added := t.tab.put(ss.str, uintptr(ss.len))
    31  	if added {
    32  		// Write the string to the buffer.
    33  		systemstack(func() {
    34  			t.writeString(gen, id, s)
    35  		})
    36  	}
    37  	return id
    38  }
    39  
    40  // emit emits a string and creates an ID for it, but doesn't add it to the table. Returns the ID.
    41  func (t *traceStringTable) emit(gen uintptr, s string) uint64 {
    42  	if len(s) == 0 {
    43  		return 0 // Empty strings are implicitly assigned ID 0 already.
    44  	}
    45  	// Grab an ID and write the string to the buffer.
    46  	id := t.tab.stealID()
    47  	systemstack(func() {
    48  		t.writeString(gen, id, s)
    49  	})
    50  	return id
    51  }
    52  
    53  // writeString writes the string to t.buf.
    54  //
    55  // Must run on the systemstack because it acquires t.lock.
    56  //
    57  //go:systemstack
    58  func (t *traceStringTable) writeString(gen uintptr, id uint64, s string) {
    59  	// Truncate the string if necessary.
    60  	if len(s) > tracev2.MaxEventTrailerDataSize {
    61  		s = s[:tracev2.MaxEventTrailerDataSize]
    62  	}
    63  
    64  	lock(&t.lock)
    65  	w := unsafeTraceWriter(gen, t.buf)
    66  
    67  	// Ensure we have a place to write to.
    68  	var flushed bool
    69  	w, flushed = w.ensure(2 + 2*traceBytesPerNumber + len(s) /* tracev2.EvStrings + tracev2.EvString + ID + len + string data */)
    70  	if flushed {
    71  		// Annotate the batch as containing strings.
    72  		w.byte(byte(tracev2.EvStrings))
    73  	}
    74  
    75  	// Write out the string.
    76  	w.byte(byte(tracev2.EvString))
    77  	w.varint(id)
    78  	w.varint(uint64(len(s)))
    79  	w.stringData(s)
    80  
    81  	// Store back buf in case it was updated during ensure.
    82  	t.buf = w.traceBuf
    83  	unlock(&t.lock)
    84  }
    85  
    86  // reset clears the string table and flushes any buffers it has.
    87  //
    88  // Must be called only once the caller is certain nothing else will be
    89  // added to this table.
    90  func (t *traceStringTable) reset(gen uintptr) {
    91  	if t.buf != nil {
    92  		systemstack(func() {
    93  			lock(&trace.lock)
    94  			traceBufFlush(t.buf, gen)
    95  			unlock(&trace.lock)
    96  		})
    97  		t.buf = nil
    98  	}
    99  
   100  	// Reset the table.
   101  	t.tab.reset()
   102  }
   103  

View as plain text