From b91fc39f4ad7503419dd617df78401fa36266cb3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 6 Apr 2015 20:43:22 -0300 Subject: perf machine: Protect the machine->threads with a rwlock In addition to using refcounts for the struct thread lifetime management, we need to protect access to machine->threads from concurrent access. That happens in 'perf top', where a thread processes events, inserting and deleting entries from that rb_tree while another thread decays hist_entries, that end up dropping references and ultimately deleting threads from the rb_tree and releasing its resources when no further hist_entry (or other data structures, like in 'perf sched') references it. So the rule is the same for refcounts + protected trees in the kernel, get the tree lock, find object, bump the refcount, drop the tree lock, return, use object, drop the refcount if no more use of it is needed, keep it if storing it in some other data structure, drop when releasing that data structure. I.e. pair "t = machine__find(new)_thread()" with a "thread__put(t)", and "perf_event__preprocess_sample(&al)" with "addr_location__put(&al)". The addr_location__put() one is because as we return references to several data structures, we may end up adding more reference counting for the other data structures and then we'll drop it at addr_location__put() time. Acked-by: David Ahern Cc: Adrian Hunter Cc: Borislav Petkov Cc: Don Zickus Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-bs9rt4n0jw3hi9f3zxyy3xln@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-kvm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'tools/perf/builtin-kvm.c') diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 1f9338f6109c..15fecd3dc5d8 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -651,6 +651,7 @@ static int process_sample_event(struct perf_tool *tool, struct perf_evsel *evsel, struct machine *machine) { + int err = 0; struct thread *thread; struct perf_kvm_stat *kvm = container_of(tool, struct perf_kvm_stat, tool); @@ -666,9 +667,10 @@ static int process_sample_event(struct perf_tool *tool, } if (!handle_kvm_event(kvm, thread, evsel, sample)) - return -1; + err = -1; - return 0; + thread__put(thread); + return err; } static int cpu_isa_config(struct perf_kvm_stat *kvm) -- cgit v1.2.3-59-g8ed1b From 9d9cad763ca79dd3697e9f2d1df648e37496582b Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Wed, 17 Jun 2015 09:51:11 -0400 Subject: perf tools: Configurable per thread proc map processing time out The time out to limit the individual proc map processing was hard code to 500ms. This patch introduce a new option --proc-map-timeout to make the time limit configurable. Signed-off-by: Kan Liang Cc: Andi Kleen Cc: David Ahern Cc: Ying Huang Link: http://lkml.kernel.org/r/1434549071-25611-2-git-send-email-kan.liang@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-kvm.txt | 6 ++++++ tools/perf/Documentation/perf-record.txt | 5 +++++ tools/perf/Documentation/perf-top.txt | 6 ++++++ tools/perf/Documentation/perf-trace.txt | 5 +++++ tools/perf/builtin-kvm.c | 5 ++++- tools/perf/builtin-record.c | 6 +++++- tools/perf/builtin-top.c | 5 ++++- tools/perf/builtin-trace.c | 6 +++++- tools/perf/perf.h | 1 + tools/perf/tests/code-reading.c | 2 +- tools/perf/tests/dwarf-unwind.c | 2 +- tools/perf/tests/mmap-thread-lookup.c | 4 ++-- tools/perf/util/event.c | 36 ++++++++++++++++++++------------ tools/perf/util/event.h | 9 +++++--- tools/perf/util/machine.c | 7 ++++--- tools/perf/util/machine.h | 9 +++++--- tools/perf/util/session.c | 4 +++- 17 files changed, 87 insertions(+), 31 deletions(-) (limited to 'tools/perf/builtin-kvm.c') diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt index 6252e776009c..6a5bb2b17039 100644 --- a/tools/perf/Documentation/perf-kvm.txt +++ b/tools/perf/Documentation/perf-kvm.txt @@ -151,6 +151,12 @@ STAT LIVE OPTIONS Show events other than HLT (x86 only) or Wait state (s390 only) that take longer than duration usecs. +--proc-map-timeout:: + When processing pre-existing threads /proc/XXX/mmap, it may take + a long time, because the file may be huge. A time out is needed + in such cases. + This option sets the time out limit. The default value is 500 ms. + SEE ALSO -------- linkperf:perf-top[1], linkperf:perf-record[1], linkperf:perf-report[1], diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 6fdf78625c51..9b9d9d086680 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -271,6 +271,11 @@ AUX area tracing event. Optionally the number of bytes to capture per snapshot can be specified. In Snapshot Mode, trace data is captured only when signal SIGUSR2 is received. +--proc-map-timeout:: +When processing pre-existing threads /proc/XXX/mmap, it may take a long time, +because the file may be huge. A time out is needed in such cases. +This option sets the time out limit. The default value is 500 ms. + SEE ALSO -------- linkperf:perf-stat[1], linkperf:perf-list[1] diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 9e5b07eb7d35..776aec4d0927 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -201,6 +201,12 @@ Default is to monitor all CPUS. Force each column width to the provided list, for large terminal readability. 0 means no limit (default behavior). +--proc-map-timeout:: + When processing pre-existing threads /proc/XXX/mmap, it may take + a long time, because the file may be huge. A time out is needed + in such cases. + This option sets the time out limit. The default value is 500 ms. + INTERACTIVE PROMPTING KEYS -------------------------- diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 1db9c8b79880..7ea078658a87 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -121,6 +121,11 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. --event:: Trace other events, see 'perf list' for a complete list. +--proc-map-timeout:: + When processing pre-existing threads /proc/XXX/mmap, it may take a long time, + because the file may be huge. A time out is needed in such cases. + This option sets the time out limit. The default value is 500 ms. + PAGEFAULTS ---------- diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 15fecd3dc5d8..74878cd75078 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -1311,6 +1311,8 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, "show events other than" " HLT (x86 only) or Wait state (s390 only)" " that take longer than duration usecs"), + OPT_UINTEGER(0, "proc-map-timeout", &kvm->opts.proc_map_timeout, + "per thread proc mmap processing timeout in ms"), OPT_END() }; const char * const live_usage[] = { @@ -1338,6 +1340,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, kvm->opts.target.uses_mmap = false; kvm->opts.target.uid_str = NULL; kvm->opts.target.uid = UINT_MAX; + kvm->opts.proc_map_timeout = 500; symbol__init(NULL); disable_buildid_cache(); @@ -1393,7 +1396,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, perf_session__set_id_hdr_size(kvm->session); ordered_events__set_copy_on_queue(&kvm->session->ordered_events, true); machine__synthesize_threads(&kvm->session->machines.host, &kvm->opts.target, - kvm->evlist->threads, false); + kvm->evlist->threads, false, kvm->opts.proc_map_timeout); err = kvm_live_open_events(kvm); if (err) goto out; diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4d6cdeb94fe1..de165a1b9240 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -598,7 +598,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) } err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->threads, - process_synthesized_event, opts->sample_address); + process_synthesized_event, opts->sample_address, + opts->proc_map_timeout); if (err != 0) goto out_child; @@ -959,6 +960,7 @@ static struct record record = { .uses_mmap = true, .default_per_cpu = true, }, + .proc_map_timeout = 500, }, .tool = { .sample = process_sample_event, @@ -1066,6 +1068,8 @@ struct option __record_options[] = { parse_clockid), OPT_STRING_OPTARG('S', "snapshot", &record.opts.auxtrace_snapshot_opts, "opts", "AUX area tracing Snapshot Mode", ""), + OPT_UINTEGER(0, "proc-map-timeout", &record.opts.proc_map_timeout, + "per thread proc mmap processing timeout in ms"), OPT_END() }; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 70a9505aae83..619a8696fda7 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -977,7 +977,7 @@ static int __cmd_top(struct perf_top *top) goto out_delete; machine__synthesize_threads(&top->session->machines.host, &opts->target, - top->evlist->threads, false); + top->evlist->threads, false, opts->proc_map_timeout); ret = perf_top__start_counters(top); if (ret) goto out_delete; @@ -1087,6 +1087,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) .target = { .uses_mmap = true, }, + .proc_map_timeout = 500, }, .max_stack = PERF_MAX_STACK_DEPTH, .sym_pcnt_filter = 5, @@ -1186,6 +1187,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str, "width[,width...]", "don't try to adjust column width, use these fixed values"), + OPT_UINTEGER(0, "proc-map-timeout", &opts->proc_map_timeout, + "per thread proc mmap processing timeout in ms"), OPT_END() }; const char * const top_usage[] = { diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 4bf805b2fbf6..de5d277d1ad7 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1518,7 +1518,8 @@ static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) return -ENOMEM; err = __machine__synthesize_threads(trace->host, &trace->tool, &trace->opts.target, - evlist->threads, trace__tool_process, false); + evlist->threads, trace__tool_process, false, + trace->opts.proc_map_timeout); if (err) symbol__exit(); @@ -2747,6 +2748,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) .user_interval = ULLONG_MAX, .no_buffering = true, .mmap_pages = UINT_MAX, + .proc_map_timeout = 500, }, .output = stdout, .show_comm = true, @@ -2796,6 +2798,8 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) "Trace pagefaults", parse_pagefaults, "maj"), OPT_BOOLEAN(0, "syscalls", &trace.trace_syscalls, "Trace syscalls"), OPT_BOOLEAN('f', "force", &trace.force, "don't complain, do it"), + OPT_UINTEGER(0, "proc-map-timeout", &trace.opts.proc_map_timeout, + "per thread proc mmap processing timeout in ms"), OPT_END() }; const char * const trace_subcommands[] = { "record", NULL }; diff --git a/tools/perf/perf.h b/tools/perf/perf.h index aa79fb8a16d4..4a5827fff799 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -69,6 +69,7 @@ struct record_opts { unsigned initial_delay; bool use_clockid; clockid_t clockid; + unsigned int proc_map_timeout; }; struct option; diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index e2a432b67d52..22f8a00446e1 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -451,7 +451,7 @@ static int do_test_code_reading(bool try_kcore) } ret = perf_event__synthesize_thread_map(NULL, threads, - perf_event__process, machine, false); + perf_event__process, machine, false, 500); if (ret < 0) { pr_debug("perf_event__synthesize_thread_map failed\n"); goto out_err; diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index 9b748e1ad46e..40b36c462427 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c @@ -28,7 +28,7 @@ static int init_live_machine(struct machine *machine) pid_t pid = getpid(); return perf_event__synthesize_mmap_events(NULL, &event, pid, pid, - mmap_handler, machine, true); + mmap_handler, machine, true, 500); } #define MAX_STACK 8 diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c index 264e215c0d36..7f48efa7e295 100644 --- a/tools/perf/tests/mmap-thread-lookup.c +++ b/tools/perf/tests/mmap-thread-lookup.c @@ -129,7 +129,7 @@ static int synth_all(struct machine *machine) { return perf_event__synthesize_threads(NULL, perf_event__process, - machine, 0); + machine, 0, 500); } static int synth_process(struct machine *machine) @@ -141,7 +141,7 @@ static int synth_process(struct machine *machine) err = perf_event__synthesize_thread_map(NULL, map, perf_event__process, - machine, 0); + machine, 0, 500); thread_map__delete(map); return err; diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 416ba80c628f..d7d986d8f23e 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -213,19 +213,19 @@ static int perf_event__synthesize_fork(struct perf_tool *tool, return 0; } -#define PROC_MAP_PARSE_TIMEOUT (500 * 1000000ULL) - int perf_event__synthesize_mmap_events(struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, perf_event__handler_t process, struct machine *machine, - bool mmap_data) + bool mmap_data, + unsigned int proc_map_timeout) { char filename[PATH_MAX]; FILE *fp; unsigned long long t; bool truncation = false; + unsigned long long timeout = proc_map_timeout * 1000000ULL; int rc = 0; if (machine__is_default_guest(machine)) @@ -258,8 +258,11 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, if (fgets(bf, sizeof(bf), fp) == NULL) break; - if ((rdclock() - t) > PROC_MAP_PARSE_TIMEOUT) { - pr_warning("Reading %s time out.\n", filename); + if ((rdclock() - t) > timeout) { + pr_warning("Reading %s time out. " + "You may want to increase " + "the time limit by --proc-map-timeout\n", + filename); truncation = true; goto out; } @@ -404,7 +407,9 @@ static int __event__synthesize_thread(union perf_event *comm_event, pid_t pid, int full, perf_event__handler_t process, struct perf_tool *tool, - struct machine *machine, bool mmap_data) + struct machine *machine, + bool mmap_data, + unsigned int proc_map_timeout) { char filename[PATH_MAX]; DIR *tasks; @@ -421,7 +426,8 @@ static int __event__synthesize_thread(union perf_event *comm_event, return -1; return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, - process, machine, mmap_data); + process, machine, mmap_data, + proc_map_timeout); } if (machine__is_default_guest(machine)) @@ -462,7 +468,7 @@ static int __event__synthesize_thread(union perf_event *comm_event, if (_pid == pid) { /* process the parent's maps too */ rc = perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, - process, machine, mmap_data); + process, machine, mmap_data, proc_map_timeout); if (rc) break; } @@ -476,7 +482,8 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, struct thread_map *threads, perf_event__handler_t process, struct machine *machine, - bool mmap_data) + bool mmap_data, + unsigned int proc_map_timeout) { union perf_event *comm_event, *mmap_event, *fork_event; int err = -1, thread, j; @@ -499,7 +506,7 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, fork_event, threads->map[thread], 0, process, tool, machine, - mmap_data)) { + mmap_data, proc_map_timeout)) { err = -1; break; } @@ -525,7 +532,7 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, fork_event, comm_event->comm.pid, 0, process, tool, machine, - mmap_data)) { + mmap_data, proc_map_timeout)) { err = -1; break; } @@ -542,7 +549,9 @@ out: int perf_event__synthesize_threads(struct perf_tool *tool, perf_event__handler_t process, - struct machine *machine, bool mmap_data) + struct machine *machine, + bool mmap_data, + unsigned int proc_map_timeout) { DIR *proc; char proc_path[PATH_MAX]; @@ -582,7 +591,8 @@ int perf_event__synthesize_threads(struct perf_tool *tool, * one thread couldn't be synthesized. */ __event__synthesize_thread(comm_event, mmap_event, fork_event, pid, - 1, process, tool, machine, mmap_data); + 1, process, tool, machine, mmap_data, + proc_map_timeout); } err = 0; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 39868f529cab..c53f36384b64 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -384,10 +384,12 @@ typedef int (*perf_event__handler_t)(struct perf_tool *tool, int perf_event__synthesize_thread_map(struct perf_tool *tool, struct thread_map *threads, perf_event__handler_t process, - struct machine *machine, bool mmap_data); + struct machine *machine, bool mmap_data, + unsigned int proc_map_timeout); int perf_event__synthesize_threads(struct perf_tool *tool, perf_event__handler_t process, - struct machine *machine, bool mmap_data); + struct machine *machine, bool mmap_data, + unsigned int proc_map_timeout); int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); @@ -469,7 +471,8 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, pid_t pid, pid_t tgid, perf_event__handler_t process, struct machine *machine, - bool mmap_data); + bool mmap_data, + unsigned int proc_map_timeout); size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp); size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp); diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 8b3b1937cb9e..4744673aff1b 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1913,12 +1913,13 @@ int machines__for_each_thread(struct machines *machines, int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, struct target *target, struct thread_map *threads, - perf_event__handler_t process, bool data_mmap) + perf_event__handler_t process, bool data_mmap, + unsigned int proc_map_timeout) { if (target__has_task(target)) - return perf_event__synthesize_thread_map(tool, threads, process, machine, data_mmap); + return perf_event__synthesize_thread_map(tool, threads, process, machine, data_mmap, proc_map_timeout); else if (target__has_cpu(target)) - return perf_event__synthesize_threads(tool, process, machine, data_mmap); + return perf_event__synthesize_threads(tool, process, machine, data_mmap, proc_map_timeout); /* command specified */ return 0; } diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index cea62f6fb144..887798e511e9 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -222,13 +222,16 @@ int machines__for_each_thread(struct machines *machines, int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, struct target *target, struct thread_map *threads, - perf_event__handler_t process, bool data_mmap); + perf_event__handler_t process, bool data_mmap, + unsigned int proc_map_timeout); static inline int machine__synthesize_threads(struct machine *machine, struct target *target, - struct thread_map *threads, bool data_mmap) + struct thread_map *threads, bool data_mmap, + unsigned int proc_map_timeout) { return __machine__synthesize_threads(machine, NULL, target, threads, - perf_event__process, data_mmap); + perf_event__process, data_mmap, + proc_map_timeout); } pid_t machine__get_current_tid(struct machine *machine, int cpu); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 2d882fd1f1b9..aa482c10469d 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1368,7 +1368,9 @@ static void perf_session__warn_about_errors(const struct perf_session *session) "not processed, if there are samples for addresses they\n" "will not be resolved, you may find out which are these\n" "threads by running with -v and redirecting the output\n" - "to a file.\n", + "to a file.\n" + "The time limit to process proc map is too short?\n" + "Increase it by --proc-map-timeout\n", stats->nr_proc_map_timeout); } } -- cgit v1.2.3-59-g8ed1b