perf session: Remove threads from tree on PERF_RECORD_EXIT
authorArnaldo Carvalho de Melo <acme@redhat.com>
Thu, 17 Jun 2010 11:37:44 +0000 (08:37 -0300)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Thu, 17 Jun 2010 11:37:44 +0000 (08:37 -0300)
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>
tools/perf/util/event.c
tools/perf/util/session.c
tools/perf/util/session.h
tools/perf/util/thread.h

index 1f08f008d289f841f7ae0089462129f97e313afc..2fbf6a463c8174e1fdf56e6714a2e402987c85f4 100644 (file)
@@ -538,8 +538,10 @@ int event__process_task(event_t *self, struct perf_session *session)
        dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
                    self->fork.ppid, self->fork.ptid);
 
-       if (self->header.type == PERF_RECORD_EXIT)
+       if (self->header.type == PERF_RECORD_EXIT) {
+               perf_session__remove_thread(session, thread);
                return 0;
+       }
 
        if (thread == NULL || parent == NULL ||
            thread__fork(thread, parent) < 0) {
index 8f83a1835766e5d1a6e509547d14559529bbba23..c422cd6763135c10575f8621165262b48a1315a3 100644 (file)
@@ -90,6 +90,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc
 
        memcpy(self->filename, filename, len);
        self->threads = RB_ROOT;
+       INIT_LIST_HEAD(&self->dead_threads);
        self->hists_tree = RB_ROOT;
        self->last_match = NULL;
        self->mmap_window = 32;
@@ -131,6 +132,16 @@ void perf_session__delete(struct perf_session *self)
        free(self);
 }
 
+void perf_session__remove_thread(struct perf_session *self, struct thread *th)
+{
+       rb_erase(&th->rb_node, &self->threads);
+       /*
+        * We may have references to this thread, for instance in some hist_entry
+        * instances, so just move them to a separate list.
+        */
+       list_add_tail(&th->node, &self->dead_threads);
+}
+
 static bool symbol__match_parent_regex(struct symbol *sym)
 {
        if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
index 55c6881b218d4ccee588877d54afb41120dba521..9fa0fc2a863f1259caf8caf7494f867d0737a297 100644 (file)
@@ -26,6 +26,7 @@ struct perf_session {
        unsigned long           size;
        unsigned long           mmap_window;
        struct rb_root          threads;
+       struct list_head        dead_threads;
        struct thread           *last_match;
        struct machine          host_machine;
        struct rb_root          machines;
@@ -99,6 +100,7 @@ int perf_session__create_kernel_maps(struct perf_session *self);
 
 int do_read(int fd, void *buf, size_t size);
 void perf_session__update_sample_type(struct perf_session *self);
+void perf_session__remove_thread(struct perf_session *self, struct thread *th);
 
 static inline
 struct machine *perf_session__find_host_machine(struct perf_session *self)
index 1dfd9ff8bdcdfc7d290c9e9f686875cd107dd4d7..ee6bbcf277cad88ea4143a8d7a6a9e21cc07a74c 100644 (file)
@@ -6,7 +6,10 @@
 #include "symbol.h"
 
 struct thread {
-       struct rb_node          rb_node;
+       union {
+               struct rb_node   rb_node;
+               struct list_head node;
+       };
        struct map_groups       mg;
        pid_t                   pid;
        char                    shortname[3];