[PATCH] libstdc++: Implement C++26 <debugging> features (P2546R5)

Peter0x44 peter0x44@disroot.org
Mon Jun 3 13:34:56 GMT 2024


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.


More information about the Libstdc++ mailing list