diff options
| author | otto <> | 2022-02-26 16:14:42 +0000 | 
|---|---|---|
| committer | otto <> | 2022-02-26 16:14:42 +0000 | 
| commit | b9a07308bf393074d3bc822f5d326959d6620f23 (patch) | |
| tree | eedb761db3c5aebae973ef73ef1f63a888027635 /src/lib/libc/stdlib/malloc.c | |
| parent | e29735531148d227a52ccd6fa19e0b2cdf8b7b83 (diff) | |
| download | openbsd-b9a07308bf393074d3bc822f5d326959d6620f23.tar.gz openbsd-b9a07308bf393074d3bc822f5d326959d6620f23.tar.bz2 openbsd-b9a07308bf393074d3bc822f5d326959d6620f23.zip | |
Currently malloc caches a number of free'ed regions up to 128k
in size. This cache is indexed by size (in # of pages), so it is
very quick to check.  Some programs allocate and deallocate larger
allocations in a frantic way.  Accomodate those programs by also
keeping a cache of regions between 128k and 2M, in a cache of variable
sized regions.
Tested by many in snaps; ok deraadt@
Diffstat (limited to 'src/lib/libc/stdlib/malloc.c')
| -rw-r--r-- | src/lib/libc/stdlib/malloc.c | 193 | 
1 files changed, 160 insertions, 33 deletions
| diff --git a/src/lib/libc/stdlib/malloc.c b/src/lib/libc/stdlib/malloc.c index 64273b49fe..a700087534 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.272 2021/09/19 09:15:22 tb Exp $ */ | 1 | /* $OpenBSD: malloc.c,v 1.273 2022/02/26 16:14:42 otto Exp $ */ | 
| 2 | /* | 2 | /* | 
| 3 | * Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek <otto@drijf.net> | 3 | * Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek <otto@drijf.net> | 
| 4 | * Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org> | 4 | * Copyright (c) 2012 Matthew Dempsky <matthew@openbsd.org> | 
| @@ -113,13 +113,27 @@ struct region_info { | |||
| 113 | 113 | ||
| 114 | LIST_HEAD(chunk_head, chunk_info); | 114 | LIST_HEAD(chunk_head, chunk_info); | 
| 115 | 115 | ||
| 116 | #define MAX_CACHEABLE_SIZE 32 | 116 | /* | 
| 117 | struct cache { | 117 | * Two caches, one for "small" regions, one for "big". | 
| 118 | void *pages[MALLOC_MAXCACHE]; | 118 | * Small cache is an array per size, big cache is one array with different | 
| 119 | * sized regions | ||
| 120 | */ | ||
| 121 | #define MAX_SMALLCACHEABLE_SIZE 32 | ||
| 122 | #define MAX_BIGCACHEABLE_SIZE 512 | ||
| 123 | /* If the total # of pages is larger than this, evict before inserting */ | ||
| 124 | #define BIGCACHE_FILL(sz) (MAX_BIGCACHEABLE_SIZE * (sz) / 4) | ||
| 125 | |||
| 126 | struct smallcache { | ||
| 127 | void **pages; | ||
| 119 | ushort length; | 128 | ushort length; | 
| 120 | ushort max; | 129 | ushort max; | 
| 121 | }; | 130 | }; | 
| 122 | 131 | ||
| 132 | struct bigcache { | ||
| 133 | void *page; | ||
| 134 | size_t psize; | ||
| 135 | }; | ||
| 136 | |||
| 123 | struct dir_info { | 137 | struct dir_info { | 
| 124 | u_int32_t canary1; | 138 | u_int32_t canary1; | 
| 125 | int active; /* status of malloc */ | 139 | int active; /* status of malloc */ | 
| @@ -139,7 +153,10 @@ struct dir_info { | |||
| 139 | void *delayed_chunks[MALLOC_DELAYED_CHUNK_MASK + 1]; | 153 | void *delayed_chunks[MALLOC_DELAYED_CHUNK_MASK + 1]; | 
| 140 | u_char rbytes[32]; /* random bytes */ | 154 | u_char rbytes[32]; /* random bytes */ | 
| 141 | /* free pages cache */ | 155 | /* free pages cache */ | 
| 142 | struct cache cache[MAX_CACHEABLE_SIZE]; | 156 | struct smallcache smallcache[MAX_SMALLCACHEABLE_SIZE]; | 
| 157 | size_t bigcache_used; | ||
| 158 | size_t bigcache_size; | ||
| 159 | struct bigcache *bigcache; | ||
| 143 | #ifdef MALLOC_STATS | 160 | #ifdef MALLOC_STATS | 
| 144 | size_t inserts; | 161 | size_t inserts; | 
| 145 | size_t insert_collisions; | 162 | size_t insert_collisions; | 
| @@ -207,7 +224,7 @@ struct malloc_readonly { | |||
| 207 | #ifdef MALLOC_STATS | 224 | #ifdef MALLOC_STATS | 
| 208 | int malloc_stats; /* dump statistics at end */ | 225 | int malloc_stats; /* dump statistics at end */ | 
| 209 | #endif | 226 | #endif | 
| 210 | u_int32_t malloc_canary; /* Matched against ones in malloc_pool */ | 227 | u_int32_t malloc_canary; /* Matched against ones in pool */ | 
| 211 | }; | 228 | }; | 
| 212 | 229 | ||
| 213 | /* This object is mapped PROT_READ after initialisation to prevent tampering */ | 230 | /* This object is mapped PROT_READ after initialisation to prevent tampering */ | 
| @@ -714,18 +731,61 @@ unmap(struct dir_info *d, void *p, size_t sz, size_t clear) | |||
| 714 | size_t psz = sz >> MALLOC_PAGESHIFT; | 731 | size_t psz = sz >> MALLOC_PAGESHIFT; | 
| 715 | void *r; | 732 | void *r; | 
| 716 | u_short i; | 733 | u_short i; | 
| 717 | struct cache *cache; | 734 | struct smallcache *cache; | 
| 718 | 735 | ||
| 719 | if (sz != PAGEROUND(sz) || psz == 0) | 736 | if (sz != PAGEROUND(sz) || psz == 0) | 
| 720 | wrterror(d, "munmap round"); | 737 | wrterror(d, "munmap round"); | 
| 721 | 738 | ||
| 722 | if (psz > MAX_CACHEABLE_SIZE || d->cache[psz - 1].max == 0) { | 739 | if (d->bigcache_size > 0 && psz > MAX_SMALLCACHEABLE_SIZE && | 
| 740 | psz <= MAX_BIGCACHEABLE_SIZE) { | ||
| 741 | u_short base = getrbyte(d); | ||
| 742 | u_short j; | ||
| 743 | |||
| 744 | /* don't look through all slots */ | ||
| 745 | for (j = 0; j < d->bigcache_size / 4; j++) { | ||
| 746 | i = (base + j) % d->bigcache_size; | ||
| 747 | if (d->bigcache_used < | ||
| 748 | BIGCACHE_FILL(d->bigcache_size)) { | ||
| 749 | if (d->bigcache[i].psize == 0) | ||
| 750 | break; | ||
| 751 | } else { | ||
| 752 | if (d->bigcache[i].psize != 0) | ||
| 753 | break; | ||
| 754 | } | ||
| 755 | } | ||
| 756 | /* if we didn't find a preferred slot, use random one */ | ||
| 757 | if (d->bigcache[i].psize != 0) { | ||
| 758 | size_t tmp; | ||
| 759 | |||
| 760 | r = d->bigcache[i].page; | ||
| 761 | d->bigcache_used -= d->bigcache[i].psize; | ||
| 762 | tmp = d->bigcache[i].psize << MALLOC_PAGESHIFT; | ||
| 763 | if (!mopts.malloc_freeunmap) | ||
| 764 | validate_junk(d, r, tmp); | ||
| 765 | if (munmap(r, tmp)) | ||
| 766 | wrterror(d, "munmap %p", r); | ||
| 767 | STATS_SUB(d->malloc_used, tmp); | ||
| 768 | } | ||
| 769 | |||
| 770 | if (clear > 0) | ||
| 771 | explicit_bzero(p, clear); | ||
| 772 | if (mopts.malloc_freeunmap) { | ||
| 773 | if (mprotect(p, sz, PROT_NONE)) | ||
| 774 | wrterror(d, "mprotect %p", r); | ||
| 775 | } else | ||
| 776 | junk_free(d->malloc_junk, p, sz); | ||
| 777 | d->bigcache[i].page = p; | ||
| 778 | d->bigcache[i].psize = psz; | ||
| 779 | d->bigcache_used += psz; | ||
| 780 | return; | ||
| 781 | } | ||
| 782 | if (psz > MAX_SMALLCACHEABLE_SIZE || d->smallcache[psz - 1].max == 0) { | ||
| 723 | if (munmap(p, sz)) | 783 | if (munmap(p, sz)) | 
| 724 | wrterror(d, "munmap %p", p); | 784 | wrterror(d, "munmap %p", p); | 
| 725 | STATS_SUB(d->malloc_used, sz); | 785 | STATS_SUB(d->malloc_used, sz); | 
| 726 | return; | 786 | return; | 
| 727 | } | 787 | } | 
| 728 | cache = &d->cache[psz - 1]; | 788 | cache = &d->smallcache[psz - 1]; | 
| 729 | if (cache->length == cache->max) { | 789 | if (cache->length == cache->max) { | 
| 730 | /* use a random slot */ | 790 | /* use a random slot */ | 
| 731 | i = getrbyte(d) % cache->max; | 791 | i = getrbyte(d) % cache->max; | 
| @@ -753,9 +813,10 @@ unmap(struct dir_info *d, void *p, size_t sz, size_t clear) | |||
| 753 | static void * | 813 | static void * | 
| 754 | map(struct dir_info *d, size_t sz, int zero_fill) | 814 | map(struct dir_info *d, size_t sz, int zero_fill) | 
| 755 | { | 815 | { | 
| 756 | size_t i, psz = sz >> MALLOC_PAGESHIFT; | 816 | size_t psz = sz >> MALLOC_PAGESHIFT; | 
| 817 | u_short i; | ||
| 757 | void *p; | 818 | void *p; | 
| 758 | struct cache *cache; | 819 | struct smallcache *cache; | 
| 759 | 820 | ||
| 760 | if (mopts.malloc_canary != (d->canary1 ^ (u_int32_t)(uintptr_t)d) || | 821 | if (mopts.malloc_canary != (d->canary1 ^ (u_int32_t)(uintptr_t)d) || | 
| 761 | d->canary1 != ~d->canary2) | 822 | d->canary1 != ~d->canary2) | 
| @@ -764,8 +825,35 @@ map(struct dir_info *d, size_t sz, int zero_fill) | |||
| 764 | wrterror(d, "map round"); | 825 | wrterror(d, "map round"); | 
| 765 | 826 | ||
| 766 | 827 | ||
| 767 | if (psz <= MAX_CACHEABLE_SIZE && d->cache[psz - 1].max > 0) { | 828 | if (d->bigcache_size > 0 && psz > MAX_SMALLCACHEABLE_SIZE && | 
| 768 | cache = &d->cache[psz - 1]; | 829 | psz <= MAX_BIGCACHEABLE_SIZE) { | 
| 830 | size_t base = getrbyte(d); | ||
| 831 | size_t cached = d->bigcache_used; | ||
| 832 | ushort j; | ||
| 833 | |||
| 834 | for (j = 0; j < d->bigcache_size && cached >= psz; j++) { | ||
| 835 | i = (j + base) % d->bigcache_size; | ||
| 836 | if (d->bigcache[i].psize == psz) { | ||
| 837 | p = d->bigcache[i].page; | ||
| 838 | d->bigcache_used -= psz; | ||
| 839 | d->bigcache[i].page = NULL; | ||
| 840 | d->bigcache[i].psize = 0; | ||
| 841 | |||
| 842 | if (!mopts.malloc_freeunmap) | ||
| 843 | validate_junk(d, p, sz); | ||
| 844 | if (mopts.malloc_freeunmap) | ||
| 845 | mprotect(p, sz, PROT_READ | PROT_WRITE); | ||
| 846 | if (zero_fill) | ||
| 847 | memset(p, 0, sz); | ||
| 848 | else if (mopts.malloc_freeunmap) | ||
| 849 | junk_free(d->malloc_junk, p, sz); | ||
| 850 | return p; | ||
| 851 | } | ||
| 852 | cached -= d->bigcache[i].psize; | ||
| 853 | } | ||
| 854 | } | ||
| 855 | if (psz <= MAX_SMALLCACHEABLE_SIZE && d->smallcache[psz - 1].max > 0) { | ||
| 856 | cache = &d->smallcache[psz - 1]; | ||
| 769 | if (cache->length > 0) { | 857 | if (cache->length > 0) { | 
| 770 | if (cache->length == 1) | 858 | if (cache->length == 1) | 
| 771 | p = cache->pages[--cache->length]; | 859 | p = cache->pages[--cache->length]; | 
| @@ -795,10 +883,12 @@ map(struct dir_info *d, size_t sz, int zero_fill) | |||
| 795 | void *q = (char*)p + i * sz; | 883 | void *q = (char*)p + i * sz; | 
| 796 | cache->pages[i] = q; | 884 | cache->pages[i] = q; | 
| 797 | if (!mopts.malloc_freeunmap) | 885 | if (!mopts.malloc_freeunmap) | 
| 798 | junk_free(d->malloc_junk, q, sz); | 886 | junk_free(d->malloc_junk, q, | 
| 887 | sz); | ||
| 799 | } | 888 | } | 
| 800 | if (mopts.malloc_freeunmap) | 889 | if (mopts.malloc_freeunmap) | 
| 801 | mprotect(p, (cache->max - 1) * sz, PROT_NONE); | 890 | mprotect(p, (cache->max - 1) * sz, | 
| 891 | PROT_NONE); | ||
| 802 | p = (char*)p + (cache->max - 1) * sz; | 892 | p = (char*)p + (cache->max - 1) * sz; | 
| 803 | /* zero fill not needed */ | 893 | /* zero fill not needed */ | 
| 804 | return p; | 894 | return p; | 
| @@ -1161,8 +1251,9 @@ omalloc(struct dir_info *pool, size_t sz, int zero_fill, void *f) | |||
| 1161 | } else { | 1251 | } else { | 
| 1162 | if (pool->malloc_junk == 2) { | 1252 | if (pool->malloc_junk == 2) { | 
| 1163 | if (zero_fill) | 1253 | if (zero_fill) | 
| 1164 | memset((char *)p + sz - mopts.malloc_guard, | 1254 | memset((char *)p + sz - | 
| 1165 | SOME_JUNK, psz - sz); | 1255 | mopts.malloc_guard, SOME_JUNK, | 
| 1256 | psz - sz); | ||
| 1166 | else | 1257 | else | 
| 1167 | memset(p, SOME_JUNK, | 1258 | memset(p, SOME_JUNK, | 
| 1168 | psz - mopts.malloc_guard); | 1259 | psz - mopts.malloc_guard); | 
| @@ -1224,13 +1315,33 @@ _malloc_init(int from_rthreads) | |||
| 1224 | if (i == 0) { | 1315 | if (i == 0) { | 
| 1225 | omalloc_poolinit(&d, MAP_CONCEAL); | 1316 | omalloc_poolinit(&d, MAP_CONCEAL); | 
| 1226 | d->malloc_junk = 2; | 1317 | d->malloc_junk = 2; | 
| 1227 | for (j = 0; j < MAX_CACHEABLE_SIZE; j++) | 1318 | d->bigcache_size = 0; | 
| 1228 | d->cache[j].max = 0; | 1319 | for (j = 0; j < MAX_SMALLCACHEABLE_SIZE; j++) | 
| 1320 | d->smallcache[j].max = 0; | ||
| 1229 | } else { | 1321 | } else { | 
| 1322 | size_t sz = 0; | ||
| 1323 | |||
| 1230 | omalloc_poolinit(&d, 0); | 1324 | omalloc_poolinit(&d, 0); | 
| 1231 | d->malloc_junk = mopts.def_malloc_junk; | 1325 | d->malloc_junk = mopts.def_malloc_junk; | 
| 1232 | for (j = 0; j < MAX_CACHEABLE_SIZE; j++) | 1326 | d->bigcache_size = mopts.def_maxcache; | 
| 1233 | d->cache[j].max = mopts.def_maxcache >> (j / 8); | 1327 | for (j = 0; j < MAX_SMALLCACHEABLE_SIZE; j++) { | 
| 1328 | d->smallcache[j].max = | ||
| 1329 | mopts.def_maxcache >> (j / 8); | ||
| 1330 | sz += d->smallcache[j].max * sizeof(void *); | ||
| 1331 | } | ||
| 1332 | sz += d->bigcache_size * sizeof(struct bigcache); | ||
| 1333 | if (sz > 0) { | ||
| 1334 | void *p = MMAP(sz, 0); | ||
| 1335 | if (p == MAP_FAILED) | ||
| 1336 | wrterror(NULL, | ||
| 1337 | "malloc init mmap failed"); | ||
| 1338 | for (j = 0; j < MAX_SMALLCACHEABLE_SIZE; j++) { | ||
| 1339 | d->smallcache[j].pages = p; | ||
| 1340 | p = (char *)p + d->smallcache[j].max * | ||
| 1341 | sizeof(void *); | ||
| 1342 | } | ||
| 1343 | d->bigcache = p; | ||
| 1344 | } | ||
| 1234 | } | 1345 | } | 
| 1235 | d->mutex = i; | 1346 | d->mutex = i; | 
| 1236 | mopts.malloc_pool[i] = d; | 1347 | mopts.malloc_pool[i] = d; | 
| @@ -1348,8 +1459,11 @@ ofree(struct dir_info **argpool, void *p, int clear, int check, size_t argsz) | |||
| 1348 | REALSIZE(sz, r); | 1459 | REALSIZE(sz, r); | 
| 1349 | if (pool->mmap_flag) { | 1460 | if (pool->mmap_flag) { | 
| 1350 | clear = 1; | 1461 | clear = 1; | 
| 1351 | if (!check) | 1462 | if (!check) { | 
| 1352 | argsz = sz; | 1463 | argsz = sz; | 
| 1464 | if (sz > MALLOC_MAXCHUNK) | ||
| 1465 | argsz -= mopts.malloc_guard; | ||
| 1466 | } | ||
| 1353 | } | 1467 | } | 
| 1354 | if (check) { | 1468 | if (check) { | 
| 1355 | if (sz <= MALLOC_MAXCHUNK) { | 1469 | if (sz <= MALLOC_MAXCHUNK) { | 
| @@ -1609,7 +1723,8 @@ orealloc(struct dir_info **argpool, void *p, size_t newsz, void *f) | |||
| 1609 | wrterror(pool, "mprotect"); | 1723 | wrterror(pool, "mprotect"); | 
| 1610 | } | 1724 | } | 
| 1611 | if (munmap((char *)r->p + rnewsz, roldsz - rnewsz)) | 1725 | if (munmap((char *)r->p + rnewsz, roldsz - rnewsz)) | 
| 1612 | wrterror(pool, "munmap %p", (char *)r->p + rnewsz); | 1726 | wrterror(pool, "munmap %p", (char *)r->p + | 
| 1727 | rnewsz); | ||
| 1613 | STATS_SUB(pool->malloc_used, roldsz - rnewsz); | 1728 | STATS_SUB(pool->malloc_used, roldsz - rnewsz); | 
| 1614 | r->size = gnewsz; | 1729 | r->size = gnewsz; | 
| 1615 | if (MALLOC_MOVE_COND(gnewsz)) { | 1730 | if (MALLOC_MOVE_COND(gnewsz)) { | 
| @@ -2091,7 +2206,7 @@ putleakinfo(void *f, size_t sz, int cnt) | |||
| 2091 | { | 2206 | { | 
| 2092 | struct leaknode key, *p; | 2207 | struct leaknode key, *p; | 
| 2093 | static struct leaknode *page; | 2208 | static struct leaknode *page; | 
| 2094 | static int used; | 2209 | static unsigned int used; | 
| 2095 | 2210 | ||
| 2096 | if (cnt == 0 || page == MAP_FAILED) | 2211 | if (cnt == 0 || page == MAP_FAILED) | 
| 2097 | return; | 2212 | return; | 
| @@ -2123,7 +2238,7 @@ static void | |||
| 2123 | dump_leaks(int fd) | 2238 | dump_leaks(int fd) | 
| 2124 | { | 2239 | { | 
| 2125 | struct leaknode *p; | 2240 | struct leaknode *p; | 
| 2126 | int i = 0; | 2241 | unsigned int i = 0; | 
| 2127 | 2242 | ||
| 2128 | dprintf(fd, "Leak report\n"); | 2243 | dprintf(fd, "Leak report\n"); | 
| 2129 | dprintf(fd, " f sum # avg\n"); | 2244 | dprintf(fd, " f sum # avg\n"); | 
| @@ -2196,16 +2311,25 @@ dump_free_chunk_info(int fd, struct dir_info *d) | |||
| 2196 | static void | 2311 | static void | 
| 2197 | dump_free_page_info(int fd, struct dir_info *d) | 2312 | dump_free_page_info(int fd, struct dir_info *d) | 
| 2198 | { | 2313 | { | 
| 2199 | struct cache *cache; | 2314 | struct smallcache *cache; | 
| 2200 | size_t i, total = 0; | 2315 | size_t i, total = 0; | 
| 2201 | 2316 | ||
| 2202 | dprintf(fd, "Cached:\n"); | 2317 | dprintf(fd, "Cached in small cache:\n"); | 
| 2203 | for (i = 0; i < MAX_CACHEABLE_SIZE; i++) { | 2318 | for (i = 0; i < MAX_SMALLCACHEABLE_SIZE; i++) { | 
| 2204 | cache = &d->cache[i]; | 2319 | cache = &d->smallcache[i]; | 
| 2205 | if (cache->length != 0) | 2320 | if (cache->length != 0) | 
| 2206 | dprintf(fd, "%zu(%u): %u = %zu\n", i + 1, cache->max, cache->length, cache->length * (i + 1)); | 2321 | dprintf(fd, "%zu(%u): %u = %zu\n", i + 1, cache->max, | 
| 2322 | cache->length, cache->length * (i + 1)); | ||
| 2207 | total += cache->length * (i + 1); | 2323 | total += cache->length * (i + 1); | 
| 2208 | } | 2324 | } | 
| 2325 | |||
| 2326 | dprintf(fd, "Cached in big cache: %zu/%zu\n", d->bigcache_used, | ||
| 2327 | d->bigcache_size); | ||
| 2328 | for (i = 0; i < d->bigcache_size; i++) { | ||
| 2329 | if (d->bigcache[i].psize != 0) | ||
| 2330 | dprintf(fd, "%zu: %zu\n", i, d->bigcache[i].psize); | ||
| 2331 | total += d->bigcache[i].psize; | ||
| 2332 | } | ||
| 2209 | dprintf(fd, "Free pages cached: %zu\n", total); | 2333 | dprintf(fd, "Free pages cached: %zu\n", total); | 
| 2210 | } | 2334 | } | 
| 2211 | 2335 | ||
| @@ -2232,7 +2356,8 @@ malloc_dump1(int fd, int poolno, struct dir_info *d) | |||
| 2232 | dump_free_chunk_info(fd, d); | 2356 | dump_free_chunk_info(fd, d); | 
| 2233 | dump_free_page_info(fd, d); | 2357 | dump_free_page_info(fd, d); | 
| 2234 | dprintf(fd, | 2358 | dprintf(fd, | 
| 2235 | "slot) hash d type page f size [free/n]\n"); | 2359 | "slot) hash d type page f " | 
| 2360 | "size [free/n]\n"); | ||
| 2236 | for (i = 0; i < d->regions_total; i++) { | 2361 | for (i = 0; i < d->regions_total; i++) { | 
| 2237 | if (d->r[i].p != NULL) { | 2362 | if (d->r[i].p != NULL) { | 
| 2238 | size_t h = hash(d->r[i].p) & | 2363 | size_t h = hash(d->r[i].p) & | 
| @@ -2298,13 +2423,15 @@ DEF_WEAK(malloc_gdump); | |||
| 2298 | static void | 2423 | static void | 
| 2299 | malloc_exit(void) | 2424 | malloc_exit(void) | 
| 2300 | { | 2425 | { | 
| 2301 | int save_errno = errno, fd, i; | 2426 | int save_errno = errno, fd; | 
| 2427 | unsigned i; | ||
| 2302 | 2428 | ||
| 2303 | fd = open("malloc.out", O_RDWR|O_APPEND); | 2429 | fd = open("malloc.out", O_RDWR|O_APPEND); | 
| 2304 | if (fd != -1) { | 2430 | if (fd != -1) { | 
| 2305 | dprintf(fd, "******** Start dump %s *******\n", __progname); | 2431 | dprintf(fd, "******** Start dump %s *******\n", __progname); | 
| 2306 | dprintf(fd, | 2432 | dprintf(fd, | 
| 2307 | "MT=%d M=%u I=%d F=%d U=%d J=%d R=%d X=%d C=%d cache=%u G=%zu\n", | 2433 | "MT=%d M=%u I=%d F=%d U=%d J=%d R=%d X=%d C=%d cache=%u " | 
| 2434 | "G=%zu\n", | ||
| 2308 | mopts.malloc_mt, mopts.malloc_mutexes, | 2435 | mopts.malloc_mt, mopts.malloc_mutexes, | 
| 2309 | mopts.internal_funcs, mopts.malloc_freecheck, | 2436 | mopts.internal_funcs, mopts.malloc_freecheck, | 
| 2310 | mopts.malloc_freeunmap, mopts.def_malloc_junk, | 2437 | mopts.malloc_freeunmap, mopts.def_malloc_junk, | 
