From 0dd80860e9cce683d29e78d129251df225953bb5 Mon Sep 17 00:00:00 2001 From: Ryan Fairfax Date: Tue, 7 Jun 2022 19:17:08 -0700 Subject: [PATCH] Add support for qThreadExtraInfo Add a new sub-IDET for `MultiThreadBase` to respond with extra information about a thread Signed-off-by: Ryan Fairfax --- README.md | 1 + examples/armv4t_multicore/gdb.rs | 24 +++++++++++++ src/protocol/commands.rs | 13 +++++++ src/protocol/commands/_qThreadExtraInfo.rs | 34 ++++++++++++++++++ src/stub/core_impl.rs | 2 ++ src/stub/core_impl/thread_extra_info.rs | 40 ++++++++++++++++++++++ src/target/ext/base/multithread.rs | 8 +++++ src/target/ext/mod.rs | 1 + src/target/ext/thread_extra_info.rs | 22 ++++++++++++ 9 files changed, 145 insertions(+) create mode 100644 src/protocol/commands/_qThreadExtraInfo.rs create mode 100644 src/stub/core_impl/thread_extra_info.rs create mode 100644 src/target/ext/thread_extra_info.rs diff --git a/README.md b/README.md index 5c6e1767..3e11a06c 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,7 @@ Of course, most use-cases will want to support additional debugging features as - Access the remote target's filesystem to read/write file - Can be used to automatically read the remote executable on attach (using `ExecFile`) - Read auxiliary vector (`info auxv`) +- Extra thread info (`info threads`) _Note:_ GDB features are implemented on an as-needed basis by `gdbstub`'s contributors. If there's a missing GDB feature that you'd like `gdbstub` to implement, please file an issue and/or open a PR! diff --git a/examples/armv4t_multicore/gdb.rs b/examples/armv4t_multicore/gdb.rs index 9559d3da..3c8cd75a 100644 --- a/examples/armv4t_multicore/gdb.rs +++ b/examples/armv4t_multicore/gdb.rs @@ -126,6 +126,13 @@ impl MultiThreadBase for Emu { ) -> Option> { Some(self) } + + #[inline(always)] + fn support_thread_extra_info( + &mut self, + ) -> Option> { + Some(self) + } } impl MultiThreadResume for Emu { @@ -272,3 +279,20 @@ impl target::ext::breakpoints::HwWatchpoint for Emu { Ok(true) } } + +impl target::ext::thread_extra_info::ThreadExtraInfo for Emu { + fn thread_extra_info(&self, tid: Tid, buf: &mut [u8]) -> Result { + let cpu_id = tid_to_cpuid(tid)?; + let info = format!("CPU {:?}", cpu_id); + + Ok(copy_to_buf(info.as_bytes(), buf)) + } +} + +/// Copy all bytes of `data` to `buf`. +/// Return the size of data copied. +pub fn copy_to_buf(data: &[u8], buf: &mut [u8]) -> usize { + let len = buf.len().min(data.len()); + buf[..len].copy_from_slice(&data[..len]); + len +} diff --git a/src/protocol/commands.rs b/src/protocol/commands.rs index e96a1a59..1e1f973f 100644 --- a/src/protocol/commands.rs +++ b/src/protocol/commands.rs @@ -92,6 +92,7 @@ macro_rules! commands { fn support_reverse_step(&mut self) -> Option<()>; fn support_reverse_cont(&mut self) -> Option<()>; fn support_x_upcase_packet(&mut self) -> Option<()>; + fn support_thread_extra_info(&mut self) -> Option<()>; } impl Hack for T { @@ -146,6 +147,14 @@ macro_rules! commands { None } } + + fn support_thread_extra_info(&mut self) -> Option<()> { + use crate::target::ext::base::BaseOps; + match self.base_ops() { + BaseOps::SingleThread(_) => None, + BaseOps::MultiThread(ops) => ops.support_thread_extra_info().map(drop), + } + } } // TODO?: use tries for more efficient longest prefix matching @@ -288,4 +297,8 @@ commands! { catch_syscalls use 'a { "QCatchSyscalls" => _QCatchSyscalls::QCatchSyscalls<'a>, } + + thread_extra_info use 'a { + "qThreadExtraInfo" => _qThreadExtraInfo::qThreadExtraInfo<'a>, + } } diff --git a/src/protocol/commands/_qThreadExtraInfo.rs b/src/protocol/commands/_qThreadExtraInfo.rs new file mode 100644 index 00000000..6008eac8 --- /dev/null +++ b/src/protocol/commands/_qThreadExtraInfo.rs @@ -0,0 +1,34 @@ +use super::prelude::*; + +use crate::protocol::common::thread_id::ThreadId; +use crate::protocol::SpecificThreadId; + +#[derive(Debug)] +pub struct qThreadExtraInfo<'a> { + pub id: SpecificThreadId, + + pub buf: &'a mut [u8], +} + +impl<'a> ParseCommand<'a> for qThreadExtraInfo<'a> { + #[inline(always)] + fn from_packet(buf: PacketBuf<'a>) -> Option { + let (buf, body_range) = buf.into_raw_buf(); + let body = buf.get(body_range.start..body_range.end)?; + + if body.is_empty() { + return None; + } + + match body { + [b',', body @ ..] => { + let id = SpecificThreadId::try_from(ThreadId::try_from(body).ok()?).ok()?; + + drop(body); + + Some(qThreadExtraInfo { id, buf }) + } + _ => None, + } + } +} diff --git a/src/stub/core_impl.rs b/src/stub/core_impl.rs index 82f3657b..d7c142e8 100644 --- a/src/stub/core_impl.rs +++ b/src/stub/core_impl.rs @@ -35,6 +35,7 @@ mod reverse_exec; mod section_offsets; mod single_register_access; mod target_xml; +mod thread_extra_info; mod x_upcase_packet; pub(crate) use resume::FinishExecStatus; @@ -207,6 +208,7 @@ impl GdbStubImpl { Command::HostIo(cmd) => self.handle_host_io(res, target, cmd), Command::ExecFile(cmd) => self.handle_exec_file(res, target, cmd), Command::Auxv(cmd) => self.handle_auxv(res, target, cmd), + Command::ThreadExtraInfo(cmd) => self.handle_thread_extra_info(res, target, cmd), // in the worst case, the command could not be parsed... Command::Unknown(cmd) => { // HACK: if the user accidentally sends a resume command to a diff --git a/src/stub/core_impl/thread_extra_info.rs b/src/stub/core_impl/thread_extra_info.rs new file mode 100644 index 00000000..b5bfad36 --- /dev/null +++ b/src/stub/core_impl/thread_extra_info.rs @@ -0,0 +1,40 @@ +use super::prelude::*; +use crate::protocol::commands::ext::ThreadExtraInfo; +use crate::protocol::SpecificIdKind; +use crate::target::ext::base::BaseOps; + +impl GdbStubImpl { + pub(crate) fn handle_thread_extra_info<'a>( + &mut self, + res: &mut ResponseWriter<'_, C>, + target: &mut T, + command: ThreadExtraInfo<'a>, + ) -> Result> { + let ops = match target.base_ops() { + BaseOps::SingleThread(_) => return Ok(HandlerStatus::Handled), + BaseOps::MultiThread(ops) => match ops.support_thread_extra_info() { + Some(ops) => ops, + None => return Ok(HandlerStatus::Handled), + }, + }; + + crate::__dead_code_marker!("thread_extra_info", "impl"); + + let handler_status = match command { + ThreadExtraInfo::qThreadExtraInfo(info) => { + if let SpecificIdKind::WithId(tid) = info.id.tid { + let size = ops + .thread_extra_info(tid, info.buf) + .map_err(Error::TargetError)?; + let data = info.buf.get(..size).ok_or(Error::PacketBufferOverflow)?; + + res.write_hex_buf(data)?; + } + + HandlerStatus::Handled + } + }; + + Ok(handler_status) + } +} diff --git a/src/target/ext/base/multithread.rs b/src/target/ext/base/multithread.rs index 0971e1f7..693030ef 100644 --- a/src/target/ext/base/multithread.rs +++ b/src/target/ext/base/multithread.rs @@ -102,6 +102,14 @@ pub trait MultiThreadBase: Target { fn support_resume(&mut self) -> Option> { None } + + /// Support for providing thread extra information. + #[inline(always)] + fn support_thread_extra_info( + &mut self, + ) -> Option> { + None + } } /// Target extension - support for resuming multi threaded targets. diff --git a/src/target/ext/mod.rs b/src/target/ext/mod.rs index 81989360..ef2c155f 100644 --- a/src/target/ext/mod.rs +++ b/src/target/ext/mod.rs @@ -269,3 +269,4 @@ pub mod memory_map; pub mod monitor_cmd; pub mod section_offsets; pub mod target_description_xml_override; +pub mod thread_extra_info; diff --git a/src/target/ext/thread_extra_info.rs b/src/target/ext/thread_extra_info.rs new file mode 100644 index 00000000..9923d4d6 --- /dev/null +++ b/src/target/ext/thread_extra_info.rs @@ -0,0 +1,22 @@ +//! Provide extra information for a thread +use crate::common::Tid; +use crate::target::Target; + +/// Target Extension - Provide extra information for a thread +pub trait ThreadExtraInfo: Target { + /// Provide extra information about a thread + /// + /// GDB queries for extra information for a thread as part of the + /// `info threads` command. This function will be called once + /// for each active thread. + /// + /// A string can be copied into `buf` that will then be displayed + /// to the client. The string is displayed as `(value)`, such as: + /// + /// `Thread 1.1 (value)` + /// + /// Return the number of bytes written into `buf`. + fn thread_extra_info(&self, tid: Tid, buf: &mut [u8]) -> Result; +} + +define_ext!(ThreadExtraInfoOps, ThreadExtraInfo);