Public dry run dryrun-20b59dc46b72
Source commit: 20b59dc46b72103c2f8a516c692b5cc3d54fab19 Public tree identity: sha256:aaa5ac7b58b2a0d23c6b11e5f76324bf3839ca1f62653a83deb736932adcfbd9
This commit is contained in:
commit
2bef715211
221 changed files with 79792 additions and 0 deletions
278
crates/disasmer-core/src/debug.rs
Normal file
278
crates/disasmer-core/src/debug.rs
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{ProcessId, TaskInstanceId};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DebugParticipantKind {
|
||||
WasmTask,
|
||||
ControlledNativeCommand,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DebugRuntimeState {
|
||||
Running,
|
||||
Frozen,
|
||||
Completed,
|
||||
Failed(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct DebugParticipant {
|
||||
pub task: TaskInstanceId,
|
||||
pub name: String,
|
||||
pub kind: DebugParticipantKind,
|
||||
pub can_freeze: bool,
|
||||
pub state: DebugRuntimeState,
|
||||
pub stack_frames: Vec<String>,
|
||||
pub local_values: Vec<(String, String)>,
|
||||
pub task_args: Vec<(String, String)>,
|
||||
pub handles: Vec<(String, String)>,
|
||||
pub command_status: Option<String>,
|
||||
pub recent_output: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DebugStopReason {
|
||||
Breakpoint { task: TaskInstanceId, line: u32 },
|
||||
PauseRequest,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ThreadInspection {
|
||||
pub task: TaskInstanceId,
|
||||
pub name: String,
|
||||
pub stack_frames: Vec<String>,
|
||||
pub local_values: Vec<(String, String)>,
|
||||
pub task_args: Vec<(String, String)>,
|
||||
pub handles: Vec<(String, String)>,
|
||||
pub command_status: Option<String>,
|
||||
pub recent_output: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct DebugEpoch {
|
||||
pub process: ProcessId,
|
||||
pub epoch: u64,
|
||||
pub reason: DebugStopReason,
|
||||
participants: BTreeMap<TaskInstanceId, DebugParticipant>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error, PartialEq, Eq)]
|
||||
pub enum DebugEpochError {
|
||||
#[error("participant `{task}` cannot freeze, so all-stop failed")]
|
||||
CannotFreeze { task: TaskInstanceId },
|
||||
#[error("participant `{0}` is not part of this debug epoch")]
|
||||
UnknownParticipant(TaskInstanceId),
|
||||
}
|
||||
|
||||
impl DebugEpoch {
|
||||
pub fn all_stop(
|
||||
process: ProcessId,
|
||||
epoch: u64,
|
||||
reason: DebugStopReason,
|
||||
participants: Vec<DebugParticipant>,
|
||||
) -> Result<Self, DebugEpochError> {
|
||||
for participant in &participants {
|
||||
if matches!(
|
||||
participant.kind,
|
||||
DebugParticipantKind::WasmTask | DebugParticipantKind::ControlledNativeCommand
|
||||
) && !participant.can_freeze
|
||||
{
|
||||
return Err(DebugEpochError::CannotFreeze {
|
||||
task: participant.task.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let participants = participants
|
||||
.into_iter()
|
||||
.map(|mut participant| {
|
||||
if matches!(participant.state, DebugRuntimeState::Running) {
|
||||
participant.state = DebugRuntimeState::Frozen;
|
||||
}
|
||||
(participant.task.clone(), participant)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Self {
|
||||
process,
|
||||
epoch,
|
||||
reason,
|
||||
participants,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pause(
|
||||
process: ProcessId,
|
||||
epoch: u64,
|
||||
participants: Vec<DebugParticipant>,
|
||||
) -> Result<Self, DebugEpochError> {
|
||||
Self::all_stop(process, epoch, DebugStopReason::PauseRequest, participants)
|
||||
}
|
||||
|
||||
pub fn continue_all(&mut self) {
|
||||
for participant in self.participants.values_mut() {
|
||||
if participant.state == DebugRuntimeState::Frozen {
|
||||
participant.state = DebugRuntimeState::Running;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inspection(&self, task: &TaskInstanceId) -> Result<ThreadInspection, DebugEpochError> {
|
||||
let participant = self
|
||||
.participants
|
||||
.get(task)
|
||||
.ok_or_else(|| DebugEpochError::UnknownParticipant(task.clone()))?;
|
||||
Ok(ThreadInspection {
|
||||
task: participant.task.clone(),
|
||||
name: participant.name.clone(),
|
||||
stack_frames: participant.stack_frames.clone(),
|
||||
local_values: participant.local_values.clone(),
|
||||
task_args: participant.task_args.clone(),
|
||||
handles: participant.handles.clone(),
|
||||
command_status: participant.command_status.clone(),
|
||||
recent_output: participant.recent_output.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn participant_state(&self, task: &TaskInstanceId) -> Option<&DebugRuntimeState> {
|
||||
self.participants
|
||||
.get(task)
|
||||
.map(|participant| &participant.state)
|
||||
}
|
||||
|
||||
pub fn thread_names(&self) -> Vec<String> {
|
||||
self.participants
|
||||
.values()
|
||||
.map(|participant| participant.name.clone())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn participant(task: &str, kind: DebugParticipantKind, can_freeze: bool) -> DebugParticipant {
|
||||
DebugParticipant {
|
||||
task: TaskInstanceId::from(task),
|
||||
name: task.to_owned(),
|
||||
kind,
|
||||
can_freeze,
|
||||
state: DebugRuntimeState::Running,
|
||||
stack_frames: vec![format!("{task}::run")],
|
||||
local_values: vec![("wasm_local_0".to_owned(), "I32(41)".to_owned())],
|
||||
task_args: vec![("target".to_owned(), "linux".to_owned())],
|
||||
handles: vec![("artifact".to_owned(), "artifact-1".to_owned())],
|
||||
command_status: Some("running".to_owned()),
|
||||
recent_output: vec!["building".to_owned()],
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn breakpoint_creates_all_stop_debug_epoch_for_wasm_and_command_tasks() {
|
||||
let epoch = DebugEpoch::all_stop(
|
||||
ProcessId::from("process"),
|
||||
1,
|
||||
DebugStopReason::Breakpoint {
|
||||
task: TaskInstanceId::from("compile-linux"),
|
||||
line: 42,
|
||||
},
|
||||
vec![
|
||||
participant("main", DebugParticipantKind::WasmTask, true),
|
||||
participant(
|
||||
"compile-linux",
|
||||
DebugParticipantKind::ControlledNativeCommand,
|
||||
true,
|
||||
),
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
epoch.participant_state(&TaskInstanceId::from("main")),
|
||||
Some(&DebugRuntimeState::Frozen)
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.participant_state(&TaskInstanceId::from("compile-linux")),
|
||||
Some(&DebugRuntimeState::Frozen)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debug_epoch_reports_freeze_failure_instead_of_claiming_all_stop() {
|
||||
let error = DebugEpoch::pause(
|
||||
ProcessId::from("process"),
|
||||
1,
|
||||
vec![participant(
|
||||
"compile-linux",
|
||||
DebugParticipantKind::ControlledNativeCommand,
|
||||
false,
|
||||
)],
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(error, DebugEpochError::CannotFreeze { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn continue_resumes_every_frozen_participant() {
|
||||
let mut epoch = DebugEpoch::pause(
|
||||
ProcessId::from("process"),
|
||||
1,
|
||||
vec![
|
||||
participant("main", DebugParticipantKind::WasmTask, true),
|
||||
participant("task", DebugParticipantKind::WasmTask, true),
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
epoch.continue_all();
|
||||
|
||||
assert_eq!(
|
||||
epoch.participant_state(&TaskInstanceId::from("main")),
|
||||
Some(&DebugRuntimeState::Running)
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.participant_state(&TaskInstanceId::from("task")),
|
||||
Some(&DebugRuntimeState::Running)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inspection_exposes_stack_args_handles_command_status_and_output() {
|
||||
let epoch = DebugEpoch::pause(
|
||||
ProcessId::from("process"),
|
||||
1,
|
||||
vec![participant(
|
||||
"compile-linux",
|
||||
DebugParticipantKind::ControlledNativeCommand,
|
||||
true,
|
||||
)],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let inspection = epoch
|
||||
.inspection(&TaskInstanceId::from("compile-linux"))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(inspection.stack_frames, vec!["compile-linux::run"]);
|
||||
assert_eq!(
|
||||
inspection.local_values[0],
|
||||
("wasm_local_0".to_owned(), "I32(41)".to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
inspection.task_args[0],
|
||||
("target".to_owned(), "linux".to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
inspection.handles[0],
|
||||
("artifact".to_owned(), "artifact-1".to_owned())
|
||||
);
|
||||
assert_eq!(inspection.command_status, Some("running".to_owned()));
|
||||
assert_eq!(inspection.recent_output, vec!["building"]);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue