Message ID | 20240601102446.878286-1-jwakely@redhat.com |
---|---|
State | New |
Headers | show |
Series | libstdc++: Implement C++26 <debugging> features (P2546R5) | expand |
Hey Jon, I think we should give debuggers a chance to announce themselves by providing an entry point they can call (in the inferior) which sets a flag. A set flag plus a tracer PID would then be a sufficient indicator. The remaining code should also stay but some additional code can be added: On Sat, Jun 1, 2024 at 12:22 PM Jonathan Wakely <jwakely@redhat.com> wrote: > +_GLIBCXX_WEAK_DEFINITION > +bool > +std::is_debugger_present() noexcept > +{ > +#if _GLIBCXX_HOSTED > +# if _GLIBCXX_USE_PROC_SELF_STATUS > + const string_view prefix = "TracerPid:\t"; > + 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. > + Here add something like: if (debugger_announced) return true; > + 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", "lldb"}) // known debuggers > + if (cmd.ends_with(i)) > + return true; > + > + // We found the TracerPid line, no need to do any more work. > + return false; > + } > +# endif And then add namespace { bool debugger_announced = false; } auto debugger_attached() { debugger_announced = true; return std::breakpoint; } I suggest to also return the breakpoint function to allow debuggers to do something clever with it (e.g., set a breakpoint on the entry instead of having to catch the fallout of the instruction that is issued in the function (there might be a difference). With the function any debugger not covered by the existing test can make itself known.
* Jonathan Wakely: > +/** 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; Would it make sense to have a special function symbol for this, on which the debugger sets the breakpoint? We already have this for a slightly different purpose in glibc, see _dl_debug_state. Otherwise you'll get crashes if the debugger detection delivers a false positive (e.g. when running under an strace-equivalent if libstdc++ cannot check the process name). Or is_debugger_present should perhaps read a volatile bool that is updated by the debugger as needed, and not bother with any ptrace checks. Thanks, Florian
On Mon, Jun 3, 2024 at 12:20 PM Florian Weimer <fweimer@redhat.com> wrote: > Would it make sense to have a special function symbol for this, on which > the debugger sets the breakpoint? […] Jon and I discussed more details off-list. Hopefully a more complete version is coming soon-ish.
On 2024-06-01 03:22, Jonathan Wakely wrote: > Here's an implementation of the C++26 <debugging> header. > > We should really have some tests that invoke GDB and check that the > breakpoint works when a debugger is attached. That seems tricky to do > via the main conformance.exp testsuite. It could be done via the > prettyprinters.exp testsuite, which already uses GDB, but would require > some changes to the gdb-test procedure, which assumes it needs to > insert > its own breakpoint at marked spots in the code. > > I think we could add this without those tests, and improve it later. > > -- >8 -- > > It would be good to provide a macOS definition of is_debugger_present, > but that isn't included in this change. > > libstdc++-v3/ChangeLog: > > * config.h.in: Regenerate. > * configure: Regenerate. > * configure.ac: Check for facilities needed by <debugging>. > * include/Makefile.am: Add new header. > * include/Makefile.in: Regenerate. > * include/bits/version.def (debugging): Add. > * include/bits/version.h: Regenerate. > * 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. > --- > libstdc++-v3/config.h.in | 9 ++ > libstdc++-v3/configure | 22 +++ > libstdc++-v3/configure.ac | 9 ++ > libstdc++-v3/include/Makefile.am | 1 + > libstdc++-v3/include/Makefile.in | 1 + > libstdc++-v3/include/bits/version.def | 9 ++ > libstdc++-v3/include/bits/version.h | 10 ++ > libstdc++-v3/include/std/debugging | 82 ++++++++++++ > libstdc++-v3/src/c++26/Makefile.am | 4 +- > libstdc++-v3/src/c++26/Makefile.in | 7 +- > libstdc++-v3/src/c++26/debugging.cc | 126 ++++++++++++++++++ > .../19_diagnostics/debugging/breakpoint.cc | 9 ++ > .../debugging/breakpoint_if_debugging.cc | 9 ++ > 13 files changed, 295 insertions(+), 3 deletions(-) > create mode 100644 libstdc++-v3/include/std/debugging > create mode 100644 libstdc++-v3/src/c++26/debugging.cc > create mode 100644 > libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint.cc > create mode 100644 > libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint_if_debugging.cc > > diff --git a/libstdc++-v3/config.h.in b/libstdc++-v3/config.h.in > index 486ba450749..07203815459 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 > @@ -436,6 +439,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 > > @@ -847,6 +853,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 > > diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure > index 5645e991af7..55ddf36a7f1 100755 > --- a/libstdc++-v3/configure > +++ b/libstdc++-v3/configure > @@ -54612,6 +54612,28 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu > > > > +# For std::is_debugger_present > +case "$target_os" in > + linux*) > + > +$as_echo "#define _GLIBCXX_USE_PROC_SELF_STATUS 1" >>confdefs.h > + > + ;; > +esac > +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_c_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 > + > + > # 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 ccb24a82be7..96b412fb7ae 100644 > --- a/libstdc++-v3/configure.ac > +++ b/libstdc++-v3/configure.ac > @@ -573,6 +573,15 @@ GLIBCXX_CHECK_FILEBUF_NATIVE_HANDLES > # For std::text_encoding > GLIBCXX_CHECK_TEXT_ENCODING > > +# For std::is_debugger_present > +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_CHECK_HEADERS([sys/ptrace.h debugapi.h]) > + > # 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 422a0f4bd0a..94eca0b42f9 100644 > --- a/libstdc++-v3/include/Makefile.am > +++ b/libstdc++-v3/include/Makefile.am > @@ -67,6 +67,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 9fd4ab4848c..299597ef657 100644 > --- a/libstdc++-v3/include/Makefile.in > +++ b/libstdc++-v3/include/Makefile.in > @@ -423,6 +423,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 683b967d54b..ea271901e5d 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -1752,6 +1752,15 @@ ftms = { > }; > }; > > +ftms = { > + name = debugging; > + values = { > + v = 202403; > + cxxmin = 26; > + extra_cond = "_GLIBCXX_USE_PROC_SELF_STATUS || > _GLIBCXX_HAVE_SYS_PTRACE_H || _GLIBCXX_HAVE_DEBUGAPI_H"; > + }; > +}; > + > ftms = { > name = fstream_native_handle; > values = { > diff --git a/libstdc++-v3/include/bits/version.h > b/libstdc++-v3/include/bits/version.h > index 4850041c0a3..50d2d604ce9 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -1953,6 +1953,16 @@ > #endif /* !defined(__cpp_lib_unreachable) && > defined(__glibcxx_want_unreachable) */ > #undef __glibcxx_want_unreachable > > +#if !defined(__cpp_lib_debugging) > +# if (__cplusplus > 202302L) && (_GLIBCXX_USE_PROC_SELF_STATUS || > _GLIBCXX_HAVE_SYS_PTRACE_H || _GLIBCXX_HAVE_DEBUGAPI_H) > +# 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/std/debugging > b/libstdc++-v3/include/std/debugging > new file mode 100644 > index 00000000000..76f81e23e72 > --- /dev/null > +++ b/libstdc++-v3/include/std/debugging > @@ -0,0 +1,82 @@ > +// 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) > +{ > +_GLIBCXX_BEGIN_NAMESPACE_VERSION > + > +/** 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 such as strace. > + * > + * @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 > + */ > +inline void > +breakpoint_if_debugging() noexcept > +{ > + if (std::is_debugger_present()) [[unlikely]] > + breakpoint(); > +} > + > +_GLIBCXX_END_NAMESPACE_VERSION > +} // 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 000ced1f501..551d08d583e 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 77e73b2b265..1c317d6ac7c 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 00000000000..150ac1ed2e7 > --- /dev/null > +++ b/libstdc++-v3/src/c++26/debugging.cc > @@ -0,0 +1,126 @@ > +// 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 > + > +#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 > + > +_GLIBCXX_WEAK_DEFINITION > +bool > +std::is_debugger_present() noexcept > +{ > +#if _GLIBCXX_HOSTED > +# if _GLIBCXX_USE_PROC_SELF_STATUS > + const string_view prefix = "TracerPid:\t"; > + 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", "lldb"}) // known debuggers > + if (cmd.ends_with(i)) > + return true; > + > + // We found the TracerPid line, no need to do any more work. > + return false; > + } > +# endif > +# if _GLIBCXX_HAVE_SYS_PTRACE_H > + 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 > +{ > +#if _GLIBCXX_HAVE_DEBUGAPI_H && defined(_WIN32) && > !defined(__CYGWIN__) > + DebugBreak(); > +#elif __has_builtin(__builtin_debugtrap) > + __builtin_debugtrap(); // Clang > +#elif defined(__i386__) || defined(__x86_64__) > + __asm__ volatile ("int3"); Worth noting, currently gcc has some bugs around its handling of int3, https://sourceware.org/bugzilla/show_bug.cgi?id=31194 int3;nop seems to work around it okay. __builtin_debugtrap() of clang does run into the same issues. > +#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"); > +#elifdef __powerpc__ > + __asm__ volatile(".4byte 0x7d821008"); > +#else > + __builtin_trap(); > +#endif > +} // If the debugger stops here, std::breakpoint() was called. > + > +#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 00000000000..ee572dc9424 > --- /dev/null > +++ b/libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint.cc > @@ -0,0 +1,9 @@ > +// { dg-do run { target c++26 xfail c++26 } } > +// { dg-options "-lstdc++exp" } > +// { dg-require-cpp-feature-test __cpp_lib_debugging } > +#include <debugging> > + > +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 00000000000..02ac245daaf > --- /dev/null > +++ > b/libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint_if_debugging.cc > @@ -0,0 +1,9 @@ > +// { dg-do run { target c++26 } } > +// { dg-options "-lstdc++exp" } > +// { dg-require-cpp-feature-test __cpp_lib_debugging } > +#include <debugging> > + > +int main() > +{ > + std::breakpoint_if_debugging(); > +} Regards, Peter D.
On Mon, 3 Jun 2024 at 14:36, Peter0x44 wrote: > > +void > > +std::breakpoint() noexcept > > +{ > > +#if _GLIBCXX_HAVE_DEBUGAPI_H && defined(_WIN32) && > > !defined(__CYGWIN__) > > + DebugBreak(); > > +#elif __has_builtin(__builtin_debugtrap) > > + __builtin_debugtrap(); // Clang > > +#elif defined(__i386__) || defined(__x86_64__) > > + __asm__ volatile ("int3"); > Worth noting, currently gcc has some bugs around its handling of int3, s/gcc/gdb/ ? > https://sourceware.org/bugzilla/show_bug.cgi?id=31194 > int3;nop seems to work around it okay. __builtin_debugtrap() of clang > does run into the same issues. It seemed to work OK for me, maybe because there's no code after it? void breakpoint() { __asm__ volatile ("int3); }
3 Jun 2024 4:14:28 pm Jonathan Wakely <jwakely@redhat.com>: > On Mon, 3 Jun 2024 at 14:36, Peter0x44 wrote: >>> +void >>> +std::breakpoint() noexcept >>> +{ >>> +#if _GLIBCXX_HAVE_DEBUGAPI_H && defined(_WIN32) && >>> !defined(__CYGWIN__) >>> + DebugBreak(); >>> +#elif __has_builtin(__builtin_debugtrap) >>> + __builtin_debugtrap(); // Clang >>> +#elif defined(__i386__) || defined(__x86_64__) >>> + __asm__ volatile ("int3"); >> Worth noting, currently gcc has some bugs around its handling of int3, > > s/gcc/gdb/ ? Yes, my bad > >> https://sourceware.org/bugzilla/show_bug.cgi?id=31194 >> int3;nop seems to work around it okay. __builtin_debugtrap() of clang >> does run into the same issues. > > It seemed to work OK for me, maybe because there's no code after it? I suspect it won't matter for the tests, the assertion failure is only if you step after hitting the int3. I just figured I'd mention it as a heads up. It would affect users writing code. > > void breakpoint() { > __asm__ volatile ("int3); > }
On Mon, Jun 3, 2024 at 9:06 AM Peter0x44 <peter0x44@disroot.org> wrote: > > 3 Jun 2024 4:14:28 pm Jonathan Wakely <jwakely@redhat.com>: > > > On Mon, 3 Jun 2024 at 14:36, Peter0x44 wrote: > >>> +void > >>> +std::breakpoint() noexcept > >>> +{ > >>> +#if _GLIBCXX_HAVE_DEBUGAPI_H && defined(_WIN32) && > >>> !defined(__CYGWIN__) > >>> + DebugBreak(); > >>> +#elif __has_builtin(__builtin_debugtrap) > >>> + __builtin_debugtrap(); // Clang > >>> +#elif defined(__i386__) || defined(__x86_64__) > >>> + __asm__ volatile ("int3"); > >> Worth noting, currently gcc has some bugs around its handling of int3, > > > > s/gcc/gdb/ ? > Yes, my bad > > > >> https://sourceware.org/bugzilla/show_bug.cgi?id=31194 > >> int3;nop seems to work around it okay. __builtin_debugtrap() of clang > >> does run into the same issues. > > > > It seemed to work OK for me, maybe because there's no code after it? > I suspect it won't matter for the tests, the assertion failure is only if > you step after hitting the int3. I just figured I'd mention it as a heads > up. It would affect users writing code. Note there is a request for adding __builtin_break to GCC; https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84595 . (and one for __builtin_debugtrap; https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99299 ). When __builtin_break is added, I think we should use that here if it is available (as a higher priority than __builtin_debugtrap). Thanks, Andrew > > > > void breakpoint() { > > __asm__ volatile ("int3); > > }
diff --git a/libstdc++-v3/config.h.in b/libstdc++-v3/config.h.in index 486ba450749..07203815459 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 @@ -436,6 +439,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 @@ -847,6 +853,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 diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure index 5645e991af7..55ddf36a7f1 100755 --- a/libstdc++-v3/configure +++ b/libstdc++-v3/configure @@ -54612,6 +54612,28 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu +# For std::is_debugger_present +case "$target_os" in + linux*) + +$as_echo "#define _GLIBCXX_USE_PROC_SELF_STATUS 1" >>confdefs.h + + ;; +esac +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_c_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 + + # 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 ccb24a82be7..96b412fb7ae 100644 --- a/libstdc++-v3/configure.ac +++ b/libstdc++-v3/configure.ac @@ -573,6 +573,15 @@ GLIBCXX_CHECK_FILEBUF_NATIVE_HANDLES # For std::text_encoding GLIBCXX_CHECK_TEXT_ENCODING +# For std::is_debugger_present +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_CHECK_HEADERS([sys/ptrace.h debugapi.h]) + # 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 422a0f4bd0a..94eca0b42f9 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -67,6 +67,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 9fd4ab4848c..299597ef657 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -423,6 +423,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 683b967d54b..ea271901e5d 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1752,6 +1752,15 @@ ftms = { }; }; +ftms = { + name = debugging; + values = { + v = 202403; + cxxmin = 26; + extra_cond = "_GLIBCXX_USE_PROC_SELF_STATUS || _GLIBCXX_HAVE_SYS_PTRACE_H || _GLIBCXX_HAVE_DEBUGAPI_H"; + }; +}; + ftms = { name = fstream_native_handle; values = { diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 4850041c0a3..50d2d604ce9 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1953,6 +1953,16 @@ #endif /* !defined(__cpp_lib_unreachable) && defined(__glibcxx_want_unreachable) */ #undef __glibcxx_want_unreachable +#if !defined(__cpp_lib_debugging) +# if (__cplusplus > 202302L) && (_GLIBCXX_USE_PROC_SELF_STATUS || _GLIBCXX_HAVE_SYS_PTRACE_H || _GLIBCXX_HAVE_DEBUGAPI_H) +# 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/std/debugging b/libstdc++-v3/include/std/debugging new file mode 100644 index 00000000000..76f81e23e72 --- /dev/null +++ b/libstdc++-v3/include/std/debugging @@ -0,0 +1,82 @@ +// 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) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + +/** 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 such as strace. + * + * @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 + */ +inline void +breakpoint_if_debugging() noexcept +{ + if (std::is_debugger_present()) [[unlikely]] + breakpoint(); +} + +_GLIBCXX_END_NAMESPACE_VERSION +} // 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 000ced1f501..551d08d583e 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 77e73b2b265..1c317d6ac7c 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 00000000000..150ac1ed2e7 --- /dev/null +++ b/libstdc++-v3/src/c++26/debugging.cc @@ -0,0 +1,126 @@ +// 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 + +#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 + +_GLIBCXX_WEAK_DEFINITION +bool +std::is_debugger_present() noexcept +{ +#if _GLIBCXX_HOSTED +# if _GLIBCXX_USE_PROC_SELF_STATUS + const string_view prefix = "TracerPid:\t"; + 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", "lldb"}) // known debuggers + if (cmd.ends_with(i)) + return true; + + // We found the TracerPid line, no need to do any more work. + return false; + } +# endif +# if _GLIBCXX_HAVE_SYS_PTRACE_H + 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 +{ +#if _GLIBCXX_HAVE_DEBUGAPI_H && defined(_WIN32) && !defined(__CYGWIN__) + DebugBreak(); +#elif __has_builtin(__builtin_debugtrap) + __builtin_debugtrap(); // Clang +#elif defined(__i386__) || defined(__x86_64__) + __asm__ volatile ("int3"); +#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"); +#elifdef __powerpc__ + __asm__ volatile(".4byte 0x7d821008"); +#else + __builtin_trap(); +#endif +} // If the debugger stops here, std::breakpoint() was called. + +#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 00000000000..ee572dc9424 --- /dev/null +++ b/libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint.cc @@ -0,0 +1,9 @@ +// { dg-do run { target c++26 xfail c++26 } } +// { dg-options "-lstdc++exp" } +// { dg-require-cpp-feature-test __cpp_lib_debugging } +#include <debugging> + +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 00000000000..02ac245daaf --- /dev/null +++ b/libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint_if_debugging.cc @@ -0,0 +1,9 @@ +// { dg-do run { target c++26 } } +// { dg-options "-lstdc++exp" } +// { dg-require-cpp-feature-test __cpp_lib_debugging } +#include <debugging> + +int main() +{ + std::breakpoint_if_debugging(); +}