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