diff options
Diffstat (limited to 'tools/perf/util')
45 files changed, 945 insertions, 188 deletions
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index eb60e613d795..1dc67efad634 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -120,7 +120,7 @@ libperf-y += demangle-rust.o ifdef CONFIG_JITDUMP libperf-$(CONFIG_LIBELF) += jitdump.o libperf-$(CONFIG_LIBELF) += genelf.o -libperf-$(CONFIG_LIBELF) += genelf_debug.o +libperf-$(CONFIG_DWARF) += genelf_debug.o endif CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 2b2c9b82f5ab..a5fd275238f7 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -241,7 +241,7 @@ parse_prog_config_kvpair(const char *config_str, struct perf_probe_event *pev) int err = 0; if (!text) { - pr_debug("No enough memory: dup config_str failed\n"); + pr_debug("Not enough memory: dup config_str failed\n"); return ERR_PTR(-ENOMEM); } @@ -531,7 +531,7 @@ static int map_prologue(struct perf_probe_event *pev, int *mapping, ptevs = malloc(array_sz); if (!ptevs) { - pr_debug("No enough memory: alloc ptevs failed\n"); + pr_debug("Not enough memory: alloc ptevs failed\n"); return -ENOMEM; } @@ -604,13 +604,13 @@ static int hook_load_preprocessor(struct bpf_program *prog) priv->need_prologue = true; priv->insns_buf = malloc(sizeof(struct bpf_insn) * BPF_MAXINSNS); if (!priv->insns_buf) { - pr_debug("No enough memory: alloc insns_buf failed\n"); + pr_debug("Not enough memory: alloc insns_buf failed\n"); return -ENOMEM; } priv->type_mapping = malloc(sizeof(int) * pev->ntevs); if (!priv->type_mapping) { - pr_debug("No enough memory: alloc type_mapping failed\n"); + pr_debug("Not enough memory: alloc type_mapping failed\n"); return -ENOMEM; } memset(priv->type_mapping, -1, @@ -864,7 +864,7 @@ bpf_map_op_setkey(struct bpf_map_op *op, struct parse_events_term *term) op->k.array.ranges = memdup(term->array.ranges, memsz); if (!op->k.array.ranges) { - pr_debug("No enough memory to alloc indices for map\n"); + pr_debug("Not enough memory to alloc indices for map\n"); return -ENOMEM; } op->key_type = BPF_MAP_KEY_RANGES; @@ -929,7 +929,7 @@ bpf_map_priv__clone(struct bpf_map_priv *priv) newpriv = zalloc(sizeof(*newpriv)); if (!newpriv) { - pr_debug("No enough memory to alloc map private\n"); + pr_debug("Not enough memory to alloc map private\n"); return NULL; } INIT_LIST_HEAD(&newpriv->ops_list); @@ -960,7 +960,7 @@ bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op) if (!priv) { priv = zalloc(sizeof(*priv)); if (!priv) { - pr_debug("No enough memory to alloc map private\n"); + pr_debug("Not enough memory to alloc map private\n"); return -ENOMEM; } INIT_LIST_HEAD(&priv->ops_list); diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 07fd30bc2f81..823befd8209a 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -193,7 +193,6 @@ int perf_callchain_config(const char *var, const char *value) if (!strcmp(var, "record-mode")) return parse_callchain_record_opt(value, &callchain_param); -#ifdef HAVE_DWARF_UNWIND_SUPPORT if (!strcmp(var, "dump-size")) { unsigned long size = 0; int ret; @@ -203,7 +202,6 @@ int perf_callchain_config(const char *var, const char *value) return ret; } -#endif if (!strcmp(var, "print-type")) return parse_callchain_mode(value); if (!strcmp(var, "order")) @@ -440,6 +438,21 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor) call->ip = cursor_node->ip; call->ms.sym = cursor_node->sym; call->ms.map = cursor_node->map; + + if (cursor_node->branch) { + call->branch_count = 1; + + if (cursor_node->branch_flags.predicted) + call->predicted_count = 1; + + if (cursor_node->branch_flags.abort) + call->abort_count = 1; + + call->cycles_count = cursor_node->branch_flags.cycles; + call->iter_count = cursor_node->nr_loop_iter; + call->samples_count = cursor_node->samples; + } + list_add_tail(&call->list, &node->val); callchain_cursor_advance(cursor); @@ -499,8 +512,23 @@ static enum match_result match_chain(struct callchain_cursor_node *node, right = node->ip; } - if (left == right) + if (left == right) { + if (node->branch) { + cnode->branch_count++; + + if (node->branch_flags.predicted) + cnode->predicted_count++; + + if (node->branch_flags.abort) + cnode->abort_count++; + + cnode->cycles_count += node->branch_flags.cycles; + cnode->iter_count += node->nr_loop_iter; + cnode->samples_count += node->samples; + } + return MATCH_EQ; + } return left > right ? MATCH_GT : MATCH_LT; } @@ -730,7 +758,8 @@ merge_chain_branch(struct callchain_cursor *cursor, list_for_each_entry_safe(list, next_list, &src->val, list) { callchain_cursor_append(cursor, list->ip, - list->ms.map, list->ms.sym); + list->ms.map, list->ms.sym, + false, NULL, 0, 0); list_del(&list->list); free(list); } @@ -767,7 +796,9 @@ int callchain_merge(struct callchain_cursor *cursor, } int callchain_cursor_append(struct callchain_cursor *cursor, - u64 ip, struct map *map, struct symbol *sym) + u64 ip, struct map *map, struct symbol *sym, + bool branch, struct branch_flags *flags, + int nr_loop_iter, int samples) { struct callchain_cursor_node *node = *cursor->last; @@ -782,6 +813,13 @@ int callchain_cursor_append(struct callchain_cursor *cursor, node->ip = ip; node->map = map; node->sym = sym; + node->branch = branch; + node->nr_loop_iter = nr_loop_iter; + node->samples = samples; + + if (flags) + memcpy(&node->branch_flags, flags, + sizeof(struct branch_flags)); cursor->nr++; @@ -939,6 +977,163 @@ int callchain_node__fprintf_value(struct callchain_node *node, return 0; } +static void callchain_counts_value(struct callchain_node *node, + u64 *branch_count, u64 *predicted_count, + u64 *abort_count, u64 *cycles_count) +{ + struct callchain_list *clist; + + list_for_each_entry(clist, &node->val, list) { + if (branch_count) + *branch_count += clist->branch_count; + + if (predicted_count) + *predicted_count += clist->predicted_count; + + if (abort_count) + *abort_count += clist->abort_count; + + if (cycles_count) + *cycles_count += clist->cycles_count; + } +} + +static int callchain_node_branch_counts_cumul(struct callchain_node *node, + u64 *branch_count, + u64 *predicted_count, + u64 *abort_count, + u64 *cycles_count) +{ + struct callchain_node *child; + struct rb_node *n; + + n = rb_first(&node->rb_root_in); + while (n) { + child = rb_entry(n, struct callchain_node, rb_node_in); + n = rb_next(n); + + callchain_node_branch_counts_cumul(child, branch_count, + predicted_count, + abort_count, + cycles_count); + + callchain_counts_value(child, branch_count, + predicted_count, abort_count, + cycles_count); + } + + return 0; +} + +int callchain_branch_counts(struct callchain_root *root, + u64 *branch_count, u64 *predicted_count, + u64 *abort_count, u64 *cycles_count) +{ + if (branch_count) + *branch_count = 0; + + if (predicted_count) + *predicted_count = 0; + + if (abort_count) + *abort_count = 0; + + if (cycles_count) + *cycles_count = 0; + + return callchain_node_branch_counts_cumul(&root->node, + branch_count, + predicted_count, + abort_count, + cycles_count); +} + +static int callchain_counts_printf(FILE *fp, char *bf, int bfsize, + u64 branch_count, u64 predicted_count, + u64 abort_count, u64 cycles_count, + u64 iter_count, u64 samples_count) +{ + double predicted_percent = 0.0; + const char *null_str = ""; + char iter_str[32]; + char *str; + u64 cycles = 0; + + if (branch_count == 0) { + if (fp) + return fprintf(fp, " (calltrace)"); + + return scnprintf(bf, bfsize, " (calltrace)"); + } + + if (iter_count && samples_count) { + scnprintf(iter_str, sizeof(iter_str), + ", iterations:%" PRId64 "", + iter_count / samples_count); + str = iter_str; + } else + str = (char *)null_str; + + predicted_percent = predicted_count * 100.0 / branch_count; + cycles = cycles_count / branch_count; + + if ((predicted_percent >= 100.0) && (abort_count == 0)) { + if (fp) + return fprintf(fp, " (cycles:%" PRId64 "%s)", + cycles, str); + + return scnprintf(bf, bfsize, " (cycles:%" PRId64 "%s)", + cycles, str); + } + + if ((predicted_percent < 100.0) && (abort_count == 0)) { + if (fp) + return fprintf(fp, + " (predicted:%.1f%%, cycles:%" PRId64 "%s)", + predicted_percent, cycles, str); + + return scnprintf(bf, bfsize, + " (predicted:%.1f%%, cycles:%" PRId64 "%s)", + predicted_percent, cycles, str); + } + + if (fp) + return fprintf(fp, + " (predicted:%.1f%%, abort:%" PRId64 ", cycles:%" PRId64 "%s)", + predicted_percent, abort_count, cycles, str); + + return scnprintf(bf, bfsize, + " (predicted:%.1f%%, abort:%" PRId64 ", cycles:%" PRId64 "%s)", + predicted_percent, abort_count, cycles, str); +} + +int callchain_list_counts__printf_value(struct callchain_node *node, + struct callchain_list *clist, + FILE *fp, char *bf, int bfsize) +{ + u64 branch_count, predicted_count; + u64 abort_count, cycles_count; + u64 iter_count = 0, samples_count = 0; + + branch_count = clist->branch_count; + predicted_count = clist->predicted_count; + abort_count = clist->abort_count; + cycles_count = clist->cycles_count; + + if (node) { + struct callchain_list *call; + + list_for_each_entry(call, &node->val, list) { + iter_count += call->iter_count; + samples_count += call->samples_count; + } + } + + return callchain_counts_printf(fp, bf, bfsize, branch_count, + predicted_count, abort_count, + cycles_count, iter_count, samples_count); +} + static void free_callchain_node(struct callchain_node *node) { struct callchain_list *list, *tmp; diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 13e75549c440..d9c70dccf06a 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -11,11 +11,7 @@ #define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace):\n\n" -#ifdef HAVE_DWARF_UNWIND_SUPPORT # define RECORD_MODE_HELP HELP_PAD "record_mode:\tcall graph recording mode (fp|dwarf|lbr)\n" -#else -# define RECORD_MODE_HELP HELP_PAD "record_mode:\tcall graph recording mode (fp|lbr)\n" -#endif #define RECORD_SIZE_HELP \ HELP_PAD "record_size:\tif record_mode is 'dwarf', max size of stack recording (<bytes>)\n" \ @@ -115,6 +111,12 @@ struct callchain_list { bool unfolded; bool has_children; }; + u64 branch_count; + u64 predicted_count; + u64 abort_count; + u64 cycles_count; + u64 iter_count; + u64 samples_count; char *srcline; struct list_head list; }; @@ -129,6 +131,10 @@ struct callchain_cursor_node { u64 ip; struct map *map; struct symbol *sym; + bool branch; + struct branch_flags branch_flags; + int nr_loop_iter; + int samples; struct callchain_cursor_node *next; }; @@ -183,7 +189,9 @@ static inline void callchain_cursor_reset(struct callchain_cursor *cursor) } int callchain_cursor_append(struct callchain_cursor *cursor, u64 ip, - struct map *map, struct symbol *sym); + struct map *map, struct symbol *sym, + bool branch, struct branch_flags *flags, + int nr_loop_iter, int samples); /* Close a cursor writing session. Initialize for the reader */ static inline void callchain_cursor_commit(struct callchain_cursor *cursor) @@ -261,8 +269,16 @@ char *callchain_node__scnprintf_value(struct callchain_node *node, int callchain_node__fprintf_value(struct callchain_node *node, FILE *fp, u64 total); +int callchain_list_counts__printf_value(struct callchain_node *node, + struct callchain_list *clist, + FILE *fp, char *bf, int bfsize); + void free_callchain(struct callchain_root *root); void decay_callchain(struct callchain_root *root); int callchain_node__make_parent_list(struct callchain_node *node); +int callchain_branch_counts(struct callchain_root *root, + u64 *branch_count, u64 *predicted_count, + u64 *abort_count, u64 *cycles_count); + #endif /* __PERF_CALLCHAIN_H */ diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 18dae745034f..3d906dbbef74 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -594,6 +594,19 @@ static int collect_config(const char *var, const char *value, goto out_free; } + /* perf_config_set can contain both user and system config items. + * So we should know where each value is from. + * The classification would be needed when a particular config file + * is overwrited by setting feature i.e. set_config(). + */ + if (strcmp(config_file_name, perf_etc_perfconfig()) == 0) { + section->from_system_config = true; + item->from_system_config = true; + } else { + section->from_system_config = false; + item->from_system_config = false; + } + ret = set_value(item, value); return ret; @@ -602,6 +615,13 @@ out_free: return -1; } +int perf_config_set__collect(struct perf_config_set *set, const char *file_name, + const char *var, const char *value) +{ + config_file_name = file_name; + return collect_config(var, value, set); +} + static int perf_config_set__init(struct perf_config_set *set) { int ret = -1; diff --git a/tools/perf/util/config.h b/tools/perf/util/config.h index 6f813d46045e..1a59a6b43f8b 100644 --- a/tools/perf/util/config.h +++ b/tools/perf/util/config.h @@ -7,12 +7,14 @@ struct perf_config_item { char *name; char *value; + bool from_system_config; struct list_head node; }; struct perf_config_section { char *name; struct list_head items; + bool from_system_config; struct list_head node; }; @@ -33,6 +35,8 @@ const char *perf_etc_perfconfig(void); struct perf_config_set *perf_config_set__new(void); void perf_config_set__delete(struct perf_config_set *set); +int perf_config_set__collect(struct perf_config_set *set, const char *file_name, + const char *var, const char *value); void perf_config__init(void); void perf_config__exit(void); void perf_config__refresh(void); diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 8d363d5e65a2..c735c53a26f8 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -177,6 +177,8 @@ enum { PERF_IP_FLAG_TRACE_BEGIN |\ PERF_IP_FLAG_TRACE_END) +#define MAX_INSN 16 + struct perf_sample { u64 ip; u32 pid, tid; @@ -193,6 +195,7 @@ struct perf_sample { u32 flags; u16 insn_len; u8 cpumode; + char insn[MAX_INSN]; void *raw_data; struct ip_callchain *callchain; struct branch_stack *branch_stack; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 8bc271141d9d..e58a2fbf3b16 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -28,6 +28,7 @@ #include "debug.h" #include "trace-event.h" #include "stat.h" +#include "util/parse-branch-options.h" static struct { bool sample_id_all; @@ -708,6 +709,14 @@ static void apply_config_terms(struct perf_evsel *evsel, case PERF_EVSEL__CONFIG_TERM_CALLGRAPH: callgraph_buf = term->val.callgraph; break; + case PERF_EVSEL__CONFIG_TERM_BRANCH: + if (term->val.branch && strcmp(term->val.branch, "no")) { + perf_evsel__set_sample_bit(evsel, BRANCH_STACK); + parse_branch_str(term->val.branch, + &attr->branch_sample_type); + } else + perf_evsel__reset_sample_bit(evsel, BRANCH_STACK); + break; case PERF_EVSEL__CONFIG_TERM_STACK_USER: dump_size = term->val.stack_user; break; diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index b1503b0ecdff..8cd7cd227483 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -47,6 +47,7 @@ enum { PERF_EVSEL__CONFIG_TERM_MAX_STACK, PERF_EVSEL__CONFIG_TERM_OVERWRITE, PERF_EVSEL__CONFIG_TERM_DRV_CFG, + PERF_EVSEL__CONFIG_TERM_BRANCH, PERF_EVSEL__CONFIG_TERM_MAX, }; @@ -63,6 +64,7 @@ struct perf_evsel_config_term { int max_stack; bool inherit; bool overwrite; + char *branch; } val; }; diff --git a/tools/perf/util/genelf.c b/tools/perf/util/genelf.c index c1ef805c6a8f..c540d47583e7 100644 --- a/tools/perf/util/genelf.c +++ b/tools/perf/util/genelf.c @@ -19,12 +19,18 @@ #include <limits.h> #include <fcntl.h> #include <err.h> +#ifdef HAVE_DWARF_SUPPORT #include <dwarf.h> +#endif #include "perf.h" #include "genelf.h" #include "../util/jitdump.h" +#ifndef NT_GNU_BUILD_ID +#define NT_GNU_BUILD_ID 3 +#endif + #define JVMTI #define BUILD_ID_URANDOM /* different uuid for each run */ @@ -67,6 +73,8 @@ static char shd_string_table[] = { '.', 'd', 'e', 'b', 'u', 'g', '_', 'l', 'i', 'n', 'e', 0, /* 52 */ '.', 'd', 'e', 'b', 'u', 'g', '_', 'i', 'n', 'f', 'o', 0, /* 64 */ '.', 'd', 'e', 'b', 'u', 'g', '_', 'a', 'b', 'b', 'r', 'e', 'v', 0, /* 76 */ + '.', 'e', 'h', '_', 'f', 'r', 'a', 'm', 'e', '_', 'h', 'd', 'r', 0, /* 90 */ + '.', 'e', 'h', '_', 'f', 'r', 'a', 'm', 'e', 0, /* 104 */ }; static struct buildid_note { @@ -147,6 +155,86 @@ gen_build_id(struct buildid_note *note, unsigned long load_addr, const void *cod } #endif +static int +jit_add_eh_frame_info(Elf *e, void* unwinding, uint64_t unwinding_header_size, + uint64_t unwinding_size, uint64_t base_offset) +{ + Elf_Data *d; + Elf_Scn *scn; + Elf_Shdr *shdr; + uint64_t unwinding_table_size = unwinding_size - unwinding_header_size; + + /* + * setup eh_frame section + */ + scn = elf_newscn(e); + if (!scn) { + warnx("cannot create section"); + return -1; + } + + d = elf_newdata(scn); + if (!d) { + warnx("cannot get new data"); + return -1; + } + + d->d_align = 8; + d->d_off = 0LL; + d->d_buf = unwinding; + d->d_type = ELF_T_BYTE; + d->d_size = unwinding_table_size; + d->d_version = EV_CURRENT; + + shdr = elf_getshdr(scn); + if (!shdr) { + warnx("cannot get section header"); + return -1; + } + + shdr->sh_name = 104; + shdr->sh_type = SHT_PROGBITS; + shdr->sh_addr = base_offset; + shdr->sh_flags = SHF_ALLOC; + shdr->sh_entsize = 0; + + /* + * setup eh_frame_hdr section + */ + scn = elf_newscn(e); + if (!scn) { + warnx("cannot create section"); + return -1; + } + + d = elf_newdata(scn); + if (!d) { + warnx("cannot get new data"); + return -1; + } + + d->d_align = 4; + d->d_off = 0LL; + d->d_buf = unwinding + unwinding_table_size; + d->d_type = ELF_T_BYTE; + d->d_size = unwinding_header_size; + d->d_version = EV_CURRENT; + + shdr = elf_getshdr(scn); + if (!shdr) { + warnx("cannot get section header"); + return -1; + } + + shdr->sh_name = 90; + shdr->sh_type = SHT_PROGBITS; + shdr->sh_addr = base_offset + unwinding_table_size; + shdr->sh_flags = SHF_ALLOC; + shdr->sh_entsize = 0; + + return 0; +} + /* * fd: file descriptor open for writing for the output file * load_addr: code load address (could be zero, just used for buildid) @@ -157,13 +245,15 @@ gen_build_id(struct buildid_note *note, unsigned long load_addr, const void *cod int jit_write_elf(int fd, uint64_t load_addr, const char *sym, const void *code, int csize, - void *debug, int nr_debug_entries) + void *debug __maybe_unused, int nr_debug_entries __maybe_unused, + void *unwinding, uint64_t unwinding_header_size, uint64_t unwinding_size) { Elf *e; Elf_Data *d; Elf_Scn *scn; Elf_Ehdr *ehdr; Elf_Shdr *shdr; + uint64_t eh_frame_base_offset; char *strsym = NULL; int symlen; int retval = -1; @@ -194,7 +284,7 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, ehdr->e_type = ET_DYN; ehdr->e_entry = GEN_ELF_TEXT_OFFSET; ehdr->e_version = EV_CURRENT; - ehdr->e_shstrndx= 2; /* shdr index for section name */ + ehdr->e_shstrndx= unwinding ? 4 : 2; /* shdr index for section name */ /* * setup text section @@ -231,6 +321,18 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, shdr->sh_entsize = 0; /* + * Setup .eh_frame_hdr and .eh_frame + */ + if (unwinding) { + eh_frame_base_offset = ALIGN_8(GEN_ELF_TEXT_OFFSET + csize); + retval = jit_add_eh_frame_info(e, unwinding, + unwinding_header_size, unwinding_size, + eh_frame_base_offset); + if (retval) + goto error; + } + + /* * setup section headers string table */ scn = elf_newscn(e); @@ -298,7 +400,7 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, shdr->sh_type = SHT_SYMTAB; shdr->sh_flags = 0; shdr->sh_entsize = sizeof(Elf_Sym); - shdr->sh_link = 4; /* index of .strtab section */ + shdr->sh_link = unwinding ? 6 : 4; /* index of .strtab section */ /* * setup symbols string table @@ -386,11 +488,14 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, shdr->sh_size = sizeof(bnote); shdr->sh_entsize = 0; +#ifdef HAVE_DWARF_SUPPORT if (debug && nr_debug_entries) { retval = jit_add_debug_info(e, load_addr, debug, nr_debug_entries); if (retval) goto error; - } else { + } else +#endif + { if (elf_update(e, ELF_C_WRITE) < 0) { warnx("elf_update 4 failed"); goto error; diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h index 2fbeb59c4bdd..2424bd9862a3 100644 --- a/tools/perf/util/genelf.h +++ b/tools/perf/util/genelf.h @@ -3,9 +3,12 @@ /* genelf.c */ int jit_write_elf(int fd, uint64_t code_addr, const char *sym, - const void *code, int csize, void *debug, int nr_debug_entries); + const void *code, int csize, void *debug, int nr_debug_entries, + void *unwinding, uint64_t unwinding_header_size, uint64_t unwinding_size); +#ifdef HAVE_DWARF_SUPPORT /* genelf_debug.c */ int jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries); +#endif #if defined(__arm__) #define GEN_ELF_ARCH EM_ARM diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 2f3eded54b0c..d89c9c7ef4e5 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2250,11 +2250,28 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) struct header_print_data hd; struct perf_header *header = &session->header; int fd = perf_data_file__fd(session->file); + struct stat st; + int ret, bit; + hd.fp = fp; hd.full = full; + ret = fstat(fd, &st); + if (ret == -1) + return -1; + + fprintf(fp, "# captured on: %s", ctime(&st.st_ctime)); + perf_header__process_sections(header, fd, &hd, perf_file_section__fprintf_info); + + fprintf(fp, "# missing features: "); + for_each_clear_bit(bit, header->adds_features, HEADER_LAST_FEATURE) { + if (bit) + fprintf(fp, "%s ", feat_ops[bit].name); + } + + fprintf(fp, "\n"); return 0; } @@ -2273,7 +2290,7 @@ static int do_write_feat(int fd, struct perf_header *h, int type, err = feat_ops[type].write(fd, h, evlist); if (err < 0) { - pr_debug("failed to write feature %d\n", type); + pr_debug("failed to write feature %s\n", feat_ops[type].name); /* undo anything written */ lseek(fd, (*p)->offset, SEEK_SET); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index a69f027368ef..6770a9645609 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1195,6 +1195,7 @@ static void hist_entry__check_and_remove_filter(struct hist_entry *he, case HIST_FILTER__GUEST: case HIST_FILTER__HOST: case HIST_FILTER__SOCKET: + case HIST_FILTER__C2C: default: return; } diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 9928fed8bc59..d4b6514eeef5 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -22,6 +22,7 @@ enum hist_filter { HIST_FILTER__GUEST, HIST_FILTER__HOST, HIST_FILTER__SOCKET, + HIST_FILTER__C2C, }; enum hist_column { diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c index f545ec1e758a..6c2eb5da4afc 100644 --- a/tools/perf/util/intel-bts.c +++ b/tools/perf/util/intel-bts.c @@ -295,6 +295,7 @@ static int intel_bts_synth_branch_sample(struct intel_bts_queue *btsq, sample.cpu = btsq->cpu; sample.flags = btsq->sample_flags; sample.insn_len = btsq->intel_pt_insn.length; + memcpy(sample.insn, btsq->intel_pt_insn.buf, INTEL_PT_INSN_BUF_SZ); if (bts->synth_opts.inject) { event.sample.header.size = bts->branches_event_size; @@ -319,15 +320,12 @@ static int intel_bts_get_next_insn(struct intel_bts_queue *btsq, u64 ip) struct machine *machine = btsq->bts->machine; struct thread *thread; struct addr_location al; - unsigned char buf[1024]; - size_t bufsz; + unsigned char buf[INTEL_PT_INSN_BUF_SZ]; ssize_t len; int x86_64; uint8_t cpumode; int err = -1; - bufsz = intel_pt_insn_max_size(); - if (machine__kernel_ip(machine, ip)) cpumode = PERF_RECORD_MISC_KERNEL; else @@ -341,7 +339,8 @@ static int intel_bts_get_next_insn(struct intel_bts_queue *btsq, u64 ip) if (!al.map || !al.map->dso) goto out_put; - len = dso__data_read_addr(al.map->dso, al.map, machine, ip, buf, bufsz); + len = dso__data_read_addr(al.map->dso, al.map, machine, ip, buf, + INTEL_PT_INSN_BUF_SZ); if (len <= 0) goto out_put; diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c index 16c06d3ae577..e4e7dc781d21 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c @@ -980,6 +980,8 @@ out: out_no_progress: decoder->state.insn_op = intel_pt_insn->op; decoder->state.insn_len = intel_pt_insn->length; + memcpy(decoder->state.insn, intel_pt_insn->buf, + INTEL_PT_INSN_BUF_SZ); if (decoder->tx_flags & INTEL_PT_IN_TX) decoder->state.flags |= INTEL_PT_IN_TX; diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h index 89399985fa4d..e90619a43c0c 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h @@ -66,6 +66,7 @@ struct intel_pt_state { uint32_t flags; enum intel_pt_insn_op insn_op; int insn_len; + char insn[INTEL_PT_INSN_BUF_SZ]; }; struct intel_pt_insn; diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c index d23138c06665..7913363bde5c 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c @@ -27,6 +27,10 @@ #include "intel-pt-insn-decoder.h" +#if INTEL_PT_INSN_BUF_SZ < MAX_INSN_SIZE || INTEL_PT_INSN_BUF_SZ > MAX_INSN +#error Instruction buffer size too small +#endif + /* Based on branch_type() from perf_event_intel_lbr.c */ static void intel_pt_insn_decoder(struct insn *insn, struct intel_pt_insn *intel_pt_insn) @@ -166,10 +170,10 @@ int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64, if (!insn_complete(&insn) || insn.length > len) return -1; intel_pt_insn_decoder(&insn, intel_pt_insn); - if (insn.length < INTEL_PT_INSN_DBG_BUF_SZ) + if (insn.length < INTEL_PT_INSN_BUF_SZ) memcpy(intel_pt_insn->buf, buf, insn.length); else - memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_DBG_BUF_SZ); + memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_BUF_SZ); return 0; } @@ -211,11 +215,6 @@ int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf, return 0; } -size_t intel_pt_insn_max_size(void) -{ - return MAX_INSN_SIZE; -} - int intel_pt_insn_type(enum intel_pt_insn_op op) { switch (op) { diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h index b0adbf37323e..37ec5627ae9b 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h +++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h @@ -20,7 +20,7 @@ #include <stdint.h> #define INTEL_PT_INSN_DESC_MAX 32 -#define INTEL_PT_INSN_DBG_BUF_SZ 16 +#define INTEL_PT_INSN_BUF_SZ 16 enum intel_pt_insn_op { INTEL_PT_OP_OTHER, @@ -47,7 +47,7 @@ struct intel_pt_insn { enum intel_pt_insn_branch branch; int length; int32_t rel; - unsigned char buf[INTEL_PT_INSN_DBG_BUF_SZ]; + unsigned char buf[INTEL_PT_INSN_BUF_SZ]; }; int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64, @@ -58,8 +58,6 @@ const char *intel_pt_insn_name(enum intel_pt_insn_op op); int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf, size_t buf_len); -size_t intel_pt_insn_max_size(void); - int intel_pt_insn_type(enum intel_pt_insn_op op); #endif diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-log.c b/tools/perf/util/intel-pt-decoder/intel-pt-log.c index 319bef33a64b..e02bc7b166a0 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-log.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-log.c @@ -119,8 +119,8 @@ void __intel_pt_log_insn(struct intel_pt_insn *intel_pt_insn, uint64_t ip) if (intel_pt_log_open()) return; - if (len > INTEL_PT_INSN_DBG_BUF_SZ) - len = INTEL_PT_INSN_DBG_BUF_SZ; + if (len > INTEL_PT_INSN_BUF_SZ) + len = INTEL_PT_INSN_BUF_SZ; intel_pt_print_data(intel_pt_insn->buf, len, ip, 8); if (intel_pt_insn_desc(intel_pt_insn, desc, INTEL_PT_INSN_DESC_MAX) > 0) fprintf(f, "%s\n", desc); diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index dc041d4368c8..85d5eeb66c75 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -143,6 +143,7 @@ struct intel_pt_queue { u32 flags; u16 insn_len; u64 last_insn_cnt; + char insn[INTEL_PT_INSN_BUF_SZ]; }; static void intel_pt_dump(struct intel_pt *pt __maybe_unused, @@ -315,6 +316,7 @@ struct intel_pt_cache_entry { enum intel_pt_insn_branch branch; int length; int32_t rel; + char insn[INTEL_PT_INSN_BUF_SZ]; }; static int intel_pt_config_div(const char *var, const char *value, void *data) @@ -400,6 +402,7 @@ static int intel_pt_cache_add(struct dso *dso, struct machine *machine, e->branch = intel_pt_insn->branch; e->length = intel_pt_insn->length; e->rel = intel_pt_insn->rel; + memcpy(e->insn, intel_pt_insn->buf, INTEL_PT_INSN_BUF_SZ); err = auxtrace_cache__add(c, offset, &e->entry); if (err) @@ -428,8 +431,7 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, struct machine *machine = ptq->pt->machine; struct thread *thread; struct addr_location al; - unsigned char buf[1024]; - size_t bufsz; + unsigned char buf[INTEL_PT_INSN_BUF_SZ]; ssize_t len; int x86_64; u8 cpumode; @@ -437,11 +439,11 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, u64 insn_cnt = 0; bool one_map = true; + intel_pt_insn->length = 0; + if (to_ip && *ip == to_ip) goto out_no_cache; - bufsz = intel_pt_insn_max_size(); - if (*ip >= ptq->pt->kernel_start) cpumode = PERF_RECORD_MISC_KERNEL; else @@ -478,6 +480,8 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, intel_pt_insn->branch = e->branch; intel_pt_insn->length = e->length; intel_pt_insn->rel = e->rel; + memcpy(intel_pt_insn->buf, e->insn, + INTEL_PT_INSN_BUF_SZ); intel_pt_log_insn_no_data(intel_pt_insn, *ip); return 0; } @@ -493,7 +497,8 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, while (1) { len = dso__data_read_offset(al.map->dso, machine, - offset, buf, bufsz); + offset, buf, + INTEL_PT_INSN_BUF_SZ); if (len <= 0) return -EINVAL; @@ -900,6 +905,7 @@ static void intel_pt_sample_flags(struct intel_pt_queue *ptq) if (ptq->state->flags & INTEL_PT_IN_TX) ptq->flags |= PERF_IP_FLAG_IN_TX; ptq->insn_len = ptq->state->insn_len; + memcpy(ptq->insn, ptq->state->insn, INTEL_PT_INSN_BUF_SZ); } } @@ -1080,6 +1086,7 @@ static int intel_pt_synth_branch_sample(struct intel_pt_queue *ptq) sample.cpu = ptq->cpu; sample.flags = ptq->flags; sample.insn_len = ptq->insn_len; + memcpy(sample.insn, ptq->insn, INTEL_PT_INSN_BUF_SZ); /* * perf report cannot handle events without a branch stack when using @@ -1141,6 +1148,7 @@ static int intel_pt_synth_instruction_sample(struct intel_pt_queue *ptq) sample.cpu = ptq->cpu; sample.flags = ptq->flags; sample.insn_len = ptq->insn_len; + memcpy(sample.insn, ptq->insn, INTEL_PT_INSN_BUF_SZ); ptq->last_insn_cnt = ptq->state->tot_insn_cnt; @@ -1203,6 +1211,7 @@ static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq) sample.cpu = ptq->cpu; sample.flags = ptq->flags; sample.insn_len = ptq->insn_len; + memcpy(sample.insn, ptq->insn, INTEL_PT_INSN_BUF_SZ); if (pt->synth_opts.callchain) { thread_stack__sample(ptq->thread, ptq->chain, diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index 95f0884aae02..c9a941ef0f6d 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c @@ -37,6 +37,10 @@ struct jit_buf_desc { bool needs_bswap; /* handles cross-endianess */ bool use_arch_timestamp; void *debug_data; + void *unwinding_data; + uint64_t unwinding_size; + uint64_t unwinding_mapped_size; + uint64_t eh_frame_hdr_size; size_t nr_debug_entries; uint32_t code_load_count; u64 bytes_written; @@ -68,7 +72,10 @@ jit_emit_elf(char *filename, const void *code, int csize, void *debug, - int nr_debug_entries) + int nr_debug_entries, + void *unwinding, + uint32_t unwinding_header_size, + uint32_t unwinding_size) { int ret, fd; @@ -81,7 +88,8 @@ jit_emit_elf(char *filename, return -1; } - ret = jit_write_elf(fd, code_addr, sym, (const void *)code, csize, debug, nr_debug_entries); + ret = jit_write_elf(fd, code_addr, sym, (const void *)code, csize, debug, nr_debug_entries, + unwinding, unwinding_header_size, unwinding_size); close(fd); @@ -172,6 +180,12 @@ jit_open(struct jit_buf_desc *jd, const char *name) header.elf_mach, jd->use_arch_timestamp); + if (header.version > JITHEADER_VERSION) { + pr_err("wrong jitdump version %u, expected " STR(JITHEADER_VERSION), + header.version); + goto error; + } + if (header.flags & JITDUMP_FLAGS_RESERVED) { pr_err("jitdump file contains invalid or unsupported flags 0x%llx\n", (unsigned long long)header.flags & JITDUMP_FLAGS_RESERVED); @@ -263,8 +277,7 @@ jit_get_next_entry(struct jit_buf_desc *jd) return NULL; if (id >= JIT_CODE_MAX) { - pr_warning("next_entry: unknown prefix %d, skipping\n", id); - return NULL; + pr_warning("next_entry: unknown record type %d, skipping\n", id); } if (bs > jd->bufsize) { void *n; @@ -296,6 +309,13 @@ jit_get_next_entry(struct jit_buf_desc *jd) } } break; + case JIT_CODE_UNWINDING_INFO: + if (jd->needs_bswap) { + jr->unwinding.unwinding_size = bswap_64(jr->unwinding.unwinding_size); + jr->unwinding.eh_frame_hdr_size = bswap_64(jr->unwinding.eh_frame_hdr_size); + jr->unwinding.mapped_size = bswap_64(jr->unwinding.mapped_size); + } + break; case JIT_CODE_CLOSE: break; case JIT_CODE_LOAD: @@ -322,7 +342,8 @@ jit_get_next_entry(struct jit_buf_desc *jd) break; case JIT_CODE_MAX: default: - return NULL; + /* skip unknown record (we have read them) */ + break; } return jr; } @@ -370,7 +391,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) u16 idr_size; const char *sym; uint32_t count; - int ret, csize; + int ret, csize, usize; pid_t pid, tid; struct { u32 pid, tid; @@ -380,6 +401,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) pid = jr->load.pid; tid = jr->load.tid; csize = jr->load.code_size; + usize = jd->unwinding_mapped_size; addr = jr->load.code_addr; sym = (void *)((unsigned long)jr + sizeof(jr->load)); code = (unsigned long)jr + jr->load.p.total_size - csize; @@ -400,7 +422,8 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) size = PERF_ALIGN(size, sizeof(u64)); uaddr = (uintptr_t)code; - ret = jit_emit_elf(filename, sym, addr, (const void *)uaddr, csize, jd->debug_data, jd->nr_debug_entries); + ret = jit_emit_elf(filename, sym, addr, (const void *)uaddr, csize, jd->debug_data, jd->nr_debug_entries, + jd->unwinding_data, jd->eh_frame_hdr_size, jd->unwinding_size); if (jd->debug_data && jd->nr_debug_entries) { free(jd->debug_data); @@ -408,6 +431,14 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) jd->nr_debug_entries = 0; } + if (jd->unwinding_data && jd->eh_frame_hdr_size) { + free(jd->unwinding_data); + jd->unwinding_data = NULL; + jd->eh_frame_hdr_size = 0; + jd->unwinding_mapped_size = 0; + jd->unwinding_size = 0; + } + if (ret) { free(event); return -1; @@ -422,7 +453,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; event->mmap2.start = addr; - event->mmap2.len = csize; + event->mmap2.len = usize ? ALIGN_8(csize) + usize : csize; event->mmap2.pid = pid; event->mmap2.tid = tid; event->mmap2.ino = st.st_ino; @@ -473,6 +504,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) char *filename; size_t size; struct stat st; + int usize; u16 idr_size; int ret; pid_t pid, tid; @@ -483,6 +515,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) pid = jr->move.pid; tid = jr->move.tid; + usize = jd->unwinding_mapped_size; idr_size = jd->machine->id_hdr_size; /* @@ -511,7 +544,8 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) (sizeof(event->mmap2.filename) - size) + idr_size); event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; event->mmap2.start = jr->move.new_code_addr; - event->mmap2.len = jr->move.code_size; + event->mmap2.len = usize ? ALIGN_8(jr->move.code_size) + usize + : jr->move.code_size; event->mmap2.pid = pid; event->mmap2.tid = tid; event->mmap2.ino = st.st_ino; @@ -578,10 +612,35 @@ static int jit_repipe_debug_info(struct jit_buf_desc *jd, union jr_entry *jr) } static int +jit_repipe_unwinding_info(struct jit_buf_desc *jd, union jr_entry *jr) +{ + void *unwinding_data; + uint32_t unwinding_data_size; + + if (!(jd && jr)) + return -1; + + unwinding_data_size = jr->prefix.total_size - sizeof(jr->unwinding); + unwinding_data = malloc(unwinding_data_size); + if (!unwinding_data) + return -1; + + memcpy(unwinding_data, &jr->unwinding.unwinding_data, + unwinding_data_size); + + jd->eh_frame_hdr_size = jr->unwinding.eh_frame_hdr_size; + jd->unwinding_size = jr->unwinding.unwinding_size; + jd->unwinding_mapped_size = jr->unwinding.mapped_size; + jd->unwinding_data = unwinding_data; + + return 0; +} + +static int jit_process_dump(struct jit_buf_desc *jd) { union jr_entry *jr; - int ret; + int ret = 0; while ((jr = jit_get_next_entry(jd))) { switch(jr->prefix.id) { @@ -594,6 +653,9 @@ jit_process_dump(struct jit_buf_desc *jd) case JIT_CODE_DEBUG_INFO: ret = jit_repipe_debug_info(jd, jr); break; + case JIT_CODE_UNWINDING_INFO: + ret = jit_repipe_unwinding_info(jd, jr); + break; default: ret = 0; continue; diff --git a/tools/perf/util/jitdump.h b/tools/perf/util/jitdump.h index bcacd20d0c1c..c6b9b67f43bf 100644 --- a/tools/perf/util/jitdump.h +++ b/tools/perf/util/jitdump.h @@ -19,6 +19,7 @@ #define JITHEADER_MAGIC_SW 0x4454694A #define PADDING_8ALIGNED(x) ((((x) + 7) & 7) ^ 7) +#define ALIGN_8(x) (((x) + 7) & (~7)) #define JITHEADER_VERSION 1 @@ -48,6 +49,7 @@ enum jit_record_type { JIT_CODE_MOVE = 1, JIT_CODE_DEBUG_INFO = 2, JIT_CODE_CLOSE = 3, + JIT_CODE_UNWINDING_INFO = 4, JIT_CODE_MAX, }; @@ -101,12 +103,22 @@ struct jr_code_debug_info { struct debug_entry entries[0]; }; +struct jr_code_unwinding_info { + struct jr_prefix p; + + uint64_t unwinding_size; + uint64_t eh_frame_hdr_size; + uint64_t mapped_size; + const char unwinding_data[0]; +}; + union jr_entry { struct jr_code_debug_info info; struct jr_code_close close; struct jr_code_load load; struct jr_code_move move; struct jr_prefix prefix; + struct jr_code_unwinding_info unwinding; }; static inline struct debug_entry * diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index bf7216b8731d..27b6f303720a 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c @@ -339,7 +339,7 @@ dump_obj(const char *path, void *obj_buf, size_t size) char *p; if (!obj_path) { - pr_warning("WARNING: No enough memory, skip object dumping\n"); + pr_warning("WARNING: Not enough memory, skip object dumping\n"); return; } diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index df85b9efd80f..9b33bef54581 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1616,7 +1616,11 @@ static int add_callchain_ip(struct thread *thread, struct symbol **parent, struct addr_location *root_al, u8 *cpumode, - u64 ip) + u64 ip, + bool branch, + struct branch_flags *flags, + int nr_loop_iter, + int samples) { struct addr_location al; @@ -1668,7 +1672,8 @@ static int add_callchain_ip(struct thread *thread, if (symbol_conf.hide_unresolved && al.sym == NULL) return 0; - return callchain_cursor_append(cursor, al.addr, al.map, al.sym); + return callchain_cursor_append(cursor, al.addr, al.map, al.sym, + branch, flags, nr_loop_iter, samples); } struct branch_info *sample__resolve_bstack(struct perf_sample *sample, @@ -1757,7 +1762,9 @@ static int resolve_lbr_callchain_sample(struct thread *thread, /* LBR only affects the user callchain */ if (i != chain_nr) { struct branch_stack *lbr_stack = sample->branch_stack; - int lbr_nr = lbr_stack->nr, j; + int lbr_nr = lbr_stack->nr, j, k; + bool branch; + struct branch_flags *flags; /* * LBR callstack can only get user call chain. * The mix_chain_nr is kernel call chain @@ -1772,23 +1779,41 @@ static int resolve_lbr_callchain_sample(struct thread *thread, for (j = 0; j < mix_chain_nr; j++) { int err; + branch = false; + flags = NULL; + if (callchain_param.order == ORDER_CALLEE) { if (j < i + 1) ip = chain->ips[j]; - else if (j > i + 1) - ip = lbr_stack->entries[j - i - 2].from; - else + else if (j > i + 1) { + k = j - i - 2; + ip = lbr_stack->entries[k].from; + branch = true; + flags = &lbr_stack->entries[k].flags; + } else { ip = lbr_stack->entries[0].to; + branch = true; + flags = &lbr_stack->entries[0].flags; + } } else { - if (j < lbr_nr) - ip = lbr_stack->entries[lbr_nr - j - 1].from; + if (j < lbr_nr) { + k = lbr_nr - j - 1; + ip = lbr_stack->entries[k].from; + branch = true; + flags = &lbr_stack->entries[k].flags; + } else if (j > lbr_nr) ip = chain->ips[i + 1 - (j - lbr_nr)]; - else + else { ip = lbr_stack->entries[0].to; + branch = true; + flags = &lbr_stack->entries[0].flags; + } } - err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip); + err = add_callchain_ip(thread, cursor, parent, + root_al, &cpumode, ip, + branch, flags, 0, 0); if (err) return (err < 0) ? err : 0; } @@ -1813,6 +1838,7 @@ static int thread__resolve_callchain_sample(struct thread *thread, int i, j, err, nr_entries; int skip_idx = -1; int first_call = 0; + int nr_loop_iter; if (perf_evsel__has_branch_callstack(evsel)) { err = resolve_lbr_callchain_sample(thread, cursor, sample, parent, @@ -1868,14 +1894,37 @@ static int thread__resolve_callchain_sample(struct thread *thread, be[i] = branch->entries[branch->nr - i - 1]; } + nr_loop_iter = nr; nr = remove_loops(be, nr); + /* + * Get the number of iterations. + * It's only approximation, but good enough in practice. + */ + if (nr_loop_iter > nr) + nr_loop_iter = nr_loop_iter - nr + 1; + else + nr_loop_iter = 0; + for (i = 0; i < nr; i++) { - err = add_callchain_ip(thread, cursor, parent, root_al, - NULL, be[i].to); + if (i == nr - 1) + err = add_callchain_ip(thread, cursor, parent, + root_al, + NULL, be[i].to, + true, &be[i].flags, + nr_loop_iter, 1); + else + err = add_callchain_ip(thread, cursor, parent, + root_al, + NULL, be[i].to, + true, &be[i].flags, + 0, 0); + if (!err) err = add_callchain_ip(thread, cursor, parent, root_al, - NULL, be[i].from); + NULL, be[i].from, + true, &be[i].flags, + 0, 0); if (err == -EINVAL) break; if (err) @@ -1903,7 +1952,9 @@ check_calls: if (ip < PERF_CONTEXT_MAX) ++nr_entries; - err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip); + err = add_callchain_ip(thread, cursor, parent, + root_al, &cpumode, ip, + false, NULL, 0, 0); if (err) return (err < 0) ? err : 0; @@ -1919,7 +1970,8 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) if (symbol_conf.hide_unresolved && entry->sym == NULL) return 0; return callchain_cursor_append(cursor, entry->ip, - entry->map, entry->sym); + entry->map, entry->sym, + false, NULL, 0, 0); } static int thread__resolve_callchain_unwind(struct thread *thread, diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index c662fef95d14..4f9a71c63026 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -682,9 +682,16 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp continue; if (verbose >= 2) { - fputs("overlapping maps:\n", fp); - map__fprintf(map, fp); - map__fprintf(pos, fp); + + if (use_browser) { + pr_warning("overlapping maps in %s " + "(disable tui for more info)\n", + map->dso->name); + } else { + fputs("overlapping maps:\n", fp); + map__fprintf(map, fp); + map__fprintf(pos, fp); + } } rb_erase_init(&pos->rb_node, root); @@ -702,7 +709,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp before->end = map->start; __map_groups__insert(pos->groups, before); - if (verbose >= 2) + if (verbose >= 2 && !use_browser) map__fprintf(before, fp); map__put(before); } @@ -717,7 +724,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp after->start = map->end; __map_groups__insert(pos->groups, after); - if (verbose >= 2) + if (verbose >= 2 && !use_browser) map__fprintf(after, fp); map__put(after); } diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c index bbc368e7d1e4..e50773286ef6 100644 --- a/tools/perf/util/mem-events.c +++ b/tools/perf/util/mem-events.c @@ -9,6 +9,7 @@ #include "mem-events.h" #include "debug.h" #include "symbol.h" +#include "sort.h" unsigned int perf_mem_events__loads_ldlat = 30; @@ -268,3 +269,130 @@ int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_in return i; } + +int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi) +{ + union perf_mem_data_src *data_src = &mi->data_src; + u64 daddr = mi->daddr.addr; + u64 op = data_src->mem_op; + u64 lvl = data_src->mem_lvl; + u64 snoop = data_src->mem_snoop; + u64 lock = data_src->mem_lock; + int err = 0; + +#define P(a, b) PERF_MEM_##a##_##b + + stats->nr_entries++; + + if (lock & P(LOCK, LOCKED)) stats->locks++; + + if (op & P(OP, LOAD)) { + /* load */ + stats->load++; + + if (!daddr) { + stats->ld_noadrs++; + return -1; + } + + if (lvl & P(LVL, HIT)) { + if (lvl & P(LVL, UNC)) stats->ld_uncache++; + if (lvl & P(LVL, IO)) stats->ld_io++; + if (lvl & P(LVL, LFB)) stats->ld_fbhit++; + if (lvl & P(LVL, L1 )) stats->ld_l1hit++; + if (lvl & P(LVL, L2 )) stats->ld_l2hit++; + if (lvl & P(LVL, L3 )) { + if (snoop & P(SNOOP, HITM)) + stats->lcl_hitm++; + else + stats->ld_llchit++; + } + + if (lvl & P(LVL, LOC_RAM)) { + stats->lcl_dram++; + if (snoop & P(SNOOP, HIT)) + stats->ld_shared++; + else + stats->ld_excl++; + } + + if ((lvl & P(LVL, REM_RAM1)) || + (lvl & P(LVL, REM_RAM2))) { + stats->rmt_dram++; + if (snoop & P(SNOOP, HIT)) + stats->ld_shared++; + else + stats->ld_excl++; + } + } + + if ((lvl & P(LVL, REM_CCE1)) || + (lvl & P(LVL, REM_CCE2))) { + if (snoop & P(SNOOP, HIT)) + stats->rmt_hit++; + else if (snoop & P(SNOOP, HITM)) + stats->rmt_hitm++; + } + + if ((lvl & P(LVL, MISS))) + stats->ld_miss++; + + } else if (op & P(OP, STORE)) { + /* store */ + stats->store++; + + if (!daddr) { + stats->st_noadrs++; + return -1; + } + + if (lvl & P(LVL, HIT)) { + if (lvl & P(LVL, UNC)) stats->st_uncache++; + if (lvl & P(LVL, L1 )) stats->st_l1hit++; + } + if (lvl & P(LVL, MISS)) + if (lvl & P(LVL, L1)) stats->st_l1miss++; + } else { + /* unparsable data_src? */ + stats->noparse++; + return -1; + } + + if (!mi->daddr.map || !mi->iaddr.map) { + stats->nomap++; + return -1; + } + +#undef P + return err; +} + +void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add) +{ + stats->nr_entries += add->nr_entries; + + stats->locks += add->locks; + stats->store += add->store; + stats->st_uncache += add->st_uncache; + stats->st_noadrs += add->st_noadrs; + stats->st_l1hit += add->st_l1hit; + stats->st_l1miss += add->st_l1miss; + stats->load += add->load; + stats->ld_excl += add->ld_excl; + stats->ld_shared += add->ld_shared; + stats->ld_uncache += add->ld_uncache; + stats->ld_io += add->ld_io; + stats->ld_miss += add->ld_miss; + stats->ld_noadrs += add->ld_noadrs; + stats->ld_fbhit += add->ld_fbhit; + stats->ld_l1hit += add->ld_l1hit; + stats->ld_l2hit += add->ld_l2hit; + stats->ld_llchit += add->ld_llchit; + stats->lcl_hitm += add->lcl_hitm; + stats->rmt_hitm += add->rmt_hitm; + stats->rmt_hit += add->rmt_hit; + stats->lcl_dram += add->lcl_dram; + stats->rmt_dram += add->rmt_dram; + stats->nomap += add->nomap; + stats->noparse += add->noparse; +} diff --git a/tools/perf/util/mem-events.h b/tools/perf/util/mem-events.h index 7f69bf9d789d..faf80403b519 100644 --- a/tools/perf/util/mem-events.h +++ b/tools/perf/util/mem-events.h @@ -2,6 +2,10 @@ #define __PERF_MEM_EVENTS_H #include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <linux/types.h> +#include "stat.h" struct perf_mem_event { bool record; @@ -33,4 +37,37 @@ int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info); int perf_script__meminfo_scnprintf(char *bf, size_t size, struct mem_info *mem_info); +struct c2c_stats { + u32 nr_entries; + + u32 locks; /* count of 'lock' transactions */ + u32 store; /* count of all stores in trace */ + u32 st_uncache; /* stores to uncacheable address */ + u32 st_noadrs; /* cacheable store with no address */ + u32 st_l1hit; /* count of stores that hit L1D */ + u32 st_l1miss; /* count of stores that miss L1D */ + u32 load; /* count of all loads in trace */ + u32 ld_excl; /* exclusive loads, rmt/lcl DRAM - snp none/miss */ + u32 ld_shared; /* shared loads, rmt/lcl DRAM - snp hit */ + u32 ld_uncache; /* loads to uncacheable address */ + u32 ld_io; /* loads to io address */ + u32 ld_miss; /* loads miss */ + u32 ld_noadrs; /* cacheable load with no address */ + u32 ld_fbhit; /* count of loads hitting Fill Buffer */ + u32 ld_l1hit; /* count of loads that hit L1D */ + u32 ld_l2hit; /* count of loads that hit L2D */ + u32 ld_llchit; /* count of loads that hit LLC */ + u32 lcl_hitm; /* count of loads with local HITM */ + u32 rmt_hitm; /* count of loads with remote HITM */ + u32 rmt_hit; /* count of loads with remote hit clean; */ + u32 lcl_dram; /* count of loads miss to local DRAM */ + u32 rmt_dram; /* count of loads miss to remote DRAM */ + u32 nomap; /* count of load/stores with no phys adrs */ + u32 noparse; /* count of unparsable data sources */ +}; + +struct hist_entry; +int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi); +void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add); + #endif /* __PERF_MEM_EVENTS_H */ diff --git a/tools/perf/util/parse-branch-options.c b/tools/perf/util/parse-branch-options.c index afc088dd7d20..38fd11504015 100644 --- a/tools/perf/util/parse-branch-options.c +++ b/tools/perf/util/parse-branch-options.c @@ -31,59 +31,51 @@ static const struct branch_mode branch_modes[] = { BRANCH_END }; -int -parse_branch_stack(const struct option *opt, const char *str, int unset) +int parse_branch_str(const char *str, __u64 *mode) { #define ONLY_PLM \ (PERF_SAMPLE_BRANCH_USER |\ PERF_SAMPLE_BRANCH_KERNEL |\ PERF_SAMPLE_BRANCH_HV) - uint64_t *mode = (uint64_t *)opt->value; + int ret = 0; + char *p, *s; + char *os = NULL; const struct branch_mode *br; - char *s, *os = NULL, *p; - int ret = -1; - if (unset) + if (str == NULL) { + *mode = PERF_SAMPLE_BRANCH_ANY; return 0; + } - /* - * cannot set it twice, -b + --branch-filter for instance - */ - if (*mode) + /* because str is read-only */ + s = os = strdup(str); + if (!s) return -1; - /* str may be NULL in case no arg is passed to -b */ - if (str) { - /* because str is read-only */ - s = os = strdup(str); - if (!s) - return -1; - - for (;;) { - p = strchr(s, ','); - if (p) - *p = '\0'; - - for (br = branch_modes; br->name; br++) { - if (!strcasecmp(s, br->name)) - break; - } - if (!br->name) { - ui__warning("unknown branch filter %s," - " check man page\n", s); - goto error; - } - - *mode |= br->mode; - - if (!p) - break; + for (;;) { + p = strchr(s, ','); + if (p) + *p = '\0'; - s = p + 1; + for (br = branch_modes; br->name; br++) { + if (!strcasecmp(s, br->name)) + break; + } + if (!br->name) { + ret = -1; + pr_warning("unknown branch filter %s," + " check man page\n", s); + goto error; } + + *mode |= br->mode; + + if (!p) + break; + + s = p + 1; } - ret = 0; /* default to any branch */ if ((*mode & ~ONLY_PLM) == 0) { @@ -93,3 +85,20 @@ error: free(os); return ret; } + +int +parse_branch_stack(const struct option *opt, const char *str, int unset) +{ + __u64 *mode = (__u64 *)opt->value; + + if (unset) + return 0; + + /* + * cannot set it twice, -b + --branch-filter for instance + */ + if (*mode) + return -1; + + return parse_branch_str(str, mode); +} diff --git a/tools/perf/util/parse-branch-options.h b/tools/perf/util/parse-branch-options.h index b9d9470c2e82..6086fd90eb23 100644 --- a/tools/perf/util/parse-branch-options.h +++ b/tools/perf/util/parse-branch-options.h @@ -1,5 +1,6 @@ #ifndef _PERF_PARSE_BRANCH_OPTIONS_H #define _PERF_PARSE_BRANCH_OPTIONS_H 1 -struct option; +#include <stdint.h> int parse_branch_stack(const struct option *opt, const char *str, int unset); +int parse_branch_str(const char *str, __u64 *mode); #endif /* _PERF_PARSE_BRANCH_OPTIONS_H */ diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 4e778eae1510..3c876b8ba4de 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -22,6 +22,7 @@ #include "cpumap.h" #include "probe-file.h" #include "asm/bug.h" +#include "util/parse-branch-options.h" #define MAX_NAME_LEN 100 @@ -973,10 +974,13 @@ do { \ CHECK_TYPE_VAL(NUM); break; case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: - /* - * TODO uncomment when the field is available - * attr->branch_sample_type = term->val.num; - */ + CHECK_TYPE_VAL(STR); + if (strcmp(term->val.str, "no") && + parse_branch_str(term->val.str, &attr->branch_sample_type)) { + err->str = strdup("invalid branch sample type"); + err->idx = term->err_val; + return -EINVAL; + } break; case PARSE_EVENTS__TERM_TYPE_TIME: CHECK_TYPE_VAL(NUM); @@ -1119,6 +1123,9 @@ do { \ case PARSE_EVENTS__TERM_TYPE_CALLGRAPH: ADD_CONFIG_TERM(CALLGRAPH, callgraph, term->val.str); break; + case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: + ADD_CONFIG_TERM(BRANCH, branch, term->val.str); + break; case PARSE_EVENTS__TERM_TYPE_STACKSIZE: ADD_CONFIG_TERM(STACK_USER, stack_user, term->val.num); break; diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index b1474dcadfa2..dc6ccaa4e927 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -504,6 +504,7 @@ static void pmu_add_cpu_aliases(struct list_head *head) struct pmu_events_map *map; struct pmu_event *pe; char *cpuid; + static bool printed; cpuid = getenv("PERF_CPUID"); if (cpuid) @@ -513,7 +514,10 @@ static void pmu_add_cpu_aliases(struct list_head *head) if (!cpuid) return; - pr_debug("Using CPUID %s\n", cpuid); + if (!printed) { + pr_debug("Using CPUID %s\n", cpuid); + printed = true; + } i = 0; while (1) { @@ -1135,9 +1139,11 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, bool is_cpu = !strcmp(pmu->name, "cpu"); if (event_glob != NULL && - !(strglobmatch(name, event_glob) || - (!is_cpu && strglobmatch(alias->name, - event_glob)))) + !(strglobmatch_nocase(name, event_glob) || + (!is_cpu && strglobmatch_nocase(alias->name, + event_glob)) || + (alias->topic && + strglobmatch_nocase(alias->topic, event_glob)))) continue; if (is_cpu && !name_only && !alias->desc) diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index b7d4f4aeee61..0546a4304347 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -18,6 +18,7 @@ util/thread_map.c util/util.c util/xyarray.c util/cgroup.c +util/parse-branch-options.c util/rblist.c util/counts.c util/strlist.c diff --git a/tools/perf/util/quote.c b/tools/perf/util/quote.c index 639d1da2f978..293534c1a474 100644 --- a/tools/perf/util/quote.c +++ b/tools/perf/util/quote.c @@ -54,7 +54,7 @@ int sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen) break; ret = sq_quote_buf(dst, argv[i]); if (maxlen && dst->len > maxlen) - die("Too many or long arguments"); + return -ENOSPC; } return ret; } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 5d61242a6e64..f268201048a0 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -2025,20 +2025,10 @@ out_delete_map: void perf_session__fprintf_info(struct perf_session *session, FILE *fp, bool full) { - struct stat st; - int fd, ret; - if (session == NULL || fp == NULL) return; - fd = perf_data_file__fd(session->file); - - ret = fstat(fd, &st); - if (ret == -1) - return; - fprintf(fp, "# ========\n"); - fprintf(fp, "# captured on: %s", ctime(&st.st_ctime)); perf_header__fprintf_info(session, fp, full); fprintf(fp, "# ========\n#\n"); } diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 452e15a10dd2..df622f4e301e 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -315,7 +315,7 @@ struct sort_entry sort_sym = { /* --sort srcline */ -static char *hist_entry__get_srcline(struct hist_entry *he) +char *hist_entry__get_srcline(struct hist_entry *he) { struct map *map = he->ms.map; diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 099c97557d33..7aff317fc7c4 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -280,4 +280,5 @@ int64_t sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right); int64_t sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right); +char *hist_entry__get_srcline(struct hist_entry *he); #endif /* __PERF_SORT_H */ diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 7f7e072be746..d8dfaf64b32e 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -193,7 +193,8 @@ error: } /* Glob/lazy pattern matching */ -static bool __match_glob(const char *str, const char *pat, bool ignore_space) +static bool __match_glob(const char *str, const char *pat, bool ignore_space, + bool case_ins) { while (*str && *pat && *pat != '*') { if (ignore_space) { @@ -219,8 +220,13 @@ static bool __match_glob(const char *str, const char *pat, bool ignore_space) return false; else if (*pat == '\\') /* Escaped char match as normal char */ pat++; - if (*str++ != *pat++) + if (case_ins) { + if (tolower(*str) != tolower(*pat)) + return false; + } else if (*str != *pat) return false; + str++; + pat++; } /* Check wild card */ if (*pat == '*') { @@ -229,7 +235,7 @@ static bool __match_glob(const char *str, const char *pat, bool ignore_space) if (!*pat) /* Tail wild card matches all */ return true; while (*str) - if (__match_glob(str++, pat, ignore_space)) + if (__match_glob(str++, pat, ignore_space, case_ins)) return true; } return !*str && !*pat; @@ -249,7 +255,12 @@ static bool __match_glob(const char *str, const char *pat, bool ignore_space) */ bool strglobmatch(const char *str, const char *pat) { - return __match_glob(str, pat, false); + return __match_glob(str, pat, false, false); +} + +bool strglobmatch_nocase(const char *str, const char *pat) +{ + return __match_glob(str, pat, false, true); } /** @@ -262,7 +273,7 @@ bool strglobmatch(const char *str, const char *pat) */ bool strlazymatch(const char *str, const char *pat) { - return __match_glob(str, pat, true); + return __match_glob(str, pat, true, false); } /** diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index d964844eb314..2d0a905c879a 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -100,6 +100,7 @@ struct symbol_conf { show_total_period, use_callchain, cumulate_callchain, + show_branchflag_count, exclude_other, show_cpu_utilization, initialized, diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index 9df61059a85d..0ac9077f62a2 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c @@ -25,6 +25,7 @@ #include <errno.h> #include "../perf.h" +#include "debug.h" #include "util.h" #include "trace-event.h" @@ -86,16 +87,15 @@ struct scripting_ops python_scripting_unsupported_ops = { static void register_python_scripting(struct scripting_ops *scripting_ops) { - int err; - err = script_spec_register("Python", scripting_ops); - if (err) - die("error registering Python script extension"); - - err = script_spec_register("py", scripting_ops); - if (err) - die("error registering py script extension"); - - scripting_context = malloc(sizeof(struct scripting_context)); + if (scripting_context == NULL) + scripting_context = malloc(sizeof(*scripting_context)); + + if (scripting_context == NULL || + script_spec_register("Python", scripting_ops) || + script_spec_register("py", scripting_ops)) { + pr_err("Error registering Python script extension: disabling it\n"); + zfree(&scripting_context); + } } #ifdef NO_LIBPYTHON @@ -150,16 +150,15 @@ struct scripting_ops perl_scripting_unsupported_ops = { static void register_perl_scripting(struct scripting_ops *scripting_ops) { - int err; - err = script_spec_register("Perl", scripting_ops); - if (err) - die("error registering Perl script extension"); - - err = script_spec_register("pl", scripting_ops); - if (err) - die("error registering pl script extension"); - - scripting_context = malloc(sizeof(struct scripting_context)); + if (scripting_context == NULL) + scripting_context = malloc(sizeof(*scripting_context)); + + if (scripting_context == NULL || + script_spec_register("Perl", scripting_ops) || + script_spec_register("pl", scripting_ops)) { + pr_err("Error registering Perl script extension: disabling it\n"); + zfree(&scripting_context); + } } #ifdef NO_LIBPERL diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index 20c2e5743903..6fec84dff3f7 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -357,8 +357,8 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, di.format = UNW_INFO_FORMAT_REMOTE_TABLE; di.start_ip = map->start; di.end_ip = map->end; - di.u.rti.segbase = map->start + segbase; - di.u.rti.table_data = map->start + table_data; + di.u.rti.segbase = map->start + segbase - map->pgoff; + di.u.rti.table_data = map->start + table_data - map->pgoff; di.u.rti.table_len = fde_count * sizeof(struct table_entry) / sizeof(unw_word_t); ret = dwarf_search_unwind_table(as, ip, &di, pi, diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 85c56800f17a..5bbd1f609f1f 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -433,6 +433,14 @@ int parse_nsec_time(const char *str, u64 *ptime) return 0; } +int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz) +{ + u64 sec = timestamp / NSEC_PER_SEC; + u64 usec = (timestamp % NSEC_PER_SEC) / NSEC_PER_USEC; + + return scnprintf(buf, sz, "%"PRIu64".%06"PRIu64, sec, usec); +} + unsigned long parse_tag_value(const char *str, struct parse_tag *tags) { struct parse_tag *i = tags; diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 43899e0d6fa1..79662d67891e 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -222,6 +222,7 @@ s64 perf_atoll(const char *str); char **argv_split(const char *str, int *argcp); void argv_free(char **argv); bool strglobmatch(const char *str, const char *pat); +bool strglobmatch_nocase(const char *str, const char *pat); bool strlazymatch(const char *str, const char *pat); static inline bool strisglob(const char *str) { @@ -361,4 +362,7 @@ extern int sched_getcpu(void); #endif int is_printable_array(char *p, unsigned int len); + +int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz); + #endif /* GIT_COMPAT_UTIL_H */ diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c index 0fb3c1fcd3e6..5074be4ed467 100644 --- a/tools/perf/util/values.c +++ b/tools/perf/util/values.c @@ -2,15 +2,18 @@ #include "util.h" #include "values.h" +#include "debug.h" -void perf_read_values_init(struct perf_read_values *values) +int perf_read_values_init(struct perf_read_values *values) { values->threads_max = 16; values->pid = malloc(values->threads_max * sizeof(*values->pid)); values->tid = malloc(values->threads_max * sizeof(*values->tid)); values->value = malloc(values->threads_max * sizeof(*values->value)); - if (!values->pid || !values->tid || !values->value) - die("failed to allocate read_values threads arrays"); + if (!values->pid || !values->tid || !values->value) { + pr_debug("failed to allocate read_values threads arrays"); + goto out_free_pid; + } values->threads = 0; values->counters_max = 16; @@ -18,9 +21,22 @@ void perf_read_values_init(struct perf_read_values *values) * sizeof(*values->counterrawid)); values->countername = malloc(values->counters_max * sizeof(*values->countername)); - if (!values->counterrawid || !values->countername) - die("failed to allocate read_values counters arrays"); + if (!values->counterrawid || !values->countername) { + pr_debug("failed to allocate read_values counters arrays"); + goto out_free_counter; + } values->counters = 0; + + return 0; + +out_free_counter: + zfree(&values->counterrawid); + zfree(&values->countername); +out_free_pid: + zfree(&values->pid); + zfree(&values->tid); + zfree(&values->value); + return -ENOMEM; } void perf_read_values_destroy(struct perf_read_values *values) @@ -41,17 +57,27 @@ void perf_read_values_destroy(struct perf_read_values *values) zfree(&values->countername); } -static void perf_read_values__enlarge_threads(struct perf_read_values *values) +static int perf_read_values__enlarge_threads(struct perf_read_values *values) { - values->threads_max *= 2; - values->pid = realloc(values->pid, - values->threads_max * sizeof(*values->pid)); - values->tid = realloc(values->tid, - values->threads_max * sizeof(*values->tid)); - values->value = realloc(values->value, - values->threads_max * sizeof(*values->value)); - if (!values->pid || !values->tid || !values->value) - die("failed to enlarge read_values threads arrays"); + int nthreads_max = values->threads_max * 2; + void *npid = realloc(values->pid, nthreads_max * sizeof(*values->pid)), + *ntid = realloc(values->tid, nthreads_max * sizeof(*values->tid)), + *nvalue = realloc(values->value, nthreads_max * sizeof(*values->value)); + + if (!npid || !ntid || !nvalue) + goto out_err; + + values->threads_max = nthreads_max; + values->pid = npid; + values->tid = ntid; + values->value = nvalue; + return 0; +out_err: + free(npid); + free(ntid); + free(nvalue); + pr_debug("failed to enlarge read_values threads arrays"); + return -ENOMEM; } static int perf_read_values__findnew_thread(struct perf_read_values *values, @@ -63,15 +89,21 @@ static int perf_read_values__findnew_thread(struct perf_read_values *values, if (values->pid[i] == pid && values->tid[i] == tid) return i; - if (values->threads == values->threads_max) - perf_read_values__enlarge_threads(values); + if (values->threads == values->threads_max) { + i = perf_read_values__enlarge_threads(values); + if (i < 0) + return i; + } - i = values->threads++; + i = values->threads + 1; + values->value[i] = malloc(values->counters_max * sizeof(**values->value)); + if (!values->value[i]) { + pr_debug("failed to allocate read_values counters array"); + return -ENOMEM; + } values->pid[i] = pid; values->tid[i] = tid; - values->value[i] = malloc(values->counters_max * sizeof(**values->value)); - if (!values->value[i]) - die("failed to allocate read_values counters array"); + values->threads = i; return i; } @@ -115,16 +147,21 @@ static int perf_read_values__findnew_counter(struct perf_read_values *values, return i; } -void perf_read_values_add_value(struct perf_read_values *values, +int perf_read_values_add_value(struct perf_read_values *values, u32 pid, u32 tid, u64 rawid, const char *name, u64 value) { int tindex, cindex; tindex = perf_read_values__findnew_thread(values, pid, tid); + if (tindex < 0) + return tindex; cindex = perf_read_values__findnew_counter(values, rawid, name); + if (cindex < 0) + return cindex; values->value[tindex][cindex] = value; + return 0; } static void perf_read_values__display_pretty(FILE *fp, diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h index b21a80c6cf8d..808ff9c73bf5 100644 --- a/tools/perf/util/values.h +++ b/tools/perf/util/values.h @@ -14,10 +14,10 @@ struct perf_read_values { u64 **value; }; -void perf_read_values_init(struct perf_read_values *values); +int perf_read_values_init(struct perf_read_values *values); void perf_read_values_destroy(struct perf_read_values *values); -void perf_read_values_add_value(struct perf_read_values *values, +int perf_read_values_add_value(struct perf_read_values *values, u32 pid, u32 tid, u64 rawid, const char *name, u64 value); |