ODR warning for "enum string_repr_result"
[binutils-gdb.git] / gdb / regcache.c
index d9af4539ab9aff3eb8eca4e405100230fc6ec6c2..037659ef8fae0d916d0d89132ca05fbef12f0440 100644 (file)
@@ -1,6 +1,6 @@
 /* Cache and manage the values of registers for GDB, the GNU debugger.
 
-   Copyright (C) 1986-2020 Free Software Foundation, Inc.
+   Copyright (C) 1986-2022 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -30,6 +30,7 @@
 #include "observable.h"
 #include "regset.h"
 #include <unordered_map>
+#include "cli/cli-cmds.h"
 
 /*
  * DATA STRUCTURE
@@ -40,7 +41,7 @@
 /* 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
 {
@@ -183,15 +184,18 @@ reg_buffer::reg_buffer (gdbarch *gdbarch, bool has_pseudo)
   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)] ());
     }
@@ -319,11 +323,14 @@ reg_buffer::assert_regnum (int regnum) const
 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.  */
 
@@ -331,7 +338,7 @@ using target_ptid_regcache_map
    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,
@@ -340,8 +347,11 @@ 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);
@@ -436,12 +446,19 @@ static void
 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;)
     {
@@ -478,15 +495,43 @@ registers_changed_ptid (process_stratum_target *target, ptid_t ptid)
       /* 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
@@ -575,15 +620,12 @@ template<typename T, typename>
 enum register_status
 readable_regcache::raw_read (int regnum, T *val)
 {
-  gdb_byte *buf;
-  enum register_status status;
-
   assert_regnum (regnum);
-  buf = (gdb_byte *) alloca (m_descr->sizeof_register[regnum]);
-  status = raw_read (regnum, buf);
+  size_t len = m_descr->sizeof_register[regnum];
+  gdb_byte *buf = (gdb_byte *) alloca (len);
+  register_status status = raw_read (regnum, buf);
   if (status == REG_VALID)
-    *val = extract_integer<T> (buf,
-                              m_descr->sizeof_register[regnum],
+    *val = extract_integer<T> ({buf, len},
                               gdbarch_byte_order (m_descr->gdbarch));
   else
     *val = 0;
@@ -667,7 +709,7 @@ readable_regcache::cooked_read (int regnum, gdb_byte *buf)
       computed = gdbarch_pseudo_register_read_value (m_descr->gdbarch,
                                                     this, regnum);
       if (value_entirely_available (computed))
-       memcpy (buf, value_contents_raw (computed),
+       memcpy (buf, value_contents_raw (computed).data (),
                m_descr->sizeof_register[regnum]);
       else
        {
@@ -704,7 +746,7 @@ readable_regcache::cooked_read_value (int regnum)
         direction than in the other one, even though the value-based
         API is preferred.  */
       if (cooked_read (regnum,
-                      value_contents_raw (result)) == REG_UNAVAILABLE)
+                      value_contents_raw (result).data ()) == REG_UNAVAILABLE)
        mark_value_bytes_unavailable (result, 0,
                                      TYPE_LENGTH (value_type (result)));
 
@@ -727,14 +769,12 @@ template<typename T, typename>
 enum register_status
 readable_regcache::cooked_read (int regnum, T *val)
 {
-  enum register_status status;
-  gdb_byte *buf;
-
   gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers);
-  buf = (gdb_byte *) alloca (m_descr->sizeof_register[regnum]);
-  status = cooked_read (regnum, buf);
+  size_t len = m_descr->sizeof_register[regnum];
+  gdb_byte *buf = (gdb_byte *) alloca (len);
+  register_status status = cooked_read (regnum, buf);
   if (status == REG_VALID)
-    *val = extract_integer<T> (buf, m_descr->sizeof_register[regnum],
+    *val = extract_integer<T> ({buf, len},
                               gdbarch_byte_order (m_descr->gdbarch));
   else
     *val = 0;
@@ -1124,7 +1164,12 @@ regcache::transfer_regset_register (struct regcache *out_regcache, int regnum,
        memset (out_buf + offs + reg_size, 0, slot_size - reg_size);
     }
   else if (in_buf != nullptr)
-    out_regcache->raw_supply_part (regnum, 0, reg_size, in_buf + offs);
+    {
+      /* Zero-extend the register value if the slot is smaller than the register.  */
+      if (slot_size < register_size (gdbarch, regnum))
+       out_regcache->raw_supply_zeroed (regnum);
+      out_regcache->raw_supply_part (regnum, 0, reg_size, in_buf + offs);
+    }
   else
     {
       /* Invalidate the register.  */
@@ -1219,6 +1264,33 @@ regcache::collect_regset (const struct regset *regset,
   transfer_regset (regset, nullptr, regnum, nullptr, (gdb_byte *) buf, size);
 }
 
+/* See regcache.h  */
+
+bool
+regcache_map_supplies (const struct regcache_map_entry *map, int regnum,
+                      struct gdbarch *gdbarch, size_t size)
+{
+  int offs = 0, count;
+
+  for (; (count = map->count) != 0; map++)
+    {
+      int regno = map->regno;
+      int slot_size = map->size;
+
+      if (slot_size == 0 && regno != REGCACHE_MAP_SKIP)
+       slot_size = register_size (gdbarch, regno);
+
+      if (regno != REGCACHE_MAP_SKIP && regnum >= regno
+         && regnum < regno + count)
+       return offs + (regnum - regno + 1) * slot_size <= size;
+
+      offs += count * slot_size;
+      if (offs >= size)
+       return false;
+    }
+  return false;
+}
+
 /* See gdbsupport/common-regcache.h.  */
 
 bool
@@ -1311,43 +1383,45 @@ regcache::debug_print_register (const char *func,  int regno)
 {
   struct gdbarch *gdbarch = arch ();
 
-  fprintf_unfiltered (gdb_stdlog, "%s ", func);
+  gdb_printf (gdb_stdlog, "%s ", func);
   if (regno >= 0 && regno < gdbarch_num_regs (gdbarch)
       && gdbarch_register_name (gdbarch, regno) != NULL
       && gdbarch_register_name (gdbarch, regno)[0] != '\0')
-    fprintf_unfiltered (gdb_stdlog, "(%s)",
-                       gdbarch_register_name (gdbarch, regno));
+    gdb_printf (gdb_stdlog, "(%s)",
+               gdbarch_register_name (gdbarch, regno));
   else
-    fprintf_unfiltered (gdb_stdlog, "(%d)", regno);
+    gdb_printf (gdb_stdlog, "(%d)", regno);
   if (regno >= 0 && regno < gdbarch_num_regs (gdbarch))
     {
       enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
       int size = register_size (gdbarch, regno);
       gdb_byte *buf = register_buffer (regno);
 
-      fprintf_unfiltered (gdb_stdlog, " = ");
+      gdb_printf (gdb_stdlog, " = ");
       for (int i = 0; i < size; i++)
        {
-         fprintf_unfiltered (gdb_stdlog, "%02x", buf[i]);
+         gdb_printf (gdb_stdlog, "%02x", buf[i]);
        }
       if (size <= sizeof (LONGEST))
        {
          ULONGEST val = extract_unsigned_integer (buf, size, byte_order);
 
-         fprintf_unfiltered (gdb_stdlog, " %s %s",
-                             core_addr_to_string_nz (val), plongest (val));
+         gdb_printf (gdb_stdlog, " %s %s",
+                     core_addr_to_string_nz (val), plongest (val));
        }
     }
-  fprintf_unfiltered (gdb_stdlog, "\n");
+  gdb_printf (gdb_stdlog, "\n");
 }
 
+/* Implement 'maint flush register-cache' command.  */
+
 static void
 reg_flush_command (const char *command, int from_tty)
 {
   /* Force-flush the register cache.  */
   registers_changed ();
   if (from_tty)
-    printf_filtered (_("Register cache flushed.\n"));
+    gdb_printf (_("Register cache flushed.\n"));
 }
 
 void
@@ -1367,7 +1441,7 @@ register_dump::dump (ui_file *file)
     {
       /* Name.  */
       if (regnum < 0)
-       fprintf_unfiltered (file, " %-10s", "Name");
+       gdb_printf (file, " %-10s", "Name");
       else
        {
          const char *p = gdbarch_register_name (m_gdbarch, regnum);
@@ -1376,31 +1450,31 @@ register_dump::dump (ui_file *file)
            p = "";
          else if (p[0] == '\0')
            p = "''";
-         fprintf_unfiltered (file, " %-10s", p);
+         gdb_printf (file, " %-10s", p);
        }
 
       /* Number.  */
       if (regnum < 0)
-       fprintf_unfiltered (file, " %4s", "Nr");
+       gdb_printf (file, " %4s", "Nr");
       else
-       fprintf_unfiltered (file, " %4d", regnum);
+       gdb_printf (file, " %4d", regnum);
 
       /* Relative number.  */
       if (regnum < 0)
-       fprintf_unfiltered (file, " %4s", "Rel");
+       gdb_printf (file, " %4s", "Rel");
       else if (regnum < gdbarch_num_regs (m_gdbarch))
-       fprintf_unfiltered (file, " %4d", regnum);
+       gdb_printf (file, " %4d", regnum);
       else
-       fprintf_unfiltered (file, " %4d",
-                           (regnum - gdbarch_num_regs (m_gdbarch)));
+       gdb_printf (file, " %4d",
+                   (regnum - gdbarch_num_regs (m_gdbarch)));
 
       /* Offset.  */
       if (regnum < 0)
-       fprintf_unfiltered (file, " %6s  ", "Offset");
+       gdb_printf (file, " %6s  ", "Offset");
       else
        {
-         fprintf_unfiltered (file, " %6ld",
-                             descr->register_offset[regnum]);
+         gdb_printf (file, " %6ld",
+                     descr->register_offset[regnum]);
          if (register_offset != descr->register_offset[regnum]
              || (regnum > 0
                  && (descr->register_offset[regnum]
@@ -1410,19 +1484,19 @@ register_dump::dump (ui_file *file)
            {
              if (!footnote_register_offset)
                footnote_register_offset = ++footnote_nr;
-             fprintf_unfiltered (file, "*%d", footnote_register_offset);
+             gdb_printf (file, "*%d", footnote_register_offset);
            }
          else
-           fprintf_unfiltered (file, "  ");
+           gdb_printf (file, "  ");
          register_offset = (descr->register_offset[regnum]
                             + descr->sizeof_register[regnum]);
        }
 
       /* Size.  */
       if (regnum < 0)
-       fprintf_unfiltered (file, " %5s ", "Size");
+       gdb_printf (file, " %5s ", "Size");
       else
-       fprintf_unfiltered (file, " %5ld", descr->sizeof_register[regnum]);
+       gdb_printf (file, " %5ld", descr->sizeof_register[regnum]);
 
       /* Type.  */
       {
@@ -1448,24 +1522,24 @@ register_dump::dump (ui_file *file)
            if (startswith (t, blt))
              t += strlen (blt);
          }
-       fprintf_unfiltered (file, " %-15s", t);
+       gdb_printf (file, " %-15s", t);
       }
 
       /* Leading space always present.  */
-      fprintf_unfiltered (file, " ");
+      gdb_printf (file, " ");
 
       dump_reg (file, regnum);
 
-      fprintf_unfiltered (file, "\n");
+      gdb_printf (file, "\n");
     }
 
   if (footnote_register_offset)
-    fprintf_unfiltered (file, "*%d: Inconsistent register offsets.\n",
-                       footnote_register_offset);
+    gdb_printf (file, "*%d: Inconsistent register offsets.\n",
+               footnote_register_offset);
   if (footnote_register_type_name_null)
-    fprintf_unfiltered (file,
-                       "*%d: Register type's name NULL.\n",
-                       footnote_register_type_name_null);
+    gdb_printf (file,
+               "*%d: Register type's name NULL.\n",
+               footnote_register_type_name_null);
 }
 
 #if GDB_SELF_TEST
@@ -1479,10 +1553,23 @@ static size_t
 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;
@@ -1493,13 +1580,21 @@ regcaches_size ()
 static int
 regcache_count (process_stratum_target *target, ptid_t ptid)
 {
-  auto ptid_regc_map_it = regcaches.find (target);
-  if (ptid_regc_map_it != regcaches.end ())
+  /* 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;
-      auto range = ptid_regc_map.equal_range (ptid);
+      pid_ptid_regcache_map &pid_ptid_regc_map = pid_ptid_regc_map_it->second;
 
-      return std::distance (range.first, range.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;
@@ -1622,6 +1717,22 @@ registers_changed_ptid_target_test ()
   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
@@ -1833,30 +1944,9 @@ cooked_read_test (struct gdbarch *gdbarch)
 static void
 cooked_write_test (struct gdbarch *gdbarch)
 {
-  /* 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)
-    error (_("target already pushed"));
-
   /* Create a mock environment.  A process_stratum target pushed.  */
-
-  target_ops_no_register mock_target;
-
-  /* Push the process_stratum target so we can mock accessing
-     registers.  */
-  push_target (&mock_target);
-
-  /* Pop it again on exit (return/exception).  */
-  struct on_exit
-  {
-    ~on_exit ()
-    {
-      pop_all_targets_at_and_above (process_stratum);
-    }
-  } pop_targets;
-
-  readwrite_regcache readwrite (&mock_target, gdbarch);
-
+  scoped_mock_context<target_ops_no_register> ctx (gdbarch);
+  readwrite_regcache readwrite (&ctx.mock_target, gdbarch);
   const int num_regs = gdbarch_num_cooked_regs (gdbarch);
 
   for (auto regnum = 0; regnum < num_regs; regnum++)
@@ -1954,15 +2044,19 @@ regcache_thread_ptid_changed ()
   /* 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;
+  target1.mock_inferior.ptid_thread_map.clear ();
+  target1.mock_inferior.ptid_thread_map[old_ptid] = &target1.mock_thread;
+
   target2.mock_inferior.pid = old_ptid.pid ();
   target2.mock_thread.ptid = old_ptid;
+  target2.mock_inferior.ptid_thread_map.clear ();
+  target2.mock_inferior.ptid_thread_map[old_ptid] = &target2.mock_thread;
 
   gdb_assert (regcaches.empty ());
 
@@ -1998,24 +2092,35 @@ void _initialize_regcache ();
 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 ("get_thread_arch_aspace_regcache",
-                           selftests::get_thread_arch_aspace_regcache_test);
+                           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_ptid",
-                           selftests::registers_changed_ptid_target_ptid_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);