// Copyright 2017 The Go 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 runtime import ( "runtime/internal/atomic" "unsafe" ) // A profBuf is a lock-free buffer for profiling events, // safe for concurrent use by one reader and one writer. // The writer may be a signal handler running without a user g. // The reader is assumed to be a user g. // // Each logged event corresponds to a fixed size header, a list of // uintptrs (typically a stack), and exactly one unsafe.Pointer tag. // The header and uintptrs are stored in the circular buffer data and the // tag is stored in a circular buffer tags, running in parallel. // In the circular buffer data, each event takes 2+hdrsize+len(stk) // words: the value 2+hdrsize+len(stk), then the time of the event, then // hdrsize words giving the fixed-size header, and then len(stk) words // for the stack. // // The current effective offsets into the tags and data circular buffers // for reading and writing are stored in the high 30 and low 32 bits of r and w. // The bottom bits of the high 32 are additional flag bits in w, unused in r. // "Effective" offsets means the total number of reads or writes, mod 2^length. // The offset in the buffer is the effective offset mod the length of the buffer. // To make wraparound mod 2^length match wraparound mod length of the buffer, // the length of the buffer must be a power of two. // // If the reader catches up to the writer, a flag passed to read controls // whether the read blocks until more data is available. A read returns a // pointer to the buffer data itself; the caller is assumed to be done with // that data at the next read. The read offset rNext tracks the next offset to // be returned by read. By definition, r ≤ rNext ≤ w (before wraparound), // and rNext is only used by the reader, so it can be accessed without atomics. // // If the writer gets ahead of the reader, so that the buffer fills, // future writes are discarded and replaced in the output stream by an // overflow entry, which has size 2+hdrsize+1, time set to the time of // the first discarded write, a header of all zeroed words, and a "stack" // containing one word, the number of discarded writes. // // Between the time the buffer fills and the buffer becomes empty enough // to hold more data, the overflow entry is stored as a pending overflow // entry in the fields overflow and overflowTime. The pending overflow // entry can be turned into a real record by either the writer or the // reader. If the writer is called to write a new record and finds that // the output buffer has room for both the pending overflow entry and the // new record, the writer emits the pending overflow entry and the new // record into the buffer. If the reader is called to read data and finds // that the output buffer is empty but that there is a pending overflow // entry, the reader will return a synthesized record for the pending // overflow entry. // // Only the writer can create or add to a pending overflow entry, but // either the reader or the writer can clear the pending overflow entry. // A pending overflow entry is indicated by the low 32 bits of 'overflow' // holding the number of discarded writes, and overflowTime holding the // time of the first discarded write. The high 32 bits of 'overflow' // increment each time the low 32 bits transition from zero to non-zero // or vice versa. This sequence number avoids ABA problems in the use of // compare-and-swap to coordinate between reader and writer. // The overflowTime is only written when the low 32 bits of overflow are // zero, that is, only when there is no pending overflow entry, in // preparation for creating a new one. The reader can therefore fetch and // clear the entry atomically using // // for { // overflow = load(&b.overflow) // if uint32(overflow) == 0 { // // no pending entry // break // } // time = load(&b.overflowTime) // if cas(&b.overflow, overflow, ((overflow>>32)+1)<<32) { // // pending entry cleared // break // } // } // if uint32(overflow) > 0 { // emit entry for uint32(overflow), time // } type profBuf struct { // accessed atomically r, w profAtomic overflow atomic.Uint64 overflowTime atomic.Uint64 eof atomic.Uint32 // immutable (excluding slice content) hdrsize uintptr data []uint64 tags []unsafe.Pointer // owned by reader rNext profIndex overflowBuf []uint64 // for use by reader to return overflow record wait note } // A profAtomic is the atomically-accessed word holding a profIndex. type profAtomic uint64 // A profIndex is the packet tag and data counts and flags bits, described above. type profIndex uint64 const ( profReaderSleeping profIndex = 1 << 32 // reader is sleeping and must be woken up profWriteExtra profIndex = 1 << 33 // overflow or eof waiting ) func (x *profAtomic) load() profIndex { return profIndex(atomic.Load64((*uint64)(x))) } func (x *profAtomic) store(new profIndex) { atomic.Store64((*uint64)(x), uint64(new)) } func (x *profAtomic) cas(old, new profIndex) bool { return atomic.Cas64((*uint64)(x), uint64(old), uint64(new)) } func (x profIndex) dataCount() uint32 { return uint32(x) } func (x profIndex) tagCount() uint32 { return uint32(x >> 34) } // countSub subtracts two counts obtained from profIndex.dataCount or profIndex.tagCount, // assuming that they are no more than 2^29 apart (guaranteed since they are never more than // len(data) or len(tags) apart, respectively). // tagCount wraps at 2^30, while dataCount wraps at 2^32. // This function works for both. func countSub(x, y uint32) int { // x-y is 32-bit signed or 30-bit signed; sign-extend to 32 bits and convert to int. return int(int32(x-y) << 2 >> 2) } // addCountsAndClearFlags returns the packed form of "x + (data, tag) - all flags". func (x profIndex) addCountsAndClearFlags(data, tag int) profIndex { return profIndex((uint64(x)>>34+uint64(uint32(tag)<<2>>2))<<34 | uint64(uint32(x)+uint32(data))) } // hasOverflow reports whether b has any overflow records pending. func (b *profBuf) hasOverflow() bool { return uint32(b.overflow.Load()) > 0 } // takeOverflow consumes the pending overflow records, returning the overflow count // and the time of the first overflow. // When called by the reader, it is racing against incrementOverflow. func (b *profBuf) takeOverflow() (count uint32, time uint64) { overflow := b.overflow.Load() time = b.overflowTime.Load() for { count = uint32(overflow) if count == 0 { time = 0 break } // Increment generation, clear overflow count in low bits. if b.overflow.CompareAndSwap(overflow, ((overflow>>32)+1)<<32) { break } overflow = b.overflow.Load() time = b.overflowTime.Load() } return uint32(overflow), time } // incrementOverflow records a single overflow at time now. // It is racing against a possible takeOverflow in the reader. func (b *profBuf) incrementOverflow(now int64) { for { overflow := b.overflow.Load() // Once we see b.overflow reach 0, it's stable: no one else is changing it underfoot. // We need to set overflowTime if we're incrementing b.overflow from 0. if uint32(overflow) == 0 { // Store overflowTime first so it's always available when overflow != 0. b.overflowTime.Store(uint64(now)) b.overflow.Store((((overflow >> 32) + 1) << 32) + 1) break } // Otherwise we're racing to increment against reader // who wants to set b.overflow to 0. // Out of paranoia, leave 2³²-1 a sticky overflow value, // to avoid wrapping around. Extremely unlikely. if int32(overflow) == -1 { break } if b.overflow.CompareAndSwap(overflow, overflow+1) { break } } } // newProfBuf returns a new profiling buffer with room for // a header of hdrsize words and a buffer of at least bufwords words. func newProfBuf(hdrsize, bufwords, tags int) *profBuf { if min := 2 + hdrsize + 1; bufwords < min { bufwords = min } // Buffer sizes must be power of two, so that we don't have to // worry about uint32 wraparound changing the effective position // within the buffers. We store 30 bits of count; limiting to 28 // gives us some room for intermediate calculations. if bufwords >= 1<<28 || tags >= 1<<28 { throw("newProfBuf: buffer too large") } var i int for i = 1; i < bufwords; i <<= 1 { } bufwords = i for i = 1; i < tags; i <<= 1 { } tags = i b := new(profBuf) b.hdrsize = uintptr(hdrsize) b.data = make([]uint64, bufwords) b.tags = make([]unsafe.Pointer, tags) b.overflowBuf = make([]uint64, 2+b.hdrsize+1) return b } // canWriteRecord reports whether the buffer has room // for a single contiguous record with a stack of length nstk. func (b *profBuf) canWriteRecord(nstk int) bool { br := b.r.load() bw := b.w.load() // room for tag? if countSub(br.tagCount(), bw.tagCount())+len(b.tags) < 1 { return false } // room for data? nd := countSub(br.dataCount(), bw.dataCount()) + len(b.data) want := 2 + int(b.hdrsize) + nstk i := int(bw.dataCount() % uint32(len(b.data))) if i+want > len(b.data) { // Can't fit in trailing fragment of slice. // Skip over that and start over at beginning of slice. nd -= len(b.data) - i } return nd >= want } // canWriteTwoRecords reports whether the buffer has room // for two records with stack lengths nstk1, nstk2, in that order. // Each record must be contiguous on its own, but the two // records need not be contiguous (one can be at the end of the buffer // and the other can wrap around and start at the beginning of the buffer). func (b *profBuf) canWriteTwoRecords(nstk1, nstk2 int) bool { br := b.r.load() bw := b.w.load() // room for tag? if countSub(br.tagCount(), bw.tagCount())+len(b.tags) < 2 { return false } // room for data? nd := countSub(br.dataCount(), bw.dataCount()) + len(b.data) // first record want := 2 + int(b.hdrsize) + nstk1 i := int(bw.dataCount() % uint32(len(b.data))) if i+want > len(b.data) { // Can't fit in trailing fragment of slice. // Skip over that and start over at beginning of slice. nd -= len(b.data) - i i = 0 } i += want nd -= want // second record want = 2 + int(b.hdrsize) + nstk2 if i+want > len(b.data) { // Can't fit in trailing fragment of slice. // Skip over that and start over at beginning of slice. nd -= len(b.data) - i i = 0 } return nd >= want } // write writes an entry to the profiling buffer b. // The entry begins with a fixed hdr, which must have // length b.hdrsize, followed by a variable-sized stack // and a single tag pointer *tagPtr (or nil if tagPtr is nil). // No write barriers allowed because this might be called from a signal handler. func (b *profBuf) write(tagPtr *unsafe.Pointer, now int64, hdr []uint64, stk []uintptr) { if b == nil { return } if len(hdr) > int(b.hdrsize) { throw("misuse of profBuf.write") } if hasOverflow := b.hasOverflow(); hasOverflow && b.canWriteTwoRecords(1, len(stk)) { // Room for both an overflow record and the one being written. // Write the overflow record if the reader hasn't gotten to it yet. // Only racing against reader, not other writers. count, time := b.takeOverflow() if count > 0 { var stk [1]uintptr stk[0] = uintptr(count) b.write(nil, int64(time), nil, stk[:]) } } else if hasOverflow || !b.canWriteRecord(len(stk)) { // Pending overflow without room to write overflow and new records // or no overflow but also no room for new record. b.incrementOverflow(now) b.wakeupExtra() return } // There's room: write the record. br := b.r.load() bw := b.w.load() // Profiling tag // // The tag is a pointer, but we can't run a write barrier here. // We have interrupted the OS-level execution of gp, but the // runtime still sees gp as executing. In effect, we are running // in place of the real gp. Since gp is the only goroutine that // can overwrite gp.labels, the value of gp.labels is stable during // this signal handler: it will still be reachable from gp when // we finish executing. If a GC is in progress right now, it must // keep gp.labels alive, because gp.labels is reachable from gp. // If gp were to overwrite gp.labels, the deletion barrier would // still shade that pointer, which would preserve it for the // in-progress GC, so all is well. Any future GC will see the // value we copied when scanning b.tags (heap-allocated). // We arrange that the store here is always overwriting a nil, // so there is no need for a deletion barrier on b.tags[wt]. wt := int(bw.tagCount() % uint32(len(b.tags))) if tagPtr != nil { *(*uintptr)(unsafe.Pointer(&b.tags[wt])) = uintptr(*tagPtr) } // Main record. // It has to fit in a contiguous section of the slice, so if it doesn't fit at the end, // leave a rewind marker (0) and start over at the beginning of the slice. wd := int(bw.dataCount() % uint32(len(b.data))) nd := countSub(br.dataCount(), bw.dataCount()) + len(b.data) skip := 0 if wd+2+int(b.hdrsize)+len(stk) > len(b.data) { b.data[wd] = 0 skip = len(b.data) - wd nd -= skip wd = 0 } data := b.data[wd:] data[0] = uint64(2 + b.hdrsize + uintptr(len(stk))) // length data[1] = uint64(now) // time stamp // header, zero-padded i := uintptr(copy(data[2:2+b.hdrsize], hdr)) for ; i < b.hdrsize; i++ { data[2+i] = 0 } for i, pc := range stk { data[2+b.hdrsize+uintptr(i)] = uint64(pc) } for { // Commit write. // Racing with reader setting flag bits in b.w, to avoid lost wakeups. old := b.w.load() new := old.addCountsAndClearFlags(skip+2+len(stk)+int(b.hdrsize), 1) if !b.w.cas(old, new) { continue } // If there was a reader, wake it up. if old&profReaderSleeping != 0 { notewakeup(&b.wait) } break } } // close signals that there will be no more writes on the buffer. // Once all the data has been read from the buffer, reads will return eof=true. func (b *profBuf) close() { if b.eof.Load() > 0 { throw("runtime: profBuf already closed") } b.eof.Store(1) b.wakeupExtra() } // wakeupExtra must be called after setting one of the "extra" // atomic fields b.overflow or b.eof. // It records the change in b.w and wakes up the reader if needed. func (b *profBuf) wakeupExtra() { for { old := b.w.load() new := old | profWriteExtra if !b.w.cas(old, new) { continue } if old&profReaderSleeping != 0 { notewakeup(&b.wait) } break } } // profBufReadMode specifies whether to block when no data is available to read. type profBufReadMode int const ( profBufBlocking profBufReadMode = iota profBufNonBlocking ) var overflowTag [1]unsafe.Pointer // always nil func (b *profBuf) read(mode profBufReadMode) (data []uint64, tags []unsafe.Pointer, eof bool) { if b == nil { return nil, nil, true } br := b.rNext // Commit previous read, returning that part of the ring to the writer. // First clear tags that have now been read, both to avoid holding // up the memory they point at for longer than necessary // and so that b.write can assume it is always overwriting // nil tag entries (see comment in b.write). rPrev := b.r.load() if rPrev != br { ntag := countSub(br.tagCount(), rPrev.tagCount()) ti := int(rPrev.tagCount() % uint32(len(b.tags))) for i := 0; i < ntag; i++ { b.tags[ti] = nil if ti++; ti == len(b.tags) { ti = 0 } } b.r.store(br) } Read: bw := b.w.load() numData := countSub(bw.dataCount(), br.dataCount()) if numData == 0 { if b.hasOverflow() { // No data to read, but there is overflow to report. // Racing with writer flushing b.overflow into a real record. count, time := b.takeOverflow() if count == 0 { // Lost the race, go around again. goto Read } // Won the race, report overflow. dst := b.overflowBuf dst[0] = uint64(2 + b.hdrsize + 1) dst[1] = time for i := uintptr(0); i < b.hdrsize; i++ { dst[2+i] = 0 } dst[2+b.hdrsize] = uint64(count) return dst[:2+b.hdrsize+1], overflowTag[:1], false } if b.eof.Load() > 0 { // No data, no overflow, EOF set: done. return nil, nil, true } if bw&profWriteExtra != 0 { // Writer claims to have published extra information (overflow or eof). // Attempt to clear notification and then check again. // If we fail to clear the notification it means b.w changed, // so we still need to check again. b.w.cas(bw, bw&^profWriteExtra) goto Read } // Nothing to read right now. // Return or sleep according to mode. if mode == profBufNonBlocking { // Necessary on Darwin, notetsleepg below does not work in signal handler, root cause of #61768. return nil, nil, false } if !b.w.cas(bw, bw|profReaderSleeping) { goto Read } // Committed to sleeping. notetsleepg(&b.wait, -1) noteclear(&b.wait) goto Read } data = b.data[br.dataCount()%uint32(len(b.data)):] if len(data) > numData { data = data[:numData] } else { numData -= len(data) // available in case of wraparound } skip := 0 if data[0] == 0 { // Wraparound record. Go back to the beginning of the ring. skip = len(data) data = b.data if len(data) > numData { data = data[:numData] } } ntag := countSub(bw.tagCount(), br.tagCount()) if ntag == 0 { throw("runtime: malformed profBuf buffer - tag and data out of sync") } tags = b.tags[br.tagCount()%uint32(len(b.tags)):] if len(tags) > ntag { tags = tags[:ntag] } // Count out whole data records until either data or tags is done. // They are always in sync in the buffer, but due to an end-of-slice // wraparound we might need to stop early and return the rest // in the next call. di := 0 ti := 0 for di < len(data) && data[di] != 0 && ti < len(tags) { if uintptr(di)+uintptr(data[di]) > uintptr(len(data)) { throw("runtime: malformed profBuf buffer - invalid size") } di += int(data[di]) ti++ } // Remember how much we returned, to commit read on next call. b.rNext = br.addCountsAndClearFlags(skip+di, ti) if raceenabled { // Match racereleasemerge in runtime_setProfLabel, // so that the setting of the labels in runtime_setProfLabel // is treated as happening before any use of the labels // by our caller. The synchronization on labelSync itself is a fiction // for the race detector. The actual synchronization is handled // by the fact that the signal handler only reads from the current // goroutine and uses atomics to write the updated queue indices, // and then the read-out from the signal handler buffer uses // atomics to read those queue indices. raceacquire(unsafe.Pointer(&labelSync)) } return data[:di], tags[:ti], false }