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 <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 | |
38 | char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */ |
39 | |
40 | static FILE *config_file; |
41 | static const char *config_file_name; |
42 | static int config_linenr; |
43 | static int config_file_eof; |
44 | static struct perf_config_set *config_set; |
45 | |
46 | const char *config_exclusive_filename; |
47 | |
48 | static 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 | |
74 | static char *parse_value(void) |
75 | { |
76 | static char value[1024]; |
77 | int quote = 0, = 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 | |
140 | static inline int iskeychar(int c) |
141 | { |
142 | return isalnum(c) || c == '-' || c == '_'; |
143 | } |
144 | |
145 | static 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 | |
176 | static 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 | |
212 | static 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 | |
232 | static int perf_parse_file(config_fn_t fn, void *data) |
233 | { |
234 | int = 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 BOM |
247 | * if present. Sane editors won't put this in on their |
248 | * 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(name: 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, name: var, len: 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 | |
298 | static int parse_unit_factor(const char *end, unsigned long *val) |
299 | { |
300 | if (!*end) |
301 | return 1; |
302 | else if (!strcasecmp(s1: end, s2: "k" )) { |
303 | *val *= 1024; |
304 | return 1; |
305 | } |
306 | else if (!strcasecmp(s1: end, s2: "m" )) { |
307 | *val *= 1024 * 1024; |
308 | return 1; |
309 | } |
310 | else if (!strcasecmp(s1: end, s2: "g" )) { |
311 | *val *= 1024 * 1024 * 1024; |
312 | return 1; |
313 | } |
314 | return 0; |
315 | } |
316 | |
317 | static 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, val: &factor)) |
325 | return 0; |
326 | *ret = val * factor; |
327 | return 1; |
328 | } |
329 | return 0; |
330 | } |
331 | |
332 | static 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, val: &factor)) |
339 | return 0; |
340 | *ret = val * factor; |
341 | return 1; |
342 | } |
343 | return 0; |
344 | } |
345 | |
346 | static 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 | else |
351 | pr_warning("bad config value for '%s', ignoring...\n" , name); |
352 | } |
353 | |
354 | int 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: &ret)) { |
359 | bad_config(name); |
360 | return -1; |
361 | } |
362 | |
363 | *dest = ret; |
364 | return 0; |
365 | } |
366 | |
367 | int perf_config_int(int *dest, const char *name, const char *value) |
368 | { |
369 | long ret = 0; |
370 | if (!perf_parse_long(value, ret: &ret)) { |
371 | bad_config(name); |
372 | return -1; |
373 | } |
374 | *dest = ret; |
375 | return 0; |
376 | } |
377 | |
378 | int perf_config_u8(u8 *dest, const char *name, const char *value) |
379 | { |
380 | long ret = 0; |
381 | |
382 | if (!perf_parse_long(value, ret: &ret)) { |
383 | bad_config(name); |
384 | return -1; |
385 | } |
386 | *dest = ret; |
387 | return 0; |
388 | } |
389 | |
390 | static 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(s1: value, s2: "true" ) || !strcasecmp(s1: value, s2: "yes" ) || !strcasecmp(s1: value, s2: "on" )) |
400 | return 1; |
401 | if (!strcasecmp(s1: value, s2: "false" ) || !strcasecmp(s1: value, s2: "no" ) || !strcasecmp(s1: value, s2: "off" )) |
402 | return 0; |
403 | *is_bool = 0; |
404 | return perf_config_int(dest: &ret, name, value) < 0 ? -1 : ret; |
405 | } |
406 | |
407 | int perf_config_bool(const char *name, const char *value) |
408 | { |
409 | int discard; |
410 | return !!perf_config_bool_or_int(name, value, is_bool: &discard); |
411 | } |
412 | |
413 | static const char *perf_config_dirname(const char *name, const char *value) |
414 | { |
415 | if (!name) |
416 | return NULL; |
417 | return value; |
418 | } |
419 | |
420 | static 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(name: 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 | |
437 | static 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 | |
449 | static 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 | |
458 | static 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(name: var, value)); |
462 | |
463 | if (!strcmp(var, "stat.no-csv-summary" )) |
464 | perf_stat__set_no_csv_summary(perf_config_bool(name: 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 | |
473 | int perf_default_config(const char *var, const char *value, |
474 | void *dummy __maybe_unused) |
475 | { |
476 | if (strstarts(str: var, prefix: "core." )) |
477 | return perf_default_core_config(var, value); |
478 | |
479 | if (strstarts(str: var, prefix: "hist." )) |
480 | return perf_hist_config(var, value); |
481 | |
482 | if (strstarts(str: var, prefix: "ui." )) |
483 | return perf_ui_config(var, value); |
484 | |
485 | if (strstarts(str: var, prefix: "call-graph." )) |
486 | return perf_callchain_config(var, value); |
487 | |
488 | if (strstarts(str: var, prefix: "buildid." )) |
489 | return perf_buildid_config(var, value); |
490 | |
491 | if (strstarts(str: var, prefix: "stat." )) |
492 | return perf_stat_config(var, value); |
493 | |
494 | /* Add other config variables here. */ |
495 | return 0; |
496 | } |
497 | |
498 | static 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 | |
516 | const 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 | |
524 | static int perf_env_bool(const char *k, int def) |
525 | { |
526 | const char *v = getenv(k); |
527 | return v ? perf_config_bool(name: k, value: v) : def; |
528 | } |
529 | |
530 | int perf_config_system(void) |
531 | { |
532 | return !perf_env_bool(k: "PERF_CONFIG_NOSYSTEM" , def: 0); |
533 | } |
534 | |
535 | int perf_config_global(void) |
536 | { |
537 | return !perf_env_bool(k: "PERF_CONFIG_NOGLOBAL" , def: 0); |
538 | } |
539 | |
540 | static 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_buf: path, sz: sizeof(path), fmt: "%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 | |
574 | out_free: |
575 | free(config); |
576 | return NULL; |
577 | } |
578 | |
579 | const 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 | |
594 | static 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 | |
606 | static 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 | |
618 | static 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(list: §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(new: §ion->node, head: sections); |
635 | return section; |
636 | } |
637 | |
638 | static 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(new: &item->node, head: §ion->items); |
654 | return item; |
655 | } |
656 | |
657 | static 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 | |
669 | static 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 file |
712 | * 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 | |
724 | out_free: |
725 | free(key); |
726 | return ret; |
727 | } |
728 | |
729 | int 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, perf_config_set: set); |
734 | } |
735 | |
736 | static 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(fn: collect_config, filename: config_exclusive_filename, data: set); |
743 | if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) { |
744 | if (perf_config_from_file(fn: collect_config, filename: perf_etc_perfconfig(), data: set) < 0) |
745 | goto out; |
746 | } |
747 | if (perf_config_global() && perf_home_perfconfig()) { |
748 | if (perf_config_from_file(fn: collect_config, filename: perf_home_perfconfig(), data: set) < 0) |
749 | goto out; |
750 | } |
751 | |
752 | out: |
753 | return ret; |
754 | } |
755 | |
756 | struct 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(list: &set->sections); |
762 | perf_config_set__init(set); |
763 | } |
764 | |
765 | return set; |
766 | } |
767 | |
768 | struct 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(list: &set->sections); |
774 | perf_config_from_file(fn: collect_config, filename: file, data: set); |
775 | } |
776 | |
777 | return set; |
778 | } |
779 | |
780 | static 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 | |
788 | int 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(buf: key, size: sizeof(key), fmt: "%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 | } |
814 | out: |
815 | return ret; |
816 | } |
817 | |
818 | int 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(set: config_set, fn, data); |
824 | } |
825 | |
826 | void perf_config__exit(void) |
827 | { |
828 | perf_config_set__delete(set: config_set); |
829 | config_set = NULL; |
830 | } |
831 | |
832 | void perf_config__refresh(void) |
833 | { |
834 | perf_config__exit(); |
835 | perf_config__init(); |
836 | } |
837 | |
838 | static void perf_config_item__delete(struct perf_config_item *item) |
839 | { |
840 | zfree(&item->name); |
841 | zfree(&item->value); |
842 | free(item); |
843 | } |
844 | |
845 | static 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(entry: &item->node); |
851 | perf_config_item__delete(item); |
852 | } |
853 | } |
854 | |
855 | static 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 | |
862 | static 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(entry: §ion->node); |
868 | perf_config_section__delete(section); |
869 | } |
870 | } |
871 | |
872 | void 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 not |
883 | * get a boolean value (i.e. "[my] var" means "true"). |
884 | */ |
885 | int config_error_nonbool(const char *var) |
886 | { |
887 | pr_err("Missing value for '%s'" , var); |
888 | return -1; |
889 | } |
890 | |
891 | void 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 | |
912 | struct perf_config_scan_data { |
913 | const char *name; |
914 | const char *fmt; |
915 | va_list args; |
916 | int ret; |
917 | }; |
918 | |
919 | static 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 | |
929 | int 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(fn: perf_config_scan_cb, data: &d); |
938 | va_end(d.args); |
939 | |
940 | return d.ret; |
941 | } |
942 | |