[libcxx-commits] [libcxx] [libc++] Add std::stacktrace (P0881R7) (PR #136528)
A. Jiang via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Jan 11 17:29:27 PST 2026
================
@@ -0,0 +1,295 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <__stacktrace/basic_stacktrace.h>
+# include <__stacktrace/stacktrace_entry.h>
+# include <cstring>
+# include <mutex>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __stacktrace {
+
+namespace {
+
+namespace __dll {
+
+template <typename F>
+bool get_func(HMODULE module, F** func, char const* name) {
+ return ((*func = reinterpret_cast<F*>(reinterpret_cast<void*>(GetProcAddress(module, name)))) != nullptr);
+}
+
+// clang-format off
+bool(WINAPI* EnumProcessModules)(HANDLE, HMODULE*, DWORD, DWORD*);
+bool(WINAPI* GetModuleInformation)(HANDLE, HMODULE, MODULEINFO*, DWORD);
+DWORD(WINAPI* GetModuleBaseName)(HANDLE, HMODULE, char**, DWORD);
+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*);
+# ifdef _WIN64
+void*(WINAPI* SymFunctionTableAccess)(HANDLE, DWORD64);
+bool(WINAPI* SymGetLineFromAddr)(HANDLE, DWORD64, DWORD*, IMAGEHLP_LINE64*);
+DWORD64(WINAPI* SymGetModuleBase)(HANDLE, DWORD64);
+bool(WINAPI* SymGetModuleInfo)(HANDLE, DWORD64, IMAGEHLP_MODULE64*);
+bool(WINAPI* SymGetSymFromAddr)(HANDLE, DWORD64, DWORD64*, IMAGEHLP_SYMBOL64*);
+DWORD64(WINAPI* SymLoadModule)(HANDLE, HANDLE, char const*, char const*, void*, DWORD);
+bool(WINAPI* StackWalk)(DWORD, HANDLE, HANDLE, STACKFRAME64*, void*, void*,
+ decltype(SymFunctionTableAccess), decltype(SymGetModuleBase), void*);
+# else
+void*(WINAPI* SymFunctionTableAccess)(HANDLE, DWORD);
+bool(WINAPI* SymGetLineFromAddr)(HANDLE, DWORD, DWORD*, IMAGEHLP_LINE*);
+DWORD(WINAPI* SymGetModuleBase)(HANDLE, DWORD);
+bool(WINAPI* SymGetModuleInfo)(HANDLE, DWORD, IMAGEHLP_MODULE*);
+bool(WINAPI* SymGetSymFromAddr)(HANDLE, DWORD, DWORD*, IMAGEHLP_SYMBOL*);
+DWORD(WINAPI* SymLoadModule)(HANDLE, HANDLE, char const*, char const*, void*, DWORD);
+bool(WINAPI* StackWalk)(DWORD, HANDLE, HANDLE, STACKFRAME*, void*, void*,
+ decltype(SymFunctionTableAccess), decltype(SymGetModuleBase), void*);
+# endif
+// clang-format on
+
+bool loadFuncs() {
+ static bool attempted{false};
+ static bool succeeded{false};
+ static std::mutex mutex;
+
+ std::lock_guard<std::mutex> g(mutex);
+
+ if (succeeded) {
+ return true;
+ }
+ if (attempted) {
+ return false;
+ }
+
+ attempted = true;
+
+ HMODULE psapi = LoadLibraryA("psapi.dll");
+ HMODULE dbghelp = LoadLibraryA("dbghelp.dll");
+
+ // clang-format off
+ succeeded = true
+ && (psapi != nullptr)
+ && (dbghelp != nullptr)
+ && get_func(psapi, &EnumProcessModules, "EnumProcessModules")
+ && get_func(psapi, &GetModuleInformation, "GetModuleInformation")
+ && get_func(psapi, &GetModuleBaseName, "GetModuleBaseNameA")
+ && get_func(dbghelp, &ImageNtHeader, "ImageNtHeader")
+ && get_func(dbghelp, &SymCleanup, "SymCleanup")
+ && get_func(dbghelp, &SymGetOptions, "SymGetOptions")
+ && get_func(dbghelp, &SymGetSearchPath, "SymGetSearchPath")
+ && get_func(dbghelp, &SymInitialize, "SymInitialize")
+ && get_func(dbghelp, &SymSetOptions, "SymSetOptions")
+ && get_func(dbghelp, &SymSetSearchPath, "SymSetSearchPath")
+#ifdef _WIN64
+ && get_func(dbghelp, &StackWalk, "StackWalk64")
+ && get_func(dbghelp, &SymFunctionTableAccess, "SymFunctionTableAccess64")
+ && get_func(dbghelp, &SymGetLineFromAddr, "SymGetLineFromAddr64")
+ && get_func(dbghelp, &SymGetModuleBase, "SymGetModuleBase64")
+ && get_func(dbghelp, &SymGetModuleInfo, "SymGetModuleInfo64")
+ && get_func(dbghelp, &SymGetSymFromAddr, "SymGetSymFromAddr64")
+ && get_func(dbghelp, &SymLoadModule, "SymLoadModule64")
+#else
+ && get_func(dbghelp, &StackWalk, "StackWalk")
+ && get_func(dbghelp, &SymFunctionTableAccess, "SymFunctionTableAccess")
+ && get_func(dbghelp, &SymGetLineFromAddr, "SymGetLineFromAddr")
+ && get_func(dbghelp, &SymGetModuleBase, "SymGetModuleBase")
+ && get_func(dbghelp, &SymGetModuleInfo, "SymGetModuleInfo")
+ && get_func(dbghelp, &SymGetSymFromAddr, "SymGetSymFromAddr")
+ && get_func(dbghelp, &SymLoadModule, "SymLoadModule")
+#endif
+ ;
+ // clang-format on
+
+ return succeeded;
+}
+
+} // namespace __dll
+
+using namespace __dll;
+
+struct _Sym_Init_Scope {
+ HANDLE proc_;
+
+ explicit _Sym_Init_Scope(HANDLE proc) : proc_(proc) { SymInitialize(proc_, nullptr, true); }
+ ~_Sym_Init_Scope() { SymCleanup(proc_); }
+};
+
+} // namespace
+
+_LIBCPP_EXPORTED_FROM_ABI void _Trace::windows_impl(size_t skip, size_t max_depth) {
+ static bool loadedDLLFuncs = loadFuncs();
+ if (!loadedDLLFuncs) {
+ return;
+ }
+
+ if (!max_depth) {
+ return;
+ }
+
+ // Use the Windows Debug Help and Process Status libraries to get a stacktrace.
+ // https://learn.microsoft.com/en-us/windows/win32/debug/debug-help-library
+ // https://learn.microsoft.com/en-us/windows/win32/psapi/process-status-helper
+
+ // These APIs are 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 = GetModuleHandleA(nullptr);
+ if (!exe) {
+ return;
+ }
+
+ _Sym_Init_Scope symscope(proc);
+
+ // Allow space for a handful of paths
+ char sym_path[MAX_PATH * 4];
+ if (!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 (!SymSetSearchPath(proc, sym_path)) {
+ return;
+ }
+ }
+
+ IMAGE_NT_HEADERS* nt_headers;
+ if (!(nt_headers = ImageNtHeader(exe))) {
+ return;
+ }
+
+ SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
+
+ HANDLE thread = GetCurrentThread();
+ int machine = nt_headers->FileHeader.Machine;
+
+ CONTEXT ccx;
+ RtlCaptureContext(&ccx);
+
+ STACKFRAME frame;
+ memset(&frame, 0, sizeof(frame));
----------------
frederick-vs-ja wrote:
```suggestion
STACKFRAME frame{};
```
Can we use `{}` to zero-out the object? Moreover, can we use designated initialization instead of assignment?
https://github.com/llvm/llvm-project/pull/136528
More information about the libcxx-commits
mailing list