/* Ada Ravenscar thread support.
- Copyright (C) 2004-2020 Free Software Foundation, Inc.
+ Copyright (C) 2004-2021 Free Software Foundation, Inc.
This file is part of GDB.
#include "top.h"
#include "regcache.h"
#include "objfiles.h"
+#include <unordered_map>
/* This module provides support for "Ravenscar" tasks (Ada) when
debugging on bare-metal targets.
strata stratum () const override { return thread_stratum; }
- ptid_t wait (ptid_t, struct target_waitstatus *, int) override;
+ ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
void resume (ptid_t, int, enum gdb_signal) override;
void fetch_registers (struct regcache *, int) override;
bool stopped_data_address (CORE_ADDR *) override;
+ enum target_xfer_status xfer_partial (enum target_object object,
+ const char *annex,
+ gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, ULONGEST len,
+ ULONGEST *xfered_len) override;
+
bool thread_alive (ptid_t ptid) override;
int core_of_thread (ptid_t ptid) override;
void update_thread_list () override;
- const char *extra_thread_info (struct thread_info *) override;
-
std::string pid_to_str (ptid_t) override;
ptid_t get_ada_task_ptid (long lwp, long thread) override;
+ struct btrace_target_info *enable_btrace (ptid_t ptid,
+ const struct btrace_config *conf)
+ override
+ {
+ ptid = get_base_thread_from_ravenscar_task (ptid);
+ return beneath ()->enable_btrace (ptid, conf);
+ }
+
void mourn_inferior () override;
void close () override
ptid_t active_task (int cpu);
bool task_is_currently_active (ptid_t ptid);
bool runtime_initialized ();
+ int get_thread_base_cpu (ptid_t ptid);
+ ptid_t get_base_thread_from_ravenscar_task (ptid_t ptid);
+ void add_thread (struct ada_task_info *task);
+
+ /* Like switch_to_thread, but uses the base ptid for the thread. */
+ void set_base_thread_from_ravenscar_task (ptid_t ptid)
+ {
+ process_stratum_target *proc_target
+ = as_process_stratum_target (this->beneath ());
+ ptid_t underlying = get_base_thread_from_ravenscar_task (ptid);
+ switch_to_thread (find_thread_ptid (proc_target, underlying));
+ }
+
+ /* This maps a TID to the CPU on which it was running. This is
+ needed because sometimes the runtime will report an active task
+ that hasn't yet been put on the list of tasks that is read by
+ ada-tasks.c. */
+ std::unordered_map<long, int> m_cpu_map;
};
/* Return true iff PTID corresponds to a ravenscar task. */
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
+ravenscar_thread_target::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;
+ /* Prefer to not read inferior memory if possible, to avoid
+ reentrancy problems with xfer_partial. */
+ auto iter = m_cpu_map.find (ptid.tid ());
+
+ if (iter != m_cpu_map.end ())
+ base_cpu = iter->second;
+ else
+ {
+ 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
{
bool
ravenscar_thread_target::task_is_currently_active (ptid_t ptid)
{
- ptid_t active_task_ptid
- = active_task (ravenscar_get_thread_base_cpu (ptid));
+ ptid_t active_task_ptid = active_task (get_thread_base_cpu (ptid));
return ptid == active_task_ptid;
}
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)
+ptid_t
+ravenscar_thread_target::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);
+ base_cpu = get_thread_base_cpu (ptid);
return ptid_t (ptid.pid (), base_cpu, 0);
}
int base_cpu;
gdb_assert (!is_ravenscar_task (m_base_ptid));
- base_cpu = ravenscar_get_thread_base_cpu (m_base_ptid);
+ base_cpu = get_thread_base_cpu (m_base_ptid);
if (!runtime_initialized ())
return nullptr;
may not always add it to the thread list. Add it here. */
thread_info *active_thr = find_thread_ptid (proc_target, active_ptid);
if (active_thr == nullptr)
- active_thr = add_thread (proc_target, active_ptid);
+ {
+ active_thr = ::add_thread (proc_target, active_ptid);
+ m_cpu_map[active_ptid.tid ()] = base_cpu;
+ }
return active_thr;
}
/* If we see a wildcard resume, we simply pass that on. Otherwise,
arrange to resume the base ptid. */
inferior_ptid = m_base_ptid;
- if (ptid != minus_one_ptid)
+ if (ptid.is_pid ())
+ {
+ /* We only have one process, so resume all threads of it. */
+ ptid = minus_one_ptid;
+ }
+ else if (ptid != minus_one_ptid)
ptid = m_base_ptid;
beneath ()->resume (ptid, step, siggnal);
}
ptid_t
ravenscar_thread_target::wait (ptid_t ptid,
struct target_waitstatus *status,
- int options)
+ target_wait_flags options)
{
process_stratum_target *beneath
= as_process_stratum_target (this->beneath ());
because we might try switching threads (and thus sending packets)
after the remote has disconnected. */
if (status->kind != TARGET_WAITKIND_EXITED
- && status->kind != TARGET_WAITKIND_SIGNALLED)
+ && status->kind != TARGET_WAITKIND_SIGNALLED
+ && runtime_initialized ())
{
m_base_ptid = event_ptid;
this->update_thread_list ();
return this->add_active_thread ()->ptid;
}
- return m_base_ptid;
+ return event_ptid;
}
/* Add the thread associated to the given TASK to the thread list
(if the thread has already been added, this is a no-op). */
-static void
-ravenscar_add_thread (struct ada_task_info *task)
+void
+ravenscar_thread_target::add_thread (struct ada_task_info *task)
{
if (find_thread_ptid (current_inferior (), task->ptid) == NULL)
- add_thread (current_inferior ()->process_target (), task->ptid);
+ {
+ ::add_thread (current_inferior ()->process_target (), task->ptid);
+ m_cpu_map[task->ptid.tid ()] = task->base_cpu;
+ }
}
void
ravenscar_thread_target::update_thread_list ()
{
+ /* iterate_over_live_ada_tasks requires that inferior_ptid be set,
+ but this isn't always the case in target methods. So, we ensure
+ it here. */
+ scoped_restore save_ptid = make_scoped_restore (&inferior_ptid,
+ m_base_ptid);
+
/* Do not clear the thread list before adding the Ada task, to keep
the thread that the process stratum has included into it
(m_base_ptid) and the running thread, that may not have been included
to system.tasking.debug's list yet. */
- iterate_over_live_ada_tasks (ravenscar_add_thread);
+ iterate_over_live_ada_tasks ([=] (struct ada_task_info *task)
+ {
+ this->add_thread (task);
+ });
}
ptid_t
return ptid_t (m_base_ptid.pid (), 0, tid);
}
-const char *
-ravenscar_thread_target::extra_thread_info (thread_info *tp)
-{
- return "Ravenscar task";
-}
-
bool
ravenscar_thread_target::thread_alive (ptid_t ptid)
{
std::string
ravenscar_thread_target::pid_to_str (ptid_t ptid)
{
- return string_printf ("Thread %#x", (int) ptid.tid ());
+ if (!is_ravenscar_task (ptid))
+ return beneath ()->pid_to_str (ptid);
+
+ return string_printf ("Ravenscar Thread %#x", (int) ptid.tid ());
}
+/* Temporarily set the ptid of a regcache to some other value. When
+ this object is destroyed, the regcache's original ptid is
+ restored. */
+
+class temporarily_change_regcache_ptid
+{
+public:
+
+ temporarily_change_regcache_ptid (struct regcache *regcache, ptid_t new_ptid)
+ : m_regcache (regcache),
+ m_save_ptid (regcache->ptid ())
+ {
+ m_regcache->set_ptid (new_ptid);
+ }
+
+ ~temporarily_change_regcache_ptid ()
+ {
+ m_regcache->set_ptid (m_save_ptid);
+ }
+
+private:
+
+ /* The regcache. */
+ struct regcache *m_regcache;
+ /* The saved ptid. */
+ ptid_t m_save_ptid;
+};
+
void
ravenscar_thread_target::fetch_registers (struct regcache *regcache, int regnum)
{
ptid_t ptid = regcache->ptid ();
- if (runtime_initialized ()
- && is_ravenscar_task (ptid)
- && !task_is_currently_active (ptid))
+ if (runtime_initialized () && is_ravenscar_task (ptid))
{
- struct gdbarch *gdbarch = regcache->arch ();
- struct ravenscar_arch_ops *arch_ops
- = gdbarch_ravenscar_ops (gdbarch);
-
- arch_ops->fetch_registers (regcache, regnum);
+ if (task_is_currently_active (ptid))
+ {
+ ptid_t base = get_base_thread_from_ravenscar_task (ptid);
+ temporarily_change_regcache_ptid changer (regcache, base);
+ beneath ()->fetch_registers (regcache, regnum);
+ }
+ else
+ {
+ struct gdbarch *gdbarch = regcache->arch ();
+ struct ravenscar_arch_ops *arch_ops
+ = gdbarch_ravenscar_ops (gdbarch);
+
+ arch_ops->fetch_registers (regcache, regnum);
+ }
}
else
beneath ()->fetch_registers (regcache, regnum);
{
ptid_t ptid = regcache->ptid ();
- if (runtime_initialized ()
- && is_ravenscar_task (ptid)
- && !task_is_currently_active (ptid))
+ if (runtime_initialized () && is_ravenscar_task (ptid))
{
- struct gdbarch *gdbarch = regcache->arch ();
- struct ravenscar_arch_ops *arch_ops
- = gdbarch_ravenscar_ops (gdbarch);
-
- arch_ops->store_registers (regcache, regnum);
+ if (task_is_currently_active (ptid))
+ {
+ ptid_t base = get_base_thread_from_ravenscar_task (ptid);
+ temporarily_change_regcache_ptid changer (regcache, base);
+ beneath ()->store_registers (regcache, regnum);
+ }
+ else
+ {
+ struct gdbarch *gdbarch = regcache->arch ();
+ struct ravenscar_arch_ops *arch_ops
+ = gdbarch_ravenscar_ops (gdbarch);
+
+ arch_ops->store_registers (regcache, regnum);
+ }
}
else
beneath ()->store_registers (regcache, regnum);
{
ptid_t ptid = regcache->ptid ();
- if (runtime_initialized ()
- && is_ravenscar_task (ptid)
- && !task_is_currently_active (ptid))
+ if (runtime_initialized () && is_ravenscar_task (ptid))
{
- /* Nothing. */
+ if (task_is_currently_active (ptid))
+ {
+ ptid_t base = get_base_thread_from_ravenscar_task (ptid);
+ temporarily_change_regcache_ptid changer (regcache, base);
+ beneath ()->prepare_to_store (regcache);
+ }
+ else
+ {
+ /* Nothing. */
+ }
}
else
beneath ()->prepare_to_store (regcache);
bool
ravenscar_thread_target::stopped_by_sw_breakpoint ()
{
- scoped_restore save_ptid = make_scoped_restore (&inferior_ptid);
- inferior_ptid = get_base_thread_from_ravenscar_task (inferior_ptid);
+ scoped_restore_current_thread saver;
+ set_base_thread_from_ravenscar_task (inferior_ptid);
return beneath ()->stopped_by_sw_breakpoint ();
}
bool
ravenscar_thread_target::stopped_by_hw_breakpoint ()
{
- scoped_restore save_ptid = make_scoped_restore (&inferior_ptid);
- inferior_ptid = get_base_thread_from_ravenscar_task (inferior_ptid);
+ scoped_restore_current_thread saver;
+ set_base_thread_from_ravenscar_task (inferior_ptid);
return beneath ()->stopped_by_hw_breakpoint ();
}
bool
ravenscar_thread_target::stopped_by_watchpoint ()
{
- scoped_restore save_ptid = make_scoped_restore (&inferior_ptid);
- inferior_ptid = get_base_thread_from_ravenscar_task (inferior_ptid);
+ scoped_restore_current_thread saver;
+ set_base_thread_from_ravenscar_task (inferior_ptid);
return beneath ()->stopped_by_watchpoint ();
}
bool
ravenscar_thread_target::stopped_data_address (CORE_ADDR *addr_p)
{
- scoped_restore save_ptid = make_scoped_restore (&inferior_ptid);
- inferior_ptid = get_base_thread_from_ravenscar_task (inferior_ptid);
+ scoped_restore_current_thread saver;
+ set_base_thread_from_ravenscar_task (inferior_ptid);
return beneath ()->stopped_data_address (addr_p);
}
{
m_base_ptid = null_ptid;
target_ops *beneath = this->beneath ();
- unpush_target (this);
+ current_inferior ()->unpush_target (this);
beneath->mourn_inferior ();
}
int
ravenscar_thread_target::core_of_thread (ptid_t ptid)
+{
+ scoped_restore_current_thread saver;
+ set_base_thread_from_ravenscar_task (inferior_ptid);
+ return beneath ()->core_of_thread (inferior_ptid);
+}
+
+/* Implement the target xfer_partial method. */
+
+enum target_xfer_status
+ravenscar_thread_target::xfer_partial (enum target_object object,
+ const char *annex,
+ gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, ULONGEST len,
+ ULONGEST *xfered_len)
{
scoped_restore save_ptid = make_scoped_restore (&inferior_ptid);
+ /* Calling get_base_thread_from_ravenscar_task can read memory from
+ the inferior. However, that function is written to prefer our
+ internal map, so it should not result in recursive calls in
+ practice. */
inferior_ptid = get_base_thread_from_ravenscar_task (inferior_ptid);
- return beneath ()->core_of_thread (inferior_ptid);
+ return beneath ()->xfer_partial (object, annex, readbuf, writebuf,
+ offset, len, xfered_len);
}
/* Observer on inferior_created: push ravenscar thread stratum if needed. */
static void
-ravenscar_inferior_created (struct target_ops *target, int from_tty)
+ravenscar_inferior_created (inferior *inf)
{
const char *err_msg;
}
ravenscar_thread_target *rtarget = new ravenscar_thread_target ();
- push_target (target_ops_up (rtarget));
+ inf->push_target (target_ops_up (rtarget));
thread_info *thr = rtarget->add_active_thread ();
if (thr != nullptr)
switch_to_thread (thr);
{
/* Notice when the inferior is created in order to push the
ravenscar ops if needed. */
- gdb::observers::inferior_created.attach (ravenscar_inferior_created);
+ gdb::observers::inferior_created.attach (ravenscar_inferior_created,
+ "ravenscar-thread");
add_basic_prefix_cmd ("ravenscar", no_class,
_("Prefix command for changing Ravenscar-specific settings."),
- &set_ravenscar_list, "set ravenscar ", 0, &setlist);
+ &set_ravenscar_list, 0, &setlist);
add_show_prefix_cmd ("ravenscar", no_class,
_("Prefix command for showing Ravenscar-specific settings."),
- &show_ravenscar_list, "show ravenscar ", 0, &showlist);
+ &show_ravenscar_list, 0, &showlist);
add_setshow_boolean_cmd ("task-switching", class_obscure,
- &ravenscar_task_support, _("\
+ &ravenscar_task_support, _("\
Enable or disable support for GNAT Ravenscar tasks."), _("\
Show whether support for GNAT Ravenscar tasks is enabled."),
- _("\
+ _("\
Enable or disable support for task/thread switching with the GNAT\n\
Ravenscar run-time library for bareboard configuration."),
NULL, show_ravenscar_task_switching_command,