On Sun, Apr 09, 2023 at 09:15:12AM +0200, Otto Moerbeek wrote: > On Sun, Apr 09, 2023 at 08:20:43AM +0200, Otto Moerbeek wrote: > > > On Sun, Apr 09, 2023 at 07:53:31AM +0200, Sebastien Marie wrote: > > > > > On Fri, Apr 07, 2023 at 09:52:52AM +0200, Otto Moerbeek wrote: > > > > > 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. > > > > > > I can't really comment on malloc(3) stuff, but I agree that utrace(2) is > > > a good > > > way to get information outside a pledged process. > > > > > > I tend to think it is safe to use it, as the pledged process need > > > cooperation > > > from outside to exfiltrate informations (a process with permission to > > > call > > > ktrace(2) on this pid). > > > > > > Please note it is a somehow generic problem: at least profiled processes > > > would > > > also get advantage of using it. > > > > > > > > > Regarding kdump options, I think that -u option should implies -h (no > > > header). > > > > > > Does it would make sens to considere a process using utrace(2) with > > > several > > > interleaved records for different sources ? A process with > > > MALLOC_OPTIONS=D and > > > profiling enabled for example ? An (another) option on kdump to filter on > > > utrace > > > label would be useful in such case, or have -u mandate a label to filter > > > on. > > > > > > $ MALLOC_OPTIONS=D ktrace -tu your_program > > > $ kdump -u mallocdumpline > > > > > > and for profiling: > > > > > > $ kdump -u profil > gmon.out > > > $ gprof your_program gmon.out > > > > > > Thanks. > > > > Thanks! Your suggestions make a lot of sense. I'll rework the kdump > > part to make it more flexable for different purposes. > > Anothew aspect of safety is the information availble in the heap > itself. I'm pretty sure the addresses of call sites into malloc are > interesting to attackers. To prevent a program having access to those > (even if they are stored inside the malloc meta data that is not > directly accesible to a program), I'm making sure the recording only > takes place if malloc option D is used. > > This is included in a diff with the kdump changes and a few other > things below. I'm also compiling with MALLOC_STATS if !SMALL. > > usage is now: > > $ MALLOC_OPTIONS=D ktrace -tu a.out > $ kdump -u malloc >
I would prefer if every utrace() call is a full line (in other words ulog should be line buffered). It makes the regular kdump output more useable. Right now you depend on kdump -u to put the lines back together. Whenever I used utrace() I normally passed binary objects to the call so I could enrich the ktrace with userland trace info. So if kdump -u is used for more then just mallocstats it should have a true binary mode. For example gmon.out is a binary format so the above example by semarie@ would not work. As usual this can be solved in tree once that problem is hit. > Index: lib/libc/stdlib/malloc.3 > =================================================================== > RCS file: /home/cvs/src/lib/libc/stdlib/malloc.3,v > retrieving revision 1.130 > diff -u -p -r1.130 malloc.3 > --- lib/libc/stdlib/malloc.3 1 Apr 2023 18:47:51 -0000 1.130 > +++ lib/libc/stdlib/malloc.3 9 Apr 2023 07:08:20 -0000 > @@ -284,10 +284,13 @@ If it has been corrupted, the process is > .It Cm D > .Dq Dump . > .Fn malloc > -will dump statistics to the file > -.Pa ./malloc.out , > -if it already exists, > +will dump statistics using > +.Xr utrace 2 > at exit. > +To record the dump: > +.Dl $ MALLOC_OPTIONS=D ktrace -tu program ... > +To view the dump: > +.Dl $ kdump -u malloc ... > This option requires the library to have been compiled with -DMALLOC_STATS in > order to have any effect. > .It Cm F > Index: lib/libc/stdlib/malloc.c > =================================================================== > RCS file: /home/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 9 Apr 2023 07:08:20 -0000 > @@ -1,6 +1,6 @@ > /* $OpenBSD: malloc.c,v 1.280 2023/04/05 06:25:38 otto Exp $ */ > /* > - * Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek <o...@drijf.net> > + * Copyright (c) 2008, 2010, 2011, 2016, 2023 Otto Moerbeek <o...@drijf.net> > * Copyright (c) 2012 Matthew Dempsky <matt...@openbsd.org> > * Copyright (c) 2008 Damien Miller <d...@openbsd.org> > * Copyright (c) 2000 Poul-Henning Kamp <p...@freebsd.org> > @@ -23,7 +23,9 @@ > * can buy me a beer in return. Poul-Henning Kamp > */ > > -/* #define MALLOC_STATS */ > +#ifndef SMALL > +#define MALLOC_STATS > +#endif > > #include <sys/types.h> > #include <sys/queue.h> > @@ -39,8 +41,10 @@ > #include <unistd.h> > > #ifdef MALLOC_STATS > +#include <sys/types.h> > #include <sys/tree.h> > -#include <fcntl.h> > +#include <sys/ktrace.h> > +#include <dlfcn.h> > #endif > > #include "thread_private.h" > @@ -225,10 +229,14 @@ struct malloc_readonly { > size_t malloc_guard; /* use guard pages after allocations? */ > #ifdef MALLOC_STATS > int malloc_stats; /* dump statistics at end */ > +#define DO_STATS mopts.malloc_stats > +#else > +#define DO_STATS 0 > #endif > u_int32_t malloc_canary; /* Matched against ones in pool */ > }; > > + > /* This object is mapped PROT_READ after initialisation to prevent tampering > */ > static union { > struct malloc_readonly mopts; > @@ -243,15 +251,11 @@ 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 > -#define CALLER NULL > #endif > +#define CALLER (DO_STATS ? __builtin_return_address(0) : NULL) > > /* low bits of r->p determine size: 0 means >= page size and r->size holding > * real size, otherwise low bits is the bucket + 1 > @@ -318,9 +322,9 @@ wrterror(struct dir_info *d, char *msg, > dprintf(STDERR_FILENO, "\n"); > > #ifdef MALLOC_STATS > - if (mopts.malloc_stats) > - malloc_gdump(STDERR_FILENO); > -#endif /* MALLOC_STATS */ > + if (DO_STATS) > + malloc_dump(); > +#endif > > errno = saved_errno; > > @@ -486,11 +490,11 @@ omalloc_init(void) > } > > #ifdef MALLOC_STATS > - if (mopts.malloc_stats && (atexit(malloc_exit) == -1)) { > + if (DO_STATS && (atexit(malloc_exit) == -1)) { > dprintf(STDERR_FILENO, "malloc() warning: atexit(2) failed." > " Will not be able to dump stats on exit\n"); > } > -#endif /* MALLOC_STATS */ > +#endif > > while ((mopts.malloc_canary = arc4random()) == 0) > ; > @@ -596,9 +600,7 @@ insert(struct dir_info *d, void *p, size > } > d->r[index].p = p; > d->r[index].size = sz; > -#ifdef MALLOC_STATS > - d->r[index].f = f; > -#endif > + STATS_SETF(&d->r[index], f); > d->regions_free--; > return 0; > } > @@ -1104,12 +1106,10 @@ malloc_bytes(struct dir_info *d, size_t > } > } > found: > -#ifdef MALLOC_STATS > - if (i == 0 && k == 0) { > + if (i == 0 && k == 0 && DO_STATS) { > struct region_info *r = find(d, bp->page); > - r->f = f; > + STATS_SETF(r, f); > } > -#endif > > *lp ^= 1 << k; > > @@ -1193,6 +1193,9 @@ free_bytes(struct dir_info *d, struct re > info = (struct chunk_info *)r->size; > chunknum = find_chunknum(d, info, ptr, 0); > > + if (chunknum == 0 && DO_STATS) > + STATS_SETF(r, NULL); > + > info->bits[chunknum / MALLOC_BITS] |= 1U << (chunknum % MALLOC_BITS); > info->free++; > > @@ -1214,7 +1217,7 @@ free_bytes(struct dir_info *d, struct re > unmap(d, info->page, MALLOC_PAGESIZE, 0); > > delete(d, r); > - mp = &d->chunk_info_list[info->bucket]; > + mp = &d->chunk_info_list[info->bucket]; > LIST_INSERT_HEAD(mp, info, entries); > } > > @@ -1694,9 +1697,11 @@ orealloc(struct dir_info **argpool, void > r = findpool(p, *argpool, &pool, &saved_function); > > REALSIZE(oldsz, r); > - if (mopts.chunk_canaries && oldsz <= MALLOC_MAXCHUNK) { > - info = (struct chunk_info *)r->size; > - chunknum = find_chunknum(pool, info, p, 0); > + if (oldsz <= MALLOC_MAXCHUNK) { > + if (DO_STATS || mopts.chunk_canaries) { > + info = (struct chunk_info *)r->size; > + chunknum = find_chunknum(pool, info, p, 0); > + } > } > > goldsz = oldsz; > @@ -1808,7 +1813,8 @@ orealloc(struct dir_info **argpool, void > info->bits[info->offset + chunknum] = newsz; > fill_canary(p, newsz, B2SIZE(info->bucket)); > } > - STATS_SETF(r, f); > + if (chunknum == 0 && DO_STATS) > + STATS_SETF(r, f); > ret = p; > } else if (newsz != oldsz || forced) { > /* create new allocation */ > @@ -1825,7 +1831,8 @@ orealloc(struct dir_info **argpool, void > /* oldsz == newsz */ > if (newsz != 0) > wrterror(pool, "realloc internal inconsistency"); > - STATS_SETF(r, f); > + if (chunknum == 0 && DO_STATS) > + STATS_SETF(r, f); > ret = p; > } > done: > @@ -2225,6 +2232,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("malloc", buf, len); > + va_end(ap); > +} > + > struct malloc_leak { > void *f; > size_t total_size; > @@ -2280,21 +2302,32 @@ 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, > - p->d.total_size, p->d.count, p->d.total_size / p->d.count); > + Dl_info info; > + const char *caller = p->d.f; > + const char *object = "?"; > + > + if (caller != NULL) { > + if (dladdr(p->d.f, &info) != 0) { > + caller -= (uintptr_t)info.dli_fbase; > + object = info.dli_fname; > + } > + } > + ulog("%18p %7zu %6u %6zu addr2line -e %s %p\n", p->d.f, > + p->d.total_size, p->d.count, p->d.total_size / p->d.count, > + object, caller); > if (malloc_leaks == MAP_FAILED || > i >= MALLOC_PAGESIZE / sizeof(struct malloc_leak)) > continue; > @@ -2306,10 +2339,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 +2358,19 @@ 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"); > + ulog("Bkt) #CI page" > + " f size free/n\n"); > for (i = 0; i <= BUCKETS; i++) { > count = 0; > LIST_FOREACH(p, &d->chunk_info_list[i], entries) > @@ -2345,99 +2380,98 @@ 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("Hash table:\n"); > + 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 +2485,40 @@ 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; > - unsigned i; > + int save_errno = errno; > > - 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.1 > =================================================================== > RCS file: /home/cvs/src/usr.bin/kdump/kdump.1,v > retrieving revision 1.35 > diff -u -p -r1.35 kdump.1 > --- usr.bin/kdump/kdump.1 30 Jul 2022 07:19:30 -0000 1.35 > +++ usr.bin/kdump/kdump.1 9 Apr 2023 07:08:20 -0000 > @@ -42,6 +42,7 @@ > .Op Fl m Ar maxdata > .Op Fl p Ar pid > .Op Fl t Ar trstr > +.Op Fl u Ar word > .Sh DESCRIPTION > .Nm > displays the kernel trace files produced with > @@ -106,6 +107,18 @@ See the > option of > .Xr ktrace 1 > for the meaning of the letters. > +.It Fl u Ar word > +Display > +.Xr utrace 2 > +records having > +.XR utrace 2 > +header > +.Ar word > +as strings with > +.Xr vis 3 > +escaping, without > +.Xr ktrace 2 > +header information. > .It Fl X > Display I/O data with hexadecimal data and printable ASCII characters > side by side. > Index: usr.bin/kdump/kdump.c > =================================================================== > RCS file: /home/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 9 Apr 2023 07:08:20 -0000 > @@ -88,6 +88,7 @@ int needtid, tail, basecol; > char *tracefile = DEF_TRACEFILE; > struct ktr_header ktr_header; > pid_t pid_opt = -1; > +char* utracefilter; > > #define eqs(s1, s2) (strcmp((s1), (s2)) == 0) > > @@ -168,7 +169,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:dHlm:np:RTt:u:xX")) != -1) > switch (ch) { > case 'f': > tracefile = optarg; > @@ -212,6 +213,9 @@ main(int argc, char *argv[]) > if (trpoints < 0) > errx(1, "unknown trace point in %s", optarg); > break; > + case 'u': > + utracefilter = optarg; > + break; > case 'x': > iohex = 1; > break; > @@ -246,7 +250,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 (utracefilter == NULL && silent == 0 && trpoints & > (1<<ktr_header.ktr_type)) > dumpheader(&ktr_header); > ktrlen = ktr_header.ktr_len; > if (ktrlen > size) { > @@ -1254,10 +1258,18 @@ showbufc(int col, unsigned char *dp, siz > static void > showbuf(unsigned char *dp, size_t datalen) > { > - int i, j; > + size_t i, j; > int col = 0, bpl; > unsigned char c; > + char visbuf[5]; > > + if (utracefilter != NULL) { > + for (i = 0; i < datalen; i++) { > + vis(visbuf, dp[i], VIS_SAFE | VIS_OCTAL, 0); > + printf("%s", visbuf); > + } > + return; > + } > if (iohex == 1) { > putchar('\t'); > col = 8; > @@ -1280,7 +1292,7 @@ showbuf(unsigned char *dp, size_t datale > if (bpl <= 0) > bpl = 1; > for (i = 0; i < datalen; i += bpl) { > - printf(" %04x: ", i); > + printf(" %04zx: ", i); > for (j = 0; j < bpl; j++) { > if (i+j >= datalen) > printf(" "); > @@ -1413,9 +1425,13 @@ 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); > - showbuf((unsigned char *)(usr + 1), len); > + if (utracefilter == NULL) { > + printf("%.*s:", KTR_USER_MAXIDLEN, usr->ktr_id); > + printf(" %zu bytes\n", len); > + showbuf((unsigned char *)(usr + 1), len); > + } > + else if (strncmp(usr->ktr_id, utracefilter, KTR_USER_MAXIDLEN) == 0) > + showbuf((unsigned char *)(usr + 1), len); > } > > static void > @@ -1473,8 +1489,8 @@ usage(void) > > extern char *__progname; > fprintf(stderr, "usage: %s " > - "[-dHlnRTXx] [-f file] [-m maxdata] [-p pid] [-t trstr]\n", > - __progname); > + "[-dHlnRTXx] [-f file] [-m maxdata] [-p pid] [-t trstr] " > + "[-u word]\n", __progname); > exit(1); > } > > > > -- :wq Claudio