Commit | Line | Data |
---|---|---|
6baa0a5a FW |
1 | #include "../perf.h" |
2 | #include <stdlib.h> | |
3 | #include <stdio.h> | |
4 | #include <string.h> | |
b3165f41 | 5 | #include "session.h" |
6baa0a5a | 6 | #include "thread.h" |
00447ccd | 7 | #include "thread-stack.h" |
6baa0a5a | 8 | #include "util.h" |
6e086437 | 9 | #include "debug.h" |
1902efe7 | 10 | #include "comm.h" |
66f066d8 | 11 | #include "unwind.h" |
6baa0a5a | 12 | |
2f3027ac ACM |
13 | #include <api/fs/fs.h> |
14 | ||
cddcef60 JO |
15 | int thread__init_map_groups(struct thread *thread, struct machine *machine) |
16 | { | |
17 | struct thread *leader; | |
18 | pid_t pid = thread->pid_; | |
19 | ||
1fcb8768 | 20 | if (pid == thread->tid || pid == -1) { |
11246c70 | 21 | thread->mg = map_groups__new(machine); |
cddcef60 | 22 | } else { |
b91fc39f | 23 | leader = __machine__findnew_thread(machine, pid, pid); |
abd82868 | 24 | if (leader) { |
cddcef60 | 25 | thread->mg = map_groups__get(leader->mg); |
abd82868 ACM |
26 | thread__put(leader); |
27 | } | |
cddcef60 JO |
28 | } |
29 | ||
30 | return thread->mg ? 0 : -1; | |
31 | } | |
32 | ||
99d725fc | 33 | struct thread *thread__new(pid_t pid, pid_t tid) |
6baa0a5a | 34 | { |
1902efe7 FW |
35 | char *comm_str; |
36 | struct comm *comm; | |
c824c433 | 37 | struct thread *thread = zalloc(sizeof(*thread)); |
6baa0a5a | 38 | |
c824c433 | 39 | if (thread != NULL) { |
c824c433 ACM |
40 | thread->pid_ = pid; |
41 | thread->tid = tid; | |
42 | thread->ppid = -1; | |
bf49c35f | 43 | thread->cpu = -1; |
1902efe7 FW |
44 | INIT_LIST_HEAD(&thread->comm_list); |
45 | ||
46 | comm_str = malloc(32); | |
47 | if (!comm_str) | |
48 | goto err_thread; | |
49 | ||
50 | snprintf(comm_str, 32, ":%d", tid); | |
65de51f9 | 51 | comm = comm__new(comm_str, 0, false); |
1902efe7 FW |
52 | free(comm_str); |
53 | if (!comm) | |
54 | goto err_thread; | |
55 | ||
56 | list_add(&comm->list, &thread->comm_list); | |
abd82868 | 57 | atomic_set(&thread->refcnt, 1); |
b91fc39f | 58 | RB_CLEAR_NODE(&thread->rb_node); |
6baa0a5a FW |
59 | } |
60 | ||
c824c433 | 61 | return thread; |
1902efe7 FW |
62 | |
63 | err_thread: | |
64 | free(thread); | |
65 | return NULL; | |
6baa0a5a FW |
66 | } |
67 | ||
c824c433 | 68 | void thread__delete(struct thread *thread) |
591765fd | 69 | { |
1902efe7 FW |
70 | struct comm *comm, *tmp; |
71 | ||
b91fc39f | 72 | BUG_ON(!RB_EMPTY_NODE(&thread->rb_node)); |
b91fc39f | 73 | |
00447ccd AH |
74 | thread_stack__free(thread); |
75 | ||
9608b84e AH |
76 | if (thread->mg) { |
77 | map_groups__put(thread->mg); | |
78 | thread->mg = NULL; | |
79 | } | |
1902efe7 FW |
80 | list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { |
81 | list_del(&comm->list); | |
82 | comm__free(comm); | |
83 | } | |
66f066d8 | 84 | unwind__finish_access(thread); |
1902efe7 | 85 | |
c824c433 | 86 | free(thread); |
591765fd ACM |
87 | } |
88 | ||
f3b623b8 ACM |
89 | struct thread *thread__get(struct thread *thread) |
90 | { | |
b91fc39f ACM |
91 | if (thread) |
92 | atomic_inc(&thread->refcnt); | |
f3b623b8 ACM |
93 | return thread; |
94 | } | |
95 | ||
96 | void thread__put(struct thread *thread) | |
97 | { | |
e1ed3a5b | 98 | if (thread && atomic_dec_and_test(&thread->refcnt)) { |
abd82868 ACM |
99 | /* |
100 | * Remove it from the dead_threads list, as last reference | |
101 | * is gone. | |
102 | */ | |
f3b623b8 ACM |
103 | list_del_init(&thread->node); |
104 | thread__delete(thread); | |
105 | } | |
106 | } | |
107 | ||
4dfced35 | 108 | struct comm *thread__comm(const struct thread *thread) |
6baa0a5a | 109 | { |
1902efe7 FW |
110 | if (list_empty(&thread->comm_list)) |
111 | return NULL; | |
4385d580 | 112 | |
1902efe7 FW |
113 | return list_first_entry(&thread->comm_list, struct comm, list); |
114 | } | |
115 | ||
65de51f9 AH |
116 | struct comm *thread__exec_comm(const struct thread *thread) |
117 | { | |
118 | struct comm *comm, *last = NULL; | |
119 | ||
120 | list_for_each_entry(comm, &thread->comm_list, list) { | |
121 | if (comm->exec) | |
122 | return comm; | |
123 | last = comm; | |
124 | } | |
125 | ||
126 | return last; | |
127 | } | |
128 | ||
65de51f9 AH |
129 | int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp, |
130 | bool exec) | |
1902efe7 FW |
131 | { |
132 | struct comm *new, *curr = thread__comm(thread); | |
3178f58b | 133 | int err; |
1902efe7 | 134 | |
a8480808 AH |
135 | /* Override the default :tid entry */ |
136 | if (!thread->comm_set) { | |
65de51f9 | 137 | err = comm__override(curr, str, timestamp, exec); |
3178f58b FW |
138 | if (err) |
139 | return err; | |
a5285ad9 | 140 | } else { |
65de51f9 | 141 | new = comm__new(str, timestamp, exec); |
a5285ad9 FW |
142 | if (!new) |
143 | return -ENOMEM; | |
144 | list_add(&new->list, &thread->comm_list); | |
380b5143 NK |
145 | |
146 | if (exec) | |
147 | unwind__flush_access(thread); | |
4385d580 | 148 | } |
1902efe7 | 149 | |
1902efe7 FW |
150 | thread->comm_set = true; |
151 | ||
152 | return 0; | |
6baa0a5a FW |
153 | } |
154 | ||
2f3027ac ACM |
155 | int thread__set_comm_from_proc(struct thread *thread) |
156 | { | |
157 | char path[64]; | |
158 | char *comm = NULL; | |
159 | size_t sz; | |
160 | int err = -1; | |
161 | ||
162 | if (!(snprintf(path, sizeof(path), "%d/task/%d/comm", | |
163 | thread->pid_, thread->tid) >= (int)sizeof(path)) && | |
164 | procfs__read_str(path, &comm, &sz) == 0) { | |
165 | comm[sz - 1] = '\0'; | |
166 | err = thread__set_comm(thread, comm, 0); | |
167 | } | |
168 | ||
169 | return err; | |
170 | } | |
171 | ||
b9c5143a FW |
172 | const char *thread__comm_str(const struct thread *thread) |
173 | { | |
1902efe7 FW |
174 | const struct comm *comm = thread__comm(thread); |
175 | ||
176 | if (!comm) | |
177 | return NULL; | |
178 | ||
179 | return comm__str(comm); | |
b9c5143a FW |
180 | } |
181 | ||
1902efe7 | 182 | /* CHECKME: it should probably better return the max comm len from its comm list */ |
c824c433 | 183 | int thread__comm_len(struct thread *thread) |
a4fb581b | 184 | { |
c824c433 | 185 | if (!thread->comm_len) { |
1902efe7 FW |
186 | const char *comm = thread__comm_str(thread); |
187 | if (!comm) | |
a4fb581b | 188 | return 0; |
1902efe7 | 189 | thread->comm_len = strlen(comm); |
a4fb581b FW |
190 | } |
191 | ||
c824c433 | 192 | return thread->comm_len; |
a4fb581b FW |
193 | } |
194 | ||
3f067dca | 195 | size_t thread__fprintf(struct thread *thread, FILE *fp) |
9958e1f0 | 196 | { |
b9c5143a | 197 | return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) + |
acebd408 | 198 | map_groups__fprintf(thread->mg, fp); |
6baa0a5a FW |
199 | } |
200 | ||
8132a2a8 | 201 | int thread__insert_map(struct thread *thread, struct map *map) |
1b46cddf | 202 | { |
8132a2a8 HK |
203 | int ret; |
204 | ||
a2873325 | 205 | ret = unwind__prepare_access(thread, map, NULL); |
8132a2a8 HK |
206 | if (ret) |
207 | return ret; | |
208 | ||
acebd408 | 209 | map_groups__fixup_overlappings(thread->mg, map, stderr); |
93d5731d | 210 | map_groups__insert(thread->mg, map); |
8132a2a8 HK |
211 | |
212 | return 0; | |
6baa0a5a FW |
213 | } |
214 | ||
6c502584 JO |
215 | static int __thread__prepare_access(struct thread *thread) |
216 | { | |
217 | bool initialized = false; | |
218 | int i, err = 0; | |
219 | ||
220 | for (i = 0; i < MAP__NR_TYPES; ++i) { | |
221 | struct maps *maps = &thread->mg->maps[i]; | |
222 | struct map *map; | |
223 | ||
224 | pthread_rwlock_rdlock(&maps->lock); | |
225 | ||
226 | for (map = maps__first(maps); map; map = map__next(map)) { | |
227 | err = unwind__prepare_access(thread, map, &initialized); | |
228 | if (err || initialized) | |
229 | break; | |
230 | } | |
231 | ||
232 | pthread_rwlock_unlock(&maps->lock); | |
233 | } | |
234 | ||
235 | return err; | |
236 | } | |
237 | ||
238 | static int thread__prepare_access(struct thread *thread) | |
239 | { | |
240 | int err = 0; | |
241 | ||
242 | if (symbol_conf.use_callchain) | |
243 | err = __thread__prepare_access(thread); | |
244 | ||
245 | return err; | |
246 | } | |
247 | ||
cddcef60 JO |
248 | static int thread__clone_map_groups(struct thread *thread, |
249 | struct thread *parent) | |
250 | { | |
251 | int i; | |
252 | ||
253 | /* This is new thread, we share map groups for process. */ | |
254 | if (thread->pid_ == parent->pid_) | |
6c502584 | 255 | return thread__prepare_access(thread); |
cddcef60 | 256 | |
0d7e7acc AH |
257 | if (thread->mg == parent->mg) { |
258 | pr_debug("broken map groups on thread %d/%d parent %d/%d\n", | |
259 | thread->pid_, thread->tid, parent->pid_, parent->tid); | |
260 | return 0; | |
261 | } | |
262 | ||
cddcef60 JO |
263 | /* But this one is new process, copy maps. */ |
264 | for (i = 0; i < MAP__NR_TYPES; ++i) | |
6c502584 | 265 | if (map_groups__clone(thread, parent->mg, i) < 0) |
cddcef60 JO |
266 | return -ENOMEM; |
267 | ||
268 | return 0; | |
269 | } | |
270 | ||
1902efe7 | 271 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) |
95011c60 | 272 | { |
cddcef60 | 273 | int err; |
6baa0a5a | 274 | |
faa5c5c3 | 275 | if (parent->comm_set) { |
1902efe7 FW |
276 | const char *comm = thread__comm_str(parent); |
277 | if (!comm) | |
faa5c5c3 | 278 | return -ENOMEM; |
1902efe7 | 279 | err = thread__set_comm(thread, comm, timestamp); |
8d00be81 | 280 | if (err) |
1902efe7 | 281 | return err; |
faa5c5c3 | 282 | } |
6baa0a5a | 283 | |
c824c433 | 284 | thread->ppid = parent->tid; |
cddcef60 | 285 | return thread__clone_map_groups(thread, parent); |
6baa0a5a | 286 | } |
52a3cb8c ACM |
287 | |
288 | void thread__find_cpumode_addr_location(struct thread *thread, | |
52a3cb8c ACM |
289 | enum map_type type, u64 addr, |
290 | struct addr_location *al) | |
291 | { | |
292 | size_t i; | |
3b556bce | 293 | const u8 cpumodes[] = { |
52a3cb8c ACM |
294 | PERF_RECORD_MISC_USER, |
295 | PERF_RECORD_MISC_KERNEL, | |
296 | PERF_RECORD_MISC_GUEST_USER, | |
297 | PERF_RECORD_MISC_GUEST_KERNEL | |
298 | }; | |
299 | ||
300 | for (i = 0; i < ARRAY_SIZE(cpumodes); i++) { | |
bb871a9c | 301 | thread__find_addr_location(thread, cpumodes[i], type, addr, al); |
52a3cb8c ACM |
302 | if (al->map) |
303 | break; | |
304 | } | |
305 | } | |
480ca357 AK |
306 | |
307 | struct thread *thread__main_thread(struct machine *machine, struct thread *thread) | |
308 | { | |
309 | if (thread->pid_ == thread->tid) | |
310 | return thread__get(thread); | |
311 | ||
312 | if (thread->pid_ == -1) | |
313 | return NULL; | |
314 | ||
315 | return machine__find_thread(machine, thread->pid_, thread->pid_); | |
316 | } |