From: Joel Brobecker Date: Tue, 21 Nov 2017 22:00:30 +0000 (-0800) Subject: Add multiple-CPU support in ravenscar-thread.c X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=9edcc12f9b714149f84bcf20dbdad91f030e62de;p=binutils-gdb.git 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. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 7300cdd6b09..d7cf0667f43 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,32 @@ +2017-11-21 Joel Brobecker + + * 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 * ada-lang.h (struct ada_task_info) : New field. diff --git a/gdb/ada-lang.h b/gdb/ada-lang.h index c11fb48c1b3..36a5820ee34 100644 --- a/gdb/ada-lang.h +++ b/gdb/ada-lang.h @@ -401,6 +401,8 @@ extern std::vector 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); diff --git a/gdb/ada-tasks.c b/gdb/ada-tasks.c index e29b563d6cd..16cee35d861 100644 --- a/gdb/ada-tasks.c +++ b/gdb/ada-tasks.c @@ -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. */ diff --git a/gdb/ravenscar-thread.c b/gdb/ravenscar-thread.c index 7edfa29263f..83dfd9d4adf 100644 --- a/gdb/ravenscar-thread.c +++ b/gdb/ravenscar-thread.c @@ -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); }