diff --git a/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp index 9172d7709fa96c..c444482bdc1fc6 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp @@ -761,8 +761,122 @@ void Registers_REGDISPLAY::setVectorRegister(int num, libunwind::v128 value) #endif // TARGET_ARM64 +// the cache relies on "endless" 64bit version counter +#if TARGET_64BIT +struct ProcInfoCacheEntry +{ + PCODE pc; + volatile size_t version; + + // unw_proc_info_t procInfo; + uint32_t start_offset; /* start address of function relative to pc */ + uint32_t end_offset; /* end address of function relative to pc */ + + uint32_t format; /* compact unwind encoding, or zero if none */ + uint32_t unwind_info_size; /* size of DWARF unwind info, or zero if none */ + unw_word_t lsda; /* address of language specific data area, */ + unw_word_t unwind_info; /* address of DWARF unwind info, or zero */ + +#ifdef __APPLE__ + unw_word_t extra; /* mach_header of mach-o image containing func */ +#endif +}; + +// we use static array with 1024 entries as a cache. +// that is about 49Kb and what we can reasonably afford. +static const int CACHE_BITS = 10; +static ProcInfoCacheEntry cache[1 << CACHE_BITS]; +#endif + +// We use a very simple caching scheme because the cost of miss is not very high. +// If there is a desire to improve the occupancy of the cache, it is possible to +// implement some bucketing strategy - like using the next cell in case of a collision. +// For now we will just use direct mapping after some reshuffling of the pc bits. +static void SetCachedProcInfo(PCODE pc, unw_proc_info_t* procInfo) +{ +#if TARGET_64BIT + + if ((procInfo->end_ip - procInfo->start_ip) > UINT32_MAX) + return; + + // randomize the addresses a bit and narrow the range to the cache size. + int idx = (int)((pc * 11400714819323198485llu) >> (64 - CACHE_BITS)); + + ProcInfoCacheEntry* pEntry = &cache[idx]; + PCODE origPc = pEntry->pc; + // there is already an entry for this pc + if (origPc == pc) + return; + + size_t origVersion = pEntry->version; + // if version is odd, someone is writing into it, we will try again next time. + if (origVersion & 1) + return; + + // claim the entry by incrementing the version + if (!__atomic_compare_exchange_n(&pEntry->version, &origVersion, origVersion + 1, true, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED)) + { + return; + } + + pEntry->pc = pc; + pEntry->start_offset = (uint32_t)(pc - procInfo->start_ip); + pEntry->end_offset = (uint32_t)(procInfo->end_ip - pc); + pEntry->format = procInfo->format; + pEntry->unwind_info_size = procInfo->unwind_info_size; + pEntry->lsda = procInfo->lsda; + pEntry->unwind_info = procInfo->unwind_info; + +#ifdef __APPLE__ + pEntry->extra = procInfo->extra; +#endif + + // increment the version again after filling the entry + __atomic_store_n(&pEntry->version, origVersion + 2, __ATOMIC_RELEASE); +#endif +} + +static bool TryGetCachedProcInfo(PCODE pc, unw_proc_info_t* procInfo) +{ +#if TARGET_64BIT + // randomize the addresses a bit and narrow the range to the cache size. + int idx = (int)((pc * 11400714819323198485llu) >> (64 - CACHE_BITS)); + + ProcInfoCacheEntry* pEntry = &cache[idx]; + // read version before reading the rest + size_t version = __atomic_load_n(&pEntry->version, __ATOMIC_ACQUIRE); + if (pc == pEntry->pc) + { + procInfo->start_ip = pc - pEntry->start_offset; + procInfo->end_ip = pc + pEntry->end_offset; + + procInfo->format = pEntry->format; + procInfo->unwind_info_size = pEntry->unwind_info_size; + procInfo->lsda = pEntry->lsda; + procInfo->handler = 0; // not used in our scenarios + procInfo->unwind_info = pEntry->unwind_info; + +#ifdef __APPLE__ + procInfo->extra = pEntry->extra; +#endif + + // make sure all reads are done before reading the version second time + __atomic_thread_fence(__ATOMIC_ACQUIRE); + + // if version is odd or changed between reads, the entry is inconsistent, treat it as a miss + if ((version & ~1) == pEntry->version) + { + return true; + } + } +#endif + + return false; +} + bool DoTheStep(uintptr_t pc, UnwindInfoSections uwInfoSections, REGDISPLAY *regs) { + #if defined(TARGET_AMD64) libunwind::UnwindCursor uc(_addressSpace); #elif defined(TARGET_ARM) @@ -775,30 +889,55 @@ bool DoTheStep(uintptr_t pc, UnwindInfoSections uwInfoSections, REGDISPLAY *regs #error "Unwinding is not implemented for this architecture yet." #endif -#if _LIBUNWIND_SUPPORT_DWARF_UNWIND + unw_proc_info_t procInfo; uint32_t dwarfOffsetHint = 0; +#if _LIBUNWIND_SUPPORT_DWARF_UNWIND + #if _LIBUNWIND_SUPPORT_COMPACT_UNWIND + { + if (TryGetCachedProcInfo(pc, &procInfo)) + { + uc.setInfo(&procInfo); +#if defined(TARGET_ARM64) + if ((procInfo.format & UNWIND_ARM64_MODE_MASK) != UNWIND_ARM64_MODE_DWARF) { + CompactUnwinder_arm64 compactInst; + int stepRet = compactInst.stepWithCompactEncoding(procInfo.format, procInfo.start_ip, _addressSpace, *(Registers_REGDISPLAY*)regs); + return stepRet == UNW_STEP_SUCCESS; + } +#elif defined(TARGET_AMD64) + if ((procInfo.format & UNWIND_X86_64_MODE_MASK) != UNWIND_X86_64_MODE_DWARF) { + CompactUnwinder_x86_64 compactInst; + int stepRet = compactInst.stepWithCompactEncoding(procInfo.format, procInfo.start_ip, _addressSpace, *(Registers_REGDISPLAY*)regs); + return stepRet == UNW_STEP_SUCCESS; + } +#endif + goto HAVE_DWARF_INFO; + } + } + // If there is a compact unwind encoding table, look there first. if (uwInfoSections.compact_unwind_section != 0 && uc.getInfoFromCompactEncodingSection(pc, uwInfoSections)) { - unw_proc_info_t procInfo; - uc.getInfo(&procInfo); + unw_proc_info_t procInfoFromCompact; + uc.getInfo(&procInfoFromCompact); #if defined(TARGET_ARM64) - if ((procInfo.format & UNWIND_ARM64_MODE_MASK) != UNWIND_ARM64_MODE_DWARF) { + if ((procInfoFromCompact.format & UNWIND_ARM64_MODE_MASK) != UNWIND_ARM64_MODE_DWARF) { + SetCachedProcInfo(pc, &procInfoFromCompact); CompactUnwinder_arm64 compactInst; - int stepRet = compactInst.stepWithCompactEncoding(procInfo.format, procInfo.start_ip, _addressSpace, *(Registers_REGDISPLAY*)regs); + int stepRet = compactInst.stepWithCompactEncoding(procInfoFromCompact.format, procInfoFromCompact.start_ip, _addressSpace, *(Registers_REGDISPLAY*)regs); return stepRet == UNW_STEP_SUCCESS; } else { - dwarfOffsetHint = procInfo.format & UNWIND_ARM64_DWARF_SECTION_OFFSET; + dwarfOffsetHint = procInfoFromCompact.format & UNWIND_ARM64_DWARF_SECTION_OFFSET; } #elif defined(TARGET_AMD64) - if ((procInfo.format & UNWIND_X86_64_MODE_MASK) != UNWIND_X86_64_MODE_DWARF) { + if ((procInfoFromCompact.format & UNWIND_X86_64_MODE_MASK) != UNWIND_X86_64_MODE_DWARF) { + SetCachedProcInfo(pc, &procInfoFromCompact); CompactUnwinder_x86_64 compactInst; - int stepRet = compactInst.stepWithCompactEncoding(procInfo.format, procInfo.start_ip, _addressSpace, *(Registers_REGDISPLAY*)regs); + int stepRet = compactInst.stepWithCompactEncoding(procInfoFromCompact.format, procInfoFromCompact.start_ip, _addressSpace, *(Registers_REGDISPLAY*)regs); return stepRet == UNW_STEP_SUCCESS; } else { - dwarfOffsetHint = procInfo.format & UNWIND_X86_64_DWARF_SECTION_OFFSET; + dwarfOffsetHint = procInfoFromCompact.format & UNWIND_X86_64_DWARF_SECTION_OFFSET; } #else PORTABILITY_ASSERT("DoTheStep"); @@ -806,14 +945,26 @@ bool DoTheStep(uintptr_t pc, UnwindInfoSections uwInfoSections, REGDISPLAY *regs } #endif - bool retVal = uc.getInfoFromDwarfSection(pc, uwInfoSections, dwarfOffsetHint); - if (!retVal) + if (TryGetCachedProcInfo(pc, &procInfo)) { - return false; + uc.setInfo(&procInfo); } + else + { + bool retVal = uc.getInfoFromDwarfSection(pc, uwInfoSections, dwarfOffsetHint); + if (!retVal) + { + return false; + } + + uc.getInfo(&procInfo); + SetCachedProcInfo(pc, &procInfo); + } + +#if _LIBUNWIND_SUPPORT_COMPACT_UNWIND + HAVE_DWARF_INFO: +#endif - unw_proc_info_t procInfo; - uc.getInfo(&procInfo); bool isSignalFrame = false; #if defined(TARGET_ARM) @@ -871,6 +1022,9 @@ bool UnwindHelpers::StepFrame(REGDISPLAY *regs) bool UnwindHelpers::GetUnwindProcInfo(PCODE pc, UnwindInfoSections &uwInfoSections, unw_proc_info_t *procInfo) { + if (TryGetCachedProcInfo(pc, procInfo)) + return true; + #if defined(TARGET_AMD64) libunwind::UnwindCursor uc(_addressSpace); #elif defined(TARGET_ARM) @@ -893,12 +1047,14 @@ bool UnwindHelpers::GetUnwindProcInfo(PCODE pc, UnwindInfoSections &uwInfoSectio #if defined(TARGET_ARM64) if ((procInfo->format & UNWIND_ARM64_MODE_MASK) != UNWIND_ARM64_MODE_DWARF) { + SetCachedProcInfo(pc, procInfo); return true; } else { dwarfOffsetHint = procInfo->format & UNWIND_ARM64_DWARF_SECTION_OFFSET; } #elif defined(TARGET_AMD64) if ((procInfo->format & UNWIND_X86_64_MODE_MASK) != UNWIND_X86_64_MODE_DWARF) { + SetCachedProcInfo(pc, procInfo); return true; } else { dwarfOffsetHint = procInfo->format & UNWIND_X86_64_DWARF_SECTION_OFFSET; @@ -926,5 +1082,7 @@ bool UnwindHelpers::GetUnwindProcInfo(PCODE pc, UnwindInfoSections &uwInfoSectio #endif uc.getInfo(procInfo); + SetCachedProcInfo(pc, procInfo); + return true; } diff --git a/src/native/external/llvm-libunwind-version.txt b/src/native/external/llvm-libunwind-version.txt index d23999cc453682..b7355fa971eef6 100644 --- a/src/native/external/llvm-libunwind-version.txt +++ b/src/native/external/llvm-libunwind-version.txt @@ -3,4 +3,4 @@ https://github.com/llvm/llvm-project/releases/tag/llvmorg-16.0.2 Apply https://github.com/dotnet/runtime/commit/1bafb60792b91747d9c2a9dd38461cf090a987a4 Apply https://github.com/dotnet/runtime/commit/0ee8827547405408b37d1aae2a83629c1632eea8 - +Apply https://github.com/dotnet/runtime/commit/3ebe51f51f6f7df31a4a73d0755c4be604406577 diff --git a/src/native/external/llvm-libunwind/src/UnwindCursor.hpp b/src/native/external/llvm-libunwind/src/UnwindCursor.hpp index b57134cdd19bb9..26510faf39f0da 100644 --- a/src/native/external/llvm-libunwind/src/UnwindCursor.hpp +++ b/src/native/external/llvm-libunwind/src/UnwindCursor.hpp @@ -932,6 +932,7 @@ class UnwindCursor : public AbstractUnwindCursor{ virtual void setFloatReg(int, unw_fpreg_t); virtual int step(bool stage2 = false); virtual void getInfo(unw_proc_info_t *); + virtual void setInfo(unw_proc_info_t *); virtual void jumpto(); virtual bool isSignalFrame(); virtual bool getFunctionName(char *buf, size_t len, unw_word_t *off); @@ -2885,6 +2886,11 @@ void UnwindCursor::getInfo(unw_proc_info_t *info) { *info = _info; } +template +void UnwindCursor::setInfo(unw_proc_info_t* info) { + _info = *info; +} + template bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, unw_word_t *offset) {