Add multiple-CPU support in ravenscar-thread.c

This patch reworks the ravenscar-thread layer to remove the
assumption that the target only has 1 CPU. In particular,
when connected to a QEMU target over the remote protocol,
QEMU reports each CPU as one thread. This patch adapts
the ravenscar-thread layer to this, and adds a large comment
explaining the general design of this unit.

gdb/ChangeLog:

        * ada-lang.h (ada_get_task_info_from_ptid): Add declaration.
        * ada-tasks.c (ada_get_task_info_from_ptid): New function.
        * ravenscar-thread.c: Add into comment.
        (base_magic_null_ptid): Delete.
        (base_ptid): Change documentation.
        (ravenscar_active_task): Renames ravenscar_running_thread.
        All callers updated throughout.
        (is_ravenscar_task, ravenscar_get_thread_base_cpu): New function.
        (ravenscar_task_is_currently_active): Likewise.
        (get_base_thread_from_ravenscar_task): Ditto.
        (ravenscar_update_inferior_ptid): Adjust to handle multiple CPUs.
        (ravenscar_runtime_initialized): Likewise.
        (get_running_thread_id): Add new parameter "cpu".  Adjust
        implementation to handle this new parameter.
        (ravenscar_fetch_registers): Small adjustment to use
        is_ravenscar_task and ravenscar_task_is_currently_active in
        order to decide whether to use the target beneath or this
        module's arch_ops.
        (ravenscar_store_registers, ravenscar_prepare_to_store): Likewise.
        (ravenscar_stopped_by_sw_breakpoint): Use
        get_base_thread_from_ravenscar_task to get the underlying
        thread, rather than using base_ptid.
        (ravenscar_stopped_by_hw_breakpoint, ravenscar_stopped_by_watchpoint)
        (ravenscar_stopped_data_address, ravenscar_core_of_thread):
        Likewise.
        (ravenscar_inferior_created): Do not set base_magic_null_ptid.
This commit is contained in:
Joel Brobecker 2017-11-21 14:00:30 -08:00
parent 65d40437e2
commit 9edcc12f9b
4 changed files with 191 additions and 34 deletions

View File

@ -1,3 +1,32 @@
2017-11-21 Joel Brobecker <brobecker@adacore.com>
* ada-lang.h (ada_get_task_info_from_ptid): Add declaration.
* ada-tasks.c (ada_get_task_info_from_ptid): New function.
* ravenscar-thread.c: Add into comment.
(base_magic_null_ptid): Delete.
(base_ptid): Change documentation.
(ravenscar_active_task): Renames ravenscar_running_thread.
All callers updated throughout.
(is_ravenscar_task, ravenscar_get_thread_base_cpu): New function.
(ravenscar_task_is_currently_active): Likewise.
(get_base_thread_from_ravenscar_task): Ditto.
(ravenscar_update_inferior_ptid): Adjust to handle multiple CPUs.
(ravenscar_runtime_initialized): Likewise.
(get_running_thread_id): Add new parameter "cpu". Adjust
implementation to handle this new parameter.
(ravenscar_fetch_registers): Small adjustment to use
is_ravenscar_task and ravenscar_task_is_currently_active in
order to decide whether to use the target beneath or this
module's arch_ops.
(ravenscar_store_registers, ravenscar_prepare_to_store): Likewise.
(ravenscar_stopped_by_sw_breakpoint): Use
get_base_thread_from_ravenscar_task to get the underlying
thread, rather than using base_ptid.
(ravenscar_stopped_by_hw_breakpoint, ravenscar_stopped_by_watchpoint)
(ravenscar_stopped_data_address, ravenscar_core_of_thread):
Likewise.
(ravenscar_inferior_created): Do not set base_magic_null_ptid.
2017-11-21 Joel Brobecker <brobecker@adacore.com>
* ada-lang.h (struct ada_task_info) <base_cpu>: New field.

View File

@ -401,6 +401,8 @@ extern std::vector<ada_exc_info> ada_exceptions_list (const char *regexp);
extern int valid_task_id (int);
extern struct ada_task_info *ada_get_task_info_from_ptid (ptid_t ptid);
extern int ada_get_task_number (ptid_t);
typedef void (ada_task_list_iterator_ftype) (struct ada_task_info *task);

View File

@ -353,6 +353,30 @@ ada_task_is_alive (struct ada_task_info *task_info)
return (task_info->state != Terminated);
}
/* Search through the list of known tasks for the one whose ptid is
PTID, and return it. Return NULL if the task was not found. */
struct ada_task_info *
ada_get_task_info_from_ptid (ptid_t ptid)
{
int i, nb_tasks;
struct ada_task_info *task;
struct ada_tasks_inferior_data *data;
ada_build_task_list ();
data = get_ada_tasks_inferior_data (current_inferior ());
nb_tasks = VEC_length (ada_task_info_s, data->task_list);
for (i = 0; i < nb_tasks; i++)
{
task = VEC_index (ada_task_info_s, data->task_list, i);
if (ptid_equal (task->ptid, ptid))
return task;
}
return NULL;
}
/* Call the ITERATOR function once for each Ada task that hasn't been
terminated yet. */

View File

@ -31,17 +31,41 @@
#include "regcache.h"
#include "objfiles.h"
/* This module provides support for "Ravenscar" tasks (Ada) when
debugging on bare-metal targets.
The typical situation is when debugging a bare-metal target over
the remote protocol. In that situation, the system does not know
about high-level comcepts such as threads, only about some code
running on one or more CPUs. And since the remote protocol does not
provide any handling for CPUs, the de facto standard for handling
them is to have one thread per CPU, where the thread's ptid has
its lwp field set to the CPU number (eg: 1 for the first CPU,
2 for the second one, etc). This module will make that assumption.
This module then creates and maintains the list of threads based
on the list of Ada tasks, with one thread per Ada tasks. The convention
is that threads corresponding to the CPUs (see assumption above)
have a ptid_t of the form (PID, LWP, 0), which threads corresponding
to our Ada tasks have a ptid_t of the form (PID, 0, TID) where TID
is the Ada task's ID as extracted from Ada runtime information.
Switching to a given Ada tasks (or its underlying thread) is performed
by fetching the registers of that tasks from the memory area where
the registers were saved. For any of the other operations, the
operation is performed by first finding the CPU on which the task
is running, switching to its corresponding ptid, and then performing
the operation on that ptid using the target beneath us. */
/* If non-null, ravenscar task support is enabled. */
static int ravenscar_task_support = 1;
/* This module's target-specific operations. */
static struct target_ops ravenscar_ops;
/* Some base target uses a special value for the null PID (exempli gratia
remote). */
static ptid_t base_magic_null_ptid;
/* Ptid of the inferior as seen by the process stratum. */
/* PTID of the last thread that received an event.
This can be useful to determine the associated task that received
the event, to make it the current task. */
static ptid_t base_ptid;
static const char running_thread_name[] = "__gnat_running_thread_table";
@ -53,7 +77,7 @@ static const char ravenscar_runtime_initializer[] =
"system__bb__threads__initialize";
static void ravenscar_update_thread_list (struct target_ops *ops);
static ptid_t ravenscar_running_thread (void);
static ptid_t ravenscar_active_task (int cpu);
static const char *ravenscar_extra_thread_info (struct target_ops *self,
struct thread_info *tp);
static int ravenscar_thread_alive (struct target_ops *ops, ptid_t ptid);
@ -72,22 +96,100 @@ static int ravenscar_runtime_initialized (void);
static void ravenscar_inferior_created (struct target_ops *target,
int from_tty);
/* Return nonzero iff PTID corresponds to a ravenscar task. */
static int
is_ravenscar_task (ptid_t ptid)
{
/* By construction, ravenscar tasks have their LWP set to zero. */
return ptid_get_lwp (ptid) == 0;
}
/* Given PTID, which can be either a ravenscar task or a CPU thread,
return which CPU that ptid is running on.
This assume that PTID is a valid ptid_t. Otherwise, a gdb_assert
will be triggered. */
static int
ravenscar_get_thread_base_cpu (ptid_t ptid)
{
int base_cpu;
if (is_ravenscar_task (ptid))
{
struct ada_task_info *task_info = ada_get_task_info_from_ptid (ptid);
gdb_assert (task_info != NULL);
base_cpu = task_info->base_cpu;
}
else
{
/* We assume that the LWP of the PTID is equal to the CPU number. */
base_cpu = ptid_get_lwp (ptid);
}
return base_cpu;
}
/* Given a ravenscar task (identified by its ptid_t PTID), return nonzero
if this task is the currently active task on the cpu that task is
running on.
In other words, this function determine which CPU this task is
currently running on, and then return nonzero if the CPU in question
is executing the code for that task. If that's the case, then
that task's registers are in the CPU bank. Otherwise, the task
is currently suspended, and its registers have been saved in memory. */
static int
ravenscar_task_is_currently_active (ptid_t ptid)
{
ptid_t active_task_ptid
= ravenscar_active_task (ravenscar_get_thread_base_cpu (ptid));
return ptid_equal (ptid, active_task_ptid);
}
/* Return the CPU thread (as a ptid_t) on which the given ravenscar
task is running.
This is the thread that corresponds to the CPU on which the task
is running. */
static ptid_t
get_base_thread_from_ravenscar_task (ptid_t ptid)
{
int base_cpu;
if (!is_ravenscar_task (ptid))
return ptid;
base_cpu = ravenscar_get_thread_base_cpu (ptid);
return ptid_build (ptid_get_pid (ptid), base_cpu, 0);
}
/* Fetch the ravenscar running thread from target memory and
update inferior_ptid accordingly. */
static void
ravenscar_update_inferior_ptid (void)
{
int base_cpu;
base_ptid = inferior_ptid;
gdb_assert (!is_ravenscar_task (inferior_ptid));
base_cpu = ravenscar_get_thread_base_cpu (base_ptid);
/* If the runtime has not been initialized yet, the inferior_ptid is
the only ptid that there is. */
if (!ravenscar_runtime_initialized ())
return;
/* Make sure we set base_ptid before calling ravenscar_running_thread
/* Make sure we set base_ptid before calling ravenscar_active_task
as the latter relies on it. */
inferior_ptid = ravenscar_running_thread ();
inferior_ptid = ravenscar_active_task (base_cpu);
gdb_assert (!ptid_equal (inferior_ptid, null_ptid));
/* The running thread may not have been added to
@ -144,14 +246,14 @@ has_ravenscar_runtime (void)
static int
ravenscar_runtime_initialized (void)
{
return (!(ptid_equal (ravenscar_running_thread (), null_ptid)));
return (!(ptid_equal (ravenscar_active_task (1), null_ptid)));
}
/* Return the ID of the thread that is currently running.
Return 0 if the ID could not be determined. */
static CORE_ADDR
get_running_thread_id (void)
get_running_thread_id (int cpu)
{
struct bound_minimal_symbol object_msym = get_running_thread_msymbol ();
int object_size;
@ -164,8 +266,9 @@ get_running_thread_id (void)
if (!object_msym.minsym)
return 0;
object_addr = BMSYMBOL_VALUE_ADDRESS (object_msym);
object_size = TYPE_LENGTH (builtin_type_void_data_ptr);
object_addr = (BMSYMBOL_VALUE_ADDRESS (object_msym)
+ (cpu - 1) * object_size);
buf_size = object_size;
buf = (gdb_byte *) alloca (buf_size);
read_memory (object_addr, buf, buf_size);
@ -231,9 +334,9 @@ ravenscar_update_thread_list (struct target_ops *ops)
}
static ptid_t
ravenscar_running_thread (void)
ravenscar_active_task (int cpu)
{
CORE_ADDR tid = get_running_thread_id ();
CORE_ADDR tid = get_running_thread_id (cpu);
if (tid == 0)
return null_ptid;
@ -270,11 +373,9 @@ ravenscar_fetch_registers (struct target_ops *ops,
struct target_ops *beneath = find_target_beneath (ops);
ptid_t ptid = regcache_get_ptid (regcache);
if (!ravenscar_runtime_initialized ()
|| ptid_equal (ptid, base_magic_null_ptid)
|| ptid_equal (ptid, ravenscar_running_thread ()))
beneath->to_fetch_registers (beneath, regcache, regnum);
else
if (ravenscar_runtime_initialized ()
&& is_ravenscar_task (ptid)
&& !ravenscar_task_is_currently_active (ptid))
{
struct gdbarch *gdbarch = regcache->arch ();
struct ravenscar_arch_ops *arch_ops
@ -282,6 +383,8 @@ ravenscar_fetch_registers (struct target_ops *ops,
arch_ops->to_fetch_registers (regcache, regnum);
}
else
beneath->to_fetch_registers (beneath, regcache, regnum);
}
static void
@ -291,11 +394,9 @@ ravenscar_store_registers (struct target_ops *ops,
struct target_ops *beneath = find_target_beneath (ops);
ptid_t ptid = regcache_get_ptid (regcache);
if (!ravenscar_runtime_initialized ()
|| ptid_equal (ptid, base_magic_null_ptid)
|| ptid_equal (ptid, ravenscar_running_thread ()))
beneath->to_store_registers (beneath, regcache, regnum);
else
if (ravenscar_runtime_initialized ()
&& is_ravenscar_task (ptid)
&& !ravenscar_task_is_currently_active (ptid))
{
struct gdbarch *gdbarch = regcache->arch ();
struct ravenscar_arch_ops *arch_ops
@ -303,6 +404,8 @@ ravenscar_store_registers (struct target_ops *ops,
arch_ops->to_store_registers (regcache, regnum);
}
else
beneath->to_store_registers (beneath, regcache, regnum);
}
static void
@ -312,11 +415,9 @@ ravenscar_prepare_to_store (struct target_ops *self,
struct target_ops *beneath = find_target_beneath (self);
ptid_t ptid = regcache_get_ptid (regcache);
if (!ravenscar_runtime_initialized ()
|| ptid_equal (ptid, base_magic_null_ptid)
|| ptid_equal (ptid, ravenscar_running_thread ()))
beneath->to_prepare_to_store (beneath, regcache);
else
if (ravenscar_runtime_initialized ()
&& is_ravenscar_task (ptid)
&& !ravenscar_task_is_currently_active (ptid))
{
struct gdbarch *gdbarch = regcache->arch ();
struct ravenscar_arch_ops *arch_ops
@ -324,6 +425,8 @@ ravenscar_prepare_to_store (struct target_ops *self,
arch_ops->to_prepare_to_store (regcache);
}
else
beneath->to_prepare_to_store (beneath, regcache);
}
/* Implement the to_stopped_by_sw_breakpoint target_ops "method". */
@ -335,7 +438,7 @@ ravenscar_stopped_by_sw_breakpoint (struct target_ops *ops)
struct target_ops *beneath = find_target_beneath (ops);
int result;
inferior_ptid = base_ptid;
inferior_ptid = get_base_thread_from_ravenscar_task (saved_ptid);
result = beneath->to_stopped_by_sw_breakpoint (beneath);
inferior_ptid = saved_ptid;
return result;
@ -350,7 +453,7 @@ ravenscar_stopped_by_hw_breakpoint (struct target_ops *ops)
struct target_ops *beneath = find_target_beneath (ops);
int result;
inferior_ptid = base_ptid;
inferior_ptid = get_base_thread_from_ravenscar_task (saved_ptid);
result = beneath->to_stopped_by_hw_breakpoint (beneath);
inferior_ptid = saved_ptid;
return result;
@ -365,7 +468,7 @@ ravenscar_stopped_by_watchpoint (struct target_ops *ops)
struct target_ops *beneath = find_target_beneath (ops);
int result;
inferior_ptid = base_ptid;
inferior_ptid = get_base_thread_from_ravenscar_task (saved_ptid);
result = beneath->to_stopped_by_watchpoint (beneath);
inferior_ptid = saved_ptid;
return result;
@ -380,7 +483,7 @@ ravenscar_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
struct target_ops *beneath = find_target_beneath (ops);
int result;
inferior_ptid = base_ptid;
inferior_ptid = get_base_thread_from_ravenscar_task (saved_ptid);
result = beneath->to_stopped_data_address (beneath, addr_p);
inferior_ptid = saved_ptid;
return result;
@ -405,7 +508,7 @@ ravenscar_core_of_thread (struct target_ops *ops, ptid_t ptid)
struct target_ops *beneath = find_target_beneath (ops);
int result;
inferior_ptid = base_ptid;
inferior_ptid = get_base_thread_from_ravenscar_task (saved_ptid);
result = beneath->to_core_of_thread (beneath, inferior_ptid);
inferior_ptid = saved_ptid;
return result;
@ -422,7 +525,6 @@ ravenscar_inferior_created (struct target_ops *target, int from_tty)
|| !has_ravenscar_runtime ())
return;
base_magic_null_ptid = inferior_ptid;
ravenscar_update_inferior_ptid ();
push_target (&ravenscar_ops);
}