| // Copyright 2023 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. |
| |
| //go:build goexperiment.exectracer2 |
| |
| // Simple not-in-heap bump-pointer traceRegion allocator. |
| |
| package runtime |
| |
| import ( |
| "internal/goarch" |
| "runtime/internal/sys" |
| "unsafe" |
| ) |
| |
| // traceRegionAlloc is a non-thread-safe region allocator. |
| // It holds a linked list of traceRegionAllocBlock. |
| type traceRegionAlloc struct { |
| head *traceRegionAllocBlock |
| off uintptr |
| } |
| |
| // traceRegionAllocBlock is a block in traceRegionAlloc. |
| // |
| // traceRegionAllocBlock is allocated from non-GC'd memory, so it must not |
| // contain heap pointers. Writes to pointers to traceRegionAllocBlocks do |
| // not need write barriers. |
| type traceRegionAllocBlock struct { |
| _ sys.NotInHeap |
| next *traceRegionAllocBlock |
| data [64<<10 - goarch.PtrSize]byte |
| } |
| |
| // alloc allocates n-byte block. |
| func (a *traceRegionAlloc) alloc(n uintptr) *notInHeap { |
| n = alignUp(n, goarch.PtrSize) |
| if a.head == nil || a.off+n > uintptr(len(a.head.data)) { |
| if n > uintptr(len(a.head.data)) { |
| throw("traceRegion: alloc too large") |
| } |
| block := (*traceRegionAllocBlock)(sysAlloc(unsafe.Sizeof(traceRegionAllocBlock{}), &memstats.other_sys)) |
| if block == nil { |
| throw("traceRegion: out of memory") |
| } |
| block.next = a.head |
| a.head = block |
| a.off = 0 |
| } |
| p := &a.head.data[a.off] |
| a.off += n |
| return (*notInHeap)(unsafe.Pointer(p)) |
| } |
| |
| // drop frees all previously allocated memory and resets the allocator. |
| func (a *traceRegionAlloc) drop() { |
| for a.head != nil { |
| block := a.head |
| a.head = block.next |
| sysFree(unsafe.Pointer(block), unsafe.Sizeof(traceRegionAllocBlock{}), &memstats.other_sys) |
| } |
| } |