diff options
Diffstat (limited to 'src/lib/libc/stdlib/malloc.c')
| -rw-r--r-- | src/lib/libc/stdlib/malloc.c | 253 |
1 files changed, 174 insertions, 79 deletions
diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c index 9da180d814..7c226657df 100644 --- a/src/lib/libc/stdlib/malloc.c +++ b/src/lib/libc/stdlib/malloc.c | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /* $OpenBSD: malloc.c,v 1.293 2023/11/04 11:02:35 otto Exp $ */ | 1 | /* $OpenBSD: malloc.c,v 1.294 2023/12/04 07:01:45 otto Exp $ */ |
| 2 | /* | 2 | /* |
| 3 | * Copyright (c) 2008, 2010, 2011, 2016, 2023 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> |
| @@ -139,6 +139,16 @@ struct bigcache { | |||
| 139 | size_t psize; | 139 | size_t psize; |
| 140 | }; | 140 | }; |
| 141 | 141 | ||
| 142 | #ifdef MALLOC_STATS | ||
| 143 | #define NUM_FRAMES 4 | ||
| 144 | struct btnode { | ||
| 145 | RBT_ENTRY(btnode) entry; | ||
| 146 | void *caller[NUM_FRAMES]; | ||
| 147 | }; | ||
| 148 | RBT_HEAD(btshead, btnode); | ||
| 149 | RBT_PROTOTYPE(btshead, btnode, entry, btcmp); | ||
| 150 | #endif /* MALLOC_STATS */ | ||
| 151 | |||
| 142 | struct dir_info { | 152 | struct dir_info { |
| 143 | u_int32_t canary1; | 153 | u_int32_t canary1; |
| 144 | int active; /* status of malloc */ | 154 | int active; /* status of malloc */ |
| @@ -175,6 +185,9 @@ struct dir_info { | |||
| 175 | size_t cheap_reallocs; | 185 | size_t cheap_reallocs; |
| 176 | size_t malloc_used; /* bytes allocated */ | 186 | size_t malloc_used; /* bytes allocated */ |
| 177 | size_t malloc_guarded; /* bytes used for guards */ | 187 | size_t malloc_guarded; /* bytes used for guards */ |
| 188 | struct btshead btraces; /* backtraces seen */ | ||
| 189 | struct btnode *btnodes; /* store of backtrace nodes */ | ||
| 190 | size_t btnodesused; | ||
| 178 | #define STATS_ADD(x,y) ((x) += (y)) | 191 | #define STATS_ADD(x,y) ((x) += (y)) |
| 179 | #define STATS_SUB(x,y) ((x) -= (y)) | 192 | #define STATS_SUB(x,y) ((x) -= (y)) |
| 180 | #define STATS_INC(x) ((x)++) | 193 | #define STATS_INC(x) ((x)++) |
| @@ -261,38 +274,52 @@ void malloc_dump(void); | |||
| 261 | PROTO_NORMAL(malloc_dump); | 274 | PROTO_NORMAL(malloc_dump); |
| 262 | static void malloc_exit(void); | 275 | static void malloc_exit(void); |
| 263 | static void print_chunk_details(struct dir_info *, void *, size_t, size_t); | 276 | static void print_chunk_details(struct dir_info *, void *, size_t, size_t); |
| 264 | #endif | 277 | static void* store_caller(struct dir_info *, struct btnode *); |
| 265 | 278 | ||
| 279 | /* below are the arches for which deeper caller info has been tested */ | ||
| 266 | #if defined(__aarch64__) || \ | 280 | #if defined(__aarch64__) || \ |
| 267 | defined(__amd64__) || \ | 281 | defined(__amd64__) || \ |
| 268 | defined(__arm__) | 282 | defined(__arm__) || \ |
| 269 | static inline void* caller(void) | 283 | defined(__i386__) || \ |
| 270 | { | 284 | defined(__powerpc__) |
| 271 | void *p; | 285 | __attribute__((always_inline)) |
| 272 | 286 | static inline void* | |
| 273 | switch (DO_STATS) { | 287 | caller(struct dir_info *d) |
| 274 | case 0: | 288 | { |
| 275 | default: | 289 | struct btnode p; |
| 290 | int level = DO_STATS; | ||
| 291 | if (level == 0) | ||
| 276 | return NULL; | 292 | return NULL; |
| 277 | case 1: | 293 | |
| 278 | p = __builtin_return_address(0); | 294 | memset(&p.caller, 0, sizeof(p.caller)); |
| 279 | break; | 295 | if (level >= 1) |
| 280 | case 2: | 296 | p.caller[0] = __builtin_extract_return_addr( |
| 281 | p = __builtin_return_address(1); | 297 | __builtin_return_address(0)); |
| 282 | break; | 298 | if (p.caller[0] != NULL && level >= 2) |
| 283 | case 3: | 299 | p.caller[1] = __builtin_extract_return_addr( |
| 284 | p = __builtin_return_address(2); | 300 | __builtin_return_address(1)); |
| 285 | break; | 301 | if (p.caller[1] != NULL && level >= 3) |
| 286 | } | 302 | p.caller[2] = __builtin_extract_return_addr( |
| 287 | return __builtin_extract_return_addr(p); | 303 | __builtin_return_address(2)); |
| 304 | if (p.caller[2] != NULL && level >= 4) | ||
| 305 | p.caller[3] = __builtin_extract_return_addr( | ||
| 306 | __builtin_return_address(3)); | ||
| 307 | return store_caller(d, &p); | ||
| 288 | } | 308 | } |
| 289 | #else | 309 | #else |
| 290 | static inline void* caller(void) | 310 | __attribute__((always_inline)) |
| 311 | static inline void* caller(struct dir_info *d) | ||
| 291 | { | 312 | { |
| 292 | return DO_STATS == 0 ? NULL : | 313 | struct btnode p; |
| 293 | __builtin_extract_return_addr(__builtin_return_address(0)); | 314 | |
| 315 | if (DO_STATS == 0) | ||
| 316 | return NULL; | ||
| 317 | memset(&p.caller, 0, sizeof(p.caller)); | ||
| 318 | p.caller[0] = __builtin_extract_return_addr(__builtin_return_address(0)); | ||
| 319 | return store_caller(d, &p); | ||
| 294 | } | 320 | } |
| 295 | #endif | 321 | #endif |
| 322 | #endif /* MALLOC_STATS */ | ||
| 296 | 323 | ||
| 297 | /* low bits of r->p determine size: 0 means >= page size and r->size holding | 324 | /* low bits of r->p determine size: 0 means >= page size and r->size holding |
| 298 | * real size, otherwise low bits is the bucket + 1 | 325 | * real size, otherwise low bits is the bucket + 1 |
| @@ -411,6 +438,9 @@ omalloc_parseopt(char opt) | |||
| 411 | case '3': | 438 | case '3': |
| 412 | mopts.malloc_stats = 3; | 439 | mopts.malloc_stats = 3; |
| 413 | break; | 440 | break; |
| 441 | case '4': | ||
| 442 | mopts.malloc_stats = 4; | ||
| 443 | break; | ||
| 414 | #endif /* MALLOC_STATS */ | 444 | #endif /* MALLOC_STATS */ |
| 415 | case 'f': | 445 | case 'f': |
| 416 | mopts.malloc_freecheck = 0; | 446 | mopts.malloc_freecheck = 0; |
| @@ -525,7 +555,7 @@ omalloc_init(void) | |||
| 525 | 555 | ||
| 526 | #ifdef MALLOC_STATS | 556 | #ifdef MALLOC_STATS |
| 527 | if (DO_STATS && (atexit(malloc_exit) == -1)) { | 557 | if (DO_STATS && (atexit(malloc_exit) == -1)) { |
| 528 | dprintf(STDERR_FILENO, "malloc() warning: atexit(2) failed." | 558 | dprintf(STDERR_FILENO, "malloc() warning: atexit(3) failed." |
| 529 | " Will not be able to dump stats on exit\n"); | 559 | " Will not be able to dump stats on exit\n"); |
| 530 | } | 560 | } |
| 531 | #endif | 561 | #endif |
| @@ -555,6 +585,9 @@ omalloc_poolinit(struct dir_info *d, int mmap_flag) | |||
| 555 | } | 585 | } |
| 556 | d->mmap_flag = mmap_flag; | 586 | d->mmap_flag = mmap_flag; |
| 557 | d->malloc_junk = mopts.def_malloc_junk; | 587 | d->malloc_junk = mopts.def_malloc_junk; |
| 588 | #ifdef MALLOC_STATS | ||
| 589 | RBT_INIT(btshead, &d->btraces); | ||
| 590 | #endif | ||
| 558 | d->canary1 = mopts.malloc_canary ^ (u_int32_t)(uintptr_t)d; | 591 | d->canary1 = mopts.malloc_canary ^ (u_int32_t)(uintptr_t)d; |
| 559 | d->canary2 = ~d->canary1; | 592 | d->canary2 = ~d->canary1; |
| 560 | } | 593 | } |
| @@ -1091,15 +1124,13 @@ bin_of(unsigned int size) | |||
| 1091 | const unsigned int linear = 6; | 1124 | const unsigned int linear = 6; |
| 1092 | const unsigned int subbin = 2; | 1125 | const unsigned int subbin = 2; |
| 1093 | 1126 | ||
| 1094 | unsigned int mask, range, rounded, sub_index, rounded_size; | 1127 | unsigned int mask, rounded, rounded_size; |
| 1095 | unsigned int n_bits, shift; | 1128 | unsigned int n_bits, shift; |
| 1096 | 1129 | ||
| 1097 | n_bits = lb(size | (1U << linear)); | 1130 | n_bits = lb(size | (1U << linear)); |
| 1098 | shift = n_bits - subbin; | 1131 | shift = n_bits - subbin; |
| 1099 | mask = (1ULL << shift) - 1; | 1132 | mask = (1ULL << shift) - 1; |
| 1100 | rounded = size + mask; /* XXX: overflow. */ | 1133 | rounded = size + mask; /* XXX: overflow. */ |
| 1101 | sub_index = rounded >> shift; | ||
| 1102 | range = n_bits - linear; | ||
| 1103 | 1134 | ||
| 1104 | rounded_size = rounded & ~mask; | 1135 | rounded_size = rounded & ~mask; |
| 1105 | return rounded_size; | 1136 | return rounded_size; |
| @@ -1135,6 +1166,7 @@ static void * | |||
| 1135 | malloc_bytes(struct dir_info *d, size_t size) | 1166 | malloc_bytes(struct dir_info *d, size_t size) |
| 1136 | { | 1167 | { |
| 1137 | u_int i, k, r, bucket, listnum; | 1168 | u_int i, k, r, bucket, listnum; |
| 1169 | int j; | ||
| 1138 | u_short *lp; | 1170 | u_short *lp; |
| 1139 | struct chunk_info *bp; | 1171 | struct chunk_info *bp; |
| 1140 | void *p; | 1172 | void *p; |
| @@ -1155,7 +1187,7 @@ malloc_bytes(struct dir_info *d, size_t size) | |||
| 1155 | return NULL; | 1187 | return NULL; |
| 1156 | } | 1188 | } |
| 1157 | 1189 | ||
| 1158 | if (bp->canary != (u_short)d->canary1) | 1190 | if (bp->canary != (u_short)d->canary1 || bucket != bp->bucket) |
| 1159 | wrterror(d, "chunk info corrupted"); | 1191 | wrterror(d, "chunk info corrupted"); |
| 1160 | 1192 | ||
| 1161 | /* bias, as bp->total is not a power of 2 */ | 1193 | /* bias, as bp->total is not a power of 2 */ |
| @@ -1163,8 +1195,8 @@ malloc_bytes(struct dir_info *d, size_t size) | |||
| 1163 | 1195 | ||
| 1164 | /* potentially start somewhere in a short */ | 1196 | /* potentially start somewhere in a short */ |
| 1165 | lp = &bp->bits[i / MALLOC_BITS]; | 1197 | lp = &bp->bits[i / MALLOC_BITS]; |
| 1166 | if (*lp) { | 1198 | j = i % MALLOC_BITS; /* j must be signed */ |
| 1167 | int j = i % MALLOC_BITS; /* j must be signed */ | 1199 | if (*lp >> j) { |
| 1168 | k = ffs(*lp >> j); | 1200 | k = ffs(*lp >> j); |
| 1169 | if (k != 0) { | 1201 | if (k != 0) { |
| 1170 | k += j - 1; | 1202 | k += j - 1; |
| @@ -1200,13 +1232,13 @@ found: | |||
| 1200 | STATS_SETFN(r, k, d->caller); | 1232 | STATS_SETFN(r, k, d->caller); |
| 1201 | } | 1233 | } |
| 1202 | 1234 | ||
| 1203 | k *= B2ALLOC(bp->bucket); | 1235 | k *= B2ALLOC(bucket); |
| 1204 | 1236 | ||
| 1205 | p = (char *)bp->page + k; | 1237 | p = (char *)bp->page + k; |
| 1206 | if (bp->bucket > 0) { | 1238 | if (bucket > 0) { |
| 1207 | validate_junk(d, p, B2SIZE(bp->bucket)); | 1239 | validate_junk(d, p, B2SIZE(bucket)); |
| 1208 | if (mopts.chunk_canaries) | 1240 | if (mopts.chunk_canaries) |
| 1209 | fill_canary(p, size, B2SIZE(bp->bucket)); | 1241 | fill_canary(p, size, B2SIZE(bucket)); |
| 1210 | } | 1242 | } |
| 1211 | return p; | 1243 | return p; |
| 1212 | } | 1244 | } |
| @@ -1511,7 +1543,7 @@ malloc(size_t size) | |||
| 1511 | int saved_errno = errno; | 1543 | int saved_errno = errno; |
| 1512 | 1544 | ||
| 1513 | PROLOGUE(getpool(), "malloc") | 1545 | PROLOGUE(getpool(), "malloc") |
| 1514 | SET_CALLER(d, caller()); | 1546 | SET_CALLER(d, caller(d)); |
| 1515 | r = omalloc(d, size, 0); | 1547 | r = omalloc(d, size, 0); |
| 1516 | EPILOGUE() | 1548 | EPILOGUE() |
| 1517 | return r; | 1549 | return r; |
| @@ -1526,7 +1558,7 @@ malloc_conceal(size_t size) | |||
| 1526 | int saved_errno = errno; | 1558 | int saved_errno = errno; |
| 1527 | 1559 | ||
| 1528 | PROLOGUE(mopts.malloc_pool[0], "malloc_conceal") | 1560 | PROLOGUE(mopts.malloc_pool[0], "malloc_conceal") |
| 1529 | SET_CALLER(d, caller()); | 1561 | SET_CALLER(d, caller(d)); |
| 1530 | r = omalloc(d, size, 0); | 1562 | r = omalloc(d, size, 0); |
| 1531 | EPILOGUE() | 1563 | EPILOGUE() |
| 1532 | return r; | 1564 | return r; |
| @@ -1937,7 +1969,7 @@ realloc(void *ptr, size_t size) | |||
| 1937 | int saved_errno = errno; | 1969 | int saved_errno = errno; |
| 1938 | 1970 | ||
| 1939 | PROLOGUE(getpool(), "realloc") | 1971 | PROLOGUE(getpool(), "realloc") |
| 1940 | SET_CALLER(d, caller()); | 1972 | SET_CALLER(d, caller(d)); |
| 1941 | r = orealloc(&d, ptr, size); | 1973 | r = orealloc(&d, ptr, size); |
| 1942 | EPILOGUE() | 1974 | EPILOGUE() |
| 1943 | return r; | 1975 | return r; |
| @@ -1958,7 +1990,7 @@ calloc(size_t nmemb, size_t size) | |||
| 1958 | int saved_errno = errno; | 1990 | int saved_errno = errno; |
| 1959 | 1991 | ||
| 1960 | PROLOGUE(getpool(), "calloc") | 1992 | PROLOGUE(getpool(), "calloc") |
| 1961 | SET_CALLER(d, caller()); | 1993 | SET_CALLER(d, caller(d)); |
| 1962 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && | 1994 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && |
| 1963 | nmemb > 0 && SIZE_MAX / nmemb < size) { | 1995 | nmemb > 0 && SIZE_MAX / nmemb < size) { |
| 1964 | d->active--; | 1996 | d->active--; |
| @@ -1984,7 +2016,7 @@ calloc_conceal(size_t nmemb, size_t size) | |||
| 1984 | int saved_errno = errno; | 2016 | int saved_errno = errno; |
| 1985 | 2017 | ||
| 1986 | PROLOGUE(mopts.malloc_pool[0], "calloc_conceal") | 2018 | PROLOGUE(mopts.malloc_pool[0], "calloc_conceal") |
| 1987 | SET_CALLER(d, caller()); | 2019 | SET_CALLER(d, caller(d)); |
| 1988 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && | 2020 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && |
| 1989 | nmemb > 0 && SIZE_MAX / nmemb < size) { | 2021 | nmemb > 0 && SIZE_MAX / nmemb < size) { |
| 1990 | d->active--; | 2022 | d->active--; |
| @@ -2130,7 +2162,7 @@ recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size) | |||
| 2130 | return recallocarray_p(ptr, oldnmemb, newnmemb, size); | 2162 | return recallocarray_p(ptr, oldnmemb, newnmemb, size); |
| 2131 | 2163 | ||
| 2132 | PROLOGUE(getpool(), "recallocarray") | 2164 | PROLOGUE(getpool(), "recallocarray") |
| 2133 | SET_CALLER(d, caller()); | 2165 | SET_CALLER(d, caller(d)); |
| 2134 | 2166 | ||
| 2135 | if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && | 2167 | if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && |
| 2136 | newnmemb > 0 && SIZE_MAX / newnmemb < size) { | 2168 | newnmemb > 0 && SIZE_MAX / newnmemb < size) { |
| @@ -2289,7 +2321,7 @@ posix_memalign(void **memptr, size_t alignment, size_t size) | |||
| 2289 | malloc_recurse(d); | 2321 | malloc_recurse(d); |
| 2290 | goto err; | 2322 | goto err; |
| 2291 | } | 2323 | } |
| 2292 | SET_CALLER(d, caller()); | 2324 | SET_CALLER(d, caller(d)); |
| 2293 | r = omemalign(d, alignment, size, 0); | 2325 | r = omemalign(d, alignment, size, 0); |
| 2294 | d->active--; | 2326 | d->active--; |
| 2295 | _MALLOC_UNLOCK(d->mutex); | 2327 | _MALLOC_UNLOCK(d->mutex); |
| @@ -2328,7 +2360,7 @@ aligned_alloc(size_t alignment, size_t size) | |||
| 2328 | } | 2360 | } |
| 2329 | 2361 | ||
| 2330 | PROLOGUE(getpool(), "aligned_alloc") | 2362 | PROLOGUE(getpool(), "aligned_alloc") |
| 2331 | SET_CALLER(d, caller()); | 2363 | SET_CALLER(d, caller(d)); |
| 2332 | r = omemalign(d, alignment, size, 0); | 2364 | r = omemalign(d, alignment, size, 0); |
| 2333 | EPILOGUE() | 2365 | EPILOGUE() |
| 2334 | return r; | 2366 | return r; |
| @@ -2337,43 +2369,74 @@ DEF_STRONG(aligned_alloc); | |||
| 2337 | 2369 | ||
| 2338 | #ifdef MALLOC_STATS | 2370 | #ifdef MALLOC_STATS |
| 2339 | 2371 | ||
| 2372 | static int | ||
| 2373 | btcmp(const struct btnode *e1, const struct btnode *e2) | ||
| 2374 | { | ||
| 2375 | return memcmp(e1->caller, e2->caller, sizeof(e1->caller)); | ||
| 2376 | } | ||
| 2377 | |||
| 2378 | RBT_GENERATE(btshead, btnode, entry, btcmp); | ||
| 2379 | |||
| 2380 | static void* | ||
| 2381 | store_caller(struct dir_info *d, struct btnode *f) | ||
| 2382 | { | ||
| 2383 | struct btnode *p; | ||
| 2384 | |||
| 2385 | if (DO_STATS == 0 || d->btnodes == MAP_FAILED) | ||
| 2386 | return NULL; | ||
| 2387 | |||
| 2388 | p = RBT_FIND(btshead, &d->btraces, f); | ||
| 2389 | if (p != NULL) | ||
| 2390 | return p; | ||
| 2391 | if (d->btnodes == NULL || | ||
| 2392 | d->btnodesused >= MALLOC_PAGESIZE / sizeof(struct btnode)) { | ||
| 2393 | d->btnodes = map(d, MALLOC_PAGESIZE, 0); | ||
| 2394 | if (d->btnodes == MAP_FAILED) | ||
| 2395 | return NULL; | ||
| 2396 | d->btnodesused = 0; | ||
| 2397 | } | ||
| 2398 | p = &d->btnodes[d->btnodesused++]; | ||
| 2399 | memcpy(p->caller, f->caller, sizeof(p->caller[0]) * DO_STATS); | ||
| 2400 | RBT_INSERT(btshead, &d->btraces, p); | ||
| 2401 | return p; | ||
| 2402 | } | ||
| 2403 | |||
| 2404 | static void fabstorel(const void *, char *, size_t); | ||
| 2405 | |||
| 2340 | static void | 2406 | static void |
| 2341 | print_chunk_details(struct dir_info *pool, void *p, size_t sz, size_t i) | 2407 | print_chunk_details(struct dir_info *pool, void *p, size_t sz, size_t index) |
| 2342 | { | 2408 | { |
| 2343 | struct region_info *r; | 2409 | struct region_info *r; |
| 2344 | struct chunk_info *chunkinfo; | 2410 | struct chunk_info *chunkinfo; |
| 2411 | struct btnode* btnode; | ||
| 2345 | uint32_t chunknum; | 2412 | uint32_t chunknum; |
| 2346 | Dl_info info; | 2413 | int frame; |
| 2347 | const char *caller, *pcaller = NULL; | 2414 | char buf1[128]; |
| 2348 | const char *object = "."; | 2415 | char buf2[128]; |
| 2349 | const char *pobject = "."; | ||
| 2350 | const char *msg = ""; | 2416 | const char *msg = ""; |
| 2351 | 2417 | ||
| 2352 | r = find(pool, p); | 2418 | r = find(pool, p); |
| 2353 | chunkinfo = (struct chunk_info *)r->size; | 2419 | chunkinfo = (struct chunk_info *)r->size; |
| 2354 | chunknum = find_chunknum(pool, chunkinfo, p, 0); | 2420 | chunknum = find_chunknum(pool, chunkinfo, p, 0); |
| 2355 | caller = r->f[chunknum]; | 2421 | btnode = (struct btnode *)r->f[chunknum]; |
| 2356 | if (dladdr(caller, &info) != 0) { | 2422 | frame = DO_STATS - 1; |
| 2357 | caller -= (uintptr_t)info.dli_fbase; | 2423 | if (btnode != NULL) |
| 2358 | object = info.dli_fname; | 2424 | fabstorel(btnode->caller[frame], buf1, sizeof(buf1)); |
| 2359 | } | 2425 | strlcpy(buf2, ". 0x0", sizeof(buf2)); |
| 2360 | if (chunknum > 0) { | 2426 | if (chunknum > 0) { |
| 2361 | chunknum--; | 2427 | chunknum--; |
| 2362 | pcaller = r->f[chunknum]; | 2428 | btnode = (struct btnode *)r->f[chunknum]; |
| 2363 | if (dladdr(pcaller, &info) != 0) { | 2429 | if (btnode != NULL) |
| 2364 | pcaller -= (uintptr_t)info.dli_fbase; | 2430 | fabstorel(btnode->caller[frame], buf2, sizeof(buf2)); |
| 2365 | pobject = info.dli_fname; | ||
| 2366 | } | ||
| 2367 | if (CHUNK_FREE(chunkinfo, chunknum)) | 2431 | if (CHUNK_FREE(chunkinfo, chunknum)) |
| 2368 | msg = " (now free)"; | 2432 | msg = " (now free)"; |
| 2369 | } | 2433 | } |
| 2370 | 2434 | ||
| 2371 | wrterror(pool, | 2435 | wrterror(pool, |
| 2372 | "write to free chunk %p[%zu..%zu]@%zu allocated at %s %p " | 2436 | "write to free chunk %p[%zu..%zu]@%zu allocated at %s " |
| 2373 | "(preceding chunk %p allocated at %s %p%s)", | 2437 | "(preceding chunk %p allocated at %s%s)", |
| 2374 | p, i * sizeof(uint64_t), | 2438 | p, index * sizeof(uint64_t), (index + 1) * sizeof(uint64_t) - 1, |
| 2375 | (i + 1) * sizeof(uint64_t) - 1, sz, object, caller, p - sz, | 2439 | sz, buf1, p - sz, buf2, msg); |
| 2376 | pobject, pcaller, msg); | ||
| 2377 | } | 2440 | } |
| 2378 | 2441 | ||
| 2379 | static void | 2442 | static void |
| @@ -2476,6 +2539,52 @@ putleakinfo(struct leaktree *leaks, void *f, size_t sz, int cnt) | |||
| 2476 | } | 2539 | } |
| 2477 | 2540 | ||
| 2478 | static void | 2541 | static void |
| 2542 | fabstorel(const void *f, char *buf, size_t size) | ||
| 2543 | { | ||
| 2544 | Dl_info info; | ||
| 2545 | const char *object = "."; | ||
| 2546 | const char *caller; | ||
| 2547 | |||
| 2548 | caller = f; | ||
| 2549 | if (caller != NULL && dladdr(f, &info) != 0) { | ||
| 2550 | caller -= (uintptr_t)info.dli_fbase; | ||
| 2551 | object = info.dli_fname; | ||
| 2552 | } | ||
| 2553 | snprintf(buf, size, "%s %p", object, caller); | ||
| 2554 | } | ||
| 2555 | |||
| 2556 | static void | ||
| 2557 | dump_leak(struct leaknode *p) | ||
| 2558 | { | ||
| 2559 | int i; | ||
| 2560 | char buf[128]; | ||
| 2561 | |||
| 2562 | if (p->d.f == NULL) { | ||
| 2563 | fabstorel(NULL, buf, sizeof(buf)); | ||
| 2564 | ulog("%18p %7zu %6u %6zu addr2line -e %s\n", | ||
| 2565 | p->d.f, p->d.total_size, p->d.count, | ||
| 2566 | p->d.total_size / p->d.count, buf); | ||
| 2567 | return; | ||
| 2568 | } | ||
| 2569 | |||
| 2570 | for (i = 0; i < DO_STATS; i++) { | ||
| 2571 | const char *abscaller; | ||
| 2572 | |||
| 2573 | abscaller = ((struct btnode*)p->d.f)->caller[i]; | ||
| 2574 | if (abscaller == NULL) | ||
| 2575 | break; | ||
| 2576 | fabstorel(abscaller, buf, sizeof(buf)); | ||
| 2577 | if (i == 0) | ||
| 2578 | ulog("%18p %7zu %6u %6zu addr2line -e %s\n", | ||
| 2579 | abscaller, p->d.total_size, p->d.count, | ||
| 2580 | p->d.total_size / p->d.count, buf); | ||
| 2581 | else | ||
| 2582 | ulog("%*p %*s %6s %6s addr2line -e %s\n", | ||
| 2583 | i + 18, abscaller, 7 - i, "-", "-", "-", buf); | ||
| 2584 | } | ||
| 2585 | } | ||
| 2586 | |||
| 2587 | static void | ||
| 2479 | dump_leaks(struct leaktree *leaks) | 2588 | dump_leaks(struct leaktree *leaks) |
| 2480 | { | 2589 | { |
| 2481 | struct leaknode *p; | 2590 | struct leaknode *p; |
| @@ -2483,22 +2592,8 @@ dump_leaks(struct leaktree *leaks) | |||
| 2483 | ulog("Leak report:\n"); | 2592 | ulog("Leak report:\n"); |
| 2484 | ulog(" f sum # avg\n"); | 2593 | ulog(" f sum # avg\n"); |
| 2485 | 2594 | ||
| 2486 | RBT_FOREACH(p, leaktree, leaks) { | 2595 | RBT_FOREACH(p, leaktree, leaks) |
| 2487 | Dl_info info; | 2596 | dump_leak(p); |
| 2488 | const char *caller = p->d.f; | ||
| 2489 | const char *object = "."; | ||
| 2490 | |||
| 2491 | if (caller != NULL) { | ||
| 2492 | if (dladdr(p->d.f, &info) != 0) { | ||
| 2493 | caller -= (uintptr_t)info.dli_fbase; | ||
| 2494 | object = info.dli_fname; | ||
| 2495 | } | ||
| 2496 | } | ||
| 2497 | ulog("%18p %7zu %6u %6zu addr2line -e %s %p\n", | ||
| 2498 | p->d.f, p->d.total_size, p->d.count, | ||
| 2499 | p->d.total_size / p->d.count, | ||
| 2500 | object, caller); | ||
| 2501 | } | ||
| 2502 | } | 2597 | } |
| 2503 | 2598 | ||
| 2504 | static void | 2599 | static void |
