source: code/trunk/vendor/modernc.org/libc/memgrind.go@ 822

Last change on this file since 822 was 822, checked in by yakumo.izuru, 22 months ago

Prefer immortal.run over runit and rc.d, use vendored modules
for convenience.

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 7.4 KB
RevLine 
[822]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
12package libc // import "modernc.org/libc"
13
14import (
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
26const memgrind = true
27
28type memReportItem struct {
29 p, pc uintptr
30 s string
31}
32
33func (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
42type memReport []memReportItem
43
44func (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
52var (
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
61func 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);
76func 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);
112func 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);
149func 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);
200func 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
241func 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.
272func 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.
291func 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
316func MemAuditAnnotate(pc uintptr, s string) {
317 allocMu.Lock()
318 allocsMore[pc] = s
319 allocMu.Unlock()
320}
Note: See TracBrowser for help on using the repository browser.