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