Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/perf/builtin-help.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* builtin-help.c
4
*
5
* Builtin help command
6
*/
7
#include "util/cache.h"
8
#include "util/config.h"
9
#include "util/strbuf.h"
10
#include "builtin.h"
11
#include <subcmd/exec-cmd.h>
12
#include "common-cmds.h"
13
#include <subcmd/parse-options.h>
14
#include <subcmd/run-command.h>
15
#include <subcmd/help.h>
16
#include "util/debug.h"
17
#include "util/util.h"
18
#include <linux/kernel.h>
19
#include <linux/string.h>
20
#include <linux/zalloc.h>
21
#include <errno.h>
22
#include <limits.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <sys/types.h>
27
#include <sys/stat.h>
28
#include <unistd.h>
29
30
static struct man_viewer_list {
31
struct man_viewer_list *next;
32
char name[0];
33
} *man_viewer_list;
34
35
static struct man_viewer_info_list {
36
struct man_viewer_info_list *next;
37
const char *info;
38
char name[0];
39
} *man_viewer_info_list;
40
41
enum help_format {
42
HELP_FORMAT_NONE,
43
HELP_FORMAT_MAN,
44
HELP_FORMAT_INFO,
45
HELP_FORMAT_WEB,
46
};
47
48
static enum help_format parse_help_format(const char *format)
49
{
50
if (!strcmp(format, "man"))
51
return HELP_FORMAT_MAN;
52
if (!strcmp(format, "info"))
53
return HELP_FORMAT_INFO;
54
if (!strcmp(format, "web") || !strcmp(format, "html"))
55
return HELP_FORMAT_WEB;
56
57
pr_err("unrecognized help format '%s'", format);
58
return HELP_FORMAT_NONE;
59
}
60
61
static const char *get_man_viewer_info(const char *name)
62
{
63
struct man_viewer_info_list *viewer;
64
65
for (viewer = man_viewer_info_list; viewer; viewer = viewer->next) {
66
if (!strcasecmp(name, viewer->name))
67
return viewer->info;
68
}
69
return NULL;
70
}
71
72
static int check_emacsclient_version(void)
73
{
74
struct strbuf buffer = STRBUF_INIT;
75
struct child_process ec_process;
76
const char *argv_ec[] = { "emacsclient", "--version", NULL };
77
int version;
78
int ret = -1;
79
80
/* emacsclient prints its version number on stderr */
81
memset(&ec_process, 0, sizeof(ec_process));
82
ec_process.argv = argv_ec;
83
ec_process.err = -1;
84
ec_process.stdout_to_stderr = 1;
85
if (start_command(&ec_process)) {
86
fprintf(stderr, "Failed to start emacsclient.\n");
87
return -1;
88
}
89
if (strbuf_read(&buffer, ec_process.err, 20) < 0) {
90
fprintf(stderr, "Failed to read emacsclient version\n");
91
goto out;
92
}
93
close(ec_process.err);
94
95
/*
96
* Don't bother checking return value, because "emacsclient --version"
97
* seems to always exits with code 1.
98
*/
99
finish_command(&ec_process);
100
101
if (!strstarts(buffer.buf, "emacsclient")) {
102
fprintf(stderr, "Failed to parse emacsclient version.\n");
103
goto out;
104
}
105
106
version = atoi(buffer.buf + strlen("emacsclient"));
107
108
if (version < 22) {
109
fprintf(stderr,
110
"emacsclient version '%d' too old (< 22).\n",
111
version);
112
} else
113
ret = 0;
114
out:
115
strbuf_release(&buffer);
116
return ret;
117
}
118
119
static void exec_failed(const char *cmd)
120
{
121
char sbuf[STRERR_BUFSIZE];
122
pr_warning("failed to exec '%s': %s", cmd, str_error_r(errno, sbuf, sizeof(sbuf)));
123
}
124
125
static void exec_woman_emacs(const char *path, const char *page)
126
{
127
if (!check_emacsclient_version()) {
128
/* This works only with emacsclient version >= 22. */
129
char *man_page;
130
131
if (!path)
132
path = "emacsclient";
133
if (asprintf(&man_page, "(woman \"%s\")", page) > 0) {
134
execlp(path, "emacsclient", "-e", man_page, NULL);
135
free(man_page);
136
}
137
exec_failed(path);
138
}
139
}
140
141
static void exec_man_konqueror(const char *path, const char *page)
142
{
143
const char *display = getenv("DISPLAY");
144
145
if (display && *display) {
146
char *man_page;
147
const char *filename = "kfmclient";
148
149
/* It's simpler to launch konqueror using kfmclient. */
150
if (path) {
151
const char *file = strrchr(path, '/');
152
if (file && !strcmp(file + 1, "konqueror")) {
153
char *new = strdup(path);
154
char *dest = strrchr(new, '/');
155
156
/* strlen("konqueror") == strlen("kfmclient") */
157
strcpy(dest + 1, "kfmclient");
158
path = new;
159
}
160
if (file)
161
filename = file;
162
} else
163
path = "kfmclient";
164
if (asprintf(&man_page, "man:%s(1)", page) > 0) {
165
execlp(path, filename, "newTab", man_page, NULL);
166
free(man_page);
167
}
168
exec_failed(path);
169
}
170
}
171
172
static void exec_man_man(const char *path, const char *page)
173
{
174
if (!path)
175
path = "man";
176
execlp(path, "man", page, NULL);
177
exec_failed(path);
178
}
179
180
static void exec_man_cmd(const char *cmd, const char *page)
181
{
182
char *shell_cmd;
183
184
if (asprintf(&shell_cmd, "%s %s", cmd, page) > 0) {
185
execl("/bin/sh", "sh", "-c", shell_cmd, NULL);
186
free(shell_cmd);
187
}
188
exec_failed(cmd);
189
}
190
191
static void add_man_viewer(const char *name)
192
{
193
struct man_viewer_list **p = &man_viewer_list;
194
size_t len = strlen(name);
195
196
while (*p)
197
p = &((*p)->next);
198
*p = zalloc(sizeof(**p) + len + 1);
199
strcpy((*p)->name, name);
200
}
201
202
static int supported_man_viewer(const char *name, size_t len)
203
{
204
return (!strncasecmp("man", name, len) ||
205
!strncasecmp("woman", name, len) ||
206
!strncasecmp("konqueror", name, len));
207
}
208
209
static void do_add_man_viewer_info(const char *name,
210
size_t len,
211
const char *value)
212
{
213
struct man_viewer_info_list *new = zalloc(sizeof(*new) + len + 1);
214
215
strncpy(new->name, name, len);
216
new->info = strdup(value);
217
new->next = man_viewer_info_list;
218
man_viewer_info_list = new;
219
}
220
221
static void unsupported_man_viewer(const char *name, const char *var)
222
{
223
pr_warning("'%s': path for unsupported man viewer.\n"
224
"Please consider using 'man.<tool>.%s' instead.", name, var);
225
}
226
227
static int add_man_viewer_path(const char *name,
228
size_t len,
229
const char *value)
230
{
231
if (supported_man_viewer(name, len))
232
do_add_man_viewer_info(name, len, value);
233
else
234
unsupported_man_viewer(name, "cmd");
235
236
return 0;
237
}
238
239
static int add_man_viewer_cmd(const char *name,
240
size_t len,
241
const char *value)
242
{
243
if (supported_man_viewer(name, len))
244
unsupported_man_viewer(name, "path");
245
else
246
do_add_man_viewer_info(name, len, value);
247
248
return 0;
249
}
250
251
static int add_man_viewer_info(const char *var, const char *value)
252
{
253
const char *name = var + 4;
254
const char *subkey = strrchr(name, '.');
255
256
if (!subkey) {
257
pr_err("Config with no key for man viewer: %s", name);
258
return -1;
259
}
260
261
if (!strcmp(subkey, ".path")) {
262
if (!value)
263
return config_error_nonbool(var);
264
return add_man_viewer_path(name, subkey - name, value);
265
}
266
if (!strcmp(subkey, ".cmd")) {
267
if (!value)
268
return config_error_nonbool(var);
269
return add_man_viewer_cmd(name, subkey - name, value);
270
}
271
272
pr_warning("'%s': unsupported man viewer sub key.", subkey);
273
return 0;
274
}
275
276
static int perf_help_config(const char *var, const char *value, void *cb)
277
{
278
enum help_format *help_formatp = cb;
279
280
if (!strcmp(var, "help.format")) {
281
if (!value)
282
return config_error_nonbool(var);
283
*help_formatp = parse_help_format(value);
284
if (*help_formatp == HELP_FORMAT_NONE)
285
return -1;
286
return 0;
287
}
288
if (!strcmp(var, "man.viewer")) {
289
if (!value)
290
return config_error_nonbool(var);
291
add_man_viewer(value);
292
return 0;
293
}
294
if (strstarts(var, "man."))
295
return add_man_viewer_info(var, value);
296
297
return 0;
298
}
299
300
static struct cmdnames main_cmds, other_cmds;
301
302
void list_common_cmds_help(void)
303
{
304
unsigned int i, longest = 0;
305
306
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
307
if (longest < strlen(common_cmds[i].name))
308
longest = strlen(common_cmds[i].name);
309
}
310
311
puts(" The most commonly used perf commands are:");
312
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
313
printf(" %-*s ", longest, common_cmds[i].name);
314
puts(common_cmds[i].help);
315
}
316
}
317
318
static const char *cmd_to_page(const char *perf_cmd)
319
{
320
char *s;
321
322
if (!perf_cmd)
323
return "perf";
324
else if (strstarts(perf_cmd, "perf"))
325
return perf_cmd;
326
327
return asprintf(&s, "perf-%s", perf_cmd) < 0 ? NULL : s;
328
}
329
330
static void setup_man_path(void)
331
{
332
char *new_path;
333
const char *old_path = getenv("MANPATH");
334
335
/* We should always put ':' after our path. If there is no
336
* old_path, the ':' at the end will let 'man' to try
337
* system-wide paths after ours to find the manual page. If
338
* there is old_path, we need ':' as delimiter. */
339
if (asprintf(&new_path, "%s:%s", system_path(PERF_MAN_PATH), old_path ?: "") > 0) {
340
setenv("MANPATH", new_path, 1);
341
free(new_path);
342
} else {
343
pr_err("Unable to setup man path");
344
}
345
}
346
347
static void exec_viewer(const char *name, const char *page)
348
{
349
const char *info = get_man_viewer_info(name);
350
351
if (!strcasecmp(name, "man"))
352
exec_man_man(info, page);
353
else if (!strcasecmp(name, "woman"))
354
exec_woman_emacs(info, page);
355
else if (!strcasecmp(name, "konqueror"))
356
exec_man_konqueror(info, page);
357
else if (info)
358
exec_man_cmd(info, page);
359
else
360
pr_warning("'%s': unknown man viewer.", name);
361
}
362
363
static int show_man_page(const char *perf_cmd)
364
{
365
struct man_viewer_list *viewer;
366
const char *page = cmd_to_page(perf_cmd);
367
const char *fallback = getenv("PERF_MAN_VIEWER");
368
369
setup_man_path();
370
for (viewer = man_viewer_list; viewer; viewer = viewer->next)
371
exec_viewer(viewer->name, page); /* will return when unable */
372
373
if (fallback)
374
exec_viewer(fallback, page);
375
exec_viewer("man", page);
376
377
pr_err("no man viewer handled the request");
378
return -1;
379
}
380
381
static int show_info_page(const char *perf_cmd)
382
{
383
const char *page = cmd_to_page(perf_cmd);
384
setenv("INFOPATH", system_path(PERF_INFO_PATH), 1);
385
execlp("info", "info", "perfman", page, NULL);
386
return -1;
387
}
388
389
static int get_html_page_path(char **page_path, const char *page)
390
{
391
struct stat st;
392
const char *html_path = system_path(PERF_HTML_PATH);
393
char path[PATH_MAX];
394
395
/* Check that we have a perf documentation directory. */
396
if (stat(mkpath(path, sizeof(path), "%s/perf.html", html_path), &st)
397
|| !S_ISREG(st.st_mode)) {
398
pr_err("'%s': not a documentation directory.", html_path);
399
return -1;
400
}
401
402
return asprintf(page_path, "%s/%s.html", html_path, page);
403
}
404
405
/*
406
* If open_html is not defined in a platform-specific way (see for
407
* example compat/mingw.h), we use the script web--browse to display
408
* HTML.
409
*/
410
#ifndef open_html
411
static void open_html(const char *path)
412
{
413
execl_cmd("web--browse", "-c", "help.browser", path, NULL);
414
}
415
#endif
416
417
static int show_html_page(const char *perf_cmd)
418
{
419
const char *page = cmd_to_page(perf_cmd);
420
char *page_path; /* it leaks but we exec below */
421
422
if (get_html_page_path(&page_path, page) < 0)
423
return -1;
424
425
open_html(page_path);
426
427
return 0;
428
}
429
430
int cmd_help(int argc, const char **argv)
431
{
432
bool show_all = false;
433
enum help_format help_format = HELP_FORMAT_MAN;
434
struct option builtin_help_options[] = {
435
OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
436
OPT_SET_UINT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
437
OPT_SET_UINT('w', "web", &help_format, "show manual in web browser",
438
HELP_FORMAT_WEB),
439
OPT_SET_UINT('i', "info", &help_format, "show info page",
440
HELP_FORMAT_INFO),
441
OPT_END(),
442
};
443
const char * const builtin_help_subcommands[] = {
444
"buildid-cache", "buildid-list", "diff", "evlist", "help", "list",
445
"record", "report", "bench", "stat", "timechart", "top", "annotate",
446
"script", "sched", "kallsyms", "kmem", "lock", "kvm", "test", "inject", "mem", "data",
447
#ifdef HAVE_LIBELF_SUPPORT
448
"probe",
449
#endif
450
"trace",
451
NULL };
452
const char *builtin_help_usage[] = {
453
"perf help [--all] [--man|--web|--info] [command]",
454
NULL
455
};
456
int rc;
457
458
load_command_list("perf-", &main_cmds, &other_cmds);
459
460
rc = perf_config(perf_help_config, &help_format);
461
if (rc)
462
return rc;
463
464
argc = parse_options_subcommand(argc, argv, builtin_help_options,
465
builtin_help_subcommands, builtin_help_usage, 0);
466
467
if (show_all) {
468
printf("\n Usage: %s\n\n", perf_usage_string);
469
list_commands("perf commands", &main_cmds, &other_cmds);
470
printf(" %s\n\n", perf_more_info_string);
471
return 0;
472
}
473
474
if (!argv[0]) {
475
printf("\n usage: %s\n\n", perf_usage_string);
476
list_common_cmds_help();
477
printf("\n %s\n\n", perf_more_info_string);
478
return 0;
479
}
480
481
switch (help_format) {
482
case HELP_FORMAT_MAN:
483
rc = show_man_page(argv[0]);
484
break;
485
case HELP_FORMAT_INFO:
486
rc = show_info_page(argv[0]);
487
break;
488
case HELP_FORMAT_WEB:
489
rc = show_html_page(argv[0]);
490
break;
491
case HELP_FORMAT_NONE:
492
/* fall-through */
493
default:
494
rc = -1;
495
break;
496
}
497
498
return rc;
499
}
500
501