brintos

brintos / linux-shallow public Read only

0
0
Text · 18.8 KiB · 7a650de Raw
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, &section->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(&section->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(&section->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, &section->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, &section->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(&section->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(&section->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