cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

config.c (18636B)


      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/llvm-utils.h"   /* perf_llvm_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 "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
     38char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */
     39
     40static FILE *config_file;
     41static const char *config_file_name;
     42static int config_linenr;
     43static int config_file_eof;
     44static struct perf_config_set *config_set;
     45
     46const char *config_exclusive_filename;
     47
     48static 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
     74static char *parse_value(void)
     75{
     76	static char value[1024];
     77	int quote = 0, comment = 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
    140static inline int iskeychar(int c)
    141{
    142	return isalnum(c) || c == '-' || c == '_';
    143}
    144
    145static 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
    176static 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
    212static 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
    232static int perf_parse_file(config_fn_t fn, void *data)
    233{
    234	int comment = 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(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, var, 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
    298static int parse_unit_factor(const char *end, unsigned long *val)
    299{
    300	if (!*end)
    301		return 1;
    302	else if (!strcasecmp(end, "k")) {
    303		*val *= 1024;
    304		return 1;
    305	}
    306	else if (!strcasecmp(end, "m")) {
    307		*val *= 1024 * 1024;
    308		return 1;
    309	}
    310	else if (!strcasecmp(end, "g")) {
    311		*val *= 1024 * 1024 * 1024;
    312		return 1;
    313	}
    314	return 0;
    315}
    316
    317static 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, &factor))
    325			return 0;
    326		*ret = val * factor;
    327		return 1;
    328	}
    329	return 0;
    330}
    331
    332static 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, &factor))
    339			return 0;
    340		*ret = val * factor;
    341		return 1;
    342	}
    343	return 0;
    344}
    345
    346static 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
    354int 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)) {
    359		bad_config(name);
    360		return -1;
    361	}
    362
    363	*dest = ret;
    364	return 0;
    365}
    366
    367int perf_config_int(int *dest, const char *name, const char *value)
    368{
    369	long ret = 0;
    370	if (!perf_parse_long(value, &ret)) {
    371		bad_config(name);
    372		return -1;
    373	}
    374	*dest = ret;
    375	return 0;
    376}
    377
    378int perf_config_u8(u8 *dest, const char *name, const char *value)
    379{
    380	long ret = 0;
    381
    382	if (!perf_parse_long(value, &ret)) {
    383		bad_config(name);
    384		return -1;
    385	}
    386	*dest = ret;
    387	return 0;
    388}
    389
    390static 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(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
    400		return 1;
    401	if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
    402		return 0;
    403	*is_bool = 0;
    404	return perf_config_int(&ret, name, value) < 0 ? -1 : ret;
    405}
    406
    407int perf_config_bool(const char *name, const char *value)
    408{
    409	int discard;
    410	return !!perf_config_bool_or_int(name, value, &discard);
    411}
    412
    413static const char *perf_config_dirname(const char *name, const char *value)
    414{
    415	if (!name)
    416		return NULL;
    417	return value;
    418}
    419
    420static 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(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
    437static int perf_default_core_config(const char *var __maybe_unused,
    438				    const char *value __maybe_unused)
    439{
    440	if (!strcmp(var, "core.proc-map-timeout"))
    441		proc_map_timeout = strtoul(value, NULL, 10);
    442
    443	/* Add other config variables here. */
    444	return 0;
    445}
    446
    447static int perf_ui_config(const char *var, const char *value)
    448{
    449	/* Add other config variables here. */
    450	if (!strcmp(var, "ui.show-headers"))
    451		symbol_conf.show_hist_headers = perf_config_bool(var, value);
    452
    453	return 0;
    454}
    455
    456static int perf_stat_config(const char *var, const char *value)
    457{
    458	if (!strcmp(var, "stat.big-num"))
    459		perf_stat__set_big_num(perf_config_bool(var, value));
    460
    461	if (!strcmp(var, "stat.no-csv-summary"))
    462		perf_stat__set_no_csv_summary(perf_config_bool(var, value));
    463
    464	if (!strcmp(var, "stat.bpf-counter-events"))
    465		evsel__bpf_counter_events = strdup(value);
    466
    467	/* Add other config variables here. */
    468	return 0;
    469}
    470
    471int perf_default_config(const char *var, const char *value,
    472			void *dummy __maybe_unused)
    473{
    474	if (strstarts(var, "core."))
    475		return perf_default_core_config(var, value);
    476
    477	if (strstarts(var, "hist."))
    478		return perf_hist_config(var, value);
    479
    480	if (strstarts(var, "ui."))
    481		return perf_ui_config(var, value);
    482
    483	if (strstarts(var, "call-graph."))
    484		return perf_callchain_config(var, value);
    485
    486	if (strstarts(var, "llvm."))
    487		return perf_llvm_config(var, value);
    488
    489	if (strstarts(var, "buildid."))
    490		return perf_buildid_config(var, value);
    491
    492	if (strstarts(var, "stat."))
    493		return perf_stat_config(var, value);
    494
    495	/* Add other config variables here. */
    496	return 0;
    497}
    498
    499static int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
    500{
    501	int ret;
    502	FILE *f = fopen(filename, "r");
    503
    504	ret = -1;
    505	if (f) {
    506		config_file = f;
    507		config_file_name = filename;
    508		config_linenr = 1;
    509		config_file_eof = 0;
    510		ret = perf_parse_file(fn, data);
    511		fclose(f);
    512		config_file_name = NULL;
    513	}
    514	return ret;
    515}
    516
    517const char *perf_etc_perfconfig(void)
    518{
    519	static const char *system_wide;
    520	if (!system_wide)
    521		system_wide = system_path(ETC_PERFCONFIG);
    522	return system_wide;
    523}
    524
    525static int perf_env_bool(const char *k, int def)
    526{
    527	const char *v = getenv(k);
    528	return v ? perf_config_bool(k, v) : def;
    529}
    530
    531int perf_config_system(void)
    532{
    533	return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0);
    534}
    535
    536int perf_config_global(void)
    537{
    538	return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0);
    539}
    540
    541static char *home_perfconfig(void)
    542{
    543	const char *home = NULL;
    544	char *config;
    545	struct stat st;
    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("%s/.perfconfig", home));
    558	if (config == NULL) {
    559		pr_warning("Not enough memory to process %s/.perfconfig, ignoring it.", 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.", config);
    568		goto out_free;
    569	}
    570
    571	if (st.st_size)
    572		return config;
    573
    574out_free:
    575	free(config);
    576	return NULL;
    577}
    578
    579const 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
    594static 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
    606static 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, &section->items, node)
    612		if (!strcmp(item->name, name))
    613			return item;
    614
    615	return NULL;
    616}
    617
    618static 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(&section->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(&section->node, sections);
    635	return section;
    636}
    637
    638static 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(&item->node, &section->items);
    654	return item;
    655}
    656
    657static 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
    669static 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
    724out_free:
    725	free(key);
    726	return ret;
    727}
    728
    729int 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, set);
    734}
    735
    736static 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(collect_config, config_exclusive_filename, set);
    743	if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) {
    744		if (perf_config_from_file(collect_config, perf_etc_perfconfig(), set) < 0)
    745			goto out;
    746	}
    747	if (perf_config_global() && perf_home_perfconfig()) {
    748		if (perf_config_from_file(collect_config, perf_home_perfconfig(), set) < 0)
    749			goto out;
    750	}
    751
    752out:
    753	return ret;
    754}
    755
    756struct 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(&set->sections);
    762		perf_config_set__init(set);
    763	}
    764
    765	return set;
    766}
    767
    768struct 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(&set->sections);
    774		perf_config_from_file(collect_config, file, set);
    775	}
    776
    777	return set;
    778}
    779
    780static 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
    788int 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(key, sizeof(key), "%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	}
    814out:
    815	return ret;
    816}
    817
    818int 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(config_set, fn, data);
    824}
    825
    826void perf_config__exit(void)
    827{
    828	perf_config_set__delete(config_set);
    829	config_set = NULL;
    830}
    831
    832void perf_config__refresh(void)
    833{
    834	perf_config__exit();
    835	perf_config__init();
    836}
    837
    838static void perf_config_item__delete(struct perf_config_item *item)
    839{
    840	zfree(&item->name);
    841	zfree(&item->value);
    842	free(item);
    843}
    844
    845static 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, &section->items, node) {
    850		list_del_init(&item->node);
    851		perf_config_item__delete(item);
    852	}
    853}
    854
    855static void perf_config_section__delete(struct perf_config_section *section)
    856{
    857	perf_config_section__purge(section);
    858	zfree(&section->name);
    859	free(section);
    860}
    861
    862static 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(&section->node);
    868		perf_config_section__delete(section);
    869	}
    870}
    871
    872void 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 */
    885int config_error_nonbool(const char *var)
    886{
    887	pr_err("Missing value for '%s'", var);
    888	return -1;
    889}
    890
    891void 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}