projects/linux·blob master · refs

995 lines · 19.9 KiB

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