File Index
Files and directories
Sign in
FILE

config.c

File preview with syntax highlighting.

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