gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

memgrind.go (7556B)


      1 // Copyright 2021 The Libc 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 //go:build !libc.membrk && libc.memgrind
      6 // +build !libc.membrk,libc.memgrind
      7 
      8 // This is a debug-only version of the memory handling functions. When a
      9 // program is built with -tags=libc.memgrind the functions MemAuditStart and
     10 // MemAuditReport can be used to check for memory leaks.
     11 
     12 package libc // import "modernc.org/libc"
     13 
     14 import (
     15 	"fmt"
     16 	"runtime"
     17 	"sort"
     18 	"strings"
     19 	"unsafe"
     20 
     21 	"modernc.org/libc/errno"
     22 	"modernc.org/libc/sys/types"
     23 	"modernc.org/memory"
     24 )
     25 
     26 const memgrind = true
     27 
     28 type memReportItem struct {
     29 	p, pc uintptr
     30 	s     string
     31 }
     32 
     33 func (it *memReportItem) String() string {
     34 	more := it.s
     35 	if more != "" {
     36 		a := strings.Split(more, "\n")
     37 		more = "\n\t\t" + strings.Join(a, "\n\t\t")
     38 	}
     39 	return fmt.Sprintf("\t%s: %#x%s", pc2origin(it.pc), it.p, more)
     40 }
     41 
     42 type memReport []memReportItem
     43 
     44 func (r memReport) Error() string {
     45 	a := []string{"memory leaks"}
     46 	for _, v := range r {
     47 		a = append(a, v.String())
     48 	}
     49 	return strings.Join(a, "\n")
     50 }
     51 
     52 var (
     53 	allocator       memory.Allocator
     54 	allocs          map[uintptr]uintptr // addr: caller
     55 	allocsMore      map[uintptr]string
     56 	frees           map[uintptr]uintptr // addr: caller
     57 	memAudit        memReport
     58 	memAuditEnabled bool
     59 )
     60 
     61 func pc2origin(pc uintptr) string {
     62 	f := runtime.FuncForPC(pc)
     63 	var fn, fns string
     64 	var fl int
     65 	if f != nil {
     66 		fn, fl = f.FileLine(pc)
     67 		fns = f.Name()
     68 		if x := strings.LastIndex(fns, "."); x > 0 {
     69 			fns = fns[x+1:]
     70 		}
     71 	}
     72 	return fmt.Sprintf("%s:%d:%s", fn, fl, fns)
     73 }
     74 
     75 // void *malloc(size_t size);
     76 func Xmalloc(t *TLS, size types.Size_t) uintptr {
     77 	if size == 0 {
     78 		return 0
     79 	}
     80 
     81 	allocMu.Lock()
     82 
     83 	defer allocMu.Unlock()
     84 
     85 	p, err := allocator.UintptrCalloc(int(size))
     86 	if dmesgs {
     87 		dmesg("%v: %v -> %#x, %v", origin(1), size, p, err)
     88 	}
     89 	if err != nil {
     90 		t.setErrno(errno.ENOMEM)
     91 		return 0
     92 	}
     93 
     94 	if memAuditEnabled {
     95 		pc, _, _, ok := runtime.Caller(1)
     96 		if !ok {
     97 			panic("cannot obtain caller's PC")
     98 		}
     99 
    100 		delete(frees, p)
    101 		if pc0, ok := allocs[p]; ok {
    102 			dmesg("%v: malloc returns same address twice, previous call at %v:", pc2origin(pc), pc2origin(pc0))
    103 			panic(fmt.Errorf("%v: malloc returns same address twice, previous call at %v:", pc2origin(pc), pc2origin(pc0)))
    104 		}
    105 
    106 		allocs[p] = pc
    107 	}
    108 	return p
    109 }
    110 
    111 // void *calloc(size_t nmemb, size_t size);
    112 func Xcalloc(t *TLS, n, size types.Size_t) uintptr {
    113 	rq := int(n * size)
    114 	if rq == 0 {
    115 		return 0
    116 	}
    117 
    118 	allocMu.Lock()
    119 
    120 	defer allocMu.Unlock()
    121 
    122 	p, err := allocator.UintptrCalloc(int(n * size))
    123 	if dmesgs {
    124 		dmesg("%v: %v -> %#x, %v", origin(1), n*size, p, err)
    125 	}
    126 	if err != nil {
    127 		t.setErrno(errno.ENOMEM)
    128 		return 0
    129 	}
    130 
    131 	if memAuditEnabled {
    132 		pc, _, _, ok := runtime.Caller(1)
    133 		if !ok {
    134 			panic("cannot obtain caller's PC")
    135 		}
    136 
    137 		delete(frees, p)
    138 		if pc0, ok := allocs[p]; ok {
    139 			dmesg("%v: calloc returns same address twice, previous call at %v:", pc2origin(pc), pc2origin(pc0))
    140 			panic(fmt.Errorf("%v: calloc returns same address twice, previous call at %v:", pc2origin(pc), pc2origin(pc0)))
    141 		}
    142 
    143 		allocs[p] = pc
    144 	}
    145 	return p
    146 }
    147 
    148 // void *realloc(void *ptr, size_t size);
    149 func Xrealloc(t *TLS, ptr uintptr, size types.Size_t) uintptr {
    150 	allocMu.Lock()
    151 
    152 	defer allocMu.Unlock()
    153 
    154 	var pc uintptr
    155 	if memAuditEnabled {
    156 		var ok bool
    157 		if pc, _, _, ok = runtime.Caller(1); !ok {
    158 			panic("cannot obtain caller's PC")
    159 		}
    160 
    161 		if ptr != 0 {
    162 			if pc0, ok := frees[ptr]; ok {
    163 				dmesg("%v: realloc: double free of %#x, previous call at %v:", pc2origin(pc), ptr, pc2origin(pc0))
    164 				panic(fmt.Errorf("%v: realloc: double free of %#x, previous call at %v:", pc2origin(pc), ptr, pc2origin(pc0)))
    165 			}
    166 
    167 			if _, ok := allocs[ptr]; !ok {
    168 				dmesg("%v: %v: realloc, free of unallocated memory: %#x", origin(1), pc2origin(pc), ptr)
    169 				panic(fmt.Errorf("%v: realloc, free of unallocated memory: %#x", pc2origin(pc), ptr))
    170 			}
    171 
    172 			delete(allocs, ptr)
    173 			delete(allocsMore, ptr)
    174 			frees[ptr] = pc
    175 		}
    176 	}
    177 
    178 	p, err := allocator.UintptrRealloc(ptr, int(size))
    179 	if dmesgs {
    180 		dmesg("%v: %#x, %v -> %#x, %v", origin(1), ptr, size, p, err)
    181 	}
    182 	if err != nil {
    183 		t.setErrno(errno.ENOMEM)
    184 		return 0
    185 	}
    186 
    187 	if memAuditEnabled && p != 0 {
    188 		delete(frees, p)
    189 		if pc0, ok := allocs[p]; ok {
    190 			dmesg("%v: realloc returns same address twice, previous call at %v:", pc2origin(pc), pc2origin(pc0))
    191 			panic(fmt.Errorf("%v: realloc returns same address twice, previous call at %v:", pc2origin(pc), pc2origin(pc0)))
    192 		}
    193 
    194 		allocs[p] = pc
    195 	}
    196 	return p
    197 }
    198 
    199 // void free(void *ptr);
    200 func Xfree(t *TLS, p uintptr) {
    201 	if p == 0 {
    202 		return
    203 	}
    204 
    205 	if dmesgs {
    206 		dmesg("%v: %#x", origin(1), p)
    207 	}
    208 
    209 	allocMu.Lock()
    210 
    211 	defer allocMu.Unlock()
    212 
    213 	sz := memory.UintptrUsableSize(p)
    214 	if memAuditEnabled {
    215 		pc, _, _, ok := runtime.Caller(1)
    216 		if !ok {
    217 			panic("cannot obtain caller's PC")
    218 		}
    219 
    220 		if pc0, ok := frees[p]; ok {
    221 			dmesg("%v: double free of %#x, previous call at %v:", pc2origin(pc), p, pc2origin(pc0))
    222 			panic(fmt.Errorf("%v: double free of %#x, previous call at %v:", pc2origin(pc), p, pc2origin(pc0)))
    223 		}
    224 
    225 		if _, ok := allocs[p]; !ok {
    226 			dmesg("%v: free of unallocated memory: %#x", pc2origin(pc), p)
    227 			panic(fmt.Errorf("%v: free of unallocated memory: %#x", pc2origin(pc), p))
    228 		}
    229 
    230 		delete(allocs, p)
    231 		delete(allocsMore, p)
    232 		frees[p] = pc
    233 	}
    234 
    235 	for i := uintptr(0); i < uintptr(sz); i++ {
    236 		*(*byte)(unsafe.Pointer(p + i)) = 0
    237 	}
    238 	allocator.UintptrFree(p)
    239 }
    240 
    241 func UsableSize(p uintptr) types.Size_t {
    242 	allocMu.Lock()
    243 
    244 	defer allocMu.Unlock()
    245 
    246 	if memAuditEnabled {
    247 		pc, _, _, ok := runtime.Caller(1)
    248 		if !ok {
    249 			panic("cannot obtain caller's PC")
    250 		}
    251 
    252 		if _, ok := allocs[p]; !ok {
    253 			dmesg("%v: usable size of unallocated memory: %#x", pc2origin(pc), p)
    254 			panic(fmt.Errorf("%v: usable size of unallocated memory: %#x", pc2origin(pc), p))
    255 		}
    256 	}
    257 
    258 	return types.Size_t(memory.UintptrUsableSize(p))
    259 }
    260 
    261 // MemAuditStart locks the memory allocator, initializes and enables memory
    262 // auditing. Finally it unlocks the memory allocator.
    263 //
    264 // Some memory handling errors, like double free or freeing of unallocated
    265 // memory, will panic when memory auditing is enabled.
    266 //
    267 // This memory auditing functionality has to be enabled using the libc.memgrind
    268 // build tag.
    269 //
    270 // It is intended only for debug/test builds. It slows down memory allocation
    271 // routines and it has additional memory costs.
    272 func MemAuditStart() {
    273 	allocMu.Lock()
    274 
    275 	defer allocMu.Unlock()
    276 
    277 	allocs = map[uintptr]uintptr{} // addr: caller
    278 	allocsMore = map[uintptr]string{}
    279 	frees = map[uintptr]uintptr{} // addr: caller
    280 	memAuditEnabled = true
    281 }
    282 
    283 // MemAuditReport locks the memory allocator, reports memory leaks, if any.
    284 // Finally it disables memory auditing and unlocks the memory allocator.
    285 //
    286 // This memory auditing functionality has to be enabled using the libc.memgrind
    287 // build tag.
    288 //
    289 // It is intended only for debug/test builds. It slows down memory allocation
    290 // routines and it has additional memory costs.
    291 func MemAuditReport() (r error) {
    292 	allocMu.Lock()
    293 
    294 	defer func() {
    295 		allocs = nil
    296 		allocsMore = nil
    297 		frees = nil
    298 		memAuditEnabled = false
    299 		memAudit = nil
    300 		allocMu.Unlock()
    301 	}()
    302 
    303 	if len(allocs) != 0 {
    304 		for p, pc := range allocs {
    305 			memAudit = append(memAudit, memReportItem{p, pc, allocsMore[p]})
    306 		}
    307 		sort.Slice(memAudit, func(i, j int) bool {
    308 			return memAudit[i].String() < memAudit[j].String()
    309 		})
    310 		return memAudit
    311 	}
    312 
    313 	return nil
    314 }
    315 
    316 func MemAuditAnnotate(pc uintptr, s string) {
    317 	allocMu.Lock()
    318 	allocsMore[pc] = s
    319 	allocMu.Unlock()
    320 }