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