package memory
import (
"fmt"
"os"
"runtime"
"strconv"
"strings"
"sync"
"sync/atomic"
"unsafe"
)
type CheckedAllocator struct {
mem Allocator
sz atomic .Int64
allocs sync .Map
}
func NewCheckedAllocator (mem Allocator ) *CheckedAllocator {
return &CheckedAllocator {mem : mem }
}
func (a *CheckedAllocator ) CurrentAlloc () int { return int (a .sz .Load ()) }
func (a *CheckedAllocator ) Allocate (size int ) []byte {
a .sz .Add (int64 (size ))
out := a .mem .Allocate (size )
if size == 0 {
return out
}
ptr := uintptr (unsafe .Pointer (&out [0 ]))
pcs := make ([]uintptr , maxRetainedFrames )
runtime .Callers (allocFrames +2 , pcs )
callersFrames := runtime .CallersFrames (pcs )
if pc , _ , l , ok := runtime .Caller (allocFrames ); ok {
a .allocs .Store (ptr , &dalloc {pc : pc , line : l , sz : size , callersFrames : callersFrames })
}
return out
}
func (a *CheckedAllocator ) Reallocate (size int , b []byte ) []byte {
a .sz .Add (int64 (size - len (b )))
oldptr := uintptr (unsafe .Pointer (&b [0 ]))
out := a .mem .Reallocate (size , b )
if size == 0 {
return out
}
newptr := uintptr (unsafe .Pointer (&out [0 ]))
a .allocs .Delete (oldptr )
pcs := make ([]uintptr , maxRetainedFrames )
runtime .Callers (reallocFrames +2 , pcs )
callersFrames := runtime .CallersFrames (pcs )
if pc , _ , l , ok := runtime .Caller (reallocFrames ); ok {
a .allocs .Store (newptr , &dalloc {pc : pc , line : l , sz : size , callersFrames : callersFrames })
}
return out
}
func (a *CheckedAllocator ) Free (b []byte ) {
a .sz .Add (int64 (len (b ) * -1 ))
defer a .mem .Free (b )
if len (b ) == 0 {
return
}
ptr := uintptr (unsafe .Pointer (&b [0 ]))
a .allocs .Delete (ptr )
}
const (
defAllocFrames = 4
defReallocFrames = 3
defMaxRetainedFrames = 0
)
var allocFrames, reallocFrames, maxRetainedFrames int = defAllocFrames , defReallocFrames , defMaxRetainedFrames
func init() {
if val , ok := os .LookupEnv ("ARROW_CHECKED_ALLOC_FRAMES" ); ok {
if f , err := strconv .Atoi (val ); err == nil {
allocFrames = f
}
}
if val , ok := os .LookupEnv ("ARROW_CHECKED_REALLOC_FRAMES" ); ok {
if f , err := strconv .Atoi (val ); err == nil {
reallocFrames = f
}
}
if val , ok := os .LookupEnv ("ARROW_CHECKED_MAX_RETAINED_FRAMES" ); ok {
if f , err := strconv .Atoi (val ); err == nil {
maxRetainedFrames = f
}
}
}
type dalloc struct {
pc uintptr
line int
sz int
callersFrames *runtime .Frames
}
type TestingT interface {
Errorf (format string , args ...interface {})
Helper ()
}
func (a *CheckedAllocator ) AssertSize (t TestingT , sz int ) {
a .allocs .Range (func (_ , value interface {}) bool {
info := value .(*dalloc )
f := runtime .FuncForPC (info .pc )
frames := info .callersFrames
var callersMsg strings .Builder
for {
frame , more := frames .Next ()
if frame .Line == 0 {
break
}
callersMsg .WriteString ("\t" )
if fn := frame .Func ; fn != nil {
callersMsg .WriteString (fmt .Sprintf ("%s+%x" , fn .Name (), frame .PC -fn .Entry ()))
} else {
callersMsg .WriteString (fmt .Sprintf ("%s, line %d" , frame .Function , frame .Line ))
}
callersMsg .WriteString ("\n\t\t" )
callersMsg .WriteString (frame .File + ":" + strconv .Itoa (frame .Line ))
callersMsg .WriteString ("\n" )
if !more {
break
}
}
file , line := f .FileLine (info .pc )
t .Errorf ("LEAK of %d bytes FROM\n\t%s+%x\n\t\t%s:%d\n%v" ,
info .sz ,
f .Name (), info .pc -f .Entry (),
file , line ,
callersMsg .String (),
)
return true
})
if int (a .sz .Load ()) != sz {
t .Helper ()
t .Errorf ("invalid memory size exp=%d, got=%d" , sz , a .sz .Load ())
}
}
type CheckedAllocatorScope struct {
alloc *CheckedAllocator
sz int
}
func NewCheckedAllocatorScope (alloc *CheckedAllocator ) *CheckedAllocatorScope {
sz := alloc .sz .Load ()
return &CheckedAllocatorScope {alloc : alloc , sz : int (sz )}
}
func (c *CheckedAllocatorScope ) CheckSize (t TestingT ) {
sz := int (c .alloc .sz .Load ())
if c .sz != sz {
t .Helper ()
t .Errorf ("invalid memory size exp=%d, got=%d" , c .sz , sz )
}
}
var _ Allocator = (*CheckedAllocator )(nil )
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .