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