Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for qThreadExtraInfo #106

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!

Expand Down
24 changes: 24 additions & 0 deletions examples/armv4t_multicore/gdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ impl MultiThreadBase for Emu {
) -> Option<target::ext::base::multithread::MultiThreadResumeOps<'_, Self>> {
Some(self)
}

#[inline(always)]
fn support_thread_extra_info(
&mut self,
) -> Option<gdbstub::target::ext::thread_extra_info::ThreadExtraInfoOps<'_, Self>> {
Some(self)
}
}

impl MultiThreadResume for Emu {
Expand Down Expand Up @@ -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<usize, Self::Error> {
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 {
thefaxman marked this conversation as resolved.
Show resolved Hide resolved
let len = buf.len().min(data.len());
buf[..len].copy_from_slice(&data[..len]);
len
}
13 changes: 13 additions & 0 deletions src/protocol/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: Target> Hack for T {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -288,4 +297,8 @@ commands! {
catch_syscalls use 'a {
"QCatchSyscalls" => _QCatchSyscalls::QCatchSyscalls<'a>,
}

thread_extra_info use 'a {
"qThreadExtraInfo" => _qThreadExtraInfo::qThreadExtraInfo<'a>,
}
}
32 changes: 32 additions & 0 deletions src/protocol/commands/_qThreadExtraInfo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use super::prelude::*;

use crate::protocol::common::thread_id::ThreadId;
use crate::protocol::ConcreteThreadId;

#[derive(Debug)]
pub struct qThreadExtraInfo<'a> {
pub id: ConcreteThreadId,

pub buf: &'a mut [u8],
}

impl<'a> ParseCommand<'a> for qThreadExtraInfo<'a> {
#[inline(always)]
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
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 = ConcreteThreadId::try_from(ThreadId::try_from(body).ok()?).ok()?;

Some(qThreadExtraInfo { id, buf })
}
_ => None,
}
}
}
34 changes: 34 additions & 0 deletions src/protocol/common/thread_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,37 @@ impl TryFrom<ThreadId> for SpecificThreadId {
})
}
}

/// Like [`ThreadId`], without the `Any`, or `All` variants.
#[derive(Debug, Copy, Clone)]
pub struct ConcreteThreadId {
/// Process ID (may or may not be present).
pub pid: Option<NonZeroUsize>,
/// Thread ID.
pub tid: NonZeroUsize,
}

impl TryFrom<ThreadId> for ConcreteThreadId {
type Error = ();

fn try_from(thread: ThreadId) -> Result<ConcreteThreadId, ()> {
Ok(ConcreteThreadId {
pid: match thread.pid {
None => None,
Some(id_kind) => Some(id_kind.try_into()?),
},
tid: thread.tid.try_into()?,
})
}
}

impl TryFrom<IdKind> for NonZeroUsize {
type Error = ();

fn try_from(value: IdKind) -> Result<NonZeroUsize, ()> {
match value {
IdKind::WithId(v) => Ok(v),
_ => Err(()),
}
}
}
2 changes: 1 addition & 1 deletion src/protocol/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ mod response_writer;
pub(crate) mod commands;
pub(crate) mod recv_packet;

pub(crate) use common::thread_id::{IdKind, SpecificIdKind, SpecificThreadId};
pub(crate) use common::thread_id::{ConcreteThreadId, IdKind, SpecificIdKind, SpecificThreadId};
pub(crate) use packet::Packet;
pub(crate) use response_writer::{Error as ResponseWriterError, ResponseWriter};

Expand Down
2 changes: 2 additions & 0 deletions src/stub/core_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -207,6 +208,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
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
Expand Down
37 changes: 37 additions & 0 deletions src/stub/core_impl/thread_extra_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use super::prelude::*;
use crate::protocol::commands::ext::ThreadExtraInfo;
use crate::target::ext::base::BaseOps;

impl<T: Target, C: Connection> GdbStubImpl<T, C> {
pub(crate) fn handle_thread_extra_info<'a>(
&mut self,
res: &mut ResponseWriter<'_, C>,
target: &mut T,
command: ThreadExtraInfo<'a>,
) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
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) => {
let size = ops
.thread_extra_info(info.id.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)
}
}
8 changes: 8 additions & 0 deletions src/target/ext/base/multithread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ pub trait MultiThreadBase: Target {
fn support_resume(&mut self) -> Option<MultiThreadResumeOps<'_, Self>> {
None
}

/// Support for providing thread extra information.
#[inline(always)]
fn support_thread_extra_info(
&mut self,
) -> Option<crate::target::ext::thread_extra_info::ThreadExtraInfoOps<'_, Self>> {
None
}
}

/// Target extension - support for resuming multi threaded targets.
Expand Down
1 change: 1 addition & 0 deletions src/target/ext/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
22 changes: 22 additions & 0 deletions src/target/ext/thread_extra_info.rs
Original file line number Diff line number Diff line change
@@ -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<usize, Self::Error>;
}

define_ext!(ThreadExtraInfoOps, ThreadExtraInfo);