diff options
Diffstat (limited to 'tools/perf/util')
53 files changed, 1503 insertions, 214 deletions
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 5da376bc1afc..2ae92da613dd 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -42,6 +42,7 @@ libperf-y += pstack.o libperf-y += session.o libperf-$(CONFIG_AUDIT) += syscalltbl.o libperf-y += ordered-events.o +libperf-y += namespaces.o libperf-y += comm.o libperf-y += thread.o libperf-y += thread_map.o @@ -81,6 +82,7 @@ libperf-$(CONFIG_AUXTRACE) += intel-pt-decoder/ libperf-$(CONFIG_AUXTRACE) += intel-pt.o libperf-$(CONFIG_AUXTRACE) += intel-bts.o libperf-y += parse-branch-options.o +libperf-y += dump-insn.o libperf-y += parse-regs-options.o libperf-y += term.o libperf-y += help-unknown-cmd.o @@ -88,6 +90,7 @@ libperf-y += mem-events.o libperf-y += vsprintf.o libperf-y += drv_configs.o libperf-y += time-utils.o +libperf-y += expr-bison.o libperf-$(CONFIG_LIBBPF) += bpf-loader.o libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o @@ -140,6 +143,10 @@ $(OUTPUT)util/parse-events-bison.c: util/parse-events.y $(call rule_mkdir) $(Q)$(call echo-cmd,bison)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $@ -p parse_events_ +$(OUTPUT)util/expr-bison.c: util/expr.y + $(call rule_mkdir) + $(Q)$(call echo-cmd,bison)$(BISON) -v util/expr.y -d $(PARSER_DEBUG_BISON) -o $@ -p expr__ + $(OUTPUT)util/pmu-flex.c: util/pmu.l $(OUTPUT)util/pmu-bison.c $(call rule_mkdir) $(Q)$(call echo-cmd,flex)$(FLEX) -o $@ --header-file=$(OUTPUT)util/pmu-flex.h util/pmu.l @@ -152,6 +159,7 @@ CFLAGS_parse-events-flex.o += -w CFLAGS_pmu-flex.o += -w CFLAGS_parse-events-bison.o += -DYYENABLE_NLS=0 -w CFLAGS_pmu-bison.o += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w +CFLAGS_expr-bison.o += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w $(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c $(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 273f21fa32b5..22cd1dbe724b 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1435,7 +1435,7 @@ int symbol__disassemble(struct symbol *sym, struct map *map, const char *arch_na snprintf(command, sizeof(command), "%s %s%s --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 - " -l -d %s %s -C %s 2>/dev/null|grep -v %s|expand", + " -l -d %s %s -C %s 2>/dev/null|grep -v %s:|expand", objdump_path ? objdump_path : "objdump", disassembler_style ? "-M " : "", disassembler_style ? disassembler_style : "", @@ -1482,6 +1482,12 @@ int symbol__disassemble(struct symbol *sym, struct map *map, const char *arch_na nline = 0; while (!feof(file)) { + /* + * The source code line number (lineno) needs to be kept in + * accross calls to symbol__parse_objdump_line(), so that it + * can associate it with the instructions till the next one. + * See disasm_line__new() and struct disasm_line::line_nr. + */ if (symbol__parse_objdump_line(sym, map, arch, file, privsize, &lineno) < 0) break; diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index eafbf11442b2..86399eda3684 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -127,19 +127,19 @@ static int add_cgroup(struct perf_evlist *evlist, char *str) goto found; n++; } - if (atomic_read(&cgrp->refcnt) == 0) + if (refcount_read(&cgrp->refcnt) == 0) free(cgrp); return -1; found: - atomic_inc(&cgrp->refcnt); + refcount_inc(&cgrp->refcnt); counter->cgrp = cgrp; return 0; } void close_cgroup(struct cgroup_sel *cgrp) { - if (cgrp && atomic_dec_and_test(&cgrp->refcnt)) { + if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) { close(cgrp->fd); zfree(&cgrp->name); free(cgrp); diff --git a/tools/perf/util/cgroup.h b/tools/perf/util/cgroup.h index 31f8dcdbd7ef..d91966b97cbd 100644 --- a/tools/perf/util/cgroup.h +++ b/tools/perf/util/cgroup.h @@ -1,14 +1,14 @@ #ifndef __CGROUP_H__ #define __CGROUP_H__ -#include <linux/atomic.h> +#include <linux/refcount.h> struct option; struct cgroup_sel { char *name; int fd; - atomic_t refcnt; + refcount_t refcnt; }; diff --git a/tools/perf/util/cloexec.h b/tools/perf/util/cloexec.h index d0d465953d36..94a5a7d829d5 100644 --- a/tools/perf/util/cloexec.h +++ b/tools/perf/util/cloexec.h @@ -3,10 +3,4 @@ unsigned long perf_event_open_cloexec_flag(void); -#ifdef __GLIBC_PREREQ -#if !__GLIBC_PREREQ(2, 6) && !defined(__UCLIBC__) -int sched_getcpu(void) __THROW; -#endif -#endif - #endif /* __PERF_CLOEXEC_H */ diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c index 21b7ff382c3f..32837b6f7879 100644 --- a/tools/perf/util/comm.c +++ b/tools/perf/util/comm.c @@ -2,12 +2,12 @@ #include "util.h" #include <stdlib.h> #include <stdio.h> -#include <linux/atomic.h> +#include <linux/refcount.h> struct comm_str { char *str; struct rb_node rb_node; - atomic_t refcnt; + refcount_t refcnt; }; /* Should perhaps be moved to struct machine */ @@ -16,13 +16,13 @@ static struct rb_root comm_str_root; static struct comm_str *comm_str__get(struct comm_str *cs) { if (cs) - atomic_inc(&cs->refcnt); + refcount_inc(&cs->refcnt); return cs; } static void comm_str__put(struct comm_str *cs) { - if (cs && atomic_dec_and_test(&cs->refcnt)) { + if (cs && refcount_dec_and_test(&cs->refcnt)) { rb_erase(&cs->rb_node, &comm_str_root); zfree(&cs->str); free(cs); @@ -43,7 +43,7 @@ static struct comm_str *comm_str__alloc(const char *str) return NULL; } - atomic_set(&cs->refcnt, 0); + refcount_set(&cs->refcnt, 1); return cs; } @@ -61,7 +61,7 @@ static struct comm_str *comm_str__findnew(const char *str, struct rb_root *root) cmp = strcmp(str, iter->str); if (!cmp) - return iter; + return comm_str__get(iter); if (cmp < 0) p = &(*p)->rb_left; @@ -95,8 +95,6 @@ struct comm *comm__new(const char *str, u64 timestamp, bool exec) return NULL; } - comm_str__get(comm->comm_str); - return comm; } @@ -108,7 +106,6 @@ int comm__override(struct comm *comm, const char *str, u64 timestamp, bool exec) if (!new) return -ENOMEM; - comm_str__get(new); comm_str__put(old); comm->comm_str = new; comm->start = timestamp; diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 8c7504939113..061018b42393 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -29,7 +29,7 @@ static struct cpu_map *cpu_map__default_new(void) cpus->map[i] = i; cpus->nr = nr_cpus; - atomic_set(&cpus->refcnt, 1); + refcount_set(&cpus->refcnt, 1); } return cpus; @@ -43,7 +43,7 @@ static struct cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus) if (cpus != NULL) { cpus->nr = nr_cpus; memcpy(cpus->map, tmp_cpus, payload_size); - atomic_set(&cpus->refcnt, 1); + refcount_set(&cpus->refcnt, 1); } return cpus; @@ -252,7 +252,7 @@ struct cpu_map *cpu_map__dummy_new(void) if (cpus != NULL) { cpus->nr = 1; cpus->map[0] = -1; - atomic_set(&cpus->refcnt, 1); + refcount_set(&cpus->refcnt, 1); } return cpus; @@ -269,7 +269,7 @@ struct cpu_map *cpu_map__empty_new(int nr) for (i = 0; i < nr; i++) cpus->map[i] = -1; - atomic_set(&cpus->refcnt, 1); + refcount_set(&cpus->refcnt, 1); } return cpus; @@ -278,7 +278,7 @@ struct cpu_map *cpu_map__empty_new(int nr) static void cpu_map__delete(struct cpu_map *map) { if (map) { - WARN_ONCE(atomic_read(&map->refcnt) != 0, + WARN_ONCE(refcount_read(&map->refcnt) != 0, "cpu_map refcnt unbalanced\n"); free(map); } @@ -287,13 +287,13 @@ static void cpu_map__delete(struct cpu_map *map) struct cpu_map *cpu_map__get(struct cpu_map *map) { if (map) - atomic_inc(&map->refcnt); + refcount_inc(&map->refcnt); return map; } void cpu_map__put(struct cpu_map *map) { - if (map && atomic_dec_and_test(&map->refcnt)) + if (map && refcount_dec_and_test(&map->refcnt)) cpu_map__delete(map); } @@ -357,7 +357,7 @@ int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res, /* ensure we process id in increasing order */ qsort(c->map, c->nr, sizeof(int), cmp_ids); - atomic_set(&c->refcnt, 1); + refcount_set(&c->refcnt, 1); *res = c; return 0; } @@ -673,3 +673,49 @@ size_t cpu_map__snprint(struct cpu_map *map, char *buf, size_t size) pr_debug("cpumask list: %s\n", buf); return ret; } + +static char hex_char(unsigned char val) +{ + if (val < 10) + return val + '0'; + if (val < 16) + return val - 10 + 'a'; + return '?'; +} + +size_t cpu_map__snprint_mask(struct cpu_map *map, char *buf, size_t size) +{ + int i, cpu; + char *ptr = buf; + unsigned char *bitmap; + int last_cpu = cpu_map__cpu(map, map->nr - 1); + + bitmap = zalloc((last_cpu + 7) / 8); + if (bitmap == NULL) { + buf[0] = '\0'; + return 0; + } + + for (i = 0; i < map->nr; i++) { + cpu = cpu_map__cpu(map, i); + bitmap[cpu / 8] |= 1 << (cpu % 8); + } + + for (cpu = last_cpu / 4 * 4; cpu >= 0; cpu -= 4) { + unsigned char bits = bitmap[cpu / 8]; + + if (cpu % 8) + bits >>= 4; + else + bits &= 0xf; + + *ptr++ = hex_char(bits); + if ((cpu % 32) == 0 && cpu > 0) + *ptr++ = ','; + } + *ptr = '\0'; + free(bitmap); + + buf[size - 1] = '\0'; + return ptr - buf; +} diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 1a0549af8f5c..6b8bff87481d 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -3,13 +3,13 @@ #include <stdio.h> #include <stdbool.h> -#include <linux/atomic.h> +#include <linux/refcount.h> #include "perf.h" #include "util/debug.h" struct cpu_map { - atomic_t refcnt; + refcount_t refcnt; int nr; int map[]; }; @@ -20,6 +20,7 @@ struct cpu_map *cpu_map__dummy_new(void); struct cpu_map *cpu_map__new_data(struct cpu_map_data *data); struct cpu_map *cpu_map__read(FILE *file); size_t cpu_map__snprint(struct cpu_map *map, char *buf, size_t size); +size_t cpu_map__snprint_mask(struct cpu_map *map, char *buf, size_t size); size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); int cpu_map__get_socket_id(int cpu); int cpu_map__get_socket(struct cpu_map *map, int idx, void *data); diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index 4e6cbc99f08e..89ece2445713 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c @@ -1468,6 +1468,7 @@ int bt_convert__perf2ctf(const char *input, const char *path, .lost = perf_event__process_lost, .tracing_data = perf_event__process_tracing_data, .build_id = perf_event__process_build_id, + .namespaces = perf_event__process_namespaces, .ordered_events = true, .ordering_requires_timestamps = true, }, diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index d38b62a700ca..42db00d78573 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -1109,7 +1109,7 @@ struct dso *dso__new(const char *name) INIT_LIST_HEAD(&dso->node); INIT_LIST_HEAD(&dso->data.open_entry); pthread_mutex_init(&dso->lock, NULL); - atomic_set(&dso->refcnt, 1); + refcount_set(&dso->refcnt, 1); } return dso; @@ -1147,13 +1147,13 @@ void dso__delete(struct dso *dso) struct dso *dso__get(struct dso *dso) { if (dso) - atomic_inc(&dso->refcnt); + refcount_inc(&dso->refcnt); return dso; } void dso__put(struct dso *dso) { - if (dso && atomic_dec_and_test(&dso->refcnt)) + if (dso && refcount_dec_and_test(&dso->refcnt)) dso__delete(dso); } diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index ecc4bbd3f82e..12350b171727 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -1,7 +1,7 @@ #ifndef __PERF_DSO #define __PERF_DSO -#include <linux/atomic.h> +#include <linux/refcount.h> #include <linux/types.h> #include <linux/rbtree.h> #include <sys/types.h> @@ -187,7 +187,7 @@ struct dso { void *priv; u64 db_id; }; - atomic_t refcnt; + refcount_t refcnt; char name[0]; }; diff --git a/tools/perf/util/dump-insn.c b/tools/perf/util/dump-insn.c new file mode 100644 index 000000000000..ffbdb19f05d0 --- /dev/null +++ b/tools/perf/util/dump-insn.c @@ -0,0 +1,14 @@ +#include <linux/compiler.h> +#include "dump-insn.h" + +/* Fallback code */ + +__weak +const char *dump_insn(struct perf_insn *x __maybe_unused, + u64 ip __maybe_unused, u8 *inbuf __maybe_unused, + int inlen __maybe_unused, int *lenp) +{ + if (lenp) + *lenp = 0; + return "?"; +} diff --git a/tools/perf/util/dump-insn.h b/tools/perf/util/dump-insn.h new file mode 100644 index 000000000000..90fb115981cf --- /dev/null +++ b/tools/perf/util/dump-insn.h @@ -0,0 +1,22 @@ +#ifndef __PERF_DUMP_INSN_H +#define __PERF_DUMP_INSN_H 1 + +#define MAXINSN 15 + +#include <linux/types.h> + +struct thread; + +struct perf_insn { + /* Initialized by callers: */ + struct thread *thread; + u8 cpumode; + bool is64bit; + int cpu; + /* Temporary */ + char out[256]; +}; + +const char *dump_insn(struct perf_insn *x, u64 ip, + u8 *inbuf, int inlen, int *lenp); +#endif diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 4ea7ce72ed9c..76b9c6bc8369 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -31,6 +31,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_LOST_SAMPLES] = "LOST_SAMPLES", [PERF_RECORD_SWITCH] = "SWITCH", [PERF_RECORD_SWITCH_CPU_WIDE] = "SWITCH_CPU_WIDE", + [PERF_RECORD_NAMESPACES] = "NAMESPACES", [PERF_RECORD_HEADER_ATTR] = "ATTR", [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", @@ -49,6 +50,16 @@ static const char *perf_event__names[] = { [PERF_RECORD_TIME_CONV] = "TIME_CONV", }; +static const char *perf_ns__names[] = { + [NET_NS_INDEX] = "net", + [UTS_NS_INDEX] = "uts", + [IPC_NS_INDEX] = "ipc", + [PID_NS_INDEX] = "pid", + [USER_NS_INDEX] = "user", + [MNT_NS_INDEX] = "mnt", + [CGROUP_NS_INDEX] = "cgroup", +}; + const char *perf_event__name(unsigned int id) { if (id >= ARRAY_SIZE(perf_event__names)) @@ -58,6 +69,13 @@ const char *perf_event__name(unsigned int id) return perf_event__names[id]; } +static const char *perf_ns__name(unsigned int id) +{ + if (id >= ARRAY_SIZE(perf_ns__names)) + return "UNKNOWN"; + return perf_ns__names[id]; +} + static int perf_tool__process_synth_event(struct perf_tool *tool, union perf_event *event, struct machine *machine, @@ -203,6 +221,58 @@ pid_t perf_event__synthesize_comm(struct perf_tool *tool, return tgid; } +static void perf_event__get_ns_link_info(pid_t pid, const char *ns, + struct perf_ns_link_info *ns_link_info) +{ + struct stat64 st; + char proc_ns[128]; + + sprintf(proc_ns, "/proc/%u/ns/%s", pid, ns); + if (stat64(proc_ns, &st) == 0) { + ns_link_info->dev = st.st_dev; + ns_link_info->ino = st.st_ino; + } +} + +int perf_event__synthesize_namespaces(struct perf_tool *tool, + union perf_event *event, + pid_t pid, pid_t tgid, + perf_event__handler_t process, + struct machine *machine) +{ + u32 idx; + struct perf_ns_link_info *ns_link_info; + + if (!tool || !tool->namespace_events) + return 0; + + memset(&event->namespaces, 0, (sizeof(event->namespaces) + + (NR_NAMESPACES * sizeof(struct perf_ns_link_info)) + + machine->id_hdr_size)); + + event->namespaces.pid = tgid; + event->namespaces.tid = pid; + + event->namespaces.nr_namespaces = NR_NAMESPACES; + + ns_link_info = event->namespaces.link_info; + + for (idx = 0; idx < event->namespaces.nr_namespaces; idx++) + perf_event__get_ns_link_info(pid, perf_ns__name(idx), + &ns_link_info[idx]); + + event->namespaces.header.type = PERF_RECORD_NAMESPACES; + + event->namespaces.header.size = (sizeof(event->namespaces) + + (NR_NAMESPACES * sizeof(struct perf_ns_link_info)) + + machine->id_hdr_size); + + if (perf_tool__process_synth_event(tool, event, machine, process) != 0) + return -1; + + return 0; +} + static int perf_event__synthesize_fork(struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, pid_t ppid, @@ -255,8 +325,8 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, if (machine__is_default_guest(machine)) return 0; - snprintf(filename, sizeof(filename), "%s/proc/%d/maps", - machine->root_dir, pid); + snprintf(filename, sizeof(filename), "%s/proc/%d/task/%d/maps", + machine->root_dir, pid, pid); fp = fopen(filename, "r"); if (fp == NULL) { @@ -434,8 +504,9 @@ int perf_event__synthesize_modules(struct perf_tool *tool, static int __event__synthesize_thread(union perf_event *comm_event, union perf_event *mmap_event, union perf_event *fork_event, + union perf_event *namespaces_event, pid_t pid, int full, - perf_event__handler_t process, + perf_event__handler_t process, struct perf_tool *tool, struct machine *machine, bool mmap_data, @@ -455,6 +526,11 @@ static int __event__synthesize_thread(union perf_event *comm_event, if (tgid == -1) return -1; + if (perf_event__synthesize_namespaces(tool, namespaces_event, pid, + tgid, process, machine) < 0) + return -1; + + return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, process, machine, mmap_data, proc_map_timeout); @@ -488,6 +564,11 @@ static int __event__synthesize_thread(union perf_event *comm_event, if (perf_event__synthesize_fork(tool, fork_event, _pid, tgid, ppid, process, machine) < 0) break; + + if (perf_event__synthesize_namespaces(tool, namespaces_event, _pid, + tgid, process, machine) < 0) + break; + /* * Send the prepared comm event */ @@ -516,6 +597,7 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, unsigned int proc_map_timeout) { union perf_event *comm_event, *mmap_event, *fork_event; + union perf_event *namespaces_event; int err = -1, thread, j; comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size); @@ -530,10 +612,16 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, if (fork_event == NULL) goto out_free_mmap; + namespaces_event = malloc(sizeof(namespaces_event->namespaces) + + (NR_NAMESPACES * sizeof(struct perf_ns_link_info)) + + machine->id_hdr_size); + if (namespaces_event == NULL) + goto out_free_fork; + err = 0; for (thread = 0; thread < threads->nr; ++thread) { if (__event__synthesize_thread(comm_event, mmap_event, - fork_event, + fork_event, namespaces_event, thread_map__pid(threads, thread), 0, process, tool, machine, mmap_data, proc_map_timeout)) { @@ -559,7 +647,7 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, /* if not, generate events for it */ if (need_leader && __event__synthesize_thread(comm_event, mmap_event, - fork_event, + fork_event, namespaces_event, comm_event->comm.pid, 0, process, tool, machine, mmap_data, proc_map_timeout)) { @@ -568,6 +656,8 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, } } } + free(namespaces_event); +out_free_fork: free(fork_event); out_free_mmap: free(mmap_event); @@ -587,6 +677,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool, char proc_path[PATH_MAX]; struct dirent *dirent; union perf_event *comm_event, *mmap_event, *fork_event; + union perf_event *namespaces_event; int err = -1; if (machine__is_default_guest(machine)) @@ -604,11 +695,17 @@ int perf_event__synthesize_threads(struct perf_tool *tool, if (fork_event == NULL) goto out_free_mmap; + namespaces_event = malloc(sizeof(namespaces_event->namespaces) + + (NR_NAMESPACES * sizeof(struct perf_ns_link_info)) + + machine->id_hdr_size); + if (namespaces_event == NULL) + goto out_free_fork; + snprintf(proc_path, sizeof(proc_path), "%s/proc", machine->root_dir); proc = opendir(proc_path); if (proc == NULL) - goto out_free_fork; + goto out_free_namespaces; while ((dirent = readdir(proc)) != NULL) { char *end; @@ -620,13 +717,16 @@ int perf_event__synthesize_threads(struct perf_tool *tool, * We may race with exiting thread, so don't stop just because * one thread couldn't be synthesized. */ - __event__synthesize_thread(comm_event, mmap_event, fork_event, pid, - 1, process, tool, machine, mmap_data, + __event__synthesize_thread(comm_event, mmap_event, fork_event, + namespaces_event, pid, 1, process, + tool, machine, mmap_data, proc_map_timeout); } err = 0; closedir(proc); +out_free_namespaces: + free(namespaces_event); out_free_fork: free(fork_event); out_free_mmap: @@ -1008,6 +1108,33 @@ size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) return fprintf(fp, "%s: %s:%d/%d\n", s, event->comm.comm, event->comm.pid, event->comm.tid); } +size_t perf_event__fprintf_namespaces(union perf_event *event, FILE *fp) +{ + size_t ret = 0; + struct perf_ns_link_info *ns_link_info; + u32 nr_namespaces, idx; + + ns_link_info = event->namespaces.link_info; + nr_namespaces = event->namespaces.nr_namespaces; + + ret += fprintf(fp, " %d/%d - nr_namespaces: %u\n\t\t[", + event->namespaces.pid, + event->namespaces.tid, + nr_namespaces); + + for (idx = 0; idx < nr_namespaces; idx++) { + if (idx && (idx % 4 == 0)) + ret += fprintf(fp, "\n\t\t "); + + ret += fprintf(fp, "%u/%s: %" PRIu64 "/%#" PRIx64 "%s", idx, + perf_ns__name(idx), (u64)ns_link_info[idx].dev, + (u64)ns_link_info[idx].ino, + ((idx + 1) != nr_namespaces) ? ", " : "]\n"); + } + + return ret; +} + int perf_event__process_comm(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, @@ -1016,6 +1143,14 @@ int perf_event__process_comm(struct perf_tool *tool __maybe_unused, return machine__process_comm_event(machine, event, sample); } +int perf_event__process_namespaces(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + return machine__process_namespaces_event(machine, event, sample); +} + int perf_event__process_lost(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, @@ -1153,11 +1288,12 @@ int perf_event__process_exit(struct perf_tool *tool __maybe_unused, size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp) { - return fprintf(fp, " offset: %#"PRIx64" size: %#"PRIx64" flags: %#"PRIx64" [%s%s]\n", + return fprintf(fp, " offset: %#"PRIx64" size: %#"PRIx64" flags: %#"PRIx64" [%s%s%s]\n", event->aux.aux_offset, event->aux.aux_size, event->aux.flags, event->aux.flags & PERF_AUX_FLAG_TRUNCATED ? "T" : "", - event->aux.flags & PERF_AUX_FLAG_OVERWRITE ? "O" : ""); + event->aux.flags & PERF_AUX_FLAG_OVERWRITE ? "O" : "", + event->aux.flags & PERF_AUX_FLAG_PARTIAL ? "P" : ""); } size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp) @@ -1196,6 +1332,9 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp) case PERF_RECORD_MMAP: ret += perf_event__fprintf_mmap(event, fp); break; + case PERF_RECORD_NAMESPACES: + ret += perf_event__fprintf_namespaces(event, fp); + break; case PERF_RECORD_MMAP2: ret += perf_event__fprintf_mmap2(event, fp); break; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index c735c53a26f8..eb7a7b200737 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -39,6 +39,13 @@ struct comm_event { char comm[16]; }; +struct namespaces_event { + struct perf_event_header header; + u32 pid, tid; + u64 nr_namespaces; + struct perf_ns_link_info link_info[]; +}; + struct fork_event { struct perf_event_header header; u32 pid, ppid; @@ -269,6 +276,7 @@ struct events_stats { u64 total_lost; u64 total_lost_samples; u64 total_aux_lost; + u64 total_aux_partial; u64 total_invalid_chains; u32 nr_events[PERF_RECORD_HEADER_MAX]; u32 nr_non_filtered_samples; @@ -485,6 +493,7 @@ union perf_event { struct mmap_event mmap; struct mmap2_event mmap2; struct comm_event comm; + struct namespaces_event namespaces; struct fork_event fork; struct lost_event lost; struct lost_samples_event lost_samples; @@ -587,6 +596,10 @@ int perf_event__process_switch(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); +int perf_event__process_namespaces(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine); int perf_event__process_mmap(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, @@ -636,6 +649,12 @@ pid_t perf_event__synthesize_comm(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_namespaces(struct perf_tool *tool, + union perf_event *event, + pid_t pid, pid_t tgid, + perf_event__handler_t process, + struct machine *machine); + int perf_event__synthesize_mmap_events(struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, @@ -653,6 +672,7 @@ size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp); size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp); size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp); size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp); +size_t perf_event__fprintf_namespaces(union perf_event *event, FILE *fp); size_t perf_event__fprintf(union perf_event *event, FILE *fp); u64 kallsyms__get_function_start(const char *kallsyms_filename, diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index b601f2814a30..50420cd35446 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -777,7 +777,7 @@ union perf_event *perf_mmap__read_forward(struct perf_mmap *md, bool check_messu /* * Check if event was unmapped due to a POLLHUP/POLLERR. */ - if (!atomic_read(&md->refcnt)) + if (!refcount_read(&md->refcnt)) return NULL; head = perf_mmap__read_head(md); @@ -794,7 +794,7 @@ perf_mmap__read_backward(struct perf_mmap *md) /* * Check if event was unmapped due to a POLLHUP/POLLERR. */ - if (!atomic_read(&md->refcnt)) + if (!refcount_read(&md->refcnt)) return NULL; head = perf_mmap__read_head(md); @@ -856,7 +856,7 @@ void perf_mmap__read_catchup(struct perf_mmap *md) { u64 head; - if (!atomic_read(&md->refcnt)) + if (!refcount_read(&md->refcnt)) return; head = perf_mmap__read_head(md); @@ -875,14 +875,14 @@ static bool perf_mmap__empty(struct perf_mmap *md) static void perf_mmap__get(struct perf_mmap *map) { - atomic_inc(&map->refcnt); + refcount_inc(&map->refcnt); } static void perf_mmap__put(struct perf_mmap *md) { - BUG_ON(md->base && atomic_read(&md->refcnt) == 0); + BUG_ON(md->base && refcount_read(&md->refcnt) == 0); - if (atomic_dec_and_test(&md->refcnt)) + if (refcount_dec_and_test(&md->refcnt)) perf_mmap__munmap(md); } @@ -894,7 +894,7 @@ void perf_mmap__consume(struct perf_mmap *md, bool overwrite) perf_mmap__write_tail(md, old); } - if (atomic_read(&md->refcnt) == 1 && perf_mmap__empty(md)) + if (refcount_read(&md->refcnt) == 1 && perf_mmap__empty(md)) perf_mmap__put(md); } @@ -937,7 +937,7 @@ static void perf_mmap__munmap(struct perf_mmap *map) munmap(map->base, perf_mmap__mmap_len(map)); map->base = NULL; map->fd = -1; - atomic_set(&map->refcnt, 0); + refcount_set(&map->refcnt, 0); } auxtrace_mmap__munmap(&map->auxtrace_mmap); } @@ -974,8 +974,19 @@ static struct perf_mmap *perf_evlist__alloc_mmap(struct perf_evlist *evlist) if (!map) return NULL; - for (i = 0; i < evlist->nr_mmaps; i++) + for (i = 0; i < evlist->nr_mmaps; i++) { map[i].fd = -1; + /* + * When the perf_mmap() call is made we grab one refcount, plus + * one extra to let perf_evlist__mmap_consume() get the last + * events after all real references (perf_mmap__get()) are + * dropped. + * + * Each PERF_EVENT_IOC_SET_OUTPUT points to this mmap and + * thus does perf_mmap__get() on it. + */ + refcount_set(&map[i].refcnt, 0); + } return map; } @@ -1001,7 +1012,7 @@ static int perf_mmap__mmap(struct perf_mmap *map, * evlist layer can't just drop it when filtering events in * perf_evlist__filter_pollfd(). */ - atomic_set(&map->refcnt, 2); + refcount_set(&map->refcnt, 2); map->prev = 0; map->mask = mp->mask; map->base = mmap(NULL, perf_mmap__mmap_len(map), mp->prot, diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 389b9ccdf8c7..39942995f537 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -1,7 +1,7 @@ #ifndef __PERF_EVLIST_H #define __PERF_EVLIST_H 1 -#include <linux/atomic.h> +#include <linux/refcount.h> #include <linux/list.h> #include <api/fd/array.h> #include <stdio.h> @@ -29,7 +29,7 @@ struct perf_mmap { void *base; int mask; int fd; - atomic_t refcnt; + refcount_t refcnt; u64 prev; struct auxtrace_mmap auxtrace_mmap; char event_copy[PERF_SAMPLE_MAX_SIZE] __attribute__((aligned(8))); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index ac59710b79e0..9dc7e2d6e48a 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -236,6 +236,10 @@ void perf_evsel__init(struct perf_evsel *evsel, evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); perf_evsel__calc_id_pos(evsel); evsel->cmdline_group_boundary = false; + evsel->metric_expr = NULL; + evsel->metric_name = NULL; + evsel->metric_events = NULL; + evsel->collect_stat = false; } struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) @@ -932,6 +936,9 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, attr->mmap2 = track && !perf_missing_features.mmap2; attr->comm = track; + if (opts->record_namespaces) + attr->namespaces = track; + if (opts->record_switch_events) attr->context_switch = track; diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 06ef6f29efa1..d101695c482c 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -131,6 +131,11 @@ struct perf_evsel { bool cmdline_group_boundary; struct list_head config_terms; int bpf_fd; + bool merged_stat; + const char * metric_expr; + const char * metric_name; + struct perf_evsel **metric_events; + bool collect_stat; }; union u64_swap { diff --git a/tools/perf/util/expr.h b/tools/perf/util/expr.h new file mode 100644 index 000000000000..9c2760a1a96e --- /dev/null +++ b/tools/perf/util/expr.h @@ -0,0 +1,25 @@ +#ifndef PARSE_CTX_H +#define PARSE_CTX_H 1 + +#define EXPR_MAX_OTHER 8 +#define MAX_PARSE_ID EXPR_MAX_OTHER + +struct parse_id { + const char *name; + double val; +}; + +struct parse_ctx { + int num_ids; + struct parse_id ids[MAX_PARSE_ID]; +}; + +void expr__ctx_init(struct parse_ctx *ctx); +void expr__add_id(struct parse_ctx *ctx, const char *id, double val); +#ifndef IN_EXPR_Y +int expr__parse(double *final_val, struct parse_ctx *ctx, const char **pp); +#endif +int expr__find_other(const char *p, const char *one, const char ***other, + int *num_other); + +#endif diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y new file mode 100644 index 000000000000..954556bea36e --- /dev/null +++ b/tools/perf/util/expr.y @@ -0,0 +1,173 @@ +/* Simple expression parser */ +%{ +#include "util.h" +#include "util/debug.h" +#define IN_EXPR_Y 1 +#include "expr.h" +#include <string.h> + +#define MAXIDLEN 256 +%} + +%pure-parser +%parse-param { double *final_val } +%parse-param { struct parse_ctx *ctx } +%parse-param { const char **pp } +%lex-param { const char **pp } + +%union { + double num; + char id[MAXIDLEN+1]; +} + +%token <num> NUMBER +%token <id> ID +%left '|' +%left '^' +%left '&' +%left '-' '+' +%left '*' '/' '%' +%left NEG NOT +%type <num> expr + +%{ +static int expr__lex(YYSTYPE *res, const char **pp); + +static void expr__error(double *final_val __maybe_unused, + struct parse_ctx *ctx __maybe_unused, + const char **pp __maybe_unused, + const char *s) +{ + pr_debug("%s\n", s); +} + +static int lookup_id(struct parse_ctx *ctx, char *id, double *val) +{ + int i; + + for (i = 0; i < ctx->num_ids; i++) { + if (!strcasecmp(ctx->ids[i].name, id)) { + *val = ctx->ids[i].val; + return 0; + } + } + return -1; +} + +%} +%% + +all_expr: expr { *final_val = $1; } + ; + +expr: NUMBER + | ID { if (lookup_id(ctx, $1, &$$) < 0) { + pr_debug("%s not found", $1); + YYABORT; + } + } + | expr '+' expr { $$ = $1 + $3; } + | expr '-' expr { $$ = $1 - $3; } + | expr '*' expr { $$ = $1 * $3; } + | expr '/' expr { if ($3 == 0) YYABORT; $$ = $1 / $3; } + | expr '%' expr { if ((long)$3 == 0) YYABORT; $$ = (long)$1 % (long)$3; } + | '-' expr %prec NEG { $$ = -$2; } + | '(' expr ')' { $$ = $2; } + ; + +%% + +static int expr__symbol(YYSTYPE *res, const char *p, const char **pp) +{ + char *dst = res->id; + const char *s = p; + + while (isalnum(*p) || *p == '_' || *p == '.') { + if (p - s >= MAXIDLEN) + return -1; + *dst++ = *p++; + } + *dst = 0; + *pp = p; + return ID; +} + +static int expr__lex(YYSTYPE *res, const char **pp) +{ + int tok; + const char *s; + const char *p = *pp; + + while (isspace(*p)) + p++; + s = p; + switch (*p++) { + case 'a' ... 'z': + case 'A' ... 'Z': + return expr__symbol(res, p - 1, pp); + case '0' ... '9': case '.': + res->num = strtod(s, (char **)&p); + tok = NUMBER; + break; + default: + tok = *s; + break; + } + *pp = p; + return tok; +} + +/* Caller must make sure id is allocated */ +void expr__add_id(struct parse_ctx *ctx, const char *name, double val) +{ + int idx; + assert(ctx->num_ids < MAX_PARSE_ID); + idx = ctx->num_ids++; + ctx->ids[idx].name = name; + ctx->ids[idx].val = val; +} + +void expr__ctx_init(struct parse_ctx *ctx) +{ + ctx->num_ids = 0; +} + +int expr__find_other(const char *p, const char *one, const char ***other, + int *num_otherp) +{ + const char *orig = p; + int err = -1; + int num_other; + + *other = malloc((EXPR_MAX_OTHER + 1) * sizeof(char *)); + if (!*other) + return -1; + + num_other = 0; + for (;;) { + YYSTYPE val; + int tok = expr__lex(&val, &p); + if (tok == 0) { + err = 0; + break; + } + if (tok == ID && strcasecmp(one, val.id)) { + if (num_other >= EXPR_MAX_OTHER - 1) { + pr_debug("Too many extra events in %s\n", orig); + break; + } + (*other)[num_other] = strdup(val.id); + if (!(*other)[num_other]) + return -1; + num_other++; + } + } + (*other)[num_other] = NULL; + *num_otherp = num_other; + if (err) { + *num_otherp = 0; + free(*other); + *other = NULL; + } + return err; +} diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index eaf72a938fb4..e3b38f629504 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -3,6 +3,7 @@ #include "hist.h" #include "map.h" #include "session.h" +#include "namespaces.h" #include "sort.h" #include "evlist.h" #include "evsel.h" @@ -169,6 +170,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) hists__set_unres_dso_col_len(hists, HISTC_MEM_DADDR_DSO); } + hists__new_col_len(hists, HISTC_CGROUP_ID, 20); hists__new_col_len(hists, HISTC_CPU, 3); hists__new_col_len(hists, HISTC_SOCKET, 6); hists__new_col_len(hists, HISTC_MEM_LOCKED, 6); @@ -574,9 +576,14 @@ __hists__add_entry(struct hists *hists, bool sample_self, struct hist_entry_ops *ops) { + struct namespaces *ns = thread__namespaces(al->thread); struct hist_entry entry = { .thread = al->thread, .comm = thread__comm(al->thread), + .cgroup_id = { + .dev = ns ? ns->link_info[CGROUP_NS_INDEX].dev : 0, + .ino = ns ? ns->link_info[CGROUP_NS_INDEX].ino : 0, + }, .ms = { .map = al->map, .sym = al->sym, diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 28c216e3d5b7..ee3670a388df 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -30,6 +30,7 @@ enum hist_column { HISTC_DSO, HISTC_THREAD, HISTC_COMM, + HISTC_CGROUP_ID, HISTC_PARENT, HISTC_CPU, HISTC_SOCKET, @@ -57,6 +58,7 @@ enum hist_column { HISTC_SRCLINE_FROM, HISTC_SRCLINE_TO, HISTC_TRACE, + HISTC_SYM_SIZE, HISTC_NR_COLS, /* Last entry */ }; 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 7913363bde5c..54818828023b 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 @@ -26,12 +26,13 @@ #include "insn.c" #include "intel-pt-insn-decoder.h" +#include "dump-insn.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 */ +/* Based on branch_type() from arch/x86/events/intel/lbr.c */ static void intel_pt_insn_decoder(struct insn *insn, struct intel_pt_insn *intel_pt_insn) { @@ -39,6 +40,8 @@ static void intel_pt_insn_decoder(struct insn *insn, enum intel_pt_insn_branch branch = INTEL_PT_BR_NO_BRANCH; int ext; + intel_pt_insn->rel = 0; + if (insn_is_avx(insn)) { intel_pt_insn->op = INTEL_PT_OP_OTHER; intel_pt_insn->branch = INTEL_PT_BR_NO_BRANCH; @@ -177,6 +180,29 @@ int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64, return 0; } +const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused, + u8 *inbuf, int inlen, int *lenp) +{ + struct insn insn; + int n, i; + int left; + + insn_init(&insn, inbuf, inlen, x->is64bit); + insn_get_length(&insn); + if (!insn_complete(&insn) || insn.length > inlen) + return "<bad>"; + if (lenp) + *lenp = insn.length; + left = sizeof(x->out); + n = snprintf(x->out, left, "insn: "); + left -= n; + for (i = 0; i < insn.length; i++) { + n += snprintf(x->out + n, left, "%02x ", inbuf[i]); + left -= n; + } + return x->out; +} + const char *branch_name[] = { [INTEL_PT_OP_OTHER] = "Other", [INTEL_PT_OP_CALL] = "Call", diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 71c9720d4973..dfc600446586 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -13,6 +13,7 @@ #include <symbol/kallsyms.h> #include "unwind.h" #include "linux/hash.h" +#include "asm/bug.h" static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock); @@ -501,6 +502,37 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event return err; } +int machine__process_namespaces_event(struct machine *machine __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused) +{ + struct thread *thread = machine__findnew_thread(machine, + event->namespaces.pid, + event->namespaces.tid); + int err = 0; + + WARN_ONCE(event->namespaces.nr_namespaces > NR_NAMESPACES, + "\nWARNING: kernel seems to support more namespaces than perf" + " tool.\nTry updating the perf tool..\n\n"); + + WARN_ONCE(event->namespaces.nr_namespaces < NR_NAMESPACES, + "\nWARNING: perf tool seems to support more namespaces than" + " the kernel.\nTry updating the kernel..\n\n"); + + if (dump_trace) + perf_event__fprintf_namespaces(event, stdout); + + if (thread == NULL || + thread__set_namespaces(thread, sample->time, &event->namespaces)) { + dump_printf("problem processing PERF_RECORD_NAMESPACES, skipping event.\n"); + err = -1; + } + + thread__put(thread); + + return err; +} + int machine__process_lost_event(struct machine *machine __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused) { @@ -1439,7 +1471,7 @@ static void __machine__remove_thread(struct machine *machine, struct thread *th, if (machine->last_match == th) machine->last_match = NULL; - BUG_ON(atomic_read(&th->refcnt) == 0); + BUG_ON(refcount_read(&th->refcnt) == 0); if (lock) pthread_rwlock_wrlock(&machine->threads_lock); rb_erase_init(&th->rb_node, &machine->threads); @@ -1538,6 +1570,8 @@ int machine__process_event(struct machine *machine, union perf_event *event, ret = machine__process_comm_event(machine, event, sample); break; case PERF_RECORD_MMAP: ret = machine__process_mmap_event(machine, event, sample); break; + case PERF_RECORD_NAMESPACES: + ret = machine__process_namespaces_event(machine, event, sample); break; case PERF_RECORD_MMAP2: ret = machine__process_mmap2_event(machine, event, sample); break; case PERF_RECORD_FORK: diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index a28305029711..3cdb1340f917 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -97,6 +97,9 @@ int machine__process_itrace_start_event(struct machine *machine, union perf_event *event); int machine__process_switch_event(struct machine *machine, union perf_event *event); +int machine__process_namespaces_event(struct machine *machine, + union perf_event *event, + struct perf_sample *sample); int machine__process_mmap_event(struct machine *machine, union perf_event *event, struct perf_sample *sample); int machine__process_mmap2_event(struct machine *machine, union perf_event *event, diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 0a943e7b1ea7..1d9ebcf9e38e 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -141,7 +141,7 @@ void map__init(struct map *map, enum map_type type, RB_CLEAR_NODE(&map->rb_node); map->groups = NULL; map->erange_warned = false; - atomic_set(&map->refcnt, 1); + refcount_set(&map->refcnt, 1); } struct map *map__new(struct machine *machine, u64 start, u64 len, @@ -255,7 +255,7 @@ void map__delete(struct map *map) void map__put(struct map *map) { - if (map && atomic_dec_and_test(&map->refcnt)) + if (map && refcount_dec_and_test(&map->refcnt)) map__delete(map); } @@ -354,7 +354,7 @@ struct map *map__clone(struct map *from) struct map *map = memdup(from, sizeof(*map)); if (map != NULL) { - atomic_set(&map->refcnt, 1); + refcount_set(&map->refcnt, 1); RB_CLEAR_NODE(&map->rb_node); dso__get(map->dso); map->groups = NULL; @@ -485,7 +485,7 @@ void map_groups__init(struct map_groups *mg, struct machine *machine) maps__init(&mg->maps[i]); } mg->machine = machine; - atomic_set(&mg->refcnt, 1); + refcount_set(&mg->refcnt, 1); } static void __maps__purge(struct maps *maps) @@ -547,7 +547,7 @@ void map_groups__delete(struct map_groups *mg) void map_groups__put(struct map_groups *mg) { - if (mg && atomic_dec_and_test(&mg->refcnt)) + if (mg && refcount_dec_and_test(&mg->refcnt)) map_groups__delete(mg); } diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index abdacf800c98..c8a5a644c0a9 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -1,7 +1,7 @@ #ifndef __PERF_MAP_H #define __PERF_MAP_H -#include <linux/atomic.h> +#include <linux/refcount.h> #include <linux/compiler.h> #include <linux/list.h> #include <linux/rbtree.h> @@ -51,7 +51,7 @@ struct map { struct dso *dso; struct map_groups *groups; - atomic_t refcnt; + refcount_t refcnt; }; struct kmap { @@ -67,7 +67,7 @@ struct maps { struct map_groups { struct maps maps[MAP__NR_TYPES]; struct machine *machine; - atomic_t refcnt; + refcount_t refcnt; }; struct map_groups *map_groups__new(struct machine *machine); @@ -77,7 +77,7 @@ bool map_groups__empty(struct map_groups *mg); static inline struct map_groups *map_groups__get(struct map_groups *mg) { if (mg) - atomic_inc(&mg->refcnt); + refcount_inc(&mg->refcnt); return mg; } @@ -150,7 +150,7 @@ struct map *map__clone(struct map *map); static inline struct map *map__get(struct map *map) { if (map) - atomic_inc(&map->refcnt); + refcount_inc(&map->refcnt); return map; } diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c new file mode 100644 index 000000000000..2de8da64d90c --- /dev/null +++ b/tools/perf/util/namespaces.c @@ -0,0 +1,36 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * Copyright (C) 2017 Hari Bathini, IBM Corporation + */ + +#include "namespaces.h" +#include "util.h" +#include "event.h" +#include <stdlib.h> +#include <stdio.h> + +struct namespaces *namespaces__new(struct namespaces_event *event) +{ + struct namespaces *namespaces; + u64 link_info_size = ((event ? event->nr_namespaces : NR_NAMESPACES) * + sizeof(struct perf_ns_link_info)); + + namespaces = zalloc(sizeof(struct namespaces) + link_info_size); + if (!namespaces) + return NULL; + + namespaces->end_time = -1; + + if (event) + memcpy(namespaces->link_info, event->link_info, link_info_size); + + return namespaces; +} + +void namespaces__free(struct namespaces *namespaces) +{ + free(namespaces); +} diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h new file mode 100644 index 000000000000..468f1e9a1484 --- /dev/null +++ b/tools/perf/util/namespaces.h @@ -0,0 +1,26 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * Copyright (C) 2017 Hari Bathini, IBM Corporation + */ + +#ifndef __PERF_NAMESPACES_H +#define __PERF_NAMESPACES_H + +#include "../perf.h" +#include <linux/list.h> + +struct namespaces_event; + +struct namespaces { + struct list_head list; + u64 end_time; + struct perf_ns_link_info link_info[]; +}; + +struct namespaces *namespaces__new(struct namespaces_event *event); +void namespaces__free(struct namespaces *namespaces); + +#endif /* __PERF_NAMESPACES_H */ diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 67a8aebc67ab..6b498aea9fde 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -316,8 +316,9 @@ __add_event(struct list_head *list, int *idx, return NULL; (*idx)++; - evsel->cpus = cpu_map__get(cpus); - evsel->own_cpus = cpu_map__get(cpus); + evsel->cpus = cpu_map__get(cpus); + evsel->own_cpus = cpu_map__get(cpus); + evsel->system_wide = !!cpus; if (name) evsel->name = strdup(name); @@ -1254,11 +1255,59 @@ int parse_events_add_pmu(struct parse_events_evlist *data, evsel->scale = info.scale; evsel->per_pkg = info.per_pkg; evsel->snapshot = info.snapshot; + evsel->metric_expr = info.metric_expr; + evsel->metric_name = info.metric_name; } return evsel ? 0 : -ENOMEM; } +int parse_events_multi_pmu_add(struct parse_events_evlist *data, + char *str, struct list_head **listp) +{ + struct list_head *head; + struct parse_events_term *term; + struct list_head *list; + struct perf_pmu *pmu = NULL; + int ok = 0; + + *listp = NULL; + /* Add it for all PMUs that support the alias */ + list = malloc(sizeof(struct list_head)); + if (!list) + return -1; + INIT_LIST_HEAD(list); + while ((pmu = perf_pmu__scan(pmu)) != NULL) { + struct perf_pmu_alias *alias; + + list_for_each_entry(alias, &pmu->aliases, list) { + if (!strcasecmp(alias->name, str)) { + head = malloc(sizeof(struct list_head)); + if (!head) + return -1; + INIT_LIST_HEAD(head); + if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, + str, 1, false, &str, NULL) < 0) + return -1; + list_add_tail(&term->list, head); + + if (!parse_events_add_pmu(data, list, + pmu->name, head)) { + pr_debug("%s -> %s/%s/\n", str, + pmu->name, alias->str); + ok++; + } + + parse_events_terms__delete(head); + } + } + } + if (!ok) + return -1; + *listp = list; + return 0; +} + int parse_events__modifier_group(struct list_head *list, char *event_mod) { @@ -2276,7 +2325,7 @@ out_enomem: * Print the help text for the event symbols: */ void print_events(const char *event_glob, bool name_only, bool quiet_flag, - bool long_desc) + bool long_desc, bool details_flag) { print_symbol_events(event_glob, PERF_TYPE_HARDWARE, event_symbols_hw, PERF_COUNT_HW_MAX, name_only); @@ -2286,7 +2335,8 @@ void print_events(const char *event_glob, bool name_only, bool quiet_flag, print_hwcache_events(event_glob, name_only); - print_pmu_events(event_glob, name_only, quiet_flag, long_desc); + print_pmu_events(event_glob, name_only, quiet_flag, long_desc, + details_flag); if (event_glob != NULL) return; @@ -2415,6 +2465,31 @@ int parse_events_term__clone(struct parse_events_term **new, return new_term(new, &temp, term->val.str, term->val.num); } +int parse_events_copy_term_list(struct list_head *old, + struct list_head **new) +{ + struct parse_events_term *term, *n; + int ret; + + if (!old) { + *new = NULL; + return 0; + } + + *new = malloc(sizeof(struct list_head)); + if (!*new) + return -ENOMEM; + INIT_LIST_HEAD(*new); + + list_for_each_entry (term, old, list) { + ret = parse_events_term__clone(&n, term); + if (ret) + return ret; + list_add_tail(&n->list, *new); + } + return 0; +} + void parse_events_terms__purge(struct list_head *terms) { struct parse_events_term *term, *h; diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 1af6a267c21b..a235f4d6d5e5 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -8,6 +8,7 @@ #include <stdbool.h> #include <linux/types.h> #include <linux/perf_event.h> +#include <string.h> struct list_head; struct perf_evsel; @@ -166,6 +167,14 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx, int parse_events_add_pmu(struct parse_events_evlist *data, struct list_head *list, char *name, struct list_head *head_config); + +int parse_events_multi_pmu_add(struct parse_events_evlist *data, + char *str, + struct list_head **listp); + +int parse_events_copy_term_list(struct list_head *old, + struct list_head **new); + enum perf_pmu_event_symbol_type perf_pmu__parse_check(const char *name); void parse_events__set_leader(char *name, struct list_head *list); @@ -175,7 +184,7 @@ void parse_events_evlist_error(struct parse_events_evlist *data, int idx, const char *str); void print_events(const char *event_glob, bool name_only, bool quiet, - bool long_desc); + bool long_desc, bool details_flag); struct event_symbol { const char *symbol; @@ -196,4 +205,23 @@ int is_valid_tracepoint(const char *event_string); int valid_event_mount(const char *eventfs); char *parse_events_formats_error_string(char *additional_terms); +#ifdef HAVE_LIBELF_SUPPORT +/* + * If the probe point starts with '%', + * or starts with "sdt_" and has a ':' but no '=', + * then it should be a SDT/cached probe point. + */ +static inline bool is_sdt_event(char *str) +{ + return (str[0] == '%' || + (!strncmp(str, "sdt_", 4) && + !!strchr(str, ':') && !strchr(str, '='))); +} +#else +static inline bool is_sdt_event(char *str __maybe_unused) +{ + return false; +} +#endif /* HAVE_LIBELF_SUPPORT */ + #endif /* __PERF_PARSE_EVENTS_H */ diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 30f018ea1370..04fd8c9af9f9 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -226,68 +226,55 @@ event_pmu: PE_NAME opt_event_config { struct parse_events_evlist *data = _data; - struct list_head *list; + struct list_head *list, *orig_terms, *terms; + + if (parse_events_copy_term_list($2, &orig_terms)) + YYABORT; ALLOC_LIST(list); - ABORT_ON(parse_events_add_pmu(data, list, $1, $2)); + if (parse_events_add_pmu(data, list, $1, $2)) { + struct perf_pmu *pmu = NULL; + int ok = 0; + + while ((pmu = perf_pmu__scan(pmu)) != NULL) { + char *name = pmu->name; + + if (!strncmp(name, "uncore_", 7) && + strncmp($1, "uncore_", 7)) + name += 7; + if (!strncmp($1, name, strlen($1))) { + if (parse_events_copy_term_list(orig_terms, &terms)) + YYABORT; + if (!parse_events_add_pmu(data, list, pmu->name, terms)) + ok++; + parse_events_terms__delete(terms); + } + } + if (!ok) + YYABORT; + } parse_events_terms__delete($2); + parse_events_terms__delete(orig_terms); $$ = list; } | PE_KERNEL_PMU_EVENT sep_dc { - struct parse_events_evlist *data = _data; - struct list_head *head; - struct parse_events_term *term; struct list_head *list; - struct perf_pmu *pmu = NULL; - int ok = 0; - - /* Add it for all PMUs that support the alias */ - ALLOC_LIST(list); - while ((pmu = perf_pmu__scan(pmu)) != NULL) { - struct perf_pmu_alias *alias; - - list_for_each_entry(alias, &pmu->aliases, list) { - if (!strcasecmp(alias->name, $1)) { - ALLOC_LIST(head); - ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, - $1, 1, false, &@1, NULL)); - list_add_tail(&term->list, head); - - if (!parse_events_add_pmu(data, list, - pmu->name, head)) { - pr_debug("%s -> %s/%s/\n", $1, - pmu->name, alias->str); - ok++; - } - parse_events_terms__delete(head); - } - } - } - if (!ok) + if (parse_events_multi_pmu_add(_data, $1, &list) < 0) YYABORT; $$ = list; } | PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF sep_dc { - struct parse_events_evlist *data = _data; - struct list_head *head; - struct parse_events_term *term; struct list_head *list; char pmu_name[128]; - snprintf(&pmu_name, 128, "%s-%s", $1, $3); - ALLOC_LIST(head); - ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, - &pmu_name, 1, false, &@1, NULL)); - list_add_tail(&term->list, head); - - ALLOC_LIST(list); - ABORT_ON(parse_events_add_pmu(data, list, "cpu", head)); - parse_events_terms__delete(head); + snprintf(&pmu_name, 128, "%s-%s", $1, $3); + if (parse_events_multi_pmu_add(_data, pmu_name, &list) < 0) + YYABORT; $$ = list; } diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c index c4023f22f287..a37e5934aa2a 100644 --- a/tools/perf/util/perf_regs.c +++ b/tools/perf/util/perf_regs.c @@ -6,6 +6,12 @@ const struct sample_reg __weak sample_reg_masks[] = { SMPL_REG_END }; +int __weak sdt_rename_register(char **pdesc __maybe_unused, + char *old_name __maybe_unused) +{ + return 0; +} + #ifdef HAVE_PERF_REGS_SUPPORT int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) { diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index 679d6e493962..7544a157e159 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h @@ -15,6 +15,12 @@ struct sample_reg { extern const struct sample_reg sample_reg_masks[]; +/* + * The table sdt_reg_renamings is used for adjusting gcc/gas-generated + * registers before filling the uprobe tracer interface. + */ +int sdt_rename_register(char **pdesc, char *old_name); + #ifdef HAVE_PERF_REGS_SUPPORT #include <perf_regs.h> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 12f84dd2ac5d..362051ea7f3d 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -231,7 +231,9 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias, static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, char *desc, char *val, char *long_desc, char *topic, - char *unit, char *perpkg) + char *unit, char *perpkg, + char *metric_expr, + char *metric_name) { struct perf_pmu_alias *alias; int ret; @@ -265,6 +267,8 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, perf_pmu__parse_snapshot(alias, dir, name); } + alias->metric_expr = metric_expr ? strdup(metric_expr) : NULL; + alias->metric_name = metric_name ? strdup(metric_name): NULL; alias->desc = desc ? strdup(desc) : NULL; alias->long_desc = long_desc ? strdup(long_desc) : desc ? strdup(desc) : NULL; @@ -294,7 +298,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI buf[ret] = 0; return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL, NULL, NULL, - NULL); + NULL, NULL, NULL); } static inline bool pmu_alias_info_file(char *name) @@ -564,7 +568,9 @@ static void pmu_add_cpu_aliases(struct list_head *head, const char *name) __perf_pmu__new_alias(head, NULL, (char *)pe->name, (char *)pe->desc, (char *)pe->event, (char *)pe->long_desc, (char *)pe->topic, - (char *)pe->unit, (char *)pe->perpkg); + (char *)pe->unit, (char *)pe->perpkg, + (char *)pe->metric_expr, + (char *)pe->metric_name); } out: @@ -991,6 +997,8 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, info->unit = NULL; info->scale = 0.0; info->snapshot = false; + info->metric_expr = NULL; + info->metric_name = NULL; list_for_each_entry_safe(term, h, head_terms, list) { alias = pmu_find_alias(pmu, term); @@ -1006,6 +1014,8 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, if (alias->per_pkg) info->per_pkg = true; + info->metric_expr = alias->metric_expr; + info->metric_name = alias->metric_name; list_del(&term->list); free(term); @@ -1100,6 +1110,8 @@ struct sevent { char *topic; char *str; char *pmu; + char *metric_expr; + char *metric_name; }; static int cmp_sevent(const void *a, const void *b) @@ -1142,7 +1154,7 @@ static void wordwrap(char *s, int start, int max, int corr) } void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, - bool long_desc) + bool long_desc, bool details_flag) { struct perf_pmu *pmu; struct perf_pmu_alias *alias; @@ -1198,6 +1210,8 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, aliases[j].topic = alias->topic; aliases[j].str = alias->str; aliases[j].pmu = pmu->name; + aliases[j].metric_expr = alias->metric_expr; + aliases[j].metric_name = alias->metric_name; j++; } if (pmu->selectable && @@ -1232,8 +1246,14 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, printf("%*s", 8, "["); wordwrap(aliases[j].desc, 8, columns, 0); printf("]\n"); - if (verbose > 0) - printf("%*s%s/%s/\n", 8, "", aliases[j].pmu, aliases[j].str); + if (details_flag) { + printf("%*s%s/%s/ ", 8, "", aliases[j].pmu, aliases[j].str); + if (aliases[j].metric_name) + printf(" MetricName: %s", aliases[j].metric_name); + if (aliases[j].metric_expr) + printf(" MetricExpr: %s", aliases[j].metric_expr); + putchar('\n'); + } } else printf(" %-50s [Kernel PMU event]\n", aliases[j].name); printed++; diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 00852ddc7741..ea7f450dc609 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -31,6 +31,8 @@ struct perf_pmu { struct perf_pmu_info { const char *unit; + const char *metric_expr; + const char *metric_name; double scale; bool per_pkg; bool snapshot; @@ -50,6 +52,8 @@ struct perf_pmu_alias { double scale; bool per_pkg; bool snapshot; + char *metric_expr; + char *metric_name; }; struct perf_pmu *perf_pmu__find(const char *name); @@ -76,7 +80,7 @@ int perf_pmu__format_parse(char *dir, struct list_head *head); struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); void print_pmu_events(const char *event_glob, bool name_only, bool quiet, - bool long_desc); + bool long_desc, bool details_flag); bool pmu_have_event(const char *pname, const char *name); int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 28fb62c32678..e4b889444447 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -47,7 +47,6 @@ #include "probe-file.h" #include "session.h" -#define MAX_CMDLEN 256 #define PERFPROBE_GROUP "probe" bool probe_event_dry_run; /* Dry run flag */ @@ -757,7 +756,9 @@ post_process_kernel_probe_trace_events(struct probe_trace_event *tevs, } for (i = 0; i < ntevs; i++) { - if (!tevs[i].point.address || tevs[i].point.retprobe) + if (!tevs[i].point.address) + continue; + if (tevs[i].point.retprobe && !kretprobe_offset_is_supported()) continue; /* If we found a wrong one, mark it by NULL symbol */ if (kprobe_warn_out_range(tevs[i].point.symbol, @@ -1339,14 +1340,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) if (!arg) return -EINVAL; - /* - * If the probe point starts with '%', - * or starts with "sdt_" and has a ':' but no '=', - * then it should be a SDT/cached probe point. - */ - if (arg[0] == '%' || - (!strncmp(arg, "sdt_", 4) && - !!strchr(arg, ':') && !strchr(arg, '='))) { + if (is_sdt_event(arg)) { pev->sdt = true; if (arg[0] == '%') arg++; @@ -1528,11 +1522,6 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) return -EINVAL; } - if (pp->retprobe && !pp->function) { - semantic_error("Return probe requires an entry function.\n"); - return -EINVAL; - } - if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) { semantic_error("Offset/Line/Lazy pattern can't be used with " "return probe.\n"); @@ -2841,7 +2830,8 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, } /* Note that the symbols in the kmodule are not relocated */ - if (!pev->uprobes && !pp->retprobe && !pev->target) { + if (!pev->uprobes && !pev->target && + (!pp->retprobe || kretprobe_offset_is_supported())) { reloc_sym = kernel_get_ref_reloc_sym(); if (!reloc_sym) { pr_warning("Relocated base symbol is not found!\n"); @@ -3057,7 +3047,7 @@ concat_probe_trace_events(struct probe_trace_event **tevs, int *ntevs, struct probe_trace_event *new_tevs; int ret = 0; - if (ntevs == 0) { + if (*ntevs == 0) { *tevs = *tevs2; *ntevs = ntevs2; *tevs2 = NULL; diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index 436b64731f65..d741634cbfc0 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c @@ -27,8 +27,10 @@ #include "probe-event.h" #include "probe-file.h" #include "session.h" +#include "perf_regs.h" -#define MAX_CMDLEN 256 +/* 4096 - 2 ('\n' + '\0') */ +#define MAX_CMDLEN 4094 static void print_open_warning(int err, bool uprobe) { @@ -70,7 +72,7 @@ static void print_both_open_warning(int kerr, int uerr) } } -static int open_probe_events(const char *trace_file, bool readwrite) +int open_trace_file(const char *trace_file, bool readwrite) { char buf[PATH_MAX]; int ret; @@ -92,12 +94,12 @@ static int open_probe_events(const char *trace_file, bool readwrite) static int open_kprobe_events(bool readwrite) { - return open_probe_events("kprobe_events", readwrite); + return open_trace_file("kprobe_events", readwrite); } static int open_uprobe_events(bool readwrite) { - return open_probe_events("uprobe_events", readwrite); + return open_trace_file("uprobe_events", readwrite); } int probe_file__open(int flag) @@ -687,6 +689,166 @@ static unsigned long long sdt_note__get_addr(struct sdt_note *note) : (unsigned long long)note->addr.a64[0]; } +static const char * const type_to_suffix[] = { + ":s64", "", "", "", ":s32", "", ":s16", ":s8", + "", ":u8", ":u16", "", ":u32", "", "", "", ":u64" +}; + +static int synthesize_sdt_probe_arg(struct strbuf *buf, int i, const char *arg) +{ + char *tmp, *desc = strdup(arg); + const char *prefix = "", *suffix = ""; + int ret = -1; + + if (desc == NULL) { + pr_debug4("Allocation error\n"); + return ret; + } + + tmp = strchr(desc, '@'); + if (tmp) { + long type_idx; + /* + * Isolate the string number and convert it into a + * binary value; this will be an index to get suffix + * of the uprobe name (defining the type) + */ + tmp[0] = '\0'; + type_idx = strtol(desc, NULL, 10); + /* Check that the conversion went OK */ + if (type_idx == LONG_MIN || type_idx == LONG_MAX) { + pr_debug4("Failed to parse sdt type\n"); + goto error; + } + /* Check that the converted value is OK */ + if (type_idx < -8 || type_idx > 8) { + pr_debug4("Failed to get a valid sdt type\n"); + goto error; + } + suffix = type_to_suffix[type_idx + 8]; + /* Get rid of the sdt prefix which is now useless */ + tmp++; + memmove(desc, tmp, strlen(tmp) + 1); + } + + /* + * The uprobe tracer format does not support all the + * addressing modes (notably: in x86 the scaled mode); so, we + * detect ',' characters, if there is just one, there is no + * use converting the sdt arg into a uprobe one. + */ + if (strchr(desc, ',')) { + pr_debug4("Skipping unsupported SDT argument; %s\n", desc); + goto out; + } + + /* + * If the argument addressing mode is indirect, we must check + * a few things... + */ + tmp = strchr(desc, '('); + if (tmp) { + int j; + + /* + * ...if the addressing mode is indirect with a + * positive offset (ex.: "1608(%ax)"), we need to add + * a '+' prefix so as to be compliant with uprobe + * format. + */ + if (desc[0] != '+' && desc[0] != '-') + prefix = "+"; + + /* + * ...or if the addressing mode is indirect with a symbol + * as offset, the argument will not be supported by + * the uprobe tracer format; so, let's skip this one. + */ + for (j = 0; j < tmp - desc; j++) { + if (desc[j] != '+' && desc[j] != '-' && + !isdigit(desc[j])) { + pr_debug4("Skipping unsupported SDT argument; " + "%s\n", desc); + goto out; + } + } + } + + /* + * The uprobe tracer format does not support constants; if we + * find one in the current argument, let's skip the argument. + */ + if (strchr(desc, '$')) { + pr_debug4("Skipping unsupported SDT argument; %s\n", desc); + goto out; + } + + /* + * The uprobe parser does not support all gas register names; + * so, we have to replace them (ex. for x86_64: %rax -> %ax); + * the loop below looks for the register names (starting with + * a '%' and tries to perform the needed renamings. + */ + tmp = strchr(desc, '%'); + while (tmp) { + size_t offset = tmp - desc; + + ret = sdt_rename_register(&desc, desc + offset); + if (ret < 0) + goto error; + + /* + * The desc pointer might have changed; so, let's not + * try to reuse tmp for next lookup + */ + tmp = strchr(desc + offset + 1, '%'); + } + + if (strbuf_addf(buf, " arg%d=%s%s%s", i + 1, prefix, desc, suffix) < 0) + goto error; + +out: + ret = 0; +error: + free(desc); + return ret; +} + +static char *synthesize_sdt_probe_command(struct sdt_note *note, + const char *pathname, + const char *sdtgrp) +{ + struct strbuf buf; + char *ret = NULL, **args; + int i, args_count; + + if (strbuf_init(&buf, 32) < 0) + return NULL; + + if (strbuf_addf(&buf, "p:%s/%s %s:0x%llx", + sdtgrp, note->name, pathname, + sdt_note__get_addr(note)) < 0) + goto error; + + if (!note->args) + goto out; + + if (note->args) { + args = argv_split(note->args, &args_count); + + for (i = 0; i < args_count; ++i) { + if (synthesize_sdt_probe_arg(&buf, i, args[i]) < 0) + goto error; + } + } + +out: + ret = strbuf_detach(&buf, NULL); +error: + strbuf_release(&buf); + return ret; +} + int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname) { struct probe_cache_entry *entry = NULL; @@ -723,11 +885,12 @@ int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname) entry->pev.group = strdup(sdtgrp); list_add_tail(&entry->node, &pcache->entries); } - ret = asprintf(&buf, "p:%s/%s %s:0x%llx", - sdtgrp, note->name, pathname, - sdt_note__get_addr(note)); - if (ret < 0) + buf = synthesize_sdt_probe_command(note, pathname, sdtgrp); + if (!buf) { + ret = -ENOMEM; break; + } + strlist__add(entry->tevlist, buf); free(buf); entry = NULL; @@ -877,59 +1040,72 @@ int probe_cache__show_all_caches(struct strfilter *filter) return 0; } +enum ftrace_readme { + FTRACE_README_PROBE_TYPE_X = 0, + FTRACE_README_KRETPROBE_OFFSET, + FTRACE_README_END, +}; + static struct { const char *pattern; - bool avail; - bool checked; -} probe_type_table[] = { -#define DEFINE_TYPE(idx, pat, def_avail) \ - [idx] = {.pattern = pat, .avail = (def_avail)} - DEFINE_TYPE(PROBE_TYPE_U, "* u8/16/32/64,*", true), - DEFINE_TYPE(PROBE_TYPE_S, "* s8/16/32/64,*", true), - DEFINE_TYPE(PROBE_TYPE_X, "* x8/16/32/64,*", false), - DEFINE_TYPE(PROBE_TYPE_STRING, "* string,*", true), - DEFINE_TYPE(PROBE_TYPE_BITFIELD, - "* b<bit-width>@<bit-offset>/<container-size>", true), + bool avail; +} ftrace_readme_table[] = { +#define DEFINE_TYPE(idx, pat) \ + [idx] = {.pattern = pat, .avail = false} + DEFINE_TYPE(FTRACE_README_PROBE_TYPE_X, "*type: * x8/16/32/64,*"), + DEFINE_TYPE(FTRACE_README_KRETPROBE_OFFSET, "*place (kretprobe): *"), }; -bool probe_type_is_available(enum probe_type type) +static bool scan_ftrace_readme(enum ftrace_readme type) { + int fd; FILE *fp; char *buf = NULL; size_t len = 0; - bool target_line = false; - bool ret = probe_type_table[type].avail; + bool ret = false; + static bool scanned = false; - if (type >= PROBE_TYPE_END) - return false; - /* We don't have to check the type which supported by default */ - if (ret || probe_type_table[type].checked) - return ret; + if (scanned) + goto result; - if (asprintf(&buf, "%s/README", tracing_path) < 0) + fd = open_trace_file("README", false); + if (fd < 0) return ret; - fp = fopen(buf, "r"); - if (!fp) - goto end; - - zfree(&buf); - while (getline(&buf, &len, fp) > 0 && !ret) { - if (!target_line) { - target_line = !!strstr(buf, " type: "); - if (!target_line) - continue; - } else if (strstr(buf, "\t ") != buf) - break; - ret = strglobmatch(buf, probe_type_table[type].pattern); + fp = fdopen(fd, "r"); + if (!fp) { + close(fd); + return ret; } - /* Cache the result */ - probe_type_table[type].checked = true; - probe_type_table[type].avail = ret; + + while (getline(&buf, &len, fp) > 0) + for (enum ftrace_readme i = 0; i < FTRACE_README_END; i++) + if (!ftrace_readme_table[i].avail) + ftrace_readme_table[i].avail = + strglobmatch(buf, ftrace_readme_table[i].pattern); + scanned = true; fclose(fp); -end: free(buf); - return ret; +result: + if (type >= FTRACE_README_END) + return false; + + return ftrace_readme_table[type].avail; +} + +bool probe_type_is_available(enum probe_type type) +{ + if (type >= PROBE_TYPE_END) + return false; + else if (type == PROBE_TYPE_X) + return scan_ftrace_readme(FTRACE_README_PROBE_TYPE_X); + + return true; +} + +bool kretprobe_offset_is_supported(void) +{ + return scan_ftrace_readme(FTRACE_README_KRETPROBE_OFFSET); } diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h index eba44c3e9dca..dbf95a00864a 100644 --- a/tools/perf/util/probe-file.h +++ b/tools/perf/util/probe-file.h @@ -35,6 +35,7 @@ enum probe_type { /* probe-file.c depends on libelf */ #ifdef HAVE_LIBELF_SUPPORT +int open_trace_file(const char *trace_file, bool readwrite); int probe_file__open(int flag); int probe_file__open_both(int *kfd, int *ufd, int flag); struct strlist *probe_file__get_namelist(int fd); @@ -64,6 +65,7 @@ struct probe_cache_entry *probe_cache__find_by_name(struct probe_cache *pcache, const char *group, const char *event); int probe_cache__show_all_caches(struct strfilter *filter); bool probe_type_is_available(enum probe_type type); +bool kretprobe_offset_is_supported(void); #else /* ! HAVE_LIBELF_SUPPORT */ static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused) { diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 1dd617d116b5..24259bc2c598 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1,5 +1,6 @@ #include <linux/kernel.h> #include <traceevent/event-parse.h> +#include <api/fs/fs.h> #include <byteswap.h> #include <unistd.h> @@ -1239,6 +1240,8 @@ static int machines__deliver_event(struct machines *machines, return tool->mmap2(tool, event, sample, machine); case PERF_RECORD_COMM: return tool->comm(tool, event, sample, machine); + case PERF_RECORD_NAMESPACES: + return tool->namespaces(tool, event, sample, machine); case PERF_RECORD_FORK: return tool->fork(tool, event, sample, machine); case PERF_RECORD_EXIT: @@ -1258,9 +1261,12 @@ static int machines__deliver_event(struct machines *machines, case PERF_RECORD_UNTHROTTLE: return tool->unthrottle(tool, event, sample, machine); case PERF_RECORD_AUX: - if (tool->aux == perf_event__process_aux && - (event->aux.flags & PERF_AUX_FLAG_TRUNCATED)) - evlist->stats.total_aux_lost += 1; + if (tool->aux == perf_event__process_aux) { + if (event->aux.flags & PERF_AUX_FLAG_TRUNCATED) + evlist->stats.total_aux_lost += 1; + if (event->aux.flags & PERF_AUX_FLAG_PARTIAL) + evlist->stats.total_aux_partial += 1; + } return tool->aux(tool, event, sample, machine); case PERF_RECORD_ITRACE_START: return tool->itrace_start(tool, event, sample, machine); @@ -1494,6 +1500,11 @@ int perf_session__register_idle_thread(struct perf_session *session) err = -1; } + if (thread == NULL || thread__set_namespaces(thread, 0, NULL)) { + pr_err("problem inserting idle task.\n"); + err = -1; + } + /* machine__findnew_thread() got the thread, so put it */ thread__put(thread); return err; @@ -1548,6 +1559,23 @@ static void perf_session__warn_about_errors(const struct perf_session *session) stats->nr_events[PERF_RECORD_AUX]); } + if (session->tool->aux == perf_event__process_aux && + stats->total_aux_partial != 0) { + bool vmm_exclusive = false; + + (void)sysfs__read_bool("module/kvm_intel/parameters/vmm_exclusive", + &vmm_exclusive); + + ui__warning("AUX data had gaps in it %" PRIu64 " times out of %u!\n\n" + "Are you running a KVM guest in the background?%s\n\n", + stats->total_aux_partial, + stats->nr_events[PERF_RECORD_AUX], + vmm_exclusive ? + "\nReloading kvm_intel module with vmm_exclusive=0\n" + "will reduce the gaps to only guest's timeslices." : + ""); + } + if (stats->nr_unknown_events != 0) { ui__warning("Found %u unknown events!\n\n" "Is this an older tool processing a perf.data " diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 0ff622288d24..8b0d4e39f640 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -536,6 +536,46 @@ struct sort_entry sort_cpu = { .se_width_idx = HISTC_CPU, }; +/* --sort cgroup_id */ + +static int64_t _sort__cgroup_dev_cmp(u64 left_dev, u64 right_dev) +{ + return (int64_t)(right_dev - left_dev); +} + +static int64_t _sort__cgroup_inode_cmp(u64 left_ino, u64 right_ino) +{ + return (int64_t)(right_ino - left_ino); +} + +static int64_t +sort__cgroup_id_cmp(struct hist_entry *left, struct hist_entry *right) +{ + int64_t ret; + + ret = _sort__cgroup_dev_cmp(right->cgroup_id.dev, left->cgroup_id.dev); + if (ret != 0) + return ret; + + return _sort__cgroup_inode_cmp(right->cgroup_id.ino, + left->cgroup_id.ino); +} + +static int hist_entry__cgroup_id_snprintf(struct hist_entry *he, + char *bf, size_t size, + unsigned int width __maybe_unused) +{ + return repsep_snprintf(bf, size, "%lu/0x%lx", he->cgroup_id.dev, + he->cgroup_id.ino); +} + +struct sort_entry sort_cgroup_id = { + .se_header = "cgroup id (dev/inode)", + .se_cmp = sort__cgroup_id_cmp, + .se_snprintf = hist_entry__cgroup_id_snprintf, + .se_width_idx = HISTC_CGROUP_ID, +}; + /* --sort socket */ static int64_t @@ -846,6 +886,9 @@ static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf, static int64_t sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right) { + if (!left->branch_info || !right->branch_info) + return cmp_null(left->branch_info, right->branch_info); + return left->branch_info->flags.cycles - right->branch_info->flags.cycles; } @@ -853,6 +896,8 @@ sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right) static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { + if (!he->branch_info) + return scnprintf(bf, size, "%-.*s", width, "N/A"); if (he->branch_info->flags.cycles == 0) return repsep_snprintf(bf, size, "%-*s", width, "-"); return repsep_snprintf(bf, size, "%-*hd", width, @@ -1396,6 +1441,46 @@ struct sort_entry sort_transaction = { .se_width_idx = HISTC_TRANSACTION, }; +/* --sort symbol_size */ + +static int64_t _sort__sym_size_cmp(struct symbol *sym_l, struct symbol *sym_r) +{ + int64_t size_l = sym_l != NULL ? symbol__size(sym_l) : 0; + int64_t size_r = sym_r != NULL ? symbol__size(sym_r) : 0; + + return size_l < size_r ? -1 : + size_l == size_r ? 0 : 1; +} + +static int64_t +sort__sym_size_cmp(struct hist_entry *left, struct hist_entry *right) +{ + return _sort__sym_size_cmp(right->ms.sym, left->ms.sym); +} + +static int _hist_entry__sym_size_snprintf(struct symbol *sym, char *bf, + size_t bf_size, unsigned int width) +{ + if (sym) + return repsep_snprintf(bf, bf_size, "%*d", width, symbol__size(sym)); + + return repsep_snprintf(bf, bf_size, "%*s", width, "unknown"); +} + +static int hist_entry__sym_size_snprintf(struct hist_entry *he, char *bf, + size_t size, unsigned int width) +{ + return _hist_entry__sym_size_snprintf(he->ms.sym, bf, size, width); +} + +struct sort_entry sort_sym_size = { + .se_header = "Symbol size", + .se_cmp = sort__sym_size_cmp, + .se_snprintf = hist_entry__sym_size_snprintf, + .se_width_idx = HISTC_SYM_SIZE, +}; + + struct sort_dimension { const char *name; struct sort_entry *entry; @@ -1418,6 +1503,8 @@ static struct sort_dimension common_sort_dimensions[] = { DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), DIM(SORT_TRANSACTION, "transaction", sort_transaction), DIM(SORT_TRACE, "trace", sort_trace), + DIM(SORT_SYM_SIZE, "symbol_size", sort_sym_size), + DIM(SORT_CGROUP_ID, "cgroup_id", sort_cgroup_id), }; #undef DIM diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 796c847e2f00..baf20a399f34 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -54,6 +54,11 @@ struct he_stat { u32 nr_events; }; +struct namespace_id { + u64 dev; + u64 ino; +}; + struct hist_entry_diff { bool computed; union { @@ -91,6 +96,7 @@ struct hist_entry { struct map_symbol ms; struct thread *thread; struct comm *comm; + struct namespace_id cgroup_id; u64 ip; u64 transaction; s32 socket; @@ -211,6 +217,8 @@ enum sort_type { SORT_GLOBAL_WEIGHT, SORT_TRANSACTION, SORT_TRACE, + SORT_SYM_SIZE, + SORT_CGROUP_ID, /* branch stack specific sort keys */ __SORT_BRANCH_STACK, diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c index 8a2bbd2a4d82..ac10cc675d39 100644 --- a/tools/perf/util/stat-shadow.c +++ b/tools/perf/util/stat-shadow.c @@ -3,6 +3,9 @@ #include "stat.h" #include "color.h" #include "pmu.h" +#include "rblist.h" +#include "evlist.h" +#include "expr.h" enum { CTX_BIT_USER = 1 << 0, @@ -41,13 +44,73 @@ static struct stats runtime_topdown_slots_issued[NUM_CTX][MAX_NR_CPUS]; static struct stats runtime_topdown_slots_retired[NUM_CTX][MAX_NR_CPUS]; static struct stats runtime_topdown_fetch_bubbles[NUM_CTX][MAX_NR_CPUS]; static struct stats runtime_topdown_recovery_bubbles[NUM_CTX][MAX_NR_CPUS]; +static struct rblist runtime_saved_values; static bool have_frontend_stalled; struct stats walltime_nsecs_stats; +struct saved_value { + struct rb_node rb_node; + struct perf_evsel *evsel; + int cpu; + int ctx; + struct stats stats; +}; + +static int saved_value_cmp(struct rb_node *rb_node, const void *entry) +{ + struct saved_value *a = container_of(rb_node, + struct saved_value, + rb_node); + const struct saved_value *b = entry; + + if (a->ctx != b->ctx) + return a->ctx - b->ctx; + if (a->cpu != b->cpu) + return a->cpu - b->cpu; + return a->evsel - b->evsel; +} + +static struct rb_node *saved_value_new(struct rblist *rblist __maybe_unused, + const void *entry) +{ + struct saved_value *nd = malloc(sizeof(struct saved_value)); + + if (!nd) + return NULL; + memcpy(nd, entry, sizeof(struct saved_value)); + return &nd->rb_node; +} + +static struct saved_value *saved_value_lookup(struct perf_evsel *evsel, + int cpu, int ctx, + bool create) +{ + struct rb_node *nd; + struct saved_value dm = { + .cpu = cpu, + .ctx = ctx, + .evsel = evsel, + }; + nd = rblist__find(&runtime_saved_values, &dm); + if (nd) + return container_of(nd, struct saved_value, rb_node); + if (create) { + rblist__add_node(&runtime_saved_values, &dm); + nd = rblist__find(&runtime_saved_values, &dm); + if (nd) + return container_of(nd, struct saved_value, rb_node); + } + return NULL; +} + void perf_stat__init_shadow_stats(void) { have_frontend_stalled = pmu_have_event("cpu", "stalled-cycles-frontend"); + rblist__init(&runtime_saved_values); + runtime_saved_values.node_cmp = saved_value_cmp; + runtime_saved_values.node_new = saved_value_new; + /* No delete for now */ } static int evsel_context(struct perf_evsel *evsel) @@ -70,6 +133,8 @@ static int evsel_context(struct perf_evsel *evsel) void perf_stat__reset_shadow_stats(void) { + struct rb_node *pos, *next; + memset(runtime_nsecs_stats, 0, sizeof(runtime_nsecs_stats)); memset(runtime_cycles_stats, 0, sizeof(runtime_cycles_stats)); memset(runtime_stalled_cycles_front_stats, 0, sizeof(runtime_stalled_cycles_front_stats)); @@ -92,6 +157,15 @@ void perf_stat__reset_shadow_stats(void) memset(runtime_topdown_slots_issued, 0, sizeof(runtime_topdown_slots_issued)); memset(runtime_topdown_fetch_bubbles, 0, sizeof(runtime_topdown_fetch_bubbles)); memset(runtime_topdown_recovery_bubbles, 0, sizeof(runtime_topdown_recovery_bubbles)); + + next = rb_first(&runtime_saved_values.entries); + while (next) { + pos = next; + next = rb_next(pos); + memset(&container_of(pos, struct saved_value, rb_node)->stats, + 0, + sizeof(struct stats)); + } } /* @@ -143,6 +217,12 @@ void perf_stat__update_shadow_stats(struct perf_evsel *counter, u64 *count, update_stats(&runtime_dtlb_cache_stats[ctx][cpu], count[0]); else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_ITLB)) update_stats(&runtime_itlb_cache_stats[ctx][cpu], count[0]); + + if (counter->collect_stat) { + struct saved_value *v = saved_value_lookup(counter, cpu, ctx, + true); + update_stats(&v->stats, count[0]); + } } /* used for get_ratio_color() */ @@ -172,6 +252,95 @@ static const char *get_ratio_color(enum grc_type type, double ratio) return color; } +static struct perf_evsel *perf_stat__find_event(struct perf_evlist *evsel_list, + const char *name) +{ + struct perf_evsel *c2; + + evlist__for_each_entry (evsel_list, c2) { + if (!strcasecmp(c2->name, name)) + return c2; + } + return NULL; +} + +/* Mark MetricExpr target events and link events using them to them. */ +void perf_stat__collect_metric_expr(struct perf_evlist *evsel_list) +{ + struct perf_evsel *counter, *leader, **metric_events, *oc; + bool found; + const char **metric_names; + int i; + int num_metric_names; + + evlist__for_each_entry(evsel_list, counter) { + bool invalid = false; + + leader = counter->leader; + if (!counter->metric_expr) + continue; + metric_events = counter->metric_events; + if (!metric_events) { + if (expr__find_other(counter->metric_expr, counter->name, + &metric_names, &num_metric_names) < 0) + continue; + + metric_events = calloc(sizeof(struct perf_evsel *), + num_metric_names + 1); + if (!metric_events) + return; + counter->metric_events = metric_events; + } + + for (i = 0; i < num_metric_names; i++) { + found = false; + if (leader) { + /* Search in group */ + for_each_group_member (oc, leader) { + if (!strcasecmp(oc->name, metric_names[i])) { + found = true; + break; + } + } + } + if (!found) { + /* Search ignoring groups */ + oc = perf_stat__find_event(evsel_list, metric_names[i]); + } + if (!oc) { + /* Deduping one is good enough to handle duplicated PMUs. */ + static char *printed; + + /* + * Adding events automatically would be difficult, because + * it would risk creating groups that are not schedulable. + * perf stat doesn't understand all the scheduling constraints + * of events. So we ask the user instead to add the missing + * events. + */ + if (!printed || strcasecmp(printed, metric_names[i])) { + fprintf(stderr, + "Add %s event to groups to get metric expression for %s\n", + metric_names[i], + counter->name); + printed = strdup(metric_names[i]); + } + invalid = true; + continue; + } + metric_events[i] = oc; + oc->collect_stat = true; + } + metric_events[i] = NULL; + free(metric_names); + if (invalid) { + free(metric_events); + counter->metric_events = NULL; + counter->metric_expr = NULL; + } + } +} + static void print_stalled_cycles_frontend(int cpu, struct perf_evsel *evsel, double avg, struct perf_stat_output_ctx *out) @@ -614,6 +783,34 @@ void perf_stat__print_shadow_stats(struct perf_evsel *evsel, be_bound * 100.); else print_metric(ctxp, NULL, NULL, name, 0); + } else if (evsel->metric_expr) { + struct parse_ctx pctx; + int i; + + expr__ctx_init(&pctx); + expr__add_id(&pctx, evsel->name, avg); + for (i = 0; evsel->metric_events[i]; i++) { + struct saved_value *v; + + v = saved_value_lookup(evsel->metric_events[i], cpu, ctx, false); + if (!v) + break; + expr__add_id(&pctx, evsel->metric_events[i]->name, + avg_stats(&v->stats)); + } + if (!evsel->metric_events[i]) { + const char *p = evsel->metric_expr; + + if (expr__parse(&ratio, &pctx, &p) == 0) + print_metric(ctxp, NULL, "%8.1f", + evsel->metric_name ? + evsel->metric_name : + out->force_header ? evsel->name : "", + ratio); + else + print_metric(ctxp, NULL, NULL, "", 0); + } else + print_metric(ctxp, NULL, NULL, "", 0); } else if (runtime_nsecs_stats[cpu].n != 0) { char unit = 'M'; char unit_buf[10]; diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index c29bb94c48a4..0a65ae23f495 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -85,11 +85,13 @@ struct perf_stat_output_ctx { void *ctx; print_metric_t print_metric; new_line_t new_line; + bool force_header; }; void perf_stat__print_shadow_stats(struct perf_evsel *evsel, double avg, int cpu, struct perf_stat_output_ctx *out); +void perf_stat__collect_metric_expr(struct perf_evlist *); int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw); void perf_evlist__free_stats(struct perf_evlist *evlist); diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 4e59ddeb4eda..0e660dba58ad 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1828,7 +1828,7 @@ void kcore_extract__delete(struct kcore_extract *kce) static int populate_sdt_note(Elf **elf, const char *data, size_t len, struct list_head *sdt_notes) { - const char *provider, *name; + const char *provider, *name, *args; struct sdt_note *tmp = NULL; GElf_Ehdr ehdr; GElf_Addr base_off = 0; @@ -1887,6 +1887,25 @@ static int populate_sdt_note(Elf **elf, const char *data, size_t len, goto out_free_prov; } + args = memchr(name, '\0', data + len - name); + + /* + * There is no argument if: + * - We reached the end of the note; + * - There is not enough room to hold a potential string; + * - The argument string is empty or just contains ':'. + */ + if (args == NULL || data + len - args < 2 || + args[1] == ':' || args[1] == '\0') + tmp->args = NULL; + else { + tmp->args = strdup(++args); + if (!tmp->args) { + ret = -ENOMEM; + goto out_free_name; + } + } + if (gelf_getclass(*elf) == ELFCLASS32) { memcpy(&tmp->addr, &buf, 3 * sizeof(Elf32_Addr)); tmp->bit32 = true; @@ -1898,7 +1917,7 @@ static int populate_sdt_note(Elf **elf, const char *data, size_t len, if (!gelf_getehdr(*elf, &ehdr)) { pr_debug("%s : cannot get elf header.\n", __func__); ret = -EBADF; - goto out_free_name; + goto out_free_args; } /* Adjust the prelink effect : @@ -1923,6 +1942,8 @@ static int populate_sdt_note(Elf **elf, const char *data, size_t len, list_add_tail(&tmp->note_list, sdt_notes); return 0; +out_free_args: + free(tmp->args); out_free_name: free(tmp->name); out_free_prov: diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 6c358b7ed336..9222c7e702f3 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -351,6 +351,7 @@ int arch__choose_best_symbol(struct symbol *syma, struct symbol *symb); struct sdt_note { char *name; /* name of the note*/ char *provider; /* provider name */ + char *args; bool bit32; /* whether the location is 32 bits? */ union { /* location, base and semaphore addrs */ Elf64_Addr a64[3]; diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index f5af87f66663..dcdb87a5d0a1 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -7,6 +7,7 @@ #include "thread-stack.h" #include "util.h" #include "debug.h" +#include "namespaces.h" #include "comm.h" #include "unwind.h" @@ -40,6 +41,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) thread->tid = tid; thread->ppid = -1; thread->cpu = -1; + INIT_LIST_HEAD(&thread->namespaces_list); INIT_LIST_HEAD(&thread->comm_list); comm_str = malloc(32); @@ -53,7 +55,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) goto err_thread; list_add(&comm->list, &thread->comm_list); - atomic_set(&thread->refcnt, 1); + refcount_set(&thread->refcnt, 1); RB_CLEAR_NODE(&thread->rb_node); } @@ -66,7 +68,8 @@ err_thread: void thread__delete(struct thread *thread) { - struct comm *comm, *tmp; + struct namespaces *namespaces, *tmp_namespaces; + struct comm *comm, *tmp_comm; BUG_ON(!RB_EMPTY_NODE(&thread->rb_node)); @@ -76,7 +79,12 @@ void thread__delete(struct thread *thread) map_groups__put(thread->mg); thread->mg = NULL; } - list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { + list_for_each_entry_safe(namespaces, tmp_namespaces, + &thread->namespaces_list, list) { + list_del(&namespaces->list); + namespaces__free(namespaces); + } + list_for_each_entry_safe(comm, tmp_comm, &thread->comm_list, list) { list_del(&comm->list); comm__free(comm); } @@ -88,13 +96,13 @@ void thread__delete(struct thread *thread) struct thread *thread__get(struct thread *thread) { if (thread) - atomic_inc(&thread->refcnt); + refcount_inc(&thread->refcnt); return thread; } void thread__put(struct thread *thread) { - if (thread && atomic_dec_and_test(&thread->refcnt)) { + if (thread && refcount_dec_and_test(&thread->refcnt)) { /* * Remove it from the dead_threads list, as last reference * is gone. @@ -104,6 +112,38 @@ void thread__put(struct thread *thread) } } +struct namespaces *thread__namespaces(const struct thread *thread) +{ + if (list_empty(&thread->namespaces_list)) + return NULL; + + return list_first_entry(&thread->namespaces_list, struct namespaces, list); +} + +int thread__set_namespaces(struct thread *thread, u64 timestamp, + struct namespaces_event *event) +{ + struct namespaces *new, *curr = thread__namespaces(thread); + + new = namespaces__new(event); + if (!new) + return -ENOMEM; + + list_add(&new->list, &thread->namespaces_list); + + if (timestamp && curr) { + /* + * setns syscall must have changed few or all the namespaces + * of this thread. Update end time for the namespaces + * previously used. + */ + curr = list_next_entry(new, list); + curr->end_time = timestamp; + } + + return 0; +} + struct comm *thread__comm(const struct thread *thread) { if (list_empty(&thread->comm_list)) diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 99263cb6e6b6..4eb849e9098f 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -1,7 +1,7 @@ #ifndef __PERF_THREAD_H #define __PERF_THREAD_H -#include <linux/atomic.h> +#include <linux/refcount.h> #include <linux/rbtree.h> #include <linux/list.h> #include <unistd.h> @@ -23,11 +23,12 @@ struct thread { pid_t tid; pid_t ppid; int cpu; - atomic_t refcnt; + refcount_t refcnt; char shortname[3]; bool comm_set; int comm_len; bool dead; /* if set thread has exited */ + struct list_head namespaces_list; struct list_head comm_list; u64 db_id; @@ -40,6 +41,7 @@ struct thread { }; struct machine; +struct namespaces; struct comm; struct thread *thread__new(pid_t pid, pid_t tid); @@ -62,6 +64,10 @@ static inline void thread__exited(struct thread *thread) thread->dead = true; } +struct namespaces *thread__namespaces(const struct thread *thread); +int thread__set_namespaces(struct thread *thread, u64 timestamp, + struct namespaces_event *event); + int __thread__set_comm(struct thread *thread, const char *comm, u64 timestamp, bool exec); static inline int thread__set_comm(struct thread *thread, const char *comm, diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 7c3fcc538a70..9026408ea55b 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -66,7 +66,7 @@ struct thread_map *thread_map__new_by_pid(pid_t pid) for (i = 0; i < items; i++) thread_map__set_pid(threads, i, atoi(namelist[i]->d_name)); threads->nr = items; - atomic_set(&threads->refcnt, 1); + refcount_set(&threads->refcnt, 1); } for (i=0; i<items; i++) @@ -83,7 +83,7 @@ struct thread_map *thread_map__new_by_tid(pid_t tid) if (threads != NULL) { thread_map__set_pid(threads, 0, tid); threads->nr = 1; - atomic_set(&threads->refcnt, 1); + refcount_set(&threads->refcnt, 1); } return threads; @@ -105,7 +105,7 @@ struct thread_map *thread_map__new_by_uid(uid_t uid) goto out_free_threads; threads->nr = 0; - atomic_set(&threads->refcnt, 1); + refcount_set(&threads->refcnt, 1); while ((dirent = readdir(proc)) != NULL) { char *end; @@ -235,7 +235,7 @@ static struct thread_map *thread_map__new_by_pid_str(const char *pid_str) out: strlist__delete(slist); if (threads) - atomic_set(&threads->refcnt, 1); + refcount_set(&threads->refcnt, 1); return threads; out_free_namelist: @@ -255,7 +255,7 @@ struct thread_map *thread_map__new_dummy(void) if (threads != NULL) { thread_map__set_pid(threads, 0, -1); threads->nr = 1; - atomic_set(&threads->refcnt, 1); + refcount_set(&threads->refcnt, 1); } return threads; } @@ -300,7 +300,7 @@ struct thread_map *thread_map__new_by_tid_str(const char *tid_str) } out: if (threads) - atomic_set(&threads->refcnt, 1); + refcount_set(&threads->refcnt, 1); return threads; out_free_threads: @@ -326,7 +326,7 @@ static void thread_map__delete(struct thread_map *threads) if (threads) { int i; - WARN_ONCE(atomic_read(&threads->refcnt) != 0, + WARN_ONCE(refcount_read(&threads->refcnt) != 0, "thread map refcnt unbalanced\n"); for (i = 0; i < threads->nr; i++) free(thread_map__comm(threads, i)); @@ -337,13 +337,13 @@ static void thread_map__delete(struct thread_map *threads) struct thread_map *thread_map__get(struct thread_map *map) { if (map) - atomic_inc(&map->refcnt); + refcount_inc(&map->refcnt); return map; } void thread_map__put(struct thread_map *map) { - if (map && atomic_dec_and_test(&map->refcnt)) + if (map && refcount_dec_and_test(&map->refcnt)) thread_map__delete(map); } @@ -423,7 +423,7 @@ static void thread_map__copy_event(struct thread_map *threads, threads->map[i].comm = strndup(event->entries[i].comm, 16); } - atomic_set(&threads->refcnt, 1); + refcount_set(&threads->refcnt, 1); } struct thread_map *thread_map__new_event(struct thread_map_event *event) diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index ea0ef08c6303..bd34d7a0b9fa 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h @@ -3,7 +3,7 @@ #include <sys/types.h> #include <stdio.h> -#include <linux/atomic.h> +#include <linux/refcount.h> struct thread_map_data { pid_t pid; @@ -11,7 +11,7 @@ struct thread_map_data { }; struct thread_map { - atomic_t refcnt; + refcount_t refcnt; int nr; struct thread_map_data map[]; }; diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index ac2590a3de2d..829471a1c6d7 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -40,6 +40,7 @@ struct perf_tool { event_op mmap, mmap2, comm, + namespaces, fork, exit, lost, @@ -66,6 +67,7 @@ struct perf_tool { event_op3 auxtrace; bool ordered_events; bool ordering_requires_timestamps; + bool namespace_events; }; #endif /* __PERF_TOOL_H */ diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index c74708da8571..b2cfa47990dc 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -355,8 +355,8 @@ void print_binary(unsigned char *data, size_t len, size_t bytes_per_line, print_binary_t printer, void *extra); -#if !defined(__GLIBC__) && !defined(__ANDROID__) -extern int sched_getcpu(void); +#ifndef HAVE_SCHED_GETCPU_SUPPORT +int sched_getcpu(void); #endif int is_printable_array(char *p, unsigned int len); |