Skip to content

Commit 16685fc

Browse files
authored
Merge pull request rust-lang#4693 from RalfJung/genmc-exit
genmc: slightly simplify exit handling; get rid of GenmcBlockedExecution
2 parents 573515c + cb5c5da commit 16685fc

File tree

3 files changed

+30
-45
lines changed

3 files changed

+30
-45
lines changed

src/tools/miri/src/concurrency/genmc/mod.rs

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ use genmc_sys::{
88
use rustc_abi::{Align, Size};
99
use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult, interp_ok};
1010
use rustc_data_structures::fx::FxHashMap;
11-
use rustc_middle::{throw_machine_stop, throw_ub_format, throw_unsup_format};
11+
use rustc_middle::{throw_ub_format, throw_unsup_format};
1212
// FIXME(genmc,tracing): Implement some work-around for enabling debug/trace level logging (currently disabled statically in rustc).
13-
use tracing::{debug, info};
13+
use tracing::debug;
1414

1515
use self::global_allocations::{EvalContextExt as _, GlobalAllocationHandler};
1616
use self::helper::{
@@ -63,12 +63,6 @@ struct ExitStatus {
6363
exit_type: ExitType,
6464
}
6565

66-
impl ExitStatus {
67-
fn do_leak_check(self) -> bool {
68-
matches!(self.exit_type, ExitType::MainThreadFinish)
69-
}
70-
}
71-
7266
/// State that is reset at the start of every execution.
7367
#[derive(Debug, Default)]
7468
struct PerExecutionState {
@@ -223,8 +217,6 @@ impl GenmcCtx {
223217

224218
/// Inform GenMC that the program's execution has ended.
225219
///
226-
/// This function must be called even when the execution is blocked
227-
/// (i.e., it returned a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcBlockedExecution`).
228220
/// Don't call this function if an error was found.
229221
///
230222
/// GenMC detects certain errors only when the execution ends.
@@ -694,39 +686,37 @@ impl GenmcCtx {
694686
}
695687

696688
/// Handle a call to `libc::exit` or the exit of the main thread.
697-
/// Unless an error is returned, the program should continue executing (in a different thread, chosen by the next scheduling call).
689+
/// Unless an error is returned, the program should continue executing (in a different thread,
690+
/// chosen by the next scheduling call).
698691
pub(crate) fn handle_exit<'tcx>(
699692
&self,
700693
thread: ThreadId,
701694
exit_code: i32,
702695
exit_type: ExitType,
703696
) -> InterpResult<'tcx> {
704-
// Calling `libc::exit` doesn't do cleanup, so we skip the leak check in that case.
705-
let exit_status = ExitStatus { exit_code, exit_type };
706-
707697
if let Some(old_exit_status) = self.exec_state.exit_status.get() {
708698
throw_ub_format!(
709-
"`exit` called twice, first with status {old_exit_status:?}, now with status {exit_status:?}",
699+
"`exit` called twice, first with exit code {old_exit_code}, now with status {exit_code}",
700+
old_exit_code = old_exit_status.exit_code,
710701
);
711702
}
712703

713-
// FIXME(genmc): Add a flag to continue exploration even when the program exits with a non-zero exit code.
714-
if exit_code != 0 {
715-
info!("GenMC: 'exit' called with non-zero argument, aborting execution.");
716-
let leak_check = exit_status.do_leak_check();
717-
throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check });
718-
}
719-
720-
if matches!(exit_type, ExitType::ExitCalled) {
721-
let thread_infos = self.exec_state.thread_id_manager.borrow();
722-
let genmc_tid = thread_infos.get_genmc_tid(thread);
723-
724-
self.handle.borrow_mut().pin_mut().handle_thread_kill(genmc_tid);
725-
} else {
726-
assert_eq!(thread, ThreadId::MAIN_THREAD);
704+
match exit_type {
705+
ExitType::ExitCalled => {
706+
// `exit` kills the current thread; we have to tell GenMC about this.
707+
let thread_infos = self.exec_state.thread_id_manager.borrow();
708+
let genmc_tid = thread_infos.get_genmc_tid(thread);
709+
self.handle.borrow_mut().pin_mut().handle_thread_kill(genmc_tid);
710+
}
711+
ExitType::MainThreadFinish => {
712+
// The main thread has already exited so we don't call `handle_thread_kill` again.
713+
assert_eq!(thread, ThreadId::MAIN_THREAD);
714+
}
727715
}
728-
// We continue executing now, so we store the exit status.
729-
self.exec_state.exit_status.set(Some(exit_status));
716+
// To cover all possible behaviors, we have to continue execution the other threads:
717+
// whatever they do next could also have happened before the `exit` call. So we just
718+
// remember the exit status and use it when the other threads are done.
719+
self.exec_state.exit_status.set(Some(ExitStatus { exit_code, exit_type }));
730720
interp_ok(())
731721
}
732722
}

src/tools/miri/src/concurrency/genmc/scheduling.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,21 @@ impl GenmcCtx {
118118
// Depending on the exec_state, we either schedule the given thread, or we are finished with this execution.
119119
match result.exec_state {
120120
ExecutionState::Ok => interp_ok(Some(thread_infos.get_miri_tid(result.next_thread))),
121-
ExecutionState::Blocked => throw_machine_stop!(TerminationInfo::GenmcBlockedExecution),
121+
ExecutionState::Blocked => {
122+
// This execution doesn't need further exploration. We treat this as "success, no
123+
// leak check needed", which makes it a NOP in the big outer loop.
124+
throw_machine_stop!(TerminationInfo::Exit {
125+
code: 0, // success
126+
leak_check: false,
127+
});
128+
}
122129
ExecutionState::Finished => {
123130
let exit_status = self.exec_state.exit_status.get().expect(
124131
"If the execution is finished, we should have a return value from the program.",
125132
);
126133
throw_machine_stop!(TerminationInfo::Exit {
127134
code: exit_status.exit_code,
128-
leak_check: exit_status.do_leak_check()
135+
leak_check: matches!(exit_status.exit_type, super::ExitType::MainThreadFinish),
129136
});
130137
}
131138
ExecutionState::Error => {

src/tools/miri/src/diagnostics.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,6 @@ pub enum TerminationInfo {
3333
},
3434
Int2PtrWithStrictProvenance,
3535
Deadlock,
36-
/// In GenMC mode, executions can get blocked, which stops the current execution without running any cleanup.
37-
/// No leak checks should be performed if this happens, since they would give false positives.
38-
GenmcBlockedExecution,
3936
MultipleSymbolDefinitions {
4037
link_name: Symbol,
4138
first: SpanData,
@@ -80,8 +77,6 @@ impl fmt::Display for TerminationInfo {
8077
StackedBorrowsUb { msg, .. } => write!(f, "{msg}"),
8178
TreeBorrowsUb { title, .. } => write!(f, "{title}"),
8279
Deadlock => write!(f, "the evaluated program deadlocked"),
83-
GenmcBlockedExecution =>
84-
write!(f, "GenMC determined that the execution got blocked (this is not an error)"),
8580
MultipleSymbolDefinitions { link_name, .. } =>
8681
write!(f, "multiple definitions of symbol `{link_name}`"),
8782
SymbolShimClashing { link_name, .. } =>
@@ -254,13 +249,6 @@ pub fn report_result<'tcx>(
254249
labels.push(format!("this thread got stuck here"));
255250
None
256251
}
257-
GenmcBlockedExecution => {
258-
// This case should only happen in GenMC mode.
259-
assert!(ecx.machine.data_race.as_genmc_ref().is_some());
260-
// The program got blocked by GenMC without finishing the execution.
261-
// No cleanup code was executed, so we don't do any leak checks.
262-
return Some((0, false));
263-
}
264252
MultipleSymbolDefinitions { .. } | SymbolShimClashing { .. } => None,
265253
};
266254
#[rustfmt::skip]

0 commit comments

Comments
 (0)