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