Skip to content

Conversation

H-G-Hristov
Copy link
Contributor

@H-G-Hristov H-G-Hristov commented Feb 12, 2024

Implements (partially):

What was implemented so far:

  • Implemented functions
    • breakpoint()
    • breakpoint_if_debugging()
    • is_debugger_present()
  • Implementation: Supported platforms
    • AIX
    • Apple
    • FreeBSD
    • Linux
      • Android
    • Picolib
    • Windows
  • Tests: Supported debuggers
    • GDB (no Android specific test)
      • breakpoint()
      • breakpoint_if_debugging()
      • is_debugger_present()
    • LLDB (no Android specific tests)
      • breakpoint()
      • breakpoint_if_debugging()
      • is_debugger_present()
    • dbx (AIX only)
      • breakpoint()
      • breakpoint_if_debugging()
      • is_debugger_present()

Closes #105392
Closes #105422

Copy link

github-actions bot commented Feb 12, 2024

✅ With the latest revision this PR passed the Python code formatter.

@H-G-Hristov H-G-Hristov force-pushed the hgh/libcxx/P2546R5-Debugging-support branch 6 times, most recently from 96b5e74 to 5223217 Compare February 17, 2024 06:24
@H-G-Hristov
Copy link
Contributor Author

H-G-Hristov commented Feb 27, 2024

WIP notes to collect feedback on the possible implementation:

  • breakpoint() - probably a test is possible but I haven't figured out how to do it yet.
  • is_debugger_present() - has tests for gdb and lldb, which fail on some Windows configurations.
  • debugger support: when the debugger breaks in breakpoint() it breaks inside the implementation, which isn't an optimal user experience. Perhaps LLDB (the debugger) can be thought to break on breakpoint() call site, similar to the /JMC option in MSVC but this is unrelated work to this patch.
  • FreeBSD implementation should be identical to Apple.
  • AIX - no implementation, no access.
  • Picolib - ???
  • No Android specific tests due to the need to setup an adb debugging (Andorid is Linux).

@H-G-Hristov H-G-Hristov changed the title WIP [libc++][debugging] P2546R5: Debugging support WIP - [libc++][debugging] P2546R5: Debugging support Mar 1, 2024
@Zingam Zingam added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Mar 1, 2024
@llvmbot
Copy link
Member

llvmbot commented Mar 1, 2024

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

Changes

Implements (partially): https://wg21.link/P0543R3

What was implemented so far:

  • Implemented functions
    • breakpoint()
    • breakpoint_if_debugging()
    • is_debugger_present()
  • Implementation: Supported platforms
    • AIX
    • Apple
    • FreeBSD
    • Linux
    • Picolib
    • Windows
  • Tests: Supported debuggers
    • GDB
      • breakpoint()
      • breakpoint_if_debugging()
      • is_debugger_present()
    • LLDB
      • breakpoint()
      • breakpoint_if_debugging()
      • is_debugger_present()

Patch is 54.03 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/81447.diff

38 Files Affected:

  • (modified) libcxx/docs/FeatureTestMacroTable.rst (+1-1)
  • (modified) libcxx/docs/Status/Cxx2cPapers.csv (+1-1)
  • (modified) libcxx/include/CMakeLists.txt (+1)
  • (modified) libcxx/include/__config (+6)
  • (modified) libcxx/include/__std_clang_module (+1)
  • (added) libcxx/include/debugging (+44)
  • (modified) libcxx/include/module.modulemap.in (+4)
  • (modified) libcxx/include/version (+3-1)
  • (modified) libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist (+3)
  • (modified) libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist (+3)
  • (modified) libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist (+3)
  • (modified) libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist (+3)
  • (modified) libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist (+3)
  • (modified) libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist (+3)
  • (modified) libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist (+3)
  • (modified) libcxx/modules/CMakeLists.txt (+1)
  • (modified) libcxx/modules/std.compat.cppm.in (-3)
  • (modified) libcxx/modules/std.cppm.in (+1-3)
  • (added) libcxx/modules/std/debugging.inc (+17)
  • (modified) libcxx/src/CMakeLists.txt (+1)
  • (added) libcxx/src/debugging.cpp (+158)
  • (modified) libcxx/test/libcxx/transitive_includes/cxx03.csv (+5)
  • (modified) libcxx/test/libcxx/transitive_includes/cxx11.csv (+1)
  • (modified) libcxx/test/libcxx/transitive_includes/cxx14.csv (+1)
  • (modified) libcxx/test/libcxx/transitive_includes/cxx17.csv (+1)
  • (modified) libcxx/test/libcxx/transitive_includes/cxx20.csv (+1)
  • (modified) libcxx/test/libcxx/transitive_includes/cxx23.csv (+1)
  • (modified) libcxx/test/libcxx/transitive_includes/cxx26.csv (+1)
  • (added) libcxx/test/std/language.support/support.limits/support.limits.general/debugging.version.compile.pass.cpp (+71)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+3-3)
  • (added) libcxx/test/std/utilities/debugging/is_debugger_present.pass.cpp (+33)
  • (added) libcxx/test/std/utilities/debugging/is_debugger_present_with_debugger_gdb.py (+120)
  • (added) libcxx/test/std/utilities/debugging/is_debugger_present_with_debugger_gdb.sh.cpp (+61)
  • (added) libcxx/test/std/utilities/debugging/is_debugger_present_with_debugger_lldb.py (+86)
  • (added) libcxx/test/std/utilities/debugging/is_debugger_present_with_debugger_lldb.sh.cpp (+59)
  • (modified) libcxx/utils/generate_feature_test_macro_components.py (+2-1)
  • (modified) libcxx/utils/libcxx/header_information.py (+1-1)
  • (modified) libcxx/utils/libcxx/test/features.py (+19)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 468226c0c2dddf..6b86aed145218c 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -400,7 +400,7 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_copyable_function``                     *unimplemented*
     --------------------------------------------------- -----------------
-    ``__cpp_lib_debugging``                             *unimplemented*
+    ``__cpp_lib_debugging``                             ``202311L``
     --------------------------------------------------- -----------------
     ``__cpp_lib_freestanding_algorithm``                *unimplemented*
     --------------------------------------------------- -----------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index a62faee4f44e22..bc391e198be4e7 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -29,7 +29,7 @@
 "","","","","","",""
 "`P0543R3 <https://wg21.link/P0543R3>`__","LWG","Saturation arithmetic","Kona November 2023","|Complete|","18.0",""
 "`P2407R5 <https://wg21.link/P2407R5>`__","LWG","Freestanding Library: Partial Classes","Kona November 2023","","",""
-"`P2546R5 <https://wg21.link/P2546R5>`__","LWG","Debugging Support","Kona November 2023","","",""
+"`P2546R5 <https://wg21.link/P2546R5>`__","LWG","Debugging Support","Kona November 2023","|Partial|","19.0",""
 "`P2905R2 <https://wg21.link/P2905R2>`__","LWG","Runtime format strings","Kona November 2023","|Complete|","18.0","|format| |DR|"
 "`P2918R2 <https://wg21.link/P2918R2>`__","LWG","Runtime format strings II","Kona November 2023","|Complete|","18.0","|format|"
 "`P2909R4 <https://wg21.link/P2909R4>`__","LWG","Fix formatting of code units as integers (Dude, where’s my ``char``?)","Kona November 2023","|Complete|","18.0","|format| |DR|"
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b44068357e7089..52f28da2a20af7 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -914,6 +914,7 @@ set(files
   cuchar
   cwchar
   cwctype
+  debugging
   deque
   errno.h
   exception
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 0797880cb2f5da..1766aea8b92190 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -516,6 +516,7 @@ _LIBCPP_HARDENING_MODE_DEBUG
 #    define _LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN
 #    define _LIBCPP_HAS_NO_INCOMPLETE_TZDB
 #    define _LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM
+#    define _LIBCPP_HAS_NO_INCOMPLETE_DEBUGGING
 #  endif
 
 // Need to detect which libc we're using if we're on Linux.
@@ -1567,6 +1568,11 @@ __sanitizer_verify_double_ended_contiguous_container(const void*, const void*, c
 #    define _LIBCPP_HAS_EXPLICIT_THIS_PARAMETER
 #  endif
 
+#  if (defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__) || defined(_LIBCPP_WIN32API)) &&               \
+      !defined(__PICOLIBC__) && !defined(_LIBCPP_HAS_NO_INCOMPLETE_DEBUGGING)
+#    define _LIBCPP_HAS_DEBUGGING
+#  endif
+
 #endif // __cplusplus
 
 #endif // _LIBCPP___CONFIG
diff --git a/libcxx/include/__std_clang_module b/libcxx/include/__std_clang_module
index 18d6ce6b46c1f6..7dcee53c68b399 100644
--- a/libcxx/include/__std_clang_module
+++ b/libcxx/include/__std_clang_module
@@ -81,6 +81,7 @@
 #if !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
 #  include <cwctype>
 #endif
+#include <debugging>
 #include <deque>
 #include <errno.h>
 #include <exception>
diff --git a/libcxx/include/debugging b/libcxx/include/debugging
new file mode 100644
index 00000000000000..686ce7c861fa7e
--- /dev/null
+++ b/libcxx/include/debugging
@@ -0,0 +1,44 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_DEBUGGING
+#define _LIBCPP_DEBUGGING
+
+/*
+// all freestanding
+namespace std {
+  // [debugging.utility], utility
+  void breakpoint() noexcept;
+  void breakpoint_if_debugging() noexcept;
+  bool is_debugger_present() noexcept;
+}
+*/
+
+#include <__config>
+#include <version>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26 && defined(_LIBCPP_HAS_DEBUGGING)
+
+_LIBCPP_EXPORTED_FROM_ABI void breakpoint() noexcept;
+
+_LIBCPP_EXPORTED_FROM_ABI void breakpoint_if_debugging() noexcept;
+
+_LIBCPP_EXPORTED_FROM_ABI bool is_debugger_present() noexcept;
+
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_DEBUGGING
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 63af3a90d88b9b..11dca60a92b5db 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -59,6 +59,10 @@ module std_coroutine [system] {
   header "coroutine"
   export *
 }
+module std_debugging [system] {
+  header "debugging"
+  export *
+}
 module std_deque [system] {
   header "deque"
   export *
diff --git a/libcxx/include/version b/libcxx/include/version
index b18927a2bc38c2..5f9203ae36091f 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -491,7 +491,9 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 # define __cpp_lib_bind_front                           202306L
 # define __cpp_lib_bitset                               202306L
 // # define __cpp_lib_copyable_function                    202306L
-// # define __cpp_lib_debugging                            202311L
+# if _LIBCPP_STD_VER >= 26 && defined(_LIBCPP_HAS_DEBUGGING)
+#   define __cpp_lib_debugging                          202311L
+# endif
 // # define __cpp_lib_freestanding_algorithm               202311L
 // # define __cpp_lib_freestanding_array                   202311L
 // # define __cpp_lib_freestanding_cstring                 202306L
diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 2064f45bf8c084..7612a3ec25ff53 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -862,6 +862,7 @@
 {'is_defined': True, 'name': '__ZNSt3__110__time_putC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__110__time_putD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__110__time_putD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__110breakpointEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__110ctype_base5alnumE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__110ctype_base5alphaE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__110ctype_base5blankE', 'size': 0, 'type': 'OBJECT'}
@@ -1516,6 +1517,7 @@
 {'is_defined': True, 'name': '__ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__119declare_no_pointersEPcm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__119is_debugger_presentEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__120__get_collation_nameEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__120__libcpp_atomic_waitEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEEx', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__120__libcpp_atomic_waitEPVKvx', 'type': 'FUNC'}
@@ -1537,6 +1539,7 @@
 {'is_defined': True, 'name': '__ZNSt3__123__cxx_atomic_notify_oneEPVKv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__123breakpoint_if_debuggingEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
index fec3a4505a0c6d..b57785484f8ae8 100644
--- a/libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -543,6 +543,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk110__time_putC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk110__time_putD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk110__time_putD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk110breakpointEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk110ctype_base5alnumE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk110ctype_base5alphaE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk110ctype_base5blankE', 'size': 4, 'type': 'OBJECT'}
@@ -1197,6 +1198,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk119declare_no_pointersEPcj', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk119is_debugger_presentEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk120__get_collation_nameEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk120__libcpp_atomic_waitEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEEi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk120__libcpp_atomic_waitEPVKvi', 'type': 'FUNC'}
@@ -1218,6 +1220,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk123__cxx_atomic_notify_oneEPVKv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk123breakpoint_if_debuggingEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index bced6b2ea81ba5..c6c24b148ea80a 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -862,6 +862,7 @@
 {'is_defined': True, 'name': '__ZNSt3__110__time_putC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__110__time_putD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__110__time_putD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__110breakpointEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__110ctype_base5alnumE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__110ctype_base5alphaE', 'size': 0, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '__ZNSt3__110ctype_base5blankE', 'size': 0, 'type': 'OBJECT'}
@@ -1516,6 +1517,7 @@
 {'is_defined': True, 'name': '__ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__119declare_no_pointersEPcm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__119is_debugger_presentEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__120__get_collation_nameEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__120__libcpp_atomic_waitEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEEx', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__120__libcpp_atomic_waitEPVKvx', 'type': 'FUNC'}
@@ -1537,6 +1539,7 @@
 {'is_defined': True, 'name': '__ZNSt3__123__cxx_atomic_notify_oneEPVKv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__123breakpoint_if_debuggingEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
 {'is_defined': True, 'name': '__ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
index efa2189e9c9287..0187b958bed61c 100644
--- a/libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -543,6 +543,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk110__time_putC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk110__time_putD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk110__time_putD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk110breakpointEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk110ctype_base5alnumE', 'size': 8, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk110ctype_base5alphaE', 'size': 8, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk110ctype_base5blankE', 'size': 8, 'type': 'OBJECT'}
@@ -1197,6 +1198,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk119declare_no_pointersEPcm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk119is_debugger_presentEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk120__get_collation_nameEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk120__libcpp_atomic_waitEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEEi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk120__libcpp_atomic_waitEPVKvi', 'type': 'FUNC'}
@@ -1218,6 +1220,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk123__cxx_atomic_notify_oneEPVKv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk123breakpoint_if_debuggingEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index ebda5b0dfba57d..467284019aab7c 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -557,6 +557,7 @@
 {'is_defined': True, 'name': '_ZNSt3__110__time_putC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__110__time_putD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__110__time_putD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__110breakpointEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__110ctype_base5alnumE', 'size': 8, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__110ctype_base5alphaE', 'size': 8, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__110ctype_base5blankE', 'size': 8, 'type': 'OBJECT'}
@@ -1211,6 +1212,7 @@
 {'is_defined': True, 'name': '_ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__119declare_no_pointersEPcm', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__119is_debugger_presentEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__120__get_collation_nameEPKc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__120__libcpp_atomic_waitEPVKNS_17__cxx_atomic_implIlNS_22__cxx_atomic_base_implIlEEEEl', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__120__libcpp_atomic_waitEPVKvl', 'type': 'FUNC'}
@@ -1232,6 +1234,7 @@
 {'is_defined': True, 'name': '_ZNSt3__123__cxx_atomic_notify_oneEPVKv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIlNS_22__cxx_atomic_base_implIlEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__123breakpoint_if_debuggingEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 6432ad3be35859..2c4045dc798ebf 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -555,6 +555,7 @@
 {'is_defined': True, 'name': '_ZNSt3__110__time_putC2ERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__110__time_putD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__110__time_putD2Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__110breakpointEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__110ctype_base5alnumE', 'size': 2, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__110ctype_base5alphaE', 'size': 2, 'type': 'O...
[truncated]

@mordante mordante self-assigned this Mar 3, 2024
@H-G-Hristov H-G-Hristov force-pushed the hgh/libcxx/P2546R5-Debugging-support branch from 5223217 to 722d2bb Compare March 11, 2024 05:53
@H-G-Hristov H-G-Hristov force-pushed the hgh/libcxx/P2546R5-Debugging-support branch 2 times, most recently from b8203d0 to 757f05e Compare March 20, 2024 20:56
@mordante
Copy link
Member

FYI This morning https://isocpp.org/files/papers/P2810R4.html was approved. This seems small and maybe can be done directly in this commit.

@H-G-Hristov
Copy link
Contributor Author

FYI This morning https://isocpp.org/files/papers/P2810R4.html was approved. This seems small and maybe can be done directly in this commit.

Thank you for reminding!

@daltenty
Copy link
Member

AIX - no implementation, no access.

Thanks for working on this. I can provide access to this if you'd like (I'll reach out on discord as well)

@H-G-Hristov
Copy link
Contributor Author

H-G-Hristov commented Mar 29, 2024

AIX - no implementation, no access.

Thanks for working on this. I can provide access to this if you'd like (I'll reach out on discord as well)

@daltenty Thank you! Please reach me on Discord (Zingam).

@H-G-Hristov H-G-Hristov force-pushed the hgh/libcxx/P2546R5-Debugging-support branch from c05baf2 to 91ca034 Compare March 29, 2024 09:38
Comment on lines 1 to 3
run
cont
quit
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daltenty Could you please check if the "breakpoint" tests makes sense and if it can be fixed?

Copy link
Member

@mordante mordante left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not do a deep review, mainly looked at the general approach.

I like the approach, but I think we could add some comments regarding the restrictions and the proposal by @vogelsgesang. I think it might be faster if we can directly check for LLDB/GDB without going through the proc filesystem.


_LIBCPP_AVAILABILITY_DEBUGGING _LIBCPP_EXPORTED_FROM_ABI void __breakpoint() noexcept;

_LIBCPP_AVAILABILITY_DEBUGGING _LIBCPP_HIDE_FROM_ABI inline void breakpoint() noexcept {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on

4.1. Unconditional Breakpoint

The goal of the std::breakpoint function is to "break" or pause the running program when called. Having an unconditional, i.e. attempts to break even if the debugger is or is not actually monitoring the program allows for use in conditions where it is not possible to detect if a debugger is present.

Implementations are expected to optimize the code generated to be as minimal as possible for the platform. For example, on X86 it’s expected that this produces a single INT3 instruction. The goal in this expectation is to place the debugger as close as possible in the caller of breakpoint() to improve the debugging experience for users.

I think it would be good to mark this function _LIBCPP_ALWAYS_INLINE with a short comment.

Did you verify the generated assembly? Also for -O0?

Comment on lines 39 to 40
# elif defined(_MSC_VER)
__debugbreak();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We support clang-cl which should behave like MSVC. We also support MinGW.

},
"headers": ["debugging"],
"unimplemented": True,
"test_suite_guard": "TEST_STD_VER >= 26 && defined(_LIBCPP_AVAILABILITY_HAS_DEBUGGING)",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why guard against the Standard version?

]


# Detect whether dbx is on the system.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please explain what DBX is, I guess the platform debugger of AIX. LLDB and GDB are well known debuggers so there only the name suffices.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.



# Detect whether LLDB is on the system.
def check_lldb(cfg):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not detect Python support in LLDB the name of the associated feature implies support host-has-lldb-with-python. I assume the tests require Python support so this looks like it needs more work. Maybe look at the LLDB test suite how they determine Python support.


#elif defined(_AIX)

static bool __is_debugger_present() noexcept {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Determining the filename of the proc feels quite hacky. Is this safe when using threading and spawing subprocesses?

Why not using std::string __filename = std::format("/proc/{}/status", getpid()); ?

Comment on lines +138 to +140
_LIBCPP_ASSERT_INTERNAL(false,
"Function is not available. Could not open '/proc/self/status' for reading, libc++ was "
"compiled with _LIBCPP_HAS_NO_FILESYSTEM.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does AIX not have the same issue?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bare metal linux systems without a filesystem are a thing, but is "freestanding AIX" a thing? I doubt it.

#include <debugging>

void test() {
static_assert(noexcept(std::breakpoint()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to test the noexcept one time in a separate test.

Since these tests do not use constexpr there is no reason for the test() indirection.


// <debugging>

// bool is_debugger_present() noexcept;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add comment that this test tests the "true" result and that libcxx/test/std/utilities/debugging/is_debugger_present.pass.cpp tests the properties and the false result. The same for the other tests.

free(__line);
fclose(__proc_status_fp);

return __is_debugger_present;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The paper has the non-normative note "On POSIX this can be achieved by checking for a tracer parent process, with best effort determination that such a tracer parent process is a debugger." Did you investigate how easy it is to determined the process name of the tracing program and add a filter for some names?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

atoi(__tokenPos + strlen(__tokenStr)) gives you the tracer's PID so you can use /proc/{pid} again to find its details (although not 100% reliably if the executable has been removed from the filesystem, or the tracer process modified its argv[0]).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer an implementation which uses plain POSIX to access the /proc filesystem.
It should be the absolute Minimum to end up in the breakpoint ASAP when stuff goes wrong.

C libraries might do any kind of buffering or additional accesses too, for example Musl warns about using stdio for /proc (likely only relevant or writes, but still).
It doesn't make sense to depend on any of at.

Following is a tested implementation, based on the beginning of the status format that should never change

    // 171 bytes would enough to include TracerPid on Linux 6.11 with hypothetical 64bit pids
#ifdef __GNUC__
    __attribute__((__aligned__(8)))
#endif
    char buffer[256 + 1];

    int buf_read = ::open("/proc/self/status", O_RDONLY | O_CLOEXEC);
    if (buf_read >= 0)
    {
        const ssize_t result = ::read(buf_read, buffer, sizeof(buffer) - 1);
        ::close(buf_read);
        buf_read = static_cast<int>(result);
    }
    if (buf_read < 80)
    {
        return false;
    }

    // to use strstr if memmem is not available
    buffer[buf_read] = '\0';
#define TRACER_KEY "\nTracerPid:\t"

    // Skip some bytes including the Name field which might contain unexpected characters
    char *pos = (char *)memmem(buffer + 64 , buf_read - 64, TRACER_KEY, sizeof(TRACER_KEY) - 1);
    return pos != nullptr && pos[sizeof(TRACER_KEY) - 1] != '0';
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nolange Thanks for the suggestion!

@jwakely
Copy link
Contributor

jwakely commented Jun 3, 2024

See https://discourse.llvm.org/t/runtime-support-for-std-is-debugger-present/79338?u=jwakely for the approach we're considering for libstdc++ and GDB, which is similar to @vogelsgesang's suggestion.

I completely agree that a better user experience would be for the debugger to recognize std::breakpoint() and std::breakpoint_if_debugging() and break in the user code where those functions are called (rather than inside those functions in std::lib code). And if the debugger does that, it should then not step into the function, but skip over it (because it already put a breakpoint there, so we don't need the __builtin_debugtrap(), __builtin_trap() etc. that the function body would do). GDB support for that is being discussed.

I also agree that an internal bool flag in the library is valuable. A public API like __cxa_attach_debugger(bool) would be useful for debuggers to set/clear the library's internal bool variable that says a debugger is present. std::is_debugger_present() would then check that bool flag. If the API is common to libc++ and libstdc++ then debuggers don't need to know 2+ different protocols for announcing when they're attached.

A suggestion for libstdc++ was that the "attach_debugger" API should return the address of std::breakpoint, as this might simplify things for the debugger if it wants to automatically insert a breakpoint on calls to that function. Otherwise the debugger has to be prepared to look up std::breakpoint(), or std::__v1::breakpoint(), or std::__8::breakpoint(), or std::__other_inline_namespace::breakpoint(). But if we want the debugger to treat std::breakpoint_if_debugging() the same way, then returning just the address of std::breakpoint is less useful.

@jwakely
Copy link
Contributor

jwakely commented Jun 3, 2024

I completely agree that a better user experience would be for the debugger to recognize std::breakpoint() and std::breakpoint_if_debugging() and break in the user code where those functions are called (rather than inside those functions in std::lib code). And if the debugger does that, it should then not step into the function, but skip over it (because it already put a breakpoint there, so we don't need the __builtin_debugtrap(), __builtin_trap() etc. that the function body would do). GDB support for that is being discussed.

If debuggers are going to do that, could they also recognize is_debugger_present() and just replace it with true, without needing the bool flag or special hooks?

@Zingam Zingam added the c++26 label Mar 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++26 libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

P2810R4: is_debugger_present is_replaceable P2546R5: Debugging Support

9 participants