diff options
| author | otto <> | 2023-04-16 19:46:17 +0000 | 
|---|---|---|
| committer | otto <> | 2023-04-16 19:46:17 +0000 | 
| commit | c2ef829610e3a4d1d65186fa3abad107f8c440bd (patch) | |
| tree | e11ef197e01c1f2b19b11f987cbdb6e428fc6ad0 /src/lib/libc/stdlib/malloc.c | |
| parent | 6afbb36ddfb06e08673c041f8b37147f2ba7aae8 (diff) | |
| download | openbsd-c2ef829610e3a4d1d65186fa3abad107f8c440bd.tar.gz openbsd-c2ef829610e3a4d1d65186fa3abad107f8c440bd.tar.bz2 openbsd-c2ef829610e3a4d1d65186fa3abad107f8c440bd.zip | |
Dump (leak) info using utrace(2) and compile the code always in
except for bootblocks. This way we have built-in leak detecction
always (if enable by malloc flags). See man pages for details.
Diffstat (limited to 'src/lib/libc/stdlib/malloc.c')
| -rw-r--r-- | src/lib/libc/stdlib/malloc.c | 323 | 
1 files changed, 182 insertions, 141 deletions
| diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c index 0df1fe3e4a..a661b9d252 100644 --- a/src/lib/libc/stdlib/malloc.c +++ b/src/lib/libc/stdlib/malloc.c | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | /* $OpenBSD: malloc.c,v 1.280 2023/04/05 06:25:38 otto Exp $ */ | 1 | /* $OpenBSD: malloc.c,v 1.281 2023/04/16 19:46:17 otto Exp $ */ | 
| 2 | /* | 2 | /* | 
| 3 | * Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek <otto@drijf.net> | 3 | * Copyright (c) 2008, 2010, 2011, 2016, 2023 Otto Moerbeek <otto@drijf.net> | 
| 4 | * Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org> | 4 | * Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org> | 
| 5 | * Copyright (c) 2008 Damien Miller <djm@openbsd.org> | 5 | * Copyright (c) 2008 Damien Miller <djm@openbsd.org> | 
| 6 | * Copyright (c) 2000 Poul-Henning Kamp <phk@FreeBSD.org> | 6 | * Copyright (c) 2000 Poul-Henning Kamp <phk@FreeBSD.org> | 
| @@ -23,7 +23,9 @@ | |||
| 23 | * can buy me a beer in return. Poul-Henning Kamp | 23 | * can buy me a beer in return. Poul-Henning Kamp | 
| 24 | */ | 24 | */ | 
| 25 | 25 | ||
| 26 | /* #define MALLOC_STATS */ | 26 | #ifndef MALLOC_SMALL | 
| 27 | #define MALLOC_STATS | ||
| 28 | #endif | ||
| 27 | 29 | ||
| 28 | #include <sys/types.h> | 30 | #include <sys/types.h> | 
| 29 | #include <sys/queue.h> | 31 | #include <sys/queue.h> | 
| @@ -39,8 +41,10 @@ | |||
| 39 | #include <unistd.h> | 41 | #include <unistd.h> | 
| 40 | 42 | ||
| 41 | #ifdef MALLOC_STATS | 43 | #ifdef MALLOC_STATS | 
| 44 | #include <sys/types.h> | ||
| 42 | #include <sys/tree.h> | 45 | #include <sys/tree.h> | 
| 43 | #include <fcntl.h> | 46 | #include <sys/ktrace.h> | 
| 47 | #include <dlfcn.h> | ||
| 44 | #endif | 48 | #endif | 
| 45 | 49 | ||
| 46 | #include "thread_private.h" | 50 | #include "thread_private.h" | 
| @@ -224,11 +228,16 @@ struct malloc_readonly { | |||
| 224 | u_int junk_loc; /* variation in location of junk */ | 228 | u_int junk_loc; /* variation in location of junk */ | 
| 225 | size_t malloc_guard; /* use guard pages after allocations? */ | 229 | size_t malloc_guard; /* use guard pages after allocations? */ | 
| 226 | #ifdef MALLOC_STATS | 230 | #ifdef MALLOC_STATS | 
| 227 | int malloc_stats; /* dump statistics at end */ | 231 | int malloc_stats; /* dump leak report at end */ | 
| 232 | int malloc_verbose; /* dump verbose statistics at end */ | ||
| 233 | #define DO_STATS mopts.malloc_stats | ||
| 234 | #else | ||
| 235 | #define DO_STATS 0 | ||
| 228 | #endif | 236 | #endif | 
| 229 | u_int32_t malloc_canary; /* Matched against ones in pool */ | 237 | u_int32_t malloc_canary; /* Matched against ones in pool */ | 
| 230 | }; | 238 | }; | 
| 231 | 239 | ||
| 240 | |||
| 232 | /* This object is mapped PROT_READ after initialisation to prevent tampering */ | 241 | /* This object is mapped PROT_READ after initialisation to prevent tampering */ | 
| 233 | static union { | 242 | static union { | 
| 234 | struct malloc_readonly mopts; | 243 | struct malloc_readonly mopts; | 
| @@ -243,15 +252,11 @@ static __dead void wrterror(struct dir_info *d, char *msg, ...) | |||
| 243 | __attribute__((__format__ (printf, 2, 3))); | 252 | __attribute__((__format__ (printf, 2, 3))); | 
| 244 | 253 | ||
| 245 | #ifdef MALLOC_STATS | 254 | #ifdef MALLOC_STATS | 
| 246 | void malloc_dump(int, int, struct dir_info *); | 255 | void malloc_dump(void); | 
| 247 | PROTO_NORMAL(malloc_dump); | 256 | PROTO_NORMAL(malloc_dump); | 
| 248 | void malloc_gdump(int); | ||
| 249 | PROTO_NORMAL(malloc_gdump); | ||
| 250 | static void malloc_exit(void); | 257 | static void malloc_exit(void); | 
| 251 | #define CALLER __builtin_return_address(0) | ||
| 252 | #else | ||
| 253 | #define CALLER NULL | ||
| 254 | #endif | 258 | #endif | 
| 259 | #define CALLER (DO_STATS ? __builtin_return_address(0) : NULL) | ||
| 255 | 260 | ||
| 256 | /* low bits of r->p determine size: 0 means >= page size and r->size holding | 261 | /* low bits of r->p determine size: 0 means >= page size and r->size holding | 
| 257 | * real size, otherwise low bits is the bucket + 1 | 262 | * real size, otherwise low bits is the bucket + 1 | 
| @@ -318,9 +323,9 @@ wrterror(struct dir_info *d, char *msg, ...) | |||
| 318 | dprintf(STDERR_FILENO, "\n"); | 323 | dprintf(STDERR_FILENO, "\n"); | 
| 319 | 324 | ||
| 320 | #ifdef MALLOC_STATS | 325 | #ifdef MALLOC_STATS | 
| 321 | if (mopts.malloc_stats) | 326 | if (DO_STATS && mopts.malloc_verbose) | 
| 322 | malloc_gdump(STDERR_FILENO); | 327 | malloc_dump(); | 
| 323 | #endif /* MALLOC_STATS */ | 328 | #endif | 
| 324 | 329 | ||
| 325 | errno = saved_errno; | 330 | errno = saved_errno; | 
| 326 | 331 | ||
| @@ -416,6 +421,14 @@ omalloc_parseopt(char opt) | |||
| 416 | case 'U': | 421 | case 'U': | 
| 417 | mopts.malloc_freeunmap = 1; | 422 | mopts.malloc_freeunmap = 1; | 
| 418 | break; | 423 | break; | 
| 424 | #ifdef MALLOC_STATS | ||
| 425 | case 'v': | ||
| 426 | mopts.malloc_verbose = 0; | ||
| 427 | break; | ||
| 428 | case 'V': | ||
| 429 | mopts.malloc_verbose = 1; | ||
| 430 | break; | ||
| 431 | #endif /* MALLOC_STATS */ | ||
| 419 | case 'x': | 432 | case 'x': | 
| 420 | mopts.malloc_xmalloc = 0; | 433 | mopts.malloc_xmalloc = 0; | 
| 421 | break; | 434 | break; | 
| @@ -486,11 +499,11 @@ omalloc_init(void) | |||
| 486 | } | 499 | } | 
| 487 | 500 | ||
| 488 | #ifdef MALLOC_STATS | 501 | #ifdef MALLOC_STATS | 
| 489 | if (mopts.malloc_stats && (atexit(malloc_exit) == -1)) { | 502 | if (DO_STATS && (atexit(malloc_exit) == -1)) { | 
| 490 | dprintf(STDERR_FILENO, "malloc() warning: atexit(2) failed." | 503 | dprintf(STDERR_FILENO, "malloc() warning: atexit(2) failed." | 
| 491 | " Will not be able to dump stats on exit\n"); | 504 | " Will not be able to dump stats on exit\n"); | 
| 492 | } | 505 | } | 
| 493 | #endif /* MALLOC_STATS */ | 506 | #endif | 
| 494 | 507 | ||
| 495 | while ((mopts.malloc_canary = arc4random()) == 0) | 508 | while ((mopts.malloc_canary = arc4random()) == 0) | 
| 496 | ; | 509 | ; | 
| @@ -596,9 +609,7 @@ insert(struct dir_info *d, void *p, size_t sz, void *f) | |||
| 596 | } | 609 | } | 
| 597 | d->r[index].p = p; | 610 | d->r[index].p = p; | 
| 598 | d->r[index].size = sz; | 611 | d->r[index].size = sz; | 
| 599 | #ifdef MALLOC_STATS | 612 | STATS_SETF(&d->r[index], f); | 
| 600 | d->r[index].f = f; | ||
| 601 | #endif | ||
| 602 | d->regions_free--; | 613 | d->regions_free--; | 
| 603 | return 0; | 614 | return 0; | 
| 604 | } | 615 | } | 
| @@ -1104,12 +1115,10 @@ malloc_bytes(struct dir_info *d, size_t size, void *f) | |||
| 1104 | } | 1115 | } | 
| 1105 | } | 1116 | } | 
| 1106 | found: | 1117 | found: | 
| 1107 | #ifdef MALLOC_STATS | 1118 | if (i == 0 && k == 0 && DO_STATS) { | 
| 1108 | if (i == 0 && k == 0) { | ||
| 1109 | struct region_info *r = find(d, bp->page); | 1119 | struct region_info *r = find(d, bp->page); | 
| 1110 | r->f = f; | 1120 | STATS_SETF(r, f); | 
| 1111 | } | 1121 | } | 
| 1112 | #endif | ||
| 1113 | 1122 | ||
| 1114 | *lp ^= 1 << k; | 1123 | *lp ^= 1 << k; | 
| 1115 | 1124 | ||
| @@ -1193,6 +1202,9 @@ free_bytes(struct dir_info *d, struct region_info *r, void *ptr) | |||
| 1193 | info = (struct chunk_info *)r->size; | 1202 | info = (struct chunk_info *)r->size; | 
| 1194 | chunknum = find_chunknum(d, info, ptr, 0); | 1203 | chunknum = find_chunknum(d, info, ptr, 0); | 
| 1195 | 1204 | ||
| 1205 | if (chunknum == 0) | ||
| 1206 | STATS_SETF(r, NULL); | ||
| 1207 | |||
| 1196 | info->bits[chunknum / MALLOC_BITS] |= 1U << (chunknum % MALLOC_BITS); | 1208 | info->bits[chunknum / MALLOC_BITS] |= 1U << (chunknum % MALLOC_BITS); | 
| 1197 | info->free++; | 1209 | info->free++; | 
| 1198 | 1210 | ||
| @@ -1214,7 +1226,7 @@ free_bytes(struct dir_info *d, struct region_info *r, void *ptr) | |||
| 1214 | unmap(d, info->page, MALLOC_PAGESIZE, 0); | 1226 | unmap(d, info->page, MALLOC_PAGESIZE, 0); | 
| 1215 | 1227 | ||
| 1216 | delete(d, r); | 1228 | delete(d, r); | 
| 1217 | mp = &d->chunk_info_list[info->bucket]; | 1229 | mp = &d->chunk_info_list[info->bucket]; | 
| 1218 | LIST_INSERT_HEAD(mp, info, entries); | 1230 | LIST_INSERT_HEAD(mp, info, entries); | 
| 1219 | } | 1231 | } | 
| 1220 | 1232 | ||
| @@ -1694,9 +1706,11 @@ orealloc(struct dir_info **argpool, void *p, size_t newsz, void *f) | |||
| 1694 | r = findpool(p, *argpool, &pool, &saved_function); | 1706 | r = findpool(p, *argpool, &pool, &saved_function); | 
| 1695 | 1707 | ||
| 1696 | REALSIZE(oldsz, r); | 1708 | REALSIZE(oldsz, r); | 
| 1697 | if (mopts.chunk_canaries && oldsz <= MALLOC_MAXCHUNK) { | 1709 | if (oldsz <= MALLOC_MAXCHUNK) { | 
| 1698 | info = (struct chunk_info *)r->size; | 1710 | if (DO_STATS || mopts.chunk_canaries) { | 
| 1699 | chunknum = find_chunknum(pool, info, p, 0); | 1711 | info = (struct chunk_info *)r->size; | 
| 1712 | chunknum = find_chunknum(pool, info, p, 0); | ||
| 1713 | } | ||
| 1700 | } | 1714 | } | 
| 1701 | 1715 | ||
| 1702 | goldsz = oldsz; | 1716 | goldsz = oldsz; | 
| @@ -1808,7 +1822,8 @@ orealloc(struct dir_info **argpool, void *p, size_t newsz, void *f) | |||
| 1808 | info->bits[info->offset + chunknum] = newsz; | 1822 | info->bits[info->offset + chunknum] = newsz; | 
| 1809 | fill_canary(p, newsz, B2SIZE(info->bucket)); | 1823 | fill_canary(p, newsz, B2SIZE(info->bucket)); | 
| 1810 | } | 1824 | } | 
| 1811 | STATS_SETF(r, f); | 1825 | if (DO_STATS && chunknum == 0) | 
| 1826 | STATS_SETF(r, f); | ||
| 1812 | ret = p; | 1827 | ret = p; | 
| 1813 | } else if (newsz != oldsz || forced) { | 1828 | } else if (newsz != oldsz || forced) { | 
| 1814 | /* create new allocation */ | 1829 | /* create new allocation */ | 
| @@ -1825,7 +1840,8 @@ orealloc(struct dir_info **argpool, void *p, size_t newsz, void *f) | |||
| 1825 | /* oldsz == newsz */ | 1840 | /* oldsz == newsz */ | 
| 1826 | if (newsz != 0) | 1841 | if (newsz != 0) | 
| 1827 | wrterror(pool, "realloc internal inconsistency"); | 1842 | wrterror(pool, "realloc internal inconsistency"); | 
| 1828 | STATS_SETF(r, f); | 1843 | if (DO_STATS && chunknum == 0) | 
| 1844 | STATS_SETF(r, f); | ||
| 1829 | ret = p; | 1845 | ret = p; | 
| 1830 | } | 1846 | } | 
| 1831 | done: | 1847 | done: | 
| @@ -2225,6 +2241,35 @@ aligned_alloc(size_t alignment, size_t size) | |||
| 2225 | 2241 | ||
| 2226 | #ifdef MALLOC_STATS | 2242 | #ifdef MALLOC_STATS | 
| 2227 | 2243 | ||
| 2244 | static void | ||
| 2245 | ulog(const char *format, ...) | ||
| 2246 | { | ||
| 2247 | va_list ap; | ||
| 2248 | static char* buf; | ||
| 2249 | static size_t filled; | ||
| 2250 | int len; | ||
| 2251 | |||
| 2252 | if (buf == NULL) | ||
| 2253 | buf = MMAP(KTR_USER_MAXLEN, 0); | ||
| 2254 | if (buf == MAP_FAILED) | ||
| 2255 | return; | ||
| 2256 | |||
| 2257 | va_start(ap, format); | ||
| 2258 | len = vsnprintf(buf + filled, KTR_USER_MAXLEN - filled, format, ap); | ||
| 2259 | va_end(ap); | ||
| 2260 | if (len < 0) | ||
| 2261 | return; | ||
| 2262 | if (len > KTR_USER_MAXLEN - filled) | ||
| 2263 | len = KTR_USER_MAXLEN - filled; | ||
| 2264 | filled += len; | ||
| 2265 | if (filled > 0) { | ||
| 2266 | if (filled == KTR_USER_MAXLEN || buf[filled - 1] == '\n') { | ||
| 2267 | utrace("malloc", buf, filled); | ||
| 2268 | filled = 0; | ||
| 2269 | } | ||
| 2270 | } | ||
| 2271 | } | ||
| 2272 | |||
| 2228 | struct malloc_leak { | 2273 | struct malloc_leak { | 
| 2229 | void *f; | 2274 | void *f; | 
| 2230 | size_t total_size; | 2275 | size_t total_size; | 
| @@ -2242,12 +2287,12 @@ leakcmp(const struct leaknode *e1, const struct leaknode *e2) | |||
| 2242 | return e1->d.f < e2->d.f ? -1 : e1->d.f > e2->d.f; | 2287 | return e1->d.f < e2->d.f ? -1 : e1->d.f > e2->d.f; | 
| 2243 | } | 2288 | } | 
| 2244 | 2289 | ||
| 2245 | static RBT_HEAD(leaktree, leaknode) leakhead; | 2290 | RBT_HEAD(leaktree, leaknode); | 
| 2246 | RBT_PROTOTYPE(leaktree, leaknode, entry, leakcmp); | 2291 | RBT_PROTOTYPE(leaktree, leaknode, entry, leakcmp); | 
| 2247 | RBT_GENERATE(leaktree, leaknode, entry, leakcmp); | 2292 | RBT_GENERATE(leaktree, leaknode, entry, leakcmp); | 
| 2248 | 2293 | ||
| 2249 | static void | 2294 | static void | 
| 2250 | putleakinfo(void *f, size_t sz, int cnt) | 2295 | putleakinfo(struct leaktree *leaks, void *f, size_t sz, int cnt) | 
| 2251 | { | 2296 | { | 
| 2252 | struct leaknode key, *p; | 2297 | struct leaknode key, *p; | 
| 2253 | static struct leaknode *page; | 2298 | static struct leaknode *page; | 
| @@ -2257,7 +2302,7 @@ putleakinfo(void *f, size_t sz, int cnt) | |||
| 2257 | return; | 2302 | return; | 
| 2258 | 2303 | ||
| 2259 | key.d.f = f; | 2304 | key.d.f = f; | 
| 2260 | p = RBT_FIND(leaktree, &leakhead, &key); | 2305 | p = RBT_FIND(leaktree, leaks, &key); | 
| 2261 | if (p == NULL) { | 2306 | if (p == NULL) { | 
| 2262 | if (page == NULL || | 2307 | if (page == NULL || | 
| 2263 | used >= MALLOC_PAGESIZE / sizeof(struct leaknode)) { | 2308 | used >= MALLOC_PAGESIZE / sizeof(struct leaknode)) { | 
| @@ -2270,72 +2315,75 @@ putleakinfo(void *f, size_t sz, int cnt) | |||
| 2270 | p->d.f = f; | 2315 | p->d.f = f; | 
| 2271 | p->d.total_size = sz * cnt; | 2316 | p->d.total_size = sz * cnt; | 
| 2272 | p->d.count = cnt; | 2317 | p->d.count = cnt; | 
| 2273 | RBT_INSERT(leaktree, &leakhead, p); | 2318 | RBT_INSERT(leaktree, leaks, p); | 
| 2274 | } else { | 2319 | } else { | 
| 2275 | p->d.total_size += sz * cnt; | 2320 | p->d.total_size += sz * cnt; | 
| 2276 | p->d.count += cnt; | 2321 | p->d.count += cnt; | 
| 2277 | } | 2322 | } | 
| 2278 | } | 2323 | } | 
| 2279 | 2324 | ||
| 2280 | static struct malloc_leak *malloc_leaks; | ||
| 2281 | |||
| 2282 | static void | 2325 | static void | 
| 2283 | dump_leaks(int fd) | 2326 | dump_leaks(struct leaktree *leaks) | 
| 2284 | { | 2327 | { | 
| 2285 | struct leaknode *p; | 2328 | struct leaknode *p; | 
| 2286 | unsigned int i = 0; | 2329 | |
| 2287 | 2330 | ulog("Leak report:\n"); | |
| 2288 | dprintf(fd, "Leak report\n"); | 2331 | ulog(" f sum # avg\n"); | 
| 2289 | dprintf(fd, " f sum # avg\n"); | 2332 | |
| 2290 | /* XXX only one page of summary */ | 2333 | RBT_FOREACH(p, leaktree, leaks) { | 
| 2291 | if (malloc_leaks == NULL) | 2334 | Dl_info info; | 
| 2292 | malloc_leaks = MMAP(MALLOC_PAGESIZE, 0); | 2335 | const char *caller = p->d.f; | 
| 2293 | if (malloc_leaks != MAP_FAILED) | 2336 | const char *object = "."; | 
| 2294 | memset(malloc_leaks, 0, MALLOC_PAGESIZE); | 2337 | |
| 2295 | RBT_FOREACH(p, leaktree, &leakhead) { | 2338 | if (caller != NULL) { | 
| 2296 | dprintf(fd, "%18p %7zu %6u %6zu\n", p->d.f, | 2339 | if (dladdr(p->d.f, &info) != 0) { | 
| 2297 | p->d.total_size, p->d.count, p->d.total_size / p->d.count); | 2340 | caller -= (uintptr_t)info.dli_fbase; | 
| 2298 | if (malloc_leaks == MAP_FAILED || | 2341 | object = info.dli_fname; | 
| 2299 | i >= MALLOC_PAGESIZE / sizeof(struct malloc_leak)) | 2342 | } | 
| 2300 | continue; | 2343 | } | 
| 2301 | malloc_leaks[i].f = p->d.f; | 2344 | ulog("%18p %7zu %6u %6zu addr2line -e %s %p\n", | 
| 2302 | malloc_leaks[i].total_size = p->d.total_size; | 2345 | p->d.f, p->d.total_size, p->d.count, | 
| 2303 | malloc_leaks[i].count = p->d.count; | 2346 | p->d.total_size / p->d.count, | 
| 2304 | i++; | 2347 | object, caller); | 
| 2305 | } | 2348 | } | 
| 2306 | } | 2349 | } | 
| 2307 | 2350 | ||
| 2308 | static void | 2351 | static void | 
| 2309 | dump_chunk(int fd, struct chunk_info *p, void *f, int fromfreelist) | 2352 | dump_chunk(struct leaktree* leaks, struct chunk_info *p, void *f, | 
| 2353 | int fromfreelist) | ||
| 2310 | { | 2354 | { | 
| 2311 | while (p != NULL) { | 2355 | while (p != NULL) { | 
| 2312 | dprintf(fd, "chunk %18p %18p %4zu %d/%d\n", | 2356 | if (mopts.malloc_verbose) | 
| 2313 | p->page, ((p->bits[0] & 1) ? NULL : f), | 2357 | ulog("chunk %18p %18p %4zu %d/%d\n", | 
| 2314 | B2SIZE(p->bucket), p->free, p->total); | 2358 | p->page, ((p->bits[0] & 1) ? NULL : f), | 
| 2359 | B2SIZE(p->bucket), p->free, p->total); | ||
| 2315 | if (!fromfreelist) { | 2360 | if (!fromfreelist) { | 
| 2316 | size_t sz = B2SIZE(p->bucket); | 2361 | size_t sz = B2SIZE(p->bucket); | 
| 2317 | if (p->bits[0] & 1) | 2362 | if (p->bits[0] & 1) | 
| 2318 | putleakinfo(NULL, sz, p->total - p->free); | 2363 | putleakinfo(leaks, NULL, sz, p->total - | 
| 2364 | p->free); | ||
| 2319 | else { | 2365 | else { | 
| 2320 | putleakinfo(f, sz, 1); | 2366 | putleakinfo(leaks, f, sz, 1); | 
| 2321 | putleakinfo(NULL, sz, | 2367 | putleakinfo(leaks, NULL, sz, | 
| 2322 | p->total - p->free - 1); | 2368 | p->total - p->free - 1); | 
| 2323 | } | 2369 | } | 
| 2324 | break; | 2370 | break; | 
| 2325 | } | 2371 | } | 
| 2326 | p = LIST_NEXT(p, entries); | 2372 | p = LIST_NEXT(p, entries); | 
| 2327 | if (p != NULL) | 2373 | if (mopts.malloc_verbose && p != NULL) | 
| 2328 | dprintf(fd, " "); | 2374 | ulog(" ->"); | 
| 2329 | } | 2375 | } | 
| 2330 | } | 2376 | } | 
| 2331 | 2377 | ||
| 2332 | static void | 2378 | static void | 
| 2333 | dump_free_chunk_info(int fd, struct dir_info *d) | 2379 | dump_free_chunk_info(struct dir_info *d, struct leaktree *leaks) | 
| 2334 | { | 2380 | { | 
| 2335 | int i, j, count; | 2381 | int i, j, count; | 
| 2336 | struct chunk_info *p; | 2382 | struct chunk_info *p; | 
| 2337 | 2383 | ||
| 2338 | dprintf(fd, "Free chunk structs:\n"); | 2384 | ulog("Free chunk structs:\n"); | 
| 2385 | ulog("Bkt) #CI page" | ||
| 2386 | " f size free/n\n"); | ||
| 2339 | for (i = 0; i <= BUCKETS; i++) { | 2387 | for (i = 0; i <= BUCKETS; i++) { | 
| 2340 | count = 0; | 2388 | count = 0; | 
| 2341 | LIST_FOREACH(p, &d->chunk_info_list[i], entries) | 2389 | LIST_FOREACH(p, &d->chunk_info_list[i], entries) | 
| @@ -2345,99 +2393,100 @@ dump_free_chunk_info(int fd, struct dir_info *d) | |||
| 2345 | if (p == NULL && count == 0) | 2393 | if (p == NULL && count == 0) | 
| 2346 | continue; | 2394 | continue; | 
| 2347 | if (j == 0) | 2395 | if (j == 0) | 
| 2348 | dprintf(fd, "%3d) %3d ", i, count); | 2396 | ulog("%3d) %3d ", i, count); | 
| 2349 | else | 2397 | else | 
| 2350 | dprintf(fd, " "); | 2398 | ulog(" "); | 
| 2351 | if (p != NULL) | 2399 | if (p != NULL) | 
| 2352 | dump_chunk(fd, p, NULL, 1); | 2400 | dump_chunk(leaks, p, NULL, 1); | 
| 2353 | else | 2401 | else | 
| 2354 | dprintf(fd, "\n"); | 2402 | ulog(".\n"); | 
| 2355 | } | 2403 | } | 
| 2356 | } | 2404 | } | 
| 2357 | 2405 | ||
| 2358 | } | 2406 | } | 
| 2359 | 2407 | ||
| 2360 | static void | 2408 | static void | 
| 2361 | dump_free_page_info(int fd, struct dir_info *d) | 2409 | dump_free_page_info(struct dir_info *d) | 
| 2362 | { | 2410 | { | 
| 2363 | struct smallcache *cache; | 2411 | struct smallcache *cache; | 
| 2364 | size_t i, total = 0; | 2412 | size_t i, total = 0; | 
| 2365 | 2413 | ||
| 2366 | dprintf(fd, "Cached in small cache:\n"); | 2414 | ulog("Cached in small cache:\n"); | 
| 2367 | for (i = 0; i < MAX_SMALLCACHEABLE_SIZE; i++) { | 2415 | for (i = 0; i < MAX_SMALLCACHEABLE_SIZE; i++) { | 
| 2368 | cache = &d->smallcache[i]; | 2416 | cache = &d->smallcache[i]; | 
| 2369 | if (cache->length != 0) | 2417 | if (cache->length != 0) | 
| 2370 | dprintf(fd, "%zu(%u): %u = %zu\n", i + 1, cache->max, | 2418 | ulog("%zu(%u): %u = %zu\n", i + 1, cache->max, | 
| 2371 | cache->length, cache->length * (i + 1)); | 2419 | cache->length, cache->length * (i + 1)); | 
| 2372 | total += cache->length * (i + 1); | 2420 | total += cache->length * (i + 1); | 
| 2373 | } | 2421 | } | 
| 2374 | 2422 | ||
| 2375 | dprintf(fd, "Cached in big cache: %zu/%zu\n", d->bigcache_used, | 2423 | ulog("Cached in big cache: %zu/%zu\n", d->bigcache_used, | 
| 2376 | d->bigcache_size); | 2424 | d->bigcache_size); | 
| 2377 | for (i = 0; i < d->bigcache_size; i++) { | 2425 | for (i = 0; i < d->bigcache_size; i++) { | 
| 2378 | if (d->bigcache[i].psize != 0) | 2426 | if (d->bigcache[i].psize != 0) | 
| 2379 | dprintf(fd, "%zu: %zu\n", i, d->bigcache[i].psize); | 2427 | ulog("%zu: %zu\n", i, d->bigcache[i].psize); | 
| 2380 | total += d->bigcache[i].psize; | 2428 | total += d->bigcache[i].psize; | 
| 2381 | } | 2429 | } | 
| 2382 | dprintf(fd, "Free pages cached: %zu\n", total); | 2430 | ulog("Free pages cached: %zu\n", total); | 
| 2383 | } | 2431 | } | 
| 2384 | 2432 | ||
| 2385 | static void | 2433 | static void | 
| 2386 | malloc_dump1(int fd, int poolno, struct dir_info *d) | 2434 | malloc_dump1(int poolno, struct dir_info *d, struct leaktree *leaks) | 
| 2387 | { | 2435 | { | 
| 2388 | size_t i, realsize; | 2436 | size_t i, realsize; | 
| 2389 | 2437 | ||
| 2390 | dprintf(fd, "Malloc dir of %s pool %d at %p\n", __progname, poolno, d); | 2438 | if (mopts.malloc_verbose) { | 
| 2391 | if (d == NULL) | 2439 | ulog("Malloc dir of %s pool %d at %p\n", __progname, poolno, d); | 
| 2392 | return; | 2440 | ulog("MT=%d J=%d Fl=%x\n", d->malloc_mt, d->malloc_junk, | 
| 2393 | dprintf(fd, "MT=%d J=%d Fl=%x\n", d->malloc_mt, d->malloc_junk, d->mmap_flag); | 2441 | d->mmap_flag); | 
| 2394 | dprintf(fd, "Region slots free %zu/%zu\n", | 2442 | ulog("Region slots free %zu/%zu\n", | 
| 2395 | d->regions_free, d->regions_total); | 2443 | d->regions_free, d->regions_total); | 
| 2396 | dprintf(fd, "Finds %zu/%zu\n", d->finds, d->find_collisions); | 2444 | ulog("Finds %zu/%zu\n", d->finds, d->find_collisions); | 
| 2397 | dprintf(fd, "Inserts %zu/%zu\n", d->inserts, d->insert_collisions); | 2445 | ulog("Inserts %zu/%zu\n", d->inserts, d->insert_collisions); | 
| 2398 | dprintf(fd, "Deletes %zu/%zu\n", d->deletes, d->delete_moves); | 2446 | ulog("Deletes %zu/%zu\n", d->deletes, d->delete_moves); | 
| 2399 | dprintf(fd, "Cheap reallocs %zu/%zu\n", | 2447 | ulog("Cheap reallocs %zu/%zu\n", | 
| 2400 | d->cheap_reallocs, d->cheap_realloc_tries); | 2448 | d->cheap_reallocs, d->cheap_realloc_tries); | 
| 2401 | dprintf(fd, "Other pool searches %zu/%zu\n", | 2449 | ulog("Other pool searches %zu/%zu\n", | 
| 2402 | d->other_pool, d->pool_searches); | 2450 | d->other_pool, d->pool_searches); | 
| 2403 | dprintf(fd, "In use %zu\n", d->malloc_used); | 2451 | ulog("In use %zu\n", d->malloc_used); | 
| 2404 | dprintf(fd, "Guarded %zu\n", d->malloc_guarded); | 2452 | ulog("Guarded %zu\n", d->malloc_guarded); | 
| 2405 | dump_free_chunk_info(fd, d); | 2453 | dump_free_chunk_info(d, leaks); | 
| 2406 | dump_free_page_info(fd, d); | 2454 | dump_free_page_info(d); | 
| 2407 | dprintf(fd, | 2455 | ulog("Hash table:\n"); | 
| 2408 | "slot) hash d type page f " | 2456 | ulog("slot) hash d type page " | 
| 2409 | "size [free/n]\n"); | 2457 | "f size [free/n]\n"); | 
| 2458 | } | ||
| 2410 | for (i = 0; i < d->regions_total; i++) { | 2459 | for (i = 0; i < d->regions_total; i++) { | 
| 2411 | if (d->r[i].p != NULL) { | 2460 | if (d->r[i].p != NULL) { | 
| 2412 | size_t h = hash(d->r[i].p) & | 2461 | size_t h = hash(d->r[i].p) & | 
| 2413 | (d->regions_total - 1); | 2462 | (d->regions_total - 1); | 
| 2414 | dprintf(fd, "%4zx) #%4zx %zd ", | 2463 | if (mopts.malloc_verbose) | 
| 2415 | i, h, h - i); | 2464 | ulog("%4zx) #%4zx %zd ", | 
| 2465 | i, h, h - i); | ||
| 2416 | REALSIZE(realsize, &d->r[i]); | 2466 | REALSIZE(realsize, &d->r[i]); | 
| 2417 | if (realsize > MALLOC_MAXCHUNK) { | 2467 | if (realsize > MALLOC_MAXCHUNK) { | 
| 2418 | putleakinfo(d->r[i].f, realsize, 1); | 2468 | putleakinfo(leaks, d->r[i].f, realsize, 1); | 
| 2419 | dprintf(fd, | 2469 | if (mopts.malloc_verbose) | 
| 2420 | "pages %18p %18p %zu\n", d->r[i].p, | 2470 | ulog("pages %18p %18p %zu\n", d->r[i].p, | 
| 2421 | d->r[i].f, realsize); | 2471 | d->r[i].f, realsize); | 
| 2422 | } else | 2472 | } else | 
| 2423 | dump_chunk(fd, | 2473 | dump_chunk(leaks, | 
| 2424 | (struct chunk_info *)d->r[i].size, | 2474 | (struct chunk_info *)d->r[i].size, | 
| 2425 | d->r[i].f, 0); | 2475 | d->r[i].f, 0); | 
| 2426 | } | 2476 | } | 
| 2427 | } | 2477 | } | 
| 2428 | dump_leaks(fd); | 2478 | if (mopts.malloc_verbose) | 
| 2429 | dprintf(fd, "\n"); | 2479 | ulog("\n"); | 
| 2430 | } | 2480 | } | 
| 2431 | 2481 | ||
| 2432 | void | 2482 | static void | 
| 2433 | malloc_dump(int fd, int poolno, struct dir_info *pool) | 2483 | malloc_dump0(int poolno, struct dir_info *pool, struct leaktree *leaks) | 
| 2434 | { | 2484 | { | 
| 2435 | int i; | 2485 | int i; | 
| 2436 | void *p; | 2486 | void *p; | 
| 2437 | struct region_info *r; | 2487 | struct region_info *r; | 
| 2438 | int saved_errno = errno; | ||
| 2439 | 2488 | ||
| 2440 | if (pool == NULL) | 2489 | if (pool == NULL || pool->r == NULL) | 
| 2441 | return; | 2490 | return; | 
| 2442 | for (i = 0; i < MALLOC_DELAYED_CHUNK_MASK + 1; i++) { | 2491 | for (i = 0; i < MALLOC_DELAYED_CHUNK_MASK + 1; i++) { | 
| 2443 | p = pool->delayed_chunks[i]; | 2492 | p = pool->delayed_chunks[i]; | 
| @@ -2449,52 +2498,44 @@ malloc_dump(int fd, int poolno, struct dir_info *pool) | |||
| 2449 | free_bytes(pool, r, p); | 2498 | free_bytes(pool, r, p); | 
| 2450 | pool->delayed_chunks[i] = NULL; | 2499 | pool->delayed_chunks[i] = NULL; | 
| 2451 | } | 2500 | } | 
| 2452 | /* XXX leak when run multiple times */ | 2501 | malloc_dump1(poolno, pool, leaks); | 
| 2453 | RBT_INIT(leaktree, &leakhead); | ||
| 2454 | malloc_dump1(fd, poolno, pool); | ||
| 2455 | errno = saved_errno; | ||
| 2456 | } | 2502 | } | 
| 2457 | DEF_WEAK(malloc_dump); | ||
| 2458 | 2503 | ||
| 2459 | void | 2504 | void | 
| 2460 | malloc_gdump(int fd) | 2505 | malloc_dump(void) | 
| 2461 | { | 2506 | { | 
| 2462 | int i; | 2507 | int i; | 
| 2463 | int saved_errno = errno; | 2508 | int saved_errno = errno; | 
| 2464 | 2509 | ||
| 2510 | /* XXX leak when run multiple times */ | ||
| 2511 | struct leaktree leaks = RBT_INITIALIZER(&leaks); | ||
| 2512 | |||
| 2465 | for (i = 0; i < mopts.malloc_mutexes; i++) | 2513 | for (i = 0; i < mopts.malloc_mutexes; i++) | 
| 2466 | malloc_dump(fd, i, mopts.malloc_pool[i]); | 2514 | malloc_dump0(i, mopts.malloc_pool[i], &leaks); | 
| 2467 | 2515 | ||
| 2516 | dump_leaks(&leaks); | ||
| 2517 | ulog("\n"); | ||
| 2468 | errno = saved_errno; | 2518 | errno = saved_errno; | 
| 2469 | } | 2519 | } | 
| 2470 | DEF_WEAK(malloc_gdump); | 2520 | DEF_WEAK(malloc_dump); | 
| 2471 | 2521 | ||
| 2472 | static void | 2522 | static void | 
| 2473 | malloc_exit(void) | 2523 | malloc_exit(void) | 
| 2474 | { | 2524 | { | 
| 2475 | int save_errno = errno, fd; | 2525 | int save_errno = errno; | 
| 2476 | unsigned i; | 2526 | |
| 2477 | 2527 | ulog("******** Start dump %s *******\n", __progname); | |
| 2478 | fd = open("malloc.out", O_RDWR|O_APPEND); | 2528 | ulog("M=%u I=%d F=%d U=%d J=%d R=%d X=%d C=%d cache=%u " | 
| 2479 | if (fd != -1) { | 2529 | "G=%zu\n", | 
| 2480 | dprintf(fd, "******** Start dump %s *******\n", __progname); | 2530 | mopts.malloc_mutexes, | 
| 2481 | dprintf(fd, | 2531 | mopts.internal_funcs, mopts.malloc_freecheck, | 
| 2482 | "M=%u I=%d F=%d U=%d J=%d R=%d X=%d C=%d cache=%u " | 2532 | mopts.malloc_freeunmap, mopts.def_malloc_junk, | 
| 2483 | "G=%zu\n", | 2533 | mopts.malloc_realloc, mopts.malloc_xmalloc, | 
| 2484 | mopts.malloc_mutexes, | 2534 | mopts.chunk_canaries, mopts.def_maxcache, | 
| 2485 | mopts.internal_funcs, mopts.malloc_freecheck, | 2535 | mopts.malloc_guard); | 
| 2486 | mopts.malloc_freeunmap, mopts.def_malloc_junk, | 2536 | |
| 2487 | mopts.malloc_realloc, mopts.malloc_xmalloc, | 2537 | malloc_dump(); | 
| 2488 | mopts.chunk_canaries, mopts.def_maxcache, | 2538 | ulog("******** End dump %s *******\n", __progname); | 
| 2489 | mopts.malloc_guard); | ||
| 2490 | |||
| 2491 | for (i = 0; i < mopts.malloc_mutexes; i++) | ||
| 2492 | malloc_dump(fd, i, mopts.malloc_pool[i]); | ||
| 2493 | dprintf(fd, "******** End dump %s *******\n", __progname); | ||
| 2494 | close(fd); | ||
| 2495 | } else | ||
| 2496 | dprintf(STDERR_FILENO, | ||
| 2497 | "malloc() warning: Couldn't dump stats\n"); | ||
| 2498 | errno = save_errno; | 2539 | errno = save_errno; | 
| 2499 | } | 2540 | } | 
| 2500 | 2541 | ||
