binutils-gdb/gdb/user-regs.c
Simon Marchi 8480a37e14 gdb: pass frames as const frame_info_ptr &
We currently pass frames to function by value, as `frame_info_ptr`.
This is somewhat expensive:

 - the size of `frame_info_ptr` is 64 bytes, which is a bit big to pass
   by value
 - the constructors and destructor link/unlink the object in the global
   `frame_info_ptr::frame_list` list.  This is an `intrusive_list`, so
   it's not so bad: it's just assigning a few points, there's no memory
   allocation as if it was `std::list`, but still it's useless to do
   that over and over.

As suggested by Tom Tromey, change many function signatures to accept
`const frame_info_ptr &` instead of `frame_info_ptr`.

Some functions reassign their `frame_info_ptr` parameter, like:

  void
  the_func (frame_info_ptr frame)
  {
    for (; frame != nullptr; frame = get_prev_frame (frame))
      {
        ...
      }
  }

I wondered what to do about them, do I leave them as-is or change them
(and need to introduce a separate local variable that can be
re-assigned).  I opted for the later for consistency.  It might not be
clear why some functions take `const frame_info_ptr &` while others take
`frame_info_ptr`.  Also, if a function took a `frame_info_ptr` because
it did re-assign its parameter, I doubt that we would think to change it
to `const frame_info_ptr &` should the implementation change such that
it doesn't need to take `frame_info_ptr` anymore.  It seems better to
have a simple rule and apply it everywhere.

Change-Id: I59d10addef687d157f82ccf4d54f5dde9a963fd0
Approved-By: Andrew Burgess <aburgess@redhat.com>
2024-02-20 10:42:25 -05:00

240 lines
6.7 KiB
C

/* User visible, per-frame registers, for GDB, the GNU debugger.
Copyright (C) 2002-2024 Free Software Foundation, Inc.
Contributed by Red Hat.
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 "user-regs.h"
#include "gdbtypes.h"
#include "frame.h"
#include "arch-utils.h"
#include "command.h"
#include "cli/cli-cmds.h"
/* A table of user registers.
User registers have regnum's that live above of the range [0
.. gdbarch_num_regs + gdbarch_num_pseudo_regs)
(which is controlled by the target).
The target should never see a user register's regnum value.
Always append, never delete. By doing this, the relative regnum
(offset from gdbarch_num_regs + gdbarch_num_pseudo_regs)
assigned to each user register never changes. */
struct user_reg
{
const char *name;
/* Avoid the "read" symbol name as it conflicts with a preprocessor symbol
in the NetBSD header for Stack Smashing Protection, that wraps the read(2)
syscall. */
struct value *(*xread) (const frame_info_ptr &frame, const void *baton);
const void *baton;
struct user_reg *next;
};
/* This structure is named gdb_user_regs instead of user_regs to avoid
conflicts with any "struct user_regs" in system headers. For instance,
on ARM GNU/Linux native builds, nm-linux.h includes <signal.h> includes
<sys/ucontext.h> includes <sys/procfs.h> includes <sys/user.h>, which
declares "struct user_regs". */
struct gdb_user_regs
{
struct user_reg *first = nullptr;
struct user_reg **last = nullptr;
};
static void
append_user_reg (struct gdb_user_regs *regs, const char *name,
user_reg_read_ftype *xread, const void *baton,
struct user_reg *reg)
{
/* The caller is responsible for allocating memory needed to store
the register. By doing this, the function can operate on a
register list stored in the common heap or a specific obstack. */
gdb_assert (reg != NULL);
reg->name = name;
reg->xread = xread;
reg->baton = baton;
reg->next = NULL;
if (regs->last == nullptr)
regs->last = &regs->first;
(*regs->last) = reg;
regs->last = &(*regs->last)->next;
}
/* An array of the builtin user registers. */
static struct gdb_user_regs builtin_user_regs;
void
user_reg_add_builtin (const char *name, user_reg_read_ftype *xread,
const void *baton)
{
append_user_reg (&builtin_user_regs, name, xread, baton,
XNEW (struct user_reg));
}
/* Per-architecture user registers. Start with the builtin user
registers and then, again, append. */
static const registry<gdbarch>::key<gdb_user_regs> user_regs_data;
static gdb_user_regs *
get_user_regs (struct gdbarch *gdbarch)
{
struct gdb_user_regs *regs = user_regs_data.get (gdbarch);
if (regs == nullptr)
{
regs = new struct gdb_user_regs;
struct obstack *obstack = gdbarch_obstack (gdbarch);
regs->last = &regs->first;
for (user_reg *reg = builtin_user_regs.first;
reg != NULL;
reg = reg->next)
append_user_reg (regs, reg->name, reg->xread, reg->baton,
OBSTACK_ZALLOC (obstack, struct user_reg));
user_regs_data.set (gdbarch, regs);
}
return regs;
}
void
user_reg_add (struct gdbarch *gdbarch, const char *name,
user_reg_read_ftype *xread, const void *baton)
{
struct gdb_user_regs *regs = get_user_regs (gdbarch);
gdb_assert (regs != NULL);
append_user_reg (regs, name, xread, baton,
GDBARCH_OBSTACK_ZALLOC (gdbarch, struct user_reg));
}
int
user_reg_map_name_to_regnum (struct gdbarch *gdbarch, const char *name,
int len)
{
/* Make life easy, set the len to something reasonable. */
if (len < 0)
len = strlen (name);
/* Search register name space first - always let an architecture
specific register override the user registers. */
{
int maxregs = gdbarch_num_cooked_regs (gdbarch);
for (int i = 0; i < maxregs; i++)
{
const char *regname = gdbarch_register_name (gdbarch, i);
if (len == strlen (regname) && strncmp (regname, name, len) == 0)
return i;
}
}
/* Search the user name space. */
{
struct gdb_user_regs *regs = get_user_regs (gdbarch);
struct user_reg *reg;
int nr;
for (nr = 0, reg = regs->first; reg != NULL; reg = reg->next, nr++)
{
if ((len < 0 && strcmp (reg->name, name))
|| (len == strlen (reg->name)
&& strncmp (reg->name, name, len) == 0))
return gdbarch_num_cooked_regs (gdbarch) + nr;
}
}
return -1;
}
static struct user_reg *
usernum_to_user_reg (struct gdbarch *gdbarch, int usernum)
{
struct gdb_user_regs *regs = get_user_regs (gdbarch);
struct user_reg *reg;
for (reg = regs->first; reg != NULL; reg = reg->next)
{
if (usernum == 0)
return reg;
usernum--;
}
return NULL;
}
const char *
user_reg_map_regnum_to_name (struct gdbarch *gdbarch, int regnum)
{
int maxregs = gdbarch_num_cooked_regs (gdbarch);
if (regnum < 0)
return NULL;
else if (regnum < maxregs)
return gdbarch_register_name (gdbarch, regnum);
else
{
struct user_reg *reg = usernum_to_user_reg (gdbarch, regnum - maxregs);
if (reg == NULL)
return NULL;
else
return reg->name;
}
}
struct value *
value_of_user_reg (int regnum, const frame_info_ptr &frame)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
int maxregs = gdbarch_num_cooked_regs (gdbarch);
struct user_reg *reg = usernum_to_user_reg (gdbarch, regnum - maxregs);
gdb_assert (reg != NULL);
return reg->xread (frame, reg->baton);
}
static void
maintenance_print_user_registers (const char *args, int from_tty)
{
struct gdbarch *gdbarch = get_current_arch ();
struct user_reg *reg;
int regnum;
struct gdb_user_regs *regs = get_user_regs (gdbarch);
regnum = gdbarch_num_cooked_regs (gdbarch);
gdb_printf (" %-11s %3s\n", "Name", "Nr");
for (reg = regs->first; reg != NULL; reg = reg->next, ++regnum)
gdb_printf (" %-11s %3d\n", reg->name, regnum);
}
void _initialize_user_regs ();
void
_initialize_user_regs ()
{
add_cmd ("user-registers", class_maintenance,
maintenance_print_user_registers,
_("List the names of the current user registers."),
&maintenanceprintlist);
}