git: de11299323e0 - main - pmcannotate: Add a '-m' mode option to control per-line annotations.

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Tue, 14 Jan 2025 15:39:57 UTC
The branch main has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=de11299323e0a647ae2164f74ca966862e7c4dd8

commit de11299323e0a647ae2164f74ca966862e7c4dd8
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2025-01-14 15:39:53 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2025-01-14 15:39:53 +0000

    pmcannotate: Add a '-m' mode option to control per-line annotations.
    
    - "block" (the default mode) displays per-block percentages in front of
       each annotated line.
    
    - "global" displays a global percentage in front of each line.
    
    - "raw" displays the raw count of sample hits in front of each line.
    
    Reviewed by:    Pau Amma <pauamma@gundo.com>, mav
    Sponsored by:   University of Cambridge, Google, Inc.
    Differential Revision:  https://reviews.freebsd.org/D35401
---
 usr.sbin/pmcannotate/pmcannotate.8 | 15 ++++++++++-
 usr.sbin/pmcannotate/pmcannotate.c | 54 ++++++++++++++++++++++++++++++++------
 2 files changed, 60 insertions(+), 9 deletions(-)

diff --git a/usr.sbin/pmcannotate/pmcannotate.8 b/usr.sbin/pmcannotate/pmcannotate.8
index 9a89c40db1e7..e2fae0a7a7e0 100644
--- 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 @@ This directory specifies where
 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
index 01af75090793..5958ff615e4b 100644
--- a/usr.sbin/pmcannotate/pmcannotate.c
+++ b/usr.sbin/pmcannotate/pmcannotate.c
@@ -34,6 +34,7 @@
 #include <sys/queue.h>
 
 #include <ctype.h>
+#include <err.h>
 #include <paths.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -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 @@ static TAILQ_HEAD(, aggent) fqueue = TAILQ_HEAD_INITIALIZER(fqueue);
  * 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 @@ general_insertent(struct entry *entry)
 	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 @@ general_printasm(FILE *fp, struct aggent *agg)
 		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 @@ printblock(FILE *fp, struct aggent *agg)
 			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 @@ usage(const char *progname)
 {
 
 	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 @@ main(int argc, char *argv[])
 	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 @@ main(int argc, char *argv[])
 		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: