Skip to content
Closed
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
4 changes: 4 additions & 0 deletions src/protocol/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,10 @@ commands! {
"qProcessInfo" => _qProcessInfo::qProcessInfo,
}

sbpf {
"qSBPFCallStack" => _qSBPFCallStack::qSBPFCallStack,
}

wasm use 'a {
"qWasmCallStack" => _qWasmCallStack::qWasmCallStack,
"qWasmLocal" => _qWasmLocal::qWasmLocal<'a>,
Expand Down
45 changes: 45 additions & 0 deletions src/protocol/commands/_qSBPFCallStack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use super::prelude::*;
use crate::protocol::common::thread_id::ThreadId;
use crate::protocol::ConcreteThreadId;

#[derive(Debug)]
pub struct qSBPFCallStack {
pub tid: ConcreteThreadId,
}

impl<'a> ParseCommand<'a> for qSBPFCallStack {
#[inline(always)]
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let body = buf.into_body();
if body.is_empty() || body[0] != b':' {
return None;
}
let tid = ConcreteThreadId::try_from(ThreadId::try_from(&body[1..]).ok()?).ok()?;
Some(qSBPFCallStack { tid })
}
}

#[cfg(test)]
mod tests {
use super::*;

fn parse(body: &mut [u8]) -> Option<qSBPFCallStack> {
let mut buf = PacketBuf::new_with_raw_body(body).unwrap();
assert!(buf.strip_prefix(b"qSBPFCallStack"));
qSBPFCallStack::from_packet(buf)
}

#[test]
fn parses_concrete_thread() {
let packet = parse(&mut *b"qSBPFCallStack:2a".to_vec()).unwrap();
assert_eq!(packet.tid.pid, None);
assert_eq!(packet.tid.tid.get(), 0x2a);
}

#[test]
fn rejects_missing_or_non_concrete_thread() {
assert!(parse(&mut *b"qSBPFCallStack".to_vec()).is_none());
assert!(parse(&mut *b"qSBPFCallStack:0".to_vec()).is_none());
assert!(parse(&mut *b"qSBPFCallStack:-1".to_vec()).is_none());
}
}
2 changes: 2 additions & 0 deletions src/stub/core_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ mod monitor_cmd;
mod no_ack_mode;
mod resume;
mod reverse_exec;
mod sbpf;
mod section_offsets;
mod single_register_access;
mod target_xml;
Expand Down Expand Up @@ -229,6 +230,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
Command::Tracepoints(cmd) => self.handle_tracepoints(res, target, cmd),
Command::HostInfo(cmd) => self.handle_host_info(res, target, cmd),
Command::ProcessInfo(cmd) => self.handle_process_info(res, target, cmd),
Command::Sbpf(cmd) => self.handle_sbpf(res, target, cmd),
Command::Wasm(cmd) => self.handle_wasm(res, target, cmd),
// in the worst case, the command could not be parsed...
Command::Unknown(cmd) => {
Expand Down
36 changes: 36 additions & 0 deletions src/stub/core_impl/sbpf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use super::prelude::*;
use crate::protocol::commands::ext::Sbpf;

impl<T: Target, C: Connection> GdbStubImpl<T, C> {
pub(crate) fn handle_sbpf(
&mut self,
res: &mut ResponseWriter<'_, C>,
target: &mut T,
command: Sbpf,
) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
let ops = match target.support_sbpf() {
Some(ops) => ops,
None => return Ok(HandlerStatus::Handled),
};

crate::__dead_code_marker!("sbpf", "impl");

match command {
Sbpf::qSBPFCallStack(cmd) => {
let mut error: Result<(), Error<T::Error, C::Error>> = Ok(());
ops.sbpf_call_stack(cmd.tid.tid, &mut |pc, frame_pointer| {
if let Err(e) = res.write_hex_buf(&pc.to_le_bytes()) {
error = Err(e.into());
}
if let Err(e) = res.write_hex_buf(&frame_pointer.to_le_bytes()) {
error = Err(e.into());
}
})
.map_err(Error::TargetError)?;
error?;
}
}

Ok(HandlerStatus::Handled)
}
}
1 change: 1 addition & 0 deletions src/target/ext/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ pub mod lldb_register_info_override;
pub mod memory_map;
pub mod monitor_cmd;
pub mod process_info;
pub mod sbpf;
pub mod section_offsets;
pub mod target_description_xml_override;
pub mod thread_extra_info;
Expand Down
21 changes: 21 additions & 0 deletions src/target/ext/sbpf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//! (LLDB extension) Provide SBPF-specific actions for the target.

use crate::common::Tid;
use crate::target::Target;

/// (LLDB extension) Target extension for SBPF runtime state.
pub trait Sbpf: Target {
/// Get the SBPF call stack for a given thread.
///
/// Call `next_frame` once for each frame, ordered from the active frame to
/// the oldest caller. `pc` is the current PC for the active frame and the
/// return PC for callers. `frame_pointer` is the value of SBPF register
/// r10 for that frame.
fn sbpf_call_stack(
&self,
tid: Tid,
next_frame: &mut dyn FnMut(u64, u64),
) -> Result<(), Self::Error>;
}

define_ext!(SbpfOps, Sbpf);
7 changes: 7 additions & 0 deletions src/target/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,12 @@ pub trait Target {
None
}

/// (LLDB extension) Support for SBPF-specific commands.
#[inline(always)]
fn support_sbpf(&mut self) -> Option<ext::sbpf::SbpfOps<'_, Self>> {
None
}

/// (LLDB extension) Support for WebAssembly (Wasm)-specific commands.
#[inline(always)]
fn support_wasm(&mut self) -> Option<ext::wasm::WasmOps<'_, Self>> {
Expand Down Expand Up @@ -857,6 +863,7 @@ macro_rules! impl_dyn_target {
__delegate_support!(monitor_cmd);
__delegate_support!(process_info);
__delegate_support!(section_offsets);
__delegate_support!(sbpf);
__delegate_support!(target_description_xml_override);
__delegate_support!(tracepoints);
__delegate_support!(wasm);
Expand Down