[libcxx-commits] [libcxx] Add C++23 stacktrace (P0881R7) (PR #136528)
Steve O'Brien via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Sep 1 13:17:58 PDT 2025
================
@@ -0,0 +1,248 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(_WIN32)
+
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+//
+# include <dbghelp.h>
+# include <psapi.h>
+//
+# include <cstring>
+# include <iostream>
+# include <mutex>
+# include <stacktrace>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+
+struct dll {
+ HMODULE module_{};
+ bool loaded_{};
+
+ explicit dll(char const* name) : module_(LoadLibraryA(name)) {}
+
+ ~dll() {
+ if (module_) {
+ FreeLibrary(module_);
+ }
+ }
+
+ template <typename F>
+ bool get_func(F** func, char const* name) {
+ return ((*func = (F*)(void*)GetProcAddress(module_, name)) != nullptr);
+ }
+};
+
+// clang-format off
+
+struct dbghelp_dll final : dll {
+ IMAGE_NT_HEADERS* (*ImageNtHeader)(void*);
+ bool (WINAPI *SymCleanup) (HANDLE);
+ DWORD (WINAPI *SymGetOptions) ();
+ bool (WINAPI *SymGetSearchPath) (HANDLE, char const*, DWORD);
+ bool (WINAPI *SymInitialize) (HANDLE, char const*, bool);
+ DWORD (WINAPI *SymSetOptions) (DWORD);
+ bool (WINAPI *SymSetSearchPath) (HANDLE, char const*);
+ bool (WINAPI *StackWalk64) (DWORD, HANDLE, HANDLE, STACKFRAME64*, void*, void*, void*, void*, void*);
+ void* (WINAPI *SymFunctionTableAccess64)(HANDLE, DWORD64);
+ bool (WINAPI *SymGetLineFromAddr64)(HANDLE, DWORD64, DWORD*, IMAGEHLP_LINE64*);
+ DWORD64 (WINAPI *SymGetModuleBase64) (HANDLE, DWORD64);
+ bool (WINAPI *SymGetModuleInfo64) (HANDLE, DWORD64, IMAGEHLP_MODULE64*);
+ bool (WINAPI *SymGetSymFromAddr64)(HANDLE, DWORD64, DWORD64*, IMAGEHLP_SYMBOL64*);
+ DWORD64 (WINAPI *SymLoadModule64) (HANDLE, HANDLE, char const*, char const*, void*, DWORD);
+
+ dbghelp_dll() : dll("dbghelp.dll") {
+ loaded_ = true
+ && get_func(&ImageNtHeader, "ImageNtHeader")
+ && get_func(&SymCleanup, "SymCleanup")
+ && get_func(&SymGetOptions, "SymGetOptions")
+ && get_func(&SymGetSearchPath, "SymGetSearchPath")
+ && get_func(&SymInitialize, "SymInitialize")
+ && get_func(&SymSetOptions, "SymSetOptions")
+ && get_func(&SymSetSearchPath, "SymSetSearchPath")
+ && get_func(&StackWalk64, "StackWalk64")
+ && get_func(&SymFunctionTableAccess64, "SymFunctionTableAccess64")
+ && get_func(&SymGetLineFromAddr64, "SymGetLineFromAddr64")
+ && get_func(&SymGetModuleBase64, "SymGetModuleBase64")
+ && get_func(&SymGetModuleInfo64, "SymGetModuleInfo64")
+ && get_func(&SymGetSymFromAddr64, "SymGetSymFromAddr64")
+ && get_func(&SymLoadModule64, "SymLoadModule64")
+ ;
+ }
+};
+
+struct psapi_dll final : dll {
+ bool (WINAPI *EnumProcessModules) (HANDLE, HMODULE*, DWORD, DWORD*);
+ bool (WINAPI *GetModuleInformation) (HANDLE, HMODULE, MODULEINFO*, DWORD);
+ DWORD (WINAPI *GetModuleBaseName) (HANDLE, HMODULE, char**, DWORD);
+
+ psapi_dll() : dll("psapi.dll") {
+ loaded_ = true
+ && get_func(&EnumProcessModules, "EnumProcessModules")
+ && get_func(&GetModuleInformation, "GetModuleInformation")
+ && get_func(&GetModuleBaseName, "GetModuleBaseNameA")
+ ;
+ }
+};
+
+struct sym_init_scope {
+ dbghelp_dll& dbghelp_;
+ HANDLE proc_;
+
+ sym_init_scope(dbghelp_dll& dbghelp, HANDLE proc)
+ : dbghelp_(dbghelp), proc_(proc) {
+ (*dbghelp_.SymInitialize)(proc_, nullptr, true);
+ }
+ ~sym_init_scope() {
+ (*dbghelp_.SymCleanup)(proc_);
+ }
+};
+
+} // namespace
+
+_LIBCPP_NO_TAIL_CALLS _LIBCPP_NOINLINE _LIBCPP_EXPORTED_FROM_ABI void
+base::current_impl(size_t skip, size_t max_depth) {
+ if (!max_depth) [[unlikely]] {
+ return;
+ }
+
+ static psapi_dll psapi;
+ static dbghelp_dll dbghelp;
+ if (!psapi.loaded_ || !dbghelp.loaded_) { return; }
+
+ // Not thread-safe according to docs
+ static std::mutex api_mutex;
+ std::lock_guard<std::mutex> api_guard(api_mutex);
+
+ HANDLE proc = GetCurrentProcess();
+ HMODULE exe = GetModuleHandle(nullptr);
+ if (!exe) { return; }
+
+ sym_init_scope symscope(dbghelp, proc);
+
+ char sym_path[MAX_PATH * 4]; // arbitrary
+ if (!(*dbghelp.SymGetSearchPath)(proc, sym_path, sizeof(sym_path))) { return; }
+
+ char exe_dir[MAX_PATH];
+ if (!GetModuleFileNameA(nullptr, exe_dir, sizeof(exe_dir))) { return; }
+ size_t exe_dir_len = strlen(exe_dir);
+ while (exe_dir_len > 0 && exe_dir[exe_dir_len - 1] != '\\') { exe_dir[--exe_dir_len] = 0; }
+ if (exe_dir_len > 0) { exe_dir[--exe_dir_len] = 0; } // strip last backslash
+
+ if (!strstr(sym_path, exe_dir)) {
+ (void) strncat(sym_path, ";", sizeof(sym_path) - 1);
+ (void) strncat(sym_path, exe_dir, sizeof(sym_path) - 1);
+ if (!(*dbghelp.SymSetSearchPath)(proc, sym_path)) { return; }
+ }
+
+ IMAGE_NT_HEADERS* nt_headers;
+ if (!(nt_headers = (*dbghelp.ImageNtHeader)(exe))) { return; }
+
+ (*dbghelp.SymSetOptions)(
+ (*dbghelp.SymGetOptions)()
+ | SYMOPT_LOAD_LINES
+ | SYMOPT_UNDNAME);
+
+ auto thread = GetCurrentThread();
+ auto machine = nt_headers->FileHeader.Machine;
+
+ CONTEXT ccx;
+ RtlCaptureContext(&ccx);
+
+ STACKFRAME64 frame;
+ memset(&frame, 0, sizeof(frame));
+ frame.AddrPC.Mode = AddrModeFlat;
+ frame.AddrStack.Mode = AddrModeFlat;
+ frame.AddrFrame.Mode = AddrModeFlat;
+#if defined(_M_AMD64)
+ frame.AddrPC.Offset = ccx.Rip;
+ frame.AddrStack.Offset = ccx.Rsp;
+ frame.AddrFrame.Offset = ccx.Rbp;
+#elif defined(_M_ARM64)
+ frame.AddrPC.Offset = ccx.Pc;
+ frame.AddrStack.Offset = ccx.Sp;
+ frame.AddrFrame.Offset = ccx.Fp;
+#else
+# warning stacktrace requires x86-64 or ARM64; returned stacktraces will be empty
+ return;
+#endif
----------------
elsteveogrande wrote:
This becomes an error, and breaks the `mingw-i686-dll` build. Will fix for 32-bit
https://github.com/llvm/llvm-project/pull/136528
More information about the libcxx-commits
mailing list