[libcxx-commits] [libcxx] [libc++] Add std::stacktrace (P0881R7) (PR #136528)

Steve O'Brien via libcxx-commits libcxx-commits at lists.llvm.org
Sun May 10 20:59:07 PDT 2026


================
@@ -0,0 +1,290 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 {
+
+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, LPDWORD);
+BOOL   (WINAPI*   GetModuleInformation)(HANDLE, HMODULE, LPMODULEINFO, DWORD);
+DWORD  (WINAPI*   GetModuleBaseName)(HANDLE, HMODULE, LPSTR, DWORD);
+PIMAGE_NT_HEADERS (IMAGEAPI* ImageNtHeader)(PVOID);
+BOOL   (IMAGEAPI* SymCleanup)(HANDLE);
+DWORD  (IMAGEAPI* SymGetOptions)();
+BOOL   (IMAGEAPI* SymGetSearchPath)(HANDLE, PSTR, DWORD);
+BOOL   (IMAGEAPI* SymInitialize)(HANDLE, PCSTR, BOOL);
+DWORD  (IMAGEAPI* SymSetOptions)(DWORD);
+BOOL   (IMAGEAPI* SymSetSearchPath)(HANDLE, PCSTR);
+#  ifdef _WIN64
+PVOID  (IMAGEAPI* SymFunctionTableAccess)(HANDLE, DWORD64);
+BOOL   (IMAGEAPI* SymGetLineFromAddr)(HANDLE, DWORD64, PDWORD, IMAGEHLP_LINE64*);
+DWORD64(IMAGEAPI* SymGetModuleBase)(HANDLE, DWORD64);
+BOOL   (IMAGEAPI* SymGetModuleInfo)(HANDLE, DWORD64, PIMAGEHLP_MODULE64);
+BOOL   (IMAGEAPI* SymGetSymFromAddr)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64);
+DWORD64(IMAGEAPI* SymLoadModule)(HANDLE, HANDLE, PCSTR, PCSTR, DWORD64, DWORD);
+BOOL   (IMAGEAPI* StackWalk)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID,
+                             PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64,
+                             PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
+#  else
+PVOID  (IMAGEAPI* SymFunctionTableAccess)(HANDLE, DWORD);
+BOOL   (IMAGEAPI* SymGetLineFromAddr)(HANDLE, DWORD, PDWORD, IMAGEHLP_LINE*);
+DWORD  (IMAGEAPI* SymGetModuleBase)(HANDLE, DWORD);
+BOOL   (IMAGEAPI* SymGetModuleInfo)(HANDLE, DWORD, PIMAGEHLP_MODULE);
+BOOL   (IMAGEAPI* SymGetSymFromAddr)(HANDLE, DWORD, PDWORD, PIMAGEHLP_SYMBOL);
+DWORD  (IMAGEAPI* SymLoadModule)(HANDLE, HANDLE, PCSTR, PCSTR, DWORD, DWORD);
+BOOL   (IMAGEAPI* StackWalk)(DWORD, HANDLE, HANDLE, STACKFRAME64*, PVOID,
+                             PREAD_PROCESS_MEMORY_ROUTINE, PFUNCTION_TABLE_ACCESS_ROUTINE,
+                             PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE);
+#  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 /* but not successful */) {
+    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;
+}
+
+struct SymInitScope {
+  HANDLE proc_;
+
+  explicit SymInitScope(HANDLE proc) : proc_(proc) { SymInitialize(proc_, nullptr, true); }
+  ~SymInitScope() { 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;
+  }
+
+  SymInitScope 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)) {
----------------
elsteveogrande wrote:

Thank you @oold and @R-Goc for help on the windows front!  I'm going to adapt [these helper functions in `filesystem/path.cpp`](https://github.com/elsteveogrande/llvm-project/blob/main/libcxx/src/filesystem/path.cpp#L422)

https://github.com/llvm/llvm-project/pull/136528


More information about the libcxx-commits mailing list