forked from Imagelibrary/binutils-gdb
Compare commits
2 Commits
users/vrie
...
users/palv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73a029bd60 | ||
|
|
6994a75776 |
@@ -464,6 +464,7 @@ SELFTESTS_SRCS = \
|
||||
unittests/offset-type-selftests.c \
|
||||
unittests/observable-selftests.c \
|
||||
unittests/optional-selftests.c \
|
||||
unittests/packed-selftests.c \
|
||||
unittests/parallel-for-selftests.c \
|
||||
unittests/parse-connection-spec-selftests.c \
|
||||
unittests/path-join-selftests.c \
|
||||
|
||||
19
gdb/infrun.c
19
gdb/infrun.c
@@ -3971,6 +3971,16 @@ prepare_for_detach (void)
|
||||
}
|
||||
}
|
||||
|
||||
/* If all-stop, but there exists a non-stop target, stop all threads
|
||||
now that we're presenting the stop to the user. */
|
||||
|
||||
static void
|
||||
stop_all_threads_if_all_stop_mode ()
|
||||
{
|
||||
if (!non_stop && exists_non_stop_target ())
|
||||
stop_all_threads ("presenting stop to user in all-stop");
|
||||
}
|
||||
|
||||
/* Wait for control to return from inferior to debugger.
|
||||
|
||||
If inferior gets a signal, we may decide to start it up again
|
||||
@@ -4017,6 +4027,8 @@ wait_for_inferior (inferior *inf)
|
||||
break;
|
||||
}
|
||||
|
||||
stop_all_threads_if_all_stop_mode ();
|
||||
|
||||
/* No error, don't finish the state yet. */
|
||||
finish_state.release ();
|
||||
}
|
||||
@@ -4240,6 +4252,8 @@ fetch_inferior_event ()
|
||||
bool should_notify_stop = true;
|
||||
int proceeded = 0;
|
||||
|
||||
stop_all_threads_if_all_stop_mode ();
|
||||
|
||||
clean_up_just_stopped_threads_fsms (ecs);
|
||||
|
||||
if (thr != nullptr && thr->thread_fsm () != nullptr)
|
||||
@@ -8138,11 +8152,6 @@ stop_waiting (struct execution_control_state *ecs)
|
||||
|
||||
/* Let callers know we don't want to wait for the inferior anymore. */
|
||||
ecs->wait_some_more = 0;
|
||||
|
||||
/* If all-stop, but there exists a non-stop target, stop all
|
||||
threads now that we're presenting the stop to the user. */
|
||||
if (!non_stop && exists_non_stop_target ())
|
||||
stop_all_threads ("presenting stop to user in all-stop");
|
||||
}
|
||||
|
||||
/* Like keep_going, but passes the signal to the inferior, even if the
|
||||
|
||||
49
gdb/testsuite/gdb.threads/step-N-all-progress.c
Normal file
49
gdb/testsuite/gdb.threads/step-N-all-progress.c
Normal file
@@ -0,0 +1,49 @@
|
||||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2022 Free Software Foundation, Inc.
|
||||
|
||||
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 <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static pthread_barrier_t barrier;
|
||||
|
||||
static void *
|
||||
thread_func (void *arg)
|
||||
{
|
||||
pthread_barrier_wait (&barrier);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
pthread_t thread;
|
||||
int ret;
|
||||
|
||||
pthread_barrier_init (&barrier, NULL, 2);
|
||||
|
||||
/* We run to this line below, and then issue "next 3". That should
|
||||
step over the 3 lines below and land on the return statement. If
|
||||
GDB prematurely stops the thread_func thread after the first of
|
||||
the 3 nexts (and never resumes it again), then the join won't
|
||||
ever return. */
|
||||
pthread_create (&thread, NULL, thread_func, NULL); /* set break here */
|
||||
pthread_barrier_wait (&barrier);
|
||||
pthread_join (thread, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
59
gdb/testsuite/gdb.threads/step-N-all-progress.exp
Normal file
59
gdb/testsuite/gdb.threads/step-N-all-progress.exp
Normal file
@@ -0,0 +1,59 @@
|
||||
# Copyright 2022 Free Software Foundation, Inc.
|
||||
|
||||
# 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/>.
|
||||
|
||||
# Test that with "next N", all threads are resumed at each of the N
|
||||
# steps. GDB used to have a bug where in target-non-stop targets, and
|
||||
# in all-stop mode, after the first "next" (or "step/stepi/nexti"),
|
||||
# GDB would prematurely stop all threads. For the subsequent N-1
|
||||
# steps, only the stepped thread would continue running, while all the
|
||||
# other threads would remain stopped.
|
||||
|
||||
standard_testfile
|
||||
|
||||
if { [build_executable "failed to prepare" $testfile \
|
||||
$srcfile {debug pthreads}] == -1 } {
|
||||
return
|
||||
}
|
||||
|
||||
proc test {non-stop target-non-stop} {
|
||||
save_vars ::GDBFLAGS {
|
||||
append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\""
|
||||
append ::GDBFLAGS " -ex \"set non-stop ${non-stop}\""
|
||||
clean_restart $::binfile
|
||||
}
|
||||
|
||||
if { ![runto_main] } {
|
||||
return
|
||||
}
|
||||
|
||||
set lineno [gdb_get_line_number "set break here"]
|
||||
|
||||
gdb_breakpoint $lineno
|
||||
|
||||
gdb_continue_to_breakpoint "break here"
|
||||
|
||||
gdb_test "next 3" "return 0;"
|
||||
}
|
||||
|
||||
foreach_with_prefix non-stop {off on} {
|
||||
foreach_with_prefix target-non-stop {off on} {
|
||||
if {${non-stop} == "on" && ${target-non-stop} == "off"} {
|
||||
# Invalid combination.
|
||||
continue
|
||||
}
|
||||
|
||||
test ${non-stop} ${target-non-stop}
|
||||
}
|
||||
}
|
||||
125
gdb/unittests/packed-selftests.c
Normal file
125
gdb/unittests/packed-selftests.c
Normal file
@@ -0,0 +1,125 @@
|
||||
/* Self tests for packed for GDB, the GNU debugger.
|
||||
|
||||
Copyright (C) 2022 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 "defs.h"
|
||||
#include "gdbsupport/selftest.h"
|
||||
#include "gdbsupport/packed.h"
|
||||
#include <atomic>
|
||||
|
||||
namespace selftests {
|
||||
namespace packed_tests {
|
||||
|
||||
gdb_static_assert (sizeof (packed<unsigned int, 1>) == 1);
|
||||
gdb_static_assert (sizeof (packed<unsigned int, 2>) == 2);
|
||||
gdb_static_assert (sizeof (packed<unsigned int, 3>) == 3);
|
||||
gdb_static_assert (sizeof (packed<unsigned int, 4>) == 4);
|
||||
|
||||
gdb_static_assert (alignof (packed<unsigned int, 1>) == 1);
|
||||
gdb_static_assert (alignof (packed<unsigned int, 2>) == 1);
|
||||
gdb_static_assert (alignof (packed<unsigned int, 3>) == 1);
|
||||
gdb_static_assert (alignof (packed<unsigned int, 4>) == 1);
|
||||
|
||||
/* Triviality checks. */
|
||||
#define CHECK_TRAIT(TRAIT) \
|
||||
static_assert (std::TRAIT<packed<unsigned int, 1>>::value, "")
|
||||
|
||||
#if HAVE_IS_TRIVIALLY_COPYABLE
|
||||
|
||||
CHECK_TRAIT (is_trivially_copyable);
|
||||
CHECK_TRAIT (is_trivially_copy_constructible);
|
||||
CHECK_TRAIT (is_trivially_move_constructible);
|
||||
CHECK_TRAIT (is_trivially_copy_assignable);
|
||||
CHECK_TRAIT (is_trivially_move_assignable);
|
||||
|
||||
#endif
|
||||
|
||||
#undef CHECK_TRAIT
|
||||
|
||||
/* Entry point. */
|
||||
|
||||
static void
|
||||
run_tests ()
|
||||
{
|
||||
typedef packed<unsigned int, 2> packed_2;
|
||||
|
||||
packed_2 p1;
|
||||
packed_2 p2 (0x0102);
|
||||
p1 = 0x0102;
|
||||
|
||||
SELF_CHECK (p1 == p1);
|
||||
SELF_CHECK (p1 == p2);
|
||||
SELF_CHECK (p1 == 0x0102);
|
||||
SELF_CHECK (0x0102 == p1);
|
||||
|
||||
SELF_CHECK (p1 != 0);
|
||||
SELF_CHECK (0 != p1);
|
||||
|
||||
SELF_CHECK (p1 != 0x0103);
|
||||
SELF_CHECK (0x0103 != p1);
|
||||
|
||||
SELF_CHECK (p1 != 0x01020102);
|
||||
SELF_CHECK (0x01020102 != p1);
|
||||
|
||||
SELF_CHECK (p1 != 0x01020000);
|
||||
SELF_CHECK (0x01020000 != p1);
|
||||
|
||||
/* Check truncation. */
|
||||
p1 = 0x030102;
|
||||
SELF_CHECK (p1 == 0x0102);
|
||||
SELF_CHECK (p1 != 0x030102);
|
||||
|
||||
/* Check that the custom std::atomic/packed/T relational operators
|
||||
work as intended. No need for fully comprehensive tests, as all
|
||||
operators are defined in the same way, via a macro. We just want
|
||||
to make sure that we can (non-atomically) compare atomic-wrapped
|
||||
packed, with packed, and with the packed underlying type. */
|
||||
|
||||
std::atomic<packed<unsigned int, 2>> atomic_packed_2 (0x0102);
|
||||
|
||||
SELF_CHECK (atomic_packed_2 == atomic_packed_2);
|
||||
SELF_CHECK (atomic_packed_2 == p1);
|
||||
SELF_CHECK (p1 == atomic_packed_2);
|
||||
SELF_CHECK (atomic_packed_2 == 0x0102u);
|
||||
SELF_CHECK (0x0102u == atomic_packed_2);
|
||||
|
||||
SELF_CHECK (atomic_packed_2 >= 0x0102u);
|
||||
SELF_CHECK (atomic_packed_2 <= 0x0102u);
|
||||
SELF_CHECK (atomic_packed_2 > 0u);
|
||||
SELF_CHECK (atomic_packed_2 < 0x0103u);
|
||||
SELF_CHECK (atomic_packed_2 >= 0u);
|
||||
SELF_CHECK (atomic_packed_2 <= 0x0102u);
|
||||
SELF_CHECK (!(atomic_packed_2 > 0x0102u));
|
||||
SELF_CHECK (!(atomic_packed_2 < 0x0102u));
|
||||
|
||||
/* Check std::atomic<packed> truncation behaves the same as without
|
||||
std::atomic. */
|
||||
atomic_packed_2 = 0x030102;
|
||||
SELF_CHECK (atomic_packed_2 == 0x0102u);
|
||||
SELF_CHECK (atomic_packed_2 != 0x030102u);
|
||||
}
|
||||
|
||||
} /* namespace packed_tests */
|
||||
} /* namespace selftests */
|
||||
|
||||
void _initialize_packed_selftests ();
|
||||
void
|
||||
_initialize_packed_selftests ()
|
||||
{
|
||||
selftests::register_test ("packed", selftests::packed_tests::run_tests);
|
||||
}
|
||||
@@ -27,6 +27,26 @@
|
||||
bit-fields (and ENUM_BITFIELD), when the fields must have separate
|
||||
memory locations to avoid data races. */
|
||||
|
||||
/* There are two implementations here -- one standard compliant, using
|
||||
a byte array for internal representation, and another that relies on
|
||||
bitfields and attribute packed (and attribute gcc_struct on
|
||||
Windows). The latter is preferable, as it is more convenient to
|
||||
debug -- an enum wrapped in struct packed is printed by GDB as an
|
||||
enum -- but may not work on all compilers. */
|
||||
|
||||
/* Clang does not support attribute gcc_struct. */
|
||||
#if defined _WIN32 && defined __clang__
|
||||
# define PACKED_USE_ARRAY 1
|
||||
#else
|
||||
# define PACKED_USE_ARRAY 0
|
||||
#endif
|
||||
|
||||
#if !PACKED_USE_ARRAY && defined _WIN32
|
||||
# define ATTRIBUTE_GCC_STRUCT __attribute__((__gcc_struct__))
|
||||
#else
|
||||
# define ATTRIBUTE_GCC_STRUCT
|
||||
#endif
|
||||
|
||||
template<typename T, size_t Bytes = sizeof (T)>
|
||||
struct packed
|
||||
{
|
||||
@@ -35,7 +55,18 @@ public:
|
||||
|
||||
packed (T val)
|
||||
{
|
||||
gdb_static_assert (sizeof (size_t) >= sizeof (T));
|
||||
|
||||
#if PACKED_USE_ARRAY
|
||||
size_t tmp = val;
|
||||
for (int i = (Bytes - 1); i >= 0; --i)
|
||||
{
|
||||
m_bytes[i] = tmp & 0xff;
|
||||
tmp >>= 8;
|
||||
}
|
||||
#else
|
||||
m_val = val;
|
||||
#endif
|
||||
|
||||
/* Ensure size and aligment are what we expect. */
|
||||
gdb_static_assert (sizeof (packed) == Bytes);
|
||||
@@ -53,44 +84,78 @@ public:
|
||||
|
||||
operator T () const noexcept
|
||||
{
|
||||
#if PACKED_USE_ARRAY
|
||||
size_t tmp = 0;
|
||||
for (int i = 0;;)
|
||||
{
|
||||
tmp |= m_bytes[i];
|
||||
if (++i == Bytes)
|
||||
break;
|
||||
tmp <<= 8;
|
||||
}
|
||||
return (T) tmp;
|
||||
#else
|
||||
return m_val;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#if PACKED_USE_ARRAY
|
||||
gdb_byte m_bytes[Bytes];
|
||||
#else
|
||||
T m_val : (Bytes * HOST_CHAR_BIT) ATTRIBUTE_PACKED;
|
||||
};
|
||||
#endif
|
||||
} ATTRIBUTE_GCC_STRUCT;
|
||||
|
||||
/* Add some comparisons between std::atomic<packed<T>> and T. We need
|
||||
this because the regular comparisons would require two implicit
|
||||
conversions to go from T to std::atomic<packed<T>>:
|
||||
/* Add some (non-atomic) comparisons between std::atomic<packed<T>>
|
||||
and packed<T> and T. We need this because even though
|
||||
std::atomic<T> doesn't define these operators, the relational
|
||||
expressions still work via implicit conversions. Thos wouldn't
|
||||
work when wrapped in packed without these operators, because we'd
|
||||
require two implicit conversions to go from T to packed<T> to
|
||||
std::atomic<packed<T>> (and back), and C++ only does one. */
|
||||
|
||||
T -> packed<T>
|
||||
packed<T> -> std::atomic<packed<T>>
|
||||
#define PACKED_ATOMIC_OP(OP) \
|
||||
template<typename T, size_t Bytes> \
|
||||
bool operator OP (const std::atomic<packed<T, Bytes>> &lhs, \
|
||||
const std::atomic<packed<T, Bytes>> &rhs) \
|
||||
{ \
|
||||
return lhs.load () OP rhs.load (); \
|
||||
} \
|
||||
\
|
||||
template<typename T, size_t Bytes> \
|
||||
bool operator OP (T lhs, const std::atomic<packed<T, Bytes>> &rhs) \
|
||||
{ \
|
||||
return lhs OP rhs.load (); \
|
||||
} \
|
||||
\
|
||||
template<typename T, size_t Bytes> \
|
||||
bool operator OP (const std::atomic<packed<T, Bytes>> &lhs, T rhs) \
|
||||
{ \
|
||||
return lhs.load () OP rhs; \
|
||||
} \
|
||||
\
|
||||
template<typename T, size_t Bytes> \
|
||||
bool operator OP (const std::atomic<packed<T, Bytes>> &lhs, \
|
||||
packed<T, Bytes> rhs) \
|
||||
{ \
|
||||
return lhs.load () OP rhs; \
|
||||
} \
|
||||
\
|
||||
template<typename T, size_t Bytes> \
|
||||
bool operator OP (packed<T, Bytes> lhs, \
|
||||
const std::atomic<packed<T, Bytes>> &rhs) \
|
||||
{ \
|
||||
return lhs OP rhs.load (); \
|
||||
}
|
||||
|
||||
and C++ only does one. */
|
||||
PACKED_ATOMIC_OP (==)
|
||||
PACKED_ATOMIC_OP (!=)
|
||||
PACKED_ATOMIC_OP (>)
|
||||
PACKED_ATOMIC_OP (<)
|
||||
PACKED_ATOMIC_OP (>=)
|
||||
PACKED_ATOMIC_OP (<=)
|
||||
|
||||
template<typename T, size_t Bytes>
|
||||
bool operator== (T lhs, const std::atomic<packed<T, Bytes>> &rhs)
|
||||
{
|
||||
return lhs == rhs.load ();
|
||||
}
|
||||
|
||||
template<typename T, size_t Bytes>
|
||||
bool operator== (const std::atomic<packed<T, Bytes>> &lhs, T rhs)
|
||||
{
|
||||
return lhs.load () == rhs;
|
||||
}
|
||||
|
||||
template<typename T, size_t Bytes>
|
||||
bool operator!= (T lhs, const std::atomic<packed<T, Bytes>> &rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename T, size_t Bytes>
|
||||
bool operator!= (const std::atomic<packed<T, Bytes>> &lhs, T rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
#undef PACKED_ATOMIC_OP
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user