Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.7-rc1 726 lines 15 kB view raw
1 2#include <linux/list.h> 3#include <sys/types.h> 4#include <sys/stat.h> 5#include <unistd.h> 6#include <stdio.h> 7#include <dirent.h> 8#include "sysfs.h" 9#include "util.h" 10#include "pmu.h" 11#include "parse-events.h" 12#include "cpumap.h" 13 14#define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/" 15 16int perf_pmu_parse(struct list_head *list, char *name); 17extern FILE *perf_pmu_in; 18 19static LIST_HEAD(pmus); 20 21/* 22 * Parse & process all the sysfs attributes located under 23 * the directory specified in 'dir' parameter. 24 */ 25static int pmu_format_parse(char *dir, struct list_head *head) 26{ 27 struct dirent *evt_ent; 28 DIR *format_dir; 29 int ret = 0; 30 31 format_dir = opendir(dir); 32 if (!format_dir) 33 return -EINVAL; 34 35 while (!ret && (evt_ent = readdir(format_dir))) { 36 char path[PATH_MAX]; 37 char *name = evt_ent->d_name; 38 FILE *file; 39 40 if (!strcmp(name, ".") || !strcmp(name, "..")) 41 continue; 42 43 snprintf(path, PATH_MAX, "%s/%s", dir, name); 44 45 ret = -EINVAL; 46 file = fopen(path, "r"); 47 if (!file) 48 break; 49 50 perf_pmu_in = file; 51 ret = perf_pmu_parse(head, name); 52 fclose(file); 53 } 54 55 closedir(format_dir); 56 return ret; 57} 58 59/* 60 * Reading/parsing the default pmu format definition, which should be 61 * located at: 62 * /sys/bus/event_source/devices/<dev>/format as sysfs group attributes. 63 */ 64static int pmu_format(char *name, struct list_head *format) 65{ 66 struct stat st; 67 char path[PATH_MAX]; 68 const char *sysfs; 69 70 sysfs = sysfs_find_mountpoint(); 71 if (!sysfs) 72 return -1; 73 74 snprintf(path, PATH_MAX, 75 "%s" EVENT_SOURCE_DEVICE_PATH "%s/format", sysfs, name); 76 77 if (stat(path, &st) < 0) 78 return 0; /* no error if format does not exist */ 79 80 if (pmu_format_parse(path, format)) 81 return -1; 82 83 return 0; 84} 85 86static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) 87{ 88 struct perf_pmu__alias *alias; 89 char buf[256]; 90 int ret; 91 92 ret = fread(buf, 1, sizeof(buf), file); 93 if (ret == 0) 94 return -EINVAL; 95 buf[ret] = 0; 96 97 alias = malloc(sizeof(*alias)); 98 if (!alias) 99 return -ENOMEM; 100 101 INIT_LIST_HEAD(&alias->terms); 102 ret = parse_events_terms(&alias->terms, buf); 103 if (ret) { 104 free(alias); 105 return ret; 106 } 107 108 alias->name = strdup(name); 109 list_add_tail(&alias->list, list); 110 return 0; 111} 112 113/* 114 * Process all the sysfs attributes located under the directory 115 * specified in 'dir' parameter. 116 */ 117static int pmu_aliases_parse(char *dir, struct list_head *head) 118{ 119 struct dirent *evt_ent; 120 DIR *event_dir; 121 int ret = 0; 122 123 event_dir = opendir(dir); 124 if (!event_dir) 125 return -EINVAL; 126 127 while (!ret && (evt_ent = readdir(event_dir))) { 128 char path[PATH_MAX]; 129 char *name = evt_ent->d_name; 130 FILE *file; 131 132 if (!strcmp(name, ".") || !strcmp(name, "..")) 133 continue; 134 135 snprintf(path, PATH_MAX, "%s/%s", dir, name); 136 137 ret = -EINVAL; 138 file = fopen(path, "r"); 139 if (!file) 140 break; 141 ret = perf_pmu__new_alias(head, name, file); 142 fclose(file); 143 } 144 145 closedir(event_dir); 146 return ret; 147} 148 149/* 150 * Reading the pmu event aliases definition, which should be located at: 151 * /sys/bus/event_source/devices/<dev>/events as sysfs group attributes. 152 */ 153static int pmu_aliases(char *name, struct list_head *head) 154{ 155 struct stat st; 156 char path[PATH_MAX]; 157 const char *sysfs; 158 159 sysfs = sysfs_find_mountpoint(); 160 if (!sysfs) 161 return -1; 162 163 snprintf(path, PATH_MAX, 164 "%s/bus/event_source/devices/%s/events", sysfs, name); 165 166 if (stat(path, &st) < 0) 167 return -1; 168 169 if (pmu_aliases_parse(path, head)) 170 return -1; 171 172 return 0; 173} 174 175static int pmu_alias_terms(struct perf_pmu__alias *alias, 176 struct list_head *terms) 177{ 178 struct parse_events__term *term, *clone; 179 LIST_HEAD(list); 180 int ret; 181 182 list_for_each_entry(term, &alias->terms, list) { 183 ret = parse_events__term_clone(&clone, term); 184 if (ret) { 185 parse_events__free_terms(&list); 186 return ret; 187 } 188 list_add_tail(&clone->list, &list); 189 } 190 list_splice(&list, terms); 191 return 0; 192} 193 194/* 195 * Reading/parsing the default pmu type value, which should be 196 * located at: 197 * /sys/bus/event_source/devices/<dev>/type as sysfs attribute. 198 */ 199static int pmu_type(char *name, __u32 *type) 200{ 201 struct stat st; 202 char path[PATH_MAX]; 203 const char *sysfs; 204 FILE *file; 205 int ret = 0; 206 207 sysfs = sysfs_find_mountpoint(); 208 if (!sysfs) 209 return -1; 210 211 snprintf(path, PATH_MAX, 212 "%s" EVENT_SOURCE_DEVICE_PATH "%s/type", sysfs, name); 213 214 if (stat(path, &st) < 0) 215 return -1; 216 217 file = fopen(path, "r"); 218 if (!file) 219 return -EINVAL; 220 221 if (1 != fscanf(file, "%u", type)) 222 ret = -1; 223 224 fclose(file); 225 return ret; 226} 227 228/* Add all pmus in sysfs to pmu list: */ 229static void pmu_read_sysfs(void) 230{ 231 char path[PATH_MAX]; 232 const char *sysfs; 233 DIR *dir; 234 struct dirent *dent; 235 236 sysfs = sysfs_find_mountpoint(); 237 if (!sysfs) 238 return; 239 240 snprintf(path, PATH_MAX, 241 "%s" EVENT_SOURCE_DEVICE_PATH, sysfs); 242 243 dir = opendir(path); 244 if (!dir) 245 return; 246 247 while ((dent = readdir(dir))) { 248 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) 249 continue; 250 /* add to static LIST_HEAD(pmus): */ 251 perf_pmu__find(dent->d_name); 252 } 253 254 closedir(dir); 255} 256 257static struct cpu_map *pmu_cpumask(char *name) 258{ 259 struct stat st; 260 char path[PATH_MAX]; 261 const char *sysfs; 262 FILE *file; 263 struct cpu_map *cpus; 264 265 sysfs = sysfs_find_mountpoint(); 266 if (!sysfs) 267 return NULL; 268 269 snprintf(path, PATH_MAX, 270 "%s/bus/event_source/devices/%s/cpumask", sysfs, name); 271 272 if (stat(path, &st) < 0) 273 return NULL; 274 275 file = fopen(path, "r"); 276 if (!file) 277 return NULL; 278 279 cpus = cpu_map__read(file); 280 fclose(file); 281 return cpus; 282} 283 284static struct perf_pmu *pmu_lookup(char *name) 285{ 286 struct perf_pmu *pmu; 287 LIST_HEAD(format); 288 LIST_HEAD(aliases); 289 __u32 type; 290 291 /* 292 * The pmu data we store & need consists of the pmu 293 * type value and format definitions. Load both right 294 * now. 295 */ 296 if (pmu_format(name, &format)) 297 return NULL; 298 299 if (pmu_type(name, &type)) 300 return NULL; 301 302 pmu = zalloc(sizeof(*pmu)); 303 if (!pmu) 304 return NULL; 305 306 pmu->cpus = pmu_cpumask(name); 307 308 pmu_aliases(name, &aliases); 309 310 INIT_LIST_HEAD(&pmu->format); 311 INIT_LIST_HEAD(&pmu->aliases); 312 list_splice(&format, &pmu->format); 313 list_splice(&aliases, &pmu->aliases); 314 pmu->name = strdup(name); 315 pmu->type = type; 316 list_add_tail(&pmu->list, &pmus); 317 return pmu; 318} 319 320static struct perf_pmu *pmu_find(char *name) 321{ 322 struct perf_pmu *pmu; 323 324 list_for_each_entry(pmu, &pmus, list) 325 if (!strcmp(pmu->name, name)) 326 return pmu; 327 328 return NULL; 329} 330 331struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu) 332{ 333 /* 334 * pmu iterator: If pmu is NULL, we start at the begin, 335 * otherwise return the next pmu. Returns NULL on end. 336 */ 337 if (!pmu) { 338 pmu_read_sysfs(); 339 pmu = list_prepare_entry(pmu, &pmus, list); 340 } 341 list_for_each_entry_continue(pmu, &pmus, list) 342 return pmu; 343 return NULL; 344} 345 346struct perf_pmu *perf_pmu__find(char *name) 347{ 348 struct perf_pmu *pmu; 349 350 /* 351 * Once PMU is loaded it stays in the list, 352 * so we keep us from multiple reading/parsing 353 * the pmu format definitions. 354 */ 355 pmu = pmu_find(name); 356 if (pmu) 357 return pmu; 358 359 return pmu_lookup(name); 360} 361 362static struct perf_pmu__format* 363pmu_find_format(struct list_head *formats, char *name) 364{ 365 struct perf_pmu__format *format; 366 367 list_for_each_entry(format, formats, list) 368 if (!strcmp(format->name, name)) 369 return format; 370 371 return NULL; 372} 373 374/* 375 * Returns value based on the format definition (format parameter) 376 * and unformated value (value parameter). 377 * 378 * TODO maybe optimize a little ;) 379 */ 380static __u64 pmu_format_value(unsigned long *format, __u64 value) 381{ 382 unsigned long fbit, vbit; 383 __u64 v = 0; 384 385 for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) { 386 387 if (!test_bit(fbit, format)) 388 continue; 389 390 if (!(value & (1llu << vbit++))) 391 continue; 392 393 v |= (1llu << fbit); 394 } 395 396 return v; 397} 398 399/* 400 * Setup one of config[12] attr members based on the 401 * user input data - temr parameter. 402 */ 403static int pmu_config_term(struct list_head *formats, 404 struct perf_event_attr *attr, 405 struct parse_events__term *term) 406{ 407 struct perf_pmu__format *format; 408 __u64 *vp; 409 410 /* 411 * Support only for hardcoded and numnerial terms. 412 * Hardcoded terms should be already in, so nothing 413 * to be done for them. 414 */ 415 if (parse_events__is_hardcoded_term(term)) 416 return 0; 417 418 if (term->type_val != PARSE_EVENTS__TERM_TYPE_NUM) 419 return -EINVAL; 420 421 format = pmu_find_format(formats, term->config); 422 if (!format) 423 return -EINVAL; 424 425 switch (format->value) { 426 case PERF_PMU_FORMAT_VALUE_CONFIG: 427 vp = &attr->config; 428 break; 429 case PERF_PMU_FORMAT_VALUE_CONFIG1: 430 vp = &attr->config1; 431 break; 432 case PERF_PMU_FORMAT_VALUE_CONFIG2: 433 vp = &attr->config2; 434 break; 435 default: 436 return -EINVAL; 437 } 438 439 /* 440 * XXX If we ever decide to go with string values for 441 * non-hardcoded terms, here's the place to translate 442 * them into value. 443 */ 444 *vp |= pmu_format_value(format->bits, term->val.num); 445 return 0; 446} 447 448static int pmu_config(struct list_head *formats, struct perf_event_attr *attr, 449 struct list_head *head_terms) 450{ 451 struct parse_events__term *term; 452 453 list_for_each_entry(term, head_terms, list) 454 if (pmu_config_term(formats, attr, term)) 455 return -EINVAL; 456 457 return 0; 458} 459 460/* 461 * Configures event's 'attr' parameter based on the: 462 * 1) users input - specified in terms parameter 463 * 2) pmu format definitions - specified by pmu parameter 464 */ 465int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, 466 struct list_head *head_terms) 467{ 468 attr->type = pmu->type; 469 return pmu_config(&pmu->format, attr, head_terms); 470} 471 472static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu, 473 struct parse_events__term *term) 474{ 475 struct perf_pmu__alias *alias; 476 char *name; 477 478 if (parse_events__is_hardcoded_term(term)) 479 return NULL; 480 481 if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) { 482 if (term->val.num != 1) 483 return NULL; 484 if (pmu_find_format(&pmu->format, term->config)) 485 return NULL; 486 name = term->config; 487 } else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) { 488 if (strcasecmp(term->config, "event")) 489 return NULL; 490 name = term->val.str; 491 } else { 492 return NULL; 493 } 494 495 list_for_each_entry(alias, &pmu->aliases, list) { 496 if (!strcasecmp(alias->name, name)) 497 return alias; 498 } 499 return NULL; 500} 501 502/* 503 * Find alias in the terms list and replace it with the terms 504 * defined for the alias 505 */ 506int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) 507{ 508 struct parse_events__term *term, *h; 509 struct perf_pmu__alias *alias; 510 int ret; 511 512 list_for_each_entry_safe(term, h, head_terms, list) { 513 alias = pmu_find_alias(pmu, term); 514 if (!alias) 515 continue; 516 ret = pmu_alias_terms(alias, &term->list); 517 if (ret) 518 return ret; 519 list_del(&term->list); 520 free(term); 521 } 522 return 0; 523} 524 525int perf_pmu__new_format(struct list_head *list, char *name, 526 int config, unsigned long *bits) 527{ 528 struct perf_pmu__format *format; 529 530 format = zalloc(sizeof(*format)); 531 if (!format) 532 return -ENOMEM; 533 534 format->name = strdup(name); 535 format->value = config; 536 memcpy(format->bits, bits, sizeof(format->bits)); 537 538 list_add_tail(&format->list, list); 539 return 0; 540} 541 542void perf_pmu__set_format(unsigned long *bits, long from, long to) 543{ 544 long b; 545 546 if (!to) 547 to = from; 548 549 memset(bits, 0, BITS_TO_LONGS(PERF_PMU_FORMAT_BITS)); 550 for (b = from; b <= to; b++) 551 set_bit(b, bits); 552} 553 554/* Simulated format definitions. */ 555static struct test_format { 556 const char *name; 557 const char *value; 558} test_formats[] = { 559 { "krava01", "config:0-1,62-63\n", }, 560 { "krava02", "config:10-17\n", }, 561 { "krava03", "config:5\n", }, 562 { "krava11", "config1:0,2,4,6,8,20-28\n", }, 563 { "krava12", "config1:63\n", }, 564 { "krava13", "config1:45-47\n", }, 565 { "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", }, 566 { "krava22", "config2:8,18,48,58\n", }, 567 { "krava23", "config2:28-29,38\n", }, 568}; 569 570#define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format)) 571 572/* Simulated users input. */ 573static struct parse_events__term test_terms[] = { 574 { 575 .config = (char *) "krava01", 576 .val.num = 15, 577 .type_val = PARSE_EVENTS__TERM_TYPE_NUM, 578 .type_term = PARSE_EVENTS__TERM_TYPE_USER, 579 }, 580 { 581 .config = (char *) "krava02", 582 .val.num = 170, 583 .type_val = PARSE_EVENTS__TERM_TYPE_NUM, 584 .type_term = PARSE_EVENTS__TERM_TYPE_USER, 585 }, 586 { 587 .config = (char *) "krava03", 588 .val.num = 1, 589 .type_val = PARSE_EVENTS__TERM_TYPE_NUM, 590 .type_term = PARSE_EVENTS__TERM_TYPE_USER, 591 }, 592 { 593 .config = (char *) "krava11", 594 .val.num = 27, 595 .type_val = PARSE_EVENTS__TERM_TYPE_NUM, 596 .type_term = PARSE_EVENTS__TERM_TYPE_USER, 597 }, 598 { 599 .config = (char *) "krava12", 600 .val.num = 1, 601 .type_val = PARSE_EVENTS__TERM_TYPE_NUM, 602 .type_term = PARSE_EVENTS__TERM_TYPE_USER, 603 }, 604 { 605 .config = (char *) "krava13", 606 .val.num = 2, 607 .type_val = PARSE_EVENTS__TERM_TYPE_NUM, 608 .type_term = PARSE_EVENTS__TERM_TYPE_USER, 609 }, 610 { 611 .config = (char *) "krava21", 612 .val.num = 119, 613 .type_val = PARSE_EVENTS__TERM_TYPE_NUM, 614 .type_term = PARSE_EVENTS__TERM_TYPE_USER, 615 }, 616 { 617 .config = (char *) "krava22", 618 .val.num = 11, 619 .type_val = PARSE_EVENTS__TERM_TYPE_NUM, 620 .type_term = PARSE_EVENTS__TERM_TYPE_USER, 621 }, 622 { 623 .config = (char *) "krava23", 624 .val.num = 2, 625 .type_val = PARSE_EVENTS__TERM_TYPE_NUM, 626 .type_term = PARSE_EVENTS__TERM_TYPE_USER, 627 }, 628}; 629#define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term)) 630 631/* 632 * Prepare format directory data, exported by kernel 633 * at /sys/bus/event_source/devices/<dev>/format. 634 */ 635static char *test_format_dir_get(void) 636{ 637 static char dir[PATH_MAX]; 638 unsigned int i; 639 640 snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX"); 641 if (!mkdtemp(dir)) 642 return NULL; 643 644 for (i = 0; i < TEST_FORMATS_CNT; i++) { 645 static char name[PATH_MAX]; 646 struct test_format *format = &test_formats[i]; 647 FILE *file; 648 649 snprintf(name, PATH_MAX, "%s/%s", dir, format->name); 650 651 file = fopen(name, "w"); 652 if (!file) 653 return NULL; 654 655 if (1 != fwrite(format->value, strlen(format->value), 1, file)) 656 break; 657 658 fclose(file); 659 } 660 661 return dir; 662} 663 664/* Cleanup format directory. */ 665static int test_format_dir_put(char *dir) 666{ 667 char buf[PATH_MAX]; 668 snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir); 669 if (system(buf)) 670 return -1; 671 672 snprintf(buf, PATH_MAX, "rmdir %s\n", dir); 673 return system(buf); 674} 675 676static struct list_head *test_terms_list(void) 677{ 678 static LIST_HEAD(terms); 679 unsigned int i; 680 681 for (i = 0; i < TERMS_CNT; i++) 682 list_add_tail(&test_terms[i].list, &terms); 683 684 return &terms; 685} 686 687#undef TERMS_CNT 688 689int perf_pmu__test(void) 690{ 691 char *format = test_format_dir_get(); 692 LIST_HEAD(formats); 693 struct list_head *terms = test_terms_list(); 694 int ret; 695 696 if (!format) 697 return -EINVAL; 698 699 do { 700 struct perf_event_attr attr; 701 702 memset(&attr, 0, sizeof(attr)); 703 704 ret = pmu_format_parse(format, &formats); 705 if (ret) 706 break; 707 708 ret = pmu_config(&formats, &attr, terms); 709 if (ret) 710 break; 711 712 ret = -EINVAL; 713 714 if (attr.config != 0xc00000000002a823) 715 break; 716 if (attr.config1 != 0x8000400000000145) 717 break; 718 if (attr.config2 != 0x0400000020041d07) 719 break; 720 721 ret = 0; 722 } while (0); 723 724 test_format_dir_put(format); 725 return ret; 726}