linux/tools/perf/util/config.c
<<
>>
Prefs
   1/*
   2 * config.c
   3 *
   4 * Helper functions for parsing config items.
   5 * Originally copied from GIT source.
   6 *
   7 * Copyright (C) Linus Torvalds, 2005
   8 * Copyright (C) Johannes Schindelin, 2005
   9 *
  10 */
  11#include "util.h"
  12#include "cache.h"
  13#include <subcmd/exec-cmd.h>
  14#include "util/hist.h"  /* perf_hist_config */
  15#include "util/llvm-utils.h"   /* perf_llvm_config */
  16
  17#define MAXNAME (256)
  18
  19#define DEBUG_CACHE_DIR ".debug"
  20
  21
  22char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */
  23
  24static FILE *config_file;
  25static const char *config_file_name;
  26static int config_linenr;
  27static int config_file_eof;
  28
  29const char *config_exclusive_filename;
  30
  31static int get_next_char(void)
  32{
  33        int c;
  34        FILE *f;
  35
  36        c = '\n';
  37        if ((f = config_file) != NULL) {
  38                c = fgetc(f);
  39                if (c == '\r') {
  40                        /* DOS like systems */
  41                        c = fgetc(f);
  42                        if (c != '\n') {
  43                                ungetc(c, f);
  44                                c = '\r';
  45                        }
  46                }
  47                if (c == '\n')
  48                        config_linenr++;
  49                if (c == EOF) {
  50                        config_file_eof = 1;
  51                        c = '\n';
  52                }
  53        }
  54        return c;
  55}
  56
  57static char *parse_value(void)
  58{
  59        static char value[1024];
  60        int quote = 0, comment = 0, space = 0;
  61        size_t len = 0;
  62
  63        for (;;) {
  64                int c = get_next_char();
  65
  66                if (len >= sizeof(value) - 1)
  67                        return NULL;
  68                if (c == '\n') {
  69                        if (quote)
  70                                return NULL;
  71                        value[len] = 0;
  72                        return value;
  73                }
  74                if (comment)
  75                        continue;
  76                if (isspace(c) && !quote) {
  77                        space = 1;
  78                        continue;
  79                }
  80                if (!quote) {
  81                        if (c == ';' || c == '#') {
  82                                comment = 1;
  83                                continue;
  84                        }
  85                }
  86                if (space) {
  87                        if (len)
  88                                value[len++] = ' ';
  89                        space = 0;
  90                }
  91                if (c == '\\') {
  92                        c = get_next_char();
  93                        switch (c) {
  94                        case '\n':
  95                                continue;
  96                        case 't':
  97                                c = '\t';
  98                                break;
  99                        case 'b':
 100                                c = '\b';
 101                                break;
 102                        case 'n':
 103                                c = '\n';
 104                                break;
 105                        /* Some characters escape as themselves */
 106                        case '\\': case '"':
 107                                break;
 108                        /* Reject unknown escape sequences */
 109                        default:
 110                                return NULL;
 111                        }
 112                        value[len++] = c;
 113                        continue;
 114                }
 115                if (c == '"') {
 116                        quote = 1-quote;
 117                        continue;
 118                }
 119                value[len++] = c;
 120        }
 121}
 122
 123static inline int iskeychar(int c)
 124{
 125        return isalnum(c) || c == '-' || c == '_';
 126}
 127
 128static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
 129{
 130        int c;
 131        char *value;
 132
 133        /* Get the full name */
 134        for (;;) {
 135                c = get_next_char();
 136                if (config_file_eof)
 137                        break;
 138                if (!iskeychar(c))
 139                        break;
 140                name[len++] = c;
 141                if (len >= MAXNAME)
 142                        return -1;
 143        }
 144        name[len] = 0;
 145        while (c == ' ' || c == '\t')
 146                c = get_next_char();
 147
 148        value = NULL;
 149        if (c != '\n') {
 150                if (c != '=')
 151                        return -1;
 152                value = parse_value();
 153                if (!value)
 154                        return -1;
 155        }
 156        return fn(name, value, data);
 157}
 158
 159static int get_extended_base_var(char *name, int baselen, int c)
 160{
 161        do {
 162                if (c == '\n')
 163                        return -1;
 164                c = get_next_char();
 165        } while (isspace(c));
 166
 167        /* We require the format to be '[base "extension"]' */
 168        if (c != '"')
 169                return -1;
 170        name[baselen++] = '.';
 171
 172        for (;;) {
 173                int ch = get_next_char();
 174
 175                if (ch == '\n')
 176                        return -1;
 177                if (ch == '"')
 178                        break;
 179                if (ch == '\\') {
 180                        ch = get_next_char();
 181                        if (ch == '\n')
 182                                return -1;
 183                }
 184                name[baselen++] = ch;
 185                if (baselen > MAXNAME / 2)
 186                        return -1;
 187        }
 188
 189        /* Final ']' */
 190        if (get_next_char() != ']')
 191                return -1;
 192        return baselen;
 193}
 194
 195static int get_base_var(char *name)
 196{
 197        int baselen = 0;
 198
 199        for (;;) {
 200                int c = get_next_char();
 201                if (config_file_eof)
 202                        return -1;
 203                if (c == ']')
 204                        return baselen;
 205                if (isspace(c))
 206                        return get_extended_base_var(name, baselen, c);
 207                if (!iskeychar(c) && c != '.')
 208                        return -1;
 209                if (baselen > MAXNAME / 2)
 210                        return -1;
 211                name[baselen++] = tolower(c);
 212        }
 213}
 214
 215static int perf_parse_file(config_fn_t fn, void *data)
 216{
 217        int comment = 0;
 218        int baselen = 0;
 219        static char var[MAXNAME];
 220
 221        /* U+FEFF Byte Order Mark in UTF8 */
 222        static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
 223        const unsigned char *bomptr = utf8_bom;
 224
 225        for (;;) {
 226                int line, c = get_next_char();
 227
 228                if (bomptr && *bomptr) {
 229                        /* We are at the file beginning; skip UTF8-encoded BOM
 230                         * if present. Sane editors won't put this in on their
 231                         * own, but e.g. Windows Notepad will do it happily. */
 232                        if ((unsigned char) c == *bomptr) {
 233                                bomptr++;
 234                                continue;
 235                        } else {
 236                                /* Do not tolerate partial BOM. */
 237                                if (bomptr != utf8_bom)
 238                                        break;
 239                                /* No BOM at file beginning. Cool. */
 240                                bomptr = NULL;
 241                        }
 242                }
 243                if (c == '\n') {
 244                        if (config_file_eof)
 245                                return 0;
 246                        comment = 0;
 247                        continue;
 248                }
 249                if (comment || isspace(c))
 250                        continue;
 251                if (c == '#' || c == ';') {
 252                        comment = 1;
 253                        continue;
 254                }
 255                if (c == '[') {
 256                        baselen = get_base_var(var);
 257                        if (baselen <= 0)
 258                                break;
 259                        var[baselen++] = '.';
 260                        var[baselen] = 0;
 261                        continue;
 262                }
 263                if (!isalpha(c))
 264                        break;
 265                var[baselen] = tolower(c);
 266
 267                /*
 268                 * The get_value function might or might not reach the '\n',
 269                 * so saving the current line number for error reporting.
 270                 */
 271                line = config_linenr;
 272                if (get_value(fn, data, var, baselen+1) < 0) {
 273                        config_linenr = line;
 274                        break;
 275                }
 276        }
 277        die("bad config file line %d in %s", config_linenr, config_file_name);
 278}
 279
 280static int parse_unit_factor(const char *end, unsigned long *val)
 281{
 282        if (!*end)
 283                return 1;
 284        else if (!strcasecmp(end, "k")) {
 285                *val *= 1024;
 286                return 1;
 287        }
 288        else if (!strcasecmp(end, "m")) {
 289                *val *= 1024 * 1024;
 290                return 1;
 291        }
 292        else if (!strcasecmp(end, "g")) {
 293                *val *= 1024 * 1024 * 1024;
 294                return 1;
 295        }
 296        return 0;
 297}
 298
 299static int perf_parse_llong(const char *value, long long *ret)
 300{
 301        if (value && *value) {
 302                char *end;
 303                long long val = strtoll(value, &end, 0);
 304                unsigned long factor = 1;
 305
 306                if (!parse_unit_factor(end, &factor))
 307                        return 0;
 308                *ret = val * factor;
 309                return 1;
 310        }
 311        return 0;
 312}
 313
 314static int perf_parse_long(const char *value, long *ret)
 315{
 316        if (value && *value) {
 317                char *end;
 318                long val = strtol(value, &end, 0);
 319                unsigned long factor = 1;
 320                if (!parse_unit_factor(end, &factor))
 321                        return 0;
 322                *ret = val * factor;
 323                return 1;
 324        }
 325        return 0;
 326}
 327
 328static void die_bad_config(const char *name)
 329{
 330        if (config_file_name)
 331                die("bad config value for '%s' in %s", name, config_file_name);
 332        die("bad config value for '%s'", name);
 333}
 334
 335u64 perf_config_u64(const char *name, const char *value)
 336{
 337        long long ret = 0;
 338
 339        if (!perf_parse_llong(value, &ret))
 340                die_bad_config(name);
 341        return (u64) ret;
 342}
 343
 344int perf_config_int(const char *name, const char *value)
 345{
 346        long ret = 0;
 347        if (!perf_parse_long(value, &ret))
 348                die_bad_config(name);
 349        return ret;
 350}
 351
 352static int perf_config_bool_or_int(const char *name, const char *value, int *is_bool)
 353{
 354        *is_bool = 1;
 355        if (!value)
 356                return 1;
 357        if (!*value)
 358                return 0;
 359        if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
 360                return 1;
 361        if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
 362                return 0;
 363        *is_bool = 0;
 364        return perf_config_int(name, value);
 365}
 366
 367int perf_config_bool(const char *name, const char *value)
 368{
 369        int discard;
 370        return !!perf_config_bool_or_int(name, value, &discard);
 371}
 372
 373const char *perf_config_dirname(const char *name, const char *value)
 374{
 375        if (!name)
 376                return NULL;
 377        return value;
 378}
 379
 380static int perf_default_core_config(const char *var __maybe_unused,
 381                                    const char *value __maybe_unused)
 382{
 383        /* Add other config variables here. */
 384        return 0;
 385}
 386
 387static int perf_ui_config(const char *var, const char *value)
 388{
 389        /* Add other config variables here. */
 390        if (!strcmp(var, "ui.show-headers")) {
 391                symbol_conf.show_hist_headers = perf_config_bool(var, value);
 392                return 0;
 393        }
 394        return 0;
 395}
 396
 397int perf_default_config(const char *var, const char *value,
 398                        void *dummy __maybe_unused)
 399{
 400        if (!prefixcmp(var, "core."))
 401                return perf_default_core_config(var, value);
 402
 403        if (!prefixcmp(var, "hist."))
 404                return perf_hist_config(var, value);
 405
 406        if (!prefixcmp(var, "ui."))
 407                return perf_ui_config(var, value);
 408
 409        if (!prefixcmp(var, "call-graph."))
 410                return perf_callchain_config(var, value);
 411
 412        if (!prefixcmp(var, "llvm."))
 413                return perf_llvm_config(var, value);
 414
 415        /* Add other config variables here. */
 416        return 0;
 417}
 418
 419static int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
 420{
 421        int ret;
 422        FILE *f = fopen(filename, "r");
 423
 424        ret = -1;
 425        if (f) {
 426                config_file = f;
 427                config_file_name = filename;
 428                config_linenr = 1;
 429                config_file_eof = 0;
 430                ret = perf_parse_file(fn, data);
 431                fclose(f);
 432                config_file_name = NULL;
 433        }
 434        return ret;
 435}
 436
 437const char *perf_etc_perfconfig(void)
 438{
 439        static const char *system_wide;
 440        if (!system_wide)
 441                system_wide = system_path(ETC_PERFCONFIG);
 442        return system_wide;
 443}
 444
 445static int perf_env_bool(const char *k, int def)
 446{
 447        const char *v = getenv(k);
 448        return v ? perf_config_bool(k, v) : def;
 449}
 450
 451static int perf_config_system(void)
 452{
 453        return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0);
 454}
 455
 456static int perf_config_global(void)
 457{
 458        return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0);
 459}
 460
 461int perf_config(config_fn_t fn, void *data)
 462{
 463        int ret = 0, found = 0;
 464        const char *home = NULL;
 465
 466        /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */
 467        if (config_exclusive_filename)
 468                return perf_config_from_file(fn, config_exclusive_filename, data);
 469        if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) {
 470                ret += perf_config_from_file(fn, perf_etc_perfconfig(),
 471                                            data);
 472                found += 1;
 473        }
 474
 475        home = getenv("HOME");
 476        if (perf_config_global() && home) {
 477                char *user_config = strdup(mkpath("%s/.perfconfig", home));
 478                struct stat st;
 479
 480                if (user_config == NULL) {
 481                        warning("Not enough memory to process %s/.perfconfig, "
 482                                "ignoring it.", home);
 483                        goto out;
 484                }
 485
 486                if (stat(user_config, &st) < 0)
 487                        goto out_free;
 488
 489                if (st.st_uid && (st.st_uid != geteuid())) {
 490                        warning("File %s not owned by current user or root, "
 491                                "ignoring it.", user_config);
 492                        goto out_free;
 493                }
 494
 495                if (!st.st_size)
 496                        goto out_free;
 497
 498                ret += perf_config_from_file(fn, user_config, data);
 499                found += 1;
 500out_free:
 501                free(user_config);
 502        }
 503out:
 504        if (found == 0)
 505                return -1;
 506        return ret;
 507}
 508
 509/*
 510 * Call this to report error for your variable that should not
 511 * get a boolean value (i.e. "[my] var" means "true").
 512 */
 513int config_error_nonbool(const char *var)
 514{
 515        return error("Missing value for '%s'", var);
 516}
 517
 518struct buildid_dir_config {
 519        char *dir;
 520};
 521
 522static int buildid_dir_command_config(const char *var, const char *value,
 523                                      void *data)
 524{
 525        struct buildid_dir_config *c = data;
 526        const char *v;
 527
 528        /* same dir for all commands */
 529        if (!strcmp(var, "buildid.dir")) {
 530                v = perf_config_dirname(var, value);
 531                if (!v)
 532                        return -1;
 533                strncpy(c->dir, v, MAXPATHLEN-1);
 534                c->dir[MAXPATHLEN-1] = '\0';
 535        }
 536        return 0;
 537}
 538
 539static void check_buildid_dir_config(void)
 540{
 541        struct buildid_dir_config c;
 542        c.dir = buildid_dir;
 543        perf_config(buildid_dir_command_config, &c);
 544}
 545
 546void set_buildid_dir(const char *dir)
 547{
 548        if (dir)
 549                scnprintf(buildid_dir, MAXPATHLEN-1, "%s", dir);
 550
 551        /* try config file */
 552        if (buildid_dir[0] == '\0')
 553                check_buildid_dir_config();
 554
 555        /* default to $HOME/.debug */
 556        if (buildid_dir[0] == '\0') {
 557                char *v = getenv("HOME");
 558                if (v) {
 559                        snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s",
 560                                 v, DEBUG_CACHE_DIR);
 561                } else {
 562                        strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1);
 563                }
 564                buildid_dir[MAXPATHLEN-1] = '\0';
 565        }
 566        /* for communicating with external commands */
 567        setenv("PERF_BUILDID_DIR", buildid_dir, 1);
 568}
 569