source: XIOS3/trunk/extern/cpptrace/src/symbols_with_libdwarf.cpp

Last change on this file was 2573, checked in by ymipsl, 9 months ago

create new external source lib : cpptrace, for statck trace output
YM

File size: 41.8 KB
Line 
1#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF
2
3#include "cpptrace.hpp"
4#include "symbols.hpp"
5#include "common.hpp"
6#include "program_name.hpp"
7#include "object.hpp"
8
9#include <cassert>
10#include <cstdint>
11#include <cstdio>
12#include <functional>
13#include <memory>
14#include <mutex>
15#include <type_traits>
16#include <vector>
17
18#include <libdwarf.h>
19#include <dwarf.h>
20
21// some stuff is based on https://github.com/davea42/libdwarf-addr2line/blob/master/addr2line.c, mainly line handling
22// then much expanded for symbols and efficiency
23// dwarf5_ranges and dwarf4_ranges utility functions are taken from there directly, also pc_in_die
24
25// TODO
26// Inlined calls
27// Memoizing / lazy loading
28// More utils to clean this up, some wrapper for unique_ptr
29// Ensure memory is being cleaned up properly
30// Efficiency tricks
31// Implementation cleanup
32// Properly get the image base
33
34#define DW_PR_DUx "llx"
35#define DW_PR_DUu "llu"
36
37Dwarf_Unsigned get_ranges_offset(Dwarf_Attribute attr) {
38    Dwarf_Unsigned off = 0;
39    Dwarf_Half attrform = 0;
40    dwarf_whatform(attr, &attrform, nullptr);
41    if (attrform == DW_FORM_rnglistx) {
42        int fres = dwarf_formudata(attr, &off, nullptr);
43        assert(fres == DW_DLV_OK);
44    } else {
45        int fres = dwarf_global_formref(attr, &off, nullptr);
46        assert(fres == DW_DLV_OK);
47    }
48    return off;
49}
50
51static int dwarf5_ranges(Dwarf_Die cu_die, Dwarf_Addr *lowest, Dwarf_Addr *highest) {
52    Dwarf_Unsigned offset = 0;
53    Dwarf_Attribute attr = 0;
54    Dwarf_Half attrform = 0;
55    Dwarf_Unsigned i = 0;
56    int res = 0;
57
58    res = dwarf_attr(cu_die, DW_AT_ranges, &attr, nullptr);
59    if(res != DW_DLV_OK) {
60        return res;
61    }
62    offset = get_ranges_offset(attr);
63    if(true) {
64        Dwarf_Unsigned rlesetoffset = 0;
65        Dwarf_Unsigned rnglists_count = 0;
66        Dwarf_Rnglists_Head head = 0;
67
68        dwarf_whatform(attr, &attrform, nullptr);
69        /* offset is in .debug_rnglists */
70        res = dwarf_rnglists_get_rle_head(
71            attr,
72            attrform,
73            offset,
74            &head,
75            &rnglists_count,
76            &rlesetoffset,
77            nullptr
78        );
79        if(res != DW_DLV_OK) {
80            /* ASSERT: is DW_DLV_NO_ENTRY */
81            dwarf_dealloc_attribute(attr);
82            return res;
83        }
84        for( ; i < rnglists_count; ++i) {
85            unsigned entrylen = 0;
86            unsigned rle_val = 0;
87            Dwarf_Unsigned raw1 = 0;
88            Dwarf_Unsigned raw2 = 0;
89            Dwarf_Bool unavail = 0;
90            Dwarf_Unsigned cooked1 = 0;
91            Dwarf_Unsigned cooked2 = 0;
92
93            res = dwarf_get_rnglists_entry_fields_a(
94                head,
95                i,
96                &entrylen,
97                &rle_val,
98                &raw1,
99                &raw2,
100                &unavail,
101                &cooked1,
102                &cooked2,
103                nullptr
104            );
105            if(res != DW_DLV_OK) {
106                /* ASSERT: is DW_DLV_NO_ENTRY */
107                continue;
108            }
109            if(unavail) {
110                continue;
111            }
112            switch(rle_val) {
113            case DW_RLE_end_of_list:
114            case DW_RLE_base_address:
115            case DW_RLE_base_addressx:
116                /* These are accounted for already */
117                break;
118            case DW_RLE_offset_pair:
119            case DW_RLE_startx_endx:
120            case DW_RLE_start_end:
121            case DW_RLE_startx_length:
122            case DW_RLE_start_length:
123                if(cooked1 < *lowest) {
124                    *lowest = cooked1;
125                }
126                if(cooked2 > *highest) {
127                    *highest = cooked2;
128                }
129            default:
130                /* Something is wrong. */
131                break;
132
133            }
134        }
135        dwarf_dealloc_rnglists_head(head);
136    }
137    dwarf_dealloc_attribute(attr);
138    return DW_DLV_OK;
139}
140
141static int dwarf4_ranges(
142    Dwarf_Debug dbg,
143    Dwarf_Die cu_die,
144    Dwarf_Addr cu_lowpc,
145    Dwarf_Addr *lowest,
146    Dwarf_Addr *highest
147) {
148    Dwarf_Unsigned offset;
149    Dwarf_Attribute attr = 0;
150    int res = 0;
151
152    res = dwarf_attr(cu_die, DW_AT_ranges, &attr, nullptr);
153    if(res != DW_DLV_OK) {
154        return res;
155    }
156    if(dwarf_global_formref(attr, &offset, nullptr) == DW_DLV_OK) {
157        Dwarf_Signed count = 0;
158        Dwarf_Ranges *ranges = 0;
159        Dwarf_Addr baseaddr = 0;
160        if(cu_lowpc != 0xffffffffffffffff) {
161            baseaddr = cu_lowpc;
162        }
163        res = dwarf_get_ranges_b(
164            dbg,
165            offset,
166            cu_die,
167            nullptr,
168            &ranges,
169            &count,
170            nullptr,
171            nullptr
172        );
173        for(int i = 0; i < count; i++) {
174            Dwarf_Ranges *cur = ranges + i;
175
176            if(cur->dwr_type == DW_RANGES_ENTRY) {
177                Dwarf_Addr rng_lowpc, rng_highpc;
178                rng_lowpc = baseaddr + cur->dwr_addr1;
179                rng_highpc = baseaddr + cur->dwr_addr2;
180                if(rng_lowpc < *lowest) {
181                    *lowest = rng_lowpc;
182                }
183                if(rng_highpc > *highest) {
184                    *highest = rng_highpc;
185                }
186            } else if(cur->dwr_type ==
187                DW_RANGES_ADDRESS_SELECTION) {
188                baseaddr = cur->dwr_addr2;
189            } else {  // DW_RANGES_END
190                baseaddr = cu_lowpc;
191            }
192        }
193        dwarf_dealloc_ranges(dbg, ranges, count);
194    }
195    dwarf_dealloc_attribute(attr);
196    return DW_DLV_OK;
197}
198
199namespace cpptrace {
200    namespace detail {
201        namespace libdwarf {
202            // printbugging as we go
203            constexpr bool dump_dwarf = false;
204            constexpr bool trace_dwarf = false;
205
206            static void err_handler(Dwarf_Error err, Dwarf_Ptr errarg) {
207                printf("libdwarf error reading %s: %lu %s\n", "xx", (unsigned long)dwarf_errno(err), dwarf_errmsg(err));
208                if(errarg) {
209                    printf("Error: errarg is nonnull but it should be null\n");
210                }
211                printf("Giving up");
212                exit(1);
213            }
214
215            static void print_line(Dwarf_Debug dbg, Dwarf_Line line, Dwarf_Addr pc, stacktrace_frame& frame) {
216                char what[] = "??";
217                char *         linesrc = what;
218                Dwarf_Unsigned lineno = 0;
219
220                (void)pc;
221
222                if(line) {
223                    /*  These never return DW_DLV_NO_ENTRY */
224                    dwarf_linesrc(line, &linesrc, nullptr);
225                    dwarf_lineno(line, &lineno, nullptr);
226                }
227                if(dump_dwarf) {
228                    printf("%s:%" DW_PR_DUu "\n", linesrc, lineno);
229                }
230                frame.line = static_cast<uint_least32_t>(lineno);
231                frame.filename = linesrc;
232                if(line) {
233                    dwarf_dealloc(dbg, linesrc, DW_DLA_STRING);
234                }
235            }
236
237            static Dwarf_Bool pc_in_die(Dwarf_Debug dbg, Dwarf_Die die,int version, Dwarf_Addr pc) {
238                int ret;
239                Dwarf_Addr cu_lowpc = 0xffffffffffffffff;
240                Dwarf_Addr cu_highpc = 0;
241                enum Dwarf_Form_Class highpc_cls;
242                Dwarf_Addr lowest = 0xffffffffffffffff;
243                Dwarf_Addr highest = 0;
244
245                ret = dwarf_lowpc(die, &cu_lowpc, nullptr);
246                if(ret == DW_DLV_OK) {
247                    if(pc == cu_lowpc) {
248                        return true;
249                    }
250                    ret = dwarf_highpc_b(die, &cu_highpc,
251                        nullptr, &highpc_cls, nullptr);
252                    if(ret == DW_DLV_OK) {
253                        if(highpc_cls == DW_FORM_CLASS_CONSTANT) {
254                            cu_highpc += cu_lowpc;
255                        }
256                        //fprintf(stderr, "low: %llx  high: %llx  pc: %llx\n", cu_lowpc, cu_highpc, pc);
257                        if(pc >= cu_lowpc && pc < cu_highpc) {
258                            return true;
259                        }
260                    }
261                }
262                if(version >= 5) {
263                    ret = dwarf5_ranges(die,
264                        &lowest,&highest);
265                } else {
266                    ret = dwarf4_ranges(dbg,die,cu_lowpc,
267                        &lowest,&highest);
268                }
269                //fprintf(stderr, "low: %llu  high: %llu\n", lowest, highest);
270                if(pc >= lowest && pc < highest) {
271                    return true;
272                }
273                return false;
274            }
275
276            static_assert(std::is_pointer<Dwarf_Die>::value, "Dwarf_Die not a pointer");
277            static_assert(std::is_pointer<Dwarf_Debug>::value, "Dwarf_Debug not a pointer");
278
279            struct die_object {
280                Dwarf_Debug dbg = nullptr;
281                Dwarf_Die die = nullptr;
282
283                die_object(Dwarf_Debug dbg, Dwarf_Die die) : dbg(dbg), die(die) {}
284
285                ~die_object() {
286                    if(die) {
287                        dwarf_dealloc(dbg, die, DW_DLA_DIE);
288                    }
289                }
290
291                die_object(const die_object&) = delete;
292
293                die_object& operator=(const die_object&) = delete;
294
295                die_object(die_object&& other) : dbg(other.dbg), die(other.die) {
296                    other.die = nullptr;
297                }
298
299                die_object& operator=(die_object&& other) {
300                    dbg = other.dbg;
301                    die = other.die;
302                    other.die = nullptr;
303                    return *this;
304                }
305
306                die_object get_child() const {
307                    Dwarf_Die child = nullptr;
308                    int ret = dwarf_child(
309                        die,
310                        &child,
311                        nullptr
312                    );
313                    if(ret == DW_DLV_OK) {
314                        return die_object(dbg, child);
315                    } else if(ret == DW_DLV_NO_ENTRY) {
316                        return die_object(dbg, 0);
317                    } else {
318                        fprintf(stderr, "Error\n");
319                        exit(1);
320                    }
321                }
322
323                die_object get_sibling() const {
324                    Dwarf_Die sibling = 0;
325                    int ret = dwarf_siblingof_b(dbg, die, true, &sibling, nullptr);
326                    if(ret == DW_DLV_OK) {
327                        return die_object(dbg, sibling);
328                    } else if(ret == DW_DLV_NO_ENTRY) {
329                        return die_object(dbg, 0);
330                    } else {
331                        fprintf(stderr, "Error\n");
332                        exit(1);
333                    }
334                }
335
336                operator bool() const {
337                    return die != nullptr;
338                }
339
340                Dwarf_Die get() const {
341                    return die;
342                }
343
344                std::string get_name() const {
345                    char* name;
346                    int ret = dwarf_diename(die, &name, nullptr);
347                    std::string str;
348                    if(ret != DW_DLV_NO_ENTRY) {
349                        str = name;
350                        dwarf_dealloc(dbg, name, DW_DLA_STRING);
351                    }
352                    return name;
353                }
354
355                optional<std::string> get_string_attribute(Dwarf_Half dw_attrnum) const {
356                    Dwarf_Attribute attr;
357                    int ret = dwarf_attr(die, dw_attrnum, &attr, nullptr);
358                    if(ret == DW_DLV_OK) {
359                        char* raw_str;
360                        std::string str;
361                        ret = dwarf_formstring(attr, &raw_str, nullptr);
362                        assert(ret == DW_DLV_OK);
363                        str = raw_str;
364                        dwarf_dealloc(dbg, raw_str, DW_DLA_STRING);
365                        dwarf_dealloc(dbg, attr, DW_DLA_ATTR);
366                        return str;
367                    } else {
368                        return nullopt;
369                    }
370                }
371
372                bool has_attr(Dwarf_Half dw_attrnum) const {
373                    Dwarf_Attribute attr;
374                    int ret = dwarf_attr(die, dw_attrnum, &attr, nullptr);
375                    if(ret == DW_DLV_NO_ENTRY) {
376                        return false;
377                    } else if(ret == DW_DLV_OK) {
378                        // TODO: Better impl that doesn't require allocation....?
379                        dwarf_dealloc(dbg, attr, DW_DLA_ATTR);
380                        return true;
381                    } else {
382                        fprintf(stderr, "Error\n");
383                        exit(1);
384                    }
385                }
386
387                Dwarf_Half get_tag() const {
388                    Dwarf_Half tag = 0;
389                    dwarf_tag(die, &tag, nullptr);
390                    return tag;
391                }
392
393                const char* get_tag_name() const {
394                    const char* tag_name;
395                    dwarf_get_TAG_name(get_tag(), &tag_name);
396                    return tag_name;
397                }
398
399                Dwarf_Off get_global_offset() const {
400                    Dwarf_Off off;
401                    int ret = dwarf_dieoffset(die, &off, nullptr);
402                    assert(ret == DW_DLV_OK);
403                    return off;
404                }
405
406                die_object resolve_reference_attribute(Dwarf_Half dw_attrnum) const {
407                    Dwarf_Attribute attr;
408                    int ret = dwarf_attr(die, dw_attrnum, &attr, nullptr);
409                    Dwarf_Half form = 0;
410                    ret = dwarf_whatform(attr, &form, nullptr);
411                    switch(form) {
412                        case DW_FORM_ref1:
413                        case DW_FORM_ref2:
414                        case DW_FORM_ref4:
415                        case DW_FORM_ref8:
416                        case DW_FORM_ref_udata:
417                            {
418                                Dwarf_Off off = 0;
419                                Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die);
420                                ret = dwarf_formref(attr, &off, &is_info, nullptr);
421                                assert(ret == DW_DLV_OK);
422                                Dwarf_Off goff = 0;
423                                ret = dwarf_convert_to_global_offset(attr, off, &goff, nullptr);
424                                assert(ret == DW_DLV_OK);
425                                Dwarf_Die targ_die_a = 0;
426                                ret = dwarf_offdie_b(dbg, goff, is_info, &targ_die_a, nullptr);
427                                assert(ret == DW_DLV_OK);
428                                return die_object(dbg, targ_die_a);
429                            }
430                        case DW_FORM_ref_addr:
431                            {
432                                Dwarf_Off off;
433                                ret = dwarf_global_formref(attr, &off, nullptr);
434                                int is_info_a = dwarf_get_die_infotypes_flag(die);
435                                Dwarf_Die targ_die_a = 0;
436                                ret = dwarf_offdie_b(dbg, off, is_info_a, &targ_die_a, nullptr);
437                                assert(ret == DW_DLV_OK);
438                                return die_object(dbg, targ_die_a);
439                            }
440                        case DW_FORM_ref_sig8:
441                            {
442                                Dwarf_Sig8 signature;
443                                ret = dwarf_formsig8(attr, &signature, nullptr);
444                                assert(ret == DW_DLV_OK);
445                                Dwarf_Die  targdie = 0;
446                                Dwarf_Bool targ_is_info = false;
447                                ret = dwarf_find_die_given_sig8(dbg, &signature, &targdie, &targ_is_info, nullptr);
448                                assert(ret == DW_DLV_OK);
449                                return die_object(dbg, targdie);
450                            }
451                        default:
452                            fprintf(stderr, "unknown form for attribute %d %d\n", dw_attrnum, form);
453                            exit(1);
454                    }
455                }
456            };
457
458            void walk_die_list(
459                Dwarf_Debug dbg,
460                const die_object& die,
461                std::function<void(Dwarf_Debug, const die_object&)> fn
462            ) {
463                fn(dbg, die);
464                die_object current = die.get_sibling();
465                while(true) {
466                    if(!current) {
467                        if(dump_dwarf) {
468                            fprintf(stderr, "End walk_die_list\n");
469                        }
470                        return;
471                    }
472                    fn(dbg, current);
473                    current = current.get_sibling();
474                }
475            }
476
477            void walk_die_list_recursive(
478                Dwarf_Debug dbg,
479                const die_object& die,
480                std::function<void(Dwarf_Debug, const die_object&)> fn
481            ) {
482                walk_die_list(
483                    dbg,
484                    die,
485                    [&fn](Dwarf_Debug dbg, const die_object& die) {
486                        auto child = die.get_child();
487                        if(child) {
488                            walk_die_list_recursive(dbg, child, fn);
489                        }
490                        fn(dbg, die);
491                    }
492                );
493            }
494
495            die_object get_type_die(Dwarf_Debug dbg, const die_object& die) {
496                Dwarf_Off type_offset;
497                Dwarf_Bool is_info;
498                int ret = dwarf_dietype_offset(die.get(), &type_offset, &is_info, nullptr);
499                if(ret == DW_DLV_OK) {
500                    Dwarf_Die type_die;
501                    ret = dwarf_offdie_b(
502                        dbg,
503                        type_offset,
504                        is_info,
505                        &type_die,
506                        nullptr
507                    );
508                    if(ret == DW_DLV_OK) {
509                        return die_object(dbg, type_die);
510                    } else {
511                        fprintf(stderr, "Error\n");
512                        exit(1);
513                    }
514                } else {
515                    fprintf(stderr, "no type offset??\n");
516                }
517                return die_object(dbg, nullptr);
518            }
519
520            bool has_type(Dwarf_Debug dbg, const die_object& die) {
521                Dwarf_Attribute attr;
522                int ret = dwarf_attr(die.get(), DW_AT_type, &attr, nullptr);
523                if(ret == DW_DLV_NO_ENTRY) {
524                    return false;
525                } else if(ret == DW_DLV_OK) {
526                    dwarf_dealloc(dbg, attr, DW_DLA_ATTR);
527                    return true;
528                } else {
529                    fprintf(stderr, "Error\n");
530                    exit(1);
531                }
532            }
533
534            struct type_result {
535                std::string base;
536                std::string extent;
537
538                std::string get_type() {
539                    return base + extent;
540                }
541            };
542
543            // TODO: ::*, namespace lookup, arrays
544            // DW_TAG_namespace
545            const char* tag_to_keyword(Dwarf_Half tag) {
546                switch(tag) {
547                    case DW_TAG_atomic_type:
548                        return "_Atomic";
549                    case DW_TAG_const_type:
550                        return "const";
551                    case DW_TAG_volatile_type:
552                        return "volatile";
553                    case DW_TAG_restrict_type:
554                        return "restrict";
555                    default:
556                        {
557                            const char* tag_name = nullptr;
558                            dwarf_get_TAG_name(tag, &tag_name);
559                            fprintf(stderr, "tag_to_keyword unknown tag %s\n", tag_name);
560                            exit(1);
561                        }
562                }
563            }
564            const char* tag_to_ptr_ref(Dwarf_Half tag) {
565                switch(tag) {
566                    case DW_TAG_pointer_type:
567                        return "*";
568                    case DW_TAG_ptr_to_member_type:
569                        return "::*"; // TODO
570                    case DW_TAG_reference_type:
571                        return "&";
572                    case DW_TAG_rvalue_reference_type:
573                        return "&&";
574                    default:
575                        {
576                            const char* tag_name = nullptr;
577                            dwarf_get_TAG_name(tag, &tag_name);
578                            fprintf(stderr, "tag_to_ptr_ref unknown tag %s\n", tag_name);
579                            exit(1);
580                        }
581                }
582            }
583
584            /*std::string resolve_type(Dwarf_Debug dbg, const die_object& die, std::string build = "");
585
586            std::string get_array_extents(Dwarf_Debug dbg, const die_object& die) {
587                assert(die.get_tag() == DW_TAG_array_type);
588                std::string extents = "";
589                walk_die_list(dbg, die.get_child(), [&extents](Dwarf_Debug dbg, const die_object& subrange) {
590                    if(subrange.get_tag() == DW_TAG_subrange_type) {
591                        Dwarf_Attribute attr = 0;
592                        int res = 0;
593                        res = dwarf_attr(subrange.get(), DW_AT_upper_bound, &attr, nullptr);
594                        if(res != DW_DLV_OK) {
595                            fprintf(stderr, "Error\n");
596                            return;
597                        }
598                        Dwarf_Half form;
599                        res = dwarf_whatform(attr, &form, nullptr);
600                        if(res != DW_DLV_OK) {
601                            fprintf(stderr, "Error\n");
602                            return;
603                        }
604                        //fprintf(stderr, "form: %d\n", form);
605                        Dwarf_Unsigned val;
606                        res = dwarf_formudata(attr, &val, nullptr);
607                        if(res != DW_DLV_OK) {
608                            fprintf(stderr, "Error\n");
609                            return;
610                        }
611                        extents += "[" + std::to_string(val + 1) + "]";
612                        dwarf_dealloc_attribute(attr);
613                    } else {
614                        fprintf(stderr, "unknown tag %s\n", subrange.get_tag_name());
615                    }
616                });
617                return extents;
618            }
619
620            std::string get_parameters(Dwarf_Debug dbg, const die_object& die) {
621                assert(die.get_tag() == DW_TAG_subroutine_type);
622                std::vector<std::string> params;
623                walk_die_list(dbg, die.get_child(), [&params](Dwarf_Debug dbg, const die_object& die) {
624                    if(die.get_tag() == DW_TAG_formal_parameter) {
625                        // TODO: Ignore DW_AT_artificial
626                        params.push_back(resolve_type(dbg, get_type_die(dbg, die)));
627                    }
628                });
629                return "(" + join(params, ", ") + ")";
630            }
631
632            std::string resolve_type(Dwarf_Debug dbg, const die_object& die, std::string build) {
633                switch(auto tag = die.get_tag()) {
634                    case DW_TAG_base_type:
635                    case DW_TAG_class_type:
636                    case DW_TAG_structure_type:
637                    case DW_TAG_union_type:
638                    case DW_TAG_enumeration_type:
639                        return die.get_name() + build;
640                    case DW_TAG_typedef:
641                        return resolve_type(dbg, get_type_die(dbg, die));
642                    //case DW_TAG_subroutine_type:
643                    //    {
644                    //        // If there's no DW_AT_type then it's a void
645                    //        std::vector<std::string> params;
646                    //        // TODO: Code duplication with retrieve_symbol_for_subprogram?
647                    //        walk_die_list(dbg, die.get_child(), [&params] (Dwarf_Debug dbg, const die_object& die) {
648                    //            if(die.get_tag() == DW_TAG_formal_parameter) {
649                    //                // TODO: Ignore DW_AT_artificial
650                    //                params.push_back(resolve_type(dbg, get_type_die(dbg, die)));
651                    //            }
652                    //        });
653                    //        if(!has_type(dbg, die)) {
654                    //            return "void" + (build.empty() ? "" : "(" + build + ")") + "(" + join(params, ", ") + ")";
655                    //        } else {
656                    //            // resolving return type, building on build
657                    //            return resolve_type(
658                    //                dbg, get_type_die(dbg, die),
659                    //                (build.empty() ? "" : "(" + build + ")")
660                    //                    + "("
661                    //                    + join(params, ", ")
662                    //                    + ")"
663                    //            );
664                    //        }
665                    //    }
666                    //case DW_TAG_array_type:
667                    //    return resolve_type(dbg, get_type_die(dbg, die), (build.empty() ? "" : "(" + build + ")") + "[" + "x" + "]");
668                    case DW_TAG_pointer_type:
669                    case DW_TAG_reference_type:
670                    case DW_TAG_rvalue_reference_type:
671                    case DW_TAG_ptr_to_member_type:
672                        {
673                            const auto child = get_type_die(dbg, die); // AST child, rather than dwarf child
674                            const auto child_tag = child.get_tag();
675                            switch(child_tag) {
676                                case DW_TAG_subroutine_type:
677                                    if(!has_type(dbg, child)) {
678                                        return "void(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_parameters(dbg, child);
679                                    } else {
680                                        return resolve_type(
681                                            dbg,
682                                            get_type_die(dbg, child),
683                                            "(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_parameters(dbg, child)
684                                        );
685                                    }
686                                case DW_TAG_array_type:
687                                    return resolve_type(
688                                        dbg,
689                                        get_type_die(dbg, child),
690                                        "(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_array_extents(dbg, child)
691                                    );
692                                default:
693                                    if(build.empty()) {
694                                        return resolve_type(dbg, get_type_die(dbg, die), tag_to_ptr_ref(tag));
695                                    } else {
696                                        return resolve_type(
697                                            dbg,
698                                            get_type_die(dbg, die),
699                                            std::string(tag_to_ptr_ref(tag)) + " " + build
700                                        );
701                                    }
702                            }
703                        }
704                    case DW_TAG_const_type:
705                    case DW_TAG_atomic_type:
706                    case DW_TAG_volatile_type:
707                    case DW_TAG_restrict_type:
708                        {
709                            const auto child = get_type_die(dbg, die); // AST child, rather than dwarf child
710                            const auto child_tag = child.get_tag();
711                            switch(child_tag) {
712                                case DW_TAG_base_type:
713                                case DW_TAG_class_type:
714                                case DW_TAG_typedef:
715                                    return std::string(tag_to_keyword(tag))
716                                            + " "
717                                            + resolve_type(dbg, get_type_die(dbg, die), build);
718                                default:
719                                    return resolve_type(
720                                        dbg,
721                                        get_type_die(dbg, die),
722                                        std::string(tag_to_keyword(tag)) + " " + build
723                                    );
724                            }
725                        }
726                    default:
727                        {
728                            fprintf(stderr, "unknown tag %s\n", die.get_tag_name());
729                            exit(1);
730                        }
731                }
732                return {"<unknown>", "<unknown>"};
733            }*/
734
735            bool is_mangled_name(const std::string& name) {
736                return name.find("_Z") || name.find("?h@@");
737            }
738
739            void retrieve_symbol_for_subprogram(
740                Dwarf_Debug dbg,
741                const die_object& die,
742                Dwarf_Addr pc,
743                Dwarf_Half dwversion,
744                stacktrace_frame& frame
745            ) {
746                assert(die.get_tag() == DW_TAG_subprogram);
747                optional<std::string> name;
748                if(auto linkage_name = die.get_string_attribute(DW_AT_linkage_name)) {
749                    name = std::move(linkage_name);
750                } else if(auto linkage_name = die.get_string_attribute(DW_AT_MIPS_linkage_name)) {
751                    name = std::move(linkage_name);
752                } else if(auto linkage_name = die.get_string_attribute(DW_AT_name)) {
753                    name = std::move(linkage_name);
754                }
755                if(name) {
756                    frame.symbol = std::move(name).unwrap();
757                } else {
758                    if(die.has_attr(DW_AT_specification)) {
759                        die_object spec = die.resolve_reference_attribute(DW_AT_specification);
760                        // TODO: Passing pc here is misleading
761                        return retrieve_symbol_for_subprogram(dbg, spec, pc, dwversion, frame);
762                    }
763                }
764                // TODO: Handle namespaces
765                // TODO: Disabled for now
766                /*std::string name = die.get_name();
767                std::vector<std::string> params;
768                auto child = die.get_child();
769                if(child) {
770                    walk_die_list_recursive(
771                        dbg,
772                        child,
773                        [pc, dwversion, &frame, &params] (Dwarf_Debug dbg, const die_object& die) {
774                            if(die.get_tag() == DW_TAG_formal_parameter) {
775                                // TODO: Ignore DW_AT_artificial
776                                params.push_back(resolve_type(dbg, get_type_die(dbg, die)));
777                            }
778                        }
779                    );
780                } else {
781                    fprintf(stderr, "no child %s\n", name.c_str());
782                }
783                frame.symbol = name + "(" + join(params, ", ") + ")";*/
784            }
785
786            void retrieve_symbol(
787                Dwarf_Debug dbg,
788                const die_object& die,
789                Dwarf_Addr pc,
790                Dwarf_Half dwversion,
791                stacktrace_frame& frame
792            ) {
793                walk_die_list(
794                    dbg,
795                    die,
796                    [pc, dwversion, &frame] (Dwarf_Debug dbg, const die_object& die) {
797                        if(dump_dwarf) {
798                            fprintf(
799                                stderr,
800                                "-------------> %d %s %s\n",
801                                dwversion,
802                                die.get_tag_name(),
803                                die.get_name().c_str()
804                            );
805                        }
806
807                        if(!pc_in_die(dbg, die.get(), dwversion, pc)) {
808                            if(dump_dwarf) {
809                                fprintf(stderr, "pc not in die\n");
810                            }
811                        } else {
812                            if(trace_dwarf) {
813                                fprintf(
814                                    stderr,
815                                    "pc in die %08llx %s\n",
816                                    (unsigned long long) die.get_global_offset(),
817                                    die.get_tag_name()
818                                );
819                            }
820                            if(dump_dwarf) {
821                                fprintf(stderr, "pc in die <-----------------------------------\n");
822                            }
823                            if(die.get_tag() == DW_TAG_subprogram) {
824                                retrieve_symbol_for_subprogram(dbg, die, pc, dwversion, frame);
825                            }
826                            auto child = die.get_child();
827                            if(child) {
828                                retrieve_symbol(dbg, child, pc, dwversion, frame);
829                            } else {
830                                if(dump_dwarf) {
831                                    fprintf(stderr, "(no child)\n");
832                                }
833                            }
834                        }
835                    }
836                );
837            }
838
839            void retrieve_line_info(
840                Dwarf_Debug dbg,
841                const die_object& die,
842                Dwarf_Addr pc,
843                Dwarf_Half dwversion,
844                stacktrace_frame& frame
845            ) {
846                Dwarf_Unsigned version;
847                Dwarf_Small table_count;
848                Dwarf_Line_Context ctxt;
849                Dwarf_Bool is_found = false;
850                (void)dwversion;
851                int ret = dwarf_srclines_b(
852                    die.get(),
853                    &version,
854                    &table_count,
855                    &ctxt,
856                    nullptr
857                );
858                if(ret == DW_DLV_NO_ENTRY) {
859                    fprintf(stderr, "dwarf_srclines_b error\n");
860                    return;
861                }
862                if(table_count == 1) {
863                    Dwarf_Line *linebuf = 0;
864                    Dwarf_Signed linecount = 0;
865                    Dwarf_Addr prev_lineaddr = 0;
866
867                    dwarf_srclines_from_linecontext(ctxt, &linebuf,
868                        &linecount, nullptr);
869                    Dwarf_Line prev_line = 0;
870                    for(int i = 0; i < linecount; i++) {
871                        Dwarf_Line line = linebuf[i];
872                        Dwarf_Addr lineaddr = 0;
873
874                        dwarf_lineaddr(line, &lineaddr, nullptr);
875                        if(pc == lineaddr) {
876                            /* Print the last line entry containing current pc. */
877                            Dwarf_Line last_pc_line = line;
878
879                            for(int j = i + 1; j < linecount; j++) {
880                                Dwarf_Line j_line = linebuf[j];
881                                dwarf_lineaddr(j_line, &lineaddr, nullptr);
882
883                                if(pc == lineaddr) {
884                                    last_pc_line = j_line;
885                                }
886                            }
887                            is_found = true;
888                            print_line(dbg, last_pc_line, pc, frame);
889                            break;
890                        } else if(prev_line && pc > prev_lineaddr &&
891                            pc < lineaddr) {
892                            is_found = true;
893                            print_line(dbg, prev_line, pc, frame);
894                            break;
895                        }
896                        Dwarf_Bool is_lne;
897                        dwarf_lineendsequence(line, &is_lne, nullptr);
898                        if(is_lne) {
899                            prev_line = 0;
900                        } else {
901                            prev_lineaddr = lineaddr;
902                            prev_line = line;
903                        }
904                    }
905                }
906                dwarf_srclines_dealloc_b(ctxt);
907            }
908
909            void walk_compilation_units(Dwarf_Debug dbg, Dwarf_Addr pc, stacktrace_frame& frame) {
910                // 0 passed as the dieto the first call of dwarf_siblingof_b immediately after dwarf_next_cu_header_d to
911                // fetch the cu die
912                die_object cu_die(dbg, nullptr);
913                cu_die = cu_die.get_sibling();
914                if(!cu_die) {
915                    if(dump_dwarf) {
916                        fprintf(stderr, "End walk_compilation_units\n");
917                    }
918                    return;
919                }
920                walk_die_list(
921                    dbg,
922                    cu_die,
923                    [&frame, pc] (Dwarf_Debug dbg, const die_object& cu_die) {
924                        Dwarf_Half offset_size = 0;
925                        Dwarf_Half dwversion = 0;
926                        dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size);
927                        if(trace_dwarf) {
928                            fprintf(stderr, "CU: %d %s\n", dwversion, cu_die.get_name().c_str());
929                        }
930                        /*auto child = cu_die.get_child();
931                        if(child) {
932                            walk_die_list_recursive(
933                                dbg,
934                                child,
935                                [&frame, pc, dwversion] (Dwarf_Debug dbg, const die_object& cu_die) {
936
937                                }
938                            );
939                        }*/
940                        //walk_die(dbg, cu_die, pc, dwversion, false, frame);
941                        if(pc_in_die(dbg, cu_die.get(), dwversion, pc)) {
942                            retrieve_line_info(dbg, cu_die, pc, dwversion, frame);
943                            retrieve_symbol(dbg, cu_die, pc, dwversion, frame);
944                        }
945                    }
946                );
947            }
948
949            void walk_dbg(Dwarf_Debug dbg, Dwarf_Addr pc, stacktrace_frame& frame) {
950                // libdwarf keeps track of where it is in the file, dwarf_next_cu_header_d is statefull
951                Dwarf_Unsigned next_cu_header;
952                Dwarf_Half header_cu_type;
953                while(true) {
954                    int ret = dwarf_next_cu_header_d(
955                        dbg,
956                        true,
957                        nullptr,
958                        nullptr,
959                        nullptr,
960                        nullptr,
961                        nullptr,
962                        nullptr,
963                        nullptr,
964                        nullptr,
965                        &next_cu_header,
966                        &header_cu_type,
967                        nullptr
968                    );
969                    if(ret == DW_DLV_NO_ENTRY) {
970                        if(dump_dwarf) {
971                            fprintf(stderr, "End walk_dbg\n");
972                        }
973                        return;
974                    }
975                    if(ret != DW_DLV_OK) {
976                        fprintf(stderr, "Error\n");
977                        return;
978                    }
979                    walk_compilation_units(dbg, pc, frame);
980                }
981            }
982
983            void lookup_pc(
984                const char* object,
985                Dwarf_Addr pc,
986                stacktrace_frame& frame
987            ) {
988                if(dump_dwarf) {
989                    fprintf(stderr, "%s\n", object);
990                    fprintf(stderr, "%llx\n", pc);
991                }
992                Dwarf_Debug dbg;
993                Dwarf_Ptr errarg = 0;
994                auto ret = dwarf_init_path(
995                    object,
996                    nullptr,
997                    0,
998                    DW_GROUPNUMBER_ANY,
999                    err_handler,
1000                    errarg,
1001                    &dbg,
1002                    nullptr
1003                );
1004                if(ret == DW_DLV_NO_ENTRY) {
1005                    // fail, no debug info
1006                } else if(ret != DW_DLV_OK) {
1007                    fprintf(stderr, "Error\n");
1008                } else {
1009                    walk_dbg(dbg, pc, frame);
1010                }
1011                dwarf_finish(dbg);
1012            }
1013
1014            stacktrace_frame resolve_frame(const dlframe& frame_info) {
1015                stacktrace_frame frame{};
1016                frame.filename = frame_info.obj_path;
1017                frame.symbol = frame_info.symbol;
1018                frame.address = frame_info.raw_address;
1019                std::string obj_path = frame_info.obj_path;
1020                #if IS_APPLE
1021                //std::string obj_path = frame_info.obj_path;
1022                //char* dir = dirname(obj_path.data());
1023                //std::string dsym =
1024                if(directory_exists(obj_path + ".dSYM")) {
1025                    obj_path += ".dSYM/Contents/Resources/DWARF/" + basename(frame_info.obj_path);
1026                }
1027                #endif
1028                if(trace_dwarf) {
1029                    fprintf(
1030                        stderr,
1031                        "%s %08llx %s\n",
1032                        obj_path.c_str(),
1033                        (unsigned long long)frame_info.obj_address,
1034                        frame_info.symbol.c_str()
1035                    );
1036                }
1037                lookup_pc(
1038                    obj_path.c_str(),
1039                    frame_info.obj_address,
1040                    frame
1041                );
1042                return frame;
1043            }
1044
1045            std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
1046                std::vector<stacktrace_frame> trace;
1047                trace.reserve(frames.size());
1048                for(const auto& frame : get_frames_object_info(frames)) {
1049                    trace.push_back(resolve_frame(frame));
1050                }
1051                return trace;
1052            }
1053        }
1054    }
1055}
1056
1057#endif
Note: See TracBrowser for help on using the repository browser.