pmus.c (003be8c4f71753092bbb86fa9d7ad26dd9fb98db) | pmus.c (1eaf496ed386934f1c2439a120fe84a05194f91a) |
---|---|
1// SPDX-License-Identifier: GPL-2.0 2#include <linux/list.h> | 1// SPDX-License-Identifier: GPL-2.0 2#include <linux/list.h> |
3#include <linux/zalloc.h> 4#include <subcmd/pager.h> 5#include <sys/types.h> 6#include <dirent.h> |
|
3#include <string.h> | 7#include <string.h> |
8#include <unistd.h> 9#include "debug.h" 10#include "evsel.h" |
|
4#include "pmus.h" 5#include "pmu.h" | 11#include "pmus.h" 12#include "pmu.h" |
13#include "print-events.h" |
|
6 | 14 |
7LIST_HEAD(pmus); | 15static LIST_HEAD(pmus); |
8 | 16 |
17void perf_pmus__destroy(void) 18{ 19 struct perf_pmu *pmu, *tmp; 20 21 list_for_each_entry_safe(pmu, tmp, &pmus, list) { 22 list_del(&pmu->list); 23 24 perf_pmu__delete(pmu); 25 } 26} 27 28static struct perf_pmu *pmu_find(const char *name) 29{ 30 struct perf_pmu *pmu; 31 32 list_for_each_entry(pmu, &pmus, list) { 33 if (!strcmp(pmu->name, name) || 34 (pmu->alias_name && !strcmp(pmu->alias_name, name))) 35 return pmu; 36 } 37 38 return NULL; 39} 40 41struct perf_pmu *perf_pmus__find(const char *name) 42{ 43 struct perf_pmu *pmu; 44 int dirfd; 45 46 /* 47 * Once PMU is loaded it stays in the list, 48 * so we keep us from multiple reading/parsing 49 * the pmu format definitions. 50 */ 51 pmu = pmu_find(name); 52 if (pmu) 53 return pmu; 54 55 dirfd = perf_pmu__event_source_devices_fd(); 56 pmu = perf_pmu__lookup(&pmus, dirfd, name); 57 close(dirfd); 58 59 return pmu; 60} 61 62static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name) 63{ 64 struct perf_pmu *pmu; 65 66 /* 67 * Once PMU is loaded it stays in the list, 68 * so we keep us from multiple reading/parsing 69 * the pmu format definitions. 70 */ 71 pmu = pmu_find(name); 72 if (pmu) 73 return pmu; 74 75 return perf_pmu__lookup(&pmus, dirfd, name); 76} 77 78/* Add all pmus in sysfs to pmu list: */ 79static void pmu_read_sysfs(void) 80{ 81 int fd; 82 DIR *dir; 83 struct dirent *dent; 84 85 fd = perf_pmu__event_source_devices_fd(); 86 if (fd < 0) 87 return; 88 89 dir = fdopendir(fd); 90 if (!dir) 91 return; 92 93 while ((dent = readdir(dir))) { 94 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) 95 continue; 96 /* add to static LIST_HEAD(pmus): */ 97 perf_pmu__find2(fd, dent->d_name); 98 } 99 100 closedir(dir); 101} 102 103struct perf_pmu *perf_pmus__find_by_type(unsigned int type) 104{ 105 struct perf_pmu *pmu; 106 107 list_for_each_entry(pmu, &pmus, list) 108 if (pmu->type == type) 109 return pmu; 110 111 return NULL; 112} 113 114struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu) 115{ 116 /* 117 * pmu iterator: If pmu is NULL, we start at the begin, 118 * otherwise return the next pmu. Returns NULL on end. 119 */ 120 if (!pmu) { 121 pmu_read_sysfs(); 122 pmu = list_prepare_entry(pmu, &pmus, list); 123 } 124 list_for_each_entry_continue(pmu, &pmus, list) 125 return pmu; 126 return NULL; 127} 128 |
|
9const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str) 10{ 11 struct perf_pmu *pmu = NULL; 12 | 129const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str) 130{ 131 struct perf_pmu *pmu = NULL; 132 |
13 while ((pmu = perf_pmu__scan(pmu)) != NULL) { | 133 while ((pmu = perf_pmus__scan(pmu)) != NULL) { |
14 if (!strcmp(pmu->name, str)) 15 return pmu; 16 /* Ignore "uncore_" prefix. */ 17 if (!strncmp(pmu->name, "uncore_", 7)) { 18 if (!strcmp(pmu->name + 7, str)) 19 return pmu; 20 } 21 /* Ignore "cpu_" prefix on Intel hybrid PMUs. */ 22 if (!strncmp(pmu->name, "cpu_", 4)) { 23 if (!strcmp(pmu->name + 4, str)) 24 return pmu; 25 } 26 } 27 return NULL; 28} | 134 if (!strcmp(pmu->name, str)) 135 return pmu; 136 /* Ignore "uncore_" prefix. */ 137 if (!strncmp(pmu->name, "uncore_", 7)) { 138 if (!strcmp(pmu->name + 7, str)) 139 return pmu; 140 } 141 /* Ignore "cpu_" prefix on Intel hybrid PMUs. */ 142 if (!strncmp(pmu->name, "cpu_", 4)) { 143 if (!strcmp(pmu->name + 4, str)) 144 return pmu; 145 } 146 } 147 return NULL; 148} |
149 150int perf_pmus__num_mem_pmus(void) 151{ 152 struct perf_pmu *pmu = NULL; 153 int count = 0; 154 155 while ((pmu = perf_pmus__scan(pmu)) != NULL) { 156 if (perf_pmu__is_mem_pmu(pmu)) 157 count++; 158 } 159 return count; 160} 161 162/** Struct for ordering events as output in perf list. */ 163struct sevent { 164 /** PMU for event. */ 165 const struct perf_pmu *pmu; 166 /** 167 * Optional event for name, desc, etc. If not present then this is a 168 * selectable PMU and the event name is shown as "//". 169 */ 170 const struct perf_pmu_alias *event; 171 /** Is the PMU for the CPU? */ 172 bool is_cpu; 173}; 174 175static int cmp_sevent(const void *a, const void *b) 176{ 177 const struct sevent *as = a; 178 const struct sevent *bs = b; 179 const char *a_pmu_name = NULL, *b_pmu_name = NULL; 180 const char *a_name = "//", *a_desc = NULL, *a_topic = ""; 181 const char *b_name = "//", *b_desc = NULL, *b_topic = ""; 182 int ret; 183 184 if (as->event) { 185 a_name = as->event->name; 186 a_desc = as->event->desc; 187 a_topic = as->event->topic ?: ""; 188 a_pmu_name = as->event->pmu_name; 189 } 190 if (bs->event) { 191 b_name = bs->event->name; 192 b_desc = bs->event->desc; 193 b_topic = bs->event->topic ?: ""; 194 b_pmu_name = bs->event->pmu_name; 195 } 196 /* Put extra events last. */ 197 if (!!a_desc != !!b_desc) 198 return !!a_desc - !!b_desc; 199 200 /* Order by topics. */ 201 ret = strcmp(a_topic, b_topic); 202 if (ret) 203 return ret; 204 205 /* Order CPU core events to be first */ 206 if (as->is_cpu != bs->is_cpu) 207 return as->is_cpu ? -1 : 1; 208 209 /* Order by PMU name. */ 210 if (as->pmu != bs->pmu) { 211 a_pmu_name = a_pmu_name ?: (as->pmu->name ?: ""); 212 b_pmu_name = b_pmu_name ?: (bs->pmu->name ?: ""); 213 ret = strcmp(a_pmu_name, b_pmu_name); 214 if (ret) 215 return ret; 216 } 217 218 /* Order by event name. */ 219 return strcmp(a_name, b_name); 220} 221 222static bool pmu_alias_is_duplicate(struct sevent *alias_a, 223 struct sevent *alias_b) 224{ 225 const char *a_pmu_name = NULL, *b_pmu_name = NULL; 226 const char *a_name = "//", *b_name = "//"; 227 228 229 if (alias_a->event) { 230 a_name = alias_a->event->name; 231 a_pmu_name = alias_a->event->pmu_name; 232 } 233 if (alias_b->event) { 234 b_name = alias_b->event->name; 235 b_pmu_name = alias_b->event->pmu_name; 236 } 237 238 /* Different names -> never duplicates */ 239 if (strcmp(a_name, b_name)) 240 return false; 241 242 /* Don't remove duplicates for different PMUs */ 243 a_pmu_name = a_pmu_name ?: (alias_a->pmu->name ?: ""); 244 b_pmu_name = b_pmu_name ?: (alias_b->pmu->name ?: ""); 245 return strcmp(a_pmu_name, b_pmu_name) == 0; 246} 247 248static int sub_non_neg(int a, int b) 249{ 250 if (b > a) 251 return 0; 252 return a - b; 253} 254 255static char *format_alias(char *buf, int len, const struct perf_pmu *pmu, 256 const struct perf_pmu_alias *alias) 257{ 258 struct parse_events_term *term; 259 int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name); 260 261 list_for_each_entry(term, &alias->terms, list) { 262 if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) 263 used += snprintf(buf + used, sub_non_neg(len, used), 264 ",%s=%s", term->config, 265 term->val.str); 266 } 267 268 if (sub_non_neg(len, used) > 0) { 269 buf[used] = '/'; 270 used++; 271 } 272 if (sub_non_neg(len, used) > 0) { 273 buf[used] = '\0'; 274 used++; 275 } else 276 buf[len - 1] = '\0'; 277 278 return buf; 279} 280 281void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state) 282{ 283 struct perf_pmu *pmu; 284 struct perf_pmu_alias *event; 285 char buf[1024]; 286 int printed = 0; 287 int len, j; 288 struct sevent *aliases; 289 290 pmu = NULL; 291 len = 0; 292 while ((pmu = perf_pmus__scan(pmu)) != NULL) { 293 list_for_each_entry(event, &pmu->aliases, list) 294 len++; 295 if (pmu->selectable) 296 len++; 297 } 298 aliases = zalloc(sizeof(struct sevent) * len); 299 if (!aliases) { 300 pr_err("FATAL: not enough memory to print PMU events\n"); 301 return; 302 } 303 pmu = NULL; 304 j = 0; 305 while ((pmu = perf_pmus__scan(pmu)) != NULL) { 306 bool is_cpu = pmu->is_core; 307 308 list_for_each_entry(event, &pmu->aliases, list) { 309 aliases[j].event = event; 310 aliases[j].pmu = pmu; 311 aliases[j].is_cpu = is_cpu; 312 j++; 313 } 314 if (pmu->selectable) { 315 aliases[j].event = NULL; 316 aliases[j].pmu = pmu; 317 aliases[j].is_cpu = is_cpu; 318 j++; 319 } 320 } 321 len = j; 322 qsort(aliases, len, sizeof(struct sevent), cmp_sevent); 323 for (j = 0; j < len; j++) { 324 const char *name, *alias = NULL, *scale_unit = NULL, 325 *desc = NULL, *long_desc = NULL, 326 *encoding_desc = NULL, *topic = NULL, 327 *pmu_name = NULL; 328 bool deprecated = false; 329 size_t buf_used; 330 331 /* Skip duplicates */ 332 if (j > 0 && pmu_alias_is_duplicate(&aliases[j], &aliases[j - 1])) 333 continue; 334 335 if (!aliases[j].event) { 336 /* A selectable event. */ 337 pmu_name = aliases[j].pmu->name; 338 buf_used = snprintf(buf, sizeof(buf), "%s//", pmu_name) + 1; 339 name = buf; 340 } else { 341 if (aliases[j].event->desc) { 342 name = aliases[j].event->name; 343 buf_used = 0; 344 } else { 345 name = format_alias(buf, sizeof(buf), aliases[j].pmu, 346 aliases[j].event); 347 if (aliases[j].is_cpu) { 348 alias = name; 349 name = aliases[j].event->name; 350 } 351 buf_used = strlen(buf) + 1; 352 } 353 pmu_name = aliases[j].event->pmu_name ?: (aliases[j].pmu->name ?: ""); 354 if (strlen(aliases[j].event->unit) || aliases[j].event->scale != 1.0) { 355 scale_unit = buf + buf_used; 356 buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, 357 "%G%s", aliases[j].event->scale, 358 aliases[j].event->unit) + 1; 359 } 360 desc = aliases[j].event->desc; 361 long_desc = aliases[j].event->long_desc; 362 topic = aliases[j].event->topic; 363 encoding_desc = buf + buf_used; 364 buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, 365 "%s/%s/", pmu_name, aliases[j].event->str) + 1; 366 deprecated = aliases[j].event->deprecated; 367 } 368 print_cb->print_event(print_state, 369 pmu_name, 370 topic, 371 name, 372 alias, 373 scale_unit, 374 deprecated, 375 "Kernel PMU event", 376 desc, 377 long_desc, 378 encoding_desc); 379 } 380 if (printed && pager_in_use()) 381 printf("\n"); 382 383 zfree(&aliases); 384} 385 386bool perf_pmus__have_event(const char *pname, const char *name) 387{ 388 struct perf_pmu *pmu = perf_pmus__find(pname); 389 390 return pmu && perf_pmu__have_event(pmu, name); 391} 392 393bool perf_pmus__has_hybrid(void) 394{ 395 static bool hybrid_scanned, has_hybrid; 396 397 if (!hybrid_scanned) { 398 struct perf_pmu *pmu = NULL; 399 400 while ((pmu = perf_pmus__scan(pmu)) != NULL) { 401 if (pmu->is_core && is_pmu_hybrid(pmu->name)) { 402 has_hybrid = true; 403 break; 404 } 405 } 406 hybrid_scanned = true; 407 } 408 return has_hybrid; 409} 410 411struct perf_pmu *evsel__find_pmu(const struct evsel *evsel) 412{ 413 struct perf_pmu *pmu = evsel->pmu; 414 415 if (!pmu) { 416 pmu = perf_pmus__find_by_type(evsel->core.attr.type); 417 ((struct evsel *)evsel)->pmu = pmu; 418 } 419 return pmu; 420} |
|