aboutsummaryrefslogtreecommitdiff
path: root/gdbsupport
diff options
context:
space:
mode:
Diffstat (limited to 'gdbsupport')
-rw-r--r--gdbsupport/Makefile.am1
-rw-r--r--gdbsupport/Makefile.in14
-rw-r--r--gdbsupport/common-defs.h15
-rw-r--r--gdbsupport/common-types.h7
-rw-r--r--gdbsupport/common-utils.cc21
-rw-r--r--gdbsupport/common-utils.h10
-rw-r--r--gdbsupport/common.m41
-rwxr-xr-xgdbsupport/configure112
-rw-r--r--gdbsupport/cxx-thread.h243
-rw-r--r--gdbsupport/event-loop.cc3
-rw-r--r--gdbsupport/filestuff.cc8
-rw-r--r--gdbsupport/filtered-iterator.h39
-rw-r--r--gdbsupport/format.cc91
-rw-r--r--gdbsupport/format.h27
-rw-r--r--gdbsupport/gdb-safe-ctype.h49
-rw-r--r--gdbsupport/gdb_argv_vec.h15
-rw-r--r--gdbsupport/iterator-range.h10
-rw-r--r--gdbsupport/parallel-for.h286
-rw-r--r--gdbsupport/pathstuff.cc17
-rw-r--r--gdbsupport/poison.h2
-rw-r--r--gdbsupport/print-utils.cc18
-rw-r--r--gdbsupport/print-utils.h30
-rw-r--r--gdbsupport/reference-to-pointer-iterator.h5
-rw-r--r--gdbsupport/remote-args.cc43
-rw-r--r--gdbsupport/remote-args.h60
-rw-r--r--gdbsupport/run-time-clock.cc43
-rw-r--r--gdbsupport/run-time-clock.h33
-rw-r--r--gdbsupport/safe-iterator.h28
-rw-r--r--gdbsupport/thread-pool.h91
-rw-r--r--gdbsupport/work-queue.h96
-rw-r--r--gdbsupport/x86-xstate.h7
31 files changed, 904 insertions, 521 deletions
diff --git a/gdbsupport/Makefile.am b/gdbsupport/Makefile.am
index 79aef95..20122e4 100644
--- a/gdbsupport/Makefile.am
+++ b/gdbsupport/Makefile.am
@@ -80,6 +80,7 @@ libgdbsupport_a_SOURCES = \
pathstuff.cc \
print-utils.cc \
ptid.cc \
+ remote-args.cc \
rsp-low.cc \
run-time-clock.cc \
safe-strerror.cc \
diff --git a/gdbsupport/Makefile.in b/gdbsupport/Makefile.in
index c2feacc..66b8891 100644
--- a/gdbsupport/Makefile.in
+++ b/gdbsupport/Makefile.in
@@ -163,12 +163,12 @@ am_libgdbsupport_a_OBJECTS = agent.$(OBJEXT) btrace-common.$(OBJEXT) \
gdb_tilde_expand.$(OBJEXT) gdb_wait.$(OBJEXT) \
gdb_vecs.$(OBJEXT) job-control.$(OBJEXT) netstuff.$(OBJEXT) \
new-op.$(OBJEXT) osabi.$(OBJEXT) pathstuff.$(OBJEXT) \
- print-utils.$(OBJEXT) ptid.$(OBJEXT) rsp-low.$(OBJEXT) \
- run-time-clock.$(OBJEXT) safe-strerror.$(OBJEXT) \
- scoped_mmap.$(OBJEXT) search.$(OBJEXT) signals.$(OBJEXT) \
- signals-state-save-restore.$(OBJEXT) task-group.$(OBJEXT) \
- tdesc.$(OBJEXT) thread-pool.$(OBJEXT) xml-utils.$(OBJEXT) \
- $(am__objects_1) $(am__objects_2)
+ print-utils.$(OBJEXT) ptid.$(OBJEXT) remote-args.$(OBJEXT) \
+ rsp-low.$(OBJEXT) run-time-clock.$(OBJEXT) \
+ safe-strerror.$(OBJEXT) scoped_mmap.$(OBJEXT) search.$(OBJEXT) \
+ signals.$(OBJEXT) signals-state-save-restore.$(OBJEXT) \
+ task-group.$(OBJEXT) tdesc.$(OBJEXT) thread-pool.$(OBJEXT) \
+ xml-utils.$(OBJEXT) $(am__objects_1) $(am__objects_2)
libgdbsupport_a_OBJECTS = $(am_libgdbsupport_a_OBJECTS)
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
@@ -438,6 +438,7 @@ libgdbsupport_a_SOURCES = \
pathstuff.cc \
print-utils.cc \
ptid.cc \
+ remote-args.cc \
rsp-low.cc \
run-time-clock.cc \
safe-strerror.cc \
@@ -548,6 +549,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pathstuff.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print-utils.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ptid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remote-args.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rsp-low.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run-time-clock.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/safe-strerror.Po@am__quote@
diff --git a/gdbsupport/common-defs.h b/gdbsupport/common-defs.h
index 07caf3b..6f58914 100644
--- a/gdbsupport/common-defs.h
+++ b/gdbsupport/common-defs.h
@@ -27,6 +27,14 @@
#pragma GCC optimize("-fno-hoist-adjacent-loads")
#endif
+#if defined (__GNUC__) && !defined (__clang__) \
+ && ((__GNUC__ >= 12 && __GNUC__ <= 15) \
+ || (__GNUC__ == 16 && __GNUC_MINOR__ < 1))
+/* Work around PR gcc/120987 starting gcc 12, and assume it will be fixed in
+ the gcc 16.1 release. */
+#pragma GCC optimize("-fno-ipa-modref")
+#endif
+
#include <gdbsupport/config.h>
#undef PACKAGE_NAME
@@ -208,13 +216,18 @@
#include "errors.h"
#include "print-utils.h"
#include "common-debug.h"
-#include "cleanups.h"
#include "common-exceptions.h"
#include "gdbsupport/poison.h"
/* Pull in gdb::unique_xmalloc_ptr. */
#include "gdbsupport/gdb_unique_ptr.h"
+/* Note that there's no simple way to enforce the use of the c-ctype
+ functions. We can't poison the <ctype.h> functions (see
+ safe-ctype.h) because that will provoke errors from libstdc++
+ headers. */
+#include "c-ctype.h"
+
/* sbrk on macOS is not useful for our purposes, since sbrk(0) always
returns the same value. brk/sbrk on macOS is just an emulation
that always returns a pointer to a 4MB section reserved for
diff --git a/gdbsupport/common-types.h b/gdbsupport/common-types.h
index 210e09b..d849ea8 100644
--- a/gdbsupport/common-types.h
+++ b/gdbsupport/common-types.h
@@ -21,6 +21,7 @@
#define GDBSUPPORT_COMMON_TYPES_H
#include <inttypes.h>
+#include "gdbsupport/offset-type.h"
/* * A byte from the program being debugged. */
typedef unsigned char gdb_byte;
@@ -29,10 +30,8 @@ typedef unsigned char gdb_byte;
typedef uint64_t CORE_ADDR;
/* Like a CORE_ADDR, but not directly convertible. This is used to
- represent an unrelocated CORE_ADDR. DEFINE_OFFSET_TYPE is not used
- here because there's no need to add or subtract values of this
- type. */
-enum class unrelocated_addr : CORE_ADDR { };
+ represent an unrelocated CORE_ADDR. */
+DEFINE_OFFSET_TYPE (unrelocated_addr, CORE_ADDR);
/* LONGEST must be at least as big as CORE_ADDR. */
diff --git a/gdbsupport/common-utils.cc b/gdbsupport/common-utils.cc
index 266d836..5c7ba31 100644
--- a/gdbsupport/common-utils.cc
+++ b/gdbsupport/common-utils.cc
@@ -19,7 +19,6 @@
#include "common-utils.h"
#include "host-defs.h"
-#include "gdbsupport/gdb-safe-ctype.h"
#include "gdbsupport/gdb-xfree.h"
void *
@@ -180,7 +179,7 @@ extract_string_maybe_quoted (const char **arg)
/* Parse p similarly to gdb_argv buildargv function. */
while (*p != '\0')
{
- if (ISSPACE (*p) && !squote && !dquote && !bsquote)
+ if (c_isspace (*p) && !squote && !dquote && !bsquote)
break;
else
{
@@ -254,21 +253,21 @@ make_quoted_string (const char *str)
static int
is_digit_in_base (unsigned char digit, int base)
{
- if (!ISALNUM (digit))
+ if (!c_isalnum (digit))
return 0;
if (base <= 10)
- return (ISDIGIT (digit) && digit < base + '0');
+ return (c_isdigit (digit) && digit < base + '0');
else
- return (ISDIGIT (digit) || TOLOWER (digit) < base - 10 + 'a');
+ return (c_isdigit (digit) || c_tolower (digit) < base - 10 + 'a');
}
static int
digit_to_int (unsigned char c)
{
- if (ISDIGIT (c))
+ if (c_isdigit (c))
return c - '0';
else
- return TOLOWER (c) - 'a' + 10;
+ return c_tolower (c) - 'a' + 10;
}
/* As for strtoul, but for ULONGEST results. */
@@ -282,7 +281,7 @@ strtoulst (const char *num, const char **trailer, int base)
int i = 0;
/* Skip leading whitespace. */
- while (ISSPACE (num[i]))
+ while (c_isspace (num[i]))
i++;
/* Handle prefixes. */
@@ -349,7 +348,7 @@ skip_spaces (char *chp)
{
if (chp == NULL)
return NULL;
- while (*chp && ISSPACE (*chp))
+ while (*chp && c_isspace (*chp))
chp++;
return chp;
}
@@ -361,7 +360,7 @@ skip_spaces (const char *chp)
{
if (chp == NULL)
return NULL;
- while (*chp && ISSPACE (*chp))
+ while (*chp && c_isspace (*chp))
chp++;
return chp;
}
@@ -373,7 +372,7 @@ skip_to_space (const char *chp)
{
if (chp == NULL)
return NULL;
- while (*chp && !ISSPACE (*chp))
+ while (*chp && !c_isspace (*chp))
chp++;
return chp;
}
diff --git a/gdbsupport/common-utils.h b/gdbsupport/common-utils.h
index a168458..10bf9f4 100644
--- a/gdbsupport/common-utils.h
+++ b/gdbsupport/common-utils.h
@@ -196,6 +196,16 @@ in_inclusive_range (T value, T low, T high)
extern ULONGEST align_up (ULONGEST v, int n);
extern ULONGEST align_down (ULONGEST v, int n);
+/* Sign-extend the value V, using N as the number of valid bits. That
+ is, bit N-1 is the sign bit. The higher-order bits (those outside
+ 0..N-1) must be zero. */
+static inline ULONGEST
+sign_extend (ULONGEST v, int n)
+{
+ ULONGEST mask = (ULONGEST) 1 << (n - 1);
+ return (v ^ mask) - mask;
+}
+
/* Convert hex digit A to a number, or throw an exception. */
extern int fromhex (int a);
diff --git a/gdbsupport/common.m4 b/gdbsupport/common.m4
index f265af9..cde8bd6 100644
--- a/gdbsupport/common.m4
+++ b/gdbsupport/common.m4
@@ -21,7 +21,6 @@ AC_DEFUN([GDB_AC_COMMON], [
# Set the 'development' global.
. $srcdir/../bfd/development.sh
- AC_HEADER_STDC
AC_FUNC_ALLOCA
WIN32APILIBS=
diff --git a/gdbsupport/configure b/gdbsupport/configure
index bcfae34..133ddfa 100755
--- a/gdbsupport/configure
+++ b/gdbsupport/configure
@@ -10704,118 +10704,6 @@ $as_echo "$ac_cv_path_SED" >&6; }
# Set the 'development' global.
. $srcdir/../bfd/development.sh
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
-$as_echo_n "checking for ANSI C header files... " >&6; }
-if ${ac_cv_header_stdc+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <float.h>
-
-int
-main ()
-{
-
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- ac_cv_header_stdc=yes
-else
- ac_cv_header_stdc=no
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-
-if test $ac_cv_header_stdc = yes; then
- # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <string.h>
-
-_ACEOF
-if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
- $EGREP "memchr" >/dev/null 2>&1; then :
-
-else
- ac_cv_header_stdc=no
-fi
-rm -f conftest*
-
-fi
-
-if test $ac_cv_header_stdc = yes; then
- # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <stdlib.h>
-
-_ACEOF
-if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
- $EGREP "free" >/dev/null 2>&1; then :
-
-else
- ac_cv_header_stdc=no
-fi
-rm -f conftest*
-
-fi
-
-if test $ac_cv_header_stdc = yes; then
- # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
- if test "$cross_compiling" = yes; then :
- :
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <ctype.h>
-#include <stdlib.h>
-#if ((' ' & 0x0FF) == 0x020)
-# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
-# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
-#else
-# define ISLOWER(c) \
- (('a' <= (c) && (c) <= 'i') \
- || ('j' <= (c) && (c) <= 'r') \
- || ('s' <= (c) && (c) <= 'z'))
-# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
-#endif
-
-#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
-int
-main ()
-{
- int i;
- for (i = 0; i < 256; i++)
- if (XOR (islower (i), ISLOWER (i))
- || toupper (i) != TOUPPER (i))
- return 2;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
-
-else
- ac_cv_header_stdc=no
-fi
-rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
- conftest.$ac_objext conftest.beam conftest.$ac_ext
-fi
-
-fi
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
-$as_echo "$ac_cv_header_stdc" >&6; }
-if test $ac_cv_header_stdc = yes; then
-
-$as_echo "#define STDC_HEADERS 1" >>confdefs.h
-
-fi
-
# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works
# for constant arguments. Useless!
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working alloca.h" >&5
diff --git a/gdbsupport/cxx-thread.h b/gdbsupport/cxx-thread.h
new file mode 100644
index 0000000..e4061eb
--- /dev/null
+++ b/gdbsupport/cxx-thread.h
@@ -0,0 +1,243 @@
+/* Wrappers for C++ threading
+
+ Copyright (C) 2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program 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 of the License, or
+ (at your option) any later version.
+
+ This program 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef GDBSUPPORT_CXX_THREAD_H
+#define GDBSUPPORT_CXX_THREAD_H
+
+/* This header implements shims for the parts of the C++ threading
+ library that are needed by gdb.
+
+ The reason this exists is that some versions of libstdc++ do not
+ supply a working C++ thread implementation. In particular this was
+ true for several versions of the Windows compiler. See
+ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93687.
+
+ For systems where this works, this header just supplies aliases of
+ the standard functionality, in the "gdb" namespace. For example,
+ "gdb::mutex" is an alias for "std::mutex".
+
+ For non-working ports, shims are provided. These are just the
+ subset needed by gdb, and they generally do nothing, or as little
+ as possible. In particular they all simply assume single-threaded
+ operation. */
+
+#if CXX_STD_THREAD
+
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <future>
+
+namespace gdb
+{
+
+using condition_variable = std::condition_variable;
+using cv_status = std::cv_status;
+using future_status = std::future_status;
+using mutex = std::mutex;
+using recursive_mutex = std::recursive_mutex;
+using thread = std::thread;
+
+namespace this_thread = std::this_thread;
+
+template<typename T>
+using lock_guard = std::lock_guard<T>;
+
+template<typename T>
+using unique_lock = std::unique_lock<T>;
+
+template<typename T>
+using future = std::future<T>;
+
+} /* namespace gdb*/
+
+#else
+
+#include <chrono>
+
+namespace gdb
+{
+
+/* A do-nothing replacement for std::mutex. */
+struct mutex
+{
+ mutex () = default;
+
+ DISABLE_COPY_AND_ASSIGN (mutex);
+
+ void lock ()
+ {
+ }
+
+ void unlock ()
+ {
+ }
+};
+
+/* A do-nothing replacement for std::recursive_mutex. */
+struct recursive_mutex
+{
+ recursive_mutex () = default;
+
+ DISABLE_COPY_AND_ASSIGN (recursive_mutex);
+
+ void lock ()
+ {
+ }
+
+ void unlock ()
+ {
+ }
+};
+
+/* A do-nothing replacement for std::lock_guard. */
+template<typename T>
+struct lock_guard
+{
+ explicit lock_guard (T &m)
+ {
+ }
+
+ DISABLE_COPY_AND_ASSIGN (lock_guard);
+};
+
+/* A do-nothing replacement for std::unique_lock. */
+template<typename T>
+struct unique_lock
+{
+ explicit unique_lock (T &m)
+ {
+ }
+
+ DISABLE_COPY_AND_ASSIGN (unique_lock);
+};
+
+/* A compatibility enum for std::cv_status. */
+enum class cv_status
+{
+ no_timeout,
+ timeout,
+};
+
+/* A do-nothing replacement for std::condition_variable. */
+struct condition_variable
+{
+ condition_variable () = default;
+
+ DISABLE_COPY_AND_ASSIGN (condition_variable);
+
+ void notify_one () noexcept
+ {
+ }
+
+ void wait (unique_lock<mutex> &lock)
+ {
+ }
+
+ template<class Rep, class Period>
+ cv_status wait_for (unique_lock<mutex> &lock,
+ const std::chrono::duration<Rep, Period> &rel_time)
+ {
+ return cv_status::no_timeout;
+ }
+};
+
+/* A compatibility enum for std::future_status. This is just the
+ subset needed by gdb. */
+enum class future_status
+{
+ ready,
+ timeout,
+};
+
+/* A compatibility implementation of std::future. */
+template<typename T>
+class future
+{
+public:
+
+ explicit future (T value)
+ : m_value (std::move (value))
+ {
+ }
+
+ future () = default;
+ future (future &&other) = default;
+ future (const future &other) = delete;
+ future &operator= (future &&other) = default;
+ future &operator= (const future &other) = delete;
+
+ void wait () const { }
+
+ template<class Rep, class Period>
+ future_status wait_for (const std::chrono::duration<Rep,Period> &duration)
+ const
+ {
+ return future_status::ready;
+ }
+
+ T get () { return std::move (m_value); }
+
+private:
+
+ T m_value;
+};
+
+/* A specialization for void. */
+
+template<>
+class future<void>
+{
+public:
+ void wait () const { }
+
+ template<class Rep, class Period>
+ future_status wait_for (const std::chrono::duration<Rep,Period> &duration)
+ const
+ {
+ return future_status::ready;
+ }
+
+ void get () { }
+};
+
+/* Rather than try to write a gdb::thread class, we just use a
+ namespace since only the 'id' type is needed. Code manipulating
+ actual std::thread objects has to be wrapped in a check anyway. */
+namespace thread
+{
+/* Replacement for std::thread::id. */
+using id = int;
+}
+
+/* Replacement for std::this_thread. */
+namespace this_thread
+{
+static inline thread::id
+get_id ()
+{
+ return 0;
+}
+}
+
+} /* namespace gdb */
+
+#endif /* CXX_STD_THREAD */
+
+#endif /* GDBSUPPORT_CXX_THREAD_H */
diff --git a/gdbsupport/event-loop.cc b/gdbsupport/event-loop.cc
index c080490..e7b21e7 100644
--- a/gdbsupport/event-loop.cc
+++ b/gdbsupport/event-loop.cc
@@ -827,7 +827,8 @@ update_wait_timeout (void)
/* Update the timeout for select/ poll. */
#ifdef HAVE_POLL
if (use_poll)
- gdb_notifier.poll_timeout = timeout.tv_sec * 1000;
+ gdb_notifier.poll_timeout = (timeout.tv_sec * 1000 +
+ (timeout.tv_usec + 1000 - 1) / 1000);
else
#endif /* HAVE_POLL */
{
diff --git a/gdbsupport/filestuff.cc b/gdbsupport/filestuff.cc
index 5c1817e..817663b 100644
--- a/gdbsupport/filestuff.cc
+++ b/gdbsupport/filestuff.cc
@@ -333,14 +333,10 @@ gdb_fopen_cloexec (const char *filename, const char *opentype)
if (!fopen_e_ever_failed_einval)
{
- char *copy;
-
- copy = (char *) alloca (strlen (opentype) + 2);
- strcpy (copy, opentype);
/* This is a glibc extension but we try it unconditionally on
this path. */
- strcat (copy, "e");
- result = fopen (filename, copy);
+ auto opentype_e = std::string (opentype) + 'e';
+ result = fopen (filename, opentype_e.c_str ());
if (result == NULL && errno == EINVAL)
{
diff --git a/gdbsupport/filtered-iterator.h b/gdbsupport/filtered-iterator.h
index e824d61..872bdeb 100644
--- a/gdbsupport/filtered-iterator.h
+++ b/gdbsupport/filtered-iterator.h
@@ -19,8 +19,6 @@
#ifndef GDBSUPPORT_FILTERED_ITERATOR_H
#define GDBSUPPORT_FILTERED_ITERATOR_H
-#include <type_traits>
-
/* A filtered iterator. This wraps BaseIterator and automatically
skips elements that FilterFunc filters out. Requires that
default-constructing a BaseIterator creates a valid one-past-end
@@ -30,21 +28,28 @@ template<typename BaseIterator, typename FilterFunc>
class filtered_iterator
{
public:
- typedef filtered_iterator self_type;
- typedef typename BaseIterator::value_type value_type;
- typedef typename BaseIterator::reference reference;
- typedef typename BaseIterator::pointer pointer;
- typedef typename BaseIterator::iterator_category iterator_category;
- typedef typename BaseIterator::difference_type difference_type;
-
- /* Construct by forwarding all arguments to the underlying
- iterator. */
- template<typename... Args>
- explicit filtered_iterator (Args &&...args)
- : m_it (std::forward<Args> (args)...)
+ using self_type = filtered_iterator;
+ using value_type = typename std::iterator_traits<BaseIterator>::value_type;
+ using reference = typename std::iterator_traits<BaseIterator>::reference;
+ using pointer = typename std::iterator_traits<BaseIterator>::pointer;
+ using iterator_category
+ = typename std::iterator_traits<BaseIterator>::iterator_category;
+ using difference_type
+ = typename std::iterator_traits<BaseIterator>::difference_type;
+
+ /* Construct by providing the begin underlying iterators. The end iterator
+ is default-constructed. */
+ filtered_iterator (BaseIterator begin)
+ : filtered_iterator (std::move (begin), BaseIterator {})
+ {}
+
+ /* Construct by providing begin and end underlying iterators. */
+ filtered_iterator (BaseIterator begin, BaseIterator end)
+ : m_it (std::move (begin)), m_end (std::move (end))
{ skip_filtered (); }
- /* Create a one-past-end iterator. */
+ /* Create a one-past-end iterator. The underlying end iterator is obtained
+ by default-constructing. */
filtered_iterator () = default;
/* Need these as the variadic constructor would be a better match
@@ -56,9 +61,7 @@ public:
: filtered_iterator (static_cast<const filtered_iterator &> (other))
{}
- typename std::invoke_result<decltype(&BaseIterator::operator*),
- BaseIterator>::type
- operator* () const
+ decltype(auto) operator* () const
{ return *m_it; }
self_type &operator++ ()
diff --git a/gdbsupport/format.cc b/gdbsupport/format.cc
index be3d821..145c876 100644
--- a/gdbsupport/format.cc
+++ b/gdbsupport/format.cc
@@ -22,14 +22,11 @@
format_pieces::format_pieces (const char **arg, bool gdb_extensions,
bool value_extension)
{
- const char *s;
+ const char *s = *arg;
const char *string;
- const char *prev_start;
- const char *percent_loc;
- char *sub_start, *current_substring;
- enum argclass this_argclass;
- s = *arg;
+ /* Buffer to hold the escaped-processed version of the string. */
+ std::string de_escaped;
if (gdb_extensions)
{
@@ -40,10 +37,6 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions,
{
/* Parse the format-control string and copy it into the string STRING,
processing some kinds of escape sequence. */
-
- char *f = (char *) alloca (strlen (s) + 1);
- string = f;
-
while (*s != '"' && *s != '\0')
{
int c = *s++;
@@ -56,34 +49,34 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions,
switch (c = *s++)
{
case '\\':
- *f++ = '\\';
+ de_escaped += '\\';
break;
case 'a':
- *f++ = '\a';
+ de_escaped += '\a';
break;
case 'b':
- *f++ = '\b';
+ de_escaped += '\b';
break;
case 'e':
- *f++ = '\e';
+ de_escaped += '\e';
break;
case 'f':
- *f++ = '\f';
+ de_escaped += '\f';
break;
case 'n':
- *f++ = '\n';
+ de_escaped += '\n';
break;
case 'r':
- *f++ = '\r';
+ de_escaped += '\r';
break;
case 't':
- *f++ = '\t';
+ de_escaped += '\t';
break;
case 'v':
- *f++ = '\v';
+ de_escaped += '\v';
break;
case '"':
- *f++ = '"';
+ de_escaped += '"';
break;
default:
/* ??? TODO: handle other escape sequences. */
@@ -93,29 +86,23 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions,
break;
default:
- *f++ = c;
+ de_escaped += c;
}
}
- /* Terminate our escape-processed copy. */
- *f++ = '\0';
+ string = de_escaped.c_str ();
/* Whether the format string ended with double-quote or zero, we're
done with it; it's up to callers to complain about syntax. */
*arg = s;
}
- /* Need extra space for the '\0's. Doubling the size is sufficient. */
-
- current_substring = (char *) xmalloc (strlen (string) * 2 + 1000);
- m_storage.reset (current_substring);
-
/* Now scan the string for %-specs and see what kinds of args they want.
argclass classifies the %-specs so we can give printf-type functions
something of the right size. */
-
const char *f = string;
- prev_start = string;
+ const char *prev_start = string;
+
while (*f)
if (*f++ == '%')
{
@@ -135,16 +122,15 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions,
continue;
}
- sub_start = current_substring;
+ std::string::size_type sub_start = m_storage.size ();
- strncpy (current_substring, prev_start, f - 1 - prev_start);
- current_substring += f - 1 - prev_start;
- *current_substring++ = '\0';
+ m_storage.append (prev_start, f - 1 - prev_start);
+ m_storage += '\0';
- if (*sub_start != '\0')
+ if (m_storage[sub_start] != '\0')
m_pieces.emplace_back (sub_start, literal_piece, 0);
- percent_loc = f - 1;
+ const char *percent_loc = f - 1;
/* Check the validity of the format specifier, and work
out what argument it expects. We only accept C89
@@ -251,6 +237,8 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions,
break;
}
+ argclass this_argclass;
+
switch (*f)
{
case 'u':
@@ -381,7 +369,7 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions,
f++;
- sub_start = current_substring;
+ sub_start = m_storage.size ();
if (lcount > 1 && !seen_i64 && USE_PRINTF_I64)
{
@@ -389,11 +377,9 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions,
Convert %lld to %I64d. */
int length_before_ll = f - percent_loc - 1 - lcount;
- strncpy (current_substring, percent_loc, length_before_ll);
- strcpy (current_substring + length_before_ll, "I64");
- current_substring[length_before_ll + 3] =
- percent_loc[length_before_ll + lcount];
- current_substring += length_before_ll + 4;
+ m_storage.append (percent_loc, length_before_ll);
+ m_storage += "I64";
+ m_storage += percent_loc[length_before_ll + lcount];
}
else if (this_argclass == wide_string_arg
|| this_argclass == wide_char_arg)
@@ -401,18 +387,13 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions,
/* Convert %ls or %lc to %s. */
int length_before_ls = f - percent_loc - 2;
- strncpy (current_substring, percent_loc, length_before_ls);
- strcpy (current_substring + length_before_ls, "s");
- current_substring += length_before_ls + 2;
+ m_storage.append (percent_loc, length_before_ls);
+ m_storage += "s";
}
else
- {
- strncpy (current_substring, percent_loc, f - percent_loc);
- current_substring += f - percent_loc;
- }
-
- *current_substring++ = '\0';
+ m_storage.append (percent_loc, f - percent_loc);
+ m_storage += '\0';
prev_start = f;
m_pieces.emplace_back (sub_start, this_argclass, n_int_args);
@@ -422,11 +403,9 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions,
if (f > prev_start)
{
- sub_start = current_substring;
-
- strncpy (current_substring, prev_start, f - prev_start);
- current_substring += f - prev_start;
- *current_substring++ = '\0';
+ std::string::size_type sub_start = m_storage.size ();
+ m_storage.append (prev_start, f - prev_start);
+ /* No need for a final '\0', std::string already has one. */
m_pieces.emplace_back (sub_start, literal_piece, 0);
}
diff --git a/gdbsupport/format.h b/gdbsupport/format.h
index 118b947..46dae22 100644
--- a/gdbsupport/format.h
+++ b/gdbsupport/format.h
@@ -20,8 +20,6 @@
#ifndef GDBSUPPORT_FORMAT_H
#define GDBSUPPORT_FORMAT_H
-#include <string_view>
-
#if defined(__MINGW32__) && !defined(PRINTF_HAS_LONG_LONG)
# define USE_PRINTF_I64 1
# define PRINTF_HAS_LONG_LONG
@@ -51,21 +49,15 @@ enum argclass
struct format_piece
{
- format_piece (const char *str, enum argclass argc, int n)
- : string (str),
+ format_piece (std::string::size_type start, enum argclass argc, int n)
+ : start (start),
argclass (argc),
n_int_args (n)
- {
- gdb_assert (str != nullptr);
- }
+ {}
- bool operator== (const format_piece &other) const
- {
- return (this->argclass == other.argclass
- && std::string_view (this->string) == other.string);
- }
+ /* Where this piece starts, within FORMAT_PIECES::M_STORAGE. */
+ std::string::size_type start;
- const char *string;
enum argclass argclass;
/* Count the number of preceding 'int' arguments that must be passed
along. This is used for a width or precision of '*'. Note that
@@ -95,10 +87,17 @@ public:
return m_pieces.end ();
}
+ /* Return the string associated to PIECE. */
+ const char *piece_str (const format_piece &piece)
+ { return &m_storage[piece.start]; }
+
private:
std::vector<format_piece> m_pieces;
- gdb::unique_xmalloc_ptr<char> m_storage;
+
+ /* This is used as a buffer of concatenated null-terminated strings. The
+ individual strings are referenced by FORMAT_PIECE::START. */
+ std::string m_storage;
};
#endif /* GDBSUPPORT_FORMAT_H */
diff --git a/gdbsupport/gdb-safe-ctype.h b/gdbsupport/gdb-safe-ctype.h
deleted file mode 100644
index 36b78f5..0000000
--- a/gdbsupport/gdb-safe-ctype.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* Wrapper around libiberty's safe-ctype.h for GDB, the GNU debugger.
-
- Copyright (C) 2019-2025 Free Software Foundation, Inc.
-
- This file is part of GDB.
-
- This program 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 of the License, or
- (at your option) any later version.
-
- This program 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.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef GDBSUPPORT_GDB_SAFE_CTYPE_H
-#define GDBSUPPORT_GDB_SAFE_CTYPE_H
-
-/* After safe-ctype.h is included, we can no longer use the host's
- ctype routines. Trying to do so results in compile errors. Code
- that uses safe-ctype.h that wants to refer to the locale-dependent
- ctype functions must call these wrapper versions instead.
- When compiling in C++ mode, also include <locale> before "safe-ctype.h"
- which also defines is* functions. */
-
-static inline int
-gdb_isprint (int ch)
-{
- return isprint (ch);
-}
-
-/* readline.h defines these symbols too, but we want libiberty's
- versions. */
-#undef ISALPHA
-#undef ISALNUM
-#undef ISDIGIT
-#undef ISLOWER
-#undef ISPRINT
-#undef ISUPPER
-#undef ISXDIGIT
-
-#include <locale>
-#include "safe-ctype.h"
-
-#endif /* GDBSUPPORT_GDB_SAFE_CTYPE_H */
diff --git a/gdbsupport/gdb_argv_vec.h b/gdbsupport/gdb_argv_vec.h
index 1f3b6db..43571ae 100644
--- a/gdbsupport/gdb_argv_vec.h
+++ b/gdbsupport/gdb_argv_vec.h
@@ -90,6 +90,15 @@ public:
m_args.push_back (value);
}
+ /* Like calling emplace_back on the underlying vector. This class takes
+ ownership of the value added to the vector, and will release the value
+ by calling xfree() on it when this object is destroyed. */
+ template<typename... Args>
+ reference emplace_back (Args &&...args)
+ {
+ return m_args.emplace_back (std::forward<Args> (args)...);
+ }
+
/* Non constant iterator to start of m_args. */
iterator begin ()
{
@@ -133,6 +142,12 @@ public:
{
return m_args.empty ();
}
+
+ /* Clear the argument vector. */
+ void clear ()
+ {
+ free_vector_argv (m_args);
+ }
};
} /* namespac gdb */
diff --git a/gdbsupport/iterator-range.h b/gdbsupport/iterator-range.h
index a8cacfb..d18939c 100644
--- a/gdbsupport/iterator-range.h
+++ b/gdbsupport/iterator-range.h
@@ -30,13 +30,11 @@ struct iterator_range
/* Create an iterator_range using BEGIN as the begin iterator.
Assume that the end iterator can be default-constructed. */
- template <typename... Args>
- iterator_range (Args &&...args)
- : m_begin (std::forward<Args> (args)...)
+ explicit iterator_range (IteratorType begin)
+ : iterator_range (std::move (begin), IteratorType {})
{}
/* Create an iterator range using explicit BEGIN and END iterators. */
- template <typename... Args>
iterator_range (IteratorType begin, IteratorType end)
: m_begin (std::move (begin)), m_end (std::move (end))
{}
@@ -57,6 +55,10 @@ struct iterator_range
std::size_t size () const
{ return std::distance (m_begin, m_end); }
+ /* Return true if this range is empty. */
+ bool empty () const
+ { return m_begin == m_end; }
+
private:
IteratorType m_begin, m_end;
};
diff --git a/gdbsupport/parallel-for.h b/gdbsupport/parallel-for.h
index c485c36..7f3fef9 100644
--- a/gdbsupport/parallel-for.h
+++ b/gdbsupport/parallel-for.h
@@ -21,116 +21,92 @@
#define GDBSUPPORT_PARALLEL_FOR_H
#include <algorithm>
-#include <type_traits>
+#include <atomic>
+#include <tuple>
+#include "gdbsupport/iterator-range.h"
#include "gdbsupport/thread-pool.h"
-#include "gdbsupport/function-view.h"
+#include "gdbsupport/work-queue.h"
namespace gdb
{
-/* A very simple "parallel for". This splits the range of iterators
- into subranges, and then passes each subrange to the callback. The
- work may or may not be done in separate threads.
+/* If enabled, print debug info about the inner workings of the parallel for
+ each functions. */
+constexpr bool parallel_for_each_debug = false;
- This approach was chosen over having the callback work on single
- items because it makes it simple for the caller to do
- once-per-subrange initialization and destruction.
+/* A "parallel-for" implementation using a shared work queue. Work items get
+ popped in batches of size up to BATCH_SIZE from the queue and handed out to
+ worker threads.
- The parameter N says how batching ought to be done -- there will be
- at least N elements processed per thread. Setting N to 0 is not
- allowed. */
+ Each worker thread instantiates an object of type Worker, forwarding ARGS to
+ its constructor. The Worker object can be used to keep some per-worker
+ thread state.
-template<class RandomIt, class RangeFunction>
+ Worker threads call Worker::operator() repeatedly until the queue is
+ empty.
+
+ This function is synchronous, meaning that it blocks and returns once the
+ processing is complete. */
+
+template<std::size_t batch_size, class RandomIt, class Worker,
+ class... WorkerArgs>
void
-parallel_for_each (unsigned n, RandomIt first, RandomIt last,
- RangeFunction callback)
+parallel_for_each (const RandomIt first, const RandomIt last,
+ WorkerArgs &&...worker_args)
{
- /* If enabled, print debug info about how the work is distributed across
- the threads. */
- const bool parallel_for_each_debug = false;
+ gdb_assert (first <= last);
- size_t n_worker_threads = thread_pool::g_thread_pool->thread_count ();
- size_t n_threads = n_worker_threads;
- size_t n_elements = last - first;
- size_t elts_per_thread = 0;
- size_t elts_left_over = 0;
-
- if (n_threads > 1)
+ if (parallel_for_each_debug)
{
- /* Require that there should be at least N elements in a
- thread. */
- gdb_assert (n > 0);
- if (n_elements / n_threads < n)
- n_threads = std::max (n_elements / n, (size_t) 1);
- elts_per_thread = n_elements / n_threads;
- elts_left_over = n_elements % n_threads;
- /* n_elements == n_threads * elts_per_thread + elts_left_over. */
+ debug_printf ("Parallel for: n elements: %zu\n",
+ static_cast<std::size_t> (last - first));
+ debug_printf ("Parallel for: batch size: %zu\n", batch_size);
}
- size_t count = n_threads == 0 ? 0 : n_threads - 1;
std::vector<gdb::future<void>> results;
+ work_queue<RandomIt, batch_size> queue (first, last);
- if (parallel_for_each_debug)
- {
- debug_printf (_("Parallel for: n_elements: %zu\n"), n_elements);
- debug_printf (_("Parallel for: minimum elements per thread: %u\n"), n);
- debug_printf (_("Parallel for: elts_per_thread: %zu\n"), elts_per_thread);
- }
+ /* The worker thread task.
+
+ We need to capture args as a tuple, because it's not possible to capture
+ the parameter pack directly in C++17. Once we migrate to C++20, the
+ capture can be simplified to:
- for (int i = 0; i < count; ++i)
+ ... args = std::forward<Args>(args)
+
+ and `args` can be used as-is in the lambda. */
+ auto args_tuple
+ = std::forward_as_tuple (std::forward<WorkerArgs> (worker_args)...);
+ auto task = [&queue, first, &args_tuple] ()
{
- RandomIt end;
- end = first + elts_per_thread;
- if (i < elts_left_over)
- /* Distribute the leftovers over the worker threads, to avoid having
- to handle all of them in a single thread. */
- end++;
-
- /* This case means we don't have enough elements to really
- distribute them. Rather than ever submit a task that does
- nothing, we short-circuit here. */
- if (first == end)
- end = last;
-
- if (end == last)
- {
- /* We're about to dispatch the last batch of elements, which
- we normally process in the main thread. So just truncate
- the result list here. This avoids submitting empty tasks
- to the thread pool. */
- count = i;
- break;
- }
+ /* Instantiate the user-defined worker. */
+ auto worker = std::make_from_tuple<Worker> (args_tuple);
- if (parallel_for_each_debug)
+ for (;;)
{
- debug_printf (_("Parallel for: elements on worker thread %i\t: %zu"),
- i, (size_t)(end - first));
- debug_printf (_("\n"));
+ const auto batch = queue.pop_batch ();
+
+ if (batch.empty ())
+ break;
+
+ if (parallel_for_each_debug)
+ debug_printf ("Processing %zu items, range [%zu, %zu[\n",
+ batch.size (),
+ batch.begin () - first,
+ batch.end () - first);
+
+ worker (batch);
}
- results.push_back (gdb::thread_pool::g_thread_pool->post_task ([=] ()
- {
- return callback (first, end);
- }));
- first = end;
- }
+ };
- for (int i = count; i < n_worker_threads; ++i)
- if (parallel_for_each_debug)
- {
- debug_printf (_("Parallel for: elements on worker thread %i\t: 0"), i);
- debug_printf (_("\n"));
- }
+ /* Start N_WORKER_THREADS tasks. */
+ const size_t n_worker_threads
+ = std::max<size_t> (thread_pool::g_thread_pool->thread_count (), 1);
- /* Process all the remaining elements in the main thread. */
- if (parallel_for_each_debug)
- {
- debug_printf (_("Parallel for: elements on main thread\t\t: %zu"),
- (size_t)(last - first));
- debug_printf (_("\n"));
- }
- callback (first, last);
+ for (int i = 0; i < n_worker_threads; ++i)
+ results.push_back (gdb::thread_pool::g_thread_pool->post_task (task));
+ /* Wait for all of them to be finished. */
for (auto &fut : results)
fut.get ();
}
@@ -139,12 +115,142 @@ parallel_for_each (unsigned n, RandomIt first, RandomIt last,
when debugging multi-threading behavior, and you want to limit
multi-threading in a fine-grained way. */
-template<class RandomIt, class RangeFunction>
+template<class RandomIt, class Worker, class... WorkerArgs>
+void
+sequential_for_each (RandomIt first, RandomIt last, WorkerArgs &&...worker_args)
+{
+ if (first == last)
+ return;
+
+ Worker (std::forward<WorkerArgs> (worker_args)...) ({ first, last });
+}
+
+namespace detail
+{
+
+/* Type to hold the state shared between threads of
+ gdb::parallel_for_each_async. */
+
+template<std::size_t min_batch_size, typename RandomIt, typename... WorkerArgs>
+struct pfea_state
+{
+ pfea_state (RandomIt first, RandomIt last, std::function<void ()> &&done,
+ WorkerArgs &&...worker_args)
+ : first (first),
+ last (last),
+ worker_args_tuple (std::forward_as_tuple
+ (std::forward<WorkerArgs> (worker_args)...)),
+ queue (first, last),
+ m_done (std::move (done))
+ {}
+
+ DISABLE_COPY_AND_ASSIGN (pfea_state);
+
+ /* This gets called by the last worker thread that drops its reference on
+ the shared state, thus when the processing is complete. */
+ ~pfea_state ()
+ {
+ if (m_done)
+ m_done ();
+ }
+
+ /* The interval to process. */
+ const RandomIt first, last;
+
+ /* Tuple of arguments to pass when constructing the user's worker object.
+
+ Use std::decay_t to avoid storing references to the caller's local
+ variables. If we didn't use it and the caller passed an lvalue `foo *`,
+ we would store it as a reference to `foo *`, thus storing a reference to
+ the caller's local variable.
+
+ The downside is that it's not possible to pass arguments by reference,
+ callers need to pass pointers or std::reference_wrappers. */
+ std::tuple<std::decay_t<WorkerArgs>...> worker_args_tuple;
+
+ /* Work queue that worker threads pull work items from. */
+ work_queue<RandomIt, min_batch_size> queue;
+
+private:
+ /* Callable called when the parallel-for is done. */
+ std::function<void ()> m_done;
+};
+
+} /* namespace detail */
+
+/* A "parallel-for" implementation using a shared work queue. Work items get
+ popped in batches from the queue and handed out to worker threads.
+
+ Batch sizes are proportional to the number of remaining items in the queue,
+ but always greater or equal to MIN_BATCH_SIZE.
+
+ The DONE callback is invoked when processing is done.
+
+ Each worker thread instantiates an object of type Worker, forwarding ARGS to
+ its constructor. The Worker object can be used to keep some per-worker
+ thread state. This version does not support passing references as arguments
+ to the worker. Use std::reference_wrapper or pointers instead.
+
+ Worker threads call Worker::operator() repeatedly until the queue is
+ empty.
+
+ This function is asynchronous. An arbitrary worker thread will call the DONE
+ callback when processing is done. */
+
+template<std::size_t min_batch_size, class RandomIt, class Worker,
+ class... WorkerArgs>
void
-sequential_for_each (unsigned n, RandomIt first, RandomIt last,
- RangeFunction callback)
+parallel_for_each_async (const RandomIt first, const RandomIt last,
+ std::function<void ()> &&done,
+ WorkerArgs &&...worker_args)
{
- callback (first, last);
+ gdb_assert (first <= last);
+
+ if (parallel_for_each_debug)
+ {
+ debug_printf ("Parallel for: n elements: %zu\n",
+ static_cast<std::size_t> (last - first));
+ debug_printf ("Parallel for: min batch size: %zu\n", min_batch_size);
+ }
+
+ const size_t n_worker_threads
+ = std::max<size_t> (thread_pool::g_thread_pool->thread_count (), 1);
+
+ /* The state shared between all worker threads. All worker threads get a
+ reference on the shared pointer through the lambda below. The last worker
+ thread to drop its reference will cause this object to be destroyed, which
+ will call the DONE callback. */
+ using state_t = detail::pfea_state<min_batch_size, RandomIt, WorkerArgs...>;
+ auto state
+ = std::make_shared<state_t> (first, last, std::move (done),
+ std::forward<WorkerArgs> (worker_args)...);
+
+ /* The worker thread task. */
+ auto task = [state] ()
+ {
+ /* Instantiate the user-defined worker. */
+ auto worker = std::make_from_tuple<Worker> (state->worker_args_tuple);
+
+ for (;;)
+ {
+ const auto batch = state->queue.pop_batch ();
+
+ if (batch.empty ())
+ break;
+
+ if (parallel_for_each_debug)
+ debug_printf ("Processing %zu items, range [%zu, %zu[\n",
+ batch.size (),
+ batch.begin () - state->first,
+ batch.end () - state->first);
+
+ worker (batch);
+ }
+ };
+
+ /* Start N_WORKER_THREADS tasks. */
+ for (int i = 0; i < n_worker_threads; ++i)
+ gdb::thread_pool::g_thread_pool->post_task (task);
}
}
diff --git a/gdbsupport/pathstuff.cc b/gdbsupport/pathstuff.cc
index ce01c95..8142bd5 100644
--- a/gdbsupport/pathstuff.cc
+++ b/gdbsupport/pathstuff.cc
@@ -89,34 +89,25 @@ std::string
gdb_realpath_keepfile (const char *filename)
{
const char *base_name = lbasename (filename);
- char *dir_name;
/* Extract the basename of filename, and return immediately
a copy of filename if it does not contain any directory prefix. */
if (base_name == filename)
return filename;
- dir_name = (char *) alloca ((size_t) (base_name - filename + 2));
- /* Allocate enough space to store the dir_name + plus one extra
- character sometimes needed under Windows (see below), and
- then the closing \000 character. */
- strncpy (dir_name, filename, base_name - filename);
- dir_name[base_name - filename] = '\000';
+ std::string dir_name (filename, base_name - filename);
#ifdef HAVE_DOS_BASED_FILE_SYSTEM
/* We need to be careful when filename is of the form 'd:foo', which
is equivalent of d:./foo, which is totally different from d:/foo. */
- if (strlen (dir_name) == 2 && isalpha (dir_name[0]) && dir_name[1] == ':')
- {
- dir_name[2] = '.';
- dir_name[3] = '\000';
- }
+ if (dir_name.size () == 2 && c_isalpha (dir_name[0]) && dir_name[1] == ':')
+ dir_name += '.';
#endif
/* Canonicalize the directory prefix, and build the resulting
filename. If the dirname realpath already contains an ending
directory separator, avoid doubling it. */
- gdb::unique_xmalloc_ptr<char> path_storage = gdb_realpath (dir_name);
+ gdb::unique_xmalloc_ptr<char> path_storage = gdb_realpath (dir_name.c_str ());
const char *real_path = path_storage.get ();
return path_join (real_path, base_name);
}
diff --git a/gdbsupport/poison.h b/gdbsupport/poison.h
index 791a18f..a91ee5d 100644
--- a/gdbsupport/poison.h
+++ b/gdbsupport/poison.h
@@ -183,7 +183,7 @@ xnewvar (size_t s)
{
static_assert (IsMallocable<T>::value, "Trying to use XNEWVAR with a \
non-POD data type.");
- return XNEWVAR (T, s);;
+ return XNEWVAR (T, s);
}
#undef XNEWVAR
diff --git a/gdbsupport/print-utils.cc b/gdbsupport/print-utils.cc
index 84a7485..8514720 100644
--- a/gdbsupport/print-utils.cc
+++ b/gdbsupport/print-utils.cc
@@ -145,7 +145,7 @@ static int thirty_two = 32;
/* See print-utils.h. */
const char *
-phex (ULONGEST l, int sizeof_l)
+phex_ulongest (ULONGEST l, int sizeof_l)
{
char *str;
@@ -170,7 +170,7 @@ phex (ULONGEST l, int sizeof_l)
xsnprintf (str, PRINT_CELL_SIZE, "%02x", (unsigned short) (l & 0xff));
break;
default:
- return phex (l, sizeof (l));
+ return phex (l);
break;
}
@@ -180,7 +180,7 @@ phex (ULONGEST l, int sizeof_l)
/* See print-utils.h. */
const char *
-phex_nz (ULONGEST l, int sizeof_l)
+phex_nz_ulongest (ULONGEST l, int sizeof_l)
{
char *str;
@@ -212,7 +212,7 @@ phex_nz (ULONGEST l, int sizeof_l)
xsnprintf (str, PRINT_CELL_SIZE, "%x", (unsigned short) (l & 0xff));
break;
default:
- return phex_nz (l, sizeof (l));
+ return phex_nz (l);
break;
}
@@ -226,7 +226,7 @@ hex_string (LONGEST num)
{
char *result = get_print_cell ();
- xsnprintf (result, PRINT_CELL_SIZE, "0x%s", phex_nz (num, sizeof (num)));
+ xsnprintf (result, PRINT_CELL_SIZE, "0x%s", phex_nz (num));
return result;
}
@@ -237,7 +237,7 @@ hex_string_custom (LONGEST num, int width)
{
char *result = get_print_cell ();
char *result_end = result + PRINT_CELL_SIZE - 1;
- const char *hex = phex_nz (num, sizeof (num));
+ const char *hex = phex_nz (num);
int hex_len = strlen (hex);
if (hex_len > width)
@@ -304,8 +304,7 @@ core_addr_to_string (const CORE_ADDR addr)
{
char *str = get_print_cell ();
- strcpy (str, "0x");
- strcat (str, phex (addr, sizeof (addr)));
+ xsnprintf (str, PRINT_CELL_SIZE, "0x%s", phex (addr));
return str;
}
@@ -316,8 +315,7 @@ core_addr_to_string_nz (const CORE_ADDR addr)
{
char *str = get_print_cell ();
- strcpy (str, "0x");
- strcat (str, phex_nz (addr, sizeof (addr)));
+ xsnprintf (str, PRINT_CELL_SIZE, "0x%s", phex_nz (addr));
return str;
}
diff --git a/gdbsupport/print-utils.h b/gdbsupport/print-utils.h
index e50d96f..dc5011c 100644
--- a/gdbsupport/print-utils.h
+++ b/gdbsupport/print-utils.h
@@ -34,15 +34,35 @@ extern const char *pulongest (ULONGEST u);
extern const char *plongest (LONGEST l);
-/* Convert a ULONGEST into a HEX string, like %lx, with leading zeros.
+/* Convert L (of type ULONGEST) into a hex string, like %lx, with leading
+ zeros. The result is stored in a circular static buffer, NUMCELLS
+ deep. */
+
+extern const char *phex_ulongest (ULONGEST l, int sizeof_l);
+
+/* Convert L into a HEX string, like %lx, with leading zeros.
The result is stored in a circular static buffer, NUMCELLS deep. */
-extern const char *phex (ULONGEST l, int sizeof_l);
+template<typename T>
+const char *phex (T l, int sizeof_l = sizeof (T))
+{
+ return phex_ulongest (l, sizeof_l);
+}
+
+/* Convert L (of type ULONGEST) into a hex string, like %lx, without leading
+ zeros. The result is stored in a circular static buffer, NUMCELLS
+ deep. */
-/* Convert a ULONGEST into a HEX string, like %lx, without leading zeros.
- The result is stored in a circular static buffer, NUMCELLS deep. */
+extern const char *phex_nz_ulongest (ULONGEST l, int sizeof_l);
+
+/* Convert L into a hex string, like %lx, without leading zeros.
+ The result is stored in a circular static buffer, NUMCELLS deep. */
-extern const char *phex_nz (ULONGEST l, int sizeof_l);
+template<typename T>
+const char *phex_nz (T l, int sizeof_l = sizeof (T))
+{
+ return phex_nz_ulongest (l, sizeof_l);
+}
/* Converts a LONGEST to a C-format hexadecimal literal and stores it
in a static string. Returns a pointer to this string. */
diff --git a/gdbsupport/reference-to-pointer-iterator.h b/gdbsupport/reference-to-pointer-iterator.h
index af58e38..67a8d54 100644
--- a/gdbsupport/reference-to-pointer-iterator.h
+++ b/gdbsupport/reference-to-pointer-iterator.h
@@ -38,9 +38,8 @@ struct reference_to_pointer_iterator
/* Construct a reference_to_pointer_iterator, passing args to the underlying
iterator. */
- template <typename... Args>
- reference_to_pointer_iterator (Args &&...args)
- : m_it (std::forward<Args> (args)...)
+ explicit reference_to_pointer_iterator (IteratorType it)
+ : m_it (std::move (it))
{}
/* Create a past-the-end iterator.
diff --git a/gdbsupport/remote-args.cc b/gdbsupport/remote-args.cc
new file mode 100644
index 0000000..2493433
--- /dev/null
+++ b/gdbsupport/remote-args.cc
@@ -0,0 +1,43 @@
+/* Copyright (C) 2023-2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program 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 of the License, or
+ (at your option) any later version.
+
+ This program 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "gdbsupport/common-defs.h"
+#include "gdbsupport/remote-args.h"
+#include "gdbsupport/common-inferior.h"
+#include "gdbsupport/buildargv.h"
+
+/* See remote-args.h. */
+
+std::vector<std::string>
+gdb::remote_args::split (const std::string &args)
+{
+ std::vector<std::string> results;
+
+ gdb_argv argv (args.c_str ());
+ for (int i = 0; argv[i] != nullptr; i++)
+ results.emplace_back (argv[i]);
+
+ return results;
+}
+
+/* See remote-args.h. */
+
+std::string
+gdb::remote_args::join (const std::vector<char *> &args)
+{
+ return construct_inferior_arguments (args, true);
+}
diff --git a/gdbsupport/remote-args.h b/gdbsupport/remote-args.h
new file mode 100644
index 0000000..0533da6
--- /dev/null
+++ b/gdbsupport/remote-args.h
@@ -0,0 +1,60 @@
+/* Functions to help when passing arguments between GDB and gdbserver.
+
+ Copyright (C) 2023-2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program 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 of the License, or
+ (at your option) any later version.
+
+ This program 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef GDBSUPPORT_REMOTE_ARGS_H
+#define GDBSUPPORT_REMOTE_ARGS_H
+
+/* The functions declared here are used when passing inferior arguments
+ from GDB to gdbserver.
+
+ The remote protocol requires that arguments are passed as a vector of
+ separate argument while GDB stores the arguments as a single string, and
+ gdbserver also requires the arguments be a single string.
+
+ These functions then provide a mechanism to split up an argument string
+ and recombine it within gdbserver while preserving escaping of special
+ characters within the argument string. */
+
+namespace gdb
+{
+
+namespace remote_args
+{
+
+/* ARGS is an inferior argument string. This function splits ARGS into
+ individual arguments and returns a vector containing each argument. */
+
+extern std::vector<std::string> split (const std::string &args);
+
+/* Join together the separate arguments in ARGS and build a single
+ inferior argument string. The string returned by this function will be
+ equivalent, but not necessarily identical to the string passed to
+ ::split, for example passing the string '"a b"' (without the single
+ quotes, but including the double quotes) to ::split, will return an
+ argument of 'a b' (without the single quotes). When this argument is
+ passed through ::join we will get back the string 'a\ b' (without the
+ single quotes), that is, we choose to escape the white space, rather
+ than wrap the argument in quotes. */
+extern std::string join (const std::vector<char *> &args);
+
+} /* namespace remote_args */
+
+} /* namespace gdb */
+
+#endif /* GDBSUPPORT_REMOTE_ARGS_H */
diff --git a/gdbsupport/run-time-clock.cc b/gdbsupport/run-time-clock.cc
index 43da1d9..cda60b0 100644
--- a/gdbsupport/run-time-clock.cc
+++ b/gdbsupport/run-time-clock.cc
@@ -37,14 +37,51 @@ timeval_to_microseconds (struct timeval *tv)
}
#endif
+/* See run-time-clock.h. */
+
+bool
+get_run_time_thread_scope_available ()
+{
+#if defined HAVE_GETRUSAGE && defined RUSAGE_THREAD
+ return true;
+#else
+ return false;
+#endif
+}
+
void
-run_time_clock::now (user_cpu_time_clock::time_point &user,
- system_cpu_time_clock::time_point &system) noexcept
+get_run_time (user_cpu_time_clock::time_point &user,
+ system_cpu_time_clock::time_point &system,
+ run_time_scope scope) noexcept
{
#ifdef HAVE_GETRUSAGE
+
+ /* If we can't provide thread scope run time usage, fallback to
+ process scope. This will at least be right if GDB is built
+ without threading support in the first place (or is set to use
+ zero worker threads). */
+# ifndef RUSAGE_THREAD
+# define RUSAGE_THREAD RUSAGE_SELF
+# endif
+
struct rusage rusage;
+ int who;
+
+ switch (scope)
+ {
+ case run_time_scope::thread:
+ who = RUSAGE_THREAD;
+ break;
+
+ case run_time_scope::process:
+ who = RUSAGE_SELF;
+ break;
+
+ default:
+ gdb_assert_not_reached ("invalid run_time_scope value");
+ }
- getrusage (RUSAGE_SELF, &rusage);
+ getrusage (who, &rusage);
microseconds utime = timeval_to_microseconds (&rusage.ru_utime);
microseconds stime = timeval_to_microseconds (&rusage.ru_stime);
diff --git a/gdbsupport/run-time-clock.h b/gdbsupport/run-time-clock.h
index a961f4c..2743514 100644
--- a/gdbsupport/run-time-clock.h
+++ b/gdbsupport/run-time-clock.h
@@ -51,6 +51,33 @@ struct system_cpu_time_clock
static time_point now () noexcept = delete;
};
+/* Whether to measure time run time for the whole process or just one
+ thread. */
+
+enum class run_time_scope
+{
+ process,
+ thread,
+};
+
+/* Return the user/system time as separate time points, if
+ supported. If not supported, then the combined user+kernel time
+ is returned in USER and SYSTEM is set to zero.
+
+ SCOPE indicates whether to return the run time for the whole
+ process or just for the calling thread. If the latter isn't
+ supported, then returns the run time for the whole process even if
+ run_time_scope::thread is requested. */
+
+void get_run_time (user_cpu_time_clock::time_point &user,
+ system_cpu_time_clock::time_point &system,
+ run_time_scope scope) noexcept;
+
+/* Returns true if is it possible for get_run_time above to return the
+ run time for just the calling thread. */
+
+bool get_run_time_thread_scope_available ();
+
/* Count the total amount of time spent executing in userspace+kernel
mode. */
@@ -64,12 +91,6 @@ struct run_time_clock
static constexpr bool is_steady = true;
static time_point now () noexcept;
-
- /* Return the user/system time as separate time points, if
- supported. If not supported, then the combined user+kernel time
- is returned in USER and SYSTEM is set to zero. */
- static void now (user_cpu_time_clock::time_point &user,
- system_cpu_time_clock::time_point &system) noexcept;
};
#endif /* GDBSUPPORT_RUN_TIME_CLOCK_H */
diff --git a/gdbsupport/safe-iterator.h b/gdbsupport/safe-iterator.h
index 5388160..4cb9a19 100644
--- a/gdbsupport/safe-iterator.h
+++ b/gdbsupport/safe-iterator.h
@@ -50,30 +50,24 @@ public:
typedef typename Iterator::iterator_category iterator_category;
typedef typename Iterator::difference_type difference_type;
- /* Construct the begin iterator using the given arguments; the end iterator is
- default constructed. */
- template<typename... Args>
- explicit basic_safe_iterator (Args &&...args)
- : m_it (std::forward<Args> (args)...),
- m_next (m_it)
- {
- if (m_it != m_end)
- ++m_next;
- }
+ /* Construct the iterator using the underlying iterator BEGIN; the end
+ iterator is default constructed. */
+ explicit basic_safe_iterator (Iterator begin)
+ : basic_safe_iterator (std::move (begin), Iterator {})
+ {}
- /* Construct the iterator using the first argument, and construct
- the end iterator using the second argument. */
- template<typename Arg>
- explicit basic_safe_iterator (Arg &&arg, Arg &&arg2)
- : m_it (std::forward<Arg> (arg)),
+ /* Construct the iterator using the underlying iterators BEGIN and END. */
+ basic_safe_iterator (Iterator begin, Iterator end)
+ : m_it (std::move (begin)),
m_next (m_it),
- m_end (std::forward<Arg> (arg2))
+ m_end (std::move (end))
{
if (m_it != m_end)
++m_next;
}
- /* Create a one-past-end iterator. */
+ /* Create a one-past-end iterator. The underlying end iterator is obtained
+ by default-constructing. */
basic_safe_iterator ()
{}
diff --git a/gdbsupport/thread-pool.h b/gdbsupport/thread-pool.h
index f3ac94b..b5b2934 100644
--- a/gdbsupport/thread-pool.h
+++ b/gdbsupport/thread-pool.h
@@ -24,99 +24,12 @@
#include <vector>
#include <functional>
#include <chrono>
-#if CXX_STD_THREAD
-#include <thread>
-#include <mutex>
-#include <condition_variable>
-#include <future>
-#endif
#include <optional>
-namespace gdb
-{
-
-#if CXX_STD_THREAD
-
-/* Simply use the standard future. */
-template<typename T>
-using future = std::future<T>;
-
-/* ... and the standard future_status. */
-using future_status = std::future_status;
-
-#else /* CXX_STD_THREAD */
-
-/* A compatibility enum for std::future_status. This is just the
- subset needed by gdb. */
-enum class future_status
-{
- ready,
- timeout,
-};
-
-/* A compatibility wrapper for std::future. Once <thread> and
- <future> are available in all GCC builds -- should that ever happen
- -- this can be removed. GCC does not implement threading for
- MinGW, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93687.
-
- Meanwhile, in this mode, there are no threads. Tasks submitted to
- the thread pool are invoked immediately and their result is stored
- here. The base template here simply wraps a T and provides some
- std::future compatibility methods. The provided methods are chosen
- based on what GDB needs presently. */
-
-template<typename T>
-class future
-{
-public:
-
- explicit future (T value)
- : m_value (std::move (value))
- {
- }
-
- future () = default;
- future (future &&other) = default;
- future (const future &other) = delete;
- future &operator= (future &&other) = default;
- future &operator= (const future &other) = delete;
+#include "gdbsupport/cxx-thread.h"
- void wait () const { }
-
- template<class Rep, class Period>
- future_status wait_for (const std::chrono::duration<Rep,Period> &duration)
- const
- {
- return future_status::ready;
- }
-
- T get () { return std::move (m_value); }
-
-private:
-
- T m_value;
-};
-
-/* A specialization for void. */
-
-template<>
-class future<void>
+namespace gdb
{
-public:
- void wait () const { }
-
- template<class Rep, class Period>
- future_status wait_for (const std::chrono::duration<Rep,Period> &duration)
- const
- {
- return future_status::ready;
- }
-
- void get () { }
-};
-
-#endif /* CXX_STD_THREAD */
-
/* A thread pool.
diff --git a/gdbsupport/work-queue.h b/gdbsupport/work-queue.h
new file mode 100644
index 0000000..66f073d
--- /dev/null
+++ b/gdbsupport/work-queue.h
@@ -0,0 +1,96 @@
+/* Synchronized work queue.
+
+ Copyright (C) 2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program 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 of the License, or
+ (at your option) any later version.
+
+ This program 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef GDBSUPPORT_WORK_QUEUE_H
+#define GDBSUPPORT_WORK_QUEUE_H
+
+#include "gdbsupport/iterator-range.h"
+
+namespace gdb
+{
+
+/* Implementation of a thread-safe work queue.
+
+ The work items are specified by two iterators of type RandomIt.
+
+ BATCH_SIZE is the number of work items to pop in a batch. */
+
+template<typename RandomIt, std::size_t batch_size>
+class work_queue
+{
+public:
+ /* The work items are specified by the range `[first, last[`. */
+ work_queue (const RandomIt first, const RandomIt last) noexcept
+ : m_next (first),
+ m_last (last)
+ {
+ gdb_assert (first <= last);
+ }
+
+ DISABLE_COPY_AND_ASSIGN (work_queue);
+
+ /* Pop a batch of work items.
+
+ The return value is an iterator range delimiting the work items. */
+ iterator_range<RandomIt> pop_batch () noexcept
+ {
+ for (;;)
+ {
+ /* Grab a snapshot of M_NEXT. */
+ auto next = m_next.load ();
+ gdb_assert (next <= m_last);
+
+ /* The number of items remaining in the queue. */
+ const auto n_remaining = static_cast<std::size_t> (m_last - next);
+
+ /* Are we done? */
+ if (n_remaining == 0)
+ return { m_last, m_last };
+
+ /* The batch size is proportional to the number of items remaining in
+ the queue. We do this to try to stike a balance, avoiding
+ synchronization overhead when there are many items to process at the
+ start, and avoiding workload imbalance when there are few items to
+ process at the end. */
+ const auto this_batch_size = std::min (batch_size, n_remaining);
+
+ /* The range of items in this batch. */
+ const auto this_batch_first = next;
+ const auto this_batch_last = next + this_batch_size;
+
+ /* Update M_NEXT. If the current value of M_NEXT doesn't match NEXT, it
+ means another thread updated it concurrently, restart. */
+ if (!m_next.compare_exchange_weak (next, this_batch_last))
+ continue;
+
+ return { this_batch_first, this_batch_last };
+ }
+ }
+
+private:
+ /* The next work item to hand out. */
+ std::atomic<RandomIt> m_next;
+
+ /* The end of the work item range. */
+ RandomIt m_last;
+};
+
+} /* namespace gdb */
+
+#endif /* GDBSUPPORT_WORK_QUEUE_H */
diff --git a/gdbsupport/x86-xstate.h b/gdbsupport/x86-xstate.h
index 5d563ff..6657c45 100644
--- a/gdbsupport/x86-xstate.h
+++ b/gdbsupport/x86-xstate.h
@@ -28,6 +28,7 @@
#define X86_XSTATE_ZMM_H_ID 6
#define X86_XSTATE_ZMM_ID 7
#define X86_XSTATE_PKRU_ID 9
+#define X86_XSTATE_CET_U_ID 11
/* The extended state feature bits. */
#define X86_XSTATE_X87 (1ULL << X86_XSTATE_X87_ID)
@@ -42,6 +43,7 @@
| X86_XSTATE_ZMM)
#define X86_XSTATE_PKRU (1ULL << X86_XSTATE_PKRU_ID)
+#define X86_XSTATE_CET_U (1ULL << X86_XSTATE_CET_U_ID)
/* Total size of the XSAVE area extended region and offsets of
register states within the region. Offsets are set to 0 to
@@ -83,8 +85,11 @@ constexpr bool operator!= (const x86_xsave_layout &lhs,
#define X86_XSTATE_AVX_AVX512_PKU_MASK (X86_XSTATE_AVX_MASK\
| X86_XSTATE_AVX512 | X86_XSTATE_PKRU)
-#define X86_XSTATE_ALL_MASK (X86_XSTATE_AVX_AVX512_PKU_MASK)
+/* Supported mask of state-component bitmap xstate_bv. The SDM defines
+ xstate_bv as XCR0 | IA32_XSS. */
+#define X86_XSTATE_ALL_MASK (X86_XSTATE_AVX_AVX512_PKU_MASK\
+ | X86_XSTATE_CET_U)
#define X86_XSTATE_SSE_SIZE 576
#define X86_XSTATE_AVX_SIZE 832