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