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 <libpmcstat.h>
69 #include "cmd_pmc.h"
70
71 #include <iostream>
72 #include <map>
73 #include <set>
74 #include <string>
75 #include <unordered_map>
76 #include <unordered_set>
77 #include <vector>
78
79 #include <dev/hwpmc/hwpmc_ibs.h>
80 #include "display.hh"
81 #include "view.hh"
82
83 struct frontend {
84 syminfo func;
85 int64_t ocmiss;
86 int64_t l2miss;
87 int64_t l3miss;
88 int64_t l1tlbmiss;
89 int64_t l2tlbmiss;
90 int64_t latency;
91 int64_t samples;
92 };
93
94 static int sortcol = 2;
95
96 class frontend_view : public pmcview
97 {
98 public:
frontend_view()99 frontend_view() : pmcview(), samples() { }
~frontend_view()100 ~frontend_view() { }
101
102 virtual void
callchain(struct pmclog_ev_callchain & p,ibsfetchinfo & f,__unused uintfptr_t * cc,__unused int len)103 callchain(struct pmclog_ev_callchain &p,
104 ibsfetchinfo &f, __unused uintfptr_t *cc, __unused int len)
105 {
106 int usermode = PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(p.pl_cpuflags);
107
108 /* Ignore zeros */
109 if (IBS_FETCH_CTL_TO_LAT(f.ctl) == 0)
110 return;
111
112 auto inst = samples.find(f.linaddr);
113 if (inst == samples.end()) {
114 syminfo sym = addrtosymbol(usermode ? p.pl_pid : 0, f.linaddr);
115 if (sym.name == "")
116 return;
117
118 samples[f.linaddr] = frontend();
119 inst = samples.find(f.linaddr);
120 inst->second.func = sym;
121 }
122
123 /*
124 * Save fetch cache and TLB miss counts
125 */
126 if (f.ctl & IBS_FETCH_CTL_OPCACHEMISS)
127 inst->second.ocmiss += 1;
128 if (f.ctl & IBS_FETCH_CTL_L2MISS)
129 inst->second.l2miss += 1;
130 if (f.ctl & IBS_FETCH_CTL_L3MISS)
131 inst->second.l3miss += 1;
132
133 if (f.ctl & IBS_FETCH_CTL_L1TLBMISS)
134 inst->second.l1tlbmiss += 1;
135 if (f.ctl & IBS_FETCH_CTL_L2TLBMISS)
136 inst->second.l2tlbmiss += 1;
137
138 inst->second.latency += IBS_FETCH_CTL_TO_LAT(f.ctl);
139 inst->second.samples += 1;
140 }
141
142 virtual void
print()143 print()
144 {
145 title("IBS Frontend Analysis");
146
147 // Print header
148 table t = table();
149 t.addcolumn("Image", true);
150 t.addcolumn("Function", true);
151 t.addcolumn("Latency");
152 t.addcolumn("Samples");
153 t.addcolumn("OC Miss");
154 t.addcolumn("L2 Miss");
155 t.addcolumn("L3 Miss");
156 t.addcolumn("L1 TLB Miss");
157 t.addcolumn("L2 TLB Miss");
158
159 for (auto &kv : samples) {
160 std::vector<field> r;
161
162 r.emplace_back(kv.second.func.binary);
163 r.emplace_back(kv.second.func.to_string());
164 r.emplace_back(kv.second.latency);
165 r.emplace_back(kv.second.samples);
166 r.emplace_back(kv.second.ocmiss, kv.second.samples, true);
167 r.emplace_back(kv.second.l2miss, kv.second.samples, true);
168 r.emplace_back(kv.second.l3miss, kv.second.samples, true);
169 r.emplace_back(kv.second.l1tlbmiss, kv.second.samples, true);
170 r.emplace_back(kv.second.l2tlbmiss, kv.second.samples, true);
171
172 t.addrow(r);
173 }
174
175 t.sort(sortcol);
176 t.print();
177 }
178 protected:
179 std::unordered_map<uint64_t, frontend> samples;
180 };
181
182
183 static struct option longopts[] = {
184 PMCFILTER_LOPTS,
185 { "sort", required_argument, NULL, 's' },
186 { NULL, 0, NULL, 0 }
187 };
188
189 static void
usage(void)190 usage(void)
191 {
192 printf("Usage: pmc frontend [options] [pmclog]\n\n");
193 printf("Analyze frontend bottlenecks\n\n");
194 printf("Options:\n");
195 printf("\t-s,--sort Sort by event\n\n");
196 PMCFILTER_PRINTOPTS();
197 }
198
199 int
cmd_pmc_frontend(int argc,char ** argv)200 cmd_pmc_frontend(int argc, char **argv)
201 {
202 pmcfilter filter = pmcfilter();
203 const char *logfile = "default.log";
204 int option, logfd;
205
206 while ((option = getopt_long(argc, argv, PMCFILTER_SOPTS, longopts, NULL)) != -1) {
207 switch (option) {
208 PMCFILTER_CASE(filter)
209 case 's':
210 sortcol = atoi(optarg);
211 break;
212 case '?':
213 default:
214 usage();
215 exit(EX_USAGE);
216 }
217 }
218 argc -= optind;
219 argv += optind;
220 if (argc != 0 && argc != 1) {
221 usage();
222 exit(EX_USAGE);
223 }
224 if (argc == 1)
225 logfile = argv[0];
226
227 setup_screen();
228
229 if ((logfd = open(logfile, O_RDONLY)) < 0) {
230 errx(EX_OSERR, "ERROR: Cannot open \"%s\" for reading: %s.", logfile,
231 strerror(errno));
232 }
233
234 frontend_view v = frontend_view();
235 v.setfilter(filter);
236 v.process(logfd);
237 v.print();
238
239 close(logfd);
240
241 return (0);
242 }
243