Commit | Line | Data |
---|---|---|
cf73e0fe | 1 | /* |
c0c0989a | 2 | * SPDX-License-Identifier: LGPL-2.1-or-later |
cf73e0fe | 3 | * |
c0c0989a MJ |
4 | * Copyright (C) 2013 Paul Woegerer <paul_woegerer@mentor.com> |
5 | * Copyright (C) 2015 Antoine Busque <abusque@efficios.com> | |
6 | * Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | |
cf73e0fe AB |
7 | */ |
8 | ||
1ddceb36 | 9 | #define _LGPL_SOURCE |
8e2aed3f | 10 | #include <link.h> |
cf73e0fe | 11 | #include <limits.h> |
cf73e0fe | 12 | #include <stdio.h> |
8e2aed3f AB |
13 | #include <stdint.h> |
14 | #include <stdlib.h> | |
97c7c238 | 15 | #include <stdbool.h> |
8e2aed3f AB |
16 | #include <sys/types.h> |
17 | #include <unistd.h> | |
cf73e0fe | 18 | |
9d315d6d MJ |
19 | #include "common/elf.h" |
20 | #include "common/macros.h" | |
cf73e0fe AB |
21 | #include "lttng-tracer-core.h" |
22 | #include "lttng-ust-statedump.h" | |
e58e5ad5 | 23 | #include "common/jhash.h" |
910dcd72 | 24 | #include "common/getenv.h" |
36c52fff | 25 | #include "lib/lttng-ust/events.h" |
cf73e0fe | 26 | |
6ba0c2b2 MD |
27 | #define LTTNG_UST_TRACEPOINT_HIDDEN_DEFINITION |
28 | #define LTTNG_UST_TRACEPOINT_PROVIDER_HIDDEN_DEFINITION | |
29 | ||
88c7c4ea | 30 | #define LTTNG_UST_TRACEPOINT_DEFINE |
97c7c238 MD |
31 | #include "ust_lib.h" /* Only define. */ |
32 | ||
660323e6 | 33 | #define LTTNG_UST_TRACEPOINT_CREATE_PROBES |
91fe3e13 | 34 | #define LTTNG_UST_TP_SESSION_CHECK |
97c7c238 | 35 | #include "lttng-ust-statedump-provider.h" /* Define and create probes. */ |
cf73e0fe AB |
36 | |
37 | struct dl_iterate_data { | |
cf73e0fe | 38 | int exec_found; |
97c7c238 MD |
39 | bool first; |
40 | bool cancel; | |
cf73e0fe AB |
41 | }; |
42 | ||
f60e49df | 43 | struct bin_info_data { |
cf73e0fe | 44 | void *base_addr_ptr; |
97c7c238 | 45 | char resolved_path[PATH_MAX]; |
8e2aed3f AB |
46 | char *dbg_file; |
47 | uint8_t *build_id; | |
48 | uint64_t memsz; | |
49 | size_t build_id_len; | |
cf73e0fe | 50 | int vdso; |
8e2aed3f | 51 | uint32_t crc; |
f5eb039d | 52 | uint8_t is_pic; |
c5c4fd82 MD |
53 | uint8_t has_build_id; |
54 | uint8_t has_debug_link; | |
cf73e0fe AB |
55 | }; |
56 | ||
97c7c238 MD |
57 | struct lttng_ust_dl_node { |
58 | struct bin_info_data bin_data; | |
59 | struct cds_hlist_node node; | |
60 | bool traced; | |
61 | bool marked; | |
62 | }; | |
63 | ||
64 | #define UST_DL_STATE_HASH_BITS 8 | |
65 | #define UST_DL_STATE_TABLE_SIZE (1 << UST_DL_STATE_HASH_BITS) | |
95fa2ba4 | 66 | static struct cds_hlist_head dl_state_table[UST_DL_STATE_TABLE_SIZE]; |
97c7c238 | 67 | |
f69fe5fb | 68 | typedef void (*tracepoint_cb)(struct lttng_ust_session *session, void *priv); |
cf73e0fe | 69 | |
97c7c238 MD |
70 | static |
71 | struct lttng_ust_dl_node *alloc_dl_node(const struct bin_info_data *bin_data) | |
72 | { | |
73 | struct lttng_ust_dl_node *e; | |
74 | ||
75 | e = zmalloc(sizeof(struct lttng_ust_dl_node)); | |
76 | if (!e) | |
77 | return NULL; | |
78 | if (bin_data->dbg_file) { | |
79 | e->bin_data.dbg_file = strdup(bin_data->dbg_file); | |
80 | if (!e->bin_data.dbg_file) | |
81 | goto error; | |
82 | } | |
83 | if (bin_data->build_id) { | |
84 | e->bin_data.build_id = zmalloc(bin_data->build_id_len); | |
85 | if (!e->bin_data.build_id) | |
86 | goto error; | |
87 | memcpy(e->bin_data.build_id, bin_data->build_id, | |
88 | bin_data->build_id_len); | |
89 | } | |
90 | e->bin_data.base_addr_ptr = bin_data->base_addr_ptr; | |
91 | memcpy(e->bin_data.resolved_path, bin_data->resolved_path, PATH_MAX); | |
92 | e->bin_data.memsz = bin_data->memsz; | |
93 | e->bin_data.build_id_len = bin_data->build_id_len; | |
94 | e->bin_data.vdso = bin_data->vdso; | |
95 | e->bin_data.crc = bin_data->crc; | |
96 | e->bin_data.is_pic = bin_data->is_pic; | |
97 | e->bin_data.has_build_id = bin_data->has_build_id; | |
98 | e->bin_data.has_debug_link = bin_data->has_debug_link; | |
99 | return e; | |
100 | ||
101 | error: | |
102 | free(e->bin_data.build_id); | |
103 | free(e->bin_data.dbg_file); | |
104 | free(e); | |
105 | return NULL; | |
106 | } | |
107 | ||
108 | static | |
109 | void free_dl_node(struct lttng_ust_dl_node *e) | |
110 | { | |
111 | free(e->bin_data.build_id); | |
112 | free(e->bin_data.dbg_file); | |
113 | free(e); | |
114 | } | |
115 | ||
116 | /* Return 0 if same, nonzero if not. */ | |
117 | static | |
118 | int compare_bin_data(const struct bin_info_data *a, | |
119 | const struct bin_info_data *b) | |
120 | { | |
121 | if (a->base_addr_ptr != b->base_addr_ptr) | |
122 | return -1; | |
123 | if (strcmp(a->resolved_path, b->resolved_path) != 0) | |
124 | return -1; | |
125 | if (a->dbg_file && !b->dbg_file) | |
126 | return -1; | |
127 | if (!a->dbg_file && b->dbg_file) | |
128 | return -1; | |
129 | if (a->dbg_file && strcmp(a->dbg_file, b->dbg_file) != 0) | |
130 | return -1; | |
131 | if (a->build_id && !b->build_id) | |
132 | return -1; | |
133 | if (!a->build_id && b->build_id) | |
134 | return -1; | |
135 | if (a->build_id_len != b->build_id_len) | |
136 | return -1; | |
137 | if (a->build_id && | |
138 | memcmp(a->build_id, b->build_id, a->build_id_len) != 0) | |
139 | return -1; | |
140 | if (a->memsz != b->memsz) | |
141 | return -1; | |
142 | if (a->vdso != b->vdso) | |
143 | return -1; | |
144 | if (a->crc != b->crc) | |
145 | return -1; | |
146 | if (a->is_pic != b->is_pic) | |
147 | return -1; | |
148 | if (a->has_build_id != b->has_build_id) | |
149 | return -1; | |
150 | if (a->has_debug_link != b->has_debug_link) | |
151 | return -1; | |
152 | return 0; | |
153 | } | |
154 | ||
155 | static | |
156 | struct lttng_ust_dl_node *find_or_create_dl_node(struct bin_info_data *bin_data) | |
157 | { | |
158 | struct cds_hlist_head *head; | |
159 | struct lttng_ust_dl_node *e; | |
160 | unsigned int hash; | |
161 | bool found = false; | |
162 | ||
163 | hash = jhash(&bin_data->base_addr_ptr, | |
164 | sizeof(bin_data->base_addr_ptr), 0); | |
165 | head = &dl_state_table[hash & (UST_DL_STATE_TABLE_SIZE - 1)]; | |
166 | cds_hlist_for_each_entry_2(e, head, node) { | |
167 | if (compare_bin_data(&e->bin_data, bin_data) != 0) | |
168 | continue; | |
169 | found = true; | |
170 | break; | |
171 | } | |
172 | if (!found) { | |
173 | /* Create */ | |
174 | e = alloc_dl_node(bin_data); | |
175 | if (!e) | |
176 | return NULL; | |
177 | cds_hlist_add_head(&e->node, head); | |
178 | } | |
179 | return e; | |
180 | } | |
181 | ||
182 | static | |
183 | void remove_dl_node(struct lttng_ust_dl_node *e) | |
184 | { | |
185 | cds_hlist_del(&e->node); | |
186 | } | |
187 | ||
cf73e0fe AB |
188 | /* |
189 | * Trace statedump event into all sessions owned by the caller thread | |
190 | * for which statedump is pending. | |
191 | */ | |
192 | static | |
97c7c238 | 193 | void trace_statedump_event(tracepoint_cb tp_cb, void *owner, void *priv) |
cf73e0fe AB |
194 | { |
195 | struct cds_list_head *sessionsp; | |
bdb12629 | 196 | struct lttng_ust_session_private *session_priv; |
cf73e0fe | 197 | |
7753d283 | 198 | sessionsp = lttng_get_sessions(); |
bdb12629 MD |
199 | cds_list_for_each_entry(session_priv, sessionsp, node) { |
200 | if (session_priv->owner != owner) | |
cf73e0fe | 201 | continue; |
bdb12629 | 202 | if (!session_priv->statedump_pending) |
cf73e0fe | 203 | continue; |
bdb12629 | 204 | tp_cb(session_priv->pub, priv); |
cf73e0fe | 205 | } |
cf73e0fe AB |
206 | } |
207 | ||
208 | static | |
f69fe5fb | 209 | void trace_bin_info_cb(struct lttng_ust_session *session, void *priv) |
cf73e0fe | 210 | { |
f60e49df | 211 | struct bin_info_data *bin_data = (struct bin_info_data *) priv; |
cf73e0fe | 212 | |
cbc06a3b | 213 | lttng_ust_tracepoint(lttng_ust_statedump, bin_info, |
f60e49df | 214 | session, bin_data->base_addr_ptr, |
c5c4fd82 MD |
215 | bin_data->resolved_path, bin_data->memsz, |
216 | bin_data->is_pic, bin_data->has_build_id, | |
217 | bin_data->has_debug_link); | |
8e2aed3f AB |
218 | } |
219 | ||
220 | static | |
f69fe5fb | 221 | void trace_build_id_cb(struct lttng_ust_session *session, void *priv) |
8e2aed3f | 222 | { |
f60e49df | 223 | struct bin_info_data *bin_data = (struct bin_info_data *) priv; |
8e2aed3f | 224 | |
cbc06a3b | 225 | lttng_ust_tracepoint(lttng_ust_statedump, build_id, |
f60e49df AB |
226 | session, bin_data->base_addr_ptr, |
227 | bin_data->build_id, bin_data->build_id_len); | |
8e2aed3f AB |
228 | } |
229 | ||
230 | static | |
f69fe5fb | 231 | void trace_debug_link_cb(struct lttng_ust_session *session, void *priv) |
8e2aed3f | 232 | { |
f60e49df | 233 | struct bin_info_data *bin_data = (struct bin_info_data *) priv; |
8e2aed3f | 234 | |
cbc06a3b | 235 | lttng_ust_tracepoint(lttng_ust_statedump, debug_link, |
f60e49df AB |
236 | session, bin_data->base_addr_ptr, |
237 | bin_data->dbg_file, bin_data->crc); | |
cf73e0fe AB |
238 | } |
239 | ||
94be38e8 | 240 | static |
f69fe5fb | 241 | void procname_cb(struct lttng_ust_session *session, void *priv) |
94be38e8 JR |
242 | { |
243 | char *procname = (char *) priv; | |
cbc06a3b | 244 | lttng_ust_tracepoint(lttng_ust_statedump, procname, session, procname); |
94be38e8 JR |
245 | } |
246 | ||
cf73e0fe | 247 | static |
2208d8b5 | 248 | void trace_start_cb(struct lttng_ust_session *session, void *priv __attribute__((unused))) |
cf73e0fe | 249 | { |
cbc06a3b | 250 | lttng_ust_tracepoint(lttng_ust_statedump, start, session); |
cf73e0fe AB |
251 | } |
252 | ||
253 | static | |
2208d8b5 | 254 | void trace_end_cb(struct lttng_ust_session *session, void *priv __attribute__((unused))) |
cf73e0fe | 255 | { |
cbc06a3b | 256 | lttng_ust_tracepoint(lttng_ust_statedump, end, session); |
cf73e0fe AB |
257 | } |
258 | ||
8e2aed3f | 259 | static |
c5c4fd82 MD |
260 | int get_elf_info(struct bin_info_data *bin_data) |
261 | { | |
8e2aed3f | 262 | struct lttng_ust_elf *elf; |
c5c4fd82 | 263 | int ret = 0, found; |
8e2aed3f | 264 | |
f60e49df | 265 | elf = lttng_ust_elf_create(bin_data->resolved_path); |
8e2aed3f AB |
266 | if (!elf) { |
267 | ret = -1; | |
268 | goto end; | |
269 | } | |
270 | ||
f60e49df | 271 | ret = lttng_ust_elf_get_memsz(elf, &bin_data->memsz); |
8e2aed3f AB |
272 | if (ret) { |
273 | goto end; | |
274 | } | |
275 | ||
c5c4fd82 | 276 | found = 0; |
f60e49df | 277 | ret = lttng_ust_elf_get_build_id(elf, &bin_data->build_id, |
c5c4fd82 MD |
278 | &bin_data->build_id_len, |
279 | &found); | |
8e2aed3f AB |
280 | if (ret) { |
281 | goto end; | |
282 | } | |
c5c4fd82 MD |
283 | bin_data->has_build_id = !!found; |
284 | found = 0; | |
f60e49df | 285 | ret = lttng_ust_elf_get_debug_link(elf, &bin_data->dbg_file, |
c5c4fd82 MD |
286 | &bin_data->crc, |
287 | &found); | |
8e2aed3f AB |
288 | if (ret) { |
289 | goto end; | |
290 | } | |
c5c4fd82 | 291 | bin_data->has_debug_link = !!found; |
8e2aed3f | 292 | |
f60e49df | 293 | bin_data->is_pic = lttng_ust_elf_is_pic(elf); |
f5eb039d | 294 | |
8e2aed3f AB |
295 | end: |
296 | lttng_ust_elf_destroy(elf); | |
297 | return ret; | |
298 | } | |
299 | ||
cf73e0fe | 300 | static |
97c7c238 MD |
301 | void trace_baddr(struct bin_info_data *bin_data, void *owner) |
302 | { | |
303 | trace_statedump_event(trace_bin_info_cb, owner, bin_data); | |
304 | ||
305 | if (bin_data->has_build_id) | |
306 | trace_statedump_event(trace_build_id_cb, owner, bin_data); | |
307 | ||
308 | if (bin_data->has_debug_link) | |
309 | trace_statedump_event(trace_debug_link_cb, owner, bin_data); | |
310 | } | |
311 | ||
312 | static | |
313 | int extract_baddr(struct bin_info_data *bin_data) | |
cf73e0fe | 314 | { |
c5c4fd82 | 315 | int ret = 0; |
97c7c238 | 316 | struct lttng_ust_dl_node *e; |
8e2aed3f | 317 | |
f60e49df | 318 | if (!bin_data->vdso) { |
c5c4fd82 | 319 | ret = get_elf_info(bin_data); |
8e2aed3f AB |
320 | if (ret) { |
321 | goto end; | |
322 | } | |
323 | } else { | |
f60e49df | 324 | bin_data->memsz = 0; |
fd783031 MD |
325 | bin_data->has_build_id = 0; |
326 | bin_data->has_debug_link = 0; | |
8e2aed3f AB |
327 | } |
328 | ||
97c7c238 MD |
329 | e = find_or_create_dl_node(bin_data); |
330 | if (!e) { | |
331 | ret = -1; | |
8e2aed3f AB |
332 | goto end; |
333 | } | |
97c7c238 | 334 | e->marked = true; |
8e2aed3f | 335 | end: |
97c7c238 MD |
336 | free(bin_data->build_id); |
337 | bin_data->build_id = NULL; | |
338 | free(bin_data->dbg_file); | |
339 | bin_data->dbg_file = NULL; | |
8e2aed3f | 340 | return ret; |
cf73e0fe AB |
341 | } |
342 | ||
343 | static | |
97c7c238 | 344 | void trace_statedump_start(void *owner) |
cf73e0fe | 345 | { |
97c7c238 | 346 | trace_statedump_event(trace_start_cb, owner, NULL); |
cf73e0fe AB |
347 | } |
348 | ||
349 | static | |
97c7c238 | 350 | void trace_statedump_end(void *owner) |
cf73e0fe | 351 | { |
97c7c238 | 352 | trace_statedump_event(trace_end_cb, owner, NULL); |
cf73e0fe AB |
353 | } |
354 | ||
355 | static | |
97c7c238 | 356 | void iter_begin(struct dl_iterate_data *data) |
cf73e0fe | 357 | { |
97c7c238 | 358 | unsigned int i; |
cf73e0fe | 359 | |
d34e6761 MD |
360 | /* |
361 | * UST lock nests within dynamic loader lock. | |
362 | * | |
97c7c238 | 363 | * Hold this lock across handling of the module listing to |
d34e6761 MD |
364 | * protect memory allocation at early process start, due to |
365 | * interactions with libc-wrapper lttng malloc instrumentation. | |
366 | */ | |
367 | if (ust_lock()) { | |
97c7c238 MD |
368 | data->cancel = true; |
369 | return; | |
d34e6761 MD |
370 | } |
371 | ||
97c7c238 MD |
372 | /* Ensure all entries are unmarked. */ |
373 | for (i = 0; i < UST_DL_STATE_TABLE_SIZE; i++) { | |
374 | struct cds_hlist_head *head; | |
375 | struct lttng_ust_dl_node *e; | |
376 | ||
377 | head = &dl_state_table[i]; | |
378 | cds_hlist_for_each_entry_2(e, head, node) | |
379 | assert(!e->marked); | |
380 | } | |
381 | } | |
382 | ||
383 | static | |
384 | void trace_lib_load(const struct bin_info_data *bin_data, void *ip) | |
385 | { | |
cbc06a3b | 386 | lttng_ust_tracepoint(lttng_ust_lib, load, |
97c7c238 MD |
387 | ip, bin_data->base_addr_ptr, bin_data->resolved_path, |
388 | bin_data->memsz, bin_data->has_build_id, | |
389 | bin_data->has_debug_link); | |
390 | ||
391 | if (bin_data->has_build_id) { | |
cbc06a3b | 392 | lttng_ust_tracepoint(lttng_ust_lib, build_id, |
97c7c238 MD |
393 | ip, bin_data->base_addr_ptr, bin_data->build_id, |
394 | bin_data->build_id_len); | |
395 | } | |
396 | ||
397 | if (bin_data->has_debug_link) { | |
cbc06a3b | 398 | lttng_ust_tracepoint(lttng_ust_lib, debug_link, |
97c7c238 MD |
399 | ip, bin_data->base_addr_ptr, bin_data->dbg_file, |
400 | bin_data->crc); | |
401 | } | |
402 | } | |
403 | ||
404 | static | |
405 | void trace_lib_unload(const struct bin_info_data *bin_data, void *ip) | |
406 | { | |
cbc06a3b | 407 | lttng_ust_tracepoint(lttng_ust_lib, unload, ip, bin_data->base_addr_ptr); |
97c7c238 MD |
408 | } |
409 | ||
410 | static | |
411 | void iter_end(struct dl_iterate_data *data, void *ip) | |
412 | { | |
413 | unsigned int i; | |
414 | ||
2eb235ec MD |
415 | if (data->cancel) |
416 | goto end; | |
97c7c238 MD |
417 | /* |
418 | * Iterate on hash table. | |
419 | * For each marked, traced, do nothing. | |
420 | * For each marked, not traced, trace lib open event. traced = true. | |
421 | * For each unmarked, traced, trace lib close event. remove node. | |
422 | * For each unmarked, not traced, remove node. | |
423 | */ | |
424 | for (i = 0; i < UST_DL_STATE_TABLE_SIZE; i++) { | |
425 | struct cds_hlist_head *head; | |
426 | struct lttng_ust_dl_node *e; | |
427 | ||
428 | head = &dl_state_table[i]; | |
429 | cds_hlist_for_each_entry_2(e, head, node) { | |
430 | if (e->marked) { | |
431 | if (!e->traced) { | |
432 | trace_lib_load(&e->bin_data, ip); | |
433 | e->traced = true; | |
434 | } | |
b891574e | 435 | e->marked = false; |
97c7c238 MD |
436 | } else { |
437 | if (e->traced) | |
438 | trace_lib_unload(&e->bin_data, ip); | |
439 | remove_dl_node(e); | |
440 | free_dl_node(e); | |
441 | } | |
97c7c238 MD |
442 | } |
443 | } | |
2eb235ec | 444 | end: |
97c7c238 MD |
445 | ust_unlock(); |
446 | } | |
447 | ||
448 | static | |
2208d8b5 | 449 | int extract_bin_info_events(struct dl_phdr_info *info, size_t size __attribute__((unused)), void *_data) |
97c7c238 MD |
450 | { |
451 | int j, ret = 0; | |
452 | struct dl_iterate_data *data = _data; | |
453 | ||
454 | if (data->first) { | |
455 | iter_begin(data); | |
456 | data->first = false; | |
457 | } | |
458 | ||
459 | if (data->cancel) | |
460 | goto end; | |
461 | ||
cf73e0fe | 462 | for (j = 0; j < info->dlpi_phnum; j++) { |
f60e49df | 463 | struct bin_info_data bin_data; |
cf73e0fe AB |
464 | |
465 | if (info->dlpi_phdr[j].p_type != PT_LOAD) | |
466 | continue; | |
467 | ||
97c7c238 MD |
468 | memset(&bin_data, 0, sizeof(bin_data)); |
469 | ||
cf73e0fe | 470 | /* Calculate virtual memory address of the loadable segment */ |
97c7c238 | 471 | bin_data.base_addr_ptr = (void *) info->dlpi_addr + |
cf73e0fe AB |
472 | info->dlpi_phdr[j].p_vaddr; |
473 | ||
474 | if ((info->dlpi_name == NULL || info->dlpi_name[0] == 0)) { | |
475 | /* | |
476 | * Only the first phdr without a dlpi_name | |
477 | * encountered is considered as the program | |
478 | * executable. The rest are vdsos. | |
479 | */ | |
480 | if (!data->exec_found) { | |
481 | ssize_t path_len; | |
482 | data->exec_found = 1; | |
483 | ||
484 | /* | |
485 | * Use /proc/self/exe to resolve the | |
486 | * executable's full path. | |
487 | */ | |
488 | path_len = readlink("/proc/self/exe", | |
97c7c238 | 489 | bin_data.resolved_path, |
cf73e0fe AB |
490 | PATH_MAX - 1); |
491 | if (path_len <= 0) | |
492 | break; | |
493 | ||
97c7c238 | 494 | bin_data.resolved_path[path_len] = '\0'; |
f60e49df | 495 | bin_data.vdso = 0; |
cf73e0fe | 496 | } else { |
97c7c238 MD |
497 | snprintf(bin_data.resolved_path, |
498 | PATH_MAX - 1, "[vdso]"); | |
f60e49df | 499 | bin_data.vdso = 1; |
cf73e0fe AB |
500 | } |
501 | } else { | |
502 | /* | |
503 | * For regular dl_phdr_info entries check if | |
f60e49df | 504 | * the path to the binary really exists. If not, |
cf73e0fe AB |
505 | * treat as vdso and use dlpi_name as 'path'. |
506 | */ | |
97c7c238 MD |
507 | if (!realpath(info->dlpi_name, |
508 | bin_data.resolved_path)) { | |
509 | snprintf(bin_data.resolved_path, | |
510 | PATH_MAX - 1, "[%s]", | |
cf73e0fe | 511 | info->dlpi_name); |
f60e49df | 512 | bin_data.vdso = 1; |
89be5359 | 513 | } else { |
f60e49df | 514 | bin_data.vdso = 0; |
cf73e0fe AB |
515 | } |
516 | } | |
517 | ||
97c7c238 | 518 | ret = extract_baddr(&bin_data); |
d34e6761 | 519 | break; |
cf73e0fe | 520 | } |
d34e6761 | 521 | end: |
d34e6761 | 522 | return ret; |
cf73e0fe AB |
523 | } |
524 | ||
cf73e0fe | 525 | static |
97c7c238 MD |
526 | void ust_dl_table_statedump(void *owner) |
527 | { | |
528 | unsigned int i; | |
529 | ||
530 | if (ust_lock()) | |
531 | goto end; | |
532 | ||
533 | /* Statedump each traced table entry into session for owner. */ | |
534 | for (i = 0; i < UST_DL_STATE_TABLE_SIZE; i++) { | |
535 | struct cds_hlist_head *head; | |
536 | struct lttng_ust_dl_node *e; | |
537 | ||
538 | head = &dl_state_table[i]; | |
539 | cds_hlist_for_each_entry_2(e, head, node) { | |
540 | if (e->traced) | |
541 | trace_baddr(&e->bin_data, owner); | |
542 | } | |
543 | } | |
544 | ||
545 | end: | |
546 | ust_unlock(); | |
547 | } | |
548 | ||
549 | void lttng_ust_dl_update(void *ip) | |
cf73e0fe AB |
550 | { |
551 | struct dl_iterate_data data; | |
552 | ||
4c41b460 | 553 | if (lttng_ust_getenv("LTTNG_UST_WITHOUT_BADDR_STATEDUMP")) |
97c7c238 | 554 | return; |
cf73e0fe | 555 | |
c362addf | 556 | /* |
a9fd951a MJ |
557 | * Force the allocation of lttng-ust TLS variables when called from |
558 | * dlopen/dlclose instrumentation. | |
c362addf | 559 | */ |
a9fd951a | 560 | lttng_ust_alloc_tls(); |
c362addf | 561 | |
cf73e0fe | 562 | data.exec_found = 0; |
97c7c238 MD |
563 | data.first = true; |
564 | data.cancel = false; | |
cf73e0fe AB |
565 | /* |
566 | * Iterate through the list of currently loaded shared objects and | |
97c7c238 | 567 | * generate tables entries for loadable segments using |
f60e49df | 568 | * extract_bin_info_events. |
97c7c238 MD |
569 | * Removed libraries are detected by mark-and-sweep: marking is |
570 | * done in the iteration over libraries, and sweeping is | |
571 | * performed by iter_end(). | |
cf73e0fe | 572 | */ |
f60e49df | 573 | dl_iterate_phdr(extract_bin_info_events, &data); |
97c7c238 MD |
574 | if (data.first) |
575 | iter_begin(&data); | |
576 | iter_end(&data, ip); | |
577 | } | |
cf73e0fe | 578 | |
97c7c238 MD |
579 | /* |
580 | * Generate a statedump of base addresses of all shared objects loaded | |
581 | * by the traced application, as well as for the application's | |
582 | * executable itself. | |
583 | */ | |
584 | static | |
585 | int do_baddr_statedump(void *owner) | |
586 | { | |
4c41b460 | 587 | if (lttng_ust_getenv("LTTNG_UST_WITHOUT_BADDR_STATEDUMP")) |
97c7c238 MD |
588 | return 0; |
589 | lttng_ust_dl_update(LTTNG_UST_CALLER_IP()); | |
590 | ust_dl_table_statedump(owner); | |
cf73e0fe AB |
591 | return 0; |
592 | } | |
593 | ||
94be38e8 JR |
594 | static |
595 | int do_procname_statedump(void *owner) | |
596 | { | |
4c41b460 | 597 | if (lttng_ust_getenv("LTTNG_UST_WITHOUT_PROCNAME_STATEDUMP")) |
94be38e8 JR |
598 | return 0; |
599 | ||
600 | trace_statedump_event(procname_cb, owner, lttng_ust_sockinfo_get_procname(owner)); | |
601 | return 0; | |
602 | } | |
603 | ||
cf73e0fe AB |
604 | /* |
605 | * Generate a statedump of a given traced application. A statedump is | |
606 | * delimited by start and end events. For a given (process, session) | |
607 | * pair, begin/end events are serialized and will match. However, in a | |
608 | * session, statedumps from different processes may be | |
609 | * interleaved. The vpid context should be used to identify which | |
610 | * events belong to which process. | |
8002ea62 MD |
611 | * |
612 | * Grab the ust_lock outside of the RCU read-side lock because we | |
613 | * perform synchronize_rcu with the ust_lock held, which can trigger | |
614 | * deadlocks otherwise. | |
cf73e0fe AB |
615 | */ |
616 | int do_lttng_ust_statedump(void *owner) | |
617 | { | |
8002ea62 | 618 | ust_lock_nocheck(); |
cf73e0fe | 619 | trace_statedump_start(owner); |
8002ea62 MD |
620 | ust_unlock(); |
621 | ||
94be38e8 | 622 | do_procname_statedump(owner); |
cf73e0fe | 623 | do_baddr_statedump(owner); |
8002ea62 MD |
624 | |
625 | ust_lock_nocheck(); | |
cf73e0fe | 626 | trace_statedump_end(owner); |
8002ea62 | 627 | ust_unlock(); |
cf73e0fe AB |
628 | |
629 | return 0; | |
630 | } | |
631 | ||
632 | void lttng_ust_statedump_init(void) | |
633 | { | |
ac9f44c2 MJ |
634 | lttng_ust__tracepoints__init(); |
635 | lttng_ust__tracepoints__ptrs_init(); | |
11bce056 | 636 | lttng_ust__events_init__lttng_ust_statedump(); |
97c7c238 MD |
637 | lttng_ust_dl_update(LTTNG_UST_CALLER_IP()); |
638 | } | |
639 | ||
640 | static | |
641 | void ust_dl_state_destroy(void) | |
642 | { | |
643 | unsigned int i; | |
644 | ||
645 | for (i = 0; i < UST_DL_STATE_TABLE_SIZE; i++) { | |
646 | struct cds_hlist_head *head; | |
647 | struct lttng_ust_dl_node *e, *tmp; | |
648 | ||
649 | head = &dl_state_table[i]; | |
650 | cds_hlist_for_each_entry_safe_2(e, tmp, head, node) | |
651 | free_dl_node(e); | |
652 | CDS_INIT_HLIST_HEAD(head); | |
653 | } | |
cf73e0fe AB |
654 | } |
655 | ||
656 | void lttng_ust_statedump_destroy(void) | |
657 | { | |
638ce920 | 658 | lttng_ust__events_exit__lttng_ust_statedump(); |
ac9f44c2 MJ |
659 | lttng_ust__tracepoints__ptrs_destroy(); |
660 | lttng_ust__tracepoints__destroy(); | |
97c7c238 | 661 | ust_dl_state_destroy(); |
cf73e0fe | 662 | } |