mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-06 12:44:14 +08:00
4968ac8fb7
Now 'perf report' can show whole time periods with 'perf script', but the user still has to find individual samples of interest manually. It would be expensive and complicated to search for the right samples in the whole perf file. Typically users only need to look at a small number of samples for useful analysis. Also the full scripts tend to show samples of all CPUs and all threads mixed up, which can be very confusing on larger systems. Add a new --samples option to save a small random number of samples per hist entry. Use a reservoir sample technique to select a representatve number of samples. Then allow browsing the samples using 'perf script' as part of the hist entry context menu. This automatically adds the right filters, so only the thread or cpu of the sample is displayed. Then we use less' search functionality to directly jump the to the time stamp of the selected sample. It uses different menus for assembler and source display. Assembler needs xed installed and source needs debuginfo. Currently it only supports as many samples as fit on the screen due to some limitations in the slang ui code. Signed-off-by: Andi Kleen <ak@linux.intel.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Acked-by: Jiri Olsa <jolsa@kernel.org> Link: http://lkml.kernel.org/r/20190311174605.GA29294@tassilo.jf.intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
166 lines
4.3 KiB
C
166 lines
4.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include "../../util/sort.h"
|
|
#include "../../util/util.h"
|
|
#include "../../util/hist.h"
|
|
#include "../../util/debug.h"
|
|
#include "../../util/symbol.h"
|
|
#include "../browser.h"
|
|
#include "../libslang.h"
|
|
|
|
#define SCRIPT_NAMELEN 128
|
|
#define SCRIPT_MAX_NO 64
|
|
/*
|
|
* Usually the full path for a script is:
|
|
* /home/username/libexec/perf-core/scripts/python/xxx.py
|
|
* /home/username/libexec/perf-core/scripts/perl/xxx.pl
|
|
* So 256 should be long enough to contain the full path.
|
|
*/
|
|
#define SCRIPT_FULLPATH_LEN 256
|
|
|
|
struct script_config {
|
|
const char **names;
|
|
char **paths;
|
|
int index;
|
|
const char *perf;
|
|
char extra_format[256];
|
|
};
|
|
|
|
void attr_to_script(char *extra_format, struct perf_event_attr *attr)
|
|
{
|
|
extra_format[0] = 0;
|
|
if (attr->read_format & PERF_FORMAT_GROUP)
|
|
strcat(extra_format, " -F +metric");
|
|
if (attr->sample_type & PERF_SAMPLE_BRANCH_STACK)
|
|
strcat(extra_format, " -F +brstackinsn --xed");
|
|
if (attr->sample_type & PERF_SAMPLE_REGS_INTR)
|
|
strcat(extra_format, " -F +iregs");
|
|
if (attr->sample_type & PERF_SAMPLE_REGS_USER)
|
|
strcat(extra_format, " -F +uregs");
|
|
if (attr->sample_type & PERF_SAMPLE_PHYS_ADDR)
|
|
strcat(extra_format, " -F +phys_addr");
|
|
}
|
|
|
|
static int add_script_option(const char *name, const char *opt,
|
|
struct script_config *c)
|
|
{
|
|
c->names[c->index] = name;
|
|
if (asprintf(&c->paths[c->index],
|
|
"%s script %s -F +metric %s %s",
|
|
c->perf, opt, symbol_conf.inline_name ? " --inline" : "",
|
|
c->extra_format) < 0)
|
|
return -1;
|
|
c->index++;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* When success, will copy the full path of the selected script
|
|
* into the buffer pointed by script_name, and return 0.
|
|
* Return -1 on failure.
|
|
*/
|
|
static int list_scripts(char *script_name, bool *custom,
|
|
struct perf_evsel *evsel)
|
|
{
|
|
char *buf, *paths[SCRIPT_MAX_NO], *names[SCRIPT_MAX_NO];
|
|
int i, num, choice;
|
|
int ret = 0;
|
|
int max_std, custom_perf;
|
|
char pbuf[256];
|
|
const char *perf = perf_exe(pbuf, sizeof pbuf);
|
|
struct script_config scriptc = {
|
|
.names = (const char **)names,
|
|
.paths = paths,
|
|
.perf = perf
|
|
};
|
|
|
|
script_name[0] = 0;
|
|
|
|
/* Preset the script name to SCRIPT_NAMELEN */
|
|
buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN));
|
|
if (!buf)
|
|
return -1;
|
|
|
|
if (evsel)
|
|
attr_to_script(scriptc.extra_format, &evsel->attr);
|
|
add_script_option("Show individual samples", "", &scriptc);
|
|
add_script_option("Show individual samples with assembler", "-F +insn --xed",
|
|
&scriptc);
|
|
add_script_option("Show individual samples with source", "-F +srcline,+srccode",
|
|
&scriptc);
|
|
custom_perf = scriptc.index;
|
|
add_script_option("Show samples with custom perf script arguments", "", &scriptc);
|
|
i = scriptc.index;
|
|
max_std = i;
|
|
|
|
for (; i < SCRIPT_MAX_NO; i++) {
|
|
names[i] = buf + (i - max_std) * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN);
|
|
paths[i] = names[i] + SCRIPT_NAMELEN;
|
|
}
|
|
|
|
num = find_scripts(names + max_std, paths + max_std);
|
|
if (num < 0)
|
|
num = 0;
|
|
choice = ui__popup_menu(num + max_std, (char * const *)names);
|
|
if (choice < 0) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (choice == custom_perf) {
|
|
char script_args[50];
|
|
int key = ui_browser__input_window("perf script command",
|
|
"Enter perf script command line (without perf script prefix)",
|
|
script_args, "", 0);
|
|
if (key != K_ENTER)
|
|
return -1;
|
|
sprintf(script_name, "%s script %s", perf, script_args);
|
|
} else if (choice < num + max_std) {
|
|
strcpy(script_name, paths[choice]);
|
|
}
|
|
*custom = choice >= max_std;
|
|
|
|
out:
|
|
free(buf);
|
|
for (i = 0; i < max_std; i++)
|
|
free(paths[i]);
|
|
return ret;
|
|
}
|
|
|
|
void run_script(char *cmd)
|
|
{
|
|
pr_debug("Running %s\n", cmd);
|
|
SLang_reset_tty();
|
|
if (system(cmd) < 0)
|
|
pr_warning("Cannot run %s\n", cmd);
|
|
/*
|
|
* SLang doesn't seem to reset the whole terminal, so be more
|
|
* forceful to get back to the original state.
|
|
*/
|
|
printf("\033[c\033[H\033[J");
|
|
fflush(stdout);
|
|
SLang_init_tty(0, 0, 0);
|
|
SLsmg_refresh();
|
|
}
|
|
|
|
int script_browse(const char *script_opt, struct perf_evsel *evsel)
|
|
{
|
|
char *cmd, script_name[SCRIPT_FULLPATH_LEN];
|
|
bool custom = false;
|
|
|
|
memset(script_name, 0, SCRIPT_FULLPATH_LEN);
|
|
if (list_scripts(script_name, &custom, evsel))
|
|
return -1;
|
|
|
|
if (asprintf(&cmd, "%s%s %s %s%s 2>&1 | less",
|
|
custom ? "perf script -s " : "",
|
|
script_name,
|
|
script_opt ? script_opt : "",
|
|
input_name ? "-i " : "",
|
|
input_name ? input_name : "") < 0)
|
|
return -1;
|
|
|
|
run_script(cmd);
|
|
free(cmd);
|
|
|
|
return 0;
|
|
}
|