blob: 43253e752035eb96adbc590495f4ef92c85a1c38 [file] [log] [blame]
use crate::emu::Emu;
use gdbstub::target;
use gdbstub::target::ext::tracepoints::ExperimentExplanation;
use gdbstub::target::ext::tracepoints::ExperimentStatus;
use gdbstub::target::ext::tracepoints::FrameDescription;
use gdbstub::target::ext::tracepoints::FrameRequest;
use gdbstub::target::ext::tracepoints::NewTracepoint;
use gdbstub::target::ext::tracepoints::SourceTracepoint;
use gdbstub::target::ext::tracepoints::TraceBufferConfig;
use gdbstub::target::ext::tracepoints::Tracepoint;
use gdbstub::target::ext::tracepoints::TracepointAction;
use gdbstub::target::ext::tracepoints::TracepointEnumerateState;
use gdbstub::target::ext::tracepoints::TracepointEnumerateStep;
use gdbstub::target::ext::tracepoints::TracepointStatus;
use gdbstub::target::TargetError;
use gdbstub::target::TargetResult;
impl Emu {
fn step_to_next_tracepoint(&self, tp: Tracepoint) -> TracepointEnumerateStep<u32> {
let next_tp = self.tracepoints.range(tp..).nth(1);
if let Some((tp, (new_tp, _, _))) = next_tp {
TracepointEnumerateStep::Next {
tp: *tp,
addr: new_tp.addr,
}
} else {
// No more tracepoints
TracepointEnumerateStep::Done
}
}
}
impl target::ext::tracepoints::Tracepoints for Emu {
fn tracepoints_init(&mut self) -> TargetResult<(), Self> {
self.tracepoints.clear();
self.traceframes.clear();
Ok(())
}
fn tracepoint_create_begin(&mut self, tp: NewTracepoint<u32>) -> TargetResult<(), Self> {
self.tracepoints.insert(tp.number, (tp, vec![], vec![]));
Ok(())
}
fn tracepoint_create_continue(
&mut self,
tp: Tracepoint,
action: &TracepointAction<'_, u32>,
) -> TargetResult<(), Self> {
if let &TracepointAction::Registers { mask: _ } = &action {
// we only handle register collection actions for the simple
// case
} else {
return Err(TargetError::NonFatal);
}
self.tracepoints
.get_mut(&tp)
.map(move |(_ctp, _source, actions)| actions.push(action.get_owned()))
.ok_or(TargetError::Fatal("extend on non-existing tracepoint"))
}
fn tracepoint_create_complete(&mut self, _tp: Tracepoint) -> TargetResult<(), Self> {
/* nothing to do */
Ok(())
}
fn tracepoint_status(
&self,
tp: Tracepoint,
_addr: u32,
) -> TargetResult<TracepointStatus, Self> {
// We don't collect "real" trace buffer frames, so just report hit count
// and say the number of bytes is always 0.
// Because we don't implement "while-stepping" actions, we don't need to
// also check that `addr` matches.
Ok(TracepointStatus {
hit_count: self
.traceframes
.iter()
.filter(|frame| frame.number.0 == tp.0)
.count() as u64,
bytes_used: 0,
})
}
fn tracepoint_enumerate_state(&mut self) -> &mut TracepointEnumerateState<u32> {
&mut self.tracepoint_enumerate_state
}
fn tracepoint_enumerate_start(
&mut self,
tp: Option<Tracepoint>,
f: &mut dyn FnMut(&NewTracepoint<u32>),
) -> TargetResult<TracepointEnumerateStep<u32>, Self> {
let tp = match tp {
Some(tp) => tp,
None => {
// We have no tracepoints to report
if self.tracepoints.is_empty() {
return Ok(TracepointEnumerateStep::Done);
} else {
// Start enumerating at the first one
*self.tracepoints.keys().next().unwrap()
}
}
};
// Report our tracepoint
(f)(&self.tracepoints[&tp].0);
let ret = if !self.tracepoints[&tp].1.is_empty() {
TracepointEnumerateStep::Source
} else if !self.tracepoints[&tp].2.is_empty() {
TracepointEnumerateStep::Action
} else {
TracepointEnumerateStep::Done
};
Ok(ret)
}
fn tracepoint_enumerate_action(
&mut self,
tp: Tracepoint,
step: u64,
f: &mut dyn FnMut(&TracepointAction<'_, u32>),
) -> TargetResult<TracepointEnumerateStep<u32>, Self> {
// Report our next action
(f)(&self.tracepoints[&tp].2[step as usize]);
let ret = if self.tracepoints[&tp].2.get((step as usize) + 1).is_some() {
// Continue stepping
TracepointEnumerateStep::Action
} else if !self.tracepoints[&tp].1.is_empty() {
// We're done with this tracepoint, report source
TracepointEnumerateStep::Source
} else {
// No sources, move to the next tracepoint
self.step_to_next_tracepoint(tp)
};
Ok(ret)
}
#[inline(always)]
fn support_tracepoint_source(
&mut self,
) -> Option<target::ext::tracepoints::TracepointSourceOps<'_, Self>> {
Some(self)
}
fn trace_buffer_configure(&mut self, _config: TraceBufferConfig) -> TargetResult<(), Self> {
// we don't collect a "real" trace buffer, so just ignore configuration
// attempts.
Ok(())
}
fn trace_buffer_request(
&mut self,
_offset: u64,
_len: usize,
_f: &mut dyn FnMut(&mut [u8]),
) -> TargetResult<(), Self> {
// We don't have a "real" trace buffer, so just don't report any data
Ok(())
}
fn trace_experiment_status(
&self,
report: &mut dyn FnMut(ExperimentStatus<'_>),
) -> TargetResult<(), Self> {
// For a bare-bones example, we don't provide in-depth status explanations.
(report)(if self.tracing {
ExperimentStatus::Running
} else {
ExperimentStatus::NotRunning
});
Ok(())
}
fn trace_experiment_info(
&self,
report: &mut dyn FnMut(ExperimentExplanation<'_>),
) -> TargetResult<(), Self> {
(report)(ExperimentExplanation::Frames(self.traceframes.len()));
Ok(())
}
fn select_frame(
&mut self,
frame: FrameRequest<u32>,
report: &mut dyn FnMut(FrameDescription),
) -> TargetResult<(), Self> {
// For a bare-bones example, we only support `tfind <number>` and `tfind
// tracepoint <tpnum>` style frame selection and not the more
// complicated ones.
let found = match frame {
FrameRequest::Select(n) => self.traceframes.get(n as usize).map(|frame| (n, frame)),
FrameRequest::Hit(tp) => {
let start = self
.selected_frame
.map(|selected| selected + 1)
.unwrap_or(0);
self.traceframes.get(start..).and_then(|frames| {
frames
.iter()
.enumerate()
.filter(|(_n, frame)| frame.number == tp)
.map(|(n, frame)| ((start + n) as u64, frame))
.next()
})
}
_ => return Err(TargetError::NonFatal),
};
if let Some((n, frame)) = found {
(report)(FrameDescription::FrameNumber(n));
(report)(FrameDescription::Hit(frame.number));
self.selected_frame = Some(n as usize);
} else {
self.selected_frame = None;
}
Ok(())
}
fn trace_experiment_start(&mut self) -> TargetResult<(), Self> {
self.tracing = true;
Ok(())
}
fn trace_experiment_stop(&mut self) -> TargetResult<(), Self> {
self.tracing = false;
Ok(())
}
}
impl target::ext::tracepoints::TracepointSource for Emu {
fn tracepoint_enumerate_source(
&mut self,
tp: Tracepoint,
step: u64,
f: &mut dyn FnMut(&SourceTracepoint<'_, u32>),
) -> TargetResult<TracepointEnumerateStep<u32>, Self> {
// Report our next source item
(f)(&self.tracepoints[&tp].1[step as usize]);
let ret = if self.tracepoints[&tp].1.get((step as usize) + 1).is_some() {
// Continue stepping
TracepointEnumerateStep::Source
} else {
// Move to next tracepoint
self.step_to_next_tracepoint(tp)
};
Ok(ret)
}
fn tracepoint_attach_source(
&mut self,
src: SourceTracepoint<'_, u32>,
) -> TargetResult<(), Self> {
self.tracepoints
.get_mut(&src.number)
.unwrap()
.1
.push(src.get_owned());
Ok(())
}
}