xref: /freebsd/usr.sbin/pmc/cmd_pmc_frontend.cc (revision cc94d1bd7414b309a62a379a8e614e3a3f9cd6d9)
1*cc94d1bdSAli Mashtizadeh /*-
2*cc94d1bdSAli Mashtizadeh  * SPDX-License-Identifier: BSD-2-Clause
3*cc94d1bdSAli Mashtizadeh  *
4*cc94d1bdSAli Mashtizadeh  * Copyright (c) 2026, Netflix, Inc.
5*cc94d1bdSAli Mashtizadeh  *
6*cc94d1bdSAli Mashtizadeh  * This software was developed by Ali Mashtizadeh under the sponsorship from
7*cc94d1bdSAli Mashtizadeh  * Netflix, Inc.
8*cc94d1bdSAli Mashtizadeh  *
9*cc94d1bdSAli Mashtizadeh  * Redistribution and use in source and binary forms, with or without
10*cc94d1bdSAli Mashtizadeh  * modification, are permitted provided that the following conditions
11*cc94d1bdSAli Mashtizadeh  * are met:
12*cc94d1bdSAli Mashtizadeh  * 1. Redistributions of source code must retain the above copyright
13*cc94d1bdSAli Mashtizadeh  *    notice, this list of conditions and the following disclaimer.
14*cc94d1bdSAli Mashtizadeh  * 2. Redistributions in binary form must reproduce the above copyright
15*cc94d1bdSAli Mashtizadeh  *    notice, this list of conditions and the following disclaimer in the
16*cc94d1bdSAli Mashtizadeh  *    documentation and/or other materials provided with the distribution.
17*cc94d1bdSAli Mashtizadeh  *
18*cc94d1bdSAli Mashtizadeh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19*cc94d1bdSAli Mashtizadeh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*cc94d1bdSAli Mashtizadeh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*cc94d1bdSAli Mashtizadeh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22*cc94d1bdSAli Mashtizadeh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23*cc94d1bdSAli Mashtizadeh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24*cc94d1bdSAli Mashtizadeh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25*cc94d1bdSAli Mashtizadeh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26*cc94d1bdSAli Mashtizadeh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27*cc94d1bdSAli Mashtizadeh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28*cc94d1bdSAli Mashtizadeh  * SUCH DAMAGE.
29*cc94d1bdSAli Mashtizadeh  *
30*cc94d1bdSAli Mashtizadeh  */
31*cc94d1bdSAli Mashtizadeh 
32*cc94d1bdSAli Mashtizadeh #include <sys/param.h>
33*cc94d1bdSAli Mashtizadeh #include <sys/cpuset.h>
34*cc94d1bdSAli Mashtizadeh #include <sys/event.h>
35*cc94d1bdSAli Mashtizadeh #include <sys/queue.h>
36*cc94d1bdSAli Mashtizadeh #include <sys/socket.h>
37*cc94d1bdSAli Mashtizadeh #include <sys/stat.h>
38*cc94d1bdSAli Mashtizadeh #include <sys/sysctl.h>
39*cc94d1bdSAli Mashtizadeh #include <sys/time.h>
40*cc94d1bdSAli Mashtizadeh #include <sys/ttycom.h>
41*cc94d1bdSAli Mashtizadeh #include <sys/user.h>
42*cc94d1bdSAli Mashtizadeh #include <sys/wait.h>
43*cc94d1bdSAli Mashtizadeh 
44*cc94d1bdSAli Mashtizadeh #include <assert.h>
45*cc94d1bdSAli Mashtizadeh #include <curses.h>
46*cc94d1bdSAli Mashtizadeh #include <err.h>
47*cc94d1bdSAli Mashtizadeh #include <errno.h>
48*cc94d1bdSAli Mashtizadeh #include <fcntl.h>
49*cc94d1bdSAli Mashtizadeh #include <getopt.h>
50*cc94d1bdSAli Mashtizadeh #include <kvm.h>
51*cc94d1bdSAli Mashtizadeh #include <libgen.h>
52*cc94d1bdSAli Mashtizadeh #include <limits.h>
53*cc94d1bdSAli Mashtizadeh #include <locale.h>
54*cc94d1bdSAli Mashtizadeh #include <math.h>
55*cc94d1bdSAli Mashtizadeh #include <pmc.h>
56*cc94d1bdSAli Mashtizadeh #include <pmclog.h>
57*cc94d1bdSAli Mashtizadeh #include <regex.h>
58*cc94d1bdSAli Mashtizadeh #include <signal.h>
59*cc94d1bdSAli Mashtizadeh #include <stdarg.h>
60*cc94d1bdSAli Mashtizadeh #include <stdint.h>
61*cc94d1bdSAli Mashtizadeh #include <stdio.h>
62*cc94d1bdSAli Mashtizadeh #include <stdlib.h>
63*cc94d1bdSAli Mashtizadeh #include <stddef.h>
64*cc94d1bdSAli Mashtizadeh #include <string.h>
65*cc94d1bdSAli Mashtizadeh #include <sysexits.h>
66*cc94d1bdSAli Mashtizadeh #include <unistd.h>
67*cc94d1bdSAli Mashtizadeh 
68*cc94d1bdSAli Mashtizadeh #include <libpmcstat.h>
69*cc94d1bdSAli Mashtizadeh #include "cmd_pmc.h"
70*cc94d1bdSAli Mashtizadeh 
71*cc94d1bdSAli Mashtizadeh #include <iostream>
72*cc94d1bdSAli Mashtizadeh #include <map>
73*cc94d1bdSAli Mashtizadeh #include <set>
74*cc94d1bdSAli Mashtizadeh #include <string>
75*cc94d1bdSAli Mashtizadeh #include <unordered_map>
76*cc94d1bdSAli Mashtizadeh #include <unordered_set>
77*cc94d1bdSAli Mashtizadeh #include <vector>
78*cc94d1bdSAli Mashtizadeh 
79*cc94d1bdSAli Mashtizadeh #include <dev/hwpmc/hwpmc_ibs.h>
80*cc94d1bdSAli Mashtizadeh #include "display.hh"
81*cc94d1bdSAli Mashtizadeh #include "view.hh"
82*cc94d1bdSAli Mashtizadeh 
83*cc94d1bdSAli Mashtizadeh struct frontend {
84*cc94d1bdSAli Mashtizadeh 	syminfo		func;
85*cc94d1bdSAli Mashtizadeh 	int64_t		ocmiss;
86*cc94d1bdSAli Mashtizadeh 	int64_t		l2miss;
87*cc94d1bdSAli Mashtizadeh 	int64_t		l3miss;
88*cc94d1bdSAli Mashtizadeh 	int64_t		l1tlbmiss;
89*cc94d1bdSAli Mashtizadeh 	int64_t		l2tlbmiss;
90*cc94d1bdSAli Mashtizadeh 	int64_t		latency;
91*cc94d1bdSAli Mashtizadeh 	int64_t		samples;
92*cc94d1bdSAli Mashtizadeh };
93*cc94d1bdSAli Mashtizadeh 
94*cc94d1bdSAli Mashtizadeh static int sortcol = 2;
95*cc94d1bdSAli Mashtizadeh 
96*cc94d1bdSAli Mashtizadeh class frontend_view : public pmcview
97*cc94d1bdSAli Mashtizadeh {
98*cc94d1bdSAli Mashtizadeh public:
frontend_view()99*cc94d1bdSAli Mashtizadeh 	frontend_view() : pmcview(), samples() { }
~frontend_view()100*cc94d1bdSAli Mashtizadeh 	~frontend_view() { }
101*cc94d1bdSAli Mashtizadeh 
102*cc94d1bdSAli Mashtizadeh 	virtual void
callchain(struct pmclog_ev_callchain & p,ibsfetchinfo & f,__unused uintfptr_t * cc,__unused int len)103*cc94d1bdSAli Mashtizadeh 	callchain(struct pmclog_ev_callchain &p,
104*cc94d1bdSAli Mashtizadeh 	    ibsfetchinfo &f, __unused uintfptr_t *cc, __unused int len)
105*cc94d1bdSAli Mashtizadeh 	{
106*cc94d1bdSAli Mashtizadeh 		int usermode = PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(p.pl_cpuflags);
107*cc94d1bdSAli Mashtizadeh 
108*cc94d1bdSAli Mashtizadeh 		/* Ignore zeros */
109*cc94d1bdSAli Mashtizadeh 		if (IBS_FETCH_CTL_TO_LAT(f.ctl) == 0)
110*cc94d1bdSAli Mashtizadeh 			return;
111*cc94d1bdSAli Mashtizadeh 
112*cc94d1bdSAli Mashtizadeh 		auto inst = samples.find(f.linaddr);
113*cc94d1bdSAli Mashtizadeh 		if (inst == samples.end()) {
114*cc94d1bdSAli Mashtizadeh 			syminfo sym = addrtosymbol(usermode ? p.pl_pid : 0, f.linaddr);
115*cc94d1bdSAli Mashtizadeh 			if (sym.name == "")
116*cc94d1bdSAli Mashtizadeh 				return;
117*cc94d1bdSAli Mashtizadeh 
118*cc94d1bdSAli Mashtizadeh 			samples[f.linaddr] = frontend();
119*cc94d1bdSAli Mashtizadeh 			inst = samples.find(f.linaddr);
120*cc94d1bdSAli Mashtizadeh 			inst->second.func = sym;
121*cc94d1bdSAli Mashtizadeh 		}
122*cc94d1bdSAli Mashtizadeh 
123*cc94d1bdSAli Mashtizadeh 		/*
124*cc94d1bdSAli Mashtizadeh 		 * Save fetch cache and TLB miss counts
125*cc94d1bdSAli Mashtizadeh 		 */
126*cc94d1bdSAli Mashtizadeh 		if (f.ctl & IBS_FETCH_CTL_OPCACHEMISS)
127*cc94d1bdSAli Mashtizadeh 			inst->second.ocmiss += 1;
128*cc94d1bdSAli Mashtizadeh 		if (f.ctl & IBS_FETCH_CTL_L2MISS)
129*cc94d1bdSAli Mashtizadeh 			inst->second.l2miss += 1;
130*cc94d1bdSAli Mashtizadeh 		if (f.ctl & IBS_FETCH_CTL_L3MISS)
131*cc94d1bdSAli Mashtizadeh 			inst->second.l3miss += 1;
132*cc94d1bdSAli Mashtizadeh 
133*cc94d1bdSAli Mashtizadeh 		if (f.ctl & IBS_FETCH_CTL_L1TLBMISS)
134*cc94d1bdSAli Mashtizadeh 			inst->second.l1tlbmiss += 1;
135*cc94d1bdSAli Mashtizadeh 		if (f.ctl & IBS_FETCH_CTL_L2TLBMISS)
136*cc94d1bdSAli Mashtizadeh 			inst->second.l2tlbmiss += 1;
137*cc94d1bdSAli Mashtizadeh 
138*cc94d1bdSAli Mashtizadeh 		inst->second.latency += IBS_FETCH_CTL_TO_LAT(f.ctl);
139*cc94d1bdSAli Mashtizadeh 		inst->second.samples += 1;
140*cc94d1bdSAli Mashtizadeh 	}
141*cc94d1bdSAli Mashtizadeh 
142*cc94d1bdSAli Mashtizadeh 	virtual void
print()143*cc94d1bdSAli Mashtizadeh 	print()
144*cc94d1bdSAli Mashtizadeh 	{
145*cc94d1bdSAli Mashtizadeh 		title("IBS Frontend Analysis");
146*cc94d1bdSAli Mashtizadeh 
147*cc94d1bdSAli Mashtizadeh 		// Print header
148*cc94d1bdSAli Mashtizadeh 		table t = table();
149*cc94d1bdSAli Mashtizadeh 		t.addcolumn("Image", true);
150*cc94d1bdSAli Mashtizadeh 		t.addcolumn("Function", true);
151*cc94d1bdSAli Mashtizadeh 		t.addcolumn("Latency");
152*cc94d1bdSAli Mashtizadeh 		t.addcolumn("Samples");
153*cc94d1bdSAli Mashtizadeh 		t.addcolumn("OC Miss");
154*cc94d1bdSAli Mashtizadeh 		t.addcolumn("L2 Miss");
155*cc94d1bdSAli Mashtizadeh 		t.addcolumn("L3 Miss");
156*cc94d1bdSAli Mashtizadeh 		t.addcolumn("L1 TLB Miss");
157*cc94d1bdSAli Mashtizadeh 		t.addcolumn("L2 TLB Miss");
158*cc94d1bdSAli Mashtizadeh 
159*cc94d1bdSAli Mashtizadeh 		for (auto &kv : samples) {
160*cc94d1bdSAli Mashtizadeh 			std::vector<field> r;
161*cc94d1bdSAli Mashtizadeh 
162*cc94d1bdSAli Mashtizadeh 			r.emplace_back(kv.second.func.binary);
163*cc94d1bdSAli Mashtizadeh 			r.emplace_back(kv.second.func.to_string());
164*cc94d1bdSAli Mashtizadeh 			r.emplace_back(kv.second.latency);
165*cc94d1bdSAli Mashtizadeh 			r.emplace_back(kv.second.samples);
166*cc94d1bdSAli Mashtizadeh 			r.emplace_back(kv.second.ocmiss, kv.second.samples, true);
167*cc94d1bdSAli Mashtizadeh 			r.emplace_back(kv.second.l2miss, kv.second.samples, true);
168*cc94d1bdSAli Mashtizadeh 			r.emplace_back(kv.second.l3miss, kv.second.samples, true);
169*cc94d1bdSAli Mashtizadeh 			r.emplace_back(kv.second.l1tlbmiss, kv.second.samples, true);
170*cc94d1bdSAli Mashtizadeh 			r.emplace_back(kv.second.l2tlbmiss, kv.second.samples, true);
171*cc94d1bdSAli Mashtizadeh 
172*cc94d1bdSAli Mashtizadeh 			t.addrow(r);
173*cc94d1bdSAli Mashtizadeh 		}
174*cc94d1bdSAli Mashtizadeh 
175*cc94d1bdSAli Mashtizadeh 		t.sort(sortcol);
176*cc94d1bdSAli Mashtizadeh 		t.print();
177*cc94d1bdSAli Mashtizadeh 	}
178*cc94d1bdSAli Mashtizadeh protected:
179*cc94d1bdSAli Mashtizadeh 	std::unordered_map<uint64_t, frontend> samples;
180*cc94d1bdSAli Mashtizadeh };
181*cc94d1bdSAli Mashtizadeh 
182*cc94d1bdSAli Mashtizadeh 
183*cc94d1bdSAli Mashtizadeh static struct option longopts[] = {
184*cc94d1bdSAli Mashtizadeh 	PMCFILTER_LOPTS,
185*cc94d1bdSAli Mashtizadeh 	{ "sort",	required_argument,	NULL,	's' },
186*cc94d1bdSAli Mashtizadeh 	{ NULL,		0,			NULL,	0 }
187*cc94d1bdSAli Mashtizadeh };
188*cc94d1bdSAli Mashtizadeh 
189*cc94d1bdSAli Mashtizadeh static void
usage(void)190*cc94d1bdSAli Mashtizadeh usage(void)
191*cc94d1bdSAli Mashtizadeh {
192*cc94d1bdSAli Mashtizadeh 	printf("Usage: pmc frontend [options] [pmclog]\n\n");
193*cc94d1bdSAli Mashtizadeh 	printf("Analyze frontend bottlenecks\n\n");
194*cc94d1bdSAli Mashtizadeh 	printf("Options:\n");
195*cc94d1bdSAli Mashtizadeh 	printf("\t-s,--sort         Sort by event\n\n");
196*cc94d1bdSAli Mashtizadeh 	PMCFILTER_PRINTOPTS();
197*cc94d1bdSAli Mashtizadeh }
198*cc94d1bdSAli Mashtizadeh 
199*cc94d1bdSAli Mashtizadeh int
cmd_pmc_frontend(int argc,char ** argv)200*cc94d1bdSAli Mashtizadeh cmd_pmc_frontend(int argc, char **argv)
201*cc94d1bdSAli Mashtizadeh {
202*cc94d1bdSAli Mashtizadeh 	pmcfilter filter = pmcfilter();
203*cc94d1bdSAli Mashtizadeh 	const char *logfile = "default.log";
204*cc94d1bdSAli Mashtizadeh 	int option, logfd;
205*cc94d1bdSAli Mashtizadeh 
206*cc94d1bdSAli Mashtizadeh 	while ((option = getopt_long(argc, argv, PMCFILTER_SOPTS, longopts, NULL)) != -1) {
207*cc94d1bdSAli Mashtizadeh 		switch (option) {
208*cc94d1bdSAli Mashtizadeh 		PMCFILTER_CASE(filter)
209*cc94d1bdSAli Mashtizadeh 		case 's':
210*cc94d1bdSAli Mashtizadeh 			sortcol = atoi(optarg);
211*cc94d1bdSAli Mashtizadeh 			break;
212*cc94d1bdSAli Mashtizadeh 		case '?':
213*cc94d1bdSAli Mashtizadeh 		default:
214*cc94d1bdSAli Mashtizadeh 			usage();
215*cc94d1bdSAli Mashtizadeh 			exit(EX_USAGE);
216*cc94d1bdSAli Mashtizadeh 		}
217*cc94d1bdSAli Mashtizadeh 	}
218*cc94d1bdSAli Mashtizadeh 	argc -= optind;
219*cc94d1bdSAli Mashtizadeh 	argv += optind;
220*cc94d1bdSAli Mashtizadeh 	if (argc != 0 && argc != 1) {
221*cc94d1bdSAli Mashtizadeh 		usage();
222*cc94d1bdSAli Mashtizadeh 		exit(EX_USAGE);
223*cc94d1bdSAli Mashtizadeh 	}
224*cc94d1bdSAli Mashtizadeh 	if (argc == 1)
225*cc94d1bdSAli Mashtizadeh 		logfile = argv[0];
226*cc94d1bdSAli Mashtizadeh 
227*cc94d1bdSAli Mashtizadeh 	setup_screen();
228*cc94d1bdSAli Mashtizadeh 
229*cc94d1bdSAli Mashtizadeh 	if ((logfd = open(logfile, O_RDONLY)) < 0) {
230*cc94d1bdSAli Mashtizadeh 		errx(EX_OSERR, "ERROR: Cannot open \"%s\" for reading: %s.", logfile,
231*cc94d1bdSAli Mashtizadeh 		    strerror(errno));
232*cc94d1bdSAli Mashtizadeh 	}
233*cc94d1bdSAli Mashtizadeh 
234*cc94d1bdSAli Mashtizadeh 	frontend_view v = frontend_view();
235*cc94d1bdSAli Mashtizadeh 	v.setfilter(filter);
236*cc94d1bdSAli Mashtizadeh 	v.process(logfd);
237*cc94d1bdSAli Mashtizadeh 	v.print();
238*cc94d1bdSAli Mashtizadeh 
239*cc94d1bdSAli Mashtizadeh 	close(logfd);
240*cc94d1bdSAli Mashtizadeh 
241*cc94d1bdSAli Mashtizadeh 	return (0);
242*cc94d1bdSAli Mashtizadeh }
243