diff --git a/usr.sbin/pmcannotate/pmcannotate.8 b/usr.sbin/pmcannotate/pmcannotate.8 --- a/usr.sbin/pmcannotate/pmcannotate.8 +++ b/usr.sbin/pmcannotate/pmcannotate.8 @@ -25,7 +25,7 @@ .\" out of the use of this software, even if advised of the possibility of .\" such damage. .\" -.Dd November 20, 2008 +.Dd January 14, 2025 .Dt PMCANNOTATE 8 .Os .Sh NAME @@ -37,6 +37,7 @@ .Op Fl h .Op Fl k Ar pathname .Op Fl l Ar level +.Op Fl m Ar mode .Ar pmcout.out binaryobj .Sh DESCRIPTION The @@ -84,6 +85,18 @@ should look for the kernel and its modules. The default is .Pa /boot/kernel . +.It Fl m Ar mode +Format to use when annotating source or assembly lines. +One of: +.Bl -tag -width indent +.It Cm block +Display percentage of matching samples relative to samples +falling in the current block. +.It Cm global +Display percentage of matching samples relative to all samples. +.It Cm raw +Display the raw count of matching samples. +.El .El .Sh LIMITATIONS As long as diff --git a/usr.sbin/pmcannotate/pmcannotate.c b/usr.sbin/pmcannotate/pmcannotate.c --- a/usr.sbin/pmcannotate/pmcannotate.c +++ b/usr.sbin/pmcannotate/pmcannotate.c @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -58,7 +59,7 @@ exit(EXIT_FAILURE); \ } while (0) -#define PERCSAMP(x) ((x) * 100 / totalsamples) +#define PERCSAMP(x) ((float)(x) * 100 / totalsamples) struct entry { TAILQ_ENTRY(entry) en_iter; @@ -108,7 +109,9 @@ * Use a float value in order to automatically promote operations * to return a float value rather than use casts. */ -static float totalsamples; +static u_int totalsamples; + +static enum { RAW, BLOCK_PERCENT, GLOBAL_PERCENT } print_mode; /* * Identifies a string cointaining objdump's assembly printout. @@ -500,6 +503,30 @@ TAILQ_INSERT_TAIL(&mainlst, entry, en_iter); } +/* + * Return a string either holding a percentage or the raw count value. + */ +static const char * +print_count(u_int nsamples, u_int totsamples) +{ + static char buf[16]; + + switch (print_mode) { + case RAW: + snprintf(buf, sizeof(buf), "%u", nsamples); + break; + case BLOCK_PERCENT: + snprintf(buf, sizeof(buf), "%.2f%%", (float)nsamples * 100 / + totsamples); + break; + case GLOBAL_PERCENT: + snprintf(buf, sizeof(buf), "%.2f%%", (float)nsamples * 100 / + totalsamples); + break; + } + return (buf); +} + /* * Printout the body of an "objdump -d" assembly function. * It does simply stops when a new function is encountered, @@ -528,8 +555,8 @@ if (obj == NULL) printf("\t| %s", buffer); else - printf("%.2f%%\t| %s", - (float)obj->en_nsamples * 100 / agg->ag_nsamples, + printf("%7s | %s", + print_count(obj->en_nsamples, agg->ag_nsamples), buffer); } } @@ -622,8 +649,8 @@ printf("\t| %s", buffer); else { done = 1; - printf("%.2f%%\t| %s", - (float)tnsamples * 100 / agg->ag_nsamples, buffer); + printf("%7s | %s", + print_count(tnsamples, agg->ag_nsamples), buffer); } } @@ -656,7 +683,7 @@ { fprintf(stderr, - "usage: %s [-a] [-h] [-k kfile] [-l lb] pmcraw.out binary\n", + "usage: %s [-a] [-h] [-k kfile] [-l lb] [-m mode] pmcraw.out binary\n", progname); exit(EXIT_SUCCESS); } @@ -681,7 +708,8 @@ kfile = NULL; asmsrc = 0; limit = 0.5; - while ((cget = getopt(argc, argv, "ahl:k:")) != -1) + print_mode = BLOCK_PERCENT; + while ((cget = getopt(argc, argv, "ahl:m:k:")) != -1) switch(cget) { case 'a': asmsrc = 1; @@ -692,6 +720,16 @@ case 'l': limit = (float)atof(optarg); break; + case 'm': + if (strcasecmp(optarg, "raw") == 0) + print_mode = RAW; + else if (strcasecmp(optarg, "global") == 0) + print_mode = GLOBAL_PERCENT; + else if (strcasecmp(optarg, "block") == 0) + print_mode = BLOCK_PERCENT; + else + errx(1, "Invalid mode %s", optarg); + break; case 'h': case '?': default: