Hi, This is work in progress. I have to think if the flags to kdump I'm introducing should be two or a single one.
Currently, malloc.c can be compiled with MALLOC_STATS defined. If run with option D it dumps its state to a malloc.out file at exit. This state can be used to find leaks amongst other things. This is not ideal for pledged processes, as they often have no way to write files. This changes malloc to use utrace(2) for that. As kdump has no nice way to show those lines without all extras it normally shows, so add two options to it to just show the lines. To use, compile and install libc with MALLOC_STATS defined. Run : $ MALLOC_OPTIONS=D ktrace -tu your_program ... $ kdump -hu Feedback appreciated. -Otto Index: lib/libc/stdlib/malloc.c =================================================================== RCS file: /cvs/src/lib/libc/stdlib/malloc.c,v retrieving revision 1.280 diff -u -p -r1.280 malloc.c --- lib/libc/stdlib/malloc.c 5 Apr 2023 06:25:38 -0000 1.280 +++ lib/libc/stdlib/malloc.c 5 Apr 2023 08:23:42 -0000 @@ -23,7 +23,7 @@ * can buy me a beer in return. Poul-Henning Kamp */ -/* #define MALLOC_STATS */ +#define MALLOC_STATS #include <sys/types.h> #include <sys/queue.h> @@ -39,7 +39,9 @@ #include <unistd.h> #ifdef MALLOC_STATS +#include <sys/types.h> #include <sys/tree.h> +#include <sys/ktrace.h> #include <fcntl.h> #endif @@ -243,10 +245,8 @@ static __dead void wrterror(struct dir_i __attribute__((__format__ (printf, 2, 3))); #ifdef MALLOC_STATS -void malloc_dump(int, int, struct dir_info *); +void malloc_dump(void); PROTO_NORMAL(malloc_dump); -void malloc_gdump(int); -PROTO_NORMAL(malloc_gdump); static void malloc_exit(void); #define CALLER __builtin_return_address(0) #else @@ -319,7 +319,7 @@ wrterror(struct dir_info *d, char *msg, #ifdef MALLOC_STATS if (mopts.malloc_stats) - malloc_gdump(STDERR_FILENO); + malloc_dump(); #endif /* MALLOC_STATS */ errno = saved_errno; @@ -2225,6 +2225,21 @@ aligned_alloc(size_t alignment, size_t s #ifdef MALLOC_STATS +static void +ulog(const char *format, ...) +{ + va_list ap; + char buf[100]; + int len; + + va_start(ap, format); + len = vsnprintf(buf, sizeof(buf), format, ap); + if (len > (int)sizeof(buf)) + len = sizeof(buf); + utrace("mallocdumpline", buf, len); + va_end(ap); +} + struct malloc_leak { void *f; size_t total_size; @@ -2280,20 +2295,20 @@ putleakinfo(void *f, size_t sz, int cnt) static struct malloc_leak *malloc_leaks; static void -dump_leaks(int fd) +dump_leaks(void) { struct leaknode *p; unsigned int i = 0; - dprintf(fd, "Leak report\n"); - dprintf(fd, " f sum # avg\n"); + ulog("Leak report\n"); + ulog(" f sum # avg\n"); /* XXX only one page of summary */ if (malloc_leaks == NULL) malloc_leaks = MMAP(MALLOC_PAGESIZE, 0); if (malloc_leaks != MAP_FAILED) memset(malloc_leaks, 0, MALLOC_PAGESIZE); RBT_FOREACH(p, leaktree, &leakhead) { - dprintf(fd, "%18p %7zu %6u %6zu\n", p->d.f, + ulog("%18p %7zu %6u %6zu\n", p->d.f, p->d.total_size, p->d.count, p->d.total_size / p->d.count); if (malloc_leaks == MAP_FAILED || i >= MALLOC_PAGESIZE / sizeof(struct malloc_leak)) @@ -2306,10 +2321,10 @@ dump_leaks(int fd) } static void -dump_chunk(int fd, struct chunk_info *p, void *f, int fromfreelist) +dump_chunk(struct chunk_info *p, void *f, int fromfreelist) { while (p != NULL) { - dprintf(fd, "chunk %18p %18p %4zu %d/%d\n", + ulog("chunk %18p %18p %4zu %d/%d\n", p->page, ((p->bits[0] & 1) ? NULL : f), B2SIZE(p->bucket), p->free, p->total); if (!fromfreelist) { @@ -2325,17 +2340,17 @@ dump_chunk(int fd, struct chunk_info *p, } p = LIST_NEXT(p, entries); if (p != NULL) - dprintf(fd, " "); + ulog(" "); } } static void -dump_free_chunk_info(int fd, struct dir_info *d) +dump_free_chunk_info(struct dir_info *d) { int i, j, count; struct chunk_info *p; - dprintf(fd, "Free chunk structs:\n"); + ulog("Free chunk structs:\n"); for (i = 0; i <= BUCKETS; i++) { count = 0; LIST_FOREACH(p, &d->chunk_info_list[i], entries) @@ -2345,99 +2360,97 @@ dump_free_chunk_info(int fd, struct dir_ if (p == NULL && count == 0) continue; if (j == 0) - dprintf(fd, "%3d) %3d ", i, count); + ulog("%3d) %3d ", i, count); else - dprintf(fd, " "); + ulog(" "); if (p != NULL) - dump_chunk(fd, p, NULL, 1); + dump_chunk(p, NULL, 1); else - dprintf(fd, "\n"); + ulog(".\n"); } } } static void -dump_free_page_info(int fd, struct dir_info *d) +dump_free_page_info(struct dir_info *d) { struct smallcache *cache; size_t i, total = 0; - dprintf(fd, "Cached in small cache:\n"); + ulog("Cached in small cache:\n"); for (i = 0; i < MAX_SMALLCACHEABLE_SIZE; i++) { cache = &d->smallcache[i]; if (cache->length != 0) - dprintf(fd, "%zu(%u): %u = %zu\n", i + 1, cache->max, + ulog("%zu(%u): %u = %zu\n", i + 1, cache->max, cache->length, cache->length * (i + 1)); total += cache->length * (i + 1); } - dprintf(fd, "Cached in big cache: %zu/%zu\n", d->bigcache_used, + ulog("Cached in big cache: %zu/%zu\n", d->bigcache_used, d->bigcache_size); for (i = 0; i < d->bigcache_size; i++) { if (d->bigcache[i].psize != 0) - dprintf(fd, "%zu: %zu\n", i, d->bigcache[i].psize); + ulog("%zu: %zu\n", i, d->bigcache[i].psize); total += d->bigcache[i].psize; } - dprintf(fd, "Free pages cached: %zu\n", total); + ulog("Free pages cached: %zu\n", total); } static void -malloc_dump1(int fd, int poolno, struct dir_info *d) +malloc_dump1(int poolno, struct dir_info *d) { size_t i, realsize; - dprintf(fd, "Malloc dir of %s pool %d at %p\n", __progname, poolno, d); + ulog("Malloc dir of %s pool %d at %p\n", __progname, poolno, d); if (d == NULL) return; - dprintf(fd, "MT=%d J=%d Fl=%x\n", d->malloc_mt, d->malloc_junk, d->mmap_flag); - dprintf(fd, "Region slots free %zu/%zu\n", + ulog("MT=%d J=%d Fl=%x\n", d->malloc_mt, d->malloc_junk, d->mmap_flag); + ulog("Region slots free %zu/%zu\n", d->regions_free, d->regions_total); - dprintf(fd, "Finds %zu/%zu\n", d->finds, d->find_collisions); - dprintf(fd, "Inserts %zu/%zu\n", d->inserts, d->insert_collisions); - dprintf(fd, "Deletes %zu/%zu\n", d->deletes, d->delete_moves); - dprintf(fd, "Cheap reallocs %zu/%zu\n", + ulog("Finds %zu/%zu\n", d->finds, d->find_collisions); + ulog("Inserts %zu/%zu\n", d->inserts, d->insert_collisions); + ulog("Deletes %zu/%zu\n", d->deletes, d->delete_moves); + ulog("Cheap reallocs %zu/%zu\n", d->cheap_reallocs, d->cheap_realloc_tries); - dprintf(fd, "Other pool searches %zu/%zu\n", + ulog("Other pool searches %zu/%zu\n", d->other_pool, d->pool_searches); - dprintf(fd, "In use %zu\n", d->malloc_used); - dprintf(fd, "Guarded %zu\n", d->malloc_guarded); - dump_free_chunk_info(fd, d); - dump_free_page_info(fd, d); - dprintf(fd, - "slot) hash d type page f " + ulog("In use %zu\n", d->malloc_used); + ulog("Guarded %zu\n", d->malloc_guarded); + dump_free_chunk_info(d); + dump_free_page_info(d); + ulog("slot) hash d type page f " "size [free/n]\n"); for (i = 0; i < d->regions_total; i++) { if (d->r[i].p != NULL) { size_t h = hash(d->r[i].p) & (d->regions_total - 1); - dprintf(fd, "%4zx) #%4zx %zd ", + ulog("%4zx) #%4zx %zd ", i, h, h - i); REALSIZE(realsize, &d->r[i]); if (realsize > MALLOC_MAXCHUNK) { putleakinfo(d->r[i].f, realsize, 1); - dprintf(fd, - "pages %18p %18p %zu\n", d->r[i].p, + ulog("pages %18p %18p %zu\n", d->r[i].p, d->r[i].f, realsize); } else - dump_chunk(fd, + dump_chunk( (struct chunk_info *)d->r[i].size, d->r[i].f, 0); } } - dump_leaks(fd); - dprintf(fd, "\n"); + dump_leaks(); + ulog("\n"); } -void -malloc_dump(int fd, int poolno, struct dir_info *pool) +static void +malloc_dump0(int poolno, struct dir_info *pool) { int i; void *p; struct region_info *r; int saved_errno = errno; - if (pool == NULL) + if (pool == NULL || pool->r == NULL) return; for (i = 0; i < MALLOC_DELAYED_CHUNK_MASK + 1; i++) { p = pool->delayed_chunks[i]; @@ -2451,50 +2464,41 @@ malloc_dump(int fd, int poolno, struct d } /* XXX leak when run multiple times */ RBT_INIT(leaktree, &leakhead); - malloc_dump1(fd, poolno, pool); + malloc_dump1(poolno, pool); errno = saved_errno; } -DEF_WEAK(malloc_dump); void -malloc_gdump(int fd) +malloc_dump(void) { int i; int saved_errno = errno; for (i = 0; i < mopts.malloc_mutexes; i++) - malloc_dump(fd, i, mopts.malloc_pool[i]); + malloc_dump0(i, mopts.malloc_pool[i]); errno = saved_errno; } -DEF_WEAK(malloc_gdump); +DEF_WEAK(malloc_dump); static void malloc_exit(void) { - int save_errno = errno, fd; + int save_errno = errno; unsigned i; - fd = open("malloc.out", O_RDWR|O_APPEND); - if (fd != -1) { - dprintf(fd, "******** Start dump %s *******\n", __progname); - dprintf(fd, - "M=%u I=%d F=%d U=%d J=%d R=%d X=%d C=%d cache=%u " - "G=%zu\n", - mopts.malloc_mutexes, - mopts.internal_funcs, mopts.malloc_freecheck, - mopts.malloc_freeunmap, mopts.def_malloc_junk, - mopts.malloc_realloc, mopts.malloc_xmalloc, - mopts.chunk_canaries, mopts.def_maxcache, - mopts.malloc_guard); + ulog("******** Start dump %s *******\n", __progname); + ulog("M=%u I=%d F=%d U=%d J=%d R=%d X=%d C=%d cache=%u " + "G=%zu\n", + mopts.malloc_mutexes, + mopts.internal_funcs, mopts.malloc_freecheck, + mopts.malloc_freeunmap, mopts.def_malloc_junk, + mopts.malloc_realloc, mopts.malloc_xmalloc, + mopts.chunk_canaries, mopts.def_maxcache, + mopts.malloc_guard); - for (i = 0; i < mopts.malloc_mutexes; i++) - malloc_dump(fd, i, mopts.malloc_pool[i]); - dprintf(fd, "******** End dump %s *******\n", __progname); - close(fd); - } else - dprintf(STDERR_FILENO, - "malloc() warning: Couldn't dump stats\n"); + malloc_dump(); + ulog("******** End dump %s *******\n", __progname); errno = save_errno; } Index: usr.bin/kdump/kdump.c =================================================================== RCS file: /cvs/src/usr.bin/kdump/kdump.c,v retrieving revision 1.156 diff -u -p -r1.156 kdump.c --- usr.bin/kdump/kdump.c 17 Feb 2023 18:01:26 -0000 1.156 +++ usr.bin/kdump/kdump.c 5 Apr 2023 08:23:42 -0000 @@ -88,6 +88,8 @@ int needtid, tail, basecol; char *tracefile = DEF_TRACEFILE; struct ktr_header ktr_header; pid_t pid_opt = -1; +int noheader; +int utracelines; #define eqs(s1, s2) (strcmp((s1), (s2)) == 0) @@ -168,7 +170,7 @@ main(int argc, char *argv[]) screenwidth = 80; } - while ((ch = getopt(argc, argv, "f:dHlm:np:RTt:xX")) != -1) + while ((ch = getopt(argc, argv, "f:dhHlm:np:RTt:uxX")) != -1) switch (ch) { case 'f': tracefile = optarg; @@ -176,6 +178,9 @@ main(int argc, char *argv[]) case 'd': decimal = 1; break; + case 'h': + noheader = 1; + break; case 'H': needtid = 1; break; @@ -212,6 +217,9 @@ main(int argc, char *argv[]) if (trpoints < 0) errx(1, "unknown trace point in %s", optarg); break; + case 'u': + utracelines = 1; + break; case 'x': iohex = 1; break; @@ -246,7 +254,7 @@ main(int argc, char *argv[]) silent = 0; if (pid_opt != -1 && pid_opt != ktr_header.ktr_pid) silent = 1; - if (silent == 0 && trpoints & (1<<ktr_header.ktr_type)) + if (!noheader && silent == 0 && trpoints & (1<<ktr_header.ktr_type)) dumpheader(&ktr_header); ktrlen = ktr_header.ktr_len; if (ktrlen > size) { @@ -1258,6 +1266,10 @@ showbuf(unsigned char *dp, size_t datale int col = 0, bpl; unsigned char c; + if (utracelines) { + printf("%.*s", (int)datalen, dp); + return; + } if (iohex == 1) { putchar('\t'); col = 8; @@ -1413,8 +1425,10 @@ ktruser(struct ktr_user *usr, size_t len if (len < sizeof(struct ktr_user)) errx(1, "invalid ktr user length %zu", len); len -= sizeof(struct ktr_user); - printf("%.*s:", KTR_USER_MAXIDLEN, usr->ktr_id); - printf(" %zu bytes\n", len); + if (!utracelines) { + printf("%.*s:", KTR_USER_MAXIDLEN, usr->ktr_id); + printf(" %zu bytes\n", len); + } showbuf((unsigned char *)(usr + 1), len); }