From 7764a385f60bd200304a33124bdb4e684caeabdf Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 9 Apr 2015 12:48:27 -0400 Subject: [PATCH 1/7] perf tools: Fix synthesizing fork_event.ppid for non-main thread Commit ca6c41c59b9 sets the ppid based on what is read from the /proc/pid/status file when synthesizing fork events. This is correct thing to do for new processes but not threads of a process. Fix ppid for threads to be the main thread when synthesizing fork events (ie., assume main thread spawned all sub-threads in a process). Reported-by: Arnaldo Carvalho de Melo Signed-off-by: David Ahern Tested-by: Arnaldo Carvalho de Melo Acked-by: Don Zickus Link: http://lkml.kernel.org/r/1428598107-178999-1-git-send-email-david.ahern@oracle.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 5516236df6ab..9d0985131252 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -183,8 +183,18 @@ static int perf_event__synthesize_fork(struct perf_tool *tool, { memset(&event->fork, 0, sizeof(event->fork) + machine->id_hdr_size); - event->fork.ppid = ppid; - event->fork.ptid = ppid; + /* + * for main thread set parent to ppid from status file. For other + * threads set parent pid to main thread. ie., assume main thread + * spawns all threads in a process + */ + if (tgid == pid) { + event->fork.ppid = ppid; + event->fork.ptid = ppid; + } else { + event->fork.ppid = tgid; + event->fork.ptid = tgid; + } event->fork.pid = tgid; event->fork.tid = pid; event->fork.header.type = PERF_RECORD_FORK; From d998b732599b304c3865e8e5c7ba6250faba6589 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 8 Apr 2015 11:57:03 -0300 Subject: [PATCH 2/7] perf tools: Fix error path to do closedir() when synthesizing threads When traversing /proc to synthesize the PERF_RECORD_FORK et al events we were bailing out on errors without calling closedir(), fix it. Reported-by: David Ahern Cc: Adrian Hunter Cc: Borislav Petkov Cc: Don Zickus Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-vxtp593rfztgbi8noy0m967p@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 9d0985131252..ff866c4d2e2f 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -387,6 +387,7 @@ static int __event__synthesize_thread(union perf_event *comm_event, DIR *tasks; struct dirent dirent, *next; pid_t tgid, ppid; + int rc = 0; /* special case: only send one comm event using passed in pid */ if (!full) { @@ -414,38 +415,38 @@ static int __event__synthesize_thread(union perf_event *comm_event, while (!readdir_r(tasks, &dirent, &next) && next) { char *end; - int rc = 0; pid_t _pid; _pid = strtol(dirent.d_name, &end, 10); if (*end) continue; + rc = -1; if (perf_event__prepare_comm(comm_event, _pid, machine, &tgid, &ppid) != 0) - return -1; + break; if (perf_event__synthesize_fork(tool, fork_event, _pid, tgid, ppid, process, machine) < 0) - return -1; + break; /* * Send the prepared comm event */ if (process(tool, comm_event, &synth_sample, machine) != 0) - return -1; + break; + rc = 0; if (_pid == pid) { /* process the parent's maps too */ rc = perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, process, machine, mmap_data); + if (rc) + break; } - - if (rc) - return rc; } closedir(tasks); - return 0; + return rc; } int perf_event__synthesize_thread_map(struct perf_tool *tool, From 1060ab857f64f33a9445881fd31fa91470aeb622 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 9 Apr 2015 16:15:46 -0400 Subject: [PATCH 3/7] perf tools: Fix cross-endian analysis Trying to analyze a big endian data file on little endian system fails with the error: 0xa9b40 [0x70]: failed to process type: 9 The problem is that header parsing is not done correctly because the file attributes are not swapped. Make it so. With this patch able to analyze a sparc64 data file on x86_64. Signed-off-by: David Ahern Acked-by: Jiri Olsa Cc: Namhyung Kim Link: http://lkml.kernel.org/r/1428610546-178789-1-git-send-email-david.ahern@oracle.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index fff3b2a455ae..918fd8ae2d80 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2504,8 +2504,11 @@ int perf_session__read_header(struct perf_session *session) if (read_attr(fd, header, &f_attr) < 0) goto out_errno; - if (header->needs_swap) + if (header->needs_swap) { + f_attr.ids.size = bswap_64(f_attr.ids.size); + f_attr.ids.offset = bswap_64(f_attr.ids.offset); perf_event__attr_swap(&f_attr.attr); + } tmp = lseek(fd, 0, SEEK_CUR); evsel = perf_evsel__new(&f_attr.attr); From 5e78c69b72276853ac64070a010e6df64723dba9 Mon Sep 17 00:00:00 2001 From: He Kuang Date: Fri, 10 Apr 2015 17:35:00 +0800 Subject: [PATCH 4/7] perf buildid-list: Fix segfault when show DSOs with hits commit: f3b623b8490a ("perf tools: Reference count struct thread") appends every thread->node to dead_threads in machine__remove_thread() and list_del_init() this node in thread__put(). perf_event__exit_del_thread() releases thread wihout using machine__remove_thread(), and causes a NULL pointer crash when list_del_init(&thread->node) is called. Fix this by using machine_remove_thread() instead of using thread__put() directly. This problem can be reproduced as following: $ perf record ls $ perf buildid-list --with-hits [ 3874.195070] perf[1018]: segfault at 0 ip 00000000004b0b15 sp 00007ffc35b44780 error 6 in perf[400000+166000] Segmentation fault After this patch: $ perf record ls $ perf buildid-list --with-hits bc23e7c3281e542650ba4324421d6acf78f4c23e /proc/kcore 643324cb0e969f30c56d660f167f84a150845511 [vdso] 0000000000000000000000000000000000000000 /bin/busybox ... Signed-off-by: He Kuang Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/r/1428658500-6483-1-git-send-email-hekuang@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/build-id.c | 8 ++------ tools/perf/util/machine.c | 4 +--- tools/perf/util/machine.h | 1 + 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index f7fb2587df69..61867dff5d5a 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -59,12 +59,8 @@ static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused, dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid, event->fork.ppid, event->fork.ptid); - if (thread) { - rb_erase(&thread->rb_node, &machine->threads); - if (machine->last_match == thread) - thread__zput(machine->last_match); - thread__put(thread); - } + if (thread) + machine__remove_thread(machine, thread); return 0; } diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 9c380a2caa54..527e032e24f6 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -14,8 +14,6 @@ #include "unwind.h" #include "linux/hash.h" -static void machine__remove_thread(struct machine *machine, struct thread *th); - static void dsos__init(struct dsos *dsos) { INIT_LIST_HEAD(&dsos->head); @@ -1256,7 +1254,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event return 0; } -static void machine__remove_thread(struct machine *machine, struct thread *th) +void machine__remove_thread(struct machine *machine, struct thread *th) { if (machine->last_match == th) thread__zput(machine->last_match); diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index e2faf3b47e7b..6d64cedb9d1e 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -120,6 +120,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid); void machine__exit(struct machine *machine); void machine__delete_threads(struct machine *machine); void machine__delete(struct machine *machine); +void machine__remove_thread(struct machine *machine, struct thread *th); struct branch_info *sample__resolve_bstack(struct perf_sample *sample, struct addr_location *al); From 7afb3fab390871b1d20b1dbb94e03b8a3861cb0d Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 1 Apr 2015 19:25:39 +0900 Subject: [PATCH 5/7] perf probe: Support multiple probes on different binaries Support multiple probes on different binaries with just one command. In the result, this example sets up the probes on icmp_rcv in kernel, on main and set_target in perf, and on pcspkr_event in pcspker.ko driver. ----- # perf probe -a icmp_rcv -x ./perf -a main -a set_target \ -m /lib/modules/4.0.0-rc5+/kernel/drivers/input/misc/pcspkr.ko \ -a pcspkr_event Added new event: probe:icmp_rcv (on icmp_rcv) You can now use it in all perf tools, such as: perf record -e probe:icmp_rcv -aR sleep 1 Added new event: probe_perf:main (on main in /home/mhiramat/ksrc/linux-3/tools/perf/perf) You can now use it in all perf tools, such as: perf record -e probe_perf:main -aR sleep 1 Added new event: probe_perf:set_target (on set_target in /home/mhiramat/ksrc/linux-3/tools/perf/perf) You can now use it in all perf tools, such as: perf record -e probe_perf:set_target -aR sleep 1 Added new event: probe:pcspkr_event (on pcspkr_event in pcspkr) You can now use it in all perf tools, such as: perf record -e probe:pcspkr_event -aR sleep 1 ----- Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20150401102539.17137.46454.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-probe.c | 9 +++++++-- tools/perf/util/probe-event.c | 5 +++-- tools/perf/util/probe-event.h | 6 +++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 921bb6942503..2df23e177da5 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -78,6 +78,11 @@ static int parse_probe_event(const char *str) } pev->uprobes = params.uprobes; + if (params.target) { + pev->target = strdup(params.target); + if (!pev->target) + return -ENOMEM; + } /* Parse a perf-probe command into event */ ret = parse_perf_probe_command(str, pev); @@ -178,7 +183,7 @@ static int opt_set_target(const struct option *opt, const char *str, int ret = -ENOENT; char *tmp; - if (str && !params.target) { + if (str) { if (!strcmp(opt->long_name, "exec")) params.uprobes = true; #ifdef HAVE_DWARF_SUPPORT @@ -200,6 +205,7 @@ static int opt_set_target(const struct option *opt, const char *str, if (!tmp) return -ENOMEM; } + free(params.target); params.target = tmp; ret = 0; } @@ -487,7 +493,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) if (params.nevents) { ret = add_perf_probe_events(params.events, params.nevents, params.max_probe_points, - params.target, params.force_add); if (ret < 0) { pr_err_with_code(" Error: Failed to add events.", ret); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index b78851732a71..30545ce2c712 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -1906,6 +1906,7 @@ void clear_perf_probe_event(struct perf_probe_event *pev) free(pev->event); free(pev->group); + free(pev->target); clear_perf_probe_point(&pev->point); for (i = 0; i < pev->nargs; i++) { @@ -2654,7 +2655,7 @@ struct __event_package { }; int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, - int max_tevs, const char *target, bool force_add) + int max_tevs, bool force_add) { int i, j, ret; struct __event_package *pkgs; @@ -2678,7 +2679,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, ret = convert_to_probe_trace_events(pkgs[i].pev, &pkgs[i].tevs, max_tevs, - target); + pkgs[i].pev->target); if (ret < 0) goto end; pkgs[i].ntevs = ret; diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index e01e9943139f..d6b783447be9 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -73,7 +73,8 @@ struct perf_probe_event { char *group; /* Group name */ struct perf_probe_point point; /* Probe point */ int nargs; /* Number of arguments */ - bool uprobes; + bool uprobes; /* Uprobe event flag */ + char *target; /* Target binary */ struct perf_probe_arg *args; /* Arguments */ }; @@ -124,8 +125,7 @@ extern int line_range__init(struct line_range *lr); extern const char *kernel_get_module_path(const char *module); extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, - int max_probe_points, const char *module, - bool force_add); + int max_probe_points, bool force_add); extern int del_perf_probe_events(struct strlist *dellist); extern int show_perf_probe_events(void); extern int show_line_range(struct line_range *lr, const char *module, From 8cb0aa4c2db395b143cd5165586dc17677960002 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 1 Apr 2015 19:25:42 +0900 Subject: [PATCH 6/7] perf probe: Check the orphaned -x option To avoid probing in unintended binary, the orphaned -x option must be checked and warned. Without this patch, following command sets up the probe in the kernel. ----- # perf probe -a strcpy -x ./perf Added new event: probe:strcpy (on strcpy) You can now use it in all perf tools, such as: perf record -e probe:strcpy -aR sleep 1 ----- But in this case, it seems that the user may want to probe in the perf binary. With this patch, perf-probe correctly handles the orphaned -x. ----- # perf probe -a strcpy -x ./perf Error: -x/-m must follow the probe definitions. ... ----- Reported-by: Jiri Olsa Acked-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20150401102541.17137.75477.stgit@localhost.localdomain Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-probe.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 2df23e177da5..f7b1af67e9f6 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -56,6 +56,7 @@ static struct { bool mod_events; bool uprobes; bool quiet; + bool target_used; int nevents; struct perf_probe_event events[MAX_PROBES]; struct strlist *dellist; @@ -82,6 +83,7 @@ static int parse_probe_event(const char *str) pev->target = strdup(params.target); if (!pev->target) return -ENOMEM; + params.target_used = true; } /* Parse a perf-probe command into event */ @@ -107,6 +109,7 @@ static int set_target(const char *ptr) params.target = strdup(ptr); if (!params.target) return -ENOMEM; + params.target_used = false; found = 1; buf = ptr + (strlen(ptr) - 3); @@ -207,6 +210,7 @@ static int opt_set_target(const struct option *opt, const char *str, } free(params.target); params.target = tmp; + params.target_used = false; ret = 0; } @@ -491,6 +495,12 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) } if (params.nevents) { + /* Ensure the last given target is used */ + if (params.target && !params.target_used) { + pr_warning(" Error: -x/-m must follow the probe definitions.\n"); + usage_with_options(probe_usage, options); + } + ret = add_perf_probe_events(params.events, params.nevents, params.max_probe_points, params.force_add); From 7b8283b56d9fb36106ff1c459dfd399a20bd374d Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 7 Apr 2015 09:20:37 -0600 Subject: [PATCH 7/7] perf evlist: Fix type for references to data_head/tail The data_head and data_tail fields are defined as __u64 in linux/perf_event.h, but perf userspace uses int and unsigned int. Convert all references to u64 for consistency. Signed-off-by: David Ahern Acked-by: Peter Zijlstra Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1428420037-26599-1-git-send-email-dsahern@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 4 ++-- tools/perf/util/evlist.c | 6 +++--- tools/perf/util/evlist.h | 9 ++++----- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index ac610488d2e1..c3efdfb630b5 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -70,8 +70,8 @@ static int process_synthesized_event(struct perf_tool *tool, static int record__mmap_read(struct record *rec, int idx) { struct perf_mmap *md = &rec->evlist->mmap[idx]; - unsigned int head = perf_mmap__read_head(md); - unsigned int old = md->prev; + u64 head = perf_mmap__read_head(md); + u64 old = md->prev; unsigned char *data = md->base + page_size; unsigned long size; void *buf; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 76ef7ee62640..080be93eea96 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -634,8 +634,8 @@ static struct perf_evsel *perf_evlist__event2evsel(struct perf_evlist *evlist, union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) { struct perf_mmap *md = &evlist->mmap[idx]; - unsigned int head = perf_mmap__read_head(md); - unsigned int old = md->prev; + u64 head = perf_mmap__read_head(md); + u64 old = md->prev; unsigned char *data = md->base + page_size; union perf_event *event = NULL; @@ -716,7 +716,7 @@ void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx) struct perf_mmap *md = &evlist->mmap[idx]; if (!evlist->overwrite) { - unsigned int old = md->prev; + u64 old = md->prev; perf_mmap__write_tail(md, old); } diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index fb19c47b8aac..b5cce95d644e 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -27,7 +27,7 @@ struct perf_mmap { void *base; int mask; int refcnt; - unsigned int prev; + u64 prev; char event_copy[PERF_SAMPLE_MAX_SIZE] __attribute__((aligned(8))); }; @@ -189,16 +189,15 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, size_t size); int perf_evlist__strerror_mmap(struct perf_evlist *evlist, int err, char *buf, size_t size); -static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) +static inline u64 perf_mmap__read_head(struct perf_mmap *mm) { struct perf_event_mmap_page *pc = mm->base; - int head = ACCESS_ONCE(pc->data_head); + u64 head = ACCESS_ONCE(pc->data_head); rmb(); return head; } -static inline void perf_mmap__write_tail(struct perf_mmap *md, - unsigned long tail) +static inline void perf_mmap__write_tail(struct perf_mmap *md, u64 tail) { struct perf_event_mmap_page *pc = md->base;