162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include "../../builtin.h"
362306a36Sopenharmony_ci#include "../../perf.h"
462306a36Sopenharmony_ci#include "../../util/util.h" // perf_exe()
562306a36Sopenharmony_ci#include "../util.h"
662306a36Sopenharmony_ci#include "../../util/hist.h"
762306a36Sopenharmony_ci#include "../../util/debug.h"
862306a36Sopenharmony_ci#include "../../util/symbol.h"
962306a36Sopenharmony_ci#include "../browser.h"
1062306a36Sopenharmony_ci#include "../libslang.h"
1162306a36Sopenharmony_ci#include "config.h"
1262306a36Sopenharmony_ci#include <linux/string.h>
1362306a36Sopenharmony_ci#include <linux/zalloc.h>
1462306a36Sopenharmony_ci#include <stdlib.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define SCRIPT_NAMELEN	128
1762306a36Sopenharmony_ci#define SCRIPT_MAX_NO	64
1862306a36Sopenharmony_ci/*
1962306a36Sopenharmony_ci * Usually the full path for a script is:
2062306a36Sopenharmony_ci *	/home/username/libexec/perf-core/scripts/python/xxx.py
2162306a36Sopenharmony_ci *	/home/username/libexec/perf-core/scripts/perl/xxx.pl
2262306a36Sopenharmony_ci * So 256 should be long enough to contain the full path.
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci#define SCRIPT_FULLPATH_LEN	256
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistruct script_config {
2762306a36Sopenharmony_ci	const char **names;
2862306a36Sopenharmony_ci	char **paths;
2962306a36Sopenharmony_ci	int index;
3062306a36Sopenharmony_ci	const char *perf;
3162306a36Sopenharmony_ci	char extra_format[256];
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_civoid attr_to_script(char *extra_format, struct perf_event_attr *attr)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	extra_format[0] = 0;
3762306a36Sopenharmony_ci	if (attr->read_format & PERF_FORMAT_GROUP)
3862306a36Sopenharmony_ci		strcat(extra_format, " -F +metric");
3962306a36Sopenharmony_ci	if (attr->sample_type & PERF_SAMPLE_BRANCH_STACK)
4062306a36Sopenharmony_ci		strcat(extra_format, " -F +brstackinsn --xed");
4162306a36Sopenharmony_ci	if (attr->sample_type & PERF_SAMPLE_REGS_INTR)
4262306a36Sopenharmony_ci		strcat(extra_format, " -F +iregs");
4362306a36Sopenharmony_ci	if (attr->sample_type & PERF_SAMPLE_REGS_USER)
4462306a36Sopenharmony_ci		strcat(extra_format, " -F +uregs");
4562306a36Sopenharmony_ci	if (attr->sample_type & PERF_SAMPLE_PHYS_ADDR)
4662306a36Sopenharmony_ci		strcat(extra_format, " -F +phys_addr");
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic int add_script_option(const char *name, const char *opt,
5062306a36Sopenharmony_ci			     struct script_config *c)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	c->names[c->index] = name;
5362306a36Sopenharmony_ci	if (asprintf(&c->paths[c->index],
5462306a36Sopenharmony_ci		     "%s script %s -F +metric %s %s",
5562306a36Sopenharmony_ci		     c->perf, opt, symbol_conf.inline_name ? " --inline" : "",
5662306a36Sopenharmony_ci		     c->extra_format) < 0)
5762306a36Sopenharmony_ci		return -1;
5862306a36Sopenharmony_ci	c->index++;
5962306a36Sopenharmony_ci	return 0;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic int scripts_config(const char *var, const char *value, void *data)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct script_config *c = data;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (!strstarts(var, "scripts."))
6762306a36Sopenharmony_ci		return -1;
6862306a36Sopenharmony_ci	if (c->index >= SCRIPT_MAX_NO)
6962306a36Sopenharmony_ci		return -1;
7062306a36Sopenharmony_ci	c->names[c->index] = strdup(var + 7);
7162306a36Sopenharmony_ci	if (!c->names[c->index])
7262306a36Sopenharmony_ci		return -1;
7362306a36Sopenharmony_ci	if (asprintf(&c->paths[c->index], "%s %s", value,
7462306a36Sopenharmony_ci		     c->extra_format) < 0)
7562306a36Sopenharmony_ci		return -1;
7662306a36Sopenharmony_ci	c->index++;
7762306a36Sopenharmony_ci	return 0;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/*
8162306a36Sopenharmony_ci * When success, will copy the full path of the selected script
8262306a36Sopenharmony_ci * into  the buffer pointed by script_name, and return 0.
8362306a36Sopenharmony_ci * Return -1 on failure.
8462306a36Sopenharmony_ci */
8562306a36Sopenharmony_cistatic int list_scripts(char *script_name, bool *custom,
8662306a36Sopenharmony_ci			struct evsel *evsel)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	char *buf, *paths[SCRIPT_MAX_NO], *names[SCRIPT_MAX_NO];
8962306a36Sopenharmony_ci	int i, num, choice;
9062306a36Sopenharmony_ci	int ret = 0;
9162306a36Sopenharmony_ci	int max_std, custom_perf;
9262306a36Sopenharmony_ci	char pbuf[256];
9362306a36Sopenharmony_ci	const char *perf = perf_exe(pbuf, sizeof pbuf);
9462306a36Sopenharmony_ci	struct script_config scriptc = {
9562306a36Sopenharmony_ci		.names = (const char **)names,
9662306a36Sopenharmony_ci		.paths = paths,
9762306a36Sopenharmony_ci		.perf = perf
9862306a36Sopenharmony_ci	};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	script_name[0] = 0;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* Preset the script name to SCRIPT_NAMELEN */
10362306a36Sopenharmony_ci	buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN));
10462306a36Sopenharmony_ci	if (!buf)
10562306a36Sopenharmony_ci		return -1;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (evsel)
10862306a36Sopenharmony_ci		attr_to_script(scriptc.extra_format, &evsel->core.attr);
10962306a36Sopenharmony_ci	add_script_option("Show individual samples", "", &scriptc);
11062306a36Sopenharmony_ci	add_script_option("Show individual samples with assembler", "-F +insn --xed",
11162306a36Sopenharmony_ci			  &scriptc);
11262306a36Sopenharmony_ci	add_script_option("Show individual samples with source", "-F +srcline,+srccode",
11362306a36Sopenharmony_ci			  &scriptc);
11462306a36Sopenharmony_ci	perf_config(scripts_config, &scriptc);
11562306a36Sopenharmony_ci	custom_perf = scriptc.index;
11662306a36Sopenharmony_ci	add_script_option("Show samples with custom perf script arguments", "", &scriptc);
11762306a36Sopenharmony_ci	i = scriptc.index;
11862306a36Sopenharmony_ci	max_std = i;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	for (; i < SCRIPT_MAX_NO; i++) {
12162306a36Sopenharmony_ci		names[i] = buf + (i - max_std) * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN);
12262306a36Sopenharmony_ci		paths[i] = names[i] + SCRIPT_NAMELEN;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	num = find_scripts(names + max_std, paths + max_std, SCRIPT_MAX_NO - max_std,
12662306a36Sopenharmony_ci			SCRIPT_FULLPATH_LEN);
12762306a36Sopenharmony_ci	if (num < 0)
12862306a36Sopenharmony_ci		num = 0;
12962306a36Sopenharmony_ci	choice = ui__popup_menu(num + max_std, (char * const *)names, NULL);
13062306a36Sopenharmony_ci	if (choice < 0) {
13162306a36Sopenharmony_ci		ret = -1;
13262306a36Sopenharmony_ci		goto out;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci	if (choice == custom_perf) {
13562306a36Sopenharmony_ci		char script_args[50];
13662306a36Sopenharmony_ci		int key = ui_browser__input_window("perf script command",
13762306a36Sopenharmony_ci				"Enter perf script command line (without perf script prefix)",
13862306a36Sopenharmony_ci				script_args, "", 0);
13962306a36Sopenharmony_ci		if (key != K_ENTER) {
14062306a36Sopenharmony_ci			ret = -1;
14162306a36Sopenharmony_ci			goto out;
14262306a36Sopenharmony_ci		}
14362306a36Sopenharmony_ci		sprintf(script_name, "%s script %s", perf, script_args);
14462306a36Sopenharmony_ci	} else if (choice < num + max_std) {
14562306a36Sopenharmony_ci		strcpy(script_name, paths[choice]);
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci	*custom = choice >= max_std;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ciout:
15062306a36Sopenharmony_ci	free(buf);
15162306a36Sopenharmony_ci	for (i = 0; i < max_std; i++)
15262306a36Sopenharmony_ci		zfree(&paths[i]);
15362306a36Sopenharmony_ci	return ret;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_civoid run_script(char *cmd)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	pr_debug("Running %s\n", cmd);
15962306a36Sopenharmony_ci	SLang_reset_tty();
16062306a36Sopenharmony_ci	if (system(cmd) < 0)
16162306a36Sopenharmony_ci		pr_warning("Cannot run %s\n", cmd);
16262306a36Sopenharmony_ci	/*
16362306a36Sopenharmony_ci	 * SLang doesn't seem to reset the whole terminal, so be more
16462306a36Sopenharmony_ci	 * forceful to get back to the original state.
16562306a36Sopenharmony_ci	 */
16662306a36Sopenharmony_ci	printf("\033[c\033[H\033[J");
16762306a36Sopenharmony_ci	fflush(stdout);
16862306a36Sopenharmony_ci	SLang_init_tty(0, 0, 0);
16962306a36Sopenharmony_ci	SLsmg_refresh();
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ciint script_browse(const char *script_opt, struct evsel *evsel)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	char *cmd, script_name[SCRIPT_FULLPATH_LEN];
17562306a36Sopenharmony_ci	bool custom = false;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	memset(script_name, 0, SCRIPT_FULLPATH_LEN);
17862306a36Sopenharmony_ci	if (list_scripts(script_name, &custom, evsel))
17962306a36Sopenharmony_ci		return -1;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (asprintf(&cmd, "%s%s %s %s%s 2>&1 | less",
18262306a36Sopenharmony_ci			custom ? "perf script -s " : "",
18362306a36Sopenharmony_ci			script_name,
18462306a36Sopenharmony_ci			script_opt ? script_opt : "",
18562306a36Sopenharmony_ci			input_name ? "-i " : "",
18662306a36Sopenharmony_ci			input_name ? input_name : "") < 0)
18762306a36Sopenharmony_ci		return -1;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	run_script(cmd);
19062306a36Sopenharmony_ci	free(cmd);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return 0;
19362306a36Sopenharmony_ci}
194