perf session: Remove threads from tree on PERF_RECORD_EXIT

Move them to a session->dead_threads list just like we do with maps that
are replaced, because we may have hist_entries pointing to them.

This fixes a bug when inserting maps for a new thread that reused the
TID, mixing maps for two different threads, causing an endless loop.

The code for insering maps should be made more robust but for .35 this
is the minimalistic patch.

Reported-by: Ingo Molnar <mingo@elte.hu>
Cc: David S. Miller <davem@davemloft.net>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Tom Zanussi <tzanussi@gmail.com>
LKML-Reference: <new-submission>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

+20 -2
+3 -1
tools/perf/util/event.c
··· 538 dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, 539 self->fork.ppid, self->fork.ptid); 540 541 - if (self->header.type == PERF_RECORD_EXIT) 542 return 0; 543 544 if (thread == NULL || parent == NULL || 545 thread__fork(thread, parent) < 0) {
··· 538 dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, 539 self->fork.ppid, self->fork.ptid); 540 541 + if (self->header.type == PERF_RECORD_EXIT) { 542 + perf_session__remove_thread(session, thread); 543 return 0; 544 + } 545 546 if (thread == NULL || parent == NULL || 547 thread__fork(thread, parent) < 0) {
+11
tools/perf/util/session.c
··· 90 91 memcpy(self->filename, filename, len); 92 self->threads = RB_ROOT; 93 self->hists_tree = RB_ROOT; 94 self->last_match = NULL; 95 self->mmap_window = 32; ··· 130 close(self->fd); 131 free(self->cwd); 132 free(self); 133 } 134 135 static bool symbol__match_parent_regex(struct symbol *sym)
··· 90 91 memcpy(self->filename, filename, len); 92 self->threads = RB_ROOT; 93 + INIT_LIST_HEAD(&self->dead_threads); 94 self->hists_tree = RB_ROOT; 95 self->last_match = NULL; 96 self->mmap_window = 32; ··· 129 close(self->fd); 130 free(self->cwd); 131 free(self); 132 + } 133 + 134 + void perf_session__remove_thread(struct perf_session *self, struct thread *th) 135 + { 136 + rb_erase(&th->rb_node, &self->threads); 137 + /* 138 + * We may have references to this thread, for instance in some hist_entry 139 + * instances, so just move them to a separate list. 140 + */ 141 + list_add_tail(&th->node, &self->dead_threads); 142 } 143 144 static bool symbol__match_parent_regex(struct symbol *sym)
+2
tools/perf/util/session.h
··· 26 unsigned long size; 27 unsigned long mmap_window; 28 struct rb_root threads; 29 struct thread *last_match; 30 struct machine host_machine; 31 struct rb_root machines; ··· 100 101 int do_read(int fd, void *buf, size_t size); 102 void perf_session__update_sample_type(struct perf_session *self); 103 104 static inline 105 struct machine *perf_session__find_host_machine(struct perf_session *self)
··· 26 unsigned long size; 27 unsigned long mmap_window; 28 struct rb_root threads; 29 + struct list_head dead_threads; 30 struct thread *last_match; 31 struct machine host_machine; 32 struct rb_root machines; ··· 99 100 int do_read(int fd, void *buf, size_t size); 101 void perf_session__update_sample_type(struct perf_session *self); 102 + void perf_session__remove_thread(struct perf_session *self, struct thread *th); 103 104 static inline 105 struct machine *perf_session__find_host_machine(struct perf_session *self)
+4 -1
tools/perf/util/thread.h
··· 6 #include "symbol.h" 7 8 struct thread { 9 - struct rb_node rb_node; 10 struct map_groups mg; 11 pid_t pid; 12 char shortname[3];
··· 6 #include "symbol.h" 7 8 struct thread { 9 + union { 10 + struct rb_node rb_node; 11 + struct list_head node; 12 + }; 13 struct map_groups mg; 14 pid_t pid; 15 char shortname[3];