Blame view

tools/perf/builtin-help.c 11.5 KB
078006012   Ingo Molnar   perf_counter tool...
1
2
3
4
5
  /*
   * builtin-help.c
   *
   * Builtin help command
   */
f37a291c5   Ingo Molnar   perf_counter tool...
6
  #include "perf.h"
148be2c15   Ingo Molnar   perf_counter tool...
7
  #include "util/cache.h"
078006012   Ingo Molnar   perf_counter tool...
8
  #include "builtin.h"
148be2c15   Ingo Molnar   perf_counter tool...
9
  #include "util/exec_cmd.h"
078006012   Ingo Molnar   perf_counter tool...
10
  #include "common-cmds.h"
148be2c15   Ingo Molnar   perf_counter tool...
11
12
13
  #include "util/parse-options.h"
  #include "util/run-command.h"
  #include "util/help.h"
078006012   Ingo Molnar   perf_counter tool...
14
15
16
17
18
19
20
21
22
23
24
25
26
  
  static struct man_viewer_list {
  	struct man_viewer_list *next;
  	char name[FLEX_ARRAY];
  } *man_viewer_list;
  
  static struct man_viewer_info_list {
  	struct man_viewer_info_list *next;
  	const char *info;
  	char name[FLEX_ARRAY];
  } *man_viewer_info_list;
  
  enum help_format {
cc5848213   David Ahern   perf help: Remove...
27
  	HELP_FORMAT_NONE,
078006012   Ingo Molnar   perf_counter tool...
28
29
30
31
  	HELP_FORMAT_MAN,
  	HELP_FORMAT_INFO,
  	HELP_FORMAT_WEB,
  };
078006012   Ingo Molnar   perf_counter tool...
32
33
34
35
36
37
38
39
  static enum help_format parse_help_format(const char *format)
  {
  	if (!strcmp(format, "man"))
  		return HELP_FORMAT_MAN;
  	if (!strcmp(format, "info"))
  		return HELP_FORMAT_INFO;
  	if (!strcmp(format, "web") || !strcmp(format, "html"))
  		return HELP_FORMAT_WEB;
cc5848213   David Ahern   perf help: Remove...
40
41
42
  
  	pr_err("unrecognized help format '%s'", format);
  	return HELP_FORMAT_NONE;
078006012   Ingo Molnar   perf_counter tool...
43
44
45
46
47
  }
  
  static const char *get_man_viewer_info(const char *name)
  {
  	struct man_viewer_info_list *viewer;
c1e530178   Thiago Farina   perf: Clean up tr...
48
  	for (viewer = man_viewer_info_list; viewer; viewer = viewer->next) {
078006012   Ingo Molnar   perf_counter tool...
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
  		if (!strcasecmp(name, viewer->name))
  			return viewer->info;
  	}
  	return NULL;
  }
  
  static int check_emacsclient_version(void)
  {
  	struct strbuf buffer = STRBUF_INIT;
  	struct child_process ec_process;
  	const char *argv_ec[] = { "emacsclient", "--version", NULL };
  	int version;
  
  	/* emacsclient prints its version number on stderr */
  	memset(&ec_process, 0, sizeof(ec_process));
  	ec_process.argv = argv_ec;
  	ec_process.err = -1;
  	ec_process.stdout_to_stderr = 1;
  	if (start_command(&ec_process)) {
  		fprintf(stderr, "Failed to start emacsclient.
  ");
  		return -1;
  	}
  	strbuf_read(&buffer, ec_process.err, 20);
  	close(ec_process.err);
  
  	/*
  	 * Don't bother checking return value, because "emacsclient --version"
  	 * seems to always exits with code 1.
  	 */
  	finish_command(&ec_process);
  
  	if (prefixcmp(buffer.buf, "emacsclient")) {
  		fprintf(stderr, "Failed to parse emacsclient version.
  ");
  		strbuf_release(&buffer);
  		return -1;
  	}
  
  	strbuf_remove(&buffer, 0, strlen("emacsclient"));
  	version = atoi(buffer.buf);
  
  	if (version < 22) {
  		fprintf(stderr,
  			"emacsclient version '%d' too old (< 22).
  ",
  			version);
  		strbuf_release(&buffer);
  		return -1;
  	}
  
  	strbuf_release(&buffer);
  	return 0;
  }
c1e530178   Thiago Farina   perf: Clean up tr...
103
  static void exec_woman_emacs(const char *path, const char *page)
078006012   Ingo Molnar   perf_counter tool...
104
105
106
107
108
109
110
111
112
113
114
115
  {
  	if (!check_emacsclient_version()) {
  		/* This works only with emacsclient version >= 22. */
  		struct strbuf man_page = STRBUF_INIT;
  
  		if (!path)
  			path = "emacsclient";
  		strbuf_addf(&man_page, "(woman \"%s\")", page);
  		execlp(path, "emacsclient", "-e", man_page.buf, NULL);
  		warning("failed to exec '%s': %s", path, strerror(errno));
  	}
  }
c1e530178   Thiago Farina   perf: Clean up tr...
116
  static void exec_man_konqueror(const char *path, const char *page)
078006012   Ingo Molnar   perf_counter tool...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
  {
  	const char *display = getenv("DISPLAY");
  	if (display && *display) {
  		struct strbuf man_page = STRBUF_INIT;
  		const char *filename = "kfmclient";
  
  		/* It's simpler to launch konqueror using kfmclient. */
  		if (path) {
  			const char *file = strrchr(path, '/');
  			if (file && !strcmp(file + 1, "konqueror")) {
  				char *new = strdup(path);
  				char *dest = strrchr(new, '/');
  
  				/* strlen("konqueror") == strlen("kfmclient") */
  				strcpy(dest + 1, "kfmclient");
  				path = new;
  			}
  			if (file)
  				filename = file;
  		} else
  			path = "kfmclient";
  		strbuf_addf(&man_page, "man:%s(1)", page);
  		execlp(path, filename, "newTab", man_page.buf, NULL);
  		warning("failed to exec '%s': %s", path, strerror(errno));
  	}
  }
c1e530178   Thiago Farina   perf: Clean up tr...
143
  static void exec_man_man(const char *path, const char *page)
078006012   Ingo Molnar   perf_counter tool...
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
  {
  	if (!path)
  		path = "man";
  	execlp(path, "man", page, NULL);
  	warning("failed to exec '%s': %s", path, strerror(errno));
  }
  
  static void exec_man_cmd(const char *cmd, const char *page)
  {
  	struct strbuf shell_cmd = STRBUF_INIT;
  	strbuf_addf(&shell_cmd, "%s %s", cmd, page);
  	execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
  	warning("failed to exec '%s': %s", cmd, strerror(errno));
  }
  
  static void add_man_viewer(const char *name)
  {
  	struct man_viewer_list **p = &man_viewer_list;
  	size_t len = strlen(name);
  
  	while (*p)
  		p = &((*p)->next);
364794845   Arnaldo Carvalho de Melo   perf tools: Intro...
166
  	*p = zalloc(sizeof(**p) + len + 1);
078006012   Ingo Molnar   perf_counter tool...
167
168
169
170
171
172
173
174
175
176
177
178
179
180
  	strncpy((*p)->name, name, len);
  }
  
  static int supported_man_viewer(const char *name, size_t len)
  {
  	return (!strncasecmp("man", name, len) ||
  		!strncasecmp("woman", name, len) ||
  		!strncasecmp("konqueror", name, len));
  }
  
  static void do_add_man_viewer_info(const char *name,
  				   size_t len,
  				   const char *value)
  {
364794845   Arnaldo Carvalho de Melo   perf tools: Intro...
181
  	struct man_viewer_info_list *new = zalloc(sizeof(*new) + len + 1);
078006012   Ingo Molnar   perf_counter tool...
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
  
  	strncpy(new->name, name, len);
  	new->info = strdup(value);
  	new->next = man_viewer_info_list;
  	man_viewer_info_list = new;
  }
  
  static int add_man_viewer_path(const char *name,
  			       size_t len,
  			       const char *value)
  {
  	if (supported_man_viewer(name, len))
  		do_add_man_viewer_info(name, len, value);
  	else
  		warning("'%s': path for unsupported man viewer.
  "
  			"Please consider using 'man.<tool>.cmd' instead.",
  			name);
  
  	return 0;
  }
  
  static int add_man_viewer_cmd(const char *name,
  			      size_t len,
  			      const char *value)
  {
  	if (supported_man_viewer(name, len))
  		warning("'%s': cmd for supported man viewer.
  "
  			"Please consider using 'man.<tool>.path' instead.",
  			name);
  	else
  		do_add_man_viewer_info(name, len, value);
  
  	return 0;
  }
  
  static int add_man_viewer_info(const char *var, const char *value)
  {
  	const char *name = var + 4;
  	const char *subkey = strrchr(name, '.');
  
  	if (!subkey)
  		return error("Config with no key for man viewer: %s", name);
  
  	if (!strcmp(subkey, ".path")) {
  		if (!value)
  			return config_error_nonbool(var);
  		return add_man_viewer_path(name, subkey - name, value);
  	}
  	if (!strcmp(subkey, ".cmd")) {
  		if (!value)
  			return config_error_nonbool(var);
  		return add_man_viewer_cmd(name, subkey - name, value);
  	}
  
  	warning("'%s': unsupported man viewer sub key.", subkey);
  	return 0;
  }
  
  static int perf_help_config(const char *var, const char *value, void *cb)
  {
2bae1d1b1   Arnaldo Carvalho de Melo   perf help: Don't ...
244
  	enum help_format *help_formatp = cb;
078006012   Ingo Molnar   perf_counter tool...
245
246
247
  	if (!strcmp(var, "help.format")) {
  		if (!value)
  			return config_error_nonbool(var);
2bae1d1b1   Arnaldo Carvalho de Melo   perf help: Don't ...
248
249
  		*help_formatp = parse_help_format(value);
  		if (*help_formatp == HELP_FORMAT_NONE)
cc5848213   David Ahern   perf help: Remove...
250
  			return -1;
078006012   Ingo Molnar   perf_counter tool...
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
  		return 0;
  	}
  	if (!strcmp(var, "man.viewer")) {
  		if (!value)
  			return config_error_nonbool(var);
  		add_man_viewer(value);
  		return 0;
  	}
  	if (!prefixcmp(var, "man."))
  		return add_man_viewer_info(var, value);
  
  	return perf_default_config(var, value, cb);
  }
  
  static struct cmdnames main_cmds, other_cmds;
  
  void list_common_cmds_help(void)
  {
f37a291c5   Ingo Molnar   perf_counter tool...
269
  	unsigned int i, longest = 0;
078006012   Ingo Molnar   perf_counter tool...
270
271
272
273
274
  
  	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
  		if (longest < strlen(common_cmds[i].name))
  			longest = strlen(common_cmds[i].name);
  	}
502fc5c72   Ingo Molnar   perf_counter tool...
275
  	puts(" The most commonly used perf commands are:");
078006012   Ingo Molnar   perf_counter tool...
276
  	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
3912f2abc   Amerigo Wang   perf: Use format ...
277
  		printf("   %-*s   ", longest, common_cmds[i].name);
078006012   Ingo Molnar   perf_counter tool...
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
  		puts(common_cmds[i].help);
  	}
  }
  
  static int is_perf_command(const char *s)
  {
  	return is_in_cmdlist(&main_cmds, s) ||
  		is_in_cmdlist(&other_cmds, s);
  }
  
  static const char *prepend(const char *prefix, const char *cmd)
  {
  	size_t pre_len = strlen(prefix);
  	size_t cmd_len = strlen(cmd);
  	char *p = malloc(pre_len + cmd_len + 1);
  	memcpy(p, prefix, pre_len);
  	strcpy(p + pre_len, cmd);
  	return p;
  }
  
  static const char *cmd_to_page(const char *perf_cmd)
  {
  	if (!perf_cmd)
  		return "perf";
  	else if (!prefixcmp(perf_cmd, "perf"))
  		return perf_cmd;
078006012   Ingo Molnar   perf_counter tool...
304
  	else
c1c2365ac   Ingo Molnar   perf_counter tool...
305
  		return prepend("perf-", perf_cmd);
078006012   Ingo Molnar   perf_counter tool...
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
  }
  
  static void setup_man_path(void)
  {
  	struct strbuf new_path = STRBUF_INIT;
  	const char *old_path = getenv("MANPATH");
  
  	/* We should always put ':' after our path. If there is no
  	 * old_path, the ':' at the end will let 'man' to try
  	 * system-wide paths after ours to find the manual page. If
  	 * there is old_path, we need ':' as delimiter. */
  	strbuf_addstr(&new_path, system_path(PERF_MAN_PATH));
  	strbuf_addch(&new_path, ':');
  	if (old_path)
  		strbuf_addstr(&new_path, old_path);
  
  	setenv("MANPATH", new_path.buf, 1);
  
  	strbuf_release(&new_path);
  }
  
  static void exec_viewer(const char *name, const char *page)
  {
  	const char *info = get_man_viewer_info(name);
  
  	if (!strcasecmp(name, "man"))
  		exec_man_man(info, page);
  	else if (!strcasecmp(name, "woman"))
  		exec_woman_emacs(info, page);
  	else if (!strcasecmp(name, "konqueror"))
  		exec_man_konqueror(info, page);
  	else if (info)
  		exec_man_cmd(info, page);
  	else
  		warning("'%s': unknown man viewer.", name);
  }
cc5848213   David Ahern   perf help: Remove...
342
  static int show_man_page(const char *perf_cmd)
078006012   Ingo Molnar   perf_counter tool...
343
344
345
346
347
348
349
  {
  	struct man_viewer_list *viewer;
  	const char *page = cmd_to_page(perf_cmd);
  	const char *fallback = getenv("PERF_MAN_VIEWER");
  
  	setup_man_path();
  	for (viewer = man_viewer_list; viewer; viewer = viewer->next)
078006012   Ingo Molnar   perf_counter tool...
350
  		exec_viewer(viewer->name, page); /* will return when unable */
c1e530178   Thiago Farina   perf: Clean up tr...
351

078006012   Ingo Molnar   perf_counter tool...
352
353
354
  	if (fallback)
  		exec_viewer(fallback, page);
  	exec_viewer("man", page);
cc5848213   David Ahern   perf help: Remove...
355
356
357
  
  	pr_err("no man viewer handled the request");
  	return -1;
078006012   Ingo Molnar   perf_counter tool...
358
  }
cc5848213   David Ahern   perf help: Remove...
359
  static int show_info_page(const char *perf_cmd)
078006012   Ingo Molnar   perf_counter tool...
360
361
362
363
  {
  	const char *page = cmd_to_page(perf_cmd);
  	setenv("INFOPATH", system_path(PERF_INFO_PATH), 1);
  	execlp("info", "info", "perfman", page, NULL);
cc5848213   David Ahern   perf help: Remove...
364
  	return -1;
078006012   Ingo Molnar   perf_counter tool...
365
  }
cc5848213   David Ahern   perf help: Remove...
366
  static int get_html_page_path(struct strbuf *page_path, const char *page)
078006012   Ingo Molnar   perf_counter tool...
367
368
369
370
371
372
  {
  	struct stat st;
  	const char *html_path = system_path(PERF_HTML_PATH);
  
  	/* Check that we have a perf documentation directory. */
  	if (stat(mkpath("%s/perf.html", html_path), &st)
cc5848213   David Ahern   perf help: Remove...
373
374
375
376
  	    || !S_ISREG(st.st_mode)) {
  		pr_err("'%s': not a documentation directory.", html_path);
  		return -1;
  	}
078006012   Ingo Molnar   perf_counter tool...
377
378
379
  
  	strbuf_init(page_path, 0);
  	strbuf_addf(page_path, "%s/%s.html", html_path, page);
cc5848213   David Ahern   perf help: Remove...
380
381
  
  	return 0;
078006012   Ingo Molnar   perf_counter tool...
382
383
384
385
386
387
388
389
  }
  
  /*
   * If open_html is not defined in a platform-specific way (see for
   * example compat/mingw.h), we use the script web--browse to display
   * HTML.
   */
  #ifndef open_html
16f762a2a   Ingo Molnar   perf_counter tool...
390
  static void open_html(const char *path)
078006012   Ingo Molnar   perf_counter tool...
391
392
393
394
  {
  	execl_perf_cmd("web--browse", "-c", "help.browser", path, NULL);
  }
  #endif
cc5848213   David Ahern   perf help: Remove...
395
  static int show_html_page(const char *perf_cmd)
078006012   Ingo Molnar   perf_counter tool...
396
397
398
  {
  	const char *page = cmd_to_page(perf_cmd);
  	struct strbuf page_path; /* it leaks but we exec bellow */
cc5848213   David Ahern   perf help: Remove...
399
400
  	if (get_html_page_path(&page_path, page) != 0)
  		return -1;
078006012   Ingo Molnar   perf_counter tool...
401
402
  
  	open_html(page_path.buf);
cc5848213   David Ahern   perf help: Remove...
403
404
  
  	return 0;
078006012   Ingo Molnar   perf_counter tool...
405
  }
1d037ca16   Irina Tirdea   perf tools: Use _...
406
  int cmd_help(int argc, const char **argv, const char *prefix __maybe_unused)
078006012   Ingo Molnar   perf_counter tool...
407
  {
2bae1d1b1   Arnaldo Carvalho de Melo   perf help: Don't ...
408
  	bool show_all = false;
670ab5d21   Namhyung Kim   perf help: Fix --...
409
  	enum help_format help_format = HELP_FORMAT_MAN;
2bae1d1b1   Arnaldo Carvalho de Melo   perf help: Don't ...
410
411
412
413
414
415
416
417
418
419
420
421
422
  	struct option builtin_help_options[] = {
  	OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
  	OPT_SET_UINT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
  	OPT_SET_UINT('w', "web", &help_format, "show manual in web browser",
  			HELP_FORMAT_WEB),
  	OPT_SET_UINT('i', "info", &help_format, "show info page",
  			HELP_FORMAT_INFO),
  	OPT_END(),
  	};
  	const char * const builtin_help_usage[] = {
  		"perf help [--all] [--man|--web|--info] [command]",
  		NULL
  	};
078006012   Ingo Molnar   perf_counter tool...
423
  	const char *alias;
cc5848213   David Ahern   perf help: Remove...
424
  	int rc = 0;
f37a291c5   Ingo Molnar   perf_counter tool...
425

078006012   Ingo Molnar   perf_counter tool...
426
  	load_command_list("perf-", &main_cmds, &other_cmds);
2bae1d1b1   Arnaldo Carvalho de Melo   perf help: Don't ...
427
  	perf_config(perf_help_config, &help_format);
078006012   Ingo Molnar   perf_counter tool...
428
429
430
431
432
  
  	argc = parse_options(argc, argv, builtin_help_options,
  			builtin_help_usage, 0);
  
  	if (show_all) {
502fc5c72   Ingo Molnar   perf_counter tool...
433
434
435
436
  		printf("
   usage: %s
  
  ", perf_usage_string);
078006012   Ingo Molnar   perf_counter tool...
437
  		list_commands("perf commands", &main_cmds, &other_cmds);
502fc5c72   Ingo Molnar   perf_counter tool...
438
439
440
  		printf(" %s
  
  ", perf_more_info_string);
078006012   Ingo Molnar   perf_counter tool...
441
442
443
444
  		return 0;
  	}
  
  	if (!argv[0]) {
502fc5c72   Ingo Molnar   perf_counter tool...
445
446
447
448
  		printf("
   usage: %s
  
  ", perf_usage_string);
078006012   Ingo Molnar   perf_counter tool...
449
  		list_common_cmds_help();
502fc5c72   Ingo Molnar   perf_counter tool...
450
451
452
453
  		printf("
   %s
  
  ", perf_more_info_string);
078006012   Ingo Molnar   perf_counter tool...
454
455
456
457
458
459
460
461
462
463
464
465
  		return 0;
  	}
  
  	alias = alias_lookup(argv[0]);
  	if (alias && !is_perf_command(argv[0])) {
  		printf("`perf %s' is aliased to `%s'
  ", argv[0], alias);
  		return 0;
  	}
  
  	switch (help_format) {
  	case HELP_FORMAT_MAN:
cc5848213   David Ahern   perf help: Remove...
466
  		rc = show_man_page(argv[0]);
078006012   Ingo Molnar   perf_counter tool...
467
468
  		break;
  	case HELP_FORMAT_INFO:
cc5848213   David Ahern   perf help: Remove...
469
  		rc = show_info_page(argv[0]);
078006012   Ingo Molnar   perf_counter tool...
470
471
  		break;
  	case HELP_FORMAT_WEB:
cc5848213   David Ahern   perf help: Remove...
472
473
474
475
  		rc = show_html_page(argv[0]);
  		break;
  	case HELP_FORMAT_NONE:
  		/* fall-through */
83a0944fa   Ingo Molnar   perf: Enable more...
476
  	default:
cc5848213   David Ahern   perf help: Remove...
477
  		rc = -1;
078006012   Ingo Molnar   perf_counter tool...
478
479
  		break;
  	}
cc5848213   David Ahern   perf help: Remove...
480
  	return rc;
078006012   Ingo Molnar   perf_counter tool...
481
  }