| // 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(unsafe.Pointer(*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] = uint64(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 { | |
| 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 | |
| } |