#include #include #include #include #include extern const char *__progname; typedef struct f F; struct f { const char *fn; FILE *fp; struct ktr_header h; int ateof; } ; static F *fv; static int nfv; /* * This is gross. * * In 1.4T, the ktrace record header looks like * * struct ktr_header { * int ktr_len; * short ktr_type; * pid_t ktr_pid; * char ktr_comm[MAXCOMLEN+1]; * struct timeval ktr_time; * caddr_t ktr_buf; * }; * * In 4.0.1, it looks like * * struct ktr_header { * int ktr_len; * #if BYTE_ORDER == LITTLE_ENDIAN * short ktr_type; * short ktr_version; * #else * short ktr_version; * short ktr_type; * #endif * pid_t ktr_pid; * char ktr_comm[MAXCOMLEN+1]; * union { * struct timeval _tv; * struct timespec _ts; * } _ktr_time; * union { * const void *_buf; * lwpid_t _lid; * } _ktr_id; * }; * * and there are #defines for things like ktr_time. ktr_time is * defined to refer to the timespec, not the timeval, which is both * bad (because they broke source-code compatability) and good * (because it means that we at least get some kind of warning that * we're up against a silent API change). * * The really annoying thing is that there doesn't seem to be any * preprocessor define for telling which API version we're faced with, * making it impossible to just compile the correct ode for dealing * with whichever variant we've got. Looking over the include files, * the least gross way seems to me to be to use the KTRFACv* macros. * If KTRFACv0 is not defined, we assume we've got the old way; * otherwise, we assume we have the new way. If KTRFACv2 is defined, * error, because it means something new has shown up. */ #ifdef KTRFACv2 #error ktrace header code needs updating #endif #ifndef KTRFACv0 /* Unfortunately, no good way to avoid tripping on versioned records. */ #define TIME_IS_LESS(r1,r2)\ ( ((r1).ktr_time.tv_sec < (r2).ktr_time.tv_sec) || \ ( ((r1).ktr_time.tv_sec == (r2).ktr_time.tv_sec) && \ ((r1).ktr_time.tv_usec < (r2).ktr_time.tv_usec) ) ) #define CHECK_HEADER(x) do; while (0) #else #define TIME_IS_LESS(r1,r2) time_is_less(&(r1),&(r2)) #define CHECK_HEADER(x) check_header(&(x)) static int time_is_less(struct ktr_header *h1, struct ktr_header *h2) { switch (h1->ktr_version) { case 0: switch (h2->ktr_version) { case 0: return( (h1->ktr_tv.tv_sec < h2->ktr_tv.tv_sec) || ( (h1->ktr_tv.tv_sec == h2->ktr_tv.tv_sec) && (h1->ktr_tv.tv_usec < h2->ktr_tv.tv_usec) ) ); break; case 1: return( (h1->ktr_tv.tv_sec < h2->ktr_ts.tv_sec) || ( (h1->ktr_tv.tv_sec == h2->ktr_ts.tv_sec) && ((h1->ktr_tv.tv_usec*1000) < h2->ktr_ts.tv_nsec) ) ); break; default: abort(); break; } break; case 1: switch (h2->ktr_version) { case 0: return( (h1->ktr_ts.tv_sec < h2->ktr_tv.tv_sec) || ( (h1->ktr_ts.tv_sec == h2->ktr_tv.tv_sec) && (h1->ktr_ts.tv_nsec < (h2->ktr_tv.tv_usec*1000)) ) ); break; case 1: return( (h1->ktr_ts.tv_sec < h2->ktr_ts.tv_sec) || ( (h1->ktr_ts.tv_sec == h2->ktr_ts.tv_sec) && (h1->ktr_ts.tv_nsec < h2->ktr_ts.tv_nsec) ) ); break; default: abort(); break; } break; default: abort(); break; } } static void check_header(const struct ktr_header *h) { switch (h->ktr_version) { case 0: case 1: break; default: fprintf(stderr,"%s: unsupported ktrace record version %d\n",__progname,h->ktr_version); exit(1); break; } } #endif static void read_next_header(F *f) { int n; n = fread(&f->h,1,sizeof(f->h),f->fp); if (n == sizeof(f->h)) return; f->ateof = 1; CHECK_HEADER(f->h); if (n > 0) fprintf(stderr,"%s: %s: truncated record\n",__progname,f->fn); } static void init(int ac, char **av) { int errs; int i; F *f; ac --; av ++; if (ac < 1) { fprintf(stderr,"Usage: %s file ...\n",__progname); exit(1); } nfv = ac; errs = 0; fv = malloc(nfv*sizeof(F)); for (i=0;ifn = av[i]; f->fp = fopen(f->fn,"r"); if (f->fp == 0) { fprintf(stderr,"%s: %s; %s\n",__progname,f->fn,strerror(errno)); errs ++; } f->ateof = 0; } if (errs) exit(1); for (i=0;i=0;i--) { f = &fv[i]; if (f->ateof) continue; if (!best || TIME_IS_LESS(f->h,best->h)) { best = f; } } if (! best) return(0); if (best->h.ktr_len > buflen) { free(buf); buf = malloc(buflen=best->h.ktr_len); if (buf == 0) { fprintf(stderr,"%s: can't allocate record buffer (size=%d)\n",__progname,buflen); exit(1); } } i = fread(buf,1,best->h.ktr_len,best->fp); if (i != best->h.ktr_len) { fprintf(stderr,"%s: %s: truncated record\n",__progname,best->fn); best->ateof = 1; return(1); } fwrite(&best->h,sizeof(best->h),1,stdout); fwrite(buf,1,best->h.ktr_len,stdout); read_next_header(best); return(1); } int main(int, char **); int main(int ac, char **av) { init(ac,av); while (copyone()) ; exit(0); }