memgrind.go 7.4 KB

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