debug-info filter plugin
[babeltrace.git] / plugins / debug-info / debug-info.c
1 /*
2 * Babeltrace - Debug Information State Tracker
3 *
4 * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation
5 * Copyright (c) 2015 Philippe Proulx <pproulx@efficios.com>
6 * Copyright (c) 2015 Antoine Busque <abusque@efficios.com>
7 * Copyright (c) 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28 #include <assert.h>
29 #include <glib.h>
30 #include "debug-info.h"
31 #include "bin-info.h"
32 #include "utils.h"
33 #include "copy.h"
34
35 struct proc_debug_info_sources {
36 /*
37 * Hash table: base address (pointer to uint64_t) to bin info; owned by
38 * proc_debug_info_sources.
39 */
40 GHashTable *baddr_to_bin_info;
41
42 /*
43 * Hash table: IP (pointer to uint64_t) to (struct debug_info_source *);
44 * owned by proc_debug_info_sources.
45 */
46 GHashTable *ip_to_debug_info_src;
47 };
48
49 struct debug_info {
50 /*
51 * Hash table of VPIDs (pointer to int64_t) to
52 * (struct ctf_proc_debug_infos*); owned by debug_info.
53 */
54 GHashTable *vpid_to_proc_dbg_info_src;
55 GQuark q_statedump_bin_info;
56 GQuark q_statedump_debug_link;
57 GQuark q_statedump_build_id;
58 GQuark q_statedump_start;
59 GQuark q_dl_open;
60 GQuark q_lib_load;
61 GQuark q_lib_unload;
62 };
63
64 static
65 int debug_info_init(struct debug_info *info)
66 {
67 info->q_statedump_bin_info = g_quark_from_string(
68 "lttng_ust_statedump:bin_info");
69 info->q_statedump_debug_link = g_quark_from_string(
70 "lttng_ust_statedump:debug_link)");
71 info->q_statedump_build_id = g_quark_from_string(
72 "lttng_ust_statedump:build_id");
73 info->q_statedump_start = g_quark_from_string(
74 "lttng_ust_statedump:start");
75 info->q_dl_open = g_quark_from_string("lttng_ust_dl:dlopen");
76 info->q_lib_load = g_quark_from_string("lttng_ust_lib:load");
77 info->q_lib_unload = g_quark_from_string("lttng_ust_lib:unload");
78
79 return bin_info_init();
80 }
81
82 static
83 void debug_info_source_destroy(struct debug_info_source *debug_info_src)
84 {
85 if (!debug_info_src) {
86 return;
87 }
88
89 free(debug_info_src->func);
90 free(debug_info_src->src_path);
91 free(debug_info_src->bin_path);
92 free(debug_info_src->bin_loc);
93 g_free(debug_info_src);
94 }
95
96 static
97 struct debug_info_source *debug_info_source_create_from_bin(struct bin_info *bin,
98 uint64_t ip)
99 {
100 int ret;
101 struct debug_info_source *debug_info_src = NULL;
102 struct source_location *src_loc = NULL;
103
104 debug_info_src = g_new0(struct debug_info_source, 1);
105
106 if (!debug_info_src) {
107 goto end;
108 }
109
110 /* Lookup function name */
111 ret = bin_info_lookup_function_name(bin, ip, &debug_info_src->func);
112 if (ret) {
113 goto error;
114 }
115
116 /* Can't retrieve src_loc from ELF, or could not find binary, skip. */
117 if (!bin->is_elf_only || !debug_info_src->func) {
118 /* Lookup source location */
119 ret = bin_info_lookup_source_location(bin, ip, &src_loc);
120 printf_verbose("Failed to lookup source location (err: %i)\n", ret);
121 }
122
123 if (src_loc) {
124 debug_info_src->line_no = src_loc->line_no;
125
126 if (src_loc->filename) {
127 debug_info_src->src_path = strdup(src_loc->filename);
128 if (!debug_info_src->src_path) {
129 goto error;
130 }
131
132 debug_info_src->short_src_path = get_filename_from_path(
133 debug_info_src->src_path);
134 }
135
136 source_location_destroy(src_loc);
137 }
138
139 if (bin->elf_path) {
140 debug_info_src->bin_path = strdup(bin->elf_path);
141 if (!debug_info_src->bin_path) {
142 goto error;
143 }
144
145 debug_info_src->short_bin_path = get_filename_from_path(
146 debug_info_src->bin_path);
147
148 ret = bin_info_get_bin_loc(bin, ip, &(debug_info_src->bin_loc));
149 if (ret) {
150 goto error;
151 }
152 }
153
154 end:
155 return debug_info_src;
156
157 error:
158 debug_info_source_destroy(debug_info_src);
159 return NULL;
160 }
161
162 static
163 void proc_debug_info_sources_destroy(
164 struct proc_debug_info_sources *proc_dbg_info_src)
165 {
166 if (!proc_dbg_info_src) {
167 return;
168 }
169
170 if (proc_dbg_info_src->baddr_to_bin_info) {
171 g_hash_table_destroy(proc_dbg_info_src->baddr_to_bin_info);
172 }
173
174 if (proc_dbg_info_src->ip_to_debug_info_src) {
175 g_hash_table_destroy(proc_dbg_info_src->ip_to_debug_info_src);
176 }
177
178 g_free(proc_dbg_info_src);
179 }
180
181 static
182 struct proc_debug_info_sources *proc_debug_info_sources_create(void)
183 {
184 struct proc_debug_info_sources *proc_dbg_info_src = NULL;
185
186 proc_dbg_info_src = g_new0(struct proc_debug_info_sources, 1);
187 if (!proc_dbg_info_src) {
188 goto end;
189 }
190
191 proc_dbg_info_src->baddr_to_bin_info = g_hash_table_new_full(
192 g_int64_hash, g_int64_equal, (GDestroyNotify) g_free,
193 (GDestroyNotify) bin_info_destroy);
194 if (!proc_dbg_info_src->baddr_to_bin_info) {
195 goto error;
196 }
197
198 proc_dbg_info_src->ip_to_debug_info_src = g_hash_table_new_full(
199 g_int64_hash, g_int64_equal, (GDestroyNotify) g_free,
200 (GDestroyNotify) debug_info_source_destroy);
201 if (!proc_dbg_info_src->ip_to_debug_info_src) {
202 goto error;
203 }
204
205 end:
206 return proc_dbg_info_src;
207
208 error:
209 proc_debug_info_sources_destroy(proc_dbg_info_src);
210 return NULL;
211 }
212
213 static
214 struct proc_debug_info_sources *proc_debug_info_sources_ht_get_entry(
215 GHashTable *ht, int64_t vpid)
216 {
217 gpointer key = g_new0(int64_t, 1);
218 struct proc_debug_info_sources *proc_dbg_info_src = NULL;
219
220 if (!key) {
221 goto end;
222 }
223
224 *((int64_t *) key) = vpid;
225
226 /* Exists? Return it */
227 proc_dbg_info_src = g_hash_table_lookup(ht, key);
228 if (proc_dbg_info_src) {
229 goto end;
230 }
231
232 /* Otherwise, create and return it */
233 proc_dbg_info_src = proc_debug_info_sources_create();
234 if (!proc_dbg_info_src) {
235 goto end;
236 }
237
238 g_hash_table_insert(ht, key, proc_dbg_info_src);
239 /* Ownership passed to ht */
240 key = NULL;
241 end:
242 g_free(key);
243 return proc_dbg_info_src;
244 }
245
246 static
247 struct debug_info_source *proc_debug_info_sources_get_entry(
248 struct proc_debug_info_sources *proc_dbg_info_src, uint64_t ip)
249 {
250 struct debug_info_source *debug_info_src = NULL;
251 gpointer key = g_new0(uint64_t, 1);
252 GHashTableIter iter;
253 gpointer baddr, value;
254
255 if (!key) {
256 goto end;
257 }
258
259 *((uint64_t *) key) = ip;
260
261 /* Look in IP to debug infos hash table first. */
262 debug_info_src = g_hash_table_lookup(
263 proc_dbg_info_src->ip_to_debug_info_src,
264 key);
265 if (debug_info_src) {
266 goto end;
267 }
268
269 /* Check in all bin_infos. */
270 g_hash_table_iter_init(&iter, proc_dbg_info_src->baddr_to_bin_info);
271
272 while (g_hash_table_iter_next(&iter, &baddr, &value))
273 {
274 struct bin_info *bin = value;
275
276 if (!bin_info_has_address(value, ip)) {
277 continue;
278 }
279
280 /*
281 * Found; add it to cache.
282 *
283 * FIXME: this should be bounded in size (and implement
284 * a caching policy), and entries should be prunned when
285 * libraries are unmapped.
286 */
287 debug_info_src = debug_info_source_create_from_bin(bin, ip);
288 if (debug_info_src) {
289 g_hash_table_insert(
290 proc_dbg_info_src->ip_to_debug_info_src,
291 key, debug_info_src);
292 /* Ownership passed to ht. */
293 key = NULL;
294 }
295 break;
296 }
297
298 end:
299 free(key);
300 return debug_info_src;
301 }
302
303 BT_HIDDEN
304 struct debug_info_source *debug_info_query(struct debug_info *debug_info,
305 int64_t vpid, uint64_t ip)
306 {
307 struct debug_info_source *dbg_info_src = NULL;
308 struct proc_debug_info_sources *proc_dbg_info_src;
309
310 proc_dbg_info_src = proc_debug_info_sources_ht_get_entry(
311 debug_info->vpid_to_proc_dbg_info_src, vpid);
312 if (!proc_dbg_info_src) {
313 goto end;
314 }
315
316 dbg_info_src = proc_debug_info_sources_get_entry(proc_dbg_info_src, ip);
317
318 end:
319 return dbg_info_src;
320 }
321
322 BT_HIDDEN
323 struct debug_info *debug_info_create(void)
324 {
325 int ret;
326 struct debug_info *debug_info;
327
328 debug_info = g_new0(struct debug_info, 1);
329 if (!debug_info) {
330 goto end;
331 }
332
333 debug_info->vpid_to_proc_dbg_info_src = g_hash_table_new_full(
334 g_int64_hash, g_int64_equal, (GDestroyNotify) g_free,
335 (GDestroyNotify) proc_debug_info_sources_destroy);
336 if (!debug_info->vpid_to_proc_dbg_info_src) {
337 goto error;
338 }
339
340 ret = debug_info_init(debug_info);
341 if (ret) {
342 goto error;
343 }
344
345 end:
346 return debug_info;
347 error:
348 g_free(debug_info);
349 return NULL;
350 }
351
352 BT_HIDDEN
353 void debug_info_destroy(struct debug_info *debug_info)
354 {
355 if (!debug_info) {
356 goto end;
357 }
358
359 if (debug_info->vpid_to_proc_dbg_info_src) {
360 g_hash_table_destroy(debug_info->vpid_to_proc_dbg_info_src);
361 }
362
363 g_free(debug_info);
364 end:
365 return;
366 }
367
368 static
369 void handle_statedump_build_id_event(FILE *err, struct debug_info *debug_info,
370 struct bt_ctf_event *event)
371 {
372 struct proc_debug_info_sources *proc_dbg_info_src;
373 struct bin_info *bin = NULL;
374 int ret;
375 int64_t vpid;
376 uint64_t baddr;
377
378 ret = get_stream_event_context_int_field_value(err,
379 event, "_vpid", &vpid);
380 if (ret) {
381 fprintf(err, "[error] %s in %s:%d\n", __func__,
382 __FILE__, __LINE__);
383 ret = -1;
384 goto end;
385 }
386
387 ret = get_payload_unsigned_int_field_value(err,
388 event, "_baddr", &baddr);
389 if (ret) {
390 fprintf(err, "[error] %s in %s:%d\n", __func__,
391 __FILE__, __LINE__);
392 ret = -1;
393 goto end;
394 }
395
396 proc_dbg_info_src = proc_debug_info_sources_ht_get_entry(
397 debug_info->vpid_to_proc_dbg_info_src, vpid);
398 if (!proc_dbg_info_src) {
399 goto end;
400 }
401
402 bin = g_hash_table_lookup(proc_dbg_info_src->baddr_to_bin_info,
403 (gpointer) &baddr);
404 if (!bin) {
405 /*
406 * The build_id event comes after the bin has been
407 * created. If it isn't found, just ignore this event.
408 */
409 goto end;
410 }
411
412 ret = get_payload_build_id_field_value(err, event, "_build_id",
413 &bin->build_id, &bin->build_id_len);
414 if (ret) {
415 fprintf(err, "[error] %s in %s:%d\n", __func__,
416 __FILE__, __LINE__);
417 ret = -1;
418 goto end;
419 }
420
421 /*
422 * Reset the is_elf_only flag in case it had been set
423 * previously, because we might find separate debug info using
424 * the new build id information.
425 */
426 bin->is_elf_only = false;
427
428 // TODO
429 // bin_info_set_build_id(bin, build_id, build_id_len);
430
431 end:
432 return;
433 }
434
435 static
436 void handle_statedump_debug_link_event(FILE *err, struct debug_info *debug_info,
437 struct bt_ctf_event *event)
438 {
439 struct proc_debug_info_sources *proc_dbg_info_src;
440 struct bin_info *bin = NULL;
441 int64_t vpid;
442 uint64_t baddr;
443 const char *filename = NULL;
444 uint32_t crc32;
445 uint64_t tmp;
446 int ret;
447
448 ret = get_stream_event_context_int_field_value(err, event,
449 "_vpid", &vpid);
450 if (ret) {
451 fprintf(err, "[error] %s in %s:%d\n", __func__,
452 __FILE__, __LINE__);
453 ret = -1;
454 goto end;
455 }
456
457 ret = get_payload_unsigned_int_field_value(err,
458 event, "_baddr", &baddr);
459 if (ret) {
460 fprintf(err, "[error] %s in %s:%d\n", __func__,
461 __FILE__, __LINE__);
462 ret = -1;
463 goto end;
464 }
465
466 ret = get_payload_unsigned_int_field_value(err, event, "_crc32", &tmp);
467 if (ret) {
468 fprintf(err, "[error] %s in %s:%d\n", __func__,
469 __FILE__, __LINE__);
470 ret = -1;
471 goto end;
472 }
473 crc32 = (uint32_t) tmp;
474
475 ret = get_payload_string_field_value(err,
476 event, "_filename", &filename);
477 if (ret) {
478 fprintf(err, "[error] %s in %s:%d\n", __func__,
479 __FILE__, __LINE__);
480 ret = -1;
481 goto end;
482 }
483
484 proc_dbg_info_src = proc_debug_info_sources_ht_get_entry(
485 debug_info->vpid_to_proc_dbg_info_src, vpid);
486 if (!proc_dbg_info_src) {
487 goto end;
488 }
489
490 bin = g_hash_table_lookup(proc_dbg_info_src->baddr_to_bin_info,
491 (gpointer) &baddr);
492 if (!bin) {
493 /*
494 * The debug_link event comes after the bin has been
495 * created. If it isn't found, just ignore this event.
496 */
497 goto end;
498 }
499
500 bin_info_set_debug_link(bin, filename, crc32);
501
502 end:
503 return;
504 }
505
506 static
507 void handle_bin_info_event(FILE *err, struct debug_info *debug_info,
508 struct bt_ctf_event *event, bool has_pic_field)
509 {
510 struct proc_debug_info_sources *proc_dbg_info_src;
511 struct bin_info *bin;
512 uint64_t baddr, memsz;
513 int64_t vpid;
514 const char *path;
515 gpointer key = NULL;
516 bool is_pic;
517 int ret;
518
519 ret = get_payload_unsigned_int_field_value(err,
520 event, "_baddr", &baddr);
521 if (ret) {
522 fprintf(err, "[error] %s in %s:%d\n", __func__,
523 __FILE__, __LINE__);
524 ret = -1;
525 goto end;
526 }
527
528 ret = get_payload_unsigned_int_field_value(err,
529 event, "_memsz", &memsz);
530 if (ret) {
531 fprintf(err, "[error] %s in %s:%d\n", __func__,
532 __FILE__, __LINE__);
533 ret = -1;
534 goto end;
535 }
536
537 ret = get_payload_string_field_value(err,
538 event, "_path", &path);
539 if (ret) {
540 fprintf(err, "[error] %s in %s:%d\n", __func__,
541 __FILE__, __LINE__);
542 ret = -1;
543 goto end;
544 }
545
546 if (has_pic_field) {
547 uint64_t tmp;
548
549 ret = get_payload_unsigned_int_field_value(err,
550 event, "_is_pic", &tmp);
551 if (ret) {
552 fprintf(err, "[error] %s in %s:%d\n", __func__,
553 __FILE__, __LINE__);
554 ret = -1;
555 goto end;
556 }
557 is_pic = (tmp == 1);
558 } else {
559 /*
560 * dlopen has no is_pic field, because the shared
561 * object is always PIC.
562 */
563 is_pic = true;
564 }
565
566 ret = get_stream_event_context_int_field_value(err, event, "_vpid",
567 &vpid);
568 if (ret) {
569 fprintf(err, "[error] %s in %s:%d\n", __func__,
570 __FILE__, __LINE__);
571 ret = -1;
572 goto end;
573 }
574
575 if (!path) {
576 goto end;
577 }
578
579 if (memsz == 0) {
580 /* Ignore VDSO. */
581 goto end;
582 }
583
584 proc_dbg_info_src = proc_debug_info_sources_ht_get_entry(
585 debug_info->vpid_to_proc_dbg_info_src, vpid);
586 if (!proc_dbg_info_src) {
587 goto end;
588 }
589
590 key = g_new0(uint64_t, 1);
591 if (!key) {
592 goto end;
593 }
594
595 *((uint64_t *) key) = baddr;
596
597 bin = g_hash_table_lookup(proc_dbg_info_src->baddr_to_bin_info,
598 key);
599 if (bin) {
600 goto end;
601 }
602
603 bin = bin_info_create(path, baddr, memsz, is_pic);
604 if (!bin) {
605 goto end;
606 }
607
608 g_hash_table_insert(proc_dbg_info_src->baddr_to_bin_info,
609 key, bin);
610 /* Ownership passed to ht. */
611 key = NULL;
612
613 end:
614 g_free(key);
615 return;
616 }
617
618 static inline
619 void handle_statedump_bin_info_event(FILE *err, struct debug_info *debug_info,
620 struct bt_ctf_event *event)
621 {
622 handle_bin_info_event(err, debug_info, event, true);
623 }
624
625 static inline
626 void handle_lib_load_event(FILE *err, struct debug_info *debug_info,
627 struct bt_ctf_event *event)
628 {
629 handle_bin_info_event(err, debug_info, event, false);
630 }
631
632 static inline
633 void handle_lib_unload_event(FILE *err, struct debug_info *debug_info,
634 struct bt_ctf_event *event)
635 {
636 struct proc_debug_info_sources *proc_dbg_info_src;
637 uint64_t baddr;
638 int64_t vpid;
639 gpointer key_ptr = NULL;
640 int ret;
641
642 ret = get_payload_unsigned_int_field_value(err,
643 event, "_baddr", &baddr);
644 if (ret) {
645 fprintf(err, "[error] %s in %s:%d\n", __func__,
646 __FILE__, __LINE__);
647 ret = -1;
648 goto end;
649 }
650
651 ret = get_stream_event_context_int_field_value(err, event, "_vpid",
652 &vpid);
653 if (ret) {
654 fprintf(err, "[error] %s in %s:%d\n", __func__,
655 __FILE__, __LINE__);
656 ret = -1;
657 goto end;
658 }
659
660 proc_dbg_info_src = proc_debug_info_sources_ht_get_entry(
661 debug_info->vpid_to_proc_dbg_info_src, vpid);
662 if (!proc_dbg_info_src) {
663 goto end;
664 }
665
666 key_ptr = (gpointer) &baddr;
667 (void) g_hash_table_remove(proc_dbg_info_src->baddr_to_bin_info,
668 key_ptr);
669 end:
670 return;
671 }
672
673 static
674 void handle_statedump_start(FILE *err, struct debug_info *debug_info,
675 struct bt_ctf_event *event)
676 {
677 struct proc_debug_info_sources *proc_dbg_info_src;
678 int64_t vpid;
679 int ret;
680
681 ret = get_stream_event_context_int_field_value(err, event,
682 "_vpid", &vpid);
683 if (ret) {
684 fprintf(err, "[error] %s in %s:%d\n", __func__,
685 __FILE__, __LINE__);
686 ret = -1;
687 goto end;
688 }
689
690 proc_dbg_info_src = proc_debug_info_sources_ht_get_entry(
691 debug_info->vpid_to_proc_dbg_info_src, vpid);
692 if (!proc_dbg_info_src) {
693 goto end;
694 }
695
696 g_hash_table_remove_all(proc_dbg_info_src->baddr_to_bin_info);
697 g_hash_table_remove_all(proc_dbg_info_src->ip_to_debug_info_src);
698
699 end:
700 return;
701 }
702
703 BT_HIDDEN
704 void debug_info_handle_event(FILE *err, struct bt_ctf_event *event,
705 struct debug_info *debug_info)
706 {
707 struct bt_ctf_event_class *event_class;
708 const char *event_name;
709 GQuark q_event_name;
710
711 if (!debug_info || !event) {
712 goto end;
713 }
714 event_class = bt_ctf_event_get_class(event);
715 if (!event_class) {
716 goto end;
717 }
718 event_name = bt_ctf_event_class_get_name(event_class);
719 if (!event_name) {
720 goto end_put_class;
721 }
722 q_event_name = g_quark_try_string(event_name);
723
724 if (q_event_name == debug_info->q_statedump_bin_info) {
725 /* State dump */
726 handle_statedump_bin_info_event(err, debug_info, event);
727 } else if (q_event_name == debug_info->q_dl_open ||
728 q_event_name == debug_info->q_lib_load) {
729 /*
730 * dl_open and lib_load events are both checked for since
731 * only dl_open was produced as of lttng-ust 2.8.
732 *
733 * lib_load, which is produced from lttng-ust 2.9+, is a lot
734 * more reliable since it will be emitted when other functions
735 * of the dlopen family are called (e.g. dlmopen) and when
736 * library are transitively loaded.
737 */
738 handle_lib_load_event(err, debug_info, event);
739 } else if (q_event_name == debug_info->q_statedump_start) {
740 /* Start state dump */
741 handle_statedump_start(err, debug_info, event);
742 } else if (q_event_name == debug_info->q_statedump_debug_link) {
743 /* Debug link info */
744 handle_statedump_debug_link_event(err, debug_info, event);
745 } else if (q_event_name == debug_info->q_statedump_build_id) {
746 /* Build ID info */
747 handle_statedump_build_id_event(err, debug_info, event);
748 } else if (q_event_name == debug_info-> q_lib_unload) {
749 handle_lib_unload_event(err, debug_info, event);
750 }
751
752 end_put_class:
753 bt_put(event_class);
754 end:
755 return;
756 }
This page took 0.048404 seconds and 4 git commands to generate.