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