1 /* Parse event JSON files */ 2 3 /* 4 * Copyright (c) 2014, Intel Corporation 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright notice, 11 * this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 21 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 28 * OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * $FreeBSD$ 31 * 32 */ 33 34 #include <sys/param.h> 35 #include <sys/resource.h> /* getrlimit */ 36 #include <sys/stat.h> 37 #include <sys/time.h> /* getrlimit */ 38 #include <ctype.h> 39 #include <dirent.h> 40 #include <errno.h> 41 #include <libgen.h> 42 #include <limits.h> 43 #include <stdarg.h> 44 #include <stddef.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <ftw.h> 50 #include "list.h" 51 #include "jsmn.h" 52 #include "json.h" 53 #include "jevents.h" 54 55 static int 56 nftw_ordered(const char *path, int (*fn)(const char *, const struct stat *, int, 57 struct FTW *), int nfds, int ftwflags); 58 59 _Noreturn void _Exit(int); 60 61 int verbose; 62 static char *prog; 63 64 int eprintf(int level, int var, const char *fmt, ...) 65 { 66 67 int ret; 68 va_list args; 69 70 if (var < level) 71 return 0; 72 73 va_start(args, fmt); 74 75 ret = vfprintf(stderr, fmt, args); 76 77 va_end(args); 78 79 return ret; 80 } 81 82 __attribute__((weak)) char *get_cpu_str(void) 83 { 84 return NULL; 85 } 86 87 static void addfield(char *map, char **dst, const char *sep, 88 const char *a, jsmntok_t *bt) 89 { 90 unsigned int len = strlen(a) + 1 + strlen(sep); 91 int olen = *dst ? strlen(*dst) : 0; 92 int blen = bt ? json_len(bt) : 0; 93 char *out; 94 95 out = realloc(*dst, len + olen + blen); 96 if (!out) { 97 /* Don't add field in this case */ 98 return; 99 } 100 *dst = out; 101 102 if (!olen) 103 *(*dst) = 0; 104 else 105 strcat(*dst, sep); 106 strcat(*dst, a); 107 if (bt) 108 strncat(*dst, map + bt->start, blen); 109 } 110 111 static void fixname(char *s) 112 { 113 for (; *s; s++) 114 *s = tolower(*s); 115 } 116 117 static void fixdesc(char *s) 118 { 119 char *e = s + strlen(s); 120 121 /* Remove trailing dots that look ugly in perf list */ 122 --e; 123 while (e >= s && isspace(*e)) 124 --e; 125 if (*e == '.') 126 *e = 0; 127 } 128 129 /* Add escapes for '\' so they are proper C strings. */ 130 static char *fixregex(char *s) 131 { 132 int len = 0; 133 int esc_count = 0; 134 char *fixed = NULL; 135 char *p, *q; 136 137 /* Count the number of '\' in string */ 138 for (p = s; *p; p++) { 139 ++len; 140 if (*p == '\\') 141 ++esc_count; 142 } 143 144 if (esc_count == 0) 145 return s; 146 147 /* allocate space for a new string */ 148 fixed = (char *) malloc(len + 1); 149 if (!fixed) 150 return NULL; 151 152 /* copy over the characters */ 153 q = fixed; 154 for (p = s; *p; p++) { 155 if (*p == '\\') { 156 *q = '\\'; 157 ++q; 158 } 159 *q = *p; 160 ++q; 161 } 162 *q = '\0'; 163 return fixed; 164 } 165 166 static struct msrmap { 167 const char *num; 168 const char *pname; 169 } msrmap[] = { 170 { "0x3F6", "ldlat=" }, 171 { "0x1A6", "offcore_rsp=" }, 172 { "0x1A7", "offcore_rsp=" }, 173 { "0x3F7", "frontend=" }, 174 { NULL, NULL } 175 }; 176 177 static struct field { 178 const char *field; 179 const char *kernel; 180 } fields[] = { 181 { "UMask", "umask=" }, 182 { "CounterMask", "cmask=" }, 183 { "Invert", "inv=" }, 184 { "AnyThread", "any=" }, 185 { "EdgeDetect", "edge=" }, 186 { "SampleAfterValue", "period=" }, 187 { "FCMask", "fc_mask=" }, 188 { "PortMask", "ch_mask=" }, 189 { "L3ThreadMask", "l3_thread_mask=" }, 190 { "L3SliceMask", "l3_slice_mask=" }, 191 { NULL, NULL } 192 }; 193 194 static void cut_comma(char *map, jsmntok_t *newval) 195 { 196 int i; 197 198 /* Cut off everything after comma */ 199 for (i = newval->start; i < newval->end; i++) { 200 if (map[i] == ',') 201 newval->end = i; 202 } 203 } 204 205 static int match_field(char *map, jsmntok_t *field, int nz, 206 char **event, jsmntok_t *val) 207 { 208 struct field *f; 209 jsmntok_t newval = *val; 210 211 for (f = fields; f->field; f++) 212 if (json_streq(map, field, f->field) && nz) { 213 cut_comma(map, &newval); 214 addfield(map, event, ",", f->kernel, &newval); 215 return 1; 216 } 217 return 0; 218 } 219 220 static struct msrmap *lookup_msr(char *map, jsmntok_t *val) 221 { 222 jsmntok_t newval = *val; 223 static bool warned; 224 int i; 225 226 cut_comma(map, &newval); 227 for (i = 0; msrmap[i].num; i++) 228 if (json_streq(map, &newval, msrmap[i].num)) 229 return &msrmap[i]; 230 if (!warned) { 231 warned = true; 232 pr_err("%s: Unknown MSR in event file %.*s\n", prog, 233 json_len(val), map + val->start); 234 } 235 return NULL; 236 } 237 238 static struct map { 239 const char *json; 240 const char *perf; 241 } unit_to_pmu[] = { 242 { "CBO", "uncore_cbox" }, 243 { "QPI LL", "uncore_qpi" }, 244 { "SBO", "uncore_sbox" }, 245 { "iMPH-U", "uncore_arb" }, 246 {} 247 }; 248 249 static const char *field_to_perf(struct map *table, char *map, jsmntok_t *val) 250 { 251 int i; 252 253 for (i = 0; table[i].json; i++) { 254 if (json_streq(map, val, table[i].json)) 255 return table[i].perf; 256 } 257 return NULL; 258 } 259 260 #define EXPECT(e, t, m) do { if (!(e)) { \ 261 jsmntok_t *loc = (t); \ 262 if (!(t)->start && (t) > tokens) \ 263 loc = (t) - 1; \ 264 pr_err("%s:%d: " m ", got %s\n", fn, \ 265 json_line(map, loc), \ 266 json_name(t)); \ 267 err = -EIO; \ 268 goto out_free; \ 269 } } while (0) 270 271 static char *topic; 272 273 static char *get_topic(void) 274 { 275 char *tp; 276 int i; 277 278 /* tp is free'd in process_one_file() */ 279 i = asprintf(&tp, "%s", topic); 280 if (i < 0) { 281 pr_info("%s: asprintf() error %s\n", prog); 282 return NULL; 283 } 284 285 for (i = 0; i < (int) strlen(tp); i++) { 286 char c = tp[i]; 287 288 if (c == '-') 289 tp[i] = ' '; 290 else if (c == '.') { 291 tp[i] = '\0'; 292 break; 293 } 294 } 295 296 return tp; 297 } 298 299 static int add_topic(const char *bname) 300 { 301 free(topic); 302 topic = strdup(bname); 303 if (!topic) { 304 pr_info("%s: strdup() error %s for file %s\n", prog, 305 strerror(errno), bname); 306 return -ENOMEM; 307 } 308 return 0; 309 } 310 311 struct perf_entry_data { 312 FILE *outfp; 313 char *topic; 314 }; 315 316 static int close_table; 317 318 static void print_events_table_prefix(FILE *fp, const char *tblname) 319 { 320 fprintf(fp, "static struct pmu_event %s[] = {\n", tblname); 321 close_table = 1; 322 } 323 324 static int print_events_table_entry(void *data, char *name, const char *event, 325 char *desc, char *long_desc, 326 char *pmu, char *unit, char *perpkg, 327 char *metric_expr, 328 char *metric_name, char *metric_group) 329 { 330 struct perf_entry_data *pd = data; 331 FILE *outfp = pd->outfp; 332 char *etopic = pd->topic; 333 334 /* 335 * TODO: Remove formatting chars after debugging to reduce 336 * string lengths. 337 */ 338 fprintf(outfp, "{\n"); 339 340 if (name) 341 fprintf(outfp, "\t.name = \"%s\",\n", name); 342 if (event) 343 fprintf(outfp, "\t.event = \"%s\",\n", event); 344 fprintf(outfp, "\t.desc = \"%s\",\n", desc); 345 fprintf(outfp, "\t.topic = \"%s\",\n", etopic); 346 if (long_desc && long_desc[0]) 347 fprintf(outfp, "\t.long_desc = \"%s\",\n", long_desc); 348 if (pmu) 349 fprintf(outfp, "\t.pmu = \"%s\",\n", pmu); 350 if (unit) 351 fprintf(outfp, "\t.unit = \"%s\",\n", unit); 352 if (perpkg) 353 fprintf(outfp, "\t.perpkg = \"%s\",\n", perpkg); 354 if (metric_expr) 355 fprintf(outfp, "\t.metric_expr = \"%s\",\n", metric_expr); 356 if (metric_name) 357 fprintf(outfp, "\t.metric_name = \"%s\",\n", metric_name); 358 if (metric_group) 359 fprintf(outfp, "\t.metric_group = \"%s\",\n", metric_group); 360 fprintf(outfp, "},\n"); 361 362 return 0; 363 } 364 365 struct event_struct { 366 struct list_head list; 367 char *name; 368 char *event; 369 char *desc; 370 char *long_desc; 371 char *pmu; 372 char *unit; 373 char *perpkg; 374 char *metric_expr; 375 char *metric_name; 376 char *metric_group; 377 }; 378 379 #define ADD_EVENT_FIELD(field) do { if (field) { \ 380 es->field = strdup(field); \ 381 if (!es->field) \ 382 goto out_free; \ 383 } } while (0) 384 385 #define FREE_EVENT_FIELD(field) free(es->field) 386 387 #define TRY_FIXUP_FIELD(field) do { if (es->field && !*field) {\ 388 *field = strdup(es->field); \ 389 if (!*field) \ 390 return -ENOMEM; \ 391 } } while (0) 392 393 #define FOR_ALL_EVENT_STRUCT_FIELDS(op) do { \ 394 op(name); \ 395 op(event); \ 396 op(desc); \ 397 op(long_desc); \ 398 op(pmu); \ 399 op(unit); \ 400 op(perpkg); \ 401 op(metric_expr); \ 402 op(metric_name); \ 403 op(metric_group); \ 404 } while (0) 405 406 static LIST_HEAD(arch_std_events); 407 408 static void free_arch_std_events(void) 409 { 410 struct event_struct *es, *next; 411 412 list_for_each_entry_safe(es, next, &arch_std_events, list) { 413 FOR_ALL_EVENT_STRUCT_FIELDS(FREE_EVENT_FIELD); 414 list_del(&es->list); 415 free(es); 416 } 417 } 418 419 static int save_arch_std_events(void *data __unused, char *name, const char *event, 420 char *desc, char *long_desc, char *pmu, 421 char *unit, char *perpkg, char *metric_expr, 422 char *metric_name, char *metric_group) 423 { 424 struct event_struct *es; 425 426 es = malloc(sizeof(*es)); 427 if (!es) 428 return -ENOMEM; 429 memset(es, 0, sizeof(*es)); 430 FOR_ALL_EVENT_STRUCT_FIELDS(ADD_EVENT_FIELD); 431 list_add_tail(&es->list, &arch_std_events); 432 return 0; 433 out_free: 434 FOR_ALL_EVENT_STRUCT_FIELDS(FREE_EVENT_FIELD); 435 free(es); 436 return -ENOMEM; 437 } 438 439 static void print_events_table_suffix(FILE *outfp) 440 { 441 fprintf(outfp, "{\n"); 442 443 fprintf(outfp, "\t.name = 0,\n"); 444 fprintf(outfp, "\t.event = 0,\n"); 445 fprintf(outfp, "\t.desc = 0,\n"); 446 447 fprintf(outfp, "},\n"); 448 fprintf(outfp, "};\n"); 449 close_table = 0; 450 } 451 452 static struct fixed { 453 const char *name; 454 const char *event; 455 } fixed[] = { 456 { "inst_retired.any", "event=0xc0" }, 457 { "inst_retired.any_p", "event=0xc0" }, 458 { "cpu_clk_unhalted.ref", "event=0x0,umask=0x03" }, 459 { "cpu_clk_unhalted.thread", "event=0x3c" }, 460 { "cpu_clk_unhalted.thread_any", "event=0x3c,any=1" }, 461 { NULL, NULL}, 462 }; 463 464 /* 465 * Handle different fixed counter encodings between JSON and perf. 466 */ 467 static const char *real_event(const char *name, char *event) 468 { 469 int i; 470 471 if (!name) 472 return NULL; 473 474 for (i = 0; fixed[i].name; i++) 475 if (!strcasecmp(name, fixed[i].name)) 476 return fixed[i].event; 477 return event; 478 } 479 480 static int 481 try_fixup(const char *fn, char *arch_std, char **event, char **desc, 482 char **name, char **long_desc, char **pmu, char **filter __unused, 483 char **perpkg, char **unit, char **metric_expr, char **metric_name, 484 char **metric_group, unsigned long long eventcode) 485 { 486 /* try to find matching event from arch standard values */ 487 struct event_struct *es; 488 489 list_for_each_entry(es, &arch_std_events, list) { 490 if (!strcmp(arch_std, es->name)) { 491 if (!eventcode && es->event) { 492 /* allow EventCode to be overridden */ 493 free(*event); 494 *event = NULL; 495 } 496 FOR_ALL_EVENT_STRUCT_FIELDS(TRY_FIXUP_FIELD); 497 return 0; 498 } 499 } 500 501 pr_err("%s: could not find matching %s for %s\n", 502 prog, arch_std, fn); 503 return -1; 504 } 505 506 /* Call func with each event in the json file */ 507 int json_events(const char *fn, 508 int (*func)(void *data, char *name, const char *event, char *desc, 509 char *long_desc, 510 char *pmu, char *unit, char *perpkg, 511 char *metric_expr, 512 char *metric_name, char *metric_group), 513 void *data) 514 { 515 int err; 516 size_t size; 517 jsmntok_t *tokens, *tok; 518 int i, j, len; 519 char *map; 520 char buf[128]; 521 522 if (!fn) 523 return -ENOENT; 524 525 tokens = parse_json(fn, &map, &size, &len); 526 if (!tokens) 527 return -EIO; 528 EXPECT(tokens->type == JSMN_ARRAY, tokens, "expected top level array"); 529 tok = tokens + 1; 530 for (i = 0; i < tokens->size; i++) { 531 char *event = NULL, *desc = NULL, *name = NULL; 532 char *long_desc = NULL; 533 char *extra_desc = NULL; 534 char *pmu = NULL; 535 char *filter = NULL; 536 char *perpkg = NULL; 537 char *unit = NULL; 538 char *metric_expr = NULL; 539 char *metric_name = NULL; 540 char *metric_group = NULL; 541 char *arch_std = NULL; 542 unsigned long long eventcode = 0; 543 struct msrmap *msr = NULL; 544 jsmntok_t *msrval = NULL; 545 jsmntok_t *precise = NULL; 546 jsmntok_t *obj = tok++; 547 548 EXPECT(obj->type == JSMN_OBJECT, obj, "expected object"); 549 for (j = 0; j < obj->size; j += 2) { 550 jsmntok_t *field, *val; 551 int nz; 552 char *s; 553 554 field = tok + j; 555 EXPECT(field->type == JSMN_STRING, tok + j, 556 "Expected field name"); 557 val = tok + j + 1; 558 EXPECT(val->type == JSMN_STRING, tok + j + 1, 559 "Expected string value"); 560 561 nz = !json_streq(map, val, "0"); 562 if (match_field(map, field, nz, &event, val)) { 563 /* ok */ 564 } else if (json_streq(map, field, "EventCode")) { 565 char *code = NULL; 566 addfield(map, &code, "", "", val); 567 eventcode |= strtoul(code, NULL, 0); 568 free(code); 569 } else if (json_streq(map, field, "ExtSel")) { 570 char *code = NULL; 571 addfield(map, &code, "", "", val); 572 eventcode |= strtoul(code, NULL, 0) << 21; 573 free(code); 574 } else if (json_streq(map, field, "EventName")) { 575 addfield(map, &name, "", "", val); 576 } else if (json_streq(map, field, "BriefDescription")) { 577 addfield(map, &desc, "", "", val); 578 fixdesc(desc); 579 } else if (json_streq(map, field, 580 "PublicDescription")) { 581 addfield(map, &long_desc, "", "", val); 582 fixdesc(long_desc); 583 } else if (json_streq(map, field, "PEBS") && nz) { 584 precise = val; 585 } else if (json_streq(map, field, "MSRIndex") && nz) { 586 msr = lookup_msr(map, val); 587 } else if (json_streq(map, field, "MSRValue")) { 588 msrval = val; 589 } else if (json_streq(map, field, "Errata") && 590 !json_streq(map, val, "null")) { 591 addfield(map, &extra_desc, ". ", 592 " Spec update: ", val); 593 } else if (json_streq(map, field, "Data_LA") && nz) { 594 addfield(map, &extra_desc, ". ", 595 " Supports address when precise", 596 NULL); 597 } else if (json_streq(map, field, "Unit")) { 598 const char *ppmu; 599 600 ppmu = field_to_perf(unit_to_pmu, map, val); 601 if (ppmu) { 602 pmu = strdup(ppmu); 603 } else { 604 if (!pmu) 605 pmu = strdup("uncore_"); 606 addfield(map, &pmu, "", "", val); 607 for (s = pmu; *s; s++) 608 *s = tolower(*s); 609 } 610 addfield(map, &desc, ". ", "Unit: ", NULL); 611 addfield(map, &desc, "", pmu, NULL); 612 addfield(map, &desc, "", " ", NULL); 613 } else if (json_streq(map, field, "Filter")) { 614 addfield(map, &filter, "", "", val); 615 } else if (json_streq(map, field, "ScaleUnit")) { 616 addfield(map, &unit, "", "", val); 617 } else if (json_streq(map, field, "PerPkg")) { 618 addfield(map, &perpkg, "", "", val); 619 } else if (json_streq(map, field, "MetricName")) { 620 addfield(map, &metric_name, "", "", val); 621 } else if (json_streq(map, field, "MetricGroup")) { 622 addfield(map, &metric_group, "", "", val); 623 } else if (json_streq(map, field, "MetricExpr")) { 624 addfield(map, &metric_expr, "", "", val); 625 for (s = metric_expr; *s; s++) 626 *s = tolower(*s); 627 } else if (json_streq(map, field, "ArchStdEvent")) { 628 addfield(map, &arch_std, "", "", val); 629 for (s = arch_std; *s; s++) 630 *s = tolower(*s); 631 } 632 /* ignore unknown fields */ 633 } 634 if (precise && desc && !strstr(desc, "(Precise Event)")) { 635 if (json_streq(map, precise, "2")) 636 addfield(map, &extra_desc, " ", 637 "(Must be precise)", NULL); 638 else 639 addfield(map, &extra_desc, " ", 640 "(Precise event)", NULL); 641 } 642 snprintf(buf, sizeof(buf), "event=%#llx", eventcode); 643 addfield(map, &event, ",", buf, NULL); 644 if (desc && extra_desc) 645 addfield(map, &desc, " ", extra_desc, NULL); 646 if (long_desc && extra_desc) 647 addfield(map, &long_desc, " ", extra_desc, NULL); 648 if (filter) 649 addfield(map, &event, ",", filter, NULL); 650 if (msr != NULL) 651 addfield(map, &event, ",", msr->pname, msrval); 652 if (name) 653 fixname(name); 654 655 if (arch_std) { 656 /* 657 * An arch standard event is referenced, so try to 658 * fixup any unassigned values. 659 */ 660 err = try_fixup(fn, arch_std, &event, &desc, &name, 661 &long_desc, &pmu, &filter, &perpkg, 662 &unit, &metric_expr, &metric_name, 663 &metric_group, eventcode); 664 if (err) 665 goto free_strings; 666 } 667 err = func(data, name, real_event(name, event), desc, long_desc, 668 pmu, unit, perpkg, metric_expr, metric_name, metric_group); 669 free_strings: 670 free(event); 671 free(desc); 672 free(name); 673 free(long_desc); 674 free(extra_desc); 675 free(pmu); 676 free(filter); 677 free(perpkg); 678 free(unit); 679 free(metric_expr); 680 free(metric_name); 681 free(metric_group); 682 free(arch_std); 683 684 if (err) 685 break; 686 tok += j; 687 } 688 EXPECT(tok - tokens == len, tok, "unexpected objects at end"); 689 err = 0; 690 out_free: 691 free_json(map, size, tokens); 692 return err; 693 } 694 695 static char *file_name_to_table_name(const char *fname) 696 { 697 unsigned int i; 698 int n; 699 int c; 700 char *tblname; 701 702 703 /* 704 * Ensure tablename starts with alphabetic character. 705 * Derive rest of table name from basename of the JSON file, 706 * replacing hyphens and stripping out .json suffix. 707 */ 708 n = asprintf(&tblname, "pme_%s", fname); 709 if (n < 0) { 710 pr_info("%s: asprintf() error %s for file %s\n", prog, 711 strerror(errno), fname); 712 return NULL; 713 } 714 715 for (i = 0; i < strlen(tblname); i++) { 716 c = tblname[i]; 717 718 if (c == '-' || c == '/') 719 tblname[i] = '_'; 720 else if (c == '.') { 721 tblname[i] = '\0'; 722 break; 723 } else if (!isalnum(c) && c != '_') { 724 char *tmp = strdup(fname); 725 pr_err("%s: Invalid character '%c' in file name %s\n", 726 prog, c, basename(tmp)); 727 free(tblname); 728 free(tmp); 729 tblname = NULL; 730 break; 731 } 732 } 733 734 return tblname; 735 } 736 737 static void print_mapping_table_prefix(FILE *outfp) 738 { 739 fprintf(outfp, "struct pmu_events_map pmu_events_map[] = {\n"); 740 } 741 742 static void print_mapping_table_suffix(FILE *outfp) 743 { 744 /* 745 * Print the terminating, NULL entry. 746 */ 747 fprintf(outfp, "{\n"); 748 fprintf(outfp, "\t.cpuid = 0,\n"); 749 fprintf(outfp, "\t.version = 0,\n"); 750 fprintf(outfp, "\t.type = 0,\n"); 751 fprintf(outfp, "\t.table = 0,\n"); 752 fprintf(outfp, "},\n"); 753 754 /* and finally, the closing curly bracket for the struct */ 755 fprintf(outfp, "};\n"); 756 } 757 758 static int process_mapfile(FILE *outfp, char *fpath) 759 { 760 int n = 16384; 761 FILE *mapfp; 762 char *save = NULL; 763 char *line, *p; 764 int line_num; 765 char *tblname; 766 767 pr_info("%s: Processing mapfile %s\n", prog, fpath); 768 769 line = malloc(n); 770 if (!line) 771 return -1; 772 773 mapfp = fopen(fpath, "r"); 774 if (!mapfp) { 775 pr_info("%s: Error %s opening %s\n", prog, strerror(errno), 776 fpath); 777 return -1; 778 } 779 780 print_mapping_table_prefix(outfp); 781 782 /* Skip first line (header) */ 783 p = fgets(line, n, mapfp); 784 if (!p) 785 goto out; 786 787 line_num = 1; 788 while (1) { 789 char *cpuid, *version, *type, *fname; 790 791 line_num++; 792 p = fgets(line, n, mapfp); 793 if (!p) 794 break; 795 796 if (line[0] == '#' || line[0] == '\n') 797 continue; 798 799 if (line[strlen(line)-1] != '\n') { 800 /* TODO Deal with lines longer than 16K */ 801 pr_info("%s: Mapfile %s: line %d too long, aborting\n", 802 prog, fpath, line_num); 803 return -1; 804 } 805 line[strlen(line)-1] = '\0'; 806 807 cpuid = fixregex(strtok_r(p, ",", &save)); 808 version = strtok_r(NULL, ",", &save); 809 fname = strtok_r(NULL, ",", &save); 810 type = strtok_r(NULL, ",", &save); 811 812 tblname = file_name_to_table_name(fname); 813 fprintf(outfp, "{\n"); 814 fprintf(outfp, "\t.cpuid = \"%s\",\n", cpuid); 815 fprintf(outfp, "\t.version = \"%s\",\n", version); 816 fprintf(outfp, "\t.type = \"%s\",\n", type); 817 818 /* 819 * CHECK: We can't use the type (eg "core") field in the 820 * table name. For us to do that, we need to somehow tweak 821 * the other caller of file_name_to_table(), process_json() 822 * to determine the type. process_json() file has no way 823 * of knowing these are "core" events unless file name has 824 * core in it. If filename has core in it, we can safely 825 * ignore the type field here also. 826 */ 827 fprintf(outfp, "\t.table = %s\n", tblname); 828 fprintf(outfp, "},\n"); 829 } 830 831 out: 832 print_mapping_table_suffix(outfp); 833 return 0; 834 } 835 836 /* 837 * If we fail to locate/process JSON and map files, create a NULL mapping 838 * table. This would at least allow perf to build even if we can't find/use 839 * the aliases. 840 */ 841 static void create_empty_mapping(const char *output_file) 842 { 843 FILE *outfp; 844 845 pr_info("%s: Creating empty pmu_events_map[] table\n", prog); 846 847 /* Truncate file to clear any partial writes to it */ 848 outfp = fopen(output_file, "w"); 849 if (!outfp) { 850 perror("fopen()"); 851 _Exit(1); 852 } 853 854 fprintf(outfp, "#include \"pmu-events/pmu-events.h\"\n"); 855 print_mapping_table_prefix(outfp); 856 print_mapping_table_suffix(outfp); 857 fclose(outfp); 858 } 859 860 static int get_maxfds(void) 861 { 862 struct rlimit rlim; 863 864 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { 865 if (rlim.rlim_max == RLIM_INFINITY) 866 return 512; 867 return MIN(rlim.rlim_max / 2, 512); 868 } 869 870 return 512; 871 } 872 873 /* 874 * nftw() doesn't let us pass an argument to the processing function, 875 * so use a global variables. 876 */ 877 static FILE *eventsfp; 878 static char *mapfile; 879 880 static int is_leaf_dir(const char *fpath) 881 { 882 DIR *d; 883 struct dirent *dir; 884 int res = 1; 885 886 d = opendir(fpath); 887 if (!d) 888 return 0; 889 890 while ((dir = readdir(d)) != NULL) { 891 if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")) 892 continue; 893 894 if (dir->d_type == DT_DIR) { 895 res = 0; 896 break; 897 } else if (dir->d_type == DT_UNKNOWN) { 898 char path[PATH_MAX]; 899 struct stat st; 900 901 sprintf(path, "%s/%s", fpath, dir->d_name); 902 if (stat(path, &st)) 903 break; 904 905 if (S_ISDIR(st.st_mode)) { 906 res = 0; 907 break; 908 } 909 } 910 } 911 912 closedir(d); 913 914 return res; 915 } 916 917 static int is_json_file(const char *name) 918 { 919 const char *suffix; 920 921 if (strlen(name) < 5) 922 return 0; 923 924 suffix = name + strlen(name) - 5; 925 926 if (strncmp(suffix, ".json", 5) == 0) 927 return 1; 928 return 0; 929 } 930 931 static int preprocess_arch_std_files(const char *fpath, const struct stat *sb, 932 int typeflag, struct FTW *ftwbuf) 933 { 934 int level = ftwbuf->level; 935 int is_file = typeflag == FTW_F; 936 937 if (level == 1 && is_file && is_json_file(fpath)) 938 return json_events(fpath, save_arch_std_events, (void *)(uintptr_t)sb); 939 940 return 0; 941 } 942 943 static int process_one_file(const char *fpath, const struct stat *sb, 944 int typeflag, struct FTW *ftwbuf) 945 { 946 char *tblname; 947 const char *bname; 948 int is_dir = typeflag == FTW_D; 949 int is_file = typeflag == FTW_F; 950 int level = ftwbuf->level; 951 int err = 0; 952 953 if (level == 2 && is_dir) { 954 /* 955 * For level 2 directory, bname will include parent name, 956 * like vendor/platform. So search back from platform dir 957 * to find this. 958 */ 959 bname = fpath + ftwbuf->base - 2; 960 for (;;) { 961 if (*bname == '/') 962 break; 963 bname--; 964 } 965 bname++; 966 } else 967 bname = fpath + ftwbuf->base; 968 969 pr_debug("%s %d %7jd %-20s %s\n", 970 is_file ? "f" : is_dir ? "d" : "x", 971 level, sb->st_size, bname, fpath); 972 973 /* base dir or too deep */ 974 if (level == 0 || level > 3) 975 return 0; 976 977 978 /* model directory, reset topic */ 979 if ((level == 1 && is_dir && is_leaf_dir(fpath)) || 980 (level == 2 && is_dir)) { 981 if (close_table) 982 print_events_table_suffix(eventsfp); 983 984 /* 985 * Drop file name suffix. Replace hyphens with underscores. 986 * Fail if file name contains any alphanum characters besides 987 * underscores. 988 */ 989 tblname = file_name_to_table_name(bname); 990 if (!tblname) { 991 pr_info("%s: Error determining table name for %s\n", prog, 992 bname); 993 return -1; 994 } 995 996 print_events_table_prefix(eventsfp, tblname); 997 return 0; 998 } 999 1000 /* 1001 * Save the mapfile name for now. We will process mapfile 1002 * after processing all JSON files (so we can write out the 1003 * mapping table after all PMU events tables). 1004 * 1005 */ 1006 if (level == 1 && is_file) { 1007 if (!strcmp(bname, "mapfile.csv")) { 1008 mapfile = strdup(fpath); 1009 return 0; 1010 } 1011 1012 pr_info("%s: Ignoring file %s\n", prog, fpath); 1013 return 0; 1014 } 1015 1016 /* 1017 * If the file name does not have a .json extension, 1018 * ignore it. It could be a readme.txt for instance. 1019 */ 1020 if (is_file) { 1021 if (!is_json_file(bname)) { 1022 pr_info("%s: Ignoring file without .json suffix %s\n", prog, 1023 fpath); 1024 return 0; 1025 } 1026 } 1027 1028 if (level > 1 && add_topic(bname)) 1029 return -ENOMEM; 1030 1031 /* 1032 * Assume all other files are JSON files. 1033 * 1034 * If mapfile refers to 'power7_core.json', we create a table 1035 * named 'power7_core'. Any inconsistencies between the mapfile 1036 * and directory tree could result in build failure due to table 1037 * names not being found. 1038 * 1039 * Atleast for now, be strict with processing JSON file names. 1040 * i.e. if JSON file name cannot be mapped to C-style table name, 1041 * fail. 1042 */ 1043 if (is_file) { 1044 struct perf_entry_data data = { 1045 .topic = get_topic(), 1046 .outfp = eventsfp, 1047 }; 1048 1049 err = json_events(fpath, print_events_table_entry, &data); 1050 1051 free(data.topic); 1052 } 1053 1054 return err; 1055 } 1056 1057 #ifndef PATH_MAX 1058 #define PATH_MAX 4096 1059 #endif 1060 1061 /* 1062 * Starting in directory 'start_dirname', find the "mapfile.csv" and 1063 * the set of JSON files for the architecture 'arch'. 1064 * 1065 * From each JSON file, create a C-style "PMU events table" from the 1066 * JSON file (see struct pmu_event). 1067 * 1068 * From the mapfile, create a mapping between the CPU revisions and 1069 * PMU event tables (see struct pmu_events_map). 1070 * 1071 * Write out the PMU events tables and the mapping table to pmu-event.c. 1072 */ 1073 int main(int argc, char *argv[]) 1074 { 1075 int rc; 1076 int maxfds; 1077 char ldirname[PATH_MAX]; 1078 1079 const char *arch; 1080 const char *output_file; 1081 const char *start_dirname; 1082 struct stat stbuf; 1083 1084 prog = basename(argv[0]); 1085 if (argc < 4) { 1086 pr_err("Usage: %s <arch> <starting_dir> <output_file>\n", prog); 1087 return 1; 1088 } 1089 1090 arch = argv[1]; 1091 start_dirname = argv[2]; 1092 output_file = argv[3]; 1093 1094 if (argc > 4) 1095 verbose = atoi(argv[4]); 1096 1097 eventsfp = fopen(output_file, "w"); 1098 if (!eventsfp) { 1099 pr_err("%s Unable to create required file %s (%s)\n", 1100 prog, output_file, strerror(errno)); 1101 return 2; 1102 } 1103 1104 sprintf(ldirname, "%s/%s", start_dirname, arch); 1105 1106 /* If architecture does not have any event lists, bail out */ 1107 if (stat(ldirname, &stbuf) < 0) { 1108 pr_info("%s: Arch %s has no PMU event lists\n", prog, arch); 1109 goto empty_map; 1110 } 1111 1112 /* Include pmu-events.h first */ 1113 fprintf(eventsfp, "#include \"pmu-events/pmu-events.h\"\n"); 1114 1115 /* 1116 * The mapfile allows multiple CPUids to point to the same JSON file, 1117 * so, not sure if there is a need for symlinks within the pmu-events 1118 * directory. 1119 * 1120 * For now, treat symlinks of JSON files as regular files and create 1121 * separate tables for each symlink (presumably, each symlink refers 1122 * to specific version of the CPU). 1123 */ 1124 1125 maxfds = get_maxfds(); 1126 mapfile = NULL; 1127 rc = nftw_ordered(ldirname, preprocess_arch_std_files, maxfds, 0); 1128 if (rc && verbose) { 1129 pr_info("%s: Error preprocessing arch standard files %s: %s\n", 1130 prog, ldirname, strerror(errno)); 1131 goto empty_map; 1132 } else if (rc < 0) { 1133 /* Make build fail */ 1134 free_arch_std_events(); 1135 return 1; 1136 } else if (rc) { 1137 goto empty_map; 1138 } 1139 1140 rc = nftw_ordered(ldirname, process_one_file, maxfds, 0); 1141 if (rc && verbose) { 1142 pr_info("%s: Error walking file tree %s\n", prog, ldirname); 1143 goto empty_map; 1144 } else if (rc < 0) { 1145 /* Make build fail */ 1146 free_arch_std_events(); 1147 return 1; 1148 } else if (rc) { 1149 goto empty_map; 1150 } 1151 1152 if (close_table) 1153 print_events_table_suffix(eventsfp); 1154 1155 if (!mapfile) { 1156 pr_info("%s: No CPU->JSON mapping?\n", prog); 1157 goto empty_map; 1158 } 1159 1160 if (process_mapfile(eventsfp, mapfile)) { 1161 pr_info("%s: Error processing mapfile %s\n", prog, mapfile); 1162 /* Make build fail */ 1163 return 1; 1164 } 1165 1166 return 0; 1167 1168 empty_map: 1169 fclose(eventsfp); 1170 create_empty_mapping(output_file); 1171 free_arch_std_events(); 1172 return 0; 1173 } 1174 1175 #include <fts.h> 1176 1177 static int 1178 fts_compare(const FTSENT * const *a, const FTSENT * const *b) 1179 { 1180 return (strcmp((*a)->fts_name, (*b)->fts_name)); 1181 } 1182 1183 static int 1184 nftw_ordered(const char *path, int (*fn)(const char *, const struct stat *, int, 1185 struct FTW *), int nfds, int ftwflags) 1186 { 1187 char * const paths[2] = { (char *)path, NULL }; 1188 struct FTW ftw; 1189 FTSENT *cur; 1190 FTS *ftsp; 1191 int error = 0, ftsflags, fnflag, postorder, sverrno; 1192 1193 /* XXX - nfds is currently unused */ 1194 if (nfds < 1) { 1195 errno = EINVAL; 1196 return (-1); 1197 } 1198 1199 ftsflags = FTS_COMFOLLOW; 1200 if (!(ftwflags & FTW_CHDIR)) 1201 ftsflags |= FTS_NOCHDIR; 1202 if (ftwflags & FTW_MOUNT) 1203 ftsflags |= FTS_XDEV; 1204 if (ftwflags & FTW_PHYS) 1205 ftsflags |= FTS_PHYSICAL; 1206 else 1207 ftsflags |= FTS_LOGICAL; 1208 postorder = (ftwflags & FTW_DEPTH) != 0; 1209 ftsp = fts_open(paths, ftsflags, fts_compare); 1210 if (ftsp == NULL) 1211 return (-1); 1212 while ((cur = fts_read(ftsp)) != NULL) { 1213 switch (cur->fts_info) { 1214 case FTS_D: 1215 if (postorder) 1216 continue; 1217 fnflag = FTW_D; 1218 break; 1219 case FTS_DC: 1220 continue; 1221 case FTS_DNR: 1222 fnflag = FTW_DNR; 1223 break; 1224 case FTS_DP: 1225 if (!postorder) 1226 continue; 1227 fnflag = FTW_DP; 1228 break; 1229 case FTS_F: 1230 case FTS_DEFAULT: 1231 fnflag = FTW_F; 1232 break; 1233 case FTS_NS: 1234 case FTS_NSOK: 1235 fnflag = FTW_NS; 1236 break; 1237 case FTS_SL: 1238 fnflag = FTW_SL; 1239 break; 1240 case FTS_SLNONE: 1241 fnflag = FTW_SLN; 1242 break; 1243 default: 1244 error = -1; 1245 goto done; 1246 } 1247 ftw.base = cur->fts_pathlen - cur->fts_namelen; 1248 ftw.level = cur->fts_level; 1249 error = fn(cur->fts_path, cur->fts_statp, fnflag, &ftw); 1250 if (error != 0) 1251 break; 1252 } 1253 done: 1254 sverrno = errno; 1255 if (fts_close(ftsp) != 0 && error == 0) 1256 error = -1; 1257 else 1258 errno = sverrno; 1259 return (error); 1260 } 1261