1959826caSMatt Macy /* Parse event JSON files */ 2959826caSMatt Macy 3959826caSMatt Macy /* 4959826caSMatt Macy * Copyright (c) 2014, Intel Corporation 5959826caSMatt Macy * All rights reserved. 6959826caSMatt Macy * 7959826caSMatt Macy * Redistribution and use in source and binary forms, with or without 8959826caSMatt Macy * modification, are permitted provided that the following conditions are met: 9959826caSMatt Macy * 10959826caSMatt Macy * 1. Redistributions of source code must retain the above copyright notice, 11959826caSMatt Macy * this list of conditions and the following disclaimer. 12959826caSMatt Macy * 13959826caSMatt Macy * 2. Redistributions in binary form must reproduce the above copyright 14959826caSMatt Macy * notice, this list of conditions and the following disclaimer in the 15959826caSMatt Macy * documentation and/or other materials provided with the distribution. 16959826caSMatt Macy * 17959826caSMatt Macy * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18959826caSMatt Macy * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19959826caSMatt Macy * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20959826caSMatt Macy * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 21959826caSMatt Macy * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 22959826caSMatt Macy * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23959826caSMatt Macy * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24959826caSMatt Macy * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25959826caSMatt Macy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26959826caSMatt Macy * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27959826caSMatt Macy * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 28959826caSMatt Macy * OF THE POSSIBILITY OF SUCH DAMAGE. 29959826caSMatt Macy * 30959826caSMatt Macy * $FreeBSD$ 31959826caSMatt Macy * 32959826caSMatt Macy */ 33959826caSMatt Macy 34b3d01a2aSEnji Cooper #include <sys/param.h> 35b3d01a2aSEnji Cooper #include <sys/resource.h> /* getrlimit */ 36b3d01a2aSEnji Cooper #include <sys/stat.h> 37b3d01a2aSEnji Cooper #include <sys/time.h> /* getrlimit */ 38b3d01a2aSEnji Cooper #include <ctype.h> 39b3d01a2aSEnji Cooper #include <dirent.h> 40b3d01a2aSEnji Cooper #include <errno.h> 41b3d01a2aSEnji Cooper #include <libgen.h> 42b3d01a2aSEnji Cooper #include <limits.h> 43b3d01a2aSEnji Cooper #include <stdarg.h> 442fa224b1SAlex Richardson #include <stddef.h> 45959826caSMatt Macy #include <stdio.h> 46959826caSMatt Macy #include <stdlib.h> 47959826caSMatt Macy #include <string.h> 48959826caSMatt Macy #include <unistd.h> 49*51f32966SAlexander Motin #include <ftw.h> 50959826caSMatt Macy #include "list.h" 51959826caSMatt Macy #include "jsmn.h" 52959826caSMatt Macy #include "json.h" 53959826caSMatt Macy #include "jevents.h" 54959826caSMatt Macy 55334fd3daSMatt Macy static int 56334fd3daSMatt Macy nftw_ordered(const char *path, int (*fn)(const char *, const struct stat *, int, 57334fd3daSMatt Macy struct FTW *), int nfds, int ftwflags); 58334fd3daSMatt Macy 59*51f32966SAlexander Motin _Noreturn void _Exit(int); 60*51f32966SAlexander Motin 61959826caSMatt Macy int verbose; 62959826caSMatt Macy static char *prog; 63959826caSMatt Macy 64*51f32966SAlexander Motin int eprintf(int level, int var, const char *fmt, ...) 65959826caSMatt Macy { 66959826caSMatt Macy 67959826caSMatt Macy int ret; 68959826caSMatt Macy va_list args; 69959826caSMatt Macy 70959826caSMatt Macy if (var < level) 71959826caSMatt Macy return 0; 72959826caSMatt Macy 73959826caSMatt Macy va_start(args, fmt); 74959826caSMatt Macy 75959826caSMatt Macy ret = vfprintf(stderr, fmt, args); 76959826caSMatt Macy 77959826caSMatt Macy va_end(args); 78959826caSMatt Macy 79959826caSMatt Macy return ret; 80959826caSMatt Macy } 81959826caSMatt Macy 82*51f32966SAlexander Motin __attribute__((weak)) char *get_cpu_str(void) 83959826caSMatt Macy { 84959826caSMatt Macy return NULL; 85959826caSMatt Macy } 86959826caSMatt Macy 87*51f32966SAlexander Motin static void addfield(char *map, char **dst, const char *sep, 88*51f32966SAlexander Motin const char *a, jsmntok_t *bt) 89959826caSMatt Macy { 90959826caSMatt Macy unsigned int len = strlen(a) + 1 + strlen(sep); 91959826caSMatt Macy int olen = *dst ? strlen(*dst) : 0; 92959826caSMatt Macy int blen = bt ? json_len(bt) : 0; 93959826caSMatt Macy char *out; 94959826caSMatt Macy 95959826caSMatt Macy out = realloc(*dst, len + olen + blen); 96959826caSMatt Macy if (!out) { 97959826caSMatt Macy /* Don't add field in this case */ 98959826caSMatt Macy return; 99959826caSMatt Macy } 100959826caSMatt Macy *dst = out; 101959826caSMatt Macy 102959826caSMatt Macy if (!olen) 103959826caSMatt Macy *(*dst) = 0; 104959826caSMatt Macy else 105959826caSMatt Macy strcat(*dst, sep); 106959826caSMatt Macy strcat(*dst, a); 107959826caSMatt Macy if (bt) 108959826caSMatt Macy strncat(*dst, map + bt->start, blen); 109959826caSMatt Macy } 110959826caSMatt Macy 111*51f32966SAlexander Motin static void fixname(char *s) 112959826caSMatt Macy { 113959826caSMatt Macy for (; *s; s++) 114959826caSMatt Macy *s = tolower(*s); 115959826caSMatt Macy } 116959826caSMatt Macy 117*51f32966SAlexander Motin static void fixdesc(char *s) 118959826caSMatt Macy { 119959826caSMatt Macy char *e = s + strlen(s); 120959826caSMatt Macy 121959826caSMatt Macy /* Remove trailing dots that look ugly in perf list */ 122959826caSMatt Macy --e; 123959826caSMatt Macy while (e >= s && isspace(*e)) 124959826caSMatt Macy --e; 1256a2a926dSEd Maste if (e >= s && *e == '.') 126959826caSMatt Macy *e = 0; 127959826caSMatt Macy } 128959826caSMatt Macy 129959826caSMatt Macy /* Add escapes for '\' so they are proper C strings. */ 130*51f32966SAlexander Motin static char *fixregex(char *s) 131959826caSMatt Macy { 132959826caSMatt Macy int len = 0; 133959826caSMatt Macy int esc_count = 0; 134959826caSMatt Macy char *fixed = NULL; 135959826caSMatt Macy char *p, *q; 136959826caSMatt Macy 137959826caSMatt Macy /* Count the number of '\' in string */ 138959826caSMatt Macy for (p = s; *p; p++) { 139959826caSMatt Macy ++len; 140959826caSMatt Macy if (*p == '\\') 141959826caSMatt Macy ++esc_count; 142959826caSMatt Macy } 143959826caSMatt Macy 144959826caSMatt Macy if (esc_count == 0) 145959826caSMatt Macy return s; 146959826caSMatt Macy 147959826caSMatt Macy /* allocate space for a new string */ 148959826caSMatt Macy fixed = (char *) malloc(len + 1); 149959826caSMatt Macy if (!fixed) 150959826caSMatt Macy return NULL; 151959826caSMatt Macy 152959826caSMatt Macy /* copy over the characters */ 153959826caSMatt Macy q = fixed; 154959826caSMatt Macy for (p = s; *p; p++) { 155959826caSMatt Macy if (*p == '\\') { 156959826caSMatt Macy *q = '\\'; 157959826caSMatt Macy ++q; 158959826caSMatt Macy } 159959826caSMatt Macy *q = *p; 160959826caSMatt Macy ++q; 161959826caSMatt Macy } 162959826caSMatt Macy *q = '\0'; 163959826caSMatt Macy return fixed; 164959826caSMatt Macy } 165959826caSMatt Macy 166959826caSMatt Macy static struct msrmap { 167959826caSMatt Macy const char *num; 168959826caSMatt Macy const char *pname; 169959826caSMatt Macy } msrmap[] = { 170959826caSMatt Macy { "0x3F6", "ldlat=" }, 171959826caSMatt Macy { "0x1A6", "offcore_rsp=" }, 172959826caSMatt Macy { "0x1A7", "offcore_rsp=" }, 173959826caSMatt Macy { "0x3F7", "frontend=" }, 174959826caSMatt Macy { NULL, NULL } 175959826caSMatt Macy }; 176959826caSMatt Macy 177959826caSMatt Macy static struct field { 178959826caSMatt Macy const char *field; 179959826caSMatt Macy const char *kernel; 180959826caSMatt Macy } fields[] = { 181959826caSMatt Macy { "UMask", "umask=" }, 182959826caSMatt Macy { "CounterMask", "cmask=" }, 183959826caSMatt Macy { "Invert", "inv=" }, 184959826caSMatt Macy { "AnyThread", "any=" }, 185959826caSMatt Macy { "EdgeDetect", "edge=" }, 186959826caSMatt Macy { "SampleAfterValue", "period=" }, 187959826caSMatt Macy { "FCMask", "fc_mask=" }, 188959826caSMatt Macy { "PortMask", "ch_mask=" }, 189dacc43dfSMatt Macy { "L3ThreadMask", "l3_thread_mask=" }, 190dacc43dfSMatt Macy { "L3SliceMask", "l3_slice_mask=" }, 191959826caSMatt Macy { NULL, NULL } 192959826caSMatt Macy }; 193959826caSMatt Macy 194*51f32966SAlexander Motin static void cut_comma(char *map, jsmntok_t *newval) 195959826caSMatt Macy { 196959826caSMatt Macy int i; 197959826caSMatt Macy 198959826caSMatt Macy /* Cut off everything after comma */ 199959826caSMatt Macy for (i = newval->start; i < newval->end; i++) { 200959826caSMatt Macy if (map[i] == ',') 201959826caSMatt Macy newval->end = i; 202959826caSMatt Macy } 203959826caSMatt Macy } 204959826caSMatt Macy 205*51f32966SAlexander Motin static int match_field(char *map, jsmntok_t *field, int nz, 206*51f32966SAlexander Motin char **event, jsmntok_t *val) 207959826caSMatt Macy { 208959826caSMatt Macy struct field *f; 209959826caSMatt Macy jsmntok_t newval = *val; 210959826caSMatt Macy 211959826caSMatt Macy for (f = fields; f->field; f++) 212959826caSMatt Macy if (json_streq(map, field, f->field) && nz) { 213959826caSMatt Macy cut_comma(map, &newval); 214959826caSMatt Macy addfield(map, event, ",", f->kernel, &newval); 215959826caSMatt Macy return 1; 216959826caSMatt Macy } 217959826caSMatt Macy return 0; 218959826caSMatt Macy } 219959826caSMatt Macy 220*51f32966SAlexander Motin static struct msrmap *lookup_msr(char *map, jsmntok_t *val) 221959826caSMatt Macy { 222959826caSMatt Macy jsmntok_t newval = *val; 223959826caSMatt Macy static bool warned; 224959826caSMatt Macy int i; 225959826caSMatt Macy 226959826caSMatt Macy cut_comma(map, &newval); 227959826caSMatt Macy for (i = 0; msrmap[i].num; i++) 228959826caSMatt Macy if (json_streq(map, &newval, msrmap[i].num)) 229959826caSMatt Macy return &msrmap[i]; 230959826caSMatt Macy if (!warned) { 231959826caSMatt Macy warned = true; 232959826caSMatt Macy pr_err("%s: Unknown MSR in event file %.*s\n", prog, 233959826caSMatt Macy json_len(val), map + val->start); 234959826caSMatt Macy } 235959826caSMatt Macy return NULL; 236959826caSMatt Macy } 237959826caSMatt Macy 238959826caSMatt Macy static struct map { 239959826caSMatt Macy const char *json; 240959826caSMatt Macy const char *perf; 241959826caSMatt Macy } unit_to_pmu[] = { 242959826caSMatt Macy { "CBO", "uncore_cbox" }, 243959826caSMatt Macy { "QPI LL", "uncore_qpi" }, 244959826caSMatt Macy { "SBO", "uncore_sbox" }, 245959826caSMatt Macy { "iMPH-U", "uncore_arb" }, 24618054d02SAlexander Motin { "UPI LL", "uncore_upi" }, 24718054d02SAlexander Motin { "L3PMC", "amd_l3" }, 24818054d02SAlexander Motin { "DFPMC", "amd_df" }, 24918054d02SAlexander Motin { "cpu_core", "cpu_core" }, 25018054d02SAlexander Motin { "cpu_atom", "cpu_atom" }, 251959826caSMatt Macy {} 252959826caSMatt Macy }; 253959826caSMatt Macy 254*51f32966SAlexander Motin static const char *field_to_perf(struct map *table, char *map, jsmntok_t *val) 255959826caSMatt Macy { 256959826caSMatt Macy int i; 257959826caSMatt Macy 258959826caSMatt Macy for (i = 0; table[i].json; i++) { 259959826caSMatt Macy if (json_streq(map, val, table[i].json)) 260959826caSMatt Macy return table[i].perf; 261959826caSMatt Macy } 262959826caSMatt Macy return NULL; 263959826caSMatt Macy } 264959826caSMatt Macy 265959826caSMatt Macy #define EXPECT(e, t, m) do { if (!(e)) { \ 266959826caSMatt Macy jsmntok_t *loc = (t); \ 267959826caSMatt Macy if (!(t)->start && (t) > tokens) \ 268959826caSMatt Macy loc = (t) - 1; \ 269959826caSMatt Macy pr_err("%s:%d: " m ", got %s\n", fn, \ 270959826caSMatt Macy json_line(map, loc), \ 271959826caSMatt Macy json_name(t)); \ 272959826caSMatt Macy err = -EIO; \ 273959826caSMatt Macy goto out_free; \ 274959826caSMatt Macy } } while (0) 275959826caSMatt Macy 276959826caSMatt Macy static char *topic; 277959826caSMatt Macy 278*51f32966SAlexander Motin static char *get_topic(void) 279959826caSMatt Macy { 280959826caSMatt Macy char *tp; 281959826caSMatt Macy int i; 282959826caSMatt Macy 283959826caSMatt Macy /* tp is free'd in process_one_file() */ 284959826caSMatt Macy i = asprintf(&tp, "%s", topic); 285959826caSMatt Macy if (i < 0) { 286959826caSMatt Macy pr_info("%s: asprintf() error %s\n", prog); 287959826caSMatt Macy return NULL; 288959826caSMatt Macy } 289959826caSMatt Macy 290959826caSMatt Macy for (i = 0; i < (int) strlen(tp); i++) { 291959826caSMatt Macy char c = tp[i]; 292959826caSMatt Macy 293959826caSMatt Macy if (c == '-') 294959826caSMatt Macy tp[i] = ' '; 295959826caSMatt Macy else if (c == '.') { 296959826caSMatt Macy tp[i] = '\0'; 297959826caSMatt Macy break; 298959826caSMatt Macy } 299959826caSMatt Macy } 300959826caSMatt Macy 301959826caSMatt Macy return tp; 302959826caSMatt Macy } 303959826caSMatt Macy 304*51f32966SAlexander Motin static int add_topic(const char *bname) 305959826caSMatt Macy { 306959826caSMatt Macy free(topic); 307959826caSMatt Macy topic = strdup(bname); 308959826caSMatt Macy if (!topic) { 309959826caSMatt Macy pr_info("%s: strdup() error %s for file %s\n", prog, 310959826caSMatt Macy strerror(errno), bname); 311959826caSMatt Macy return -ENOMEM; 312959826caSMatt Macy } 313959826caSMatt Macy return 0; 314959826caSMatt Macy } 315959826caSMatt Macy 316959826caSMatt Macy struct perf_entry_data { 317959826caSMatt Macy FILE *outfp; 318959826caSMatt Macy char *topic; 319959826caSMatt Macy }; 320959826caSMatt Macy 321959826caSMatt Macy static int close_table; 322959826caSMatt Macy 323*51f32966SAlexander Motin static void print_events_table_prefix(FILE *fp, const char *tblname) 324959826caSMatt Macy { 325959826caSMatt Macy fprintf(fp, "static struct pmu_event %s[] = {\n", tblname); 326959826caSMatt Macy close_table = 1; 327959826caSMatt Macy } 328959826caSMatt Macy 329*51f32966SAlexander Motin static int print_events_table_entry(void *data, char *name, const char *event, 330*51f32966SAlexander Motin char *desc, char *long_desc, 331*51f32966SAlexander Motin char *pmu, char *unit, char *perpkg, 332*51f32966SAlexander Motin char *metric_expr, 333959826caSMatt Macy char *metric_name, char *metric_group) 334959826caSMatt Macy { 335959826caSMatt Macy struct perf_entry_data *pd = data; 336959826caSMatt Macy FILE *outfp = pd->outfp; 337959826caSMatt Macy char *etopic = pd->topic; 338959826caSMatt Macy 339959826caSMatt Macy /* 340959826caSMatt Macy * TODO: Remove formatting chars after debugging to reduce 341959826caSMatt Macy * string lengths. 342959826caSMatt Macy */ 343959826caSMatt Macy fprintf(outfp, "{\n"); 344959826caSMatt Macy 345959826caSMatt Macy if (name) 346959826caSMatt Macy fprintf(outfp, "\t.name = \"%s\",\n", name); 347959826caSMatt Macy if (event) 348959826caSMatt Macy fprintf(outfp, "\t.event = \"%s\",\n", event); 349959826caSMatt Macy fprintf(outfp, "\t.desc = \"%s\",\n", desc); 350959826caSMatt Macy fprintf(outfp, "\t.topic = \"%s\",\n", etopic); 351959826caSMatt Macy if (long_desc && long_desc[0]) 352959826caSMatt Macy fprintf(outfp, "\t.long_desc = \"%s\",\n", long_desc); 353959826caSMatt Macy if (pmu) 354959826caSMatt Macy fprintf(outfp, "\t.pmu = \"%s\",\n", pmu); 355959826caSMatt Macy if (unit) 356959826caSMatt Macy fprintf(outfp, "\t.unit = \"%s\",\n", unit); 357959826caSMatt Macy if (perpkg) 358959826caSMatt Macy fprintf(outfp, "\t.perpkg = \"%s\",\n", perpkg); 359959826caSMatt Macy if (metric_expr) 360959826caSMatt Macy fprintf(outfp, "\t.metric_expr = \"%s\",\n", metric_expr); 361959826caSMatt Macy if (metric_name) 362959826caSMatt Macy fprintf(outfp, "\t.metric_name = \"%s\",\n", metric_name); 363959826caSMatt Macy if (metric_group) 364959826caSMatt Macy fprintf(outfp, "\t.metric_group = \"%s\",\n", metric_group); 365959826caSMatt Macy fprintf(outfp, "},\n"); 366959826caSMatt Macy 367959826caSMatt Macy return 0; 368959826caSMatt Macy } 369959826caSMatt Macy 370959826caSMatt Macy struct event_struct { 371959826caSMatt Macy struct list_head list; 372959826caSMatt Macy char *name; 373959826caSMatt Macy char *event; 374959826caSMatt Macy char *desc; 375959826caSMatt Macy char *long_desc; 376959826caSMatt Macy char *pmu; 377959826caSMatt Macy char *unit; 378959826caSMatt Macy char *perpkg; 379959826caSMatt Macy char *metric_expr; 380959826caSMatt Macy char *metric_name; 381959826caSMatt Macy char *metric_group; 382959826caSMatt Macy }; 383959826caSMatt Macy 384959826caSMatt Macy #define ADD_EVENT_FIELD(field) do { if (field) { \ 385959826caSMatt Macy es->field = strdup(field); \ 386959826caSMatt Macy if (!es->field) \ 387959826caSMatt Macy goto out_free; \ 388959826caSMatt Macy } } while (0) 389959826caSMatt Macy 390959826caSMatt Macy #define FREE_EVENT_FIELD(field) free(es->field) 391959826caSMatt Macy 392959826caSMatt Macy #define TRY_FIXUP_FIELD(field) do { if (es->field && !*field) {\ 393959826caSMatt Macy *field = strdup(es->field); \ 394959826caSMatt Macy if (!*field) \ 395959826caSMatt Macy return -ENOMEM; \ 396959826caSMatt Macy } } while (0) 397959826caSMatt Macy 398959826caSMatt Macy #define FOR_ALL_EVENT_STRUCT_FIELDS(op) do { \ 399959826caSMatt Macy op(name); \ 400959826caSMatt Macy op(event); \ 401959826caSMatt Macy op(desc); \ 402959826caSMatt Macy op(long_desc); \ 403959826caSMatt Macy op(pmu); \ 404959826caSMatt Macy op(unit); \ 405959826caSMatt Macy op(perpkg); \ 406959826caSMatt Macy op(metric_expr); \ 407959826caSMatt Macy op(metric_name); \ 408959826caSMatt Macy op(metric_group); \ 409959826caSMatt Macy } while (0) 410959826caSMatt Macy 411959826caSMatt Macy static LIST_HEAD(arch_std_events); 412959826caSMatt Macy 413*51f32966SAlexander Motin static void free_arch_std_events(void) 414959826caSMatt Macy { 415959826caSMatt Macy struct event_struct *es, *next; 416959826caSMatt Macy 417959826caSMatt Macy list_for_each_entry_safe(es, next, &arch_std_events, list) { 418959826caSMatt Macy FOR_ALL_EVENT_STRUCT_FIELDS(FREE_EVENT_FIELD); 419959826caSMatt Macy list_del(&es->list); 420959826caSMatt Macy free(es); 421959826caSMatt Macy } 422959826caSMatt Macy } 423959826caSMatt Macy 424*51f32966SAlexander Motin static int save_arch_std_events(void *data __unused, char *name, const char *event, 425*51f32966SAlexander Motin char *desc, char *long_desc, char *pmu, 426*51f32966SAlexander Motin char *unit, char *perpkg, char *metric_expr, 427*51f32966SAlexander Motin char *metric_name, char *metric_group) 428959826caSMatt Macy { 429959826caSMatt Macy struct event_struct *es; 430959826caSMatt Macy 431959826caSMatt Macy es = malloc(sizeof(*es)); 432959826caSMatt Macy if (!es) 433959826caSMatt Macy return -ENOMEM; 434959826caSMatt Macy memset(es, 0, sizeof(*es)); 435959826caSMatt Macy FOR_ALL_EVENT_STRUCT_FIELDS(ADD_EVENT_FIELD); 436959826caSMatt Macy list_add_tail(&es->list, &arch_std_events); 437959826caSMatt Macy return 0; 438959826caSMatt Macy out_free: 439959826caSMatt Macy FOR_ALL_EVENT_STRUCT_FIELDS(FREE_EVENT_FIELD); 440959826caSMatt Macy free(es); 441959826caSMatt Macy return -ENOMEM; 442959826caSMatt Macy } 443959826caSMatt Macy 444*51f32966SAlexander Motin static void print_events_table_suffix(FILE *outfp) 445959826caSMatt Macy { 446959826caSMatt Macy fprintf(outfp, "{\n"); 447959826caSMatt Macy 448959826caSMatt Macy fprintf(outfp, "\t.name = 0,\n"); 449959826caSMatt Macy fprintf(outfp, "\t.event = 0,\n"); 450959826caSMatt Macy fprintf(outfp, "\t.desc = 0,\n"); 451959826caSMatt Macy 452959826caSMatt Macy fprintf(outfp, "},\n"); 453959826caSMatt Macy fprintf(outfp, "};\n"); 454959826caSMatt Macy close_table = 0; 455959826caSMatt Macy } 456959826caSMatt Macy 457959826caSMatt Macy static struct fixed { 458959826caSMatt Macy const char *name; 459959826caSMatt Macy const char *event; 460959826caSMatt Macy } fixed[] = { 461959826caSMatt Macy { "inst_retired.any", "event=0xc0" }, 462959826caSMatt Macy { "inst_retired.any_p", "event=0xc0" }, 463959826caSMatt Macy { "cpu_clk_unhalted.ref", "event=0x0,umask=0x03" }, 464959826caSMatt Macy { "cpu_clk_unhalted.thread", "event=0x3c" }, 465959826caSMatt Macy { "cpu_clk_unhalted.thread_any", "event=0x3c,any=1" }, 466959826caSMatt Macy { NULL, NULL}, 467959826caSMatt Macy }; 468959826caSMatt Macy 469959826caSMatt Macy /* 470959826caSMatt Macy * Handle different fixed counter encodings between JSON and perf. 471959826caSMatt Macy */ 472*51f32966SAlexander Motin static const char *real_event(const char *name, char *event) 473959826caSMatt Macy { 474959826caSMatt Macy int i; 475959826caSMatt Macy 476959826caSMatt Macy if (!name) 477959826caSMatt Macy return NULL; 478959826caSMatt Macy 479959826caSMatt Macy for (i = 0; fixed[i].name; i++) 480959826caSMatt Macy if (!strcasecmp(name, fixed[i].name)) 481959826caSMatt Macy return fixed[i].event; 482959826caSMatt Macy return event; 483959826caSMatt Macy } 484959826caSMatt Macy 485959826caSMatt Macy static int 486959826caSMatt Macy try_fixup(const char *fn, char *arch_std, char **event, char **desc, 487959826caSMatt Macy char **name, char **long_desc, char **pmu, char **filter __unused, 488959826caSMatt Macy char **perpkg, char **unit, char **metric_expr, char **metric_name, 489959826caSMatt Macy char **metric_group, unsigned long long eventcode) 490959826caSMatt Macy { 491959826caSMatt Macy /* try to find matching event from arch standard values */ 492959826caSMatt Macy struct event_struct *es; 493959826caSMatt Macy 494959826caSMatt Macy list_for_each_entry(es, &arch_std_events, list) { 495959826caSMatt Macy if (!strcmp(arch_std, es->name)) { 496959826caSMatt Macy if (!eventcode && es->event) { 497959826caSMatt Macy /* allow EventCode to be overridden */ 498959826caSMatt Macy free(*event); 499959826caSMatt Macy *event = NULL; 500959826caSMatt Macy } 501959826caSMatt Macy FOR_ALL_EVENT_STRUCT_FIELDS(TRY_FIXUP_FIELD); 502959826caSMatt Macy return 0; 503959826caSMatt Macy } 504959826caSMatt Macy } 505959826caSMatt Macy 506959826caSMatt Macy pr_err("%s: could not find matching %s for %s\n", 507959826caSMatt Macy prog, arch_std, fn); 508959826caSMatt Macy return -1; 509959826caSMatt Macy } 510959826caSMatt Macy 511959826caSMatt Macy /* Call func with each event in the json file */ 512*51f32966SAlexander Motin int json_events(const char *fn, 513959826caSMatt Macy int (*func)(void *data, char *name, const char *event, char *desc, 514959826caSMatt Macy char *long_desc, 515959826caSMatt Macy char *pmu, char *unit, char *perpkg, 516959826caSMatt Macy char *metric_expr, 517959826caSMatt Macy char *metric_name, char *metric_group), 518959826caSMatt Macy void *data) 519959826caSMatt Macy { 520959826caSMatt Macy int err; 521959826caSMatt Macy size_t size; 522959826caSMatt Macy jsmntok_t *tokens, *tok; 523959826caSMatt Macy int i, j, len; 524959826caSMatt Macy char *map; 525959826caSMatt Macy char buf[128]; 526959826caSMatt Macy 527959826caSMatt Macy if (!fn) 528959826caSMatt Macy return -ENOENT; 529959826caSMatt Macy 530959826caSMatt Macy tokens = parse_json(fn, &map, &size, &len); 531959826caSMatt Macy if (!tokens) 532959826caSMatt Macy return -EIO; 533959826caSMatt Macy EXPECT(tokens->type == JSMN_ARRAY, tokens, "expected top level array"); 534959826caSMatt Macy tok = tokens + 1; 535959826caSMatt Macy for (i = 0; i < tokens->size; i++) { 536959826caSMatt Macy char *event = NULL, *desc = NULL, *name = NULL; 537959826caSMatt Macy char *long_desc = NULL; 538959826caSMatt Macy char *extra_desc = NULL; 539959826caSMatt Macy char *pmu = NULL; 540959826caSMatt Macy char *filter = NULL; 541959826caSMatt Macy char *perpkg = NULL; 542959826caSMatt Macy char *unit = NULL; 543959826caSMatt Macy char *metric_expr = NULL; 544959826caSMatt Macy char *metric_name = NULL; 545959826caSMatt Macy char *metric_group = NULL; 546959826caSMatt Macy char *arch_std = NULL; 547959826caSMatt Macy unsigned long long eventcode = 0; 548959826caSMatt Macy struct msrmap *msr = NULL; 549959826caSMatt Macy jsmntok_t *msrval = NULL; 550959826caSMatt Macy jsmntok_t *precise = NULL; 551959826caSMatt Macy jsmntok_t *obj = tok++; 552959826caSMatt Macy 553959826caSMatt Macy EXPECT(obj->type == JSMN_OBJECT, obj, "expected object"); 554959826caSMatt Macy for (j = 0; j < obj->size; j += 2) { 555959826caSMatt Macy jsmntok_t *field, *val; 556959826caSMatt Macy int nz; 557959826caSMatt Macy char *s; 558959826caSMatt Macy 559959826caSMatt Macy field = tok + j; 560959826caSMatt Macy EXPECT(field->type == JSMN_STRING, tok + j, 561959826caSMatt Macy "Expected field name"); 562959826caSMatt Macy val = tok + j + 1; 563959826caSMatt Macy EXPECT(val->type == JSMN_STRING, tok + j + 1, 564959826caSMatt Macy "Expected string value"); 565959826caSMatt Macy 566959826caSMatt Macy nz = !json_streq(map, val, "0"); 567959826caSMatt Macy if (match_field(map, field, nz, &event, val)) { 568959826caSMatt Macy /* ok */ 569959826caSMatt Macy } else if (json_streq(map, field, "EventCode")) { 570959826caSMatt Macy char *code = NULL; 571959826caSMatt Macy addfield(map, &code, "", "", val); 572959826caSMatt Macy eventcode |= strtoul(code, NULL, 0); 573959826caSMatt Macy free(code); 574959826caSMatt Macy } else if (json_streq(map, field, "ExtSel")) { 575959826caSMatt Macy char *code = NULL; 576959826caSMatt Macy addfield(map, &code, "", "", val); 577959826caSMatt Macy eventcode |= strtoul(code, NULL, 0) << 21; 578959826caSMatt Macy free(code); 579959826caSMatt Macy } else if (json_streq(map, field, "EventName")) { 580959826caSMatt Macy addfield(map, &name, "", "", val); 581959826caSMatt Macy } else if (json_streq(map, field, "BriefDescription")) { 582959826caSMatt Macy addfield(map, &desc, "", "", val); 583959826caSMatt Macy fixdesc(desc); 584959826caSMatt Macy } else if (json_streq(map, field, 585959826caSMatt Macy "PublicDescription")) { 586959826caSMatt Macy addfield(map, &long_desc, "", "", val); 587959826caSMatt Macy fixdesc(long_desc); 588959826caSMatt Macy } else if (json_streq(map, field, "PEBS") && nz) { 589959826caSMatt Macy precise = val; 590959826caSMatt Macy } else if (json_streq(map, field, "MSRIndex") && nz) { 591959826caSMatt Macy msr = lookup_msr(map, val); 592959826caSMatt Macy } else if (json_streq(map, field, "MSRValue")) { 593959826caSMatt Macy msrval = val; 594959826caSMatt Macy } else if (json_streq(map, field, "Errata") && 595959826caSMatt Macy !json_streq(map, val, "null")) { 596959826caSMatt Macy addfield(map, &extra_desc, ". ", 597959826caSMatt Macy " Spec update: ", val); 598959826caSMatt Macy } else if (json_streq(map, field, "Data_LA") && nz) { 599959826caSMatt Macy addfield(map, &extra_desc, ". ", 600959826caSMatt Macy " Supports address when precise", 601959826caSMatt Macy NULL); 602959826caSMatt Macy } else if (json_streq(map, field, "Unit")) { 603959826caSMatt Macy const char *ppmu; 604959826caSMatt Macy 605959826caSMatt Macy ppmu = field_to_perf(unit_to_pmu, map, val); 606959826caSMatt Macy if (ppmu) { 607959826caSMatt Macy pmu = strdup(ppmu); 608959826caSMatt Macy } else { 609959826caSMatt Macy if (!pmu) 610959826caSMatt Macy pmu = strdup("uncore_"); 611959826caSMatt Macy addfield(map, &pmu, "", "", val); 612959826caSMatt Macy for (s = pmu; *s; s++) 613959826caSMatt Macy *s = tolower(*s); 614959826caSMatt Macy } 615959826caSMatt Macy addfield(map, &desc, ". ", "Unit: ", NULL); 616959826caSMatt Macy addfield(map, &desc, "", pmu, NULL); 617959826caSMatt Macy addfield(map, &desc, "", " ", NULL); 618959826caSMatt Macy } else if (json_streq(map, field, "Filter")) { 619959826caSMatt Macy addfield(map, &filter, "", "", val); 620959826caSMatt Macy } else if (json_streq(map, field, "ScaleUnit")) { 621959826caSMatt Macy addfield(map, &unit, "", "", val); 622959826caSMatt Macy } else if (json_streq(map, field, "PerPkg")) { 623959826caSMatt Macy addfield(map, &perpkg, "", "", val); 624959826caSMatt Macy } else if (json_streq(map, field, "MetricName")) { 625959826caSMatt Macy addfield(map, &metric_name, "", "", val); 626959826caSMatt Macy } else if (json_streq(map, field, "MetricGroup")) { 627959826caSMatt Macy addfield(map, &metric_group, "", "", val); 628959826caSMatt Macy } else if (json_streq(map, field, "MetricExpr")) { 629959826caSMatt Macy addfield(map, &metric_expr, "", "", val); 630959826caSMatt Macy for (s = metric_expr; *s; s++) 631959826caSMatt Macy *s = tolower(*s); 632959826caSMatt Macy } else if (json_streq(map, field, "ArchStdEvent")) { 633959826caSMatt Macy addfield(map, &arch_std, "", "", val); 634959826caSMatt Macy for (s = arch_std; *s; s++) 635959826caSMatt Macy *s = tolower(*s); 636959826caSMatt Macy } 637959826caSMatt Macy /* ignore unknown fields */ 638959826caSMatt Macy } 639959826caSMatt Macy if (precise && desc && !strstr(desc, "(Precise Event)")) { 640959826caSMatt Macy if (json_streq(map, precise, "2")) 641959826caSMatt Macy addfield(map, &extra_desc, " ", 642959826caSMatt Macy "(Must be precise)", NULL); 643959826caSMatt Macy else 644959826caSMatt Macy addfield(map, &extra_desc, " ", 645959826caSMatt Macy "(Precise event)", NULL); 646959826caSMatt Macy } 647b3d01a2aSEnji Cooper snprintf(buf, sizeof(buf), "event=%#llx", eventcode); 648959826caSMatt Macy addfield(map, &event, ",", buf, NULL); 649959826caSMatt Macy if (desc && extra_desc) 650959826caSMatt Macy addfield(map, &desc, " ", extra_desc, NULL); 651959826caSMatt Macy if (long_desc && extra_desc) 652959826caSMatt Macy addfield(map, &long_desc, " ", extra_desc, NULL); 653959826caSMatt Macy if (filter) 654959826caSMatt Macy addfield(map, &event, ",", filter, NULL); 655959826caSMatt Macy if (msr != NULL) 656959826caSMatt Macy addfield(map, &event, ",", msr->pname, msrval); 657959826caSMatt Macy if (name) 658959826caSMatt Macy fixname(name); 659959826caSMatt Macy 660959826caSMatt Macy if (arch_std) { 661959826caSMatt Macy /* 662959826caSMatt Macy * An arch standard event is referenced, so try to 663959826caSMatt Macy * fixup any unassigned values. 664959826caSMatt Macy */ 665959826caSMatt Macy err = try_fixup(fn, arch_std, &event, &desc, &name, 666959826caSMatt Macy &long_desc, &pmu, &filter, &perpkg, 667959826caSMatt Macy &unit, &metric_expr, &metric_name, 668959826caSMatt Macy &metric_group, eventcode); 669959826caSMatt Macy if (err) 670959826caSMatt Macy goto free_strings; 671959826caSMatt Macy } 672959826caSMatt Macy err = func(data, name, real_event(name, event), desc, long_desc, 673959826caSMatt Macy pmu, unit, perpkg, metric_expr, metric_name, metric_group); 674959826caSMatt Macy free_strings: 675959826caSMatt Macy free(event); 676959826caSMatt Macy free(desc); 677959826caSMatt Macy free(name); 678959826caSMatt Macy free(long_desc); 679959826caSMatt Macy free(extra_desc); 680959826caSMatt Macy free(pmu); 681959826caSMatt Macy free(filter); 682959826caSMatt Macy free(perpkg); 683959826caSMatt Macy free(unit); 684959826caSMatt Macy free(metric_expr); 685959826caSMatt Macy free(metric_name); 686959826caSMatt Macy free(metric_group); 687959826caSMatt Macy free(arch_std); 688959826caSMatt Macy 689959826caSMatt Macy if (err) 690959826caSMatt Macy break; 691959826caSMatt Macy tok += j; 692959826caSMatt Macy } 693959826caSMatt Macy EXPECT(tok - tokens == len, tok, "unexpected objects at end"); 694959826caSMatt Macy err = 0; 695959826caSMatt Macy out_free: 696959826caSMatt Macy free_json(map, size, tokens); 697959826caSMatt Macy return err; 698959826caSMatt Macy } 699959826caSMatt Macy 700*51f32966SAlexander Motin static char *file_name_to_table_name(const char *fname) 701959826caSMatt Macy { 702959826caSMatt Macy unsigned int i; 703959826caSMatt Macy int n; 704959826caSMatt Macy int c; 705959826caSMatt Macy char *tblname; 706959826caSMatt Macy 707959826caSMatt Macy 708959826caSMatt Macy /* 709959826caSMatt Macy * Ensure tablename starts with alphabetic character. 710959826caSMatt Macy * Derive rest of table name from basename of the JSON file, 711959826caSMatt Macy * replacing hyphens and stripping out .json suffix. 712959826caSMatt Macy */ 713959826caSMatt Macy n = asprintf(&tblname, "pme_%s", fname); 714959826caSMatt Macy if (n < 0) { 715959826caSMatt Macy pr_info("%s: asprintf() error %s for file %s\n", prog, 716959826caSMatt Macy strerror(errno), fname); 717959826caSMatt Macy return NULL; 718959826caSMatt Macy } 719959826caSMatt Macy 720959826caSMatt Macy for (i = 0; i < strlen(tblname); i++) { 721959826caSMatt Macy c = tblname[i]; 722959826caSMatt Macy 723959826caSMatt Macy if (c == '-' || c == '/') 724959826caSMatt Macy tblname[i] = '_'; 725959826caSMatt Macy else if (c == '.') { 726959826caSMatt Macy tblname[i] = '\0'; 727959826caSMatt Macy break; 728959826caSMatt Macy } else if (!isalnum(c) && c != '_') { 729959826caSMatt Macy char *tmp = strdup(fname); 730959826caSMatt Macy pr_err("%s: Invalid character '%c' in file name %s\n", 731959826caSMatt Macy prog, c, basename(tmp)); 732959826caSMatt Macy free(tblname); 733959826caSMatt Macy free(tmp); 734959826caSMatt Macy tblname = NULL; 735959826caSMatt Macy break; 736959826caSMatt Macy } 737959826caSMatt Macy } 738959826caSMatt Macy 739959826caSMatt Macy return tblname; 740959826caSMatt Macy } 741959826caSMatt Macy 742*51f32966SAlexander Motin static void print_mapping_table_prefix(FILE *outfp) 743959826caSMatt Macy { 744959826caSMatt Macy fprintf(outfp, "struct pmu_events_map pmu_events_map[] = {\n"); 745959826caSMatt Macy } 746959826caSMatt Macy 747*51f32966SAlexander Motin static void print_mapping_table_suffix(FILE *outfp) 748959826caSMatt Macy { 749959826caSMatt Macy /* 750959826caSMatt Macy * Print the terminating, NULL entry. 751959826caSMatt Macy */ 752959826caSMatt Macy fprintf(outfp, "{\n"); 753959826caSMatt Macy fprintf(outfp, "\t.cpuid = 0,\n"); 754959826caSMatt Macy fprintf(outfp, "\t.version = 0,\n"); 755959826caSMatt Macy fprintf(outfp, "\t.type = 0,\n"); 756959826caSMatt Macy fprintf(outfp, "\t.table = 0,\n"); 757959826caSMatt Macy fprintf(outfp, "},\n"); 758959826caSMatt Macy 759959826caSMatt Macy /* and finally, the closing curly bracket for the struct */ 760959826caSMatt Macy fprintf(outfp, "};\n"); 761959826caSMatt Macy } 762959826caSMatt Macy 763*51f32966SAlexander Motin static int process_mapfile(FILE *outfp, char *fpath) 764959826caSMatt Macy { 765959826caSMatt Macy int n = 16384; 766959826caSMatt Macy FILE *mapfp; 767959826caSMatt Macy char *save = NULL; 768959826caSMatt Macy char *line, *p; 769959826caSMatt Macy int line_num; 770959826caSMatt Macy char *tblname; 771959826caSMatt Macy 772959826caSMatt Macy pr_info("%s: Processing mapfile %s\n", prog, fpath); 773959826caSMatt Macy 774959826caSMatt Macy line = malloc(n); 775959826caSMatt Macy if (!line) 776959826caSMatt Macy return -1; 777959826caSMatt Macy 778959826caSMatt Macy mapfp = fopen(fpath, "r"); 779959826caSMatt Macy if (!mapfp) { 780959826caSMatt Macy pr_info("%s: Error %s opening %s\n", prog, strerror(errno), 781959826caSMatt Macy fpath); 7824644463cSEric van Gyzen free(line); 783959826caSMatt Macy return -1; 784959826caSMatt Macy } 785959826caSMatt Macy 786959826caSMatt Macy print_mapping_table_prefix(outfp); 787959826caSMatt Macy 788959826caSMatt Macy /* Skip first line (header) */ 789959826caSMatt Macy p = fgets(line, n, mapfp); 790959826caSMatt Macy if (!p) 791959826caSMatt Macy goto out; 792959826caSMatt Macy 793959826caSMatt Macy line_num = 1; 794959826caSMatt Macy while (1) { 795959826caSMatt Macy char *cpuid, *version, *type, *fname; 796959826caSMatt Macy 797959826caSMatt Macy line_num++; 798959826caSMatt Macy p = fgets(line, n, mapfp); 799959826caSMatt Macy if (!p) 800959826caSMatt Macy break; 801959826caSMatt Macy 802959826caSMatt Macy if (line[0] == '#' || line[0] == '\n') 803959826caSMatt Macy continue; 804959826caSMatt Macy 805959826caSMatt Macy if (line[strlen(line)-1] != '\n') { 806959826caSMatt Macy /* TODO Deal with lines longer than 16K */ 807959826caSMatt Macy pr_info("%s: Mapfile %s: line %d too long, aborting\n", 808959826caSMatt Macy prog, fpath, line_num); 809acde2586SEric van Gyzen free(line); 810acde2586SEric van Gyzen fclose(mapfp); 811959826caSMatt Macy return -1; 812959826caSMatt Macy } 813959826caSMatt Macy line[strlen(line)-1] = '\0'; 814959826caSMatt Macy 815959826caSMatt Macy cpuid = fixregex(strtok_r(p, ",", &save)); 816959826caSMatt Macy version = strtok_r(NULL, ",", &save); 817959826caSMatt Macy fname = strtok_r(NULL, ",", &save); 818959826caSMatt Macy type = strtok_r(NULL, ",", &save); 819959826caSMatt Macy 820959826caSMatt Macy tblname = file_name_to_table_name(fname); 821959826caSMatt Macy fprintf(outfp, "{\n"); 822959826caSMatt Macy fprintf(outfp, "\t.cpuid = \"%s\",\n", cpuid); 823959826caSMatt Macy fprintf(outfp, "\t.version = \"%s\",\n", version); 824959826caSMatt Macy fprintf(outfp, "\t.type = \"%s\",\n", type); 825959826caSMatt Macy 826959826caSMatt Macy /* 827959826caSMatt Macy * CHECK: We can't use the type (eg "core") field in the 828959826caSMatt Macy * table name. For us to do that, we need to somehow tweak 829959826caSMatt Macy * the other caller of file_name_to_table(), process_json() 830959826caSMatt Macy * to determine the type. process_json() file has no way 831959826caSMatt Macy * of knowing these are "core" events unless file name has 832959826caSMatt Macy * core in it. If filename has core in it, we can safely 833959826caSMatt Macy * ignore the type field here also. 834959826caSMatt Macy */ 835959826caSMatt Macy fprintf(outfp, "\t.table = %s\n", tblname); 836959826caSMatt Macy fprintf(outfp, "},\n"); 837959826caSMatt Macy } 838959826caSMatt Macy 839959826caSMatt Macy out: 840959826caSMatt Macy print_mapping_table_suffix(outfp); 8414644463cSEric van Gyzen free(line); 8424644463cSEric van Gyzen fclose(mapfp); 843959826caSMatt Macy return 0; 844959826caSMatt Macy } 845959826caSMatt Macy 846959826caSMatt Macy /* 847959826caSMatt Macy * If we fail to locate/process JSON and map files, create a NULL mapping 848959826caSMatt Macy * table. This would at least allow perf to build even if we can't find/use 849959826caSMatt Macy * the aliases. 850959826caSMatt Macy */ 851*51f32966SAlexander Motin static void create_empty_mapping(const char *output_file) 852959826caSMatt Macy { 853959826caSMatt Macy FILE *outfp; 854959826caSMatt Macy 855959826caSMatt Macy pr_info("%s: Creating empty pmu_events_map[] table\n", prog); 856959826caSMatt Macy 857959826caSMatt Macy /* Truncate file to clear any partial writes to it */ 858959826caSMatt Macy outfp = fopen(output_file, "w"); 859959826caSMatt Macy if (!outfp) { 860959826caSMatt Macy perror("fopen()"); 861959826caSMatt Macy _Exit(1); 862959826caSMatt Macy } 863959826caSMatt Macy 864959826caSMatt Macy fprintf(outfp, "#include \"pmu-events/pmu-events.h\"\n"); 865959826caSMatt Macy print_mapping_table_prefix(outfp); 866959826caSMatt Macy print_mapping_table_suffix(outfp); 867959826caSMatt Macy fclose(outfp); 868959826caSMatt Macy } 869959826caSMatt Macy 870*51f32966SAlexander Motin static int get_maxfds(void) 871959826caSMatt Macy { 872959826caSMatt Macy struct rlimit rlim; 873959826caSMatt Macy 8742fa224b1SAlex Richardson if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { 8752fa224b1SAlex Richardson if (rlim.rlim_max == RLIM_INFINITY) 8762fa224b1SAlex Richardson return 512; 877b3d01a2aSEnji Cooper return MIN(rlim.rlim_max / 2, 512); 8782fa224b1SAlex Richardson } 879959826caSMatt Macy 880959826caSMatt Macy return 512; 881959826caSMatt Macy } 882959826caSMatt Macy 883959826caSMatt Macy /* 884959826caSMatt Macy * nftw() doesn't let us pass an argument to the processing function, 885959826caSMatt Macy * so use a global variables. 886959826caSMatt Macy */ 887959826caSMatt Macy static FILE *eventsfp; 888959826caSMatt Macy static char *mapfile; 889959826caSMatt Macy 890*51f32966SAlexander Motin static int is_leaf_dir(const char *fpath) 891959826caSMatt Macy { 892959826caSMatt Macy DIR *d; 893959826caSMatt Macy struct dirent *dir; 894959826caSMatt Macy int res = 1; 895959826caSMatt Macy 896959826caSMatt Macy d = opendir(fpath); 897959826caSMatt Macy if (!d) 898959826caSMatt Macy return 0; 899959826caSMatt Macy 900959826caSMatt Macy while ((dir = readdir(d)) != NULL) { 901959826caSMatt Macy if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")) 902959826caSMatt Macy continue; 903959826caSMatt Macy 904959826caSMatt Macy if (dir->d_type == DT_DIR) { 905959826caSMatt Macy res = 0; 906959826caSMatt Macy break; 907959826caSMatt Macy } else if (dir->d_type == DT_UNKNOWN) { 908959826caSMatt Macy char path[PATH_MAX]; 909959826caSMatt Macy struct stat st; 910959826caSMatt Macy 911f824ea0cSConrad Meyer snprintf(path, sizeof(path), "%s/%s", fpath, 912f824ea0cSConrad Meyer dir->d_name); 913959826caSMatt Macy if (stat(path, &st)) 914959826caSMatt Macy break; 915959826caSMatt Macy 916959826caSMatt Macy if (S_ISDIR(st.st_mode)) { 917959826caSMatt Macy res = 0; 918959826caSMatt Macy break; 919959826caSMatt Macy } 920959826caSMatt Macy } 921959826caSMatt Macy } 922959826caSMatt Macy 923959826caSMatt Macy closedir(d); 924959826caSMatt Macy 925959826caSMatt Macy return res; 926959826caSMatt Macy } 927959826caSMatt Macy 928*51f32966SAlexander Motin static int is_json_file(const char *name) 929959826caSMatt Macy { 930959826caSMatt Macy const char *suffix; 931959826caSMatt Macy 932959826caSMatt Macy if (strlen(name) < 5) 933959826caSMatt Macy return 0; 934959826caSMatt Macy 935959826caSMatt Macy suffix = name + strlen(name) - 5; 936959826caSMatt Macy 937959826caSMatt Macy if (strncmp(suffix, ".json", 5) == 0) 938959826caSMatt Macy return 1; 939959826caSMatt Macy return 0; 940959826caSMatt Macy } 941959826caSMatt Macy 942*51f32966SAlexander Motin static int preprocess_arch_std_files(const char *fpath, const struct stat *sb, 943959826caSMatt Macy int typeflag, struct FTW *ftwbuf) 944959826caSMatt Macy { 945959826caSMatt Macy int level = ftwbuf->level; 946959826caSMatt Macy int is_file = typeflag == FTW_F; 947959826caSMatt Macy 948959826caSMatt Macy if (level == 1 && is_file && is_json_file(fpath)) 949959826caSMatt Macy return json_events(fpath, save_arch_std_events, (void *)(uintptr_t)sb); 950959826caSMatt Macy 951959826caSMatt Macy return 0; 952959826caSMatt Macy } 953959826caSMatt Macy 954*51f32966SAlexander Motin static int process_one_file(const char *fpath, const struct stat *sb, 955*51f32966SAlexander Motin int typeflag, struct FTW *ftwbuf) 956959826caSMatt Macy { 957959826caSMatt Macy char *tblname; 958959826caSMatt Macy const char *bname; 959959826caSMatt Macy int is_dir = typeflag == FTW_D; 960959826caSMatt Macy int is_file = typeflag == FTW_F; 961959826caSMatt Macy int level = ftwbuf->level; 962959826caSMatt Macy int err = 0; 963959826caSMatt Macy 964959826caSMatt Macy if (level == 2 && is_dir) { 965959826caSMatt Macy /* 966959826caSMatt Macy * For level 2 directory, bname will include parent name, 967959826caSMatt Macy * like vendor/platform. So search back from platform dir 968959826caSMatt Macy * to find this. 969959826caSMatt Macy */ 970959826caSMatt Macy bname = fpath + ftwbuf->base - 2; 971959826caSMatt Macy for (;;) { 972959826caSMatt Macy if (*bname == '/') 973959826caSMatt Macy break; 974959826caSMatt Macy bname--; 975959826caSMatt Macy } 976959826caSMatt Macy bname++; 977959826caSMatt Macy } else 978959826caSMatt Macy bname = fpath + ftwbuf->base; 979959826caSMatt Macy 980959826caSMatt Macy pr_debug("%s %d %7jd %-20s %s\n", 981959826caSMatt Macy is_file ? "f" : is_dir ? "d" : "x", 982959826caSMatt Macy level, sb->st_size, bname, fpath); 983959826caSMatt Macy 984959826caSMatt Macy /* base dir or too deep */ 985959826caSMatt Macy if (level == 0 || level > 3) 986959826caSMatt Macy return 0; 987959826caSMatt Macy 988959826caSMatt Macy 989959826caSMatt Macy /* model directory, reset topic */ 990959826caSMatt Macy if ((level == 1 && is_dir && is_leaf_dir(fpath)) || 991959826caSMatt Macy (level == 2 && is_dir)) { 992959826caSMatt Macy if (close_table) 993959826caSMatt Macy print_events_table_suffix(eventsfp); 994959826caSMatt Macy 995959826caSMatt Macy /* 996959826caSMatt Macy * Drop file name suffix. Replace hyphens with underscores. 997959826caSMatt Macy * Fail if file name contains any alphanum characters besides 998959826caSMatt Macy * underscores. 999959826caSMatt Macy */ 1000959826caSMatt Macy tblname = file_name_to_table_name(bname); 1001959826caSMatt Macy if (!tblname) { 1002959826caSMatt Macy pr_info("%s: Error determining table name for %s\n", prog, 1003959826caSMatt Macy bname); 1004959826caSMatt Macy return -1; 1005959826caSMatt Macy } 1006959826caSMatt Macy 1007959826caSMatt Macy print_events_table_prefix(eventsfp, tblname); 1008959826caSMatt Macy return 0; 1009959826caSMatt Macy } 1010959826caSMatt Macy 1011959826caSMatt Macy /* 1012959826caSMatt Macy * Save the mapfile name for now. We will process mapfile 1013959826caSMatt Macy * after processing all JSON files (so we can write out the 1014959826caSMatt Macy * mapping table after all PMU events tables). 1015959826caSMatt Macy * 1016959826caSMatt Macy */ 1017959826caSMatt Macy if (level == 1 && is_file) { 1018959826caSMatt Macy if (!strcmp(bname, "mapfile.csv")) { 1019959826caSMatt Macy mapfile = strdup(fpath); 1020959826caSMatt Macy return 0; 1021959826caSMatt Macy } 1022959826caSMatt Macy 1023959826caSMatt Macy pr_info("%s: Ignoring file %s\n", prog, fpath); 1024959826caSMatt Macy return 0; 1025959826caSMatt Macy } 1026959826caSMatt Macy 1027959826caSMatt Macy /* 1028959826caSMatt Macy * If the file name does not have a .json extension, 1029959826caSMatt Macy * ignore it. It could be a readme.txt for instance. 1030959826caSMatt Macy */ 1031959826caSMatt Macy if (is_file) { 1032959826caSMatt Macy if (!is_json_file(bname)) { 1033959826caSMatt Macy pr_info("%s: Ignoring file without .json suffix %s\n", prog, 1034959826caSMatt Macy fpath); 1035959826caSMatt Macy return 0; 1036959826caSMatt Macy } 1037959826caSMatt Macy } 1038959826caSMatt Macy 1039959826caSMatt Macy if (level > 1 && add_topic(bname)) 1040959826caSMatt Macy return -ENOMEM; 1041959826caSMatt Macy 1042959826caSMatt Macy /* 1043959826caSMatt Macy * Assume all other files are JSON files. 1044959826caSMatt Macy * 1045959826caSMatt Macy * If mapfile refers to 'power7_core.json', we create a table 1046959826caSMatt Macy * named 'power7_core'. Any inconsistencies between the mapfile 1047959826caSMatt Macy * and directory tree could result in build failure due to table 1048959826caSMatt Macy * names not being found. 1049959826caSMatt Macy * 1050959826caSMatt Macy * Atleast for now, be strict with processing JSON file names. 1051959826caSMatt Macy * i.e. if JSON file name cannot be mapped to C-style table name, 1052959826caSMatt Macy * fail. 1053959826caSMatt Macy */ 1054959826caSMatt Macy if (is_file) { 1055959826caSMatt Macy struct perf_entry_data data = { 1056959826caSMatt Macy .topic = get_topic(), 1057959826caSMatt Macy .outfp = eventsfp, 1058959826caSMatt Macy }; 1059959826caSMatt Macy 1060959826caSMatt Macy err = json_events(fpath, print_events_table_entry, &data); 1061959826caSMatt Macy 1062959826caSMatt Macy free(data.topic); 1063959826caSMatt Macy } 1064959826caSMatt Macy 1065959826caSMatt Macy return err; 1066959826caSMatt Macy } 1067959826caSMatt Macy 1068959826caSMatt Macy /* 1069959826caSMatt Macy * Starting in directory 'start_dirname', find the "mapfile.csv" and 1070959826caSMatt Macy * the set of JSON files for the architecture 'arch'. 1071959826caSMatt Macy * 1072959826caSMatt Macy * From each JSON file, create a C-style "PMU events table" from the 1073959826caSMatt Macy * JSON file (see struct pmu_event). 1074959826caSMatt Macy * 1075959826caSMatt Macy * From the mapfile, create a mapping between the CPU revisions and 1076959826caSMatt Macy * PMU event tables (see struct pmu_events_map). 1077959826caSMatt Macy * 1078959826caSMatt Macy * Write out the PMU events tables and the mapping table to pmu-event.c. 1079959826caSMatt Macy */ 1080*51f32966SAlexander Motin int main(int argc, char *argv[]) 1081959826caSMatt Macy { 1082959826caSMatt Macy int rc; 1083959826caSMatt Macy int maxfds; 1084959826caSMatt Macy char ldirname[PATH_MAX]; 1085959826caSMatt Macy 1086959826caSMatt Macy const char *arch; 1087959826caSMatt Macy const char *output_file; 1088959826caSMatt Macy const char *start_dirname; 1089959826caSMatt Macy struct stat stbuf; 1090959826caSMatt Macy 1091959826caSMatt Macy prog = basename(argv[0]); 1092959826caSMatt Macy if (argc < 4) { 1093959826caSMatt Macy pr_err("Usage: %s <arch> <starting_dir> <output_file>\n", prog); 1094959826caSMatt Macy return 1; 1095959826caSMatt Macy } 1096959826caSMatt Macy 1097959826caSMatt Macy arch = argv[1]; 1098959826caSMatt Macy start_dirname = argv[2]; 1099959826caSMatt Macy output_file = argv[3]; 1100959826caSMatt Macy 1101959826caSMatt Macy if (argc > 4) 1102959826caSMatt Macy verbose = atoi(argv[4]); 1103959826caSMatt Macy 1104959826caSMatt Macy eventsfp = fopen(output_file, "w"); 1105959826caSMatt Macy if (!eventsfp) { 1106959826caSMatt Macy pr_err("%s Unable to create required file %s (%s)\n", 1107959826caSMatt Macy prog, output_file, strerror(errno)); 1108959826caSMatt Macy return 2; 1109959826caSMatt Macy } 1110959826caSMatt Macy 1111f824ea0cSConrad Meyer snprintf(ldirname, sizeof(ldirname), "%s/%s", start_dirname, arch); 1112959826caSMatt Macy 1113959826caSMatt Macy /* If architecture does not have any event lists, bail out */ 1114959826caSMatt Macy if (stat(ldirname, &stbuf) < 0) { 1115959826caSMatt Macy pr_info("%s: Arch %s has no PMU event lists\n", prog, arch); 1116959826caSMatt Macy goto empty_map; 1117959826caSMatt Macy } 1118959826caSMatt Macy 1119959826caSMatt Macy /* Include pmu-events.h first */ 1120959826caSMatt Macy fprintf(eventsfp, "#include \"pmu-events/pmu-events.h\"\n"); 1121959826caSMatt Macy 1122959826caSMatt Macy /* 1123959826caSMatt Macy * The mapfile allows multiple CPUids to point to the same JSON file, 1124959826caSMatt Macy * so, not sure if there is a need for symlinks within the pmu-events 1125959826caSMatt Macy * directory. 1126959826caSMatt Macy * 1127959826caSMatt Macy * For now, treat symlinks of JSON files as regular files and create 1128959826caSMatt Macy * separate tables for each symlink (presumably, each symlink refers 1129959826caSMatt Macy * to specific version of the CPU). 1130959826caSMatt Macy */ 1131959826caSMatt Macy 1132959826caSMatt Macy maxfds = get_maxfds(); 1133959826caSMatt Macy mapfile = NULL; 1134334fd3daSMatt Macy rc = nftw_ordered(ldirname, preprocess_arch_std_files, maxfds, 0); 1135959826caSMatt Macy if (rc && verbose) { 11362fa224b1SAlex Richardson pr_info("%s: Error preprocessing arch standard files %s: %s\n", 11372fa224b1SAlex Richardson prog, ldirname, strerror(errno)); 1138959826caSMatt Macy goto empty_map; 1139959826caSMatt Macy } else if (rc < 0) { 1140959826caSMatt Macy /* Make build fail */ 1141959826caSMatt Macy free_arch_std_events(); 1142959826caSMatt Macy return 1; 1143959826caSMatt Macy } else if (rc) { 1144959826caSMatt Macy goto empty_map; 1145959826caSMatt Macy } 1146959826caSMatt Macy 1147334fd3daSMatt Macy rc = nftw_ordered(ldirname, process_one_file, maxfds, 0); 1148959826caSMatt Macy if (rc && verbose) { 1149959826caSMatt Macy pr_info("%s: Error walking file tree %s\n", prog, ldirname); 1150959826caSMatt Macy goto empty_map; 1151959826caSMatt Macy } else if (rc < 0) { 1152959826caSMatt Macy /* Make build fail */ 1153959826caSMatt Macy free_arch_std_events(); 1154959826caSMatt Macy return 1; 1155959826caSMatt Macy } else if (rc) { 1156959826caSMatt Macy goto empty_map; 1157959826caSMatt Macy } 1158959826caSMatt Macy 1159959826caSMatt Macy if (close_table) 1160959826caSMatt Macy print_events_table_suffix(eventsfp); 1161959826caSMatt Macy 1162959826caSMatt Macy if (!mapfile) { 1163959826caSMatt Macy pr_info("%s: No CPU->JSON mapping?\n", prog); 1164959826caSMatt Macy goto empty_map; 1165959826caSMatt Macy } 1166959826caSMatt Macy 1167959826caSMatt Macy if (process_mapfile(eventsfp, mapfile)) { 1168959826caSMatt Macy pr_info("%s: Error processing mapfile %s\n", prog, mapfile); 1169959826caSMatt Macy /* Make build fail */ 1170959826caSMatt Macy return 1; 1171959826caSMatt Macy } 1172959826caSMatt Macy 1173959826caSMatt Macy return 0; 1174959826caSMatt Macy 1175959826caSMatt Macy empty_map: 1176959826caSMatt Macy fclose(eventsfp); 1177959826caSMatt Macy create_empty_mapping(output_file); 1178959826caSMatt Macy free_arch_std_events(); 1179959826caSMatt Macy return 0; 1180959826caSMatt Macy } 1181334fd3daSMatt Macy 1182*51f32966SAlexander Motin #include <fts.h> 1183*51f32966SAlexander Motin 1184334fd3daSMatt Macy static int 1185334fd3daSMatt Macy fts_compare(const FTSENT * const *a, const FTSENT * const *b) 1186334fd3daSMatt Macy { 1187334fd3daSMatt Macy return (strcmp((*a)->fts_name, (*b)->fts_name)); 1188334fd3daSMatt Macy } 1189334fd3daSMatt Macy 1190334fd3daSMatt Macy static int 1191334fd3daSMatt Macy nftw_ordered(const char *path, int (*fn)(const char *, const struct stat *, int, 1192334fd3daSMatt Macy struct FTW *), int nfds, int ftwflags) 1193334fd3daSMatt Macy { 1194334fd3daSMatt Macy char * const paths[2] = { (char *)path, NULL }; 1195334fd3daSMatt Macy struct FTW ftw; 1196334fd3daSMatt Macy FTSENT *cur; 1197334fd3daSMatt Macy FTS *ftsp; 1198334fd3daSMatt Macy int error = 0, ftsflags, fnflag, postorder, sverrno; 1199334fd3daSMatt Macy 1200334fd3daSMatt Macy /* XXX - nfds is currently unused */ 1201334fd3daSMatt Macy if (nfds < 1) { 1202334fd3daSMatt Macy errno = EINVAL; 1203334fd3daSMatt Macy return (-1); 1204334fd3daSMatt Macy } 1205334fd3daSMatt Macy 1206334fd3daSMatt Macy ftsflags = FTS_COMFOLLOW; 1207334fd3daSMatt Macy if (!(ftwflags & FTW_CHDIR)) 1208334fd3daSMatt Macy ftsflags |= FTS_NOCHDIR; 1209334fd3daSMatt Macy if (ftwflags & FTW_MOUNT) 1210334fd3daSMatt Macy ftsflags |= FTS_XDEV; 1211334fd3daSMatt Macy if (ftwflags & FTW_PHYS) 1212334fd3daSMatt Macy ftsflags |= FTS_PHYSICAL; 1213334fd3daSMatt Macy else 1214334fd3daSMatt Macy ftsflags |= FTS_LOGICAL; 1215334fd3daSMatt Macy postorder = (ftwflags & FTW_DEPTH) != 0; 1216334fd3daSMatt Macy ftsp = fts_open(paths, ftsflags, fts_compare); 1217334fd3daSMatt Macy if (ftsp == NULL) 1218334fd3daSMatt Macy return (-1); 1219334fd3daSMatt Macy while ((cur = fts_read(ftsp)) != NULL) { 1220334fd3daSMatt Macy switch (cur->fts_info) { 1221334fd3daSMatt Macy case FTS_D: 1222334fd3daSMatt Macy if (postorder) 1223334fd3daSMatt Macy continue; 1224334fd3daSMatt Macy fnflag = FTW_D; 1225334fd3daSMatt Macy break; 1226334fd3daSMatt Macy case FTS_DC: 1227334fd3daSMatt Macy continue; 1228334fd3daSMatt Macy case FTS_DNR: 1229334fd3daSMatt Macy fnflag = FTW_DNR; 1230334fd3daSMatt Macy break; 1231334fd3daSMatt Macy case FTS_DP: 1232334fd3daSMatt Macy if (!postorder) 1233334fd3daSMatt Macy continue; 1234334fd3daSMatt Macy fnflag = FTW_DP; 1235334fd3daSMatt Macy break; 1236334fd3daSMatt Macy case FTS_F: 1237334fd3daSMatt Macy case FTS_DEFAULT: 1238334fd3daSMatt Macy fnflag = FTW_F; 1239334fd3daSMatt Macy break; 1240334fd3daSMatt Macy case FTS_NS: 1241334fd3daSMatt Macy case FTS_NSOK: 1242334fd3daSMatt Macy fnflag = FTW_NS; 1243334fd3daSMatt Macy break; 1244334fd3daSMatt Macy case FTS_SL: 1245334fd3daSMatt Macy fnflag = FTW_SL; 1246334fd3daSMatt Macy break; 1247334fd3daSMatt Macy case FTS_SLNONE: 1248334fd3daSMatt Macy fnflag = FTW_SLN; 1249334fd3daSMatt Macy break; 1250334fd3daSMatt Macy default: 1251334fd3daSMatt Macy error = -1; 1252334fd3daSMatt Macy goto done; 1253334fd3daSMatt Macy } 1254334fd3daSMatt Macy ftw.base = cur->fts_pathlen - cur->fts_namelen; 1255334fd3daSMatt Macy ftw.level = cur->fts_level; 1256334fd3daSMatt Macy error = fn(cur->fts_path, cur->fts_statp, fnflag, &ftw); 1257334fd3daSMatt Macy if (error != 0) 1258334fd3daSMatt Macy break; 1259334fd3daSMatt Macy } 1260334fd3daSMatt Macy done: 1261334fd3daSMatt Macy sverrno = errno; 1262334fd3daSMatt Macy if (fts_close(ftsp) != 0 && error == 0) 1263334fd3daSMatt Macy error = -1; 1264334fd3daSMatt Macy else 1265334fd3daSMatt Macy errno = sverrno; 1266334fd3daSMatt Macy return (error); 1267334fd3daSMatt Macy } 1268