xref: /openbmc/linux/tools/perf/util/thread.c (revision faa5c5c36ec50bf43e39c7798ce9701e6b002db3)
1 #include "../perf.h"
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include "session.h"
6 #include "thread.h"
7 #include "util.h"
8 #include "debug.h"
9 
10 void map_groups__init(struct map_groups *self)
11 {
12 	int i;
13 	for (i = 0; i < MAP__NR_TYPES; ++i) {
14 		self->maps[i] = RB_ROOT;
15 		INIT_LIST_HEAD(&self->removed_maps[i]);
16 	}
17 }
18 
19 static struct thread *thread__new(pid_t pid)
20 {
21 	struct thread *self = zalloc(sizeof(*self));
22 
23 	if (self != NULL) {
24 		map_groups__init(&self->mg);
25 		self->pid = pid;
26 		self->comm = malloc(32);
27 		if (self->comm)
28 			snprintf(self->comm, 32, ":%d", self->pid);
29 	}
30 
31 	return self;
32 }
33 
34 int thread__set_comm(struct thread *self, const char *comm)
35 {
36 	if (self->comm)
37 		free(self->comm);
38 	self->comm = strdup(comm);
39 	if (self->comm == NULL)
40 		return -ENOMEM;
41 	self->comm_set = true;
42 	return 0;
43 }
44 
45 int thread__comm_len(struct thread *self)
46 {
47 	if (!self->comm_len) {
48 		if (!self->comm)
49 			return 0;
50 		self->comm_len = strlen(self->comm);
51 	}
52 
53 	return self->comm_len;
54 }
55 
56 static const char *map_type__name[MAP__NR_TYPES] = {
57 	[MAP__FUNCTION] = "Functions",
58 	[MAP__VARIABLE] = "Variables",
59 };
60 
61 static size_t __map_groups__fprintf_maps(struct map_groups *self,
62 					 enum map_type type, FILE *fp)
63 {
64 	size_t printed = fprintf(fp, "%s:\n", map_type__name[type]);
65 	struct rb_node *nd;
66 
67 	for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) {
68 		struct map *pos = rb_entry(nd, struct map, rb_node);
69 		printed += fprintf(fp, "Map:");
70 		printed += map__fprintf(pos, fp);
71 		if (verbose > 1) {
72 			printed += dso__fprintf(pos->dso, type, fp);
73 			printed += fprintf(fp, "--\n");
74 		}
75 	}
76 
77 	return printed;
78 }
79 
80 size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp)
81 {
82 	size_t printed = 0, i;
83 	for (i = 0; i < MAP__NR_TYPES; ++i)
84 		printed += __map_groups__fprintf_maps(self, i, fp);
85 	return printed;
86 }
87 
88 static size_t __map_groups__fprintf_removed_maps(struct map_groups *self,
89 						 enum map_type type, FILE *fp)
90 {
91 	struct map *pos;
92 	size_t printed = 0;
93 
94 	list_for_each_entry(pos, &self->removed_maps[type], node) {
95 		printed += fprintf(fp, "Map:");
96 		printed += map__fprintf(pos, fp);
97 		if (verbose > 1) {
98 			printed += dso__fprintf(pos->dso, type, fp);
99 			printed += fprintf(fp, "--\n");
100 		}
101 	}
102 	return printed;
103 }
104 
105 static size_t map_groups__fprintf_removed_maps(struct map_groups *self, FILE *fp)
106 {
107 	size_t printed = 0, i;
108 	for (i = 0; i < MAP__NR_TYPES; ++i)
109 		printed += __map_groups__fprintf_removed_maps(self, i, fp);
110 	return printed;
111 }
112 
113 static size_t map_groups__fprintf(struct map_groups *self, FILE *fp)
114 {
115 	size_t printed = map_groups__fprintf_maps(self, fp);
116 	printed += fprintf(fp, "Removed maps:\n");
117 	return printed + map_groups__fprintf_removed_maps(self, fp);
118 }
119 
120 static size_t thread__fprintf(struct thread *self, FILE *fp)
121 {
122 	return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) +
123 	       map_groups__fprintf(&self->mg, fp);
124 }
125 
126 struct thread *perf_session__findnew(struct perf_session *self, pid_t pid)
127 {
128 	struct rb_node **p = &self->threads.rb_node;
129 	struct rb_node *parent = NULL;
130 	struct thread *th;
131 
132 	/*
133 	 * Font-end cache - PID lookups come in blocks,
134 	 * so most of the time we dont have to look up
135 	 * the full rbtree:
136 	 */
137 	if (self->last_match && self->last_match->pid == pid)
138 		return self->last_match;
139 
140 	while (*p != NULL) {
141 		parent = *p;
142 		th = rb_entry(parent, struct thread, rb_node);
143 
144 		if (th->pid == pid) {
145 			self->last_match = th;
146 			return th;
147 		}
148 
149 		if (pid < th->pid)
150 			p = &(*p)->rb_left;
151 		else
152 			p = &(*p)->rb_right;
153 	}
154 
155 	th = thread__new(pid);
156 	if (th != NULL) {
157 		rb_link_node(&th->rb_node, parent, p);
158 		rb_insert_color(&th->rb_node, &self->threads);
159 		self->last_match = th;
160 	}
161 
162 	return th;
163 }
164 
165 static void map_groups__remove_overlappings(struct map_groups *self,
166 					    struct map *map)
167 {
168 	struct rb_root *root = &self->maps[map->type];
169 	struct rb_node *next = rb_first(root);
170 
171 	while (next) {
172 		struct map *pos = rb_entry(next, struct map, rb_node);
173 		next = rb_next(&pos->rb_node);
174 
175 		if (!map__overlap(pos, map))
176 			continue;
177 
178 		if (verbose >= 2) {
179 			fputs("overlapping maps:\n", stderr);
180 			map__fprintf(map, stderr);
181 			map__fprintf(pos, stderr);
182 		}
183 
184 		rb_erase(&pos->rb_node, root);
185 		/*
186 		 * We may have references to this map, for instance in some
187 		 * hist_entry instances, so just move them to a separate
188 		 * list.
189 		 */
190 		list_add_tail(&pos->node, &self->removed_maps[map->type]);
191 	}
192 }
193 
194 void maps__insert(struct rb_root *maps, struct map *map)
195 {
196 	struct rb_node **p = &maps->rb_node;
197 	struct rb_node *parent = NULL;
198 	const u64 ip = map->start;
199 	struct map *m;
200 
201 	while (*p != NULL) {
202 		parent = *p;
203 		m = rb_entry(parent, struct map, rb_node);
204 		if (ip < m->start)
205 			p = &(*p)->rb_left;
206 		else
207 			p = &(*p)->rb_right;
208 	}
209 
210 	rb_link_node(&map->rb_node, parent, p);
211 	rb_insert_color(&map->rb_node, maps);
212 }
213 
214 struct map *maps__find(struct rb_root *maps, u64 ip)
215 {
216 	struct rb_node **p = &maps->rb_node;
217 	struct rb_node *parent = NULL;
218 	struct map *m;
219 
220 	while (*p != NULL) {
221 		parent = *p;
222 		m = rb_entry(parent, struct map, rb_node);
223 		if (ip < m->start)
224 			p = &(*p)->rb_left;
225 		else if (ip > m->end)
226 			p = &(*p)->rb_right;
227 		else
228 			return m;
229 	}
230 
231 	return NULL;
232 }
233 
234 void thread__insert_map(struct thread *self, struct map *map)
235 {
236 	map_groups__remove_overlappings(&self->mg, map);
237 	map_groups__insert(&self->mg, map);
238 }
239 
240 /*
241  * XXX This should not really _copy_ te maps, but refcount them.
242  */
243 static int map_groups__clone(struct map_groups *self,
244 			     struct map_groups *parent, enum map_type type)
245 {
246 	struct rb_node *nd;
247 	for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) {
248 		struct map *map = rb_entry(nd, struct map, rb_node);
249 		struct map *new = map__clone(map);
250 		if (new == NULL)
251 			return -ENOMEM;
252 		map_groups__insert(self, new);
253 	}
254 	return 0;
255 }
256 
257 int thread__fork(struct thread *self, struct thread *parent)
258 {
259 	int i;
260 
261 	if (parent->comm_set) {
262 		if (self->comm)
263 			free(self->comm);
264 		self->comm = strdup(parent->comm);
265 		if (!self->comm)
266 			return -ENOMEM;
267 		self->comm_set = true;
268 	}
269 
270 	for (i = 0; i < MAP__NR_TYPES; ++i)
271 		if (map_groups__clone(&self->mg, &parent->mg, i) < 0)
272 			return -ENOMEM;
273 	return 0;
274 }
275 
276 size_t perf_session__fprintf(struct perf_session *self, FILE *fp)
277 {
278 	size_t ret = 0;
279 	struct rb_node *nd;
280 
281 	for (nd = rb_first(&self->threads); nd; nd = rb_next(nd)) {
282 		struct thread *pos = rb_entry(nd, struct thread, rb_node);
283 
284 		ret += thread__fprintf(pos, fp);
285 	}
286 
287 	return ret;
288 }
289 
290 struct symbol *map_groups__find_symbol(struct map_groups *self,
291 				       enum map_type type, u64 addr,
292 				       symbol_filter_t filter)
293 {
294 	struct map *map = map_groups__find(self, type, addr);
295 
296 	if (map != NULL)
297 		return map__find_symbol(map, map->map_ip(map, addr), filter);
298 
299 	return NULL;
300 }
301