Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/tools/perf/perf.c
10820 views
1
/*
2
* perf.c
3
*
4
* Performance analysis utility.
5
*
6
* This is the main hub from which the sub-commands (perf stat,
7
* perf top, perf record, perf report, etc.) are started.
8
*/
9
#include "builtin.h"
10
11
#include "util/exec_cmd.h"
12
#include "util/cache.h"
13
#include "util/quote.h"
14
#include "util/run-command.h"
15
#include "util/parse-events.h"
16
#include "util/debugfs.h"
17
18
const char perf_usage_string[] =
19
"perf [--version] [--help] COMMAND [ARGS]";
20
21
const char perf_more_info_string[] =
22
"See 'perf help COMMAND' for more information on a specific command.";
23
24
int use_browser = -1;
25
static int use_pager = -1;
26
27
struct pager_config {
28
const char *cmd;
29
int val;
30
};
31
32
static char debugfs_mntpt[MAXPATHLEN];
33
34
static int pager_command_config(const char *var, const char *value, void *data)
35
{
36
struct pager_config *c = data;
37
if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd))
38
c->val = perf_config_bool(var, value);
39
return 0;
40
}
41
42
/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
43
int check_pager_config(const char *cmd)
44
{
45
struct pager_config c;
46
c.cmd = cmd;
47
c.val = -1;
48
perf_config(pager_command_config, &c);
49
return c.val;
50
}
51
52
static int tui_command_config(const char *var, const char *value, void *data)
53
{
54
struct pager_config *c = data;
55
if (!prefixcmp(var, "tui.") && !strcmp(var + 4, c->cmd))
56
c->val = perf_config_bool(var, value);
57
return 0;
58
}
59
60
/* returns 0 for "no tui", 1 for "use tui", and -1 for "not specified" */
61
static int check_tui_config(const char *cmd)
62
{
63
struct pager_config c;
64
c.cmd = cmd;
65
c.val = -1;
66
perf_config(tui_command_config, &c);
67
return c.val;
68
}
69
70
static void commit_pager_choice(void)
71
{
72
switch (use_pager) {
73
case 0:
74
setenv("PERF_PAGER", "cat", 1);
75
break;
76
case 1:
77
/* setup_pager(); */
78
break;
79
default:
80
break;
81
}
82
}
83
84
static void set_debugfs_path(void)
85
{
86
char *path;
87
88
path = getenv(PERF_DEBUGFS_ENVIRONMENT);
89
snprintf(debugfs_path, MAXPATHLEN, "%s/%s", path ?: debugfs_mntpt,
90
"tracing/events");
91
}
92
93
static int handle_options(const char ***argv, int *argc, int *envchanged)
94
{
95
int handled = 0;
96
97
while (*argc > 0) {
98
const char *cmd = (*argv)[0];
99
if (cmd[0] != '-')
100
break;
101
102
/*
103
* For legacy reasons, the "version" and "help"
104
* commands can be written with "--" prepended
105
* to make them look like flags.
106
*/
107
if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version"))
108
break;
109
110
/*
111
* Check remaining flags.
112
*/
113
if (!prefixcmp(cmd, CMD_EXEC_PATH)) {
114
cmd += strlen(CMD_EXEC_PATH);
115
if (*cmd == '=')
116
perf_set_argv_exec_path(cmd + 1);
117
else {
118
puts(perf_exec_path());
119
exit(0);
120
}
121
} else if (!strcmp(cmd, "--html-path")) {
122
puts(system_path(PERF_HTML_PATH));
123
exit(0);
124
} else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
125
use_pager = 1;
126
} else if (!strcmp(cmd, "--no-pager")) {
127
use_pager = 0;
128
if (envchanged)
129
*envchanged = 1;
130
} else if (!strcmp(cmd, "--perf-dir")) {
131
if (*argc < 2) {
132
fprintf(stderr, "No directory given for --perf-dir.\n");
133
usage(perf_usage_string);
134
}
135
setenv(PERF_DIR_ENVIRONMENT, (*argv)[1], 1);
136
if (envchanged)
137
*envchanged = 1;
138
(*argv)++;
139
(*argc)--;
140
handled++;
141
} else if (!prefixcmp(cmd, CMD_PERF_DIR)) {
142
setenv(PERF_DIR_ENVIRONMENT, cmd + strlen(CMD_PERF_DIR), 1);
143
if (envchanged)
144
*envchanged = 1;
145
} else if (!strcmp(cmd, "--work-tree")) {
146
if (*argc < 2) {
147
fprintf(stderr, "No directory given for --work-tree.\n");
148
usage(perf_usage_string);
149
}
150
setenv(PERF_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
151
if (envchanged)
152
*envchanged = 1;
153
(*argv)++;
154
(*argc)--;
155
} else if (!prefixcmp(cmd, CMD_WORK_TREE)) {
156
setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + strlen(CMD_WORK_TREE), 1);
157
if (envchanged)
158
*envchanged = 1;
159
} else if (!strcmp(cmd, "--debugfs-dir")) {
160
if (*argc < 2) {
161
fprintf(stderr, "No directory given for --debugfs-dir.\n");
162
usage(perf_usage_string);
163
}
164
strncpy(debugfs_mntpt, (*argv)[1], MAXPATHLEN);
165
debugfs_mntpt[MAXPATHLEN - 1] = '\0';
166
if (envchanged)
167
*envchanged = 1;
168
(*argv)++;
169
(*argc)--;
170
} else if (!prefixcmp(cmd, CMD_DEBUGFS_DIR)) {
171
strncpy(debugfs_mntpt, cmd + strlen(CMD_DEBUGFS_DIR), MAXPATHLEN);
172
debugfs_mntpt[MAXPATHLEN - 1] = '\0';
173
if (envchanged)
174
*envchanged = 1;
175
} else {
176
fprintf(stderr, "Unknown option: %s\n", cmd);
177
usage(perf_usage_string);
178
}
179
180
(*argv)++;
181
(*argc)--;
182
handled++;
183
}
184
return handled;
185
}
186
187
static int handle_alias(int *argcp, const char ***argv)
188
{
189
int envchanged = 0, ret = 0, saved_errno = errno;
190
int count, option_count;
191
const char **new_argv;
192
const char *alias_command;
193
char *alias_string;
194
195
alias_command = (*argv)[0];
196
alias_string = alias_lookup(alias_command);
197
if (alias_string) {
198
if (alias_string[0] == '!') {
199
if (*argcp > 1) {
200
struct strbuf buf;
201
202
strbuf_init(&buf, PATH_MAX);
203
strbuf_addstr(&buf, alias_string);
204
sq_quote_argv(&buf, (*argv) + 1, PATH_MAX);
205
free(alias_string);
206
alias_string = buf.buf;
207
}
208
ret = system(alias_string + 1);
209
if (ret >= 0 && WIFEXITED(ret) &&
210
WEXITSTATUS(ret) != 127)
211
exit(WEXITSTATUS(ret));
212
die("Failed to run '%s' when expanding alias '%s'",
213
alias_string + 1, alias_command);
214
}
215
count = split_cmdline(alias_string, &new_argv);
216
if (count < 0)
217
die("Bad alias.%s string", alias_command);
218
option_count = handle_options(&new_argv, &count, &envchanged);
219
if (envchanged)
220
die("alias '%s' changes environment variables\n"
221
"You can use '!perf' in the alias to do this.",
222
alias_command);
223
memmove(new_argv - option_count, new_argv,
224
count * sizeof(char *));
225
new_argv -= option_count;
226
227
if (count < 1)
228
die("empty alias for %s", alias_command);
229
230
if (!strcmp(alias_command, new_argv[0]))
231
die("recursive alias: %s", alias_command);
232
233
new_argv = realloc(new_argv, sizeof(char *) *
234
(count + *argcp + 1));
235
/* insert after command name */
236
memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp);
237
new_argv[count + *argcp] = NULL;
238
239
*argv = new_argv;
240
*argcp += count - 1;
241
242
ret = 1;
243
}
244
245
errno = saved_errno;
246
247
return ret;
248
}
249
250
const char perf_version_string[] = PERF_VERSION;
251
252
#define RUN_SETUP (1<<0)
253
#define USE_PAGER (1<<1)
254
/*
255
* require working tree to be present -- anything uses this needs
256
* RUN_SETUP for reading from the configuration file.
257
*/
258
#define NEED_WORK_TREE (1<<2)
259
260
struct cmd_struct {
261
const char *cmd;
262
int (*fn)(int, const char **, const char *);
263
int option;
264
};
265
266
static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
267
{
268
int status;
269
struct stat st;
270
const char *prefix;
271
272
prefix = NULL;
273
if (p->option & RUN_SETUP)
274
prefix = NULL; /* setup_perf_directory(); */
275
276
if (use_browser == -1)
277
use_browser = check_tui_config(p->cmd);
278
279
if (use_pager == -1 && p->option & RUN_SETUP)
280
use_pager = check_pager_config(p->cmd);
281
if (use_pager == -1 && p->option & USE_PAGER)
282
use_pager = 1;
283
commit_pager_choice();
284
set_debugfs_path();
285
286
status = p->fn(argc, argv, prefix);
287
exit_browser(status);
288
289
if (status)
290
return status & 0xff;
291
292
/* Somebody closed stdout? */
293
if (fstat(fileno(stdout), &st))
294
return 0;
295
/* Ignore write errors for pipes and sockets.. */
296
if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))
297
return 0;
298
299
/* Check for ENOSPC and EIO errors.. */
300
if (fflush(stdout))
301
die("write failure on standard output: %s", strerror(errno));
302
if (ferror(stdout))
303
die("unknown write failure on standard output");
304
if (fclose(stdout))
305
die("close failed on standard output: %s", strerror(errno));
306
return 0;
307
}
308
309
static void handle_internal_command(int argc, const char **argv)
310
{
311
const char *cmd = argv[0];
312
static struct cmd_struct commands[] = {
313
{ "buildid-cache", cmd_buildid_cache, 0 },
314
{ "buildid-list", cmd_buildid_list, 0 },
315
{ "diff", cmd_diff, 0 },
316
{ "evlist", cmd_evlist, 0 },
317
{ "help", cmd_help, 0 },
318
{ "list", cmd_list, 0 },
319
{ "record", cmd_record, 0 },
320
{ "report", cmd_report, 0 },
321
{ "bench", cmd_bench, 0 },
322
{ "stat", cmd_stat, 0 },
323
{ "timechart", cmd_timechart, 0 },
324
{ "top", cmd_top, 0 },
325
{ "annotate", cmd_annotate, 0 },
326
{ "version", cmd_version, 0 },
327
{ "script", cmd_script, 0 },
328
{ "sched", cmd_sched, 0 },
329
{ "probe", cmd_probe, 0 },
330
{ "kmem", cmd_kmem, 0 },
331
{ "lock", cmd_lock, 0 },
332
{ "kvm", cmd_kvm, 0 },
333
{ "test", cmd_test, 0 },
334
{ "inject", cmd_inject, 0 },
335
};
336
unsigned int i;
337
static const char ext[] = STRIP_EXTENSION;
338
339
if (sizeof(ext) > 1) {
340
i = strlen(argv[0]) - strlen(ext);
341
if (i > 0 && !strcmp(argv[0] + i, ext)) {
342
char *argv0 = strdup(argv[0]);
343
argv[0] = cmd = argv0;
344
argv0[i] = '\0';
345
}
346
}
347
348
/* Turn "perf cmd --help" into "perf help cmd" */
349
if (argc > 1 && !strcmp(argv[1], "--help")) {
350
argv[1] = argv[0];
351
argv[0] = cmd = "help";
352
}
353
354
for (i = 0; i < ARRAY_SIZE(commands); i++) {
355
struct cmd_struct *p = commands+i;
356
if (strcmp(p->cmd, cmd))
357
continue;
358
exit(run_builtin(p, argc, argv));
359
}
360
}
361
362
static void execv_dashed_external(const char **argv)
363
{
364
struct strbuf cmd = STRBUF_INIT;
365
const char *tmp;
366
int status;
367
368
strbuf_addf(&cmd, "perf-%s", argv[0]);
369
370
/*
371
* argv[0] must be the perf command, but the argv array
372
* belongs to the caller, and may be reused in
373
* subsequent loop iterations. Save argv[0] and
374
* restore it on error.
375
*/
376
tmp = argv[0];
377
argv[0] = cmd.buf;
378
379
/*
380
* if we fail because the command is not found, it is
381
* OK to return. Otherwise, we just pass along the status code.
382
*/
383
status = run_command_v_opt(argv, 0);
384
if (status != -ERR_RUN_COMMAND_EXEC) {
385
if (IS_RUN_COMMAND_ERR(status))
386
die("unable to run '%s'", argv[0]);
387
exit(-status);
388
}
389
errno = ENOENT; /* as if we called execvp */
390
391
argv[0] = tmp;
392
393
strbuf_release(&cmd);
394
}
395
396
static int run_argv(int *argcp, const char ***argv)
397
{
398
int done_alias = 0;
399
400
while (1) {
401
/* See if it's an internal command */
402
handle_internal_command(*argcp, *argv);
403
404
/* .. then try the external ones */
405
execv_dashed_external(*argv);
406
407
/* It could be an alias -- this works around the insanity
408
* of overriding "perf log" with "perf show" by having
409
* alias.log = show
410
*/
411
if (done_alias || !handle_alias(argcp, argv))
412
break;
413
done_alias = 1;
414
}
415
416
return done_alias;
417
}
418
419
/* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */
420
static void get_debugfs_mntpt(void)
421
{
422
const char *path = debugfs_mount(NULL);
423
424
if (path)
425
strncpy(debugfs_mntpt, path, sizeof(debugfs_mntpt));
426
else
427
debugfs_mntpt[0] = '\0';
428
}
429
430
int main(int argc, const char **argv)
431
{
432
const char *cmd;
433
434
cmd = perf_extract_argv0_path(argv[0]);
435
if (!cmd)
436
cmd = "perf-help";
437
/* get debugfs mount point from /proc/mounts */
438
get_debugfs_mntpt();
439
/*
440
* "perf-xxxx" is the same as "perf xxxx", but we obviously:
441
*
442
* - cannot take flags in between the "perf" and the "xxxx".
443
* - cannot execute it externally (since it would just do
444
* the same thing over again)
445
*
446
* So we just directly call the internal command handler, and
447
* die if that one cannot handle it.
448
*/
449
if (!prefixcmp(cmd, "perf-")) {
450
cmd += 5;
451
argv[0] = cmd;
452
handle_internal_command(argc, argv);
453
die("cannot handle %s internally", cmd);
454
}
455
456
/* Look for flags.. */
457
argv++;
458
argc--;
459
handle_options(&argv, &argc, NULL);
460
commit_pager_choice();
461
set_debugfs_path();
462
set_buildid_dir();
463
464
if (argc > 0) {
465
if (!prefixcmp(argv[0], "--"))
466
argv[0] += 2;
467
} else {
468
/* The user didn't specify a command; give them help */
469
printf("\n usage: %s\n\n", perf_usage_string);
470
list_common_cmds_help();
471
printf("\n %s\n\n", perf_more_info_string);
472
exit(1);
473
}
474
cmd = argv[0];
475
476
/*
477
* We use PATH to find perf commands, but we prepend some higher
478
* precedence paths: the "--exec-path" option, the PERF_EXEC_PATH
479
* environment, and the $(perfexecdir) from the Makefile at build
480
* time.
481
*/
482
setup_path();
483
484
while (1) {
485
static int done_help;
486
static int was_alias;
487
488
was_alias = run_argv(&argc, &argv);
489
if (errno != ENOENT)
490
break;
491
492
if (was_alias) {
493
fprintf(stderr, "Expansion of alias '%s' failed; "
494
"'%s' is not a perf-command\n",
495
cmd, argv[0]);
496
exit(1);
497
}
498
if (!done_help) {
499
cmd = argv[0] = help_unknown_cmd(cmd);
500
done_help = 1;
501
} else
502
break;
503
}
504
505
fprintf(stderr, "Failed to run command '%s': %s\n",
506
cmd, strerror(errno));
507
508
return 1;
509
}
510
511