cregit-Linux how code gets into the kernel

Release 4.14 tools/perf/builtin-script.c

Directory: tools/perf
// SPDX-License-Identifier: GPL-2.0
#include "builtin.h"

#include "perf.h"
#include "util/cache.h"
#include "util/debug.h"
#include <subcmd/exec-cmd.h>
#include "util/header.h"
#include <subcmd/parse-options.h>
#include "util/perf_regs.h"
#include "util/session.h"
#include "util/tool.h"
#include "util/symbol.h"
#include "util/thread.h"
#include "util/trace-event.h"
#include "util/util.h"
#include "util/evlist.h"
#include "util/evsel.h"
#include "util/sort.h"
#include "util/data.h"
#include "util/auxtrace.h"
#include "util/cpumap.h"
#include "util/thread_map.h"
#include "util/stat.h"
#include "util/string2.h"
#include "util/thread-stack.h"
#include "util/time-utils.h"
#include "print_binary.h"
#include <linux/bitmap.h>
#include <linux/kernel.h>
#include <linux/stringify.h>
#include <linux/time64.h>
#include "asm/bug.h"
#include "util/mem-events.h"
#include "util/dump-insn.h"
#include <dirent.h>
#include <errno.h>
#include <inttypes.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include "sane_ctype.h"


static char const		*script_name;

static char const		*generate_script_lang;

static bool			debug_mode;

static u64			last_timestamp;

static u64			nr_unordered;

static bool			no_callchain;

static bool			latency_format;

static bool			system_wide;

static bool			print_flags;

static bool			nanosecs;

static const char		*cpu_list;
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);

static struct perf_stat_config	stat_config;

static int			max_blocks;


unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH;


enum perf_output_field {
	
PERF_OUTPUT_COMM            = 1U << 0,
	
PERF_OUTPUT_TID             = 1U << 1,
	
PERF_OUTPUT_PID             = 1U << 2,
	
PERF_OUTPUT_TIME            = 1U << 3,
	
PERF_OUTPUT_CPU             = 1U << 4,
	
PERF_OUTPUT_EVNAME          = 1U << 5,
	
PERF_OUTPUT_TRACE           = 1U << 6,
	
PERF_OUTPUT_IP              = 1U << 7,
	
PERF_OUTPUT_SYM             = 1U << 8,
	
PERF_OUTPUT_DSO             = 1U << 9,
	
PERF_OUTPUT_ADDR            = 1U << 10,
	
PERF_OUTPUT_SYMOFFSET       = 1U << 11,
	
PERF_OUTPUT_SRCLINE         = 1U << 12,
	
PERF_OUTPUT_PERIOD          = 1U << 13,
	
PERF_OUTPUT_IREGS	    = 1U << 14,
	
PERF_OUTPUT_BRSTACK	    = 1U << 15,
	
PERF_OUTPUT_BRSTACKSYM	    = 1U << 16,
	
PERF_OUTPUT_DATA_SRC	    = 1U << 17,
	
PERF_OUTPUT_WEIGHT	    = 1U << 18,
	
PERF_OUTPUT_BPF_OUTPUT	    = 1U << 19,
	
PERF_OUTPUT_CALLINDENT	    = 1U << 20,
	
PERF_OUTPUT_INSN	    = 1U << 21,
	
PERF_OUTPUT_INSNLEN	    = 1U << 22,
	
PERF_OUTPUT_BRSTACKINSN	    = 1U << 23,
	
PERF_OUTPUT_BRSTACKOFF	    = 1U << 24,
	
PERF_OUTPUT_SYNTH           = 1U << 25,
	
PERF_OUTPUT_PHYS_ADDR       = 1U << 26,
};


struct output_option {
	
const char *str;
	
enum perf_output_field field;

} all_output_options[] = {
	{.str = "comm",  .field = PERF_OUTPUT_COMM},
	{.str = "tid",   .field = PERF_OUTPUT_TID},
	{.str = "pid",   .field = PERF_OUTPUT_PID},
	{.str = "time",  .field = PERF_OUTPUT_TIME},
	{.str = "cpu",   .field = PERF_OUTPUT_CPU},
	{.str = "event", .field = PERF_OUTPUT_EVNAME},
	{.str = "trace", .field = PERF_OUTPUT_TRACE},
	{.str = "ip",    .field = PERF_OUTPUT_IP},
	{.str = "sym",   .field = PERF_OUTPUT_SYM},
	{.str = "dso",   .field = PERF_OUTPUT_DSO},
	{.str = "addr",  .field = PERF_OUTPUT_ADDR},
	{.str = "symoff", .field = PERF_OUTPUT_SYMOFFSET},
	{.str = "srcline", .field = PERF_OUTPUT_SRCLINE},
	{.str = "period", .field = PERF_OUTPUT_PERIOD},
	{.str = "iregs", .field = PERF_OUTPUT_IREGS},
	{.str = "brstack", .field = PERF_OUTPUT_BRSTACK},
	{.str = "brstacksym", .field = PERF_OUTPUT_BRSTACKSYM},
	{.str = "data_src", .field = PERF_OUTPUT_DATA_SRC},
	{.str = "weight",   .field = PERF_OUTPUT_WEIGHT},
	{.str = "bpf-output",   .field = PERF_OUTPUT_BPF_OUTPUT},
	{.str = "callindent", .field = PERF_OUTPUT_CALLINDENT},
	{.str = "insn", .field = PERF_OUTPUT_INSN},
	{.str = "insnlen", .field = PERF_OUTPUT_INSNLEN},
	{.str = "brstackinsn", .field = PERF_OUTPUT_BRSTACKINSN},
	{.str = "brstackoff", .field = PERF_OUTPUT_BRSTACKOFF},
	{.str = "synth", .field = PERF_OUTPUT_SYNTH},
	{.str = "phys_addr", .field = PERF_OUTPUT_PHYS_ADDR},
};

enum {
	
OUTPUT_TYPE_SYNTH = PERF_TYPE_MAX,
	
OUTPUT_TYPE_MAX
};

/* default set to maintain compatibility with current format */
static struct {
	
bool user_set;
	
bool wildcard_set;
	
unsigned int print_ip_opts;
	
u64 fields;
	
u64 invalid_fields;

} output[OUTPUT_TYPE_MAX] = {

	[PERF_TYPE_HARDWARE] = {
		.user_set = false,

		.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
			      PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
			      PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |
			      PERF_OUTPUT_SYM | PERF_OUTPUT_DSO |
			      PERF_OUTPUT_PERIOD,

		.invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT,
        },

	[PERF_TYPE_SOFTWARE] = {
		.user_set = false,

		.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
			      PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
			      PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |
			      PERF_OUTPUT_SYM | PERF_OUTPUT_DSO |
			      PERF_OUTPUT_PERIOD | PERF_OUTPUT_BPF_OUTPUT,

		.invalid_fields = PERF_OUTPUT_TRACE,
        },

	[PERF_TYPE_TRACEPOINT] = {
		.user_set = false,

		.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
				  PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
				  PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE
	},

	[PERF_TYPE_RAW] = {
		.user_set = false,

		.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
			      PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
			      PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |
			      PERF_OUTPUT_SYM | PERF_OUTPUT_DSO |
			      PERF_OUTPUT_PERIOD |  PERF_OUTPUT_ADDR |
			      PERF_OUTPUT_DATA_SRC | PERF_OUTPUT_WEIGHT |
			      PERF_OUTPUT_PHYS_ADDR,

		.invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT,
        },

	[PERF_TYPE_BREAKPOINT] = {
		.user_set = false,

		.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
			      PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
			      PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |
			      PERF_OUTPUT_SYM | PERF_OUTPUT_DSO |
			      PERF_OUTPUT_PERIOD,

		.invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT,
        },

	[OUTPUT_TYPE_SYNTH] = {
		.user_set = false,

		.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
			      PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
			      PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |
			      PERF_OUTPUT_SYM | PERF_OUTPUT_DSO |
			      PERF_OUTPUT_SYNTH,

		.invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT,
        },
};


static inline int output_type(unsigned int type) { switch (type) { case PERF_TYPE_SYNTH: return OUTPUT_TYPE_SYNTH; default: return type; } }

Contributors

PersonTokensPropCommitsCommitProp
Adrian Hunter27100.00%1100.00%
Total27100.00%1100.00%


static inline unsigned int attr_type(unsigned int type) { switch (type) { case OUTPUT_TYPE_SYNTH: return PERF_TYPE_SYNTH; default: return type; } }

Contributors

PersonTokensPropCommitsCommitProp
Adrian Hunter28100.00%1100.00%
Total28100.00%1100.00%


static bool output_set_by_user(void) { int j; for (j = 0; j < OUTPUT_TYPE_MAX; ++j) { if (output[j].user_set) return true; } return false; }

Contributors

PersonTokensPropCommitsCommitProp
David Ahern4097.56%266.67%
Adrian Hunter12.44%133.33%
Total41100.00%3100.00%


static const char *output_field2str(enum perf_output_field field) { int i, imax = ARRAY_SIZE(all_output_options); const char *str = ""; for (i = 0; i < imax; ++i) { if (all_output_options[i].field == field) { str = all_output_options[i].str; break; } } return str; }

Contributors

PersonTokensPropCommitsCommitProp
David Ahern70100.00%1100.00%
Total70100.00%1100.00%

#define PRINT_FIELD(x) (output[output_type(attr->type)].fields & PERF_OUTPUT_##x)
static int perf_evsel__do_check_stype(struct perf_evsel *evsel, u64 sample_type, const char *sample_msg, enum perf_output_field field, bool allow_user_set) { struct perf_event_attr *attr = &evsel->attr; int type = output_type(attr->type); const char *evname; if (attr->sample_type & sample_type) return 0; if (output[type].user_set) { if (allow_user_set) return 0; evname = perf_evsel__name(evsel); pr_err("Samples for '%s' event do not have %s attribute set. " "Cannot print '%s' field.\n", evname, sample_msg, output_field2str(field)); return -1; } /* user did not ask for it explicitly so remove from the default list */ output[type].fields &= ~field; evname = perf_evsel__name(evsel); pr_debug("Samples for '%s' event do not have %s attribute set. " "Skipping '%s' field.\n", evname, sample_msg, output_field2str(field)); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
David Ahern11278.87%125.00%
Arnaldo Carvalho de Melo1611.27%125.00%
Adrian Hunter149.86%250.00%
Total142100.00%4100.00%


static int perf_evsel__check_stype(struct perf_evsel *evsel, u64 sample_type, const char *sample_msg, enum perf_output_field field) { return perf_evsel__do_check_stype(evsel, sample_type, sample_msg, field, false); }

Contributors

PersonTokensPropCommitsCommitProp
Adrian Hunter37100.00%1100.00%
Total37100.00%1100.00%


static int perf_evsel__check_attr(struct perf_evsel *evsel, struct perf_session *session) { struct perf_event_attr *attr = &evsel->attr; bool allow_user_set; if (perf_header__has_feat(&session->header, HEADER_STAT)) return 0; allow_user_set = perf_header__has_feat(&session->header, HEADER_AUXTRACE); if (PRINT_FIELD(TRACE) && !perf_session__has_traces(session, "record -R")) return -EINVAL; if (PRINT_FIELD(IP)) { if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP", PERF_OUTPUT_IP)) return -EINVAL; } if (PRINT_FIELD(ADDR) && perf_evsel__do_check_stype(evsel, PERF_SAMPLE_ADDR, "ADDR", PERF_OUTPUT_ADDR, allow_user_set)) return -EINVAL; if (PRINT_FIELD(DATA_SRC) && perf_evsel__check_stype(evsel, PERF_SAMPLE_DATA_SRC, "DATA_SRC", PERF_OUTPUT_DATA_SRC)) return -EINVAL; if (PRINT_FIELD(WEIGHT) && perf_evsel__check_stype(evsel, PERF_SAMPLE_WEIGHT, "WEIGHT", PERF_OUTPUT_WEIGHT)) return -EINVAL; if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { pr_err("Display of symbols requested but neither sample IP nor " "sample address\nis selected. Hence, no addresses to convert " "to symbols.\n"); return -EINVAL; } if (PRINT_FIELD(SYMOFFSET) && !PRINT_FIELD(SYM)) { pr_err("Display of offsets requested but symbol is not" "selected.\n"); return -EINVAL; } if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR) && !PRINT_FIELD(BRSTACK) && !PRINT_FIELD(BRSTACKSYM) && !PRINT_FIELD(BRSTACKOFF)) { pr_err("Display of DSO requested but no address to convert. Select\n" "sample IP, sample address, brstack, brstacksym, or brstackoff.\n"); return -EINVAL; } if (PRINT_FIELD(SRCLINE) && !PRINT_FIELD(IP)) { pr_err("Display of source line number requested but sample IP is not\n" "selected. Hence, no address to lookup the source line number.\n"); return -EINVAL; } if (PRINT_FIELD(BRSTACKINSN) && !(perf_evlist__combined_branch_type(session->evlist) & PERF_SAMPLE_BRANCH_ANY)) { pr_err("Display of branch stack assembler requested, but non all-branch filter set\n" "Hint: run 'perf record -b ...'\n"); return -EINVAL; } if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID", PERF_OUTPUT_TID|PERF_OUTPUT_PID)) return -EINVAL; if (PRINT_FIELD(TIME) && perf_evsel__check_stype(evsel, PERF_SAMPLE_TIME, "TIME", PERF_OUTPUT_TIME)) return -EINVAL; if (PRINT_FIELD(CPU) && perf_evsel__do_check_stype(evsel, PERF_SAMPLE_CPU, "CPU", PERF_OUTPUT_CPU, allow_user_set)) return -EINVAL; if (PRINT_FIELD(PERIOD) && perf_evsel__check_stype(evsel, PERF_SAMPLE_PERIOD, "PERIOD", PERF_OUTPUT_PERIOD)) return -EINVAL; if (PRINT_FIELD(IREGS) && perf_evsel__check_stype(evsel, PERF_SAMPLE_REGS_INTR, "IREGS", PERF_OUTPUT_IREGS)) return -EINVAL; if (PRINT_FIELD(PHYS_ADDR) && perf_evsel__check_stype(evsel, PERF_SAMPLE_PHYS_ADDR, "PHYS_ADDR", PERF_OUTPUT_PHYS_ADDR)) return -EINVAL; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
David Ahern22246.54%529.41%
Jiri Olsa8116.98%317.65%
Adrian Hunter469.64%211.76%
Andi Kleen316.50%15.88%
Akihiro Nagai255.24%15.88%
Kan Liang224.61%15.88%
Stéphane Eranian224.61%15.88%
Mark Santaniello204.19%211.76%
Arnaldo Carvalho de Melo81.68%15.88%
Total477100.00%17100.00%


static void set_print_ip_opts(struct perf_event_attr *attr) { unsigned int type = output_type(attr->type); output[type].print_ip_opts = 0; if (PRINT_FIELD(IP)) output[type].print_ip_opts |= EVSEL__PRINT_IP; if (PRINT_FIELD(SYM)) output[type].print_ip_opts |= EVSEL__PRINT_SYM; if (PRINT_FIELD(DSO)) output[type].print_ip_opts |= EVSEL__PRINT_DSO; if (PRINT_FIELD(SYMOFFSET)) output[type].print_ip_opts |= EVSEL__PRINT_SYMOFFSET; if (PRINT_FIELD(SRCLINE)) output[type].print_ip_opts |= EVSEL__PRINT_SRCLINE; }

Contributors

PersonTokensPropCommitsCommitProp
Adrian Hunter10695.50%375.00%
Arnaldo Carvalho de Melo54.50%125.00%
Total111100.00%4100.00%

/* * verify all user requested events exist and the samples * have the expected data */
static int perf_session__check_output_opt(struct perf_session *session) { unsigned int j; struct perf_evsel *evsel; for (j = 0; j < OUTPUT_TYPE_MAX; ++j) { evsel = perf_session__find_first_evtype(session, attr_type(j)); /* * even if fields is set to 0 (ie., show nothing) event must * exist if user explicitly includes it on the command line */ if (!evsel && output[j].user_set && !output[j].wildcard_set && j != OUTPUT_TYPE_SYNTH) { pr_err("%s events do not exist. " "Remove corresponding -F option to proceed.\n", event_type(j)); return -1; } if (evsel && output[j].fields && perf_evsel__check_attr(evsel, session)) return -1; if (evsel == NULL) continue; set_print_ip_opts(&evsel->attr); } if (!no_callchain) { bool use_callchain = false; bool not_pipe = false; evlist__for_each_entry(session->evlist, evsel) { not_pipe = true; if (evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) { use_callchain = true; break; } } if (not_pipe && !use_callchain) symbol_conf.use_callchain = false; } /* * set default for tracepoints to print symbols only * if callchains are present */ if (symbol_conf.use_callchain && !output[PERF_TYPE_TRACEPOINT].user_set) { struct perf_event_attr *attr; j = PERF_TYPE_TRACEPOINT; evlist__for_each_entry(session->evlist, evsel) { if (evsel->attr.type != j) continue; attr = &evsel->attr; if (attr->sample_type & PERF_SAMPLE_CALLCHAIN) { output[j].fields |= PERF_OUTPUT_IP; output[j].fields |= PERF_OUTPUT_SYM; output[j].fields |= PERF_OUTPUT_DSO; set_print_ip_opts(attr); goto out; } } } out: return 0; }

Contributors

PersonTokensPropCommitsCommitProp
David Ahern20170.03%436.36%
Adrian Hunter5820.21%436.36%
He Kuang269.06%218.18%
Arnaldo Carvalho de Melo20.70%19.09%
Total287100.00%11100.00%


static void print_sample_iregs(struct perf_sample *sample, struct perf_event_attr *attr) { struct regs_dump *regs = &sample->intr_regs; uint64_t mask = attr->sample_regs_intr; unsigned i = 0, r; if (!regs) return; for_each_set_bit(r, (unsigned long *) &mask, sizeof(mask) * 8) { u64 val = regs->regs[i++]; printf("%5s:0x%"PRIx64" ", perf_reg_name(r), val); } }

Contributors

PersonTokensPropCommitsCommitProp
Stéphane Eranian82100.00%1100.00%
Total82100.00%1100.00%


static void print_sample_start(struct perf_sample *sample, struct thread *thread, struct perf_evsel *evsel) { struct perf_event_attr *attr = &evsel->attr; unsigned long secs; unsigned long long nsecs; if (PRINT_FIELD(COMM)) { if (latency_format) printf("%8.8s ", thread__comm_str(thread)); else if (PRINT_FIELD(IP) && symbol_conf.use_callchain) printf("%s ", thread__comm_str(thread)); else printf("%16s ", thread__comm_str(thread)); } if (PRINT_FIELD(PID) && PRINT_FIELD(TID)) printf("%5d/%-5d ", sample->pid, sample->tid); else if (PRINT_FIELD(PID)) printf("%5d ", sample->pid); else if (PRINT_FIELD(TID)) printf("%5d ", sample->tid); if (PRINT_FIELD(CPU)) { if (latency_format) printf("%3d ", sample->cpu); else printf("[%03d] ", sample->cpu); } if (PRINT_FIELD(TIME)) { nsecs = sample->time; secs = nsecs / NSEC_PER_SEC; nsecs -= secs * NSEC_PER_SEC; if (nanosecs) printf("%5lu.%09llu: ", secs, nsecs); else { char sample_time[32]; timestamp__scnprintf_usec(sample->time, sample_time, sizeof(sample_time)); printf("%12s: ", sample_time); } } }

Contributors

PersonTokensPropCommitsCommitProp
David Ahern19776.65%550.00%
Namhyung Kim249.34%110.00%
Adrian Hunter145.45%110.00%
Arnaldo Carvalho de Melo135.06%220.00%
Frédéric Weisbecker93.50%110.00%
Total257100.00%10100.00%


static inline char mispred_str(struct branch_entry *br) { if (!(br->flags.mispred || br->flags.predicted)) return '-'; return br->flags.predicted ? 'P' : 'M'; }

Contributors

PersonTokensPropCommitsCommitProp
Stéphane Eranian43100.00%1100.00%
Total43100.00%1100.00%


static void print_sample_brstack(struct perf_sample *sample, struct thread *thread, struct perf_event_attr *attr) { struct branch_stack *br = sample->branch_stack; struct addr_location alf, alt; u64 i, from, to; if (!(br && br->nr)) return; for (i = 0; i < br->nr; i++) { from = br->entries[i].from; to = br->entries[i].to; if (PRINT_FIELD(DSO)) { memset(&alf, 0, sizeof(alf)); memset(&alt, 0, sizeof(alt)); thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, from, &alf); thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, to, &alt); } printf(" 0x%"PRIx64, from); if (PRINT_FIELD(DSO)) { printf("("); map__fprintf_dsoname(alf.map, stdout); printf(")"); } printf("/0x%"PRIx64, to); if (PRINT_FIELD(DSO)) { printf("("); map__fprintf_dsoname(alt.map, stdout); printf(")"); } printf("/%c/%c/%c/%d ", mispred_str( br->entries + i), br->entries[i].flags.in_tx? 'X' : '-', br->entries[i].flags.abort? 'A' : '-', br->entries[i].flags.cycles); } }

Contributors

PersonTokensPropCommitsCommitProp
Mark Santaniello16858.33%266.67%
Stéphane Eranian12041.67%133.33%
Total288100.00%3100.00%


static void print_sample_brstacksym(struct perf_sample *sample, struct thread *thread, struct perf_event_attr *attr) { struct branch_stack *br = sample->branch_stack; struct addr_location alf, alt; u64 i, from, to; if (!(br && br->nr)) return; for (i = 0; i <