perf tools: Improve thread comm resolution in perf sched
[linux-3.10.git] / tools / perf / util / thread.c
index 00c14b9..8bd5ca2 100644 (file)
@@ -4,17 +4,21 @@
 #include <string.h>
 #include "thread.h"
 #include "util.h"
+#include "debug.h"
 
-static struct thread *thread__new(pid_t pid)
+static struct thread *thread__new(pid_t pid, int set_comm)
 {
-       struct thread *self = malloc(sizeof(*self));
+       struct thread *self = calloc(1, sizeof(*self));
 
        if (self != NULL) {
                self->pid = pid;
-               self->comm = malloc(32);
-               if (self->comm)
-                       snprintf(self->comm, 32, ":%d", self->pid);
-               INIT_LIST_HEAD(&self->maps);
+               if (set_comm) {
+                       self->comm = malloc(32);
+                       if (self->comm)
+                               snprintf(self->comm, 32, ":%d", self->pid);
+               }
+               self->maps = RB_ROOT;
+               INIT_LIST_HEAD(&self->removed_maps);
        }
 
        return self;
@@ -30,17 +34,28 @@ int thread__set_comm(struct thread *self, const char *comm)
 
 static size_t thread__fprintf(struct thread *self, FILE *fp)
 {
+       struct rb_node *nd;
        struct map *pos;
-       size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm);
+       size_t ret = fprintf(fp, "Thread %d %s\nCurrent maps:\n",
+                            self->pid, self->comm);
 
-       list_for_each_entry(pos, &self->maps, node)
+       for (nd = rb_first(&self->maps); nd; nd = rb_next(nd)) {
+               pos = rb_entry(nd, struct map, rb_node);
+               ret += map__fprintf(pos, fp);
+       }
+
+       ret = fprintf(fp, "Removed maps:\n");
+
+       list_for_each_entry(pos, &self->removed_maps, node)
                ret += map__fprintf(pos, fp);
 
        return ret;
 }
 
-struct thread *
-threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match)
+static struct thread *
+__threads__findnew(pid_t pid, struct rb_root *threads,
+                  struct thread **last_match,
+                  int set_comm)
 {
        struct rb_node **p = &threads->rb_node;
        struct rb_node *parent = NULL;
@@ -69,7 +84,8 @@ threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match)
                        p = &(*p)->rb_right;
        }
 
-       th = thread__new(pid);
+       th = thread__new(pid, set_comm);
+
        if (th != NULL) {
                rb_link_node(&th->rb_node, parent, p);
                rb_insert_color(&th->rb_node, threads);
@@ -79,24 +95,108 @@ threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match)
        return th;
 }
 
-void thread__insert_map(struct thread *self, struct map *map)
+struct thread *
+threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match)
+{
+       return __threads__findnew(pid, threads, last_match, 1);
+}
+
+struct thread *
+threads__findnew_nocomm(pid_t pid, struct rb_root *threads,
+                       struct thread **last_match)
 {
-       struct map *pos, *tmp;
+       return __threads__findnew(pid, threads, last_match, 0);
+}
 
-       list_for_each_entry_safe(pos, tmp, &self->maps, node) {
-               if (map__overlap(pos, map)) {
-                       list_del_init(&pos->node);
-                       /* XXX leaks dsos */
-                       free(pos);
+struct thread *
+register_idle_thread(struct rb_root *threads, struct thread **last_match)
+{
+       struct thread *thread = threads__findnew(0, threads, last_match);
+
+       if (!thread || thread__set_comm(thread, "swapper")) {
+               fprintf(stderr, "problem inserting idle task.\n");
+               exit(-1);
+       }
+
+       return thread;
+}
+
+static void thread__remove_overlappings(struct thread *self, struct map *map)
+{
+       struct rb_node *next = rb_first(&self->maps);
+
+       while (next) {
+               struct map *pos = rb_entry(next, struct map, rb_node);
+               next = rb_next(&pos->rb_node);
+
+               if (!map__overlap(pos, map))
+                       continue;
+
+               if (verbose >= 2) {
+                       printf("overlapping maps:\n");
+                       map__fprintf(map, stdout);
+                       map__fprintf(pos, stdout);
                }
+
+               rb_erase(&pos->rb_node, &self->maps);
+               /*
+                * We may have references to this map, for instance in some
+                * hist_entry instances, so just move them to a separate
+                * list.
+                */
+               list_add_tail(&pos->node, &self->removed_maps);
+       }
+}
+
+void maps__insert(struct rb_root *maps, struct map *map)
+{
+       struct rb_node **p = &maps->rb_node;
+       struct rb_node *parent = NULL;
+       const u64 ip = map->start;
+       struct map *m;
+
+       while (*p != NULL) {
+               parent = *p;
+               m = rb_entry(parent, struct map, rb_node);
+               if (ip < m->start)
+                       p = &(*p)->rb_left;
+               else
+                       p = &(*p)->rb_right;
        }
 
-       list_add_tail(&map->node, &self->maps);
+       rb_link_node(&map->rb_node, parent, p);
+       rb_insert_color(&map->rb_node, maps);
+}
+
+struct map *maps__find(struct rb_root *maps, u64 ip)
+{
+       struct rb_node **p = &maps->rb_node;
+       struct rb_node *parent = NULL;
+       struct map *m;
+
+       while (*p != NULL) {
+               parent = *p;
+               m = rb_entry(parent, struct map, rb_node);
+               if (ip < m->start)
+                       p = &(*p)->rb_left;
+               else if (ip > m->end)
+                       p = &(*p)->rb_right;
+               else
+                       return m;
+       }
+
+       return NULL;
+}
+
+void thread__insert_map(struct thread *self, struct map *map)
+{
+       thread__remove_overlappings(self, map);
+       maps__insert(&self->maps, map);
 }
 
 int thread__fork(struct thread *self, struct thread *parent)
 {
-       struct map *map;
+       struct rb_node *nd;
 
        if (self->comm)
                free(self->comm);
@@ -104,7 +204,8 @@ int thread__fork(struct thread *self, struct thread *parent)
        if (!self->comm)
                return -ENOMEM;
 
-       list_for_each_entry(map, &parent->maps, node) {
+       for (nd = rb_first(&parent->maps); nd; nd = rb_next(nd)) {
+               struct map *map = rb_entry(nd, struct map, rb_node);
                struct map *new = map__clone(map);
                if (!new)
                        return -ENOMEM;
@@ -114,20 +215,6 @@ int thread__fork(struct thread *self, struct thread *parent)
        return 0;
 }
 
-struct map *thread__find_map(struct thread *self, u64 ip)
-{
-       struct map *pos;
-
-       if (self == NULL)
-               return NULL;
-
-       list_for_each_entry(pos, &self->maps, node)
-               if (ip >= pos->start && ip <= pos->end)
-                       return pos;
-
-       return NULL;
-}
-
 size_t threads__fprintf(FILE *fp, struct rb_root *threads)
 {
        size_t ret = 0;