/* Cache and manage the values of registers for GDB, the GNU debugger.
- Copyright (C) 1986-2020 Free Software Foundation, Inc.
+ Copyright (C) 1986-2021 Free Software Foundation, Inc.
This file is part of GDB.
#include "observable.h"
#include "regset.h"
#include <unordered_map>
+#include "cli/cli-cmds.h"
/*
* DATA STRUCTURE
/* Per-architecture object describing the layout of a register cache.
Computed once when the architecture is created. */
-struct gdbarch_data *regcache_descr_handle;
+static struct gdbarch_data *regcache_descr_handle;
struct regcache_descr
{
gdb_assert (gdbarch != NULL);
m_descr = regcache_descr (gdbarch);
+ /* We don't zero-initialize the M_REGISTERS array, as the bytes it contains
+ aren't meaningful as long as the corresponding register status is not
+ REG_VALID. */
if (has_pseudo)
{
- m_registers.reset (new gdb_byte[m_descr->sizeof_cooked_registers] ());
+ m_registers.reset (new gdb_byte[m_descr->sizeof_cooked_registers]);
m_register_status.reset
(new register_status[m_descr->nr_cooked_registers] ());
}
else
{
- m_registers.reset (new gdb_byte[m_descr->sizeof_raw_registers] ());
+ m_registers.reset (new gdb_byte[m_descr->sizeof_raw_registers]);
m_register_status.reset
(new register_status[gdbarch_num_regs (gdbarch)] ());
}
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. */
+/* Type holding regcaches for a given pid. */
-using target_ptid_regcache_map
- = std::unordered_map<process_stratum_target *, ptid_regcache_map>;
+using pid_ptid_regcache_map = std::unordered_map<int, ptid_regcache_map>;
+
+/* Type holding regcaches for a given target. */
+
+using target_pid_ptid_regcache_map
+ = std::unordered_map<process_stratum_target *, pid_ptid_regcache_map>;
/* Global structure containing the existing regcaches. */
recording if the register values have been changed (eg. by the
user). Therefore all registers must be written back to the
target when appropriate. */
-static target_ptid_regcache_map regcaches;
+static target_pid_ptid_regcache_map regcaches;
struct regcache *
get_thread_arch_aspace_regcache (process_stratum_target *target,
{
gdb_assert (target != nullptr);
- /* Find the ptid -> regcache map for this target. */
- auto &ptid_regc_map = regcaches[target];
+ /* Find the map for this target. */
+ pid_ptid_regcache_map &pid_ptid_regc_map = regcaches[target];
+
+ /* Find the map for this pid. */
+ ptid_regcache_map &ptid_regc_map = pid_ptid_regc_map[ptid.pid ()];
/* Check first if a regcache for this arch already exists. */
auto range = ptid_regc_map.equal_range (ptid);
regcache_thread_ptid_changed (process_stratum_target *target,
ptid_t old_ptid, ptid_t new_ptid)
{
- auto ptid_regc_map_it = regcaches.find (target);
+ /* Look up map for target. */
+ auto pid_ptid_regc_map_it = regcaches.find (target);
+ if (pid_ptid_regc_map_it == regcaches.end ())
+ return;
- if (ptid_regc_map_it == regcaches.end ())
+ /* Look up map for pid. */
+ pid_ptid_regcache_map &pid_ptid_regc_map = pid_ptid_regc_map_it->second;
+ auto ptid_regc_map_it = pid_ptid_regc_map.find (old_ptid.pid ());
+ if (ptid_regc_map_it == pid_ptid_regc_map.end ())
return;
- auto &ptid_regc_map = ptid_regc_map_it->second;
+ /* Update all regcaches belonging to old_ptid. */
+ ptid_regcache_map &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;)
{
/* Delete all the regcaches of all targets. */
regcaches.clear ();
}
+ else if (ptid.is_pid ())
+ {
+ /* Non-NULL target and pid ptid, delete all regcaches belonging
+ to this (TARGET, PID). */
+
+ /* Look up map for target. */
+ auto pid_ptid_regc_map_it = regcaches.find (target);
+ if (pid_ptid_regc_map_it != regcaches.end ())
+ {
+ pid_ptid_regcache_map &pid_ptid_regc_map
+ = pid_ptid_regc_map_it->second;
+
+ pid_ptid_regc_map.erase (ptid.pid ());
+ }
+ }
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 ())
+ to this (TARGET, PTID). */
+
+ /* Look up map for target. */
+ auto pid_ptid_regc_map_it = regcaches.find (target);
+ if (pid_ptid_regc_map_it != regcaches.end ())
{
- auto &ptid_regc_map = ptid_regc_map_it->second;
- ptid_regc_map.erase (ptid);
+ pid_ptid_regcache_map &pid_ptid_regc_map
+ = pid_ptid_regc_map_it->second;
+
+ /* Look up map for pid. */
+ auto ptid_regc_map_it
+ = pid_ptid_regc_map.find (ptid.pid ());
+ if (ptid_regc_map_it != pid_ptid_regc_map.end ())
+ {
+ ptid_regcache_map &ptid_regc_map
+ = ptid_regc_map_it->second;
+
+ ptid_regc_map.erase (ptid);
+ }
}
}
else
fprintf_unfiltered (gdb_stdlog, "\n");
}
+/* Implement 'maint flush register-cache' command. */
+
static void
reg_flush_command (const char *command, int from_tty)
{
regcaches_size ()
{
size_t size = 0;
- for (auto it = regcaches.begin (); it != regcaches.end (); ++it)
+
+ for (auto pid_ptid_regc_map_it = regcaches.cbegin ();
+ pid_ptid_regc_map_it != regcaches.cend ();
+ ++pid_ptid_regc_map_it)
{
- auto &ptid_regc_map = it->second;
- size += ptid_regc_map.size ();
+ const pid_ptid_regcache_map &pid_ptid_regc_map
+ = pid_ptid_regc_map_it->second;
+
+ for (auto ptid_regc_map_it = pid_ptid_regc_map.cbegin ();
+ ptid_regc_map_it != pid_ptid_regc_map.cend ();
+ ++ptid_regc_map_it)
+ {
+ const ptid_regcache_map &ptid_regc_map
+ = ptid_regc_map_it->second;
+
+ size += ptid_regc_map.size ();
+ }
}
return size;
}
+/* Return the count of regcaches for (TARGET, PTID) in REGCACHES. */
+
+static int
+regcache_count (process_stratum_target *target, ptid_t ptid)
+{
+ /* Look up map for target. */
+ auto pid_ptid_regc_map_it = regcaches.find (target);
+ if (pid_ptid_regc_map_it != regcaches.end ())
+ {
+ pid_ptid_regcache_map &pid_ptid_regc_map = pid_ptid_regc_map_it->second;
+
+ /* Look map for pid. */
+ auto ptid_regc_map_it = pid_ptid_regc_map.find (ptid.pid ());
+ if (ptid_regc_map_it != pid_ptid_regc_map.end ())
+ {
+ ptid_regcache_map &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;
+};
+
/* Wrapper around get_thread_arch_aspace_regcache that does some self checks. */
static void
-test_get_thread_arch_aspace_regcache (process_stratum_target *target,
- ptid_t ptid, struct gdbarch *gdbarch,
- address_space *aspace)
+get_thread_arch_aspace_regcache_and_check (process_stratum_target *target,
+ ptid_t ptid)
{
- struct regcache *regcache
- = get_thread_arch_aspace_regcache (target, ptid, gdbarch, aspace);
+ /* We currently only test with a single gdbarch. Any gdbarch will do, so use
+ the current inferior's gdbarch. Also use the current inferior's address
+ space. */
+ gdbarch *arch = current_inferior ()->gdbarch;
+ address_space *aspace = current_inferior ()->aspace;
+ regcache *regcache
+ = get_thread_arch_aspace_regcache (target, ptid, arch, aspace);
+
SELF_CHECK (regcache != NULL);
SELF_CHECK (regcache->target () == target);
SELF_CHECK (regcache->ptid () == ptid);
+ SELF_CHECK (regcache->arch () == arch);
SELF_CHECK (regcache->aspace () == aspace);
}
-static void
-regcaches_test ()
+/* The data that the regcaches selftests must hold onto for the duration of the
+ test. */
+
+struct regcache_test_data
{
- /* Ensure the regcaches container is empty at the start. */
- registers_changed ();
- SELF_CHECK (regcaches_size () == 0);
+ regcache_test_data ()
+ {
+ /* Ensure the regcaches container is empty at the start. */
+ registers_changed ();
+ }
- ptid_t ptid1 (1), ptid2 (2), ptid3 (3);
+ ~regcache_test_data ()
+ {
+ /* Make sure to leave the global regcaches container empty. */
+ registers_changed ();
+ }
test_target_ops test_target1;
test_target_ops test_target2;
+};
+
+using regcache_test_data_up = std::unique_ptr<regcache_test_data>;
+
+/* Set up a few regcaches from two different targets, for use in
+ regcache-management tests.
+
+ Return a pointer, because the `regcache_test_data` type is not moveable. */
+
+static regcache_test_data_up
+populate_regcaches_for_test ()
+{
+ regcache_test_data_up data (new regcache_test_data);
+ size_t expected_regcache_size = 0;
+
+ SELF_CHECK (regcaches_size () == 0);
+
+ /* Populate the regcache container with a few regcaches for the two test
+ targets. */
+ for (int pid : { 1, 2 })
+ {
+ for (long lwp : { 1, 2, 3 })
+ {
+ get_thread_arch_aspace_regcache_and_check
+ (&data->test_target1, ptid_t (pid, lwp));
+ expected_regcache_size++;
+ SELF_CHECK (regcaches_size () == expected_regcache_size);
+
+ get_thread_arch_aspace_regcache_and_check
+ (&data->test_target2, ptid_t (pid, lwp));
+ expected_regcache_size++;
+ SELF_CHECK (regcaches_size () == expected_regcache_size);
+ }
+ }
+
+ return data;
+}
+
+static void
+get_thread_arch_aspace_regcache_test ()
+{
+ /* populate_regcaches_for_test already tests most of the
+ get_thread_arch_aspace_regcache functionality. */
+ regcache_test_data_up data = populate_regcaches_for_test ();
+ size_t regcaches_size_before = regcaches_size ();
+
+ /* Test that getting an existing regcache doesn't create a new one. */
+ get_thread_arch_aspace_regcache_and_check (&data->test_target1, ptid_t (2, 2));
+ SELF_CHECK (regcaches_size () == regcaches_size_before);
+}
+
+ /* Test marking all regcaches of all targets as changed. */
+
+static void
+registers_changed_ptid_all_test ()
+{
+ regcache_test_data_up data = populate_regcaches_for_test ();
- /* Get regcache from (target1,ptid1), a new regcache is added to
- REGCACHES. */
- test_get_thread_arch_aspace_regcache (&test_target1, ptid1,
- target_gdbarch (),
- NULL);
- SELF_CHECK (regcaches_size () == 1);
-
- /* Get regcache from (target1,ptid2), a new regcache is added to
- REGCACHES. */
- test_get_thread_arch_aspace_regcache (&test_target1, ptid2,
- target_gdbarch (),
- NULL);
- SELF_CHECK (regcaches_size () == 2);
-
- /* Get regcache from (target1,ptid3), a new regcache is added to
- REGCACHES. */
- test_get_thread_arch_aspace_regcache (&test_target1, ptid3,
- target_gdbarch (),
- NULL);
- SELF_CHECK (regcaches_size () == 3);
-
- /* Get regcache from (target1,ptid2) again, nothing is added to
- REGCACHES. */
- test_get_thread_arch_aspace_regcache (&test_target1, ptid2,
- target_gdbarch (),
- NULL);
- SELF_CHECK (regcaches_size () == 3);
-
- /* Get regcache from (target2,ptid2), a new regcache is added to
- REGCACHES, since this time we're using a different target. */
- test_get_thread_arch_aspace_regcache (&test_target2, ptid2,
- target_gdbarch (),
- NULL);
- SELF_CHECK (regcaches_size () == 4);
-
- /* Mark that (target1,ptid2) changed. The regcache of (target1,
- ptid2) should be removed from REGCACHES. */
- registers_changed_ptid (&test_target1, ptid2);
- 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 (regcaches_size () == 3);
-
- /* Confirm that marking all regcaches of all targets as changed
- clears REGCACHES. */
registers_changed_ptid (nullptr, minus_one_ptid);
SELF_CHECK (regcaches_size () == 0);
+}
- /* Make sure to leave the global regcaches container empty. */
- registers_changed ();
+/* Test marking regcaches of a specific target as changed. */
+
+static void
+registers_changed_ptid_target_test ()
+{
+ regcache_test_data_up data = populate_regcaches_for_test ();
+
+ registers_changed_ptid (&data->test_target1, minus_one_ptid);
+ SELF_CHECK (regcaches_size () == 6);
+
+ /* Check that we deleted the regcache for the right target. */
+ SELF_CHECK (regcache_count (&data->test_target1, ptid_t (2, 2)) == 0);
+ SELF_CHECK (regcache_count (&data->test_target2, ptid_t (2, 2)) == 1);
+}
+
+/* Test marking regcaches of a specific (target, pid) as changed. */
+
+static void
+registers_changed_ptid_target_pid_test ()
+{
+ regcache_test_data_up data = populate_regcaches_for_test ();
+
+ registers_changed_ptid (&data->test_target1, ptid_t (2));
+ SELF_CHECK (regcaches_size () == 9);
+
+ /* Regcaches from target1 should not exist, while regcaches from target2
+ should exist. */
+ SELF_CHECK (regcache_count (&data->test_target1, ptid_t (2, 2)) == 0);
+ SELF_CHECK (regcache_count (&data->test_target2, ptid_t (2, 2)) == 1);
+}
+
+/* Test marking regcaches of a specific (target, ptid) as changed. */
+
+static void
+registers_changed_ptid_target_ptid_test ()
+{
+ regcache_test_data_up data = populate_regcaches_for_test ();
+
+ registers_changed_ptid (&data->test_target1, ptid_t (2, 2));
+ SELF_CHECK (regcaches_size () == 11);
+
+ /* Check that we deleted the regcache for the right target. */
+ SELF_CHECK (regcache_count (&data->test_target1, ptid_t (2, 2)) == 0);
+ SELF_CHECK (regcache_count (&data->test_target2, ptid_t (2, 2)) == 1);
}
class target_ops_no_register : public test_target_ops
{
/* Error out if debugging something, because we're going to push the
test target, which would pop any existing target. */
- if (current_top_target ()->stratum () >= process_stratum)
+ if (current_inferior ()->top_target ()->stratum () >= process_stratum)
error (_("target already pushed"));
/* Create a mock environment. A process_stratum target pushed. */
/* Push the process_stratum target so we can mock accessing
registers. */
- push_target (&mock_target);
+ current_inferior ()->push_target (&mock_target);
/* Pop it again on exit (return/exception). */
struct on_exit
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);
void
_initialize_regcache ()
{
+ struct cmd_list_element *c;
+
regcache_descr_handle
= gdbarch_data_register_post_init (init_regcache_descr);
- gdb::observers::target_changed.attach (regcache_observer_target_changed);
- gdb::observers::thread_ptid_changed.attach (regcache_thread_ptid_changed);
+ gdb::observers::target_changed.attach (regcache_observer_target_changed,
+ "regcache");
+ gdb::observers::thread_ptid_changed.attach (regcache_thread_ptid_changed,
+ "regcache");
- add_com ("flushregs", class_maintenance, reg_flush_command,
- _("Force gdb to flush its register cache (maintainer command)."));
+ cmd_list_element *maintenance_flush_register_cache_cmd
+ = add_cmd ("register-cache", class_maintenance, reg_flush_command,
+ _("Force gdb to flush its register and frame cache."),
+ &maintenanceflushlist);
+ c = add_com_alias ("flushregs", maintenance_flush_register_cache_cmd,
+ class_maintenance, 0);
+ deprecate_cmd (c, "maintenance flush register-cache");
#if GDB_SELF_TEST
- selftests::register_test ("regcaches", selftests::regcaches_test);
+ selftests::register_test ("get_thread_arch_aspace_regcache",
+ selftests::get_thread_arch_aspace_regcache_test);
+ selftests::register_test ("registers_changed_ptid_all",
+ selftests::registers_changed_ptid_all_test);
+ selftests::register_test ("registers_changed_ptid_target",
+ selftests::registers_changed_ptid_target_test);
+ selftests::register_test ("registers_changed_ptid_target_pid",
+ selftests::registers_changed_ptid_target_pid_test);
+ selftests::register_test ("registers_changed_ptid_target_ptid",
+ selftests::registers_changed_ptid_target_ptid_test);
selftests::register_test_foreach_arch ("regcache::cooked_read_test",
selftests::cooked_read_test);