diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2015-03-02 20:21:35 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2015-03-02 22:17:08 -0500 |
commit | f3b623b8490af7a9b819cbcf2d99ab4597ece94b (patch) | |
tree | 13926b268a431ab99d8518b0b9c9d4c7c124c188 /tools | |
parent | 2ed11312eb19506c027e7cac039994ad42a9cb2c (diff) |
perf tools: Reference count struct thread
We need to do that to stop accumulating entries in the dead_threads
linked list, i.e. we were keeping references to threads in struct hists
that continue to exist even after a thread exited and was removed from
the machine threads rbtree.
We still keep the dead_threads list, but just for debugging, allowing us
to iterate at any given point over the threads that still are referenced
by things like struct hist_entry.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Borislav Petkov <bp@suse.de>
Cc: David Ahern <dsahern@gmail.com>
Cc: Don Zickus <dzickus@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-3ejvfyed0r7ue61dkurzjux4@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/perf/builtin-sched.c | 2 | ||||
-rw-r--r-- | tools/perf/builtin-trace.c | 7 | ||||
-rw-r--r-- | tools/perf/ui/browsers/hists.c | 6 | ||||
-rw-r--r-- | tools/perf/util/build-id.c | 5 | ||||
-rw-r--r-- | tools/perf/util/hist.c | 2 | ||||
-rw-r--r-- | tools/perf/util/hist.h | 2 | ||||
-rw-r--r-- | tools/perf/util/machine.c | 44 | ||||
-rw-r--r-- | tools/perf/util/machine.h | 1 | ||||
-rw-r--r-- | tools/perf/util/session.c | 6 | ||||
-rw-r--r-- | tools/perf/util/thread.c | 14 | ||||
-rw-r--r-- | tools/perf/util/thread.h | 13 |
11 files changed, 66 insertions, 36 deletions
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 7ce296618717..e00e2eaf89da 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -831,7 +831,7 @@ static int thread_atoms_insert(struct perf_sched *sched, struct thread *thread) | |||
831 | return -1; | 831 | return -1; |
832 | } | 832 | } |
833 | 833 | ||
834 | atoms->thread = thread; | 834 | atoms->thread = thread__get(thread); |
835 | INIT_LIST_HEAD(&atoms->work_list); | 835 | INIT_LIST_HEAD(&atoms->work_list); |
836 | __thread_latency_insert(&sched->atom_root, atoms, &sched->cmp_pid); | 836 | __thread_latency_insert(&sched->atom_root, atoms, &sched->cmp_pid); |
837 | return 0; | 837 | return 0; |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index d95a8f4d988c..211614fba217 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -1741,7 +1741,10 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
1741 | } else | 1741 | } else |
1742 | ttrace->entry_pending = true; | 1742 | ttrace->entry_pending = true; |
1743 | 1743 | ||
1744 | trace->current = thread; | 1744 | if (trace->current != thread) { |
1745 | thread__put(trace->current); | ||
1746 | trace->current = thread__get(thread); | ||
1747 | } | ||
1745 | 1748 | ||
1746 | return 0; | 1749 | return 0; |
1747 | } | 1750 | } |
@@ -2274,6 +2277,8 @@ next_event: | |||
2274 | } | 2277 | } |
2275 | 2278 | ||
2276 | out_disable: | 2279 | out_disable: |
2280 | thread__zput(trace->current); | ||
2281 | |||
2277 | perf_evlist__disable(evlist); | 2282 | perf_evlist__disable(evlist); |
2278 | 2283 | ||
2279 | if (!err) { | 2284 | if (!err) { |
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 788506eef567..ad312d91caed 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
@@ -1467,7 +1467,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1467 | perf_hpp__set_user_width(symbol_conf.col_width_list_str); | 1467 | perf_hpp__set_user_width(symbol_conf.col_width_list_str); |
1468 | 1468 | ||
1469 | while (1) { | 1469 | while (1) { |
1470 | const struct thread *thread = NULL; | 1470 | struct thread *thread = NULL; |
1471 | const struct dso *dso = NULL; | 1471 | const struct dso *dso = NULL; |
1472 | int choice = 0, | 1472 | int choice = 0, |
1473 | annotate = -2, zoom_dso = -2, zoom_thread = -2, | 1473 | annotate = -2, zoom_dso = -2, zoom_thread = -2, |
@@ -1754,13 +1754,13 @@ zoom_thread: | |||
1754 | pstack__remove(fstack, &browser->hists->thread_filter); | 1754 | pstack__remove(fstack, &browser->hists->thread_filter); |
1755 | zoom_out_thread: | 1755 | zoom_out_thread: |
1756 | ui_helpline__pop(); | 1756 | ui_helpline__pop(); |
1757 | browser->hists->thread_filter = NULL; | 1757 | thread__zput(browser->hists->thread_filter); |
1758 | perf_hpp__set_elide(HISTC_THREAD, false); | 1758 | perf_hpp__set_elide(HISTC_THREAD, false); |
1759 | } else { | 1759 | } else { |
1760 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", | 1760 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", |
1761 | thread->comm_set ? thread__comm_str(thread) : "", | 1761 | thread->comm_set ? thread__comm_str(thread) : "", |
1762 | thread->tid); | 1762 | thread->tid); |
1763 | browser->hists->thread_filter = thread; | 1763 | browser->hists->thread_filter = thread__get(thread); |
1764 | perf_hpp__set_elide(HISTC_THREAD, false); | 1764 | perf_hpp__set_elide(HISTC_THREAD, false); |
1765 | pstack__push(fstack, &browser->hists->thread_filter); | 1765 | pstack__push(fstack, &browser->hists->thread_filter); |
1766 | } | 1766 | } |
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index ffdc338df925..a19674666b4e 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -61,8 +61,9 @@ static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused, | |||
61 | 61 | ||
62 | if (thread) { | 62 | if (thread) { |
63 | rb_erase(&thread->rb_node, &machine->threads); | 63 | rb_erase(&thread->rb_node, &machine->threads); |
64 | machine->last_match = NULL; | 64 | if (machine->last_match == thread) |
65 | thread__delete(thread); | 65 | thread__zput(machine->last_match); |
66 | thread__put(thread); | ||
66 | } | 67 | } |
67 | 68 | ||
68 | return 0; | 69 | return 0; |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 70b48a65064c..95f5ab707b74 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -355,6 +355,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template, | |||
355 | callchain_init(he->callchain); | 355 | callchain_init(he->callchain); |
356 | 356 | ||
357 | INIT_LIST_HEAD(&he->pairs.node); | 357 | INIT_LIST_HEAD(&he->pairs.node); |
358 | thread__get(he->thread); | ||
358 | } | 359 | } |
359 | 360 | ||
360 | return he; | 361 | return he; |
@@ -941,6 +942,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
941 | 942 | ||
942 | void hist_entry__delete(struct hist_entry *he) | 943 | void hist_entry__delete(struct hist_entry *he) |
943 | { | 944 | { |
945 | thread__zput(he->thread); | ||
944 | zfree(&he->branch_info); | 946 | zfree(&he->branch_info); |
945 | zfree(&he->mem_info); | 947 | zfree(&he->mem_info); |
946 | zfree(&he->stat_acc); | 948 | zfree(&he->stat_acc); |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 2b690d028907..e988c9fcd1bc 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -60,7 +60,7 @@ struct hists { | |||
60 | struct rb_root entries_collapsed; | 60 | struct rb_root entries_collapsed; |
61 | u64 nr_entries; | 61 | u64 nr_entries; |
62 | u64 nr_non_filtered_entries; | 62 | u64 nr_non_filtered_entries; |
63 | const struct thread *thread_filter; | 63 | struct thread *thread_filter; |
64 | const struct dso *dso_filter; | 64 | const struct dso *dso_filter; |
65 | const char *uid_filter_str; | 65 | const char *uid_filter_str; |
66 | const char *symbol_filter_str; | 66 | const char *symbol_filter_str; |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 9e0f60a7e7b3..24f8c978cfd4 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -14,6 +14,8 @@ | |||
14 | #include "unwind.h" | 14 | #include "unwind.h" |
15 | #include "linux/hash.h" | 15 | #include "linux/hash.h" |
16 | 16 | ||
17 | static void machine__remove_thread(struct machine *machine, struct thread *th); | ||
18 | |||
17 | static void dsos__init(struct dsos *dsos) | 19 | static void dsos__init(struct dsos *dsos) |
18 | { | 20 | { |
19 | INIT_LIST_HEAD(&dsos->head); | 21 | INIT_LIST_HEAD(&dsos->head); |
@@ -89,16 +91,6 @@ static void dsos__delete(struct dsos *dsos) | |||
89 | } | 91 | } |
90 | } | 92 | } |
91 | 93 | ||
92 | void machine__delete_dead_threads(struct machine *machine) | ||
93 | { | ||
94 | struct thread *n, *t; | ||
95 | |||
96 | list_for_each_entry_safe(t, n, &machine->dead_threads, node) { | ||
97 | list_del(&t->node); | ||
98 | thread__delete(t); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | void machine__delete_threads(struct machine *machine) | 94 | void machine__delete_threads(struct machine *machine) |
103 | { | 95 | { |
104 | struct rb_node *nd = rb_first(&machine->threads); | 96 | struct rb_node *nd = rb_first(&machine->threads); |
@@ -106,9 +98,8 @@ void machine__delete_threads(struct machine *machine) | |||
106 | while (nd) { | 98 | while (nd) { |
107 | struct thread *t = rb_entry(nd, struct thread, rb_node); | 99 | struct thread *t = rb_entry(nd, struct thread, rb_node); |
108 | 100 | ||
109 | rb_erase(&t->rb_node, &machine->threads); | ||
110 | nd = rb_next(nd); | 101 | nd = rb_next(nd); |
111 | thread__delete(t); | 102 | machine__remove_thread(machine, t); |
112 | } | 103 | } |
113 | } | 104 | } |
114 | 105 | ||
@@ -361,9 +352,13 @@ static struct thread *__machine__findnew_thread(struct machine *machine, | |||
361 | * the full rbtree: | 352 | * the full rbtree: |
362 | */ | 353 | */ |
363 | th = machine->last_match; | 354 | th = machine->last_match; |
364 | if (th && th->tid == tid) { | 355 | if (th != NULL) { |
365 | machine__update_thread_pid(machine, th, pid); | 356 | if (th->tid == tid) { |
366 | return th; | 357 | machine__update_thread_pid(machine, th, pid); |
358 | return th; | ||
359 | } | ||
360 | |||
361 | thread__zput(machine->last_match); | ||
367 | } | 362 | } |
368 | 363 | ||
369 | while (*p != NULL) { | 364 | while (*p != NULL) { |
@@ -371,7 +366,7 @@ static struct thread *__machine__findnew_thread(struct machine *machine, | |||
371 | th = rb_entry(parent, struct thread, rb_node); | 366 | th = rb_entry(parent, struct thread, rb_node); |
372 | 367 | ||
373 | if (th->tid == tid) { | 368 | if (th->tid == tid) { |
374 | machine->last_match = th; | 369 | machine->last_match = thread__get(th); |
375 | machine__update_thread_pid(machine, th, pid); | 370 | machine__update_thread_pid(machine, th, pid); |
376 | return th; | 371 | return th; |
377 | } | 372 | } |
@@ -403,8 +398,11 @@ static struct thread *__machine__findnew_thread(struct machine *machine, | |||
403 | thread__delete(th); | 398 | thread__delete(th); |
404 | return NULL; | 399 | return NULL; |
405 | } | 400 | } |
406 | 401 | /* | |
407 | machine->last_match = th; | 402 | * It is now in the rbtree, get a ref |
403 | */ | ||
404 | thread__get(th); | ||
405 | machine->last_match = thread__get(th); | ||
408 | } | 406 | } |
409 | 407 | ||
410 | return th; | 408 | return th; |
@@ -1238,13 +1236,17 @@ out_problem: | |||
1238 | 1236 | ||
1239 | static void machine__remove_thread(struct machine *machine, struct thread *th) | 1237 | static void machine__remove_thread(struct machine *machine, struct thread *th) |
1240 | { | 1238 | { |
1241 | machine->last_match = NULL; | 1239 | if (machine->last_match == th) |
1240 | thread__zput(machine->last_match); | ||
1241 | |||
1242 | rb_erase(&th->rb_node, &machine->threads); | 1242 | rb_erase(&th->rb_node, &machine->threads); |
1243 | /* | 1243 | /* |
1244 | * We may have references to this thread, for instance in some hist_entry | 1244 | * Move it first to the dead_threads list, then drop the reference, |
1245 | * instances, so just move them to a separate list. | 1245 | * if this is the last reference, then the thread__delete destructor |
1246 | * will be called and we will remove it from the dead_threads list. | ||
1246 | */ | 1247 | */ |
1247 | list_add_tail(&th->node, &machine->dead_threads); | 1248 | list_add_tail(&th->node, &machine->dead_threads); |
1249 | thread__put(th); | ||
1248 | } | 1250 | } |
1249 | 1251 | ||
1250 | int machine__process_fork_event(struct machine *machine, union perf_event *event, | 1252 | int machine__process_fork_event(struct machine *machine, union perf_event *event, |
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index e8b7779a0a3f..e2faf3b47e7b 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
@@ -118,7 +118,6 @@ void machines__set_comm_exec(struct machines *machines, bool comm_exec); | |||
118 | struct machine *machine__new_host(void); | 118 | struct machine *machine__new_host(void); |
119 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); | 119 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); |
120 | void machine__exit(struct machine *machine); | 120 | void machine__exit(struct machine *machine); |
121 | void machine__delete_dead_threads(struct machine *machine); | ||
122 | void machine__delete_threads(struct machine *machine); | 121 | void machine__delete_threads(struct machine *machine); |
123 | void machine__delete(struct machine *machine); | 122 | void machine__delete(struct machine *machine); |
124 | 123 | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index e4f166981ff0..ed4e5cf2bd9d 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -138,11 +138,6 @@ struct perf_session *perf_session__new(struct perf_data_file *file, | |||
138 | return NULL; | 138 | return NULL; |
139 | } | 139 | } |
140 | 140 | ||
141 | static void perf_session__delete_dead_threads(struct perf_session *session) | ||
142 | { | ||
143 | machine__delete_dead_threads(&session->machines.host); | ||
144 | } | ||
145 | |||
146 | static void perf_session__delete_threads(struct perf_session *session) | 141 | static void perf_session__delete_threads(struct perf_session *session) |
147 | { | 142 | { |
148 | machine__delete_threads(&session->machines.host); | 143 | machine__delete_threads(&session->machines.host); |
@@ -167,7 +162,6 @@ static void perf_session_env__delete(struct perf_session_env *env) | |||
167 | void perf_session__delete(struct perf_session *session) | 162 | void perf_session__delete(struct perf_session *session) |
168 | { | 163 | { |
169 | perf_session__destroy_kernel_maps(session); | 164 | perf_session__destroy_kernel_maps(session); |
170 | perf_session__delete_dead_threads(session); | ||
171 | perf_session__delete_threads(session); | 165 | perf_session__delete_threads(session); |
172 | perf_session_env__delete(&session->header.env); | 166 | perf_session_env__delete(&session->header.env); |
173 | machines__exit(&session->machines); | 167 | machines__exit(&session->machines); |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 9ebc8b1f9be5..a5dbba95107f 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -82,6 +82,20 @@ void thread__delete(struct thread *thread) | |||
82 | free(thread); | 82 | free(thread); |
83 | } | 83 | } |
84 | 84 | ||
85 | struct thread *thread__get(struct thread *thread) | ||
86 | { | ||
87 | ++thread->refcnt; | ||
88 | return thread; | ||
89 | } | ||
90 | |||
91 | void thread__put(struct thread *thread) | ||
92 | { | ||
93 | if (thread && --thread->refcnt == 0) { | ||
94 | list_del_init(&thread->node); | ||
95 | thread__delete(thread); | ||
96 | } | ||
97 | } | ||
98 | |||
85 | struct comm *thread__comm(const struct thread *thread) | 99 | struct comm *thread__comm(const struct thread *thread) |
86 | { | 100 | { |
87 | if (list_empty(&thread->comm_list)) | 101 | if (list_empty(&thread->comm_list)) |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 160fd066a7d1..783b6688d2f7 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -20,6 +20,7 @@ struct thread { | |||
20 | pid_t tid; | 20 | pid_t tid; |
21 | pid_t ppid; | 21 | pid_t ppid; |
22 | int cpu; | 22 | int cpu; |
23 | int refcnt; | ||
23 | char shortname[3]; | 24 | char shortname[3]; |
24 | bool comm_set; | 25 | bool comm_set; |
25 | bool dead; /* if set thread has exited */ | 26 | bool dead; /* if set thread has exited */ |
@@ -37,6 +38,18 @@ struct comm; | |||
37 | struct thread *thread__new(pid_t pid, pid_t tid); | 38 | struct thread *thread__new(pid_t pid, pid_t tid); |
38 | int thread__init_map_groups(struct thread *thread, struct machine *machine); | 39 | int thread__init_map_groups(struct thread *thread, struct machine *machine); |
39 | void thread__delete(struct thread *thread); | 40 | void thread__delete(struct thread *thread); |
41 | |||
42 | struct thread *thread__get(struct thread *thread); | ||
43 | void thread__put(struct thread *thread); | ||
44 | |||
45 | static inline void __thread__zput(struct thread **thread) | ||
46 | { | ||
47 | thread__put(*thread); | ||
48 | *thread = NULL; | ||
49 | } | ||
50 | |||
51 | #define thread__zput(thread) __thread__zput(&thread) | ||
52 | |||
40 | static inline void thread__exited(struct thread *thread) | 53 | static inline void thread__exited(struct thread *thread) |
41 | { | 54 | { |
42 | thread->dead = true; | 55 | thread->dead = true; |