[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