linux/tools/perf/util/config.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * config.c
   4 *
   5 * Helper functions for parsing config items.
   6 * Originally copied from GIT source.
   7 *
   8 * Copyright (C) Linus Torvalds, 2005
   9 * Copyright (C) Johannes Schindelin, 2005
  10 *
  11 */
  12#include <errno.h>
  13#include <sys/param.h>
  14#include "util.h"
  15#include "cache.h"
  16#include <subcmd/exec-cmd.h>
  17#include "util/hist.h"  /* perf_hist_config */
  18#include "util/llvm-utils.h"   /* perf_llvm_config */
  19#include "config.h"
  20#include <sys/types.h>
  21#include <sys/stat.h>
  22#include <unistd.h>
  23#include <linux/string.h>
  24
  25#include "sane_ctype.h"
  26
  27#define MAXNAME (256)
  28
  29#define DEBUG_CACHE_DIR ".debug"
  30
  31
  32char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */
  33
  34static FILE *config_file;
  35static const char *config_file_name;
  36static int config_linenr;
  37static int config_file_eof;
  38static struct perf_config_set *config_set;
  39
  40const char *config_exclusive_filename;
  41
  42static int get_next_char(void)
  43{
  44        int c;
  45        FILE *f;
  46
  47        c = '\n';
  48        if ((f = config_file) != NULL) {
  49                c = fgetc(f);
  50                if (c == '\r') {
  51                        /* DOS like systems */
  52                        c = fgetc(f);
  53                        if (c != '\n') {
  54                                ungetc(c, f);
  55                                c = '\r';
  56                        }
  57                }
  58                if (c == '\n')
  59                        config_linenr++;
  60                if (c == EOF) {
  61                        config_file_eof = 1;
  62                        c = '\n';
  63                }
  64        }
  65        return c;
  66}
  67
  68static char *parse_value(void)
  69{
  70        static char value[1024];
  71        int quote = 0, comment = 0, space = 0;
  72        size_t len = 0;
  73
  74        for (;;) {
  75                int c = get_next_char();
  76
  77                if (len >= sizeof(value) - 1)
  78                        return NULL;
  79                if (c == '\n') {
  80                        if (quote)
  81                                return NULL;
  82                        value[len] = 0;
  83                        return value;
  84                }
  85                if (comment)
  86                        continue;
  87                if (isspace(c) && !quote) {
  88                        space = 1;
  89                        continue;
  90                }
  91                if (!quote) {
  92                        if (c == ';' || c == '#') {
  93                                comment = 1;
  94                                continue;
  95                        }
  96                }
  97                if (space) {
  98                        if (len)
  99                                value[len++] = ' ';
 100                        space = 0;
 101                }
 102                if (c == '\\') {
 103                        c = get_next_char();
 104                        switch (c) {
 105                        case '\n':
 106                                continue;
 107                        case 't':
 108                                c = '\t';
 109                                break;
 110                        case 'b':
 111                                c = '\b';
 112                                break;
 113                        case 'n':
 114                                c = '\n';
 115                                break;
 116                        /* Some characters escape as themselves */
 117                        case '\\': case '"':
 118                                break;
 119                        /* Reject unknown escape sequences */
 120                        default:
 121                                return NULL;
 122                        }
 123                        value[len++] = c;
 124                        continue;
 125                }
 126                if (c == '"') {
 127                        quote = 1-quote;
 128                        continue;
 129                }
 130                value[len++] = c;
 131        }
 132}
 133
 134static inline int iskeychar(int c)
 135{
 136        return isalnum(c) || c == '-' || c == '_';
 137}
 138
 139static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
 140{
 141        int c;
 142        char *value;
 143
 144        /* Get the full name */
 145        for (;;) {
 146                c = get_next_char();
 147                if (config_file_eof)
 148                        break;
 149                if (!iskeychar(c))
 150                        break;
 151                name[len++] = c;
 152                if (len >= MAXNAME)
 153                        return -1;
 154        }
 155        name[len] = 0;
 156        while (c == ' ' || c == '\t')
 157                c = get_next_char();
 158
 159        value = NULL;
 160        if (c != '\n') {
 161                if (c != '=')
 162                        return -1;
 163                value = parse_value();
 164                if (!value)
 165                        return -1;
 166        }
 167        return fn(name, value, data);
 168}
 169
 170static int get_extended_base_var(char *name, int baselen, int c)
 171{
 172        do {
 173                if (c == '\n')
 174                        return -1;
 175                c = get_next_char();
 176        } while (isspace(c));
 177
 178        /* We require the format to be '[base "extension"]' */
 179        if (c != '"')
 180                return -1;
 181        name[baselen++] = '.';
 182
 183        for (;;) {
 184                int ch = get_next_char();
 185
 186                if (ch == '\n')
 187                        return -1;
 188                if (ch == '"')
 189                        break;
 190                if (ch == '\\') {
 191                        ch = get_next_char();
 192                        if (ch == '\n')
 193                                return -1;
 194                }
 195                name[baselen++] = ch;
 196                if (baselen > MAXNAME / 2)
 197                        return -1;
 198        }
 199
 200        /* Final ']' */
 201        if (get_next_char() != ']')
 202                return -1;
 203        return baselen;
 204}
 205
 206static int get_base_var(char *name)
 207{
 208        int baselen = 0;
 209
 210        for (;;) {
 211                int c = get_next_char();
 212                if (config_file_eof)
 213                        return -1;
 214                if (c == ']')
 215                        return baselen;
 216                if (isspace(c))
 217                        return get_extended_base_var(name, baselen, c);
 218                if (!iskeychar(c) && c != '.')
 219                        return -1;
 220                if (baselen > MAXNAME / 2)
 221                        return -1;
 222                name[baselen++] = tolower(c);
 223        }
 224}
 225
 226static int perf_parse_file(config_fn_t fn, void *data)
 227{
 228        int comment = 0;
 229        int baselen = 0;
 230        static char var[MAXNAME];
 231
 232        /* U+FEFF Byte Order Mark in UTF8 */
 233        static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
 234        const unsigned char *bomptr = utf8_bom;
 235
 236        for (;;) {
 237                int line, c = get_next_char();
 238
 239                if (bomptr && *bomptr) {
 240                        /* We are at the file beginning; skip UTF8-encoded BOM
 241                         * if present. Sane editors won't put this in on their
 242                         * own, but e.g. Windows Notepad will do it happily. */
 243                        if ((unsigned char) c == *bomptr) {
 244                                bomptr++;
 245                                continue;
 246                        } else {
 247                                /* Do not tolerate partial BOM. */
 248                                if (bomptr != utf8_bom)
 249                                        break;
 250                                /* No BOM at file beginning. Cool. */
 251                                bomptr = NULL;
 252                        }
 253                }
 254                if (c == '\n') {
 255                        if (config_file_eof)
 256                                return 0;
 257                        comment = 0;
 258                        continue;
 259                }
 260                if (comment || isspace(c))
 261                        continue;
 262                if (c == '#' || c == ';') {
 263                        comment = 1;
 264                        continue;
 265                }
 266                if (c == '[') {
 267                        baselen = get_base_var(var);
 268                        if (baselen <= 0)
 269                                break;
 270                        var[baselen++] = '.';
 271                        var[baselen] = 0;
 272                        continue;
 273                }
 274                if (!isalpha(c))
 275                        break;
 276                var[baselen] = tolower(c);
 277
 278                /*
 279                 * The get_value function might or might not reach the '\n',
 280                 * so saving the current line number for error reporting.
 281                 */
 282                line = config_linenr;
 283                if (get_value(fn, data, var, baselen+1) < 0) {
 284                        config_linenr = line;
 285                        break;
 286                }
 287        }
 288        pr_err("bad config file line %d in %s\n", config_linenr, config_file_name);
 289        return -1;
 290}
 291
 292static int parse_unit_factor(const char *end, unsigned long *val)
 293{
 294        if (!*end)
 295                return 1;
 296        else if (!strcasecmp(end, "k")) {
 297                *val *= 1024;
 298                return 1;
 299        }
 300        else if (!strcasecmp(end, "m")) {
 301                *val *= 1024 * 1024;
 302                return 1;
 303        }
 304        else if (!strcasecmp(end, "g")) {
 305                *val *= 1024 * 1024 * 1024;
 306                return 1;
 307        }
 308        return 0;
 309}
 310
 311static int perf_parse_llong(const char *value, long long *ret)
 312{
 313        if (value && *value) {
 314                char *end;
 315                long long val = strtoll(value, &end, 0);
 316                unsigned long factor = 1;
 317
 318                if (!parse_unit_factor(end, &factor))
 319                        return 0;
 320                *ret = val * factor;
 321                return 1;
 322        }
 323        return 0;
 324}
 325
 326static int perf_parse_long(const char *value, long *ret)
 327{
 328        if (value && *value) {
 329                char *end;
 330                long val = strtol(value, &end, 0);
 331                unsigned long factor = 1;
 332                if (!parse_unit_factor(end, &factor))
 333                        return 0;
 334                *ret = val * factor;
 335                return 1;
 336        }
 337        return 0;
 338}
 339
 340static void bad_config(const char *name)
 341{
 342        if (config_file_name)
 343                pr_warning("bad config value for '%s' in %s, ignoring...\n", name, config_file_name);
 344        else
 345                pr_warning("bad config value for '%s', ignoring...\n", name);
 346}
 347
 348int perf_config_u64(u64 *dest, const char *name, const char *value)
 349{
 350        long long ret = 0;
 351
 352        if (!perf_parse_llong(value, &ret)) {
 353                bad_config(name);
 354                return -1;
 355        }
 356
 357        *dest = ret;
 358        return 0;
 359}
 360
 361int perf_config_int(int *dest, const char *name, const char *value)
 362{
 363        long ret = 0;
 364        if (!perf_parse_long(value, &ret)) {
 365                bad_config(name);
 366                return -1;
 367        }
 368        *dest = ret;
 369        return 0;
 370}
 371
 372static int perf_config_bool_or_int(const char *name, const char *value, int *is_bool)
 373{
 374        int ret;
 375
 376        *is_bool = 1;
 377        if (!value)
 378                return 1;
 379        if (!*value)
 380                return 0;
 381        if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
 382                return 1;
 383        if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
 384                return 0;
 385        *is_bool = 0;
 386        return perf_config_int(&ret, name, value) < 0 ? -1 : ret;
 387}
 388
 389int perf_config_bool(const char *name, const char *value)
 390{
 391        int discard;
 392        return !!perf_config_bool_or_int(name, value, &discard);
 393}
 394
 395static const char *perf_config_dirname(const char *name, const char *value)
 396{
 397        if (!name)
 398                return NULL;
 399        return value;
 400}
 401
 402static int perf_buildid_config(const char *var, const char *value)
 403{
 404        /* same dir for all commands */
 405        if (!strcmp(var, "buildid.dir")) {
 406                const char *dir = perf_config_dirname(var, value);
 407
 408                if (!dir) {
 409                        pr_err("Invalid buildid directory!\n");
 410                        return -1;
 411                }
 412                strncpy(buildid_dir, dir, MAXPATHLEN-1);
 413                buildid_dir[MAXPATHLEN-1] = '\0';
 414        }
 415
 416        return 0;
 417}
 418
 419static int perf_default_core_config(const char *var __maybe_unused,
 420                                    const char *value __maybe_unused)
 421{
 422        /* Add other config variables here. */
 423        return 0;
 424}
 425
 426static int perf_ui_config(const char *var, const char *value)
 427{
 428        /* Add other config variables here. */
 429        if (!strcmp(var, "ui.show-headers"))
 430                symbol_conf.show_hist_headers = perf_config_bool(var, value);
 431
 432        return 0;
 433}
 434
 435int perf_default_config(const char *var, const char *value,
 436                        void *dummy __maybe_unused)
 437{
 438        if (strstarts(var, "core."))
 439                return perf_default_core_config(var, value);
 440
 441        if (strstarts(var, "hist."))
 442                return perf_hist_config(var, value);
 443
 444        if (strstarts(var, "ui."))
 445                return perf_ui_config(var, value);
 446
 447        if (strstarts(var, "call-graph."))
 448                return perf_callchain_config(var, value);
 449
 450        if (strstarts(var, "llvm."))
 451                return perf_llvm_config(var, value);
 452
 453        if (strstarts(var, "buildid."))
 454                return perf_buildid_config(var, value);
 455
 456        /* Add other config variables here. */
 457        return 0;
 458}
 459
 460static int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
 461{
 462        int ret;
 463        FILE *f = fopen(filename, "r");
 464
 465        ret = -1;
 466        if (f) {
 467                config_file = f;
 468                config_file_name = filename;
 469                config_linenr = 1;
 470                config_file_eof = 0;
 471                ret = perf_parse_file(fn, data);
 472                fclose(f);
 473                config_file_name = NULL;
 474        }
 475        return ret;
 476}
 477
 478const char *perf_etc_perfconfig(void)
 479{
 480        static const char *system_wide;
 481        if (!system_wide)
 482                system_wide = system_path(ETC_PERFCONFIG);
 483        return system_wide;
 484}
 485
 486static int perf_env_bool(const char *k, int def)
 487{
 488        const char *v = getenv(k);
 489        return v ? perf_config_bool(k, v) : def;
 490}
 491
 492static int perf_config_system(void)
 493{
 494        return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0);
 495}
 496
 497static int perf_config_global(void)
 498{
 499        return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0);
 500}
 501
 502static struct perf_config_section *find_section(struct list_head *sections,
 503                                                const char *section_name)
 504{
 505        struct perf_config_section *section;
 506
 507        list_for_each_entry(section, sections, node)
 508                if (!strcmp(section->name, section_name))
 509                        return section;
 510
 511        return NULL;
 512}
 513
 514static struct perf_config_item *find_config_item(const char *name,
 515                                                 struct perf_config_section *section)
 516{
 517        struct perf_config_item *item;
 518
 519        list_for_each_entry(item, &section->items, node)
 520                if (!strcmp(item->name, name))
 521                        return item;
 522
 523        return NULL;
 524}
 525
 526static struct perf_config_section *add_section(struct list_head *sections,
 527                                               const char *section_name)
 528{
 529        struct perf_config_section *section = zalloc(sizeof(*section));
 530
 531        if (!section)
 532                return NULL;
 533
 534        INIT_LIST_HEAD(&section->items);
 535        section->name = strdup(section_name);
 536        if (!section->name) {
 537                pr_debug("%s: strdup failed\n", __func__);
 538                free(section);
 539                return NULL;
 540        }
 541
 542        list_add_tail(&section->node, sections);
 543        return section;
 544}
 545
 546static struct perf_config_item *add_config_item(struct perf_config_section *section,
 547                                                const char *name)
 548{
 549        struct perf_config_item *item = zalloc(sizeof(*item));
 550
 551        if (!item)
 552                return NULL;
 553
 554        item->name = strdup(name);
 555        if (!item->name) {
 556                pr_debug("%s: strdup failed\n", __func__);
 557                free(item);
 558                return NULL;
 559        }
 560
 561        list_add_tail(&item->node, &section->items);
 562        return item;
 563}
 564
 565static int set_value(struct perf_config_item *item, const char *value)
 566{
 567        char *val = strdup(value);
 568
 569        if (!val)
 570                return -1;
 571
 572        zfree(&item->value);
 573        item->value = val;
 574        return 0;
 575}
 576
 577static int collect_config(const char *var, const char *value,
 578                          void *perf_config_set)
 579{
 580        int ret = -1;
 581        char *ptr, *key;
 582        char *section_name, *name;
 583        struct perf_config_section *section = NULL;
 584        struct perf_config_item *item = NULL;
 585        struct perf_config_set *set = perf_config_set;
 586        struct list_head *sections;
 587
 588        if (set == NULL)
 589                return -1;
 590
 591        sections = &set->sections;
 592        key = ptr = strdup(var);
 593        if (!key) {
 594                pr_debug("%s: strdup failed\n", __func__);
 595                return -1;
 596        }
 597
 598        section_name = strsep(&ptr, ".");
 599        name = ptr;
 600        if (name == NULL || value == NULL)
 601                goto out_free;
 602
 603        section = find_section(sections, section_name);
 604        if (!section) {
 605                section = add_section(sections, section_name);
 606                if (!section)
 607                        goto out_free;
 608        }
 609
 610        item = find_config_item(name, section);
 611        if (!item) {
 612                item = add_config_item(section, name);
 613                if (!item)
 614                        goto out_free;
 615        }
 616
 617        /* perf_config_set can contain both user and system config items.
 618         * So we should know where each value is from.
 619         * The classification would be needed when a particular config file
 620         * is overwrited by setting feature i.e. set_config().
 621         */
 622        if (strcmp(config_file_name, perf_etc_perfconfig()) == 0) {
 623                section->from_system_config = true;
 624                item->from_system_config = true;
 625        } else {
 626                section->from_system_config = false;
 627                item->from_system_config = false;
 628        }
 629
 630        ret = set_value(item, value);
 631        return ret;
 632
 633out_free:
 634        free(key);
 635        return -1;
 636}
 637
 638int perf_config_set__collect(struct perf_config_set *set, const char *file_name,
 639                             const char *var, const char *value)
 640{
 641        config_file_name = file_name;
 642        return collect_config(var, value, set);
 643}
 644
 645static int perf_config_set__init(struct perf_config_set *set)
 646{
 647        int ret = -1;
 648        const char *home = NULL;
 649        char *user_config;
 650        struct stat st;
 651
 652        /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */
 653        if (config_exclusive_filename)
 654                return perf_config_from_file(collect_config, config_exclusive_filename, set);
 655        if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) {
 656                if (perf_config_from_file(collect_config, perf_etc_perfconfig(), set) < 0)
 657                        goto out;
 658        }
 659
 660        home = getenv("HOME");
 661
 662        /*
 663         * Skip reading user config if:
 664         *   - there is no place to read it from (HOME)
 665         *   - we are asked not to (PERF_CONFIG_NOGLOBAL=1)
 666         */
 667        if (!home || !*home || !perf_config_global())
 668                return 0;
 669
 670        user_config = strdup(mkpath("%s/.perfconfig", home));
 671        if (user_config == NULL) {
 672                pr_warning("Not enough memory to process %s/.perfconfig, ignoring it.", home);
 673                goto out;
 674        }
 675
 676        if (stat(user_config, &st) < 0) {
 677                if (errno == ENOENT)
 678                        ret = 0;
 679                goto out_free;
 680        }
 681
 682        ret = 0;
 683
 684        if (st.st_uid && (st.st_uid != geteuid())) {
 685                pr_warning("File %s not owned by current user or root, ignoring it.", user_config);
 686                goto out_free;
 687        }
 688
 689        if (st.st_size)
 690                ret = perf_config_from_file(collect_config, user_config, set);
 691
 692out_free:
 693        free(user_config);
 694out:
 695        return ret;
 696}
 697
 698struct perf_config_set *perf_config_set__new(void)
 699{
 700        struct perf_config_set *set = zalloc(sizeof(*set));
 701
 702        if (set) {
 703                INIT_LIST_HEAD(&set->sections);
 704                perf_config_set__init(set);
 705        }
 706
 707        return set;
 708}
 709
 710int perf_config(config_fn_t fn, void *data)
 711{
 712        int ret = 0;
 713        char key[BUFSIZ];
 714        struct perf_config_section *section;
 715        struct perf_config_item *item;
 716
 717        if (config_set == NULL)
 718                return -1;
 719
 720        perf_config_set__for_each_entry(config_set, section, item) {
 721                char *value = item->value;
 722
 723                if (value) {
 724                        scnprintf(key, sizeof(key), "%s.%s",
 725                                  section->name, item->name);
 726                        ret = fn(key, value, data);
 727                        if (ret < 0) {
 728                                pr_err("Error: wrong config key-value pair %s=%s\n",
 729                                       key, value);
 730                                break;
 731                        }
 732                }
 733        }
 734
 735        return ret;
 736}
 737
 738void perf_config__init(void)
 739{
 740        if (config_set == NULL)
 741                config_set = perf_config_set__new();
 742}
 743
 744void perf_config__exit(void)
 745{
 746        perf_config_set__delete(config_set);
 747        config_set = NULL;
 748}
 749
 750void perf_config__refresh(void)
 751{
 752        perf_config__exit();
 753        perf_config__init();
 754}
 755
 756static void perf_config_item__delete(struct perf_config_item *item)
 757{
 758        zfree(&item->name);
 759        zfree(&item->value);
 760        free(item);
 761}
 762
 763static void perf_config_section__purge(struct perf_config_section *section)
 764{
 765        struct perf_config_item *item, *tmp;
 766
 767        list_for_each_entry_safe(item, tmp, &section->items, node) {
 768                list_del_init(&item->node);
 769                perf_config_item__delete(item);
 770        }
 771}
 772
 773static void perf_config_section__delete(struct perf_config_section *section)
 774{
 775        perf_config_section__purge(section);
 776        zfree(&section->name);
 777        free(section);
 778}
 779
 780static void perf_config_set__purge(struct perf_config_set *set)
 781{
 782        struct perf_config_section *section, *tmp;
 783
 784        list_for_each_entry_safe(section, tmp, &set->sections, node) {
 785                list_del_init(&section->node);
 786                perf_config_section__delete(section);
 787        }
 788}
 789
 790void perf_config_set__delete(struct perf_config_set *set)
 791{
 792        if (set == NULL)
 793                return;
 794
 795        perf_config_set__purge(set);
 796        free(set);
 797}
 798
 799/*
 800 * Call this to report error for your variable that should not
 801 * get a boolean value (i.e. "[my] var" means "true").
 802 */
 803int config_error_nonbool(const char *var)
 804{
 805        pr_err("Missing value for '%s'", var);
 806        return -1;
 807}
 808
 809void set_buildid_dir(const char *dir)
 810{
 811        if (dir)
 812                scnprintf(buildid_dir, MAXPATHLEN-1, "%s", dir);
 813
 814        /* default to $HOME/.debug */
 815        if (buildid_dir[0] == '\0') {
 816                char *home = getenv("HOME");
 817
 818                if (home) {
 819                        snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s",
 820                                 home, DEBUG_CACHE_DIR);
 821                } else {
 822                        strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1);
 823                }
 824                buildid_dir[MAXPATHLEN-1] = '\0';
 825        }
 826        /* for communicating with external commands */
 827        setenv("PERF_BUILDID_DIR", buildid_dir, 1);
 828}
 829