in reverie-ptrace/src/trace/mod.rs [1066:1126]
fn serialized_threads() -> Result<(), Box<dyn std::error::Error + 'static>> {
const THREAD_COUNT: usize = 8;
let (pid, tracee) = trace(
move || {
// Create a handful of threads that do nothing but exit.
let threads = (0..THREAD_COUNT)
.map(|i| thread::spawn(move || i))
.collect::<Vec<_>>();
for t in threads {
t.join().unwrap();
}
42
},
Options::PTRACE_O_EXITKILL
| Options::PTRACE_O_TRACEEXIT
| ptrace::Options::PTRACE_O_TRACECLONE,
)?;
let mut parent = tracee.resume(None)?;
// We should observe threads getting created.
for _ in 0..THREAD_COUNT {
let (stopped, event) = parent.wait()?.assume_stopped();
let child = match event {
Event::NewChild(ChildOp::Clone, child) => child,
e => panic!("Expected clone event, got {:?}", e),
};
// Should be at a group stop.
let (child, event) = child.wait()?.assume_stopped();
assert_eq!(event, Event::Stop);
// Resume the child.
let child = child.resume(None)?;
// Wait for it to exit.
let (child, event) = child.wait()?.assume_stopped();
assert_eq!(event, Event::Exit);
// Resume one last time to let it fully exit.
let (_child_pid, exit_status) = child.resume(None)?.wait()?.assume_exited();
assert_eq!(exit_status, ExitStatus::Exited(0));
// Resume the parent.
parent = stopped.resume(None)?;
}
// ptrace stop just before fully exiting.
let (parent, event) = parent.wait()?.assume_stopped();
assert_eq!(event, Event::Exit);
// Fully exited.
let parent = parent.resume(None)?;
assert_eq!(parent.wait()?, Wait::Exited(pid, ExitStatus::Exited(42)));
Ok(())
}