https://gcc.gnu.org/g:3c95766e92d6782eb945593c043a16be096707af
commit r16-3445-g3c95766e92d6782eb945593c043a16be096707af Author: Jonathan Wakely <jwak...@redhat.com> Date: Thu Apr 10 17:50:59 2025 +0100 libstdc++: Implement C++26 <debugging> features [PR119670] This implements P2546R5 (Debugging Support), including the P2810R4 (is_debugger_present is_replaceable) changes, allowing std::is_debugger_present to be replaced by the program. It would be good to provide a macOS definition of is_debugger_present as per https://developer.apple.com/library/archive/qa/qa1361/_index.html but that isn't included in this change. The src/c++26/debugging.cc file defines a global volatile int which can be set by debuggers to indicate when they are attached and detached from a running process. This allows std::is_debugger_present() to give a reliable answer, and additionally allows a debugger to choose how std::breakpoint() should behave. Setting the global to a positive value will cause std::breakpoint() to use that value as an argument to std::raise, so debuggers that prefer SIGABRT for breakpoints can select that. By default std::breakpoint() will use a platform-specific action such as the INT3 instruction on x86, or GCC's __builtin_trap(). On Linux the std::is_debugger_present() function checks whether the process is being traced by a process named "gdb", "gdbserver" or "lldb-server", to try to avoid interpreting other tracing processes (such as strace) as a debugger. There have been comments suggesting this isn't desirable and that std::is_debugger_present() should just return true for any tracing process (which is the case for non-Linux targets that support the ptrace system call). libstdc++-v3/ChangeLog: PR libstdc++/119670 * acinclude.m4 (GLIBCXX_CHECK_DEBUGGING): Check for facilities needed by <debugging>. * config.h.in: Regenerate. * configure: Regenerate. * configure.ac: Use GLIBCXX_CHECK_DEBUGGING. * include/Makefile.am: Add new header. * include/Makefile.in: Regenerate. * include/bits/version.def (debugging): Add. * include/bits/version.h: Regenerate. * include/precompiled/stdc++.h: Add new header. * src/c++26/Makefile.am: Add new file. * src/c++26/Makefile.in: Regenerate. * include/std/debugging: New file. * src/c++26/debugging.cc: New file. * testsuite/19_diagnostics/debugging/breakpoint.cc: New test. * testsuite/19_diagnostics/debugging/breakpoint_if_debugging.cc: New test. * testsuite/19_diagnostics/debugging/is_debugger_present.cc: New test. * testsuite/19_diagnostics/debugging/is_debugger_present-2.cc: New test. Reviewed-by: Tomasz KamiĆski <tkami...@redhat.com> Diff: --- libstdc++-v3/acinclude.m4 | 36 +++++ libstdc++-v3/config.h.in | 12 ++ libstdc++-v3/configure | 73 +++++++++ libstdc++-v3/configure.ac | 3 + libstdc++-v3/include/Makefile.am | 1 + libstdc++-v3/include/Makefile.in | 1 + libstdc++-v3/include/bits/version.def | 8 + libstdc++-v3/include/bits/version.h | 10 ++ libstdc++-v3/include/precompiled/stdc++.h | 1 + libstdc++-v3/include/std/debugging | 77 +++++++++ libstdc++-v3/src/c++26/Makefile.am | 4 +- libstdc++-v3/src/c++26/Makefile.in | 7 +- libstdc++-v3/src/c++26/debugging.cc | 175 +++++++++++++++++++++ .../19_diagnostics/debugging/breakpoint.cc | 13 ++ .../debugging/breakpoint_if_debugging.cc | 13 ++ .../debugging/is_debugger_present-2.cc | 19 +++ .../debugging/is_debugger_present.cc | 14 ++ 17 files changed, 464 insertions(+), 3 deletions(-) diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4 index 027735eeeb3b..eb2d26286561 100644 --- a/libstdc++-v3/acinclude.m4 +++ b/libstdc++-v3/acinclude.m4 @@ -5769,6 +5769,42 @@ AC_LANG_SAVE AC_LANG_RESTORE ]) +dnl +dnl Check whether the dependencies for std::is_debugger_present are available. +dnl +dnl Defines: +dnl _GLIBCXX_USE_PTRACE if ptrace(int, pid_t, int, int) is in <sys/ptrace.h>. +dnl _GLIBCXX_USE_PROC_SELF_STATUS if /proc/self/status should be used. +dnl +AC_DEFUN([GLIBCXX_CHECK_DEBUGGING], [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + + AC_CHECK_HEADERS([sys/ptrace.h debugapi.h]) + + case "$target_os" in + linux*) + AC_DEFINE([_GLIBCXX_USE_PROC_SELF_STATUS],1, + [Define if /proc/self/status should be used for <debugging>.]) + ;; + esac + + AC_MSG_CHECKING([whether ptrace(int, pid_t, int, int) is in <sys/ptrace.h>]) + AC_TRY_COMPILE([ + #include <sys/ptrace.h> + ],[ + int i = ptrace(PTRACE_TRACEME, (pid_t)0, 1, 0); + ], [ac_ptrace=yes], [ac_ptrace=no]) + AC_MSG_RESULT($ac_ptrace) + if test "$ac_ptrace" = yes; then + AC_DEFINE_UNQUOTED(_GLIBCXX_USE_PTRACE, 1, + [Define if ptrace should be used for std::is_debugger_present.]) + fi + + AC_LANG_RESTORE +]) + + # Macros from the top-level gcc directory. m4_include([../config/gc++filt.m4]) m4_include([../config/tls.m4]) diff --git a/libstdc++-v3/config.h.in b/libstdc++-v3/config.h.in index ffacdabac854..818117aa6cce 100644 --- a/libstdc++-v3/config.h.in +++ b/libstdc++-v3/config.h.in @@ -70,6 +70,9 @@ /* Define to 1 if you have the `cosl' function. */ #undef HAVE_COSL +/* Define to 1 if you have the <debugapi.h> header file. */ +#undef HAVE_DEBUGAPI_H + /* Define to 1 if you have the declaration of `strnlen', and to 0 if you don't. */ #undef HAVE_DECL_STRNLEN @@ -435,6 +438,9 @@ /* Define to 1 if you have the <sys/param.h> header file. */ #undef HAVE_SYS_PARAM_H +/* Define to 1 if you have the <sys/ptrace.h> header file. */ +#undef HAVE_SYS_PTRACE_H + /* Define to 1 if you have the <sys/resource.h> header file. */ #undef HAVE_SYS_RESOURCE_H @@ -846,6 +852,9 @@ /* Define if nl_langinfo_l should be used for std::text_encoding. */ #undef _GLIBCXX_USE_NL_LANGINFO_L +/* Define if /proc/self/status should be used for <debugging>. */ +#undef _GLIBCXX_USE_PROC_SELF_STATUS + /* Define if pthreads_num_processors_np is available in <pthread.h>. */ #undef _GLIBCXX_USE_PTHREADS_NUM_PROCESSORS_NP @@ -862,6 +871,9 @@ /* Define if POSIX read/write locks are available in <gthr.h>. */ #undef _GLIBCXX_USE_PTHREAD_RWLOCK_T +/* Define if ptrace should be used for std::is_debugger_present. */ +#undef _GLIBCXX_USE_PTRACE + /* Define if /dev/random and /dev/urandom are available for the random_device of TR1 (Chapter 5.1). */ #undef _GLIBCXX_USE_RANDOM_TR1 diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure index d57cfd581adc..c0eaeb97c499 100755 --- a/libstdc++-v3/configure +++ b/libstdc++-v3/configure @@ -54501,6 +54501,79 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu +# For std::is_debugger_present + + + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + + for ac_header in sys/ptrace.h debugapi.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + case "$target_os" in + linux*) + +$as_echo "#define _GLIBCXX_USE_PROC_SELF_STATUS 1" >>confdefs.h + + ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ptrace(int, pid_t, int, int) is in <sys/ptrace.h>" >&5 +$as_echo_n "checking whether ptrace(int, pid_t, int, int) is in <sys/ptrace.h>... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include <sys/ptrace.h> + +int +main () +{ + + int i = ptrace(PTRACE_TRACEME, (pid_t)0, 1, 0); + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_ptrace=yes +else + ac_ptrace=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ptrace" >&5 +$as_echo "$ac_ptrace" >&6; } + if test "$ac_ptrace" = yes; then + +cat >>confdefs.h <<_ACEOF +#define _GLIBCXX_USE_PTRACE 1 +_ACEOF + + fi + + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + # Define documentation rules conditionally. # See if makeinfo has been installed and is modern enough diff --git a/libstdc++-v3/configure.ac b/libstdc++-v3/configure.ac index a6c01b29e94b..0bf219174fe7 100644 --- a/libstdc++-v3/configure.ac +++ b/libstdc++-v3/configure.ac @@ -587,6 +587,9 @@ GLIBCXX_CHECK_FILEBUF_NATIVE_HANDLES # For std::text_encoding GLIBCXX_CHECK_TEXT_ENCODING +# For std::is_debugger_present +GLIBCXX_CHECK_DEBUGGING + # Define documentation rules conditionally. # See if makeinfo has been installed and is modern enough diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 6f248fe48cb2..6f5946970a89 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -68,6 +68,7 @@ std_headers = \ ${std_srcdir}/codecvt \ ${std_srcdir}/complex \ ${std_srcdir}/condition_variable \ + ${std_srcdir}/debugging \ ${std_srcdir}/deque \ ${std_srcdir}/execution \ ${std_srcdir}/filesystem \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index 014466fc40b8..4b5917ebb4ae 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -424,6 +424,7 @@ std_freestanding = \ @GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/codecvt \ @GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/complex \ @GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/condition_variable \ +@GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/debugging \ @GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/deque \ @GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/execution \ @GLIBCXX_HOSTED_TRUE@ ${std_srcdir}/filesystem \ diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 9d5122fed17b..84c755da10e2 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1906,6 +1906,14 @@ ftms = { }; }; +ftms = { + name = debugging; + values = { + v = 202403; + cxxmin = 26; + }; +}; + ftms = { name = fstream_native_handle; values = { diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 6feeaa4d15a7..410e3205339d 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -2134,6 +2134,16 @@ #endif /* !defined(__cpp_lib_constexpr_new) && defined(__glibcxx_want_constexpr_new) */ #undef __glibcxx_want_constexpr_new +#if !defined(__cpp_lib_debugging) +# if (__cplusplus > 202302L) +# define __glibcxx_debugging 202403L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_debugging) +# define __cpp_lib_debugging 202403L +# endif +# endif +#endif /* !defined(__cpp_lib_debugging) && defined(__glibcxx_want_debugging) */ +#undef __glibcxx_want_debugging + #if !defined(__cpp_lib_fstream_native_handle) # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED # define __glibcxx_fstream_native_handle 202306L diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h index 733a5e5fb0be..636632a44392 100644 --- a/libstdc++-v3/include/precompiled/stdc++.h +++ b/libstdc++-v3/include/precompiled/stdc++.h @@ -237,6 +237,7 @@ #endif #if __cplusplus > 202302L +#include <debugging> #include <inplace_vector> #include <text_encoding> #include <stdbit.h> diff --git a/libstdc++-v3/include/std/debugging b/libstdc++-v3/include/std/debugging new file mode 100644 index 000000000000..4cf7e4acbc4d --- /dev/null +++ b/libstdc++-v3/include/std/debugging @@ -0,0 +1,77 @@ +// Debugging support -*- C++ -*- + +// Copyright The GNU Toolchain Authors. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// <http://www.gnu.org/licenses/>. + +/** @file include/debugging + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_DEBUGGING +#define _GLIBCXX_DEBUGGING 1 + +#define __glibcxx_want_debugging +#include <bits/version.h> + +#if __cpp_lib_debugging // C++ >= 26 +namespace std _GLIBCXX_VISIBILITY(default) +{ +// N.B. _GLIBCXX_BEGIN_NAMESPACE_VERSION is not used here. + +/** Try to determine if the program is running under control of a debugger. + * + * On GNU/Linux systems this function will only return true if the program + * is being traced by another program which is known to be a debugger. + * This is determined by checking the command name of the tracing program + * against a list of known debuggers, such as "gdb". + * + * On other POSIX-based systems, this function will return true if the + * program is being traced by any other process, which means it can return + * true for non-debugger utilities that use the ptrace system call. + * + * @since C++26 + */ +bool +is_debugger_present() noexcept; + +/** Stop the program with a breakpoint or debug trap. + * + * The details of how a breakpoint is implemented are platform-specific. + * Some systems provide a special instruction, such as `int3` in x86. + * When no more appropriate mechanism is available, this will stop the + * program using `__builtin_trap()`. It might not be possible for the + * program to continue after such a breakpoint. + * + * @since C++26 + */ +void +breakpoint() noexcept; + +/** Stop the program if it is running under control of a debugger. + * + * @since C++26 + */ +void +breakpoint_if_debugging() noexcept; + +} // namespace std +#endif +#endif // _GLIBCXX_DEBUGGING diff --git a/libstdc++-v3/src/c++26/Makefile.am b/libstdc++-v3/src/c++26/Makefile.am index 5defa4ac481a..4123b7dd3695 100644 --- a/libstdc++-v3/src/c++26/Makefile.am +++ b/libstdc++-v3/src/c++26/Makefile.am @@ -35,7 +35,9 @@ else inst_sources = endif -sources = text_encoding.cc +sources = \ + debugging.cc \ + text_encoding.cc vpath % $(top_srcdir)/src/c++26 diff --git a/libstdc++-v3/src/c++26/Makefile.in b/libstdc++-v3/src/c++26/Makefile.in index 77e73b2b2650..1c317d6ac7c2 100644 --- a/libstdc++-v3/src/c++26/Makefile.in +++ b/libstdc++-v3/src/c++26/Makefile.in @@ -121,7 +121,7 @@ CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libc__26convenience_la_LIBADD = -am__objects_1 = text_encoding.lo +am__objects_1 = debugging.lo text_encoding.lo am__objects_2 = @GLIBCXX_HOSTED_TRUE@am_libc__26convenience_la_OBJECTS = \ @GLIBCXX_HOSTED_TRUE@ $(am__objects_1) $(am__objects_2) @@ -430,7 +430,10 @@ headers = # XTEMPLATE_FLAGS = -fno-implicit-templates @ENABLE_EXTERN_TEMPLATE_TRUE@inst_sources = -sources = text_encoding.cc +sources = \ + debugging.cc \ + text_encoding.cc + @GLIBCXX_HOSTED_FALSE@libc__26convenience_la_SOURCES = @GLIBCXX_HOSTED_TRUE@libc__26convenience_la_SOURCES = $(sources) $(inst_sources) diff --git a/libstdc++-v3/src/c++26/debugging.cc b/libstdc++-v3/src/c++26/debugging.cc new file mode 100644 index 000000000000..72e305d3272b --- /dev/null +++ b/libstdc++-v3/src/c++26/debugging.cc @@ -0,0 +1,175 @@ +// Implementation of <debugging> -*- C++ -*- + +// Copyright The GNU Toolchain Authors. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// <http://www.gnu.org/licenses/>. + +#include <debugging> + +#if __cpp_lib_debugging + +#include <csignal> // std::raise + +#if _GLIBCXX_USE_PROC_SELF_STATUS +# include <fstream> +# include <string> +#endif + +#if _GLIBCXX_HAVE_SYS_PTRACE_H +# include <sys/ptrace.h> +# include <errno.h> +#endif + +#if _GLIBCXX_HAVE_DEBUGAPI_H +# include <debugapi.h> +#endif + +#ifdef _GLIBCXX_HAVE_SYS_SDT_H +# include <sys/sdt.h> +/* We only want to use stap probes starting with v3. Earlier versions + added too much startup cost. */ +# if defined (STAP_PROBE) && _SDT_NOTE_TYPE >= 3 +# define PROBE(name) STAP_PROBE(libstdcxx, name) +# endif +#endif + +#ifndef PROBE +# define PROBE(name) +#endif + +namespace __gnu_cxx +{ + // This should be changed to non-zero by debuggers when they attach + // and back to zero when they detach. + // If the value is positive, std::breakpoint() will use it as the argument + // to std::raise, so it should be a valid signal number, e.g. SIGABRT or + // SIGTRAP. + // If the value is negative, std::breakpoint() will use a target-specific + // trap, e.g. asm("int3") or __builtin_trap(). + volatile int debugger_signal_for_breakpoint = 0; +} + +_GLIBCXX_WEAK_DEFINITION +bool +std::is_debugger_present() noexcept +{ + PROBE(std::is_debugger_present); + + if (__gnu_cxx::debugger_signal_for_breakpoint != 0) + return true; + +#if _GLIBCXX_HOSTED +# if _GLIBCXX_USE_PROC_SELF_STATUS + const string_view prefix = "TracerPid:\t"; // populated since Linux 2.6.0 + ifstream in("/proc/self/status"); + string line; + while (std::getline(in, line)) + { + if (!line.starts_with(prefix)) + continue; + + string_view tracer = line; + tracer.remove_prefix(prefix.size()); + if (tracer.size() == 1 && tracer[0] == '0') [[likely]] + return false; // Not being traced. + + in.close(); + string_view cmd; + string proc_dir = "/proc/" + string(tracer) + '/'; + in.open(proc_dir + "comm"); // since Linux 2.6.33 + if (std::getline(in, line)) [[likely]] + cmd = line; + else + { + in.close(); + in.open(proc_dir + "cmdline"); + if (std::getline(in, line)) + cmd = line.c_str(); // Only up to first '\0' + else + return false; + } + + for (auto i : {"gdb", "gdbserver", "lldb-server"}) // known debuggers + if (cmd.ends_with(i)) + return true; + + // We found the TracerPid line, no need to do any more work. + return false; + } +# elif _GLIBCXX_USE_PTRACE + if (::ptrace(PTRACE_TRACEME, 0, 1, 0) == -1) + return errno == EPERM; +# endif +# if _GLIBCXX_HAVE_DEBUGAPI_H && defined(_WIN32) && !defined(__CYGWIN__) + return IsDebuggerPresent(); +# endif +#endif // HOSTED + return false; +} + +void +std::breakpoint() noexcept +{ + PROBE(std::breakpoint); + + if (__gnu_cxx::debugger_signal_for_breakpoint > 0) + std::raise(__gnu_cxx::debugger_signal_for_breakpoint); + +#if _GLIBCXX_HAVE_DEBUGAPI_H && defined(_WIN32) && !defined(__CYGWIN__) + DebugBreak(); +#elif __has_builtin(__builtin_debugtrap) + __builtin_debugtrap(); // Clang +#elif defined(__i386__) || defined(__x86_64__) + // nop is for GDB, see https://sourceware.org/bugzilla/show_bug.cgi?id=31194 + __asm__ volatile ("int3\n\tnop"); +#elifdef __thumb__ + __asm__ volatile (".inst 0xde01"); +#elifdef __aarch64__ + __asm__ volatile (".inst 0xd4200000"); +#elifdef __arm__ + __asm__ volatile (".inst 0xe7f001f0"); +#elifdef __riscv + /* section 2.8 in the RISC-V unprivileged ISA manual says for semi-hosted + * environments we want the sequence: + * slli x0, x0, 0x1f # Entry NOP + * ebreak # Break to debugger + * srai x0, x0, 7 # NOP encoding the semihosting call number 7 + */ + __asm__ volatile (".4byte 0x00100073"); +#elif defined __powerpc__ && ! defined _AIX + __asm__ volatile(".4byte 0x7d821008"); +#else + __builtin_trap(); +#endif +} // If the debugger stops here, std::breakpoint() was called. + +// This is intentionally not defined inline. A non-inline definition allows +// debuggers to insert a breakpoint on calls to the function, avoiding the +// overhead of calling `is_debugger_present()`. +void +std::breakpoint_if_debugging() noexcept +{ + PROBE(std::breakpoint_if_debugging); + + if (std::is_debugger_present()) [[unlikely]] + std::breakpoint(); +} + +#endif // __cpp_lib_debugging diff --git a/libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint.cc b/libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint.cc new file mode 100644 index 000000000000..ad24177f6440 --- /dev/null +++ b/libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint.cc @@ -0,0 +1,13 @@ +// { dg-do run { target c++26 xfail c++26 } } +// { dg-options "-lstdc++exp" } +// { dg-require-cpp-feature-test __cpp_lib_debugging } +#include <debugging> +#include <type_traits> + +static_assert( noexcept(std::breakpoint()) ); +static_assert( std::is_void_v<decltype(std::breakpoint())> ); + +int main() +{ + std::breakpoint(); +} diff --git a/libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint_if_debugging.cc b/libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint_if_debugging.cc new file mode 100644 index 000000000000..26461836ef77 --- /dev/null +++ b/libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint_if_debugging.cc @@ -0,0 +1,13 @@ +// { dg-do run { target c++26 } } +// { dg-options "-lstdc++exp" } +// { dg-require-cpp-feature-test __cpp_lib_debugging } +#include <debugging> +#include <type_traits> + +static_assert( noexcept(std::breakpoint_if_debugging()) ); +static_assert( std::is_void_v<decltype(std::breakpoint_if_debugging())> ); + +int main() +{ + std::breakpoint_if_debugging(); +} diff --git a/libstdc++-v3/testsuite/19_diagnostics/debugging/is_debugger_present-2.cc b/libstdc++-v3/testsuite/19_diagnostics/debugging/is_debugger_present-2.cc new file mode 100644 index 000000000000..aa4690c55407 --- /dev/null +++ b/libstdc++-v3/testsuite/19_diagnostics/debugging/is_debugger_present-2.cc @@ -0,0 +1,19 @@ +// { dg-do run { target c++26 } } +// { dg-options "-lstdc++exp" } +// { dg-require-cpp-feature-test __cpp_lib_debugging } +// { dg-xfail-run-if "no replaceable functions on AIX" { powerpc-ibm-aix* } } + +// P2810R4 is_debugger_present is_replaceable + +#include <debugging> +#include <testsuite_hooks.h> + +bool called = false; + +bool std::is_debugger_present() noexcept { called = true; return true; } + +int main() +{ + VERIFY( std::is_debugger_present() ); + VERIFY( called ); +} diff --git a/libstdc++-v3/testsuite/19_diagnostics/debugging/is_debugger_present.cc b/libstdc++-v3/testsuite/19_diagnostics/debugging/is_debugger_present.cc new file mode 100644 index 000000000000..8dbfa6942432 --- /dev/null +++ b/libstdc++-v3/testsuite/19_diagnostics/debugging/is_debugger_present.cc @@ -0,0 +1,14 @@ +// { dg-do run { target c++26 } } +// { dg-options "-lstdc++exp" } +// { dg-require-cpp-feature-test __cpp_lib_debugging } +#include <debugging> +#include <type_traits> +#include <testsuite_hooks.h> + +static_assert( noexcept(std::is_debugger_present()) ); +static_assert( std::is_same_v<decltype(std::is_debugger_present()), bool> ); + +int main() +{ + VERIFY( ! std::is_debugger_present() ); +}