diff options
Diffstat (limited to 'tools/perf/util/thread.c')
| -rw-r--r-- | tools/perf/util/thread.c | 250 | 
1 files changed, 187 insertions, 63 deletions
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 45efb5db0d1..603f5610861 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -6,16 +6,29 @@  #include "util.h"  #include "debug.h" +static struct rb_root threads; +static struct thread *last_match; + +void thread__init(struct thread *self, pid_t pid) +{ +	int i; +	self->pid = pid; +	self->comm = NULL; +	for (i = 0; i < MAP__NR_TYPES; ++i) { +		self->maps[i] = RB_ROOT; +		INIT_LIST_HEAD(&self->removed_maps[i]); +	} +} +  static struct thread *thread__new(pid_t pid)  { -	struct thread *self = calloc(1, sizeof(*self)); +	struct thread *self = zalloc(sizeof(*self));  	if (self != NULL) { -		self->pid = pid; +		thread__init(self, pid);  		self->comm = malloc(32);  		if (self->comm)  			snprintf(self->comm, 32, ":%d", self->pid); -		INIT_LIST_HEAD(&self->maps);  	}  	return self; @@ -29,21 +42,84 @@ int thread__set_comm(struct thread *self, const char *comm)  	return self->comm ? 0 : -ENOMEM;  } -static size_t thread__fprintf(struct thread *self, FILE *fp) +int thread__comm_len(struct thread *self) +{ +	if (!self->comm_len) { +		if (!self->comm) +			return 0; +		self->comm_len = strlen(self->comm); +	} + +	return self->comm_len; +} + +static const char *map_type__name[MAP__NR_TYPES] = { +	[MAP__FUNCTION] = "Functions", +}; + +static size_t __thread__fprintf_maps(struct thread *self, +				     enum map_type type, FILE *fp) +{ +	size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); +	struct rb_node *nd; + +	for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { +		struct map *pos = rb_entry(nd, struct map, rb_node); +		printed += fprintf(fp, "Map:"); +		printed += map__fprintf(pos, fp); +		if (verbose > 1) { +			printed += dso__fprintf(pos->dso, type, fp); +			printed += fprintf(fp, "--\n"); +		} +	} + +	return printed; +} + +size_t thread__fprintf_maps(struct thread *self, FILE *fp) +{ +	size_t printed = 0, i; +	for (i = 0; i < MAP__NR_TYPES; ++i) +		printed += __thread__fprintf_maps(self, i, fp); +	return printed; +} + +static size_t __thread__fprintf_removed_maps(struct thread *self, +					     enum map_type type, FILE *fp)  {  	struct map *pos; -	size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm); +	size_t printed = 0; -	list_for_each_entry(pos, &self->maps, node) -		ret += map__fprintf(pos, fp); +	list_for_each_entry(pos, &self->removed_maps[type], node) { +		printed += fprintf(fp, "Map:"); +		printed += map__fprintf(pos, fp); +		if (verbose > 1) { +			printed += dso__fprintf(pos->dso, type, fp); +			printed += fprintf(fp, "--\n"); +		} +	} +	return printed; +} -	return ret; +static size_t thread__fprintf_removed_maps(struct thread *self, FILE *fp) +{ +	size_t printed = 0, i; +	for (i = 0; i < MAP__NR_TYPES; ++i) +		printed += __thread__fprintf_removed_maps(self, i, fp); +	return printed;  } -struct thread * -threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match) +static size_t thread__fprintf(struct thread *self, FILE *fp)  { -	struct rb_node **p = &threads->rb_node; +	size_t printed = fprintf(fp, "Thread %d %s\n", self->pid, self->comm); +	printed += thread__fprintf_removed_maps(self, fp); +	printed += fprintf(fp, "Removed maps:\n"); +	return printed + thread__fprintf_removed_maps(self, fp); +} + +struct thread *threads__findnew(pid_t pid) +{ +	struct rb_node **p = &threads.rb_node;  	struct rb_node *parent = NULL;  	struct thread *th; @@ -52,15 +128,15 @@ threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match)  	 * so most of the time we dont have to look up  	 * the full rbtree:  	 */ -	if (*last_match && (*last_match)->pid == pid) -		return *last_match; +	if (last_match && last_match->pid == pid) +		return last_match;  	while (*p != NULL) {  		parent = *p;  		th = rb_entry(parent, struct thread, rb_node);  		if (th->pid == pid) { -			*last_match = th; +			last_match = th;  			return th;  		} @@ -73,17 +149,16 @@ threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match)  	th = thread__new(pid);  	if (th != NULL) {  		rb_link_node(&th->rb_node, parent, p); -		rb_insert_color(&th->rb_node, threads); -		*last_match = th; +		rb_insert_color(&th->rb_node, &threads); +		last_match = th;  	}  	return th;  } -struct thread * -register_idle_thread(struct rb_root *threads, struct thread **last_match) +struct thread *register_idle_thread(void)  { -	struct thread *thread = threads__findnew(0, threads, last_match); +	struct thread *thread = threads__findnew(0);  	if (!thread || thread__set_comm(thread, "swapper")) {  		fprintf(stderr, "problem inserting idle task.\n"); @@ -93,79 +168,116 @@ register_idle_thread(struct rb_root *threads, struct thread **last_match)  	return thread;  } -void thread__insert_map(struct thread *self, struct map *map) +static void thread__remove_overlappings(struct thread *self, struct map *map)  { -	struct map *pos, *tmp; +	struct rb_root *root = &self->maps[map->type]; +	struct rb_node *next = rb_first(root); -	list_for_each_entry_safe(pos, tmp, &self->maps, node) { -		if (map__overlap(pos, map)) { -			if (verbose >= 2) { -				printf("overlapping maps:\n"); -				map__fprintf(map, stdout); -				map__fprintf(pos, stdout); -			} +	while (next) { +		struct map *pos = rb_entry(next, struct map, rb_node); +		next = rb_next(&pos->rb_node); -			if (map->start <= pos->start && map->end > pos->start) -				pos->start = map->end; +		if (!map__overlap(pos, map)) +			continue; -			if (map->end >= pos->end && map->start < pos->end) -				pos->end = map->start; +		if (verbose >= 2) { +			fputs("overlapping maps:\n", stderr); +			map__fprintf(map, stderr); +			map__fprintf(pos, stderr); +		} -			if (verbose >= 2) { -				printf("after collision:\n"); -				map__fprintf(pos, stdout); -			} +		rb_erase(&pos->rb_node, root); +		/* +		 * 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[map->type]); +	} +} -			if (pos->start >= pos->end) { -				list_del_init(&pos->node); -				free(pos); -			} -		} +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);  } -int thread__fork(struct thread *self, struct thread *parent) +struct map *maps__find(struct rb_root *maps, u64 ip)  { -	struct map *map; +	struct rb_node **p = &maps->rb_node; +	struct rb_node *parent = NULL; +	struct map *m; -	if (self->comm) -		free(self->comm); -	self->comm = strdup(parent->comm); -	if (!self->comm) -		return -ENOMEM; +	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->type], map); +} -	list_for_each_entry(map, &parent->maps, node) { +static int thread__clone_maps(struct thread *self, struct thread *parent, +			      enum map_type type) +{ +	struct rb_node *nd; +	for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { +		struct map *map = rb_entry(nd, struct map, rb_node);  		struct map *new = map__clone(map); -		if (!new) +		if (new == NULL)  			return -ENOMEM;  		thread__insert_map(self, new);  	} -  	return 0;  } -struct map *thread__find_map(struct thread *self, u64 ip) +int thread__fork(struct thread *self, struct thread *parent)  { -	struct map *pos; +	int i; -	if (self == NULL) -		return NULL; - -	list_for_each_entry(pos, &self->maps, node) -		if (ip >= pos->start && ip <= pos->end) -			return pos; +	if (self->comm) +		free(self->comm); +	self->comm = strdup(parent->comm); +	if (!self->comm) +		return -ENOMEM; -	return NULL; +	for (i = 0; i < MAP__NR_TYPES; ++i) +		if (thread__clone_maps(self, parent, i) < 0) +			return -ENOMEM; +	return 0;  } -size_t threads__fprintf(FILE *fp, struct rb_root *threads) +size_t threads__fprintf(FILE *fp)  {  	size_t ret = 0;  	struct rb_node *nd; -	for (nd = rb_first(threads); nd; nd = rb_next(nd)) { +	for (nd = rb_first(&threads); nd; nd = rb_next(nd)) {  		struct thread *pos = rb_entry(nd, struct thread, rb_node);  		ret += thread__fprintf(pos, fp); @@ -173,3 +285,15 @@ size_t threads__fprintf(FILE *fp, struct rb_root *threads)  	return ret;  } + +struct symbol *thread__find_symbol(struct thread *self, +				   enum map_type type, u64 addr, +				   symbol_filter_t filter) +{ +	struct map *map = thread__find_map(self, type, addr); + +	if (map != NULL) +		return map__find_symbol(map, map->map_ip(map, addr), filter); + +	return NULL; +}  |