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