465 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <dirent.h>3#include <errno.h>4#include <stdio.h>5#include <stdlib.h>6#include <string.h>7#include <fcntl.h>8#include <sys/param.h>9#include <unistd.h>10 11#include <api/fs/tracing_path.h>12#include <api/io.h>13#include <linux/stddef.h>14#include <linux/perf_event.h>15#include <linux/zalloc.h>16#include <subcmd/pager.h>17 18#include "build-id.h"19#include "debug.h"20#include "evsel.h"21#include "metricgroup.h"22#include "parse-events.h"23#include "pmu.h"24#include "pmus.h"25#include "print-events.h"26#include "probe-file.h"27#include "string2.h"28#include "strlist.h"29#include "tracepoint.h"30#include "pfm.h"31#include "thread_map.h"32#include "util.h"33 34#define MAX_NAME_LEN 10035 36/** Strings corresponding to enum perf_type_id. */37static const char * const event_type_descriptors[] = {38 "Hardware event",39 "Software event",40 "Tracepoint event",41 "Hardware cache event",42 "Raw event descriptor",43 "Hardware breakpoint",44};45 46static const struct event_symbol event_symbols_tool[PERF_TOOL_MAX] = {47 [PERF_TOOL_DURATION_TIME] = {48 .symbol = "duration_time",49 .alias = "",50 },51 [PERF_TOOL_USER_TIME] = {52 .symbol = "user_time",53 .alias = "",54 },55 [PERF_TOOL_SYSTEM_TIME] = {56 .symbol = "system_time",57 .alias = "",58 },59};60 61/*62 * Print the events from <debugfs_mount_point>/tracing/events63 */64void print_tracepoint_events(const struct print_callbacks *print_cb __maybe_unused, void *print_state __maybe_unused)65{66 char *events_path = get_tracing_file("events");67 int events_fd = open(events_path, O_PATH);68 struct dirent **sys_namelist = NULL;69 int sys_items;70 71 if (events_fd < 0) {72 pr_err("Error: failed to open tracing events directory\n");73 pr_err("%s: %s\n", events_path, strerror(errno));74 return;75 }76 put_tracing_file(events_path);77 78 sys_items = tracing_events__scandir_alphasort(&sys_namelist);79 80 for (int i = 0; i < sys_items; i++) {81 struct dirent *sys_dirent = sys_namelist[i];82 struct dirent **evt_namelist = NULL;83 int dir_fd;84 int evt_items;85 86 if (sys_dirent->d_type != DT_DIR ||87 !strcmp(sys_dirent->d_name, ".") ||88 !strcmp(sys_dirent->d_name, ".."))89 goto next_sys;90 91 dir_fd = openat(events_fd, sys_dirent->d_name, O_PATH);92 if (dir_fd < 0)93 goto next_sys;94 95 evt_items = scandirat(events_fd, sys_dirent->d_name, &evt_namelist, NULL, alphasort);96 for (int j = 0; j < evt_items; j++) {97 /*98 * Buffer sized at twice the max filename length + 199 * separator + 1 \0 terminator.100 */101 char buf[NAME_MAX * 2 + 2];102 /* 16 possible hex digits and 22 other characters and \0. */103 char encoding[16 + 22];104 struct dirent *evt_dirent = evt_namelist[j];105 struct io id;106 __u64 config;107 108 if (evt_dirent->d_type != DT_DIR ||109 !strcmp(evt_dirent->d_name, ".") ||110 !strcmp(evt_dirent->d_name, ".."))111 goto next_evt;112 113 snprintf(buf, sizeof(buf), "%s/id", evt_dirent->d_name);114 io__init(&id, openat(dir_fd, buf, O_RDONLY), buf, sizeof(buf));115 116 if (id.fd < 0)117 goto next_evt;118 119 if (io__get_dec(&id, &config) < 0) {120 close(id.fd);121 goto next_evt;122 }123 close(id.fd);124 125 snprintf(buf, sizeof(buf), "%s:%s",126 sys_dirent->d_name, evt_dirent->d_name);127 snprintf(encoding, sizeof(encoding), "tracepoint/config=0x%llx/", config);128 print_cb->print_event(print_state,129 /*topic=*/NULL,130 /*pmu_name=*/NULL, /* really "tracepoint" */131 /*event_name=*/buf,132 /*event_alias=*/NULL,133 /*scale_unit=*/NULL,134 /*deprecated=*/false,135 "Tracepoint event",136 /*desc=*/NULL,137 /*long_desc=*/NULL,138 encoding);139next_evt:140 free(evt_namelist[j]);141 }142 close(dir_fd);143 free(evt_namelist);144next_sys:145 free(sys_namelist[i]);146 }147 148 free(sys_namelist);149 close(events_fd);150}151 152void print_sdt_events(const struct print_callbacks *print_cb, void *print_state)153{154 struct strlist *bidlist, *sdtlist;155 struct str_node *bid_nd, *sdt_name, *next_sdt_name;156 const char *last_sdt_name = NULL;157 158 /*159 * The implicitly sorted sdtlist will hold the tracepoint name followed160 * by @<buildid>. If the tracepoint name is unique (determined by161 * looking at the adjacent nodes) the @<buildid> is dropped otherwise162 * the executable path and buildid are added to the name.163 */164 sdtlist = strlist__new(NULL, NULL);165 if (!sdtlist) {166 pr_debug("Failed to allocate new strlist for SDT\n");167 return;168 }169 bidlist = build_id_cache__list_all(true);170 if (!bidlist) {171 pr_debug("Failed to get buildids: %d\n", errno);172 return;173 }174 strlist__for_each_entry(bid_nd, bidlist) {175 struct probe_cache *pcache;176 struct probe_cache_entry *ent;177 178 pcache = probe_cache__new(bid_nd->s, NULL);179 if (!pcache)180 continue;181 list_for_each_entry(ent, &pcache->entries, node) {182 char buf[1024];183 184 snprintf(buf, sizeof(buf), "%s:%s@%s",185 ent->pev.group, ent->pev.event, bid_nd->s);186 strlist__add(sdtlist, buf);187 }188 probe_cache__delete(pcache);189 }190 strlist__delete(bidlist);191 192 strlist__for_each_entry(sdt_name, sdtlist) {193 bool show_detail = false;194 char *bid = strchr(sdt_name->s, '@');195 char *evt_name = NULL;196 197 if (bid)198 *(bid++) = '\0';199 200 if (last_sdt_name && !strcmp(last_sdt_name, sdt_name->s)) {201 show_detail = true;202 } else {203 next_sdt_name = strlist__next(sdt_name);204 if (next_sdt_name) {205 char *bid2 = strchr(next_sdt_name->s, '@');206 207 if (bid2)208 *bid2 = '\0';209 if (strcmp(sdt_name->s, next_sdt_name->s) == 0)210 show_detail = true;211 if (bid2)212 *bid2 = '@';213 }214 }215 last_sdt_name = sdt_name->s;216 217 if (show_detail) {218 char *path = build_id_cache__origname(bid);219 220 if (path) {221 if (asprintf(&evt_name, "%s@%s(%.12s)", sdt_name->s, path, bid) < 0)222 evt_name = NULL;223 free(path);224 }225 }226 print_cb->print_event(print_state,227 /*topic=*/NULL,228 /*pmu_name=*/NULL,229 evt_name ?: sdt_name->s,230 /*event_alias=*/NULL,231 /*deprecated=*/false,232 /*scale_unit=*/NULL,233 "SDT event",234 /*desc=*/NULL,235 /*long_desc=*/NULL,236 /*encoding_desc=*/NULL);237 238 free(evt_name);239 }240 strlist__delete(sdtlist);241}242 243bool is_event_supported(u8 type, u64 config)244{245 bool ret = true;246 struct evsel *evsel;247 struct perf_event_attr attr = {248 .type = type,249 .config = config,250 .disabled = 1,251 };252 struct perf_thread_map *tmap = thread_map__new_by_tid(0);253 254 if (tmap == NULL)255 return false;256 257 evsel = evsel__new(&attr);258 if (evsel) {259 ret = evsel__open(evsel, NULL, tmap) >= 0;260 261 if (!ret) {262 /*263 * The event may fail to open if the paranoid value264 * /proc/sys/kernel/perf_event_paranoid is set to 2265 * Re-run with exclude_kernel set; we don't do that by266 * default as some ARM machines do not support it.267 */268 evsel->core.attr.exclude_kernel = 1;269 ret = evsel__open(evsel, NULL, tmap) >= 0;270 }271 272 if (!ret) {273 /*274 * The event may fail to open if the PMU requires275 * exclude_guest to be set (e.g. as the Apple M1 PMU276 * requires).277 * Re-run with exclude_guest set; we don't do that by278 * default as it's equally legitimate for another PMU279 * driver to require that exclude_guest is clear.280 */281 evsel->core.attr.exclude_guest = 1;282 ret = evsel__open(evsel, NULL, tmap) >= 0;283 }284 285 evsel__delete(evsel);286 }287 288 perf_thread_map__put(tmap);289 return ret;290}291 292int print_hwcache_events(const struct print_callbacks *print_cb, void *print_state)293{294 struct perf_pmu *pmu = NULL;295 const char *event_type_descriptor = event_type_descriptors[PERF_TYPE_HW_CACHE];296 297 /*298 * Only print core PMUs, skipping uncore for performance and299 * PERF_TYPE_SOFTWARE that can succeed in opening legacy cache evenst.300 */301 while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {302 if (pmu->is_uncore || pmu->type == PERF_TYPE_SOFTWARE)303 continue;304 305 for (int type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {306 for (int op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {307 /* skip invalid cache type */308 if (!evsel__is_cache_op_valid(type, op))309 continue;310 311 for (int res = 0; res < PERF_COUNT_HW_CACHE_RESULT_MAX; res++) {312 char name[64];313 char alias_name[128];314 __u64 config;315 int ret;316 317 __evsel__hw_cache_type_op_res_name(type, op, res,318 name, sizeof(name));319 320 ret = parse_events__decode_legacy_cache(name, pmu->type,321 &config);322 if (ret || !is_event_supported(PERF_TYPE_HW_CACHE, config))323 continue;324 snprintf(alias_name, sizeof(alias_name), "%s/%s/",325 pmu->name, name);326 print_cb->print_event(print_state,327 "cache",328 pmu->name,329 name,330 alias_name,331 /*scale_unit=*/NULL,332 /*deprecated=*/false,333 event_type_descriptor,334 /*desc=*/NULL,335 /*long_desc=*/NULL,336 /*encoding_desc=*/NULL);337 }338 }339 }340 }341 return 0;342}343 344void print_tool_events(const struct print_callbacks *print_cb, void *print_state)345{346 // Start at 1 because the first enum entry means no tool event.347 for (int i = 1; i < PERF_TOOL_MAX; ++i) {348 print_cb->print_event(print_state,349 "tool",350 /*pmu_name=*/NULL,351 event_symbols_tool[i].symbol,352 event_symbols_tool[i].alias,353 /*scale_unit=*/NULL,354 /*deprecated=*/false,355 "Tool event",356 /*desc=*/NULL,357 /*long_desc=*/NULL,358 /*encoding_desc=*/NULL);359 }360}361 362void print_symbol_events(const struct print_callbacks *print_cb, void *print_state,363 unsigned int type, const struct event_symbol *syms,364 unsigned int max)365{366 struct strlist *evt_name_list = strlist__new(NULL, NULL);367 struct str_node *nd;368 369 if (!evt_name_list) {370 pr_debug("Failed to allocate new strlist for symbol events\n");371 return;372 }373 for (unsigned int i = 0; i < max; i++) {374 /*375 * New attr.config still not supported here, the latest376 * example was PERF_COUNT_SW_CGROUP_SWITCHES377 */378 if (syms[i].symbol == NULL)379 continue;380 381 if (!is_event_supported(type, i))382 continue;383 384 if (strlen(syms[i].alias)) {385 char name[MAX_NAME_LEN];386 387 snprintf(name, MAX_NAME_LEN, "%s OR %s", syms[i].symbol, syms[i].alias);388 strlist__add(evt_name_list, name);389 } else390 strlist__add(evt_name_list, syms[i].symbol);391 }392 393 strlist__for_each_entry(nd, evt_name_list) {394 char *alias = strstr(nd->s, " OR ");395 396 if (alias) {397 *alias = '\0';398 alias += 4;399 }400 print_cb->print_event(print_state,401 /*topic=*/NULL,402 /*pmu_name=*/NULL,403 nd->s,404 alias,405 /*scale_unit=*/NULL,406 /*deprecated=*/false,407 event_type_descriptors[type],408 /*desc=*/NULL,409 /*long_desc=*/NULL,410 /*encoding_desc=*/NULL);411 }412 strlist__delete(evt_name_list);413}414 415/*416 * Print the help text for the event symbols:417 */418void print_events(const struct print_callbacks *print_cb, void *print_state)419{420 print_symbol_events(print_cb, print_state, PERF_TYPE_HARDWARE,421 event_symbols_hw, PERF_COUNT_HW_MAX);422 print_symbol_events(print_cb, print_state, PERF_TYPE_SOFTWARE,423 event_symbols_sw, PERF_COUNT_SW_MAX);424 425 print_tool_events(print_cb, print_state);426 427 print_hwcache_events(print_cb, print_state);428 429 perf_pmus__print_pmu_events(print_cb, print_state);430 431 print_cb->print_event(print_state,432 /*topic=*/NULL,433 /*pmu_name=*/NULL,434 "rNNN",435 /*event_alias=*/NULL,436 /*scale_unit=*/NULL,437 /*deprecated=*/false,438 event_type_descriptors[PERF_TYPE_RAW],439 /*desc=*/NULL,440 /*long_desc=*/NULL,441 /*encoding_desc=*/NULL);442 443 perf_pmus__print_raw_pmu_events(print_cb, print_state);444 445 print_cb->print_event(print_state,446 /*topic=*/NULL,447 /*pmu_name=*/NULL,448 "mem:<addr>[/len][:access]",449 /*scale_unit=*/NULL,450 /*event_alias=*/NULL,451 /*deprecated=*/false,452 event_type_descriptors[PERF_TYPE_BREAKPOINT],453 /*desc=*/NULL,454 /*long_desc=*/NULL,455 /*encoding_desc=*/NULL);456 457 print_tracepoint_events(print_cb, print_state);458 459 print_sdt_events(print_cb, print_state);460 461 metricgroup__print(print_cb, print_state);462 463 print_libpfm_events(print_cb, print_state);464}465