diff --git a/sys/sys/gmon.h b/sys/sys/gmon.h index 79434a4888a9..90e4e228e9c3 100644 --- a/sys/sys/gmon.h +++ b/sys/sys/gmon.h @@ -48,8 +48,8 @@ struct gmonhdr { int ncnt; /* size of sample buffer (plus this header) */ int version; /* version number */ int profrate; /* profiling clock rate */ - int spare[3]; /* reserved */ - /* XXX should record counter size and density */ + int histcounter_type; /* size (in bits) and sign of HISTCOUNTER */ + int spare[2]; /* reserved */ }; #define GMONVERSION 0x00051879 @@ -180,6 +180,7 @@ struct gmonparam { int mexitcount_overhead; int mexitcount_post_overhead; int mexitcount_pre_overhead; + int histcounter_type; }; extern struct gmonparam _gmonparam; diff --git a/usr.sbin/kgmon/kgmon.c b/usr.sbin/kgmon/kgmon.c index 8ab42274a750..3ee5a737cee0 100644 --- a/usr.sbin/kgmon/kgmon.c +++ b/usr.sbin/kgmon/kgmon.c @@ -57,6 +57,7 @@ static const char rcsid[] = #include #include #include +#include #include #include #include @@ -272,9 +273,37 @@ getprof(kvp) if (sysctl(mib, 3, &kvp->gpm, &size, NULL, 0) < 0) size = 0; } - if (size != sizeof kvp->gpm) + + /* + * Accept certain undersized "structs" from old kernels. We need + * everything up to hashfraction, and want profrate and + * histcounter_type. Assume that the kernel doesn't put garbage + * in any padding that is returned instead of profrate and + * histcounter_type. This is a bad assumption for dead kernels, + * since kvm_read() will normally return garbage for bytes beyond + * the end of the actual kernel struct, if any. + */ + if (size < offsetof(struct gmonparam, hashfraction) + + sizeof(kvp->gpm.hashfraction) || size > sizeof(kvp->gpm)) errx(4, "cannot get gmonparam: %s", kflag ? kvm_geterr(kvp->kd) : strerror(errno)); + bzero((char *)&kvp->gpm + size, sizeof(kvp->gpm) - size); + if (kvp->gpm.profrate == 0) + kvp->gpm.profrate = getprofhz(kvp); +#ifdef __i386__ + if (kvp->gpm.histcounter_type == 0) { + /* + * This fixup only works for not-so-old i386 kernels. The + * magic 16 is the kernel FUNCTION_ALIGNMENT. 64-bit + * counters are signed; smaller counters are unsigned. + */ + kvp->gpm.histcounter_type = 16 / + (kvp->gpm.textsize / kvp->gpm.kcountsize) * CHAR_BIT; + if (kvp->gpm.histcounter_type == 64) + kvp->gpm.histcounter_type = -64; + } +#endif + return (kvp->gpm.state); } @@ -344,8 +373,7 @@ dumpstate(kvp) h.ncnt = kvp->gpm.kcountsize + sizeof(h); h.version = GMONVERSION; h.profrate = kvp->gpm.profrate; - if (h.profrate == 0) - h.profrate = getprofhz(kvp); /* ancient kernel */ + h.histcounter_type = kvp->gpm.histcounter_type; fwrite((char *)&h, sizeof(h), 1, fp); /*