Skip to content

Commit ed43ae1

Browse files
cczetiercczetier
and
cczetier
authored
Tracepoint extension support (#160)
This PR adds basic [tracepoint extension](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Tracepoints.html) support. Closes #157. --------- Co-authored-by: cczetier <cczetier@zetier.com>
1 parent 6b686e7 commit ed43ae1

26 files changed

+2042
-7
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ Of course, most use-cases will want to support additional debugging features as
8989
- Read auxiliary vector (`info auxv`)
9090
- Extra thread info (`info threads`)
9191
- Extra library information (`info sharedlibraries`)
92+
- Tracepoints
93+
- Configure tracepoints and actions to perform when hit
94+
- Select and interrogate collected trace frames
95+
- _Note:_ Feature support is not exhaustive, and many feature haven't been implemented yet.
9296

9397
_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!
9498

examples/armv4t/emu.rs

+51
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ use armv4t_emu::ExampleMem;
77
use armv4t_emu::Memory;
88
use armv4t_emu::Mode;
99
use gdbstub::common::Pid;
10+
use gdbstub::target::ext::tracepoints::NewTracepoint;
11+
use gdbstub::target::ext::tracepoints::SourceTracepoint;
12+
use gdbstub::target::ext::tracepoints::Tracepoint;
13+
use gdbstub::target::ext::tracepoints::TracepointAction;
14+
use gdbstub::target::ext::tracepoints::TracepointEnumerateState;
15+
use std::collections::BTreeMap;
1016

1117
const HLE_RETURN_ADDR: u32 = 0x12345678;
1218

@@ -25,6 +31,12 @@ pub enum ExecMode {
2531
RangeStep(u32, u32),
2632
}
2733

34+
#[derive(Debug)]
35+
pub struct TraceFrame {
36+
pub number: Tracepoint,
37+
pub snapshot: Cpu,
38+
}
39+
2840
/// incredibly barebones armv4t-based emulator
2941
pub struct Emu {
3042
start_addr: u32,
@@ -41,6 +53,19 @@ pub struct Emu {
4153
pub(crate) breakpoints: Vec<u32>,
4254
pub(crate) files: Vec<Option<std::fs::File>>,
4355

56+
pub(crate) tracepoints: BTreeMap<
57+
Tracepoint,
58+
(
59+
NewTracepoint<u32>,
60+
Vec<SourceTracepoint<'static, u32>>,
61+
Vec<TracepointAction<'static, u32>>,
62+
),
63+
>,
64+
pub(crate) traceframes: Vec<TraceFrame>,
65+
pub(crate) tracepoint_enumerate_state: TracepointEnumerateState<u32>,
66+
pub(crate) tracing: bool,
67+
pub(crate) selected_frame: Option<usize>,
68+
4469
pub(crate) reported_pid: Pid,
4570
}
4671

@@ -93,6 +118,12 @@ impl Emu {
93118
breakpoints: Vec::new(),
94119
files: Vec::new(),
95120

121+
tracepoints: BTreeMap::new(),
122+
traceframes: Vec::new(),
123+
tracepoint_enumerate_state: Default::default(),
124+
tracing: false,
125+
selected_frame: None,
126+
96127
reported_pid: Pid::new(1).unwrap(),
97128
})
98129
}
@@ -106,6 +137,26 @@ impl Emu {
106137

107138
/// single-step the interpreter
108139
pub fn step(&mut self) -> Option<Event> {
140+
if self.tracing {
141+
let pc = self.cpu.reg_get(self.cpu.mode(), reg::PC);
142+
let frames: Vec<_> = self
143+
.tracepoints
144+
.iter()
145+
.filter(|(_tracepoint, (ctp, _source, _actions))| ctp.enabled && ctp.addr == pc)
146+
.map(|(tracepoint, _definition)| {
147+
// our `tracepoint_define` restricts our loaded tracepoints to only contain
148+
// register collect actions. instead of only collecting the registers requested
149+
// in the register mask and recording a minimal trace frame, we just collect
150+
// all of them by cloning the cpu itself.
151+
TraceFrame {
152+
number: *tracepoint,
153+
snapshot: self.cpu,
154+
}
155+
})
156+
.collect();
157+
self.traceframes.extend(frames);
158+
}
159+
109160
let mut hit_watchpoint = None;
110161

111162
let mut sniffer = MemSniffer::new(&mut self.mem, &self.watchpoints, |access| {

examples/armv4t/gdb/mod.rs

+35-6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ mod memory_map;
2626
mod monitor_cmd;
2727
mod section_offsets;
2828
mod target_description_xml_override;
29+
pub(crate) mod tracepoints;
2930

3031
/// Turn a `ArmCoreRegId` into an internal register number of `armv4t_emu`.
3132
fn cpu_reg_id(id: ArmCoreRegId) -> Option<u8> {
@@ -161,29 +162,46 @@ impl Target for Emu {
161162
) -> Option<target::ext::libraries::LibrariesSvr4Ops<'_, Self>> {
162163
Some(self)
163164
}
165+
166+
#[inline(always)]
167+
fn support_tracepoints(
168+
&mut self,
169+
) -> Option<target::ext::tracepoints::TracepointsOps<'_, Self>> {
170+
Some(self)
171+
}
164172
}
165173

166174
impl SingleThreadBase for Emu {
167175
fn read_registers(
168176
&mut self,
169177
regs: &mut custom_arch::ArmCoreRegsCustom,
170178
) -> TargetResult<(), Self> {
171-
let mode = self.cpu.mode();
179+
// if we selected a frame from a tracepoint, return registers from that snapshot
180+
let cpu = self
181+
.selected_frame
182+
.and_then(|selected| self.traceframes.get(selected))
183+
.map(|frame| frame.snapshot)
184+
.unwrap_or_else(|| self.cpu);
185+
let mode = cpu.mode();
172186

173187
for i in 0..13 {
174-
regs.core.r[i] = self.cpu.reg_get(mode, i as u8);
188+
regs.core.r[i] = cpu.reg_get(mode, i as u8);
175189
}
176-
regs.core.sp = self.cpu.reg_get(mode, reg::SP);
177-
regs.core.lr = self.cpu.reg_get(mode, reg::LR);
178-
regs.core.pc = self.cpu.reg_get(mode, reg::PC);
179-
regs.core.cpsr = self.cpu.reg_get(mode, reg::CPSR);
190+
regs.core.sp = cpu.reg_get(mode, reg::SP);
191+
regs.core.lr = cpu.reg_get(mode, reg::LR);
192+
regs.core.pc = cpu.reg_get(mode, reg::PC);
193+
regs.core.cpsr = cpu.reg_get(mode, reg::CPSR);
180194

181195
regs.custom = self.custom_reg;
182196

183197
Ok(())
184198
}
185199

186200
fn write_registers(&mut self, regs: &custom_arch::ArmCoreRegsCustom) -> TargetResult<(), Self> {
201+
if self.selected_frame.is_some() {
202+
// we can't modify registers in a tracepoint frame
203+
return Err(TargetError::NonFatal);
204+
}
187205
let mode = self.cpu.mode();
188206

189207
for i in 0..13 {
@@ -208,6 +226,12 @@ impl SingleThreadBase for Emu {
208226
}
209227

210228
fn read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<usize, Self> {
229+
if self.selected_frame.is_some() {
230+
// we only support register collection actions for our tracepoint frames.
231+
// if we have a selected frame, then we don't have any memory we can
232+
// return from the frame snapshot.
233+
return Ok(0);
234+
}
211235
// this is a simple emulator, with RAM covering the entire 32 bit address space
212236
for (addr, val) in (start_addr..).zip(data.iter_mut()) {
213237
*val = self.mem.r8(addr)
@@ -216,6 +240,11 @@ impl SingleThreadBase for Emu {
216240
}
217241

218242
fn write_addrs(&mut self, start_addr: u32, data: &[u8]) -> TargetResult<(), Self> {
243+
if self.selected_frame.is_some() {
244+
// we can't modify memory in a tracepoint frame
245+
return Err(TargetError::NonFatal);
246+
}
247+
219248
// this is a simple emulator, with RAM covering the entire 32 bit address space
220249
for (addr, val) in (start_addr..).zip(data.iter().copied()) {
221250
self.mem.w8(addr, val)

0 commit comments

Comments
 (0)