1/* 2 * config.c 3 * 4 * Helper functions for parsing config items. 5 * Originally copied from GIT source. 6 * 7 * Copyright (C) Linus Torvalds, 2005 8 * Copyright (C) Johannes Schindelin, 2005 9 * 10 */ 11#include "util.h" 12#include "cache.h" 13#include "exec_cmd.h" 14#include "util/hist.h" /* perf_hist_config */ 15#include "util/llvm-utils.h" /* perf_llvm_config */ 16 17#define MAXNAME (256) 18 19#define DEBUG_CACHE_DIR ".debug" 20 21 22char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */ 23 24static FILE *config_file; 25static const char *config_file_name; 26static int config_linenr; 27static int config_file_eof; 28 29static const char *config_exclusive_filename; 30 31static int get_next_char(void) 32{ 33 int c; 34 FILE *f; 35 36 c = '\n'; 37 if ((f = config_file) != NULL) { 38 c = fgetc(f); 39 if (c == '\r') { 40 /* DOS like systems */ 41 c = fgetc(f); 42 if (c != '\n') { 43 ungetc(c, f); 44 c = '\r'; 45 } 46 } 47 if (c == '\n') 48 config_linenr++; 49 if (c == EOF) { 50 config_file_eof = 1; 51 c = '\n'; 52 } 53 } 54 return c; 55} 56 57static char *parse_value(void) 58{ 59 static char value[1024]; 60 int quote = 0, comment = 0, space = 0; 61 size_t len = 0; 62 63 for (;;) { 64 int c = get_next_char(); 65 66 if (len >= sizeof(value) - 1) 67 return NULL; 68 if (c == '\n') { 69 if (quote) 70 return NULL; 71 value[len] = 0; 72 return value; 73 } 74 if (comment) 75 continue; 76 if (isspace(c) && !quote) { 77 space = 1; 78 continue; 79 } 80 if (!quote) { 81 if (c == ';' || c == '#') { 82 comment = 1; 83 continue; 84 } 85 } 86 if (space) { 87 if (len) 88 value[len++] = ' '; 89 space = 0; 90 } 91 if (c == '\\') { 92 c = get_next_char(); 93 switch (c) { 94 case '\n': 95 continue; 96 case 't': 97 c = '\t'; 98 break; 99 case 'b': 100 c = '\b'; 101 break; 102 case 'n': 103 c = '\n'; 104 break; 105 /* Some characters escape as themselves */ 106 case '\\': case '"': 107 break; 108 /* Reject unknown escape sequences */ 109 default: 110 return NULL; 111 } 112 value[len++] = c; 113 continue; 114 } 115 if (c == '"') { 116 quote = 1-quote; 117 continue; 118 } 119 value[len++] = c; 120 } 121} 122 123static inline int iskeychar(int c) 124{ 125 return isalnum(c) || c == '-' || c == '_'; 126} 127 128static int get_value(config_fn_t fn, void *data, char *name, unsigned int len) 129{ 130 int c; 131 char *value; 132 133 /* Get the full name */ 134 for (;;) { 135 c = get_next_char(); 136 if (config_file_eof) 137 break; 138 if (!iskeychar(c)) 139 break; 140 name[len++] = c; 141 if (len >= MAXNAME) 142 return -1; 143 } 144 name[len] = 0; 145 while (c == ' ' || c == '\t') 146 c = get_next_char(); 147 148 value = NULL; 149 if (c != '\n') { 150 if (c != '=') 151 return -1; 152 value = parse_value(); 153 if (!value) 154 return -1; 155 } 156 return fn(name, value, data); 157} 158 159static int get_extended_base_var(char *name, int baselen, int c) 160{ 161 do { 162 if (c == '\n') 163 return -1; 164 c = get_next_char(); 165 } while (isspace(c)); 166 167 /* We require the format to be '[base "extension"]' */ 168 if (c != '"') 169 return -1; 170 name[baselen++] = '.'; 171 172 for (;;) { 173 int ch = get_next_char(); 174 175 if (ch == '\n') 176 return -1; 177 if (ch == '"') 178 break; 179 if (ch == '\\') { 180 ch = get_next_char(); 181 if (ch == '\n') 182 return -1; 183 } 184 name[baselen++] = ch; 185 if (baselen > MAXNAME / 2) 186 return -1; 187 } 188 189 /* Final ']' */ 190 if (get_next_char() != ']') 191 return -1; 192 return baselen; 193} 194 195static int get_base_var(char *name) 196{ 197 int baselen = 0; 198 199 for (;;) { 200 int c = get_next_char(); 201 if (config_file_eof) 202 return -1; 203 if (c == ']') 204 return baselen; 205 if (isspace(c)) 206 return get_extended_base_var(name, baselen, c); 207 if (!iskeychar(c) && c != '.') 208 return -1; 209 if (baselen > MAXNAME / 2) 210 return -1; 211 name[baselen++] = tolower(c); 212 } 213} 214 215static int perf_parse_file(config_fn_t fn, void *data) 216{ 217 int comment = 0; 218 int baselen = 0; 219 static char var[MAXNAME]; 220 221 /* U+FEFF Byte Order Mark in UTF8 */ 222 static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf"; 223 const unsigned char *bomptr = utf8_bom; 224 225 for (;;) { 226 int line, c = get_next_char(); 227 228 if (bomptr && *bomptr) { 229 /* We are at the file beginning; skip UTF8-encoded BOM 230 * if present. Sane editors won't put this in on their 231 * own, but e.g. Windows Notepad will do it happily. */ 232 if ((unsigned char) c == *bomptr) { 233 bomptr++; 234 continue; 235 } else { 236 /* Do not tolerate partial BOM. */ 237 if (bomptr != utf8_bom) 238 break; 239 /* No BOM at file beginning. Cool. */ 240 bomptr = NULL; 241 } 242 } 243 if (c == '\n') { 244 if (config_file_eof) 245 return 0; 246 comment = 0; 247 continue; 248 } 249 if (comment || isspace(c)) 250 continue; 251 if (c == '#' || c == ';') { 252 comment = 1; 253 continue; 254 } 255 if (c == '[') { 256 baselen = get_base_var(var); 257 if (baselen <= 0) 258 break; 259 var[baselen++] = '.'; 260 var[baselen] = 0; 261 continue; 262 } 263 if (!isalpha(c)) 264 break; 265 var[baselen] = tolower(c); 266 267 /* 268 * The get_value function might or might not reach the '\n', 269 * so saving the current line number for error reporting. 270 */ 271 line = config_linenr; 272 if (get_value(fn, data, var, baselen+1) < 0) { 273 config_linenr = line; 274 break; 275 } 276 } 277 die("bad config file line %d in %s", config_linenr, config_file_name); 278} 279 280static int parse_unit_factor(const char *end, unsigned long *val) 281{ 282 if (!*end) 283 return 1; 284 else if (!strcasecmp(end, "k")) { 285 *val *= 1024; 286 return 1; 287 } 288 else if (!strcasecmp(end, "m")) { 289 *val *= 1024 * 1024; 290 return 1; 291 } 292 else if (!strcasecmp(end, "g")) { 293 *val *= 1024 * 1024 * 1024; 294 return 1; 295 } 296 return 0; 297} 298 299static int perf_parse_llong(const char *value, long long *ret) 300{ 301 if (value && *value) { 302 char *end; 303 long long val = strtoll(value, &end, 0); 304 unsigned long factor = 1; 305 306 if (!parse_unit_factor(end, &factor)) 307 return 0; 308 *ret = val * factor; 309 return 1; 310 } 311 return 0; 312} 313 314static int perf_parse_long(const char *value, long *ret) 315{ 316 if (value && *value) { 317 char *end; 318 long val = strtol(value, &end, 0); 319 unsigned long factor = 1; 320 if (!parse_unit_factor(end, &factor)) 321 return 0; 322 *ret = val * factor; 323 return 1; 324 } 325 return 0; 326} 327 328static void die_bad_config(const char *name) 329{ 330 if (config_file_name) 331 die("bad config value for '%s' in %s", name, config_file_name); 332 die("bad config value for '%s'", name); 333} 334 335u64 perf_config_u64(const char *name, const char *value) 336{ 337 long long ret = 0; 338 339 if (!perf_parse_llong(value, &ret)) 340 die_bad_config(name); 341 return (u64) ret; 342} 343 344int perf_config_int(const char *name, const char *value) 345{ 346 long ret = 0; 347 if (!perf_parse_long(value, &ret)) 348 die_bad_config(name); 349 return ret; 350} 351 352static int perf_config_bool_or_int(const char *name, const char *value, int *is_bool) 353{ 354 *is_bool = 1; 355 if (!value) 356 return 1; 357 if (!*value) 358 return 0; 359 if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on")) 360 return 1; 361 if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off")) 362 return 0; 363 *is_bool = 0; 364 return perf_config_int(name, value); 365} 366 367int perf_config_bool(const char *name, const char *value) 368{ 369 int discard; 370 return !!perf_config_bool_or_int(name, value, &discard); 371} 372 373const char *perf_config_dirname(const char *name, const char *value) 374{ 375 if (!name) 376 return NULL; 377 return value; 378} 379 380static int perf_default_core_config(const char *var __maybe_unused, 381 const char *value __maybe_unused) 382{ 383 /* Add other config variables here. */ 384 return 0; 385} 386 387static int perf_ui_config(const char *var, const char *value) 388{ 389 /* Add other config variables here. */ 390 if (!strcmp(var, "ui.show-headers")) { 391 symbol_conf.show_hist_headers = perf_config_bool(var, value); 392 return 0; 393 } 394 return 0; 395} 396 397int perf_default_config(const char *var, const char *value, 398 void *dummy __maybe_unused) 399{ 400 if (!prefixcmp(var, "core.")) 401 return perf_default_core_config(var, value); 402 403 if (!prefixcmp(var, "hist.")) 404 return perf_hist_config(var, value); 405 406 if (!prefixcmp(var, "ui.")) 407 return perf_ui_config(var, value); 408 409 if (!prefixcmp(var, "call-graph.")) 410 return perf_callchain_config(var, value); 411 412 if (!prefixcmp(var, "llvm.")) 413 return perf_llvm_config(var, value); 414 415 /* Add other config variables here. */ 416 return 0; 417} 418 419static int perf_config_from_file(config_fn_t fn, const char *filename, void *data) 420{ 421 int ret; 422 FILE *f = fopen(filename, "r"); 423 424 ret = -1; 425 if (f) { 426 config_file = f; 427 config_file_name = filename; 428 config_linenr = 1; 429 config_file_eof = 0; 430 ret = perf_parse_file(fn, data); 431 fclose(f); 432 config_file_name = NULL; 433 } 434 return ret; 435} 436 437static const char *perf_etc_perfconfig(void) 438{ 439 static const char *system_wide; 440 if (!system_wide) 441 system_wide = system_path(ETC_PERFCONFIG); 442 return system_wide; 443} 444 445static int perf_env_bool(const char *k, int def) 446{ 447 const char *v = getenv(k); 448 return v ? perf_config_bool(k, v) : def; 449} 450 451static int perf_config_system(void) 452{ 453 return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0); 454} 455 456static int perf_config_global(void) 457{ 458 return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0); 459} 460 461int perf_config(config_fn_t fn, void *data) 462{ 463 int ret = 0, found = 0; 464 const char *home = NULL; 465 466 /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ 467 if (config_exclusive_filename) 468 return perf_config_from_file(fn, config_exclusive_filename, data); 469 if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) { 470 ret += perf_config_from_file(fn, perf_etc_perfconfig(), 471 data); 472 found += 1; 473 } 474 475 home = getenv("HOME"); 476 if (perf_config_global() && home) { 477 char *user_config = strdup(mkpath("%s/.perfconfig", home)); 478 struct stat st; 479 480 if (user_config == NULL) { 481 warning("Not enough memory to process %s/.perfconfig, " 482 "ignoring it.", home); 483 goto out; 484 } 485 486 if (stat(user_config, &st) < 0) 487 goto out_free; 488 489 if (st.st_uid && (st.st_uid != geteuid())) { 490 warning("File %s not owned by current user or root, " 491 "ignoring it.", user_config); 492 goto out_free; 493 } 494 495 if (!st.st_size) 496 goto out_free; 497 498 ret += perf_config_from_file(fn, user_config, data); 499 found += 1; 500out_free: 501 free(user_config); 502 } 503out: 504 if (found == 0) 505 return -1; 506 return ret; 507} 508 509/* 510 * Call this to report error for your variable that should not 511 * get a boolean value (i.e. "[my] var" means "true"). 512 */ 513int config_error_nonbool(const char *var) 514{ 515 return error("Missing value for '%s'", var); 516} 517 518struct buildid_dir_config { 519 char *dir; 520}; 521 522static int buildid_dir_command_config(const char *var, const char *value, 523 void *data) 524{ 525 struct buildid_dir_config *c = data; 526 const char *v; 527 528 /* same dir for all commands */ 529 if (!strcmp(var, "buildid.dir")) { 530 v = perf_config_dirname(var, value); 531 if (!v) 532 return -1; 533 strncpy(c->dir, v, MAXPATHLEN-1); 534 c->dir[MAXPATHLEN-1] = '\0'; 535 } 536 return 0; 537} 538 539static void check_buildid_dir_config(void) 540{ 541 struct buildid_dir_config c; 542 c.dir = buildid_dir; 543 perf_config(buildid_dir_command_config, &c); 544} 545 546void set_buildid_dir(const char *dir) 547{ 548 if (dir) 549 scnprintf(buildid_dir, MAXPATHLEN-1, "%s", dir); 550 551 /* try config file */ 552 if (buildid_dir[0] == '\0') 553 check_buildid_dir_config(); 554 555 /* default to $HOME/.debug */ 556 if (buildid_dir[0] == '\0') { 557 char *v = getenv("HOME"); 558 if (v) { 559 snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s", 560 v, DEBUG_CACHE_DIR); 561 } else { 562 strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1); 563 } 564 buildid_dir[MAXPATHLEN-1] = '\0'; 565 } 566 /* for communicating with external commands */ 567 setenv("PERF_BUILDID_DIR", buildid_dir, 1); 568} 569