Skip to content

Commit

Permalink
Migrate Miri to the orchestrator
Browse files Browse the repository at this point in the history
  • Loading branch information
shepmaster committed Nov 29, 2023
1 parent aee946b commit c9a730c
Show file tree
Hide file tree
Showing 5 changed files with 309 additions and 170 deletions.
219 changes: 219 additions & 0 deletions compiler/base/orchestrator/src/coordinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,54 @@ pub struct ClippyResponse {
pub exit_detail: String,
}

#[derive(Debug, Clone)]
pub struct MiriRequest {
pub channel: Channel,
pub crate_type: CrateType,
pub edition: Edition,
pub code: String,
}

impl MiriRequest {
pub(crate) fn delete_previous_main_request(&self) -> DeleteFileRequest {
delete_previous_primary_file_request(self.crate_type)
}

pub(crate) fn write_main_request(&self) -> WriteFileRequest {
write_primary_file_request(self.crate_type, &self.code)
}

pub(crate) fn execute_cargo_request(&self) -> ExecuteCommandRequest {
ExecuteCommandRequest {
cmd: "cargo".to_owned(),
args: vec!["miri-playground".to_owned()],
envs: Default::default(),
cwd: None,
}
}
}

impl CargoTomlModifier for MiriRequest {
fn modify_cargo_toml(&self, mut cargo_toml: toml::Value) -> toml::Value {
if self.edition == Edition::Rust2024 {
cargo_toml = modify_cargo_toml::set_feature_edition2024(cargo_toml);
}

cargo_toml = modify_cargo_toml::set_edition(cargo_toml, self.edition.to_cargo_toml_key());

if let Some(crate_type) = self.crate_type.to_library_cargo_toml_key() {
cargo_toml = modify_cargo_toml::set_crate_type(cargo_toml, crate_type);
}
cargo_toml
}
}

#[derive(Debug, Clone)]
pub struct MiriResponse {
pub success: bool,
pub exit_detail: String,
}

#[derive(Debug, Clone)]
pub struct WithOutput<T> {
pub response: T,
Expand Down Expand Up @@ -649,6 +697,30 @@ where
.await
}

pub async fn miri(&self, request: MiriRequest) -> Result<WithOutput<MiriResponse>, MiriError> {
use miri_error::*;

self.select_channel(request.channel)
.await
.context(CouldNotStartContainerSnafu)?
.miri(request)
.await
}

pub async fn begin_miri(
&self,
token: CancellationToken,
request: MiriRequest,
) -> Result<ActiveMiri, MiriError> {
use miri_error::*;

self.select_channel(request.channel)
.await
.context(CouldNotStartContainerSnafu)?
.begin_miri(token, request)
.await
}

pub async fn idle(&mut self) -> Result<()> {
let Self {
stable,
Expand Down Expand Up @@ -1076,6 +1148,75 @@ impl Container {
})
}

async fn miri(&self, request: MiriRequest) -> Result<WithOutput<MiriResponse>, MiriError> {
let token = Default::default();

let ActiveMiri {
task,
stdout_rx,
stderr_rx,
} = self.begin_miri(token, request).await?;

WithOutput::try_absorb(task, stdout_rx, stderr_rx).await
}

async fn begin_miri(
&self,
token: CancellationToken,
request: MiriRequest,
) -> Result<ActiveMiri, MiriError> {
use miri_error::*;

let delete_previous_main = request.delete_previous_main_request();
let write_main = request.write_main_request();
let execute_cargo = request.execute_cargo_request();

let delete_previous_main = self.commander.one(delete_previous_main);
let write_main = self.commander.one(write_main);
let modify_cargo_toml = self.modify_cargo_toml.modify_for(&request);

let (delete_previous_main, write_main, modify_cargo_toml) =
join!(delete_previous_main, write_main, modify_cargo_toml);

delete_previous_main.context(CouldNotDeletePreviousCodeSnafu)?;
write_main.context(CouldNotWriteCodeSnafu)?;
modify_cargo_toml.context(CouldNotModifyCargoTomlSnafu)?;

let SpawnCargo {
task,
stdin_tx,
stdout_rx,
stderr_rx,
} = self
.spawn_cargo_task(token, execute_cargo)
.await
.context(CouldNotStartCargoSnafu)?;

drop(stdin_tx);

let task = async move {
let ExecuteCommandResponse {
success,
exit_detail,
} = task
.await
.context(CargoTaskPanickedSnafu)?
.context(CargoFailedSnafu)?;

Ok(MiriResponse {
success,
exit_detail,
})
}
.boxed();

Ok(ActiveMiri {
task,
stdout_rx,
stderr_rx,
})
}

async fn spawn_cargo_task(
&self,
token: CancellationToken,
Expand Down Expand Up @@ -1346,6 +1487,47 @@ pub enum ClippyError {
CargoFailed { source: SpawnCargoError },
}

pub struct ActiveMiri {
pub task: BoxFuture<'static, Result<MiriResponse, MiriError>>,
pub stdout_rx: mpsc::Receiver<String>,
pub stderr_rx: mpsc::Receiver<String>,
}

impl fmt::Debug for ActiveMiri {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ActiveMiri")
.field("task", &"<future>")
.field("stdout_rx", &self.stdout_rx)
.field("stderr_rx", &self.stderr_rx)
.finish()
}
}

#[derive(Debug, Snafu)]
#[snafu(module)]
pub enum MiriError {
#[snafu(display("Could not start the container"))]
CouldNotStartContainer { source: Error },

#[snafu(display("Could not modify Cargo.toml"))]
CouldNotModifyCargoToml { source: ModifyCargoTomlError },

#[snafu(display("Could not delete previous source code"))]
CouldNotDeletePreviousCode { source: CommanderError },

#[snafu(display("Could not write source code"))]
CouldNotWriteCode { source: CommanderError },

#[snafu(display("Could not start Cargo task"))]
CouldNotStartCargo { source: SpawnCargoError },

#[snafu(display("The Cargo task panicked"))]
CargoTaskPanicked { source: tokio::task::JoinError },

#[snafu(display("Cargo task failed"))]
CargoFailed { source: SpawnCargoError },
}

struct SpawnCargo {
task: JoinHandle<Result<ExecuteCommandResponse, SpawnCargoError>>,
stdin_tx: mpsc::Sender<String>,
Expand Down Expand Up @@ -2910,6 +3092,43 @@ mod tests {
Ok(())
}

const ARBITRARY_MIRI_REQUEST: MiriRequest = MiriRequest {
channel: Channel::Nightly,
crate_type: CrateType::Binary,
edition: Edition::Rust2021,
code: String::new(),
};

#[tokio::test]
#[snafu::report]
async fn miri() -> Result<()> {
// cargo-miri-playground only exists inside the container
let coordinator = new_coordinator_docker().await;

let req = MiriRequest {
code: r#"
fn main() {
let mut a: [u8; 0] = [];
unsafe { *a.get_unchecked_mut(1) = 1; }
}
"#
.into(),
..ARBITRARY_MIRI_REQUEST
};

let response = coordinator.miri(req).with_timeout().await.unwrap();

assert!(!response.success, "stderr: {}", response.stderr);

assert_contains!(response.stderr, "Undefined Behavior");
assert_contains!(response.stderr, "pointer to 1 byte");
assert_contains!(response.stderr, "starting at offset 0");
assert_contains!(response.stderr, "is out-of-bounds");
assert_contains!(response.stderr, "has size 0");

Ok(())
}

// The next set of tests are broader than the functionality of a
// single operation.

Expand Down
34 changes: 11 additions & 23 deletions ui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,6 @@ enum Error {
SandboxCreation { source: sandbox::Error },
#[snafu(display("Expansion operation failed: {}", source))]
Expansion { source: sandbox::Error },
#[snafu(display("Interpreting operation failed: {}", source))]
Interpreting { source: sandbox::Error },
#[snafu(display("Caching operation failed: {}", source))]
Caching { source: sandbox::Error },
#[snafu(display("Gist creation failed: {}", source))]
Expand Down Expand Up @@ -207,6 +205,11 @@ enum Error {
source: server_axum::api_orchestrator_integration_impls::ParseClippyRequestError,
},

#[snafu(context(false))]
MiriRequest {
source: server_axum::api_orchestrator_integration_impls::ParseMiriRequestError,
},

// Remove at a later point. From here ...
#[snafu(display("The value {:?} is not a valid edition", value))]
InvalidEdition { value: String },
Expand Down Expand Up @@ -248,6 +251,11 @@ enum Error {
source: orchestrator::coordinator::ClippyError,
},

#[snafu(display("Unable to convert the Miri request"))]
Miri {
source: orchestrator::coordinator::MiriError,
},

#[snafu(display("The operation timed out"))]
Timeout { source: tokio::time::error::Elapsed },

Expand Down Expand Up @@ -377,6 +385,7 @@ struct MiriRequest {
#[derive(Debug, Clone, Serialize)]
struct MiriResponse {
success: bool,
exit_detail: String,
stdout: String,
stderr: String,
}
Expand Down Expand Up @@ -443,27 +452,6 @@ struct EvaluateResponse {
error: Option<String>,
}

impl TryFrom<MiriRequest> for sandbox::MiriRequest {
type Error = Error;

fn try_from(me: MiriRequest) -> Result<Self> {
Ok(sandbox::MiriRequest {
code: me.code,
edition: parse_edition(&me.edition)?,
})
}
}

impl From<sandbox::MiriResponse> for MiriResponse {
fn from(me: sandbox::MiriResponse) -> Self {
MiriResponse {
success: me.success,
stdout: me.stdout,
stderr: me.stderr,
}
}
}

impl TryFrom<MacroExpansionRequest> for sandbox::MacroExpansionRequest {
type Error = Error;

Expand Down
46 changes: 21 additions & 25 deletions ui/src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,25 +197,6 @@ where
}
}

impl GenerateLabels for sandbox::MiriRequest {
fn generate_labels(&self, outcome: Outcome) -> Labels {
let Self { code: _, edition } = *self;

Labels {
endpoint: Endpoint::Miri,
outcome,

target: None,
channel: None,
mode: None,
edition: Some(edition),
crate_type: None,
tests: None,
backtrace: None,
}
}
}

impl GenerateLabels for sandbox::MacroExpansionRequest {
fn generate_labels(&self, outcome: Outcome) -> Labels {
let Self { code: _, edition } = *self;
Expand Down Expand Up @@ -271,12 +252,6 @@ fn common_success_details(success: bool, stderr: &str) -> Outcome {
}
}

impl SuccessDetails for sandbox::MiriResponse {
fn success_details(&self) -> Outcome {
common_success_details(self.success, &self.stderr)
}
}

impl SuccessDetails for sandbox::MacroExpansionResponse {
fn success_details(&self) -> Outcome {
common_success_details(self.success, &self.stderr)
Expand Down Expand Up @@ -457,6 +432,27 @@ impl HasLabelsCore for coordinator::ClippyRequest {
}
}

impl HasLabelsCore for coordinator::MiriRequest {
fn labels_core(&self) -> LabelsCore {
let Self {
channel,
crate_type,
edition,
code: _,
} = *self;

LabelsCore {
target: None,
channel: Some(channel.into()),
mode: None,
edition: Some(Some(edition.into())),
crate_type: Some(crate_type.into()),
tests: None,
backtrace: None,
}
}
}

pub(crate) fn record_metric(
endpoint: Endpoint,
labels_core: LabelsCore,
Expand Down
Loading

0 comments on commit c9a730c

Please sign in to comment.