aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2025-04-10 17:50:59 +0100
committerJonathan Wakely <redi@gcc.gnu.org>2025-08-28 17:47:00 +0100
commit3c95766e92d6782eb945593c043a16be096707af (patch)
tree29c852170cb6d60b739b1e0ef6729a0124aead43
parent00e8690ef0d7ac1ced87ceee309dbfa27909a35c (diff)
downloadgcc-3c95766e92d6782eb945593c043a16be096707af.zip
gcc-3c95766e92d6782eb945593c043a16be096707af.tar.gz
gcc-3c95766e92d6782eb945593c043a16be096707af.tar.bz2
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 <tkaminsk@redhat.com>
-rw-r--r--libstdc++-v3/acinclude.m436
-rw-r--r--libstdc++-v3/config.h.in12
-rwxr-xr-xlibstdc++-v3/configure73
-rw-r--r--libstdc++-v3/configure.ac3
-rw-r--r--libstdc++-v3/include/Makefile.am1
-rw-r--r--libstdc++-v3/include/Makefile.in1
-rw-r--r--libstdc++-v3/include/bits/version.def8
-rw-r--r--libstdc++-v3/include/bits/version.h10
-rw-r--r--libstdc++-v3/include/precompiled/stdc++.h1
-rw-r--r--libstdc++-v3/include/std/debugging77
-rw-r--r--libstdc++-v3/src/c++26/Makefile.am4
-rw-r--r--libstdc++-v3/src/c++26/Makefile.in7
-rw-r--r--libstdc++-v3/src/c++26/debugging.cc175
-rw-r--r--libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint.cc13
-rw-r--r--libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint_if_debugging.cc13
-rw-r--r--libstdc++-v3/testsuite/19_diagnostics/debugging/is_debugger_present-2.cc19
-rw-r--r--libstdc++-v3/testsuite/19_diagnostics/debugging/is_debugger_present.cc14
17 files changed, 464 insertions, 3 deletions
diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4
index 027735e..eb2d262 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 ffacdab..818117a 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 d57cfd5..c0eaeb9 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 a6c01b2..0bf2191 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 6f248fe..6f59469 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 014466f..4b5917e 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 9d5122f..84c755d 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1907,6 +1907,14 @@ ftms = {
};
ftms = {
+ name = debugging;
+ values = {
+ v = 202403;
+ cxxmin = 26;
+ };
+};
+
+ftms = {
name = fstream_native_handle;
values = {
v = 202306;
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index 6feeaa4..410e320 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 733a5e5..636632a 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 0000000..4cf7e4a
--- /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 5defa4a..4123b7d 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 77e73b2..1c317d6 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 0000000..72e305d
--- /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 0000000..ad24177
--- /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 0000000..2646183
--- /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 0000000..aa4690c
--- /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 0000000..8dbfa69
--- /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() );
+}