Compare commits

...

5 Commits

Author SHA1 Message Date
Simon Marchi
fe0e47ac81 Two-level map
Change-Id: I008ddd9a1ef773a8d17e439d24d75d37bbf1a076
2020-08-05 15:40:21 -04:00
Simon Marchi
5672720dd1 gdb: change regcache list to be a map
One regcache object is created for each stopped thread and is stored in
the regcache::regcaches linked list.  Looking up a regcache for a given
thread is therefore in O(number of threads).  Stopping all threads then
becomes O((number of threads) ^ 2).  It becomes noticeable when
debugging thousands of threads, which is typical with GPU targets.  This
patch replaces the linked list with an std::unordered_multimap, indexed
by (target, ptid).

I originally designed it using an std::unordered_map with (target, ptid,
arch) as the key, because that's how lookups are done (in
get_thread_arch_aspace_regcache).  However, the registers_changed_ptid
function, also somewhat on the hot path (it is used when resuming
threads), needs to delete all regcaches associated to a given (target,
ptid) tuple.  Using (target, ptid) as a key allows to do this more
efficiently (see exception below).  If the key of the map was (target,
ptid, arch), we'd have to walk all items of the map.

The lookup (in get_thread_arch_aspace_regcache), walks over all existing
regcaches belonging to this (target, ptid), looking to find the one with
the right arch.  This is ok, as there will be very few regcaches for a
given key (typically one).  Lookups become faster when the number of
threads grows, compared to the linked list.  With a small number of
threads, it will probably be a bit slower to do a map lookup than to
walk a few linked list nodes, but I don't think it will be noticeable in
practice.

The function registers_changed_ptid deletes all regcaches related to a
given (target, ptid).  We must now handle the different cases
separately:

- NULL target and minus_one_ptid: we delete all the entries
- NULL target and non-minus_one_ptid: invalid (checked by assert)
- non-NULL target and non-minus_one_ptid: we delete all the entries
  associated to that tuple, this is done efficiently
- a non-NULL target and minus_one_ptid: we delete all the entries
  associated to that target, whatever the ptid.  This is the slightly
  annoying case, as we can't easily look up all items having this target
  in their key.  I implemented it by walking the list, which is not
  ideal.

The function regcache_thread_ptid_changed is called when a thread
changes ptid.  It is implemented efficiently using the map, although
that's not very important: it is not called often, mostly when creating
an inferior, on some specific platforms.

Note: In hash_target_ptid, I am combining hash values from std::hash by
summing them.  I don't think it's ideal, since std::hash is just the
identity function for base types.  But I don't know what would be better
to reduce the change of collisions.  If anybody has a better idea, I'd
be interested.

This patch is a tiny bit from ROCm GDB [1] we would like to merge
upstream.  Laurent Morichetti gave be these performance numbers:

The benchmark used is:

  time ./gdb --data-directory=data-directory /extra/lmoriche/hip/samples/0_Intro/bit_extract/bit_extract -ex "set pagination off" -ex "set breakpoint pending on" -ex "b bit_extract_kernel if \$_thread == 5" -ex run -ex c -batch

It measures the time it takes to continue from a conditional breakpoint with
2048 threads at that breakpoint, one of them reporting the breakpoint.

baseline:
real    0m10.227s
real    0m10.177s
real    0m10.362s

with patch:
real    0m8.356s
real    0m8.424s
real    0m8.494s

[1] https://github.com/ROCm-Developer-Tools/ROCgdb

gdb/ChangeLog:

	* regcache.c (struct target_ptid): New struct.
	(hash_target_ptid): New struct.
	(target_ptid_regcache_map): New type.
	(regcaches): Change type to target_ptid_regcache_map.
	(get_thread_arch_aspace_regcache): Update to regcaches' new
	type.
	(regcache_thread_ptid_changed): Likewise.
	(registers_changed_ptid): Likewise.
	(regcaches_size): Likewise.
	(regcaches_test): Update.
	(regcache_thread_ptid_changed): Update.
	* gdbsupport/ptid.h (hash_ptid): New struct.

Change-Id: Iabb0a1111707936ca111ddb13f3b09efa83d3402
2020-08-05 15:40:20 -04:00
Simon Marchi
2dd1cd011c gdb: pass target to thread_ptid_changed observable
I noticed what I think is a potential bug.  I did not observe it nor was
I able to reproduce it using actual debugging.  It's quite unlikely,
because it involves multi-target and ptid clashes.  I added selftests
that demonstrate it though.

The thread_ptid_changed observer says that thread with OLD_PTID now has
NEW_PTID.  Now, if for some reason we happen to have two targets
defining a thread with OLD_PTID, the observers don't know which thread
this is about.

regcache::regcache_thread_ptid_changed changes all regcaches with
OLD_PTID.  If there is a regcache for a thread with ptid OLD_PTID, but
that belongs to a different target, this regcache will be erroneously
changed.

Similarly, infrun_thread_ptid_changed updates inferior_ptid if
inferior_ptid matches OLD_PTID.  But if inferior_ptid currently refers
not to the thread is being changed, but to a thread with the same ptid
belonging to a different target, then inferior_ptid will erroneously be
changed.

This patch adds a `process_stratum_target *` parameter to the
`thread_ptid_changed` observable and makes the two observers use it.
Tests for both are added, which would fail if the corresponding fix
wasn't done.

gdb/ChangeLog:

	* observable.h (thread_ptid_changed): Add parameter
	`process_stratum_target *`.
	* infrun.c (infrun_thread_ptid_changed): Add parameter
	`process_stratum_target *` and use it.
	(selftests): New namespace.
	(infrun_thread_ptid_changed): New function.
	(_initialize_infrun): Register selftest.
	* regcache.c (regcache_thread_ptid_changed): Add parameter
	`process_stratum_target *` and use it.
	(regcache_thread_ptid_changed): New function.
	(_initialize_regcache): Register selftest.
	* thread.c (thread_change_ptid): Pass target to
	thread_ptid_changed observable.

Change-Id: I0599e61224b6d154a7b55088a894cb88298c3c71
2020-08-05 15:40:20 -04:00
Simon Marchi
7e17ef8ca4 gdb: move regcache::regcaches to regcache.c
I don't really understand why `regcache_thread_ptid_changed` is a static
method of `struct regcache` instead of being a static free function in
regcache.c.  And I don't understand why `current_regcache` is a static
member of `struct regcache` instead of being a static global in
regcache.c.  It's not wrong per-se, but there's no other place where we
do it like this in GDB (as far as I remember) and it just exposes things
unnecessarily in the .h.

Move them to be just static in regcache.c.  As a result,
registers_changed_ptid doesn't need to be friend of the regcache class
anymore.

Removing the include of forward_list in regcache.h showed that we were
missing an include for it in dwarf2/index-write.c, record-btrace.c and
sparc64-tdep.c.

gdb/ChangeLog:

	* regcache.h (class regcache): Remove friend
	registers_changed_ptid.
	<regcache_thread_ptid_changed>: Remove.
	<regcaches>: Remove.
	* regcache.c (regcache::regcaches): Rename to...
	(regcaches): ... this.  Make static.
	(get_thread_arch_aspace_regcache): Update.
	(regcache::regcache_thread_ptid_changed): Rename to...
	(regcache_thread_ptid_changed): ... this.  Update.
	(class regcache_access): Remove.
	(regcaches_test): Update.
	(_initialize_regcache): Update.
	* sparc64-tdep.c, dwarf2/index-write.c, record-btrace.c: Include
	<forward_list>.

Change-Id: Iabc25759848010cfbb7ee7e27f60eaca17d61c12
2020-08-05 15:40:20 -04:00
Simon Marchi
5c879edf04 gdb: rename regcache::current_regcache to regcache::regcaches
The name `current_regcache` for the list of currently-existing regcaches
sounds wrong.  The name is singular, but it holds multiple regcaches, so
it could at least be `current_regcaches`.

But in other places in GDB, "current" usually means "the object we are
working with right now".  For example, we swap the "current thread" when
we want to operate on a given thread.  This is not the case here, this
variable just holds all regcaches that exist at any given time, not "the
regcache we are working with right now".

So, I think calling it `regcaches` is better.  I also considered
`regcache_list`, but a subsequent patch will make it a map and not a
list, so it would sound wrong again.  `regcaches` sounds right for any
collection of regcache, whatever the type.

Rename a few other things that were related to this `current_regcache`
field.  Note that there is a `get_current_regcache` function, which
returns the regcache of the current thread.  That one is fine, because
it returns the regcache for the current thread.

gdb/ChangeLog:

	* regcache.h (class regcache) <current_regcache>: Rename to...
	<regcaches>: ... this.  Move doc here.
	* regcache.c (regcache::current_regcache) Rename to...
	(regcache::regcaches): ... this.  Move doc to header.
	(get_thread_arch_aspace_regcache): Update.
	(regcache::regcache_thread_ptid_changed): Update.
	(registers_changed_ptid): Update.
	(class regcache_access) <current_regcache_size>: Rename to...
	<regcaches_size>: ... this.
	(current_regcache_test): Rename to...
	(regcaches_test): ... this.
	(_initialize_regcache): Update.

Change-Id: I87de67154f5fe17a1f6aee7c4f2036647ee27b99
2020-08-05 15:40:20 -04:00
9 changed files with 268 additions and 75 deletions

View File

@@ -41,6 +41,7 @@
#include <algorithm>
#include <cmath>
#include <forward_list>
#include <set>
#include <unordered_map>
#include <unordered_set>

View File

@@ -67,6 +67,9 @@
#include "gdbsupport/gdb_select.h"
#include <unordered_map>
#include "async-event.h"
#include "gdbsupport/selftest.h"
#include "scoped-mock-context.h"
#include "test-target.h"
/* Prototypes for local functions */
@@ -2068,9 +2071,11 @@ start_step_over (void)
/* Update global variables holding ptids to hold NEW_PTID if they were
holding OLD_PTID. */
static void
infrun_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid)
infrun_thread_ptid_changed (process_stratum_target *target,
ptid_t old_ptid, ptid_t new_ptid)
{
if (inferior_ptid == old_ptid)
if (inferior_ptid == old_ptid
&& current_inferior ()->process_target () == target)
inferior_ptid = new_ptid;
}
@@ -9455,6 +9460,70 @@ infrun_async_inferior_event_handler (gdb_client_data data)
inferior_event_handler (INF_REG_EVENT);
}
namespace selftests
{
/* Verify that when two threads with the same ptid exist (from two different
targets) and one of them changes ptid, we only update inferior_ptid if
it is appropriate. */
static void
infrun_thread_ptid_changed ()
{
gdbarch *arch = current_inferior ()->gdbarch;
/* The thread which inferior_ptid represents changes ptid. */
{
scoped_restore_current_pspace_and_thread restore;
scoped_mock_context<test_target_ops> target1 (arch);
scoped_mock_context<test_target_ops> target2 (arch);
target2.mock_inferior.next = &target1.mock_inferior;
ptid_t old_ptid (111, 222);
ptid_t new_ptid (111, 333);
target1.mock_inferior.pid = old_ptid.pid ();
target1.mock_thread.ptid = old_ptid;
target2.mock_inferior.pid = old_ptid.pid ();
target2.mock_thread.ptid = old_ptid;
auto restore_inferior_ptid = make_scoped_restore (&inferior_ptid, old_ptid);
set_current_inferior (&target1.mock_inferior);
thread_change_ptid (&target1.mock_target, old_ptid, new_ptid);
gdb_assert (inferior_ptid == new_ptid);
}
/* A thread with the same ptid as inferior_ptid, but from another target,
changes ptid. */
{
scoped_restore_current_pspace_and_thread restore;
scoped_mock_context<test_target_ops> target1 (arch);
scoped_mock_context<test_target_ops> target2 (arch);
target2.mock_inferior.next = &target1.mock_inferior;
ptid_t old_ptid (111, 222);
ptid_t new_ptid (111, 333);
target1.mock_inferior.pid = old_ptid.pid ();
target1.mock_thread.ptid = old_ptid;
target2.mock_inferior.pid = old_ptid.pid ();
target2.mock_thread.ptid = old_ptid;
auto restore_inferior_ptid = make_scoped_restore (&inferior_ptid, old_ptid);
set_current_inferior (&target2.mock_inferior);
thread_change_ptid (&target1.mock_target, old_ptid, new_ptid);
gdb_assert (inferior_ptid == old_ptid);
}
}
} /* namespace selftests */
void _initialize_infrun ();
void
_initialize_infrun ()
@@ -9756,4 +9825,9 @@ or signalled."),
show_observer_mode,
&setlist,
&showlist);
#if GDB_SELF_TEST
selftests::register_test ("infrun_thread_ptid_changed",
selftests::infrun_thread_ptid_changed);
#endif
}

View File

@@ -27,6 +27,7 @@ struct so_list;
struct objfile;
struct thread_info;
struct inferior;
struct process_stratum_target;
struct trace_state_variable;
namespace gdb
@@ -165,8 +166,9 @@ extern observable<struct gdbarch */* newarch */> architecture_changed;
/* The thread's ptid has changed. The OLD_PTID parameter specifies
the old value, and NEW_PTID specifies the new value. */
extern observable<ptid_t /* old_ptid */, ptid_t /* new_ptid */>
thread_ptid_changed;
extern observable<process_stratum_target * /* target */,
ptid_t /* old_ptid */, ptid_t /* new_ptid */>
thread_ptid_changed;
/* The inferior INF has been added to the list of inferiors. At
this point, it might not be associated with any process. */

View File

@@ -43,6 +43,7 @@
#include "gdbarch.h"
#include "cli/cli-style.h"
#include "async-event.h"
#include <forward_list>
static const target_info record_btrace_target_info = {
"record-btrace",

View File

@@ -29,7 +29,7 @@
#include "reggroups.h"
#include "observable.h"
#include "regset.h"
#include <forward_list>
#include <unordered_map>
/*
* DATA STRUCTURE
@@ -313,31 +313,48 @@ reg_buffer::assert_regnum (int regnum) const
gdb_assert (regnum < gdbarch_num_regs (arch ()));
}
/* Global structure containing the current regcache. */
/* Type to map a ptid to a list of regcaches (one thread may have multiple
regcaches, associated to different gdbarches). */
using ptid_regcache_map
= std::unordered_multimap<ptid_t, regcache_up, hash_ptid>;
/* Type to map a target to a ptid_regcache_map, holding the regcaches for the
threads defined by that target. */
using target_ptid_regcache_map
= std::unordered_map<process_stratum_target *, ptid_regcache_map>;
/* Global structure containing the existing regcaches. */
/* NOTE: this is a write-through cache. There is no "dirty" bit for
recording if the register values have been changed (eg. by the
user). Therefore all registers must be written back to the
target when appropriate. */
std::forward_list<regcache *> regcache::current_regcache;
static target_ptid_regcache_map regcaches;
struct regcache *
get_thread_arch_aspace_regcache (process_stratum_target *target,
ptid_t ptid, struct gdbarch *gdbarch,
ptid_t ptid, gdbarch *arch,
struct address_space *aspace)
{
gdb_assert (target != nullptr);
for (const auto &regcache : regcache::current_regcache)
if (regcache->target () == target
&& regcache->ptid () == ptid
&& regcache->arch () == gdbarch)
return regcache;
/* Find the ptid -> regcache map for this target. */
auto &ptid_regc_map = regcaches[target];
regcache *new_regcache = new regcache (target, gdbarch, aspace);
/* Check first if a regcache for this arch already exists. */
auto range = ptid_regc_map.equal_range (ptid);
for (auto it = range.first; it != range.second; it++)
{
if (it->second->arch () == arch)
return it->second.get ();
}
regcache::current_regcache.push_front (new_regcache);
/* It does not exist, create it. */
regcache *new_regcache (new regcache (target, arch, aspace));
new_regcache->set_ptid (ptid);
ptid_regc_map.insert (std::make_pair (ptid, new_regcache));
return new_regcache;
}
@@ -412,15 +429,27 @@ regcache_observer_target_changed (struct target_ops *target)
registers_changed ();
}
/* Update global variables old ptids to hold NEW_PTID if they were
holding OLD_PTID. */
void
regcache::regcache_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid)
/* Update regcaches related to OLD_PTID to now use NEW_PTID. */
static void
regcache_thread_ptid_changed (process_stratum_target *target,
ptid_t old_ptid, ptid_t new_ptid)
{
for (auto &regcache : regcache::current_regcache)
auto ptid_regc_map_it = regcaches.find (target);
if (ptid_regc_map_it == regcaches.end ())
return;
auto &ptid_regc_map = ptid_regc_map_it->second;
auto range = ptid_regc_map.equal_range (old_ptid);
for (auto it = range.first; it != range.second;)
{
if (regcache->ptid () == old_ptid)
regcache->set_ptid (new_ptid);
regcache_up rc = std::move (it->second);
rc->set_ptid (new_ptid);
/* Remove old before inserting new, to avoid rehashing,
which would invalidate iterators. */
it = ptid_regc_map.erase (it);
ptid_regc_map.insert (std::make_pair (new_ptid, std::move (rc)));
}
}
@@ -438,20 +467,37 @@ regcache::regcache_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid)
void
registers_changed_ptid (process_stratum_target *target, ptid_t ptid)
{
for (auto oit = regcache::current_regcache.before_begin (),
it = std::next (oit);
it != regcache::current_regcache.end ();
)
if (target == nullptr)
{
struct regcache *regcache = *it;
if ((target == nullptr || regcache->target () == target)
&& regcache->ptid ().matches (ptid))
/* Since there can be ptid clashes between targets, it's not valid to
pass a ptid without saying to which target it belongs. */
gdb_assert (ptid == minus_one_ptid);
/* Delete all the regcaches. */
for (auto &pair : regcaches)
pair.second.clear ();
}
else if (ptid != minus_one_ptid)
{
/* Non-NULL target and non-minus_one_ptid, delete all regcaches belonging
to this (TARGET, PTID). */
auto ptid_regc_map_it = regcaches.find (target);
if (ptid_regc_map_it != regcaches.end ())
{
delete regcache;
it = regcache::current_regcache.erase_after (oit);
auto &ptid_regc_map = ptid_regc_map_it->second;
ptid_regc_map.erase (ptid);
}
}
else
{
/* Non-NULL target and minus_one_ptid, delete all regcaches
associated to this target. */
auto ptid_regc_map_it = regcaches.find (target);
if (ptid_regc_map_it != regcaches.end ())
{
auto &ptid_regc_map = ptid_regc_map_it->second;
ptid_regc_map.clear ();
}
else
oit = it++;
}
if ((target == nullptr || current_thread_target == target)
@@ -1433,19 +1479,17 @@ register_dump::dump (ui_file *file)
namespace selftests {
class regcache_access : public regcache
static size_t
regcaches_size ()
{
public:
/* Return the number of elements in current_regcache. */
static size_t
current_regcache_size ()
{
return std::distance (regcache::current_regcache.begin (),
regcache::current_regcache.end ());
}
};
size_t size = 0;
for (auto it = regcaches.begin (); it != regcaches.end (); ++it)
{
auto &ptid_regc_map = it->second;
size += ptid_regc_map.size ();
}
return size;
}
/* Wrapper around get_thread_arch_aspace_regcache that does some self checks. */
@@ -1463,10 +1507,10 @@ test_get_thread_arch_aspace_regcache (process_stratum_target *target,
}
static void
current_regcache_test (void)
regcaches_test (void)
{
/* It is empty at the start. */
SELF_CHECK (regcache_access::current_regcache_size () == 0);
SELF_CHECK (regcaches_size () == 0);
ptid_t ptid1 (1), ptid2 (2), ptid3 (3);
@@ -1474,57 +1518,56 @@ current_regcache_test (void)
test_target_ops test_target2;
/* Get regcache from (target1,ptid1), a new regcache is added to
current_regcache. */
REGCACHES. */
test_get_thread_arch_aspace_regcache (&test_target1, ptid1,
target_gdbarch (),
NULL);
SELF_CHECK (regcache_access::current_regcache_size () == 1);
SELF_CHECK (regcaches_size () == 1);
/* Get regcache from (target1,ptid2), a new regcache is added to
current_regcache. */
REGCACHES. */
test_get_thread_arch_aspace_regcache (&test_target1, ptid2,
target_gdbarch (),
NULL);
SELF_CHECK (regcache_access::current_regcache_size () == 2);
SELF_CHECK (regcaches_size () == 2);
/* Get regcache from (target1,ptid3), a new regcache is added to
current_regcache. */
REGCACHES. */
test_get_thread_arch_aspace_regcache (&test_target1, ptid3,
target_gdbarch (),
NULL);
SELF_CHECK (regcache_access::current_regcache_size () == 3);
SELF_CHECK (regcaches_size () == 3);
/* Get regcache from (target1,ptid2) again, nothing is added to
current_regcache. */
REGCACHES. */
test_get_thread_arch_aspace_regcache (&test_target1, ptid2,
target_gdbarch (),
NULL);
SELF_CHECK (regcache_access::current_regcache_size () == 3);
SELF_CHECK (regcaches_size () == 3);
/* Get regcache from (target2,ptid2), a new regcache is added to
current_regcache, since this time we're using a differen
target. */
REGCACHES, since this time we're using a different target. */
test_get_thread_arch_aspace_regcache (&test_target2, ptid2,
target_gdbarch (),
NULL);
SELF_CHECK (regcache_access::current_regcache_size () == 4);
SELF_CHECK (regcaches_size () == 4);
/* Mark that (target1,ptid2) changed. The regcache of (target1,
ptid2) should be removed from current_regcache. */
ptid2) should be removed from REGCACHES. */
registers_changed_ptid (&test_target1, ptid2);
SELF_CHECK (regcache_access::current_regcache_size () == 3);
SELF_CHECK (regcaches_size () == 3);
/* Get the regcache from (target2,ptid2) again, confirming the
registers_changed_ptid call above did not delete it. */
test_get_thread_arch_aspace_regcache (&test_target2, ptid2,
target_gdbarch (),
NULL);
SELF_CHECK (regcache_access::current_regcache_size () == 3);
SELF_CHECK (regcaches_size () == 3);
/* Confirm that marking all regcaches of all targets as changed
clears current_regcache. */
clears REGCACHES. */
registers_changed_ptid (nullptr, minus_one_ptid);
SELF_CHECK (regcache_access::current_regcache_size () == 0);
SELF_CHECK (regcaches_size () == 0);
}
class target_ops_no_register : public test_target_ops
@@ -1828,6 +1871,66 @@ cooked_write_test (struct gdbarch *gdbarch)
}
}
/* Verify that when two threads with the same ptid exist (from two different
targets) and one of them changes ptid, we only update the appropriate
regcaches. */
static void
regcache_thread_ptid_changed ()
{
/* Any arch will do. */
gdbarch *arch = current_inferior ()->gdbarch;
/* Prepare two targets with one thread each, with the same ptid. */
scoped_mock_context<test_target_ops> target1 (arch);
scoped_mock_context<test_target_ops> target2 (arch);
target2.mock_inferior.next = &target1.mock_inferior;
ptid_t old_ptid (111, 222);
ptid_t new_ptid (111, 333);
target1.mock_inferior.pid = old_ptid.pid ();
target1.mock_thread.ptid = old_ptid;
target2.mock_inferior.pid = old_ptid.pid ();
target2.mock_thread.ptid = old_ptid;
gdb_assert (regcaches.empty ());
/* Populate the regcaches container. */
get_thread_arch_aspace_regcache (&target1.mock_target, old_ptid, arch,
nullptr);
get_thread_arch_aspace_regcache (&target2.mock_target, old_ptid, arch,
nullptr);
/* Return the count of regcaches for (TARGET, PTID) in REGCACHES. */
auto regcache_count = [] (process_stratum_target *target, ptid_t ptid)
-> int
{
auto ptid_regc_map_it = regcaches.find (target);
if (ptid_regc_map_it != regcaches.end ())
{
auto &ptid_regc_map = ptid_regc_map_it->second;
auto range = ptid_regc_map.equal_range (ptid);
return std::distance (range.first, range.second);
}
return 0;
};
gdb_assert (regcaches.size () == 2);
gdb_assert (regcache_count (&target1.mock_target, old_ptid) == 1);
gdb_assert (regcache_count (&target1.mock_target, new_ptid) == 0);
gdb_assert (regcache_count (&target2.mock_target, old_ptid) == 1);
gdb_assert (regcache_count (&target2.mock_target, new_ptid) == 0);
thread_change_ptid (&target1.mock_target, old_ptid, new_ptid);
gdb_assert (regcaches.size () == 2);
gdb_assert (regcache_count (&target1.mock_target, old_ptid) == 0);
gdb_assert (regcache_count (&target1.mock_target, new_ptid) == 1);
gdb_assert (regcache_count (&target2.mock_target, old_ptid) == 1);
gdb_assert (regcache_count (&target2.mock_target, new_ptid) == 0);
}
} // namespace selftests
#endif /* GDB_SELF_TEST */
@@ -1839,18 +1942,19 @@ _initialize_regcache ()
= gdbarch_data_register_post_init (init_regcache_descr);
gdb::observers::target_changed.attach (regcache_observer_target_changed);
gdb::observers::thread_ptid_changed.attach
(regcache::regcache_thread_ptid_changed);
gdb::observers::thread_ptid_changed.attach (regcache_thread_ptid_changed);
add_com ("flushregs", class_maintenance, reg_flush_command,
_("Force gdb to flush its register cache (maintainer command)."));
#if GDB_SELF_TEST
selftests::register_test ("current_regcache", selftests::current_regcache_test);
selftests::register_test ("regcaches", selftests::regcaches_test);
selftests::register_test_foreach_arch ("regcache::cooked_read_test",
selftests::cooked_read_test);
selftests::register_test_foreach_arch ("regcache::cooked_write_test",
selftests::cooked_write_test);
selftests::register_test ("regcache_thread_ptid_changed",
selftests::regcache_thread_ptid_changed);
#endif
}

View File

@@ -22,7 +22,6 @@
#include "gdbsupport/common-regcache.h"
#include "gdbsupport/function-view.h"
#include <forward_list>
struct regcache;
struct regset;
@@ -397,13 +396,10 @@ public:
debug. */
void debug_print_register (const char *func, int regno);
static void regcache_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid);
protected:
regcache (process_stratum_target *target, gdbarch *gdbarch,
const address_space *aspace);
static std::forward_list<regcache *> current_regcache;
private:
/* Helper function for transfer_regset. Copies across a single register. */
@@ -437,11 +433,10 @@ private:
get_thread_arch_aspace_regcache (process_stratum_target *target, ptid_t ptid,
struct gdbarch *gdbarch,
struct address_space *aspace);
friend void
registers_changed_ptid (process_stratum_target *target, ptid_t ptid);
};
using regcache_up = std::unique_ptr<regcache>;
class readonly_detached_regcache : public readable_regcache
{
public:

View File

@@ -33,8 +33,8 @@
#include "target-descriptions.h"
#include "target.h"
#include "value.h"
#include "sparc64-tdep.h"
#include <forward_list>
/* This file implements the SPARC 64-bit ABI as defined by the
section "Low-Level System Information" of the SPARC Compliance

View File

@@ -775,7 +775,7 @@ thread_change_ptid (process_stratum_target *targ,
tp = find_thread_ptid (inf, old_ptid);
tp->ptid = new_ptid;
gdb::observers::thread_ptid_changed.notify (old_ptid, new_ptid);
gdb::observers::thread_ptid_changed.notify (targ, old_ptid, new_ptid);
}
/* See gdbthread.h. */

View File

@@ -32,6 +32,8 @@
thread_stratum target that might want to sit on top.
*/
#include <functional>
class ptid_t
{
public:
@@ -143,6 +145,20 @@ private:
long m_tid;
};
/* Functor to hash a ptid. */
struct hash_ptid
{
size_t operator() (const ptid_t &ptid) const
{
std::hash<long> long_hash;
return (long_hash (ptid.pid ())
+ long_hash (ptid.lwp ())
+ long_hash (ptid.tid ()));
}
};
/* The null or zero ptid, often used to indicate no process. */
extern const ptid_t null_ptid;