xref: /freebsd/usr.sbin/pmc/view.hh (revision 880865783d9c3b807a26f4ea8ab3ef6ce1ca6ff8)
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 #ifndef __VIEW_HH__
32 #define __VIEW_HH__
33 
34 #include <libdwarf.h>
35 #include <libelf.h>
36 #include <libutil.h>
37 #include <gelf.h>
38 
39 #include <vector>
40 
41 #include "headers.hh"
42 #include "util.hh"
43 
44 /*
45  * PMC counter state.
46  */
47 struct pmcinfo
48 {
pmcinfopmcinfo49 	pmcinfo() : name(), rate(-1), event(0), count(0) { }
pmcinfopmcinfo50 	pmcinfo(struct pmclog_ev_pmcallocate &p) {
51 		name = p.pl_evname;
52 		rate = p.pl_rate;
53 		event = p.pl_event;
54 		count = 0;
55 	}
pmcinfopmcinfo56 	pmcinfo(struct pmclog_ev_pmcallocatedyn &p) {
57 		name = p.pl_evname;
58 		rate = 0;
59 		event = p.pl_event;
60 		count = 0;
61 	}
~pmcinfopmcinfo62 	~pmcinfo() { }
63 	std::string			name;
64 	int				rate;
65 	uint32_t			event;
66 	uint64_t			count; // Available for application use
67 };
68 
69 /*
70  * Extended pmcinfo structure stores the complete event description passed to
71  * libpmc.
72  */
73 struct pmcinfox
74 {
pmcinfoxpmcinfox75 	pmcinfox() : rate(0), event() { }
pmcinfoxpmcinfox76 	pmcinfox(uint32_t rate_, std::string event_) : rate(rate_), event(event_) { }
~pmcinfoxpmcinfox77 	~pmcinfox() { }
78 	uint32_t			rate;
79 	std::string			event;
80 };
81 
82 /*
83  * CPUID Leafs for X86 Logs
84  */
85 struct cpuidleaf
86 {
87 	uint32_t			eax;
88 	uint32_t			ebx;
89 	uint32_t			ecx;
90 	uint32_t			edx;
91 };
92 
93 /*
94  * Stores the address range for an executable segment in the process.
95  */
96 struct vmmap
97 {
vmmapvmmap98 	vmmap() : lowpc(0), highpc(0), image("") { }
~vmmapvmmap99 	~vmmap() { }
100 	uint64_t			lowpc;
101 	uint64_t			highpc;
102 	std::string			image;
103 };
104 
105 struct threadinfo
106 {
threadinfothreadinfo107 	threadinfo() : name("") { }
threadinfothreadinfo108 	threadinfo(const std::string &name_) : name(name_) { }
~threadinfothreadinfo109 	~threadinfo() { }
110 	std::string			name;
111 };
112 
113 struct procinfo
114 {
procinfoprocinfo115 	procinfo() : name(""), fullpath(""), map(), threads(),
116 	    baseaddr(0), dynaddr(0) { }
procinfoprocinfo117 	procinfo(struct pmclog_ev_proccreate &p) {
118 		std::string pname = p.pl_pcomm;
119 
120 		if ((p.pl_flags & P_KPROC) != 0)
121 			name = "[" + pname + "]";
122 		else
123 			name = pname;
124 		map = std::map<uint64_t, vmmap>();
125 		threads = std::map<pid_t, threadinfo>();
126 		baseaddr = 0;
127 		dynaddr = 0;
128 	}
~procinfoprocinfo129 	~procinfo() { }
130 	std::string			name;
131 	std::string			fullpath;
132 	std::map<uint64_t, vmmap>	map;
133 	std::map<pid_t, threadinfo>	threads;
134 	uint64_t			baseaddr;
135 	uint64_t			dynaddr;
136 };
137 
138 /*
139  * Symbol information
140  */
141 struct syminfo
142 {
syminfosyminfo143 	syminfo() : binary(""), name(""), offset(0), length(0), funcoff(0),
144 	    line(0), column(0) { }
~syminfosyminfo145 	~syminfo() { }
146 	std::string to_string(bool show_line = true);
147 	std::string			binary;
148 	std::string			name;
149 	uint64_t			offset;
150 	uint64_t			length;
151 	uint64_t			funcoff;
152 	uint32_t			line;
153 	uint32_t			column;
154 };
155 
156 struct image
157 {
imageimage158 	image() : isdynamic(false), vaddr(0), start(0), end(0),
159 	    offset(0), name(""), binary(""), path(""), loader(""),
160 	    dwarf(""), symbols(), dbg_fd(-1), dbg_elf(NULL) { }
~imageimage161 	~image() { }
162 	bool				isdynamic;
163 	uint64_t			vaddr;
164 	uint64_t			start;
165 	uint64_t			end;
166 	uint64_t			offset;
167 	std::string			name;
168 	std::string			binary;
169 	std::string			path;
170 	std::string			loader;
171 	std::string			dwarf;
172 	std::map<uint64_t, syminfo>	symbols;
173 	int				dbg_fd;
174 	Elf				*dbg_elf;
175 	Dwarf_Debug			dbg_dwarf;
176 };
177 
178 struct pmcfilter
179 {
pmcfilterpmcfilter180 	pmcfilter() : filterpid(false), filtertid(false),
181 	    filterprogram(false), filterthread(false),
182 	    filterevent(false), filtercpu(false),
183 	    kernelonly(false), useronly(false),
184 	    pids(), tids(), programs(), threads(),
185 	    ibs_ldlat(0), ibs_oplat(0) { CPU_ZERO(&cpus); }
~pmcfilterpmcfilter186 	~pmcfilter() { }
187 	bool				filterpid;
188 	bool				filtertid;
189 	bool				filterprogram;
190 	bool				filterthread;
191 	bool				filterevent;
192 	bool				filtercpu;
193 	bool				kernelonly;
194 	bool				useronly;
195 	std::unordered_set<int>		pids;
196 	std::unordered_set<int>		tids;
197 	std::unordered_set<std::string>	programs;
198 	std::unordered_set<std::string>	threads;
199 	std::unordered_set<std::string>	events;
200 	cpuset_t			cpus;
201 	/*
202 	 * Advanced filters for AMD IBS but should be generalized to support
203 	 * other processors.
204 	 */
205 	uint64_t			ibs_ldlat;
206 	uint64_t			ibs_oplat;
207 	bool				ibs_mmio;
addpidspmcfilter208 	void addpids(const char *ids) {
209 		split_and_insert(&pids, ids);
210 		filterpid = true;
211 	}
addtidspmcfilter212 	void addtids(const char *ids) {
213 		split_and_insert(&tids, ids);
214 		filtertid = true;
215 	}
addthreadspmcfilter216 	void addthreads(const char *ids) {
217 		split_and_insert(&threads, ids);
218 		filterthread = true;
219 	}
addprogramspmcfilter220 	void addprograms(const char *ids) {
221 		split_and_insert(&programs, ids);
222 		filterprogram = true;
223 	}
addeventspmcfilter224 	void addevents(const char *ids) {
225 		split_and_insert(&events, ids);
226 		filterevent = true;
227 	}
addcpuspmcfilter228 	void addcpus(const char *ids) {
229 		if (cpuset_parselist(ids, &cpus) == CPUSET_PARSE_OK)
230 			filtercpu = true;
231 		else
232 			fprintf(stderr, "Cannot parse cpulist");
233 	}
234 };
235 
236 /*
237  * These macros help provide a uniform filter facility across all passes.
238  * Filter options that are specific to a special feature, e.g., IBS, should
239  * only be accessible through long options, and we reserve option number 0-9
240  * for tool specific arguments.
241  */
242 #define PMCFILTER_PRINTOPTS()							\
243 	do {									\
244 		printf("Filter Options:\n");					\
245 		printf("\t-t,--lwps         Filter by thread id\n");		\
246 		printf("\t-p,--pids         Filter by process id\n");		\
247 		printf("\t-T,--threads      Filter by thread name\n");		\
248 		printf("\t-P,--processes    Filter by process name\n");		\
249 		printf("\t-e,--event        Filter by event\n");		\
250 		printf("\t-c,--cpus         Filter by CPU id\n");		\
251 		printf("\t-k,--kernel       Show only kernel events\n");	\
252 		printf("\t-u,--user         Show only user events\n");		\
253 		printf("\nIBS Fetch Options:\n");				\
254 		printf("\t--ldlat           Filter by load latency\n");		\
255 		printf("\nIBS Op Options:\n");					\
256 		printf("\t--ldlat           Filter by load latency\n");		\
257 		printf("\t--oplat           Filter by operation latency\n");	\
258 		printf("\t--mmio            Show only mmio events\n");		\
259 	} while (0)
260 #define PMCFILTER_LOPTS						\
261 	{ "lwps",	required_argument,	NULL,	't' },	\
262 	{ "pids",	required_argument,	NULL,	'p' },	\
263 	{ "threads",	required_argument,	NULL,	'T' },	\
264 	{ "processes",	required_argument,	NULL,	'P' },	\
265 	{ "events",	required_argument,	NULL,	'e' },	\
266 	{ "cpus",	required_argument,	NULL,	'c' },	\
267 	{ "kernel",	no_argument,		NULL,	'k' },	\
268 	{ "user",	no_argument,		NULL,	'u' },	\
269 	{ "ldlat",	required_argument,	NULL,	10  },	\
270 	{ "oplat",	required_argument,	NULL,	11  },	\
271 	{ "mmio",	no_argument,		NULL,	12  }
272 #define PMCFILTER_SOPTS "t:p:T:P:e:c:ku"
273 #define PMCFILTER_CASE(_filt) \
274 	case 't':						\
275 		_filt.addtids(optarg);				\
276 		break;						\
277 	case 'p':						\
278 		_filt.addpids(optarg);				\
279 		break;						\
280 	case 'T':						\
281 		_filt.addthreads(optarg);			\
282 		break;						\
283 	case 'P':						\
284 		_filt.addprograms(optarg);			\
285 		break;						\
286 	case 'e':						\
287 		_filt.addevents(optarg);			\
288 		break;						\
289 	case 'c':						\
290 		_filt.addcpus(optarg);				\
291 		break;						\
292 	case 'k':						\
293 		_filt.kernelonly = true;			\
294 		break;						\
295 	case 'u':						\
296 		_filt.useronly = true;			\
297 		break;						\
298 	case 10:						\
299 		_filt.ibs_ldlat = atoi(optarg);			\
300 		break;						\
301 	case 11:						\
302 		_filt.ibs_oplat = atoi(optarg);			\
303 		break;						\
304 	case 12:						\
305 		_filt.ibs_mmio = true;				\
306 		break;
307 
308 struct ibsfetchinfo
309 {
310 	int				len;
311 	uint64_t			ctl;
312 	uint64_t			extctl;
313 	uint64_t			linaddr;
314 	uint64_t			physaddr;
315 };
316 
317 struct ibsopinfo
318 {
319 	int				len;
320 	uint64_t			ctl;
321 	uint64_t			rip;
322 	uint64_t			data;
323 	uint64_t			data2;
324 	uint64_t			data3;
325 	uint64_t			linaddr;
326 	uint64_t			physaddr;
327 	uint64_t			tgtrip;
328 	uint64_t			data4;
329 };
330 
331 class pmcview
332 {
333 protected:
334 	pmcview();
335 	virtual ~pmcview();
336 public:
337 	// Filter
338 	void setfilter(const pmcfilter &pmcfilter);
339 	// View Hooks
proccreate(__unused pid_t pid)340 	virtual void proccreate(__unused pid_t pid) { };
procexec(__unused pid_t pid)341 	virtual void procexec(__unused pid_t pid) { };
procexit(__unused pid_t pid)342 	virtual void procexit(__unused pid_t pid) { };
callchain(struct pmclog_ev_callchain & p,__unused ibsfetchinfo & f,uintfptr_t * cc,int len)343 	virtual void callchain(struct pmclog_ev_callchain &p,
344 	    __unused ibsfetchinfo &f, uintfptr_t *cc, int len) {
345 		callchain(p, cc, len);
346 	}
callchain(struct pmclog_ev_callchain & p,__unused ibsopinfo & o,uintfptr_t * cc,int len)347 	virtual void callchain(struct pmclog_ev_callchain &p,
348 	    __unused ibsopinfo &o, uintfptr_t *cc, int len) {
349 		callchain(p, cc, len);
350 	}
callchain(struct pmclog_ev_callchain & p,__unused uintfptr_t * cc,__unused int len)351 	virtual void callchain(struct pmclog_ev_callchain &p,
352 	    __unused uintfptr_t *cc, __unused int len) {
353 		callchain(p);
354 	}
callchain(__unused struct pmclog_ev_callchain & p)355 	virtual void callchain(__unused struct pmclog_ev_callchain &p) { }
356 	// Console/File output
print()357 	virtual void print() { };
358 	virtual int process_sysinfo(int logfd, const pmchdr_infohdr &infohdr);
359 	virtual int process_pmcinfo(int logfd, const pmchdr_infohdr &infohdr);
360 	virtual int process_cpuidinfo(int logfd, const pmchdr_infohdr &infohdr);
361 	// Process log
362 	virtual void process(int logfd);
363 	virtual void process(struct pmclog_ev &p);
364 	virtual void process(struct pmclog_parse_state *ps);
365 	// Generic events
366 	virtual void process(struct pmclog_ev_initialize &p);
367 	virtual void process(struct pmclog_ev_closelog &p);
368 	virtual void process(struct pmclog_ev_pmcallocate &p);
369 	virtual void process(struct pmclog_ev_pmcallocatedyn &p);
370 	// Process events
371 	virtual void process(struct pmclog_ev_proccreate &p);
372 	virtual void process(struct pmclog_ev_procexit &p);
373 	virtual void process(struct pmclog_ev_procexec &p);
374 	virtual void process(struct pmclog_ev_procfork &p);
375 	virtual void process(struct pmclog_ev_sysexit &p);
376 	// Thread events
377 	virtual void process(struct pmclog_ev_threadcreate &p);
378 	virtual void process(struct pmclog_ev_threadexit &p);
379 	// Map events
380 	virtual void process(struct pmclog_ev_map_in &p);
381 	virtual void process(struct pmclog_ev_map_out &p);
382 	// Callchain events
383 	virtual void process(struct pmclog_ev_callchain &p);
384 protected:
385 	// Helper functions for views
386 	uint32_t pmcidtoeventid(uint32_t pmcid);
387 	std::string pidtoname(pid_t pid);
388 	syminfo addrtosymbol(pid_t pid, uint64_t addr);
389 	void printvm(pid_t pid);
390 	// Fields available to views
391 	uint64_t				tscfreq;
392 	std::unordered_map<uint32_t, uint32_t>	pmcid;
393 	std::unordered_map<uint32_t, struct pmcinfo> pmcinfo;
394 	std::unordered_map<pid_t, struct procinfo> procs;
395 	std::unordered_map<pid_t, pid_t>	tidtopid;
396 	std::unordered_map<std::string, image>	images;
397 	std::string				sysroot;
398 	pmcfilter				filter;
399 	// Extended info
400 	int					arch;
401 	std::string				cpumodel;
402 	std::string				osrelease;
403 	std::string				buildid;
404 	std::vector<struct pmcinfox>		extpmcinfo;
405 	std::map<uint32_t, struct cpuidleaf>	cpuid; // x86 Only
406 private:
407 	image loadimage(const std::string &path);
408 	void mapimage(pid_t pid, const image &im, uint64_t linkaddr);
409 	void loadsymboltable(image *im, Elf *e, Elf_Scn *scn, GElf_Shdr *sh);
410 	void loadsymbols(image *im);
411 };
412 
413 #endif /* __VIEW_HH__ */
414