generated from EmbarkStudios/opensource-template
/
diskwrite.rs
127 lines (104 loc) · 4.51 KB
/
diskwrite.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
const SOCKET_NAME: &str = "minidumper-disk-example";
use minidumper::{Client, Server};
fn main() {
pretty_env_logger::init();
if std::env::args().any(|a| a == "--server") {
let mut server = Server::with_name(SOCKET_NAME).expect("failed to create server");
let ab = std::sync::atomic::AtomicBool::new(false);
struct Handler;
impl minidumper::ServerHandler for Handler {
/// Called when a crash has been received and a backing file needs to be
/// created to store it.
fn create_minidump_file(
&self,
) -> Result<(std::fs::File, std::path::PathBuf), std::io::Error> {
let uuid = uuid::Uuid::new_v4();
let dump_path = std::path::PathBuf::from(format!("dumps/{uuid}.dmp"));
if let Some(dir) = dump_path.parent() {
if !dir.try_exists()? {
std::fs::create_dir_all(dir)?;
}
}
let file = std::fs::File::create(&dump_path)?;
Ok((file, dump_path))
}
/// Called when a crash has been fully written as a minidump to the provided
/// file. Also returns the full heap buffer as well.
fn on_minidump_created(
&self,
result: Result<minidumper::MinidumpBinary, minidumper::Error>,
) -> minidumper::LoopAction {
match result {
Ok(mut md_bin) => {
use std::io::Write;
let _ = md_bin.file.flush();
log::info!("wrote minidump to disk");
}
Err(e) => {
log::error!("failed to write minidump: {:#}", e);
}
}
// Tells the server to exit, which will in turn exit the process
minidumper::LoopAction::Exit
}
fn on_message(&self, kind: u32, buffer: Vec<u8>) {
log::info!(
"kind: {kind}, message: {}",
String::from_utf8(buffer).unwrap()
);
}
}
server
.run(Box::new(Handler), &ab, None)
.expect("failed to run server");
return;
}
let mut server = None;
// Attempt to connect to the server
let (client, _server) = loop {
if let Ok(client) = Client::with_name(SOCKET_NAME) {
break (client, server.unwrap());
}
let exe = std::env::current_exe().expect("unable to find ourselves");
server = Some(
std::process::Command::new(exe)
.arg("--server")
.spawn()
.expect("unable to spawn server process"),
);
// Give it time to start
std::thread::sleep(std::time::Duration::from_millis(100));
};
// Register our exception handler
client.send_message(1, "mistakes will be made").unwrap();
#[allow(unsafe_code)]
let handler = crash_handler::CrashHandler::attach(unsafe {
crash_handler::make_crash_event(move |crash_context: &crash_handler::CrashContext| {
// Before we request the crash, send a message to the server
client.send_message(2, "mistakes were made").unwrap();
// Send a ping to the server, this ensures that all messages that have been sent
// are "flushed" before the crash event is sent. This is only really useful
// on macos where messages and crash events are sent via different, unsynchronized,
// methods which can result in the crash event closing the server before
// the non-crash messages are received/processed
client.ping().unwrap();
crash_handler::CrashEventResult::Handled(client.request_dump(crash_context).is_ok())
})
})
.expect("failed to attach signal handler");
// On linux we can explicitly allow only the server process to inspect the
// process we are monitoring (this one) for crashes
#[cfg(any(target_os = "linux", target_os = "android"))]
{
handler.set_ptracer(Some(_server.id()));
}
cfg_if::cfg_if! {
if #[cfg(any(target_os = "linux", target_os = "android"))] {
handler.simulate_signal(libc::SIGALRM as _);
} else if #[cfg(windows)] {
handler.simulate_exception(None);
} else if #[cfg(target_os = "macos")] {
handler.simulate_exception(None);
}
}
}