diff --git a/ddprof-lib/src/main/cpp/codeCache.cpp b/ddprof-lib/src/main/cpp/codeCache.cpp index 6ea17e3d0..c2cec7448 100644 --- a/ddprof-lib/src/main/cpp/codeCache.cpp +++ b/ddprof-lib/src/main/cpp/codeCache.cpp @@ -63,6 +63,7 @@ CodeCache::CodeCache(const char *name, short lib_index, _dwarf_table = NULL; _dwarf_table_length = 0; + _default_frame = &FrameDesc::default_frame; _capacity = INITIAL_CODE_CACHE_CAPACITY; _count = 0; @@ -101,6 +102,7 @@ void CodeCache::copyFrom(const CodeCache& other) { _dwarf_table = new FrameDesc[_dwarf_table_length]; memcpy(_dwarf_table, other._dwarf_table, _dwarf_table_length * sizeof(FrameDesc)); + _default_frame = other._default_frame; _capacity = other._capacity; _count = other._count; @@ -388,16 +390,15 @@ void CodeCache::makeImportsPatchable() { } } -void CodeCache::setDwarfTable(FrameDesc *table, int length) { +void CodeCache::setDwarfTable(FrameDesc *table, int length, const FrameDesc &default_frame) { _dwarf_table = table; _dwarf_table_length = length; + _default_frame = &default_frame; } FrameDesc CodeCache::findFrameDesc(const void *pc) { if (_dwarf_table == NULL || _dwarf_table_length == 0) { - // No DWARF data available - use default frame pointer unwinding - // This handles OpenJ9 and other VMs that don't provide DWARF info - return FrameDesc::default_frame; + return *_default_frame; } u32 target_loc = (const char *)pc - _text_base; @@ -420,7 +421,7 @@ FrameDesc CodeCache::findFrameDesc(const void *pc) { } else if (target_loc - _plt_offset < _plt_size) { return FrameDesc::empty_frame; } else { - return FrameDesc::default_frame; + return *_default_frame; } } diff --git a/ddprof-lib/src/main/cpp/codeCache.h b/ddprof-lib/src/main/cpp/codeCache.h index e5f4faefb..170ac22a3 100644 --- a/ddprof-lib/src/main/cpp/codeCache.h +++ b/ddprof-lib/src/main/cpp/codeCache.h @@ -8,6 +8,7 @@ #include "common.h" #include "counters.h" +#include "dwarf.h" #include "utils.h" #include @@ -116,8 +117,6 @@ class CodeBlob { } }; -class FrameDesc; - class CodeCache { private: char *_name; @@ -141,6 +140,7 @@ class CodeCache { FrameDesc *_dwarf_table; int _dwarf_table_length; + const FrameDesc *_default_frame; int _capacity; int _count; @@ -241,7 +241,7 @@ class CodeCache { void findSymbolsByPrefix(std::vector &prefixes, std::vector &symbols); - void setDwarfTable(FrameDesc *table, int length); + void setDwarfTable(FrameDesc *table, int length, const FrameDesc &default_frame = FrameDesc::default_frame); FrameDesc findFrameDesc(const void *pc); long long memoryUsage() { diff --git a/ddprof-lib/src/main/cpp/dwarf.cpp b/ddprof-lib/src/main/cpp/dwarf.cpp index bea2d17e3..b2c18ae67 100644 --- a/ddprof-lib/src/main/cpp/dwarf.cpp +++ b/ddprof-lib/src/main/cpp/dwarf.cpp @@ -104,6 +104,7 @@ DwarfParser::DwarfParser(const char *name, const char *image_base, _code_align = sizeof(instruction_t); _data_align = -(int)sizeof(void *); + _linked_frame_size = -1; parse(eh_frame_hdr); } @@ -411,6 +412,14 @@ void DwarfParser::addRecord(u32 loc, u32 cfa_reg, int cfa_off, int fp_off, // cfa_reg and cfa_off can be encoded to a single 32 bit value, considering the existing and supported systems u32 cfa = static_cast(cfa_off) << 8 | static_cast(cfa_reg & 0xff); + // Detect the linked frame size from the first FP-based entry with a non-zero offset. + // Both GCC and Clang emit DW_REG_FP with cfa_off = LINKED_FRAME_CLANG_SIZE in function + // bodies after the prologue completes. Terminal records use cfa_off = 0 (LINKED_FRAME_SIZE + // on aarch64) and do not influence detection. + if (_linked_frame_size < 0 && cfa_reg == DW_REG_FP && cfa_off > 0) { + _linked_frame_size = cfa_off; + } + if (_prev == NULL || (_prev->loc == loc && --_count >= 0) || _prev->cfa != cfa || _prev->fp_off != fp_off || _prev->pc_off != pc_off) { _prev = addRecordRaw(loc, cfa, fp_off, pc_off); diff --git a/ddprof-lib/src/main/cpp/dwarf.h b/ddprof-lib/src/main/cpp/dwarf.h index 0a62ab40d..88dfff379 100644 --- a/ddprof-lib/src/main/cpp/dwarf.h +++ b/ddprof-lib/src/main/cpp/dwarf.h @@ -83,6 +83,17 @@ struct FrameDesc { static FrameDesc default_clang_frame; static FrameDesc no_dwarf_frame; + // Best-guess fallback frame layout when a PC doesn't map to any known library. + // Per-library detection overrides this: on macOS via __eh_frame section presence, + // on Linux via DwarfParser::detectedDefaultFrame(). + static const FrameDesc& fallback_default_frame() { +#if defined(__APPLE__) && defined(__aarch64__) + return default_clang_frame; +#else + return default_frame; +#endif + } + static int comparator(const void* p1, const void* p2) { FrameDesc* fd1 = (FrameDesc*)p1; FrameDesc* fd2 = (FrameDesc*)p2; @@ -104,6 +115,7 @@ class DwarfParser { u32 _code_align; int _data_align; + int _linked_frame_size; // detected from FP-based DWARF entries; -1 = undetected const char* add(size_t size) { const char* ptr = _ptr; @@ -185,6 +197,13 @@ class DwarfParser { int count() const { return _count; } + + const FrameDesc& detectedDefaultFrame() const { + if (_linked_frame_size == LINKED_FRAME_CLANG_SIZE && LINKED_FRAME_CLANG_SIZE != LINKED_FRAME_SIZE) { + return FrameDesc::default_clang_frame; + } + return FrameDesc::default_frame; + } }; #endif // _DWARF_H diff --git a/ddprof-lib/src/main/cpp/stackWalker.cpp b/ddprof-lib/src/main/cpp/stackWalker.cpp index 4210551ad..4e78bfc8d 100644 --- a/ddprof-lib/src/main/cpp/stackWalker.cpp +++ b/ddprof-lib/src/main/cpp/stackWalker.cpp @@ -158,7 +158,7 @@ int StackWalker::walkDwarf(void* ucontext, const void** callchain, int max_depth uintptr_t prev_sp = sp; CodeCache* cc = profiler->findLibraryByAddress(pc); - FrameDesc f = cc != NULL ? cc->findFrameDesc(pc) : FrameDesc::default_frame; + FrameDesc f = cc != NULL ? cc->findFrameDesc(pc) : FrameDesc::fallback_default_frame(); u8 cfa_reg = (u8)f.cfa; int cfa_off = f.cfa >> 8; @@ -694,7 +694,7 @@ __attribute__((no_sanitize("address"))) int StackWalker::walkVM(void* ucontext, dwarf_unwind: uintptr_t prev_sp = sp; CodeCache* cc = profiler->findLibraryByAddress(pc); - FrameDesc f = cc != NULL ? cc->findFrameDesc(pc) : FrameDesc::default_frame; + FrameDesc f = cc != NULL ? cc->findFrameDesc(pc) : FrameDesc::fallback_default_frame(); u8 cfa_reg = (u8)f.cfa; int cfa_off = f.cfa >> 8; diff --git a/ddprof-lib/src/main/cpp/symbols_linux.cpp b/ddprof-lib/src/main/cpp/symbols_linux.cpp index 3efc7857c..973a6fc44 100644 --- a/ddprof-lib/src/main/cpp/symbols_linux.cpp +++ b/ddprof-lib/src/main/cpp/symbols_linux.cpp @@ -604,8 +604,11 @@ void ElfParser::parseDwarfInfo() { ElfProgramHeader* eh_frame_hdr = findProgramHeader(PT_GNU_EH_FRAME); if (eh_frame_hdr != NULL) { if (eh_frame_hdr->p_vaddr != 0) { + // Parse per-PC frame descriptions and detect per-library default frame layout. + // On aarch64 this distinguishes GCC (LINKED_FRAME_SIZE=0) from clang + // (LINKED_FRAME_CLANG_SIZE=16) conventions for each shared library. DwarfParser dwarf(_cc->name(), _base, at(eh_frame_hdr)); - _cc->setDwarfTable(dwarf.table(), dwarf.count()); + _cc->setDwarfTable(dwarf.table(), dwarf.count(), dwarf.detectedDefaultFrame()); } else if (strcmp(_cc->name(), "[vdso]") == 0) { FrameDesc* table = (FrameDesc*)malloc(sizeof(FrameDesc)); *table = FrameDesc::empty_frame; diff --git a/ddprof-lib/src/main/cpp/symbols_macos.cpp b/ddprof-lib/src/main/cpp/symbols_macos.cpp index 1c7786ae9..f7f4c5c76 100644 --- a/ddprof-lib/src/main/cpp/symbols_macos.cpp +++ b/ddprof-lib/src/main/cpp/symbols_macos.cpp @@ -12,6 +12,7 @@ #include #include #include "symbols.h" +#include "dwarf.h" #include "log.h" UnloadProtection::UnloadProtection(const CodeCache *cc) { @@ -138,6 +139,7 @@ class MachOParser { const symtab_command* symtab = NULL; const dysymtab_command* dysymtab = NULL; const section_64* stubs_section = NULL; + bool has_eh_frame = false; for (uint32_t i = 0; i < header->ncmds; i++) { if (lc->cmd == LC_SEGMENT_64) { @@ -145,6 +147,7 @@ class MachOParser { if (strcmp(sc->segname, "__TEXT") == 0) { _cc->updateBounds(_image_base, add(_image_base, sc->vmsize)); stubs_section = findSection(sc, "__stubs"); + has_eh_frame = findSection(sc, "__eh_frame") != NULL; } else if (strcmp(sc->segname, "__LINKEDIT") == 0) { link_base = _vmaddr_slide + sc->vmaddr - sc->fileoff; } else if (strcmp(sc->segname, "__DATA") == 0 || strcmp(sc->segname, "__DATA_CONST") == 0) { @@ -168,6 +171,12 @@ class MachOParser { } } + // GCC emits __eh_frame (DWARF CFI); clang emits __unwind_info (compact unwind). + // On aarch64, GCC and clang use different frame layouts, so detecting the + // compiler matters. On x86_64 both use the same layout (no-op distinction). + const FrameDesc& frame = has_eh_frame ? FrameDesc::default_frame : FrameDesc::fallback_default_frame(); + _cc->setDwarfTable(NULL, 0, frame); + return true; } };