Source code
Revision control
Copy as Markdown
Other Tools
use std::{borrow::Cow, slice};
use parking_lot::{lock_api::RawMutex, Mutex};
use windows::Win32::{Foundation, System::Diagnostics::Debug};
// This is a mutex as opposed to an atomic as we need to completely
// lock everyone out until we have registered or unregistered the
// exception handler, otherwise really nasty races could happen.
//
// By routing all the registration through these functions we can guarantee
// there is either 1 or 0 exception handlers registered, not multiple.
static EXCEPTION_HANDLER_COUNT: Mutex<usize> = Mutex::const_new(parking_lot::RawMutex::INIT, 0);
pub fn register_exception_handler() {
let mut count_guard = EXCEPTION_HANDLER_COUNT.lock();
if *count_guard == 0 {
unsafe { Debug::AddVectoredExceptionHandler(0, Some(output_debug_string_handler)) };
}
*count_guard += 1;
}
pub fn unregister_exception_handler() {
let mut count_guard = EXCEPTION_HANDLER_COUNT.lock();
if *count_guard == 1 {
unsafe { Debug::RemoveVectoredExceptionHandler(output_debug_string_handler as *mut _) };
}
*count_guard -= 1;
}
const MESSAGE_PREFIXES: &[(&str, log::Level)] = &[
("CORRUPTION", log::Level::Error),
("ERROR", log::Level::Error),
("WARNING", log::Level::Warn),
("INFO", log::Level::Info),
("MESSAGE", log::Level::Debug),
];
unsafe extern "system" fn output_debug_string_handler(
exception_info: *mut Debug::EXCEPTION_POINTERS,
) -> i32 {
let record = unsafe { &*(*exception_info).ExceptionRecord };
if record.NumberParameters != 2 {
return Debug::EXCEPTION_CONTINUE_SEARCH;
}
let message = match record.ExceptionCode {
Foundation::DBG_PRINTEXCEPTION_C => String::from_utf8_lossy(unsafe {
slice::from_raw_parts(
record.ExceptionInformation[1] as *const u8,
record.ExceptionInformation[0],
)
}),
Foundation::DBG_PRINTEXCEPTION_WIDE_C => Cow::Owned(String::from_utf16_lossy(unsafe {
slice::from_raw_parts(
record.ExceptionInformation[1] as *const u16,
record.ExceptionInformation[0],
)
})),
_ => return Debug::EXCEPTION_CONTINUE_SEARCH,
};
let message = match message.strip_prefix("D3D12 ") {
Some(msg) => msg
.trim_end_matches("\n\0")
.trim_end_matches("[ STATE_CREATION WARNING #0: UNKNOWN]"),
None => return Debug::EXCEPTION_CONTINUE_SEARCH,
};
let (message, level) = match MESSAGE_PREFIXES
.iter()
.find(|&&(prefix, _)| message.starts_with(prefix))
{
Some(&(prefix, level)) => (&message[prefix.len() + 2..], level),
None => (message, log::Level::Debug),
};
if level == log::Level::Warn && message.contains("#82") {
// This is are useless spammy warnings (#820, #821):
// "The application did not pass any clear value to resource creation"
return Debug::EXCEPTION_CONTINUE_SEARCH;
}
if level == log::Level::Warn && message.contains("DRAW_EMPTY_SCISSOR_RECTANGLE") {
// This is normal, WebGPU allows passing empty scissor rectangles.
return Debug::EXCEPTION_CONTINUE_SEARCH;
}
let _ = std::panic::catch_unwind(|| {
log::log!(level, "{}", message);
});
if cfg!(debug_assertions) && level == log::Level::Error {
// Set canary and continue
crate::VALIDATION_CANARY.add(message.to_string());
}
Debug::EXCEPTION_CONTINUE_EXECUTION
}