xref: /freebsd/usr.sbin/pmc/cmd_pmc_info.cc (revision 3adde0e55d7c01018b0877e1407aede7e757ce04)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2026, Netflix, Inc.
5  *
6  * This software was developed by Ali Mashtizadeh under the sponsorship from
7  * Netflix, Inc.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  */
31 
32 #include <sys/param.h>
33 #include <sys/cpuset.h>
34 #include <sys/event.h>
35 #include <sys/queue.h>
36 #include <sys/socket.h>
37 #include <sys/stat.h>
38 #include <sys/sysctl.h>
39 #include <sys/time.h>
40 #include <sys/ttycom.h>
41 #include <sys/user.h>
42 #include <sys/wait.h>
43 
44 #include <assert.h>
45 #include <curses.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <getopt.h>
50 #include <kvm.h>
51 #include <libgen.h>
52 #include <limits.h>
53 #include <locale.h>
54 #include <math.h>
55 #include <pmc.h>
56 #include <pmclog.h>
57 #include <regex.h>
58 #include <signal.h>
59 #include <stdarg.h>
60 #include <stdint.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <stddef.h>
64 #include <string.h>
65 #include <sysexits.h>
66 #include <unistd.h>
67 
68 #include "cmd_pmc.h"
69 
70 #include <iostream>
71 #include <map>
72 #include <set>
73 #include <string>
74 #include <unordered_map>
75 #include <unordered_set>
76 #include <vector>
77 
78 #include "display.hh"
79 #include "view.hh"
80 
81 class info_view : public pmcview
82 {
83 public:
84 	virtual void
callchain(struct pmclog_ev_callchain & p)85 	callchain(struct pmclog_ev_callchain &p)
86 	{
87 		uint64_t eventid = pmcidtoeventid(p.pl_pmcid);
88 
89 		pmcinfo[eventid].count++;
90 	}
91 
92 	virtual void
print()93 	print()
94 	{
95 		title("PMC Info");
96 
97 		header("System Info");
98 		printf("CPU Model: %s\n", cpumodel.c_str());
99 		printf("OS Release: %s\n", osrelease.c_str());
100 		printf("Build ID: %s\n", buildid.c_str());
101 		printf("\n");
102 
103 		if (arch  == PMC_ARCH_AMD64) {
104 			header("CPUID");
105 			for (auto &c : cpuid)
106 				printf("0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
107 				    c.first, c.second.eax, c.second.ebx,
108 				    c.second.ecx, c.second.edx);
109 			printf("\n");
110 		}
111 
112 		header("PMC Info");
113 		for (auto &p : extpmcinfo)
114 			printf("%s\n", p.event.c_str());
115 		printf("\n");
116 
117 		table t = table();
118 		t.addcolumn("Counter", true);
119 		t.addcolumn("Counts", true);
120 
121 		for (auto &p : pmcinfo) {
122 			std::vector<field> r;
123 
124 			r.emplace_back(p.second.name);
125 			r.emplace_back((int64_t)p.second.count);
126 
127 			t.addrow(r);
128 		}
129 
130 		t.print();
131 	}
132 };
133 
134 static struct option longopts[] = {
135 	PMCFILTER_LOPTS,
136 	{ NULL,		0,			NULL,	0 }
137 };
138 
139 static void
usage(void)140 usage(void)
141 {
142 	printf("Usage: pmc info [options] [pmclog]\n\n");
143 	printf("Display a log summary\n\n");
144 	printf("Options:\n");
145 	PMCFILTER_PRINTOPTS();
146 }
147 
148 int
cmd_pmc_info(int argc,char ** argv)149 cmd_pmc_info(int argc, char **argv)
150 {
151 	struct pmcfilter filter = pmcfilter();
152 	const char *logfile = "default.log";
153 	int option, logfd;
154 
155 	while ((option = getopt_long(argc, argv, PMCFILTER_SOPTS "s:", longopts, NULL)) != -1) {
156 		switch (option) {
157 		PMCFILTER_CASE(filter);
158 		case '?':
159 		default:
160 			usage();
161 		}
162 	}
163 	argc -= optind;
164 	argv += optind;
165 	if (argc != 0 && argc != 1) {
166 		usage();
167 		exit(EX_USAGE);
168 	}
169 	if (argc == 1)
170 		logfile = argv[0];
171 
172 	setup_screen();
173 
174 	if ((logfd = open(logfile, O_RDONLY)) < 0) {
175 		errx(EX_OSERR, "ERROR: Cannot open \"%s\" for reading: %s.", logfile,
176 		    strerror(errno));
177 		return (EX_NOINPUT);
178 	}
179 
180 	info_view s = info_view();
181 	s.setfilter(filter);
182 	s.process(logfd);
183 	s.print();
184 
185 	return (EX_OK);
186 }
187