update panic handler with system information

This commit is contained in:
Chance 2025-04-03 16:05:52 -04:00 committed by BitSyndicate
parent ed93baa404
commit ed23402212
4 changed files with 47 additions and 78 deletions

View file

@ -46,7 +46,7 @@ fn process_panic(info: &std::panic::PanicHookInfo<'_>) -> Result<(), Box<dyn Err
}; };
writeln!(file, "{}", payload_str)?; writeln!(file, "{}", payload_str)?;
writeln!(file, "{}", render_backtrace().sanitize_path())?; writeln!(file, "{}", capture_backtrace().sanitize_path())?;
let panic_msg = format!( let panic_msg = format!(
r#"Zenyx had a problem and crashed. To help us diagnose the problem you can send us a crash report. r#"Zenyx had a problem and crashed. To help us diagnose the problem you can send us a crash report.
@ -54,8 +54,7 @@ fn process_panic(info: &std::panic::PanicHookInfo<'_>) -> Result<(), Box<dyn Err
We have generated a report file at '{}'. Submit an issue or email with the subject of 'Zenyx Crash Report' and include the report as an attachment. We have generated a report file at '{}'. Submit an issue or email with the subject of 'Zenyx Crash Report' and include the report as an attachment.
To submit the crash report: To submit the crash report:
https://github.com/Zenyx-Engine/Zenyx/issues https://codeberg.org/Caznix/Zenyx/issues
We take privacy seriously, and do not perform any automated error collection. In order to improve the software, we rely on people to submit reports. We take privacy seriously, and do not perform any automated error collection. In order to improve the software, we rely on people to submit reports.
Thank you kindly!"#, Thank you kindly!"#,
@ -72,66 +71,28 @@ For future reference, the error summary is as follows:
println!("{}", final_msg.red().bold()); println!("{}", final_msg.red().bold());
MessageDialog::new() if let Err(e) = MessageDialog::new()
.set_type(MessageType::Error) .set_type(MessageType::Error)
.set_title("A fatal error in Zenyx has occurred") .set_title("A fatal error in Zenyx has occurred")
.set_text(&final_msg) .set_text(&final_msg)
.show_alert() .show_confirm() {
.map_err(|e| { error!("Failed to show message dialog: {e}")
error!("Failed to show error dialog: {}", e);
e.into()
})
}
fn render_backtrace() -> String {
const HEX_WIDTH: usize = mem::size_of::<usize>() * 2 + 2;
const NEXT_SYMBOL_PADDING: usize = HEX_WIDTH + 6;
let mut backtrace = String::new();
let bt = Backtrace::new();
let symbols = bt
.frames()
.iter()
.flat_map(|frame| {
let symbols = frame.symbols();
if symbols.is_empty() {
vec![(frame, None, "<unresolved>".to_owned())]
} else {
symbols
.iter()
.map(|s| {
(
frame,
Some(s),
s.name()
.map(|n| n.to_string())
.unwrap_or_else(|| "<unknown>".to_owned()),
)
})
.collect::<Vec<_>>()
}
})
.collect::<Vec<_>>();
let begin_unwind = "rust_begin_unwind";
let begin_unwind_start = symbols
.iter()
.position(|(_, _, n)| n == begin_unwind)
.unwrap_or(0);
for (entry_idx, (frame, symbol, name)) in symbols.iter().skip(begin_unwind_start).enumerate() {
let ip = frame.ip();
let _ = writeln!(backtrace, "{entry_idx:4}: {ip:HEX_WIDTH$?} - {name}");
if let Some(symbol) = symbol {
if let (Some(file), Some(line)) = (symbol.filename(), symbol.lineno()) {
let _ = writeln!(
backtrace,
"{:3$}at {}:{}",
"",
file.display(),
line,
NEXT_SYMBOL_PADDING
);
}
} }
}
Ok(())
}
fn capture_backtrace() -> String {
let mut backtrace = String::new();
let sysinfo = crate::metadata::SystemMetadata::current();
backtrace.push_str(&sysinfo.verbose_summary());
let trace = backtrace::Backtrace::new();
let message = format!("\nBacktrace:\n\n");
backtrace.push_str(&message);
backtrace.push_str(&format!("{trace:?}"));
backtrace backtrace
} }

View file

@ -5,7 +5,6 @@ use std::time::Instant;
use cgmath::{Deg, Matrix4, Point3, Rad, SquareMatrix, Vector3, perspective}; use cgmath::{Deg, Matrix4, Point3, Rad, SquareMatrix, Vector3, perspective};
use futures::executor::block_on; use futures::executor::block_on;
use thiserror::Error;
use tracing::{debug, error, info, trace}; use tracing::{debug, error, info, trace};
use wgpu::TextureUsages; use wgpu::TextureUsages;
use wgpu::{Backends, InstanceDescriptor, util::DeviceExt}; use wgpu::{Backends, InstanceDescriptor, util::DeviceExt};
@ -370,13 +369,12 @@ impl<'window> Renderer<'window> {
surface.configure(&device, &surface_config); surface.configure(&device, &surface_config);
let (depth_texture, depth_texture_view) = let (depth_texture, depth_texture_view) =
create_depth_texture(&device, surface_config.width, surface_config.height); create_depth_texture(&device, surface_config.width, surface_config.height);
let (depth_texture, depth_texture_view) =
create_depth_texture(&device, surface_config.width, surface_config.height);
let font_bytes = include_bytes!("DejaVuSans.ttf"); let font_bytes = include_bytes!("DejaVuSans.ttf");
let font = FontRef::try_from_slice(font_bytes).map_err(|e| { let font = FontRef::try_from_slice(font_bytes).map_err(|e| {
ZenyxError::builder(ZenyxErrorKind::DeviceRequest) ZenyxError::builder(ZenyxErrorKind::FontLoading)
.with_message("Font loading failed") .with_message("Font loading failed")
.with_source(e)
.build() .build()
})?; })?;

View file

@ -8,7 +8,6 @@ use std::sync::Arc;
use ctx::{Renderer, Vertex}; use ctx::{Renderer, Vertex};
use image::ImageDecoder; use image::ImageDecoder;
use image::ImageFormat; use image::ImageFormat;
use tobj::Mesh;
use tobj::{LoadOptions, Model}; use tobj::{LoadOptions, Model};
use tracing::{debug, error, info, trace, warn}; use tracing::{debug, error, info, trace, warn};
use wgpu::rwh::HasWindowHandle; use wgpu::rwh::HasWindowHandle;
@ -17,7 +16,6 @@ use winit::dpi::LogicalSize;
use winit::dpi::Size; use winit::dpi::Size;
use winit::event::{KeyEvent, WindowEvent}; use winit::event::{KeyEvent, WindowEvent};
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::monitor::MonitorHandle;
use winit::platform::windows::WindowAttributesExtWindows; use winit::platform::windows::WindowAttributesExtWindows;
use winit::window::Fullscreen; use winit::window::Fullscreen;
use winit::window::Icon; use winit::window::Icon;
@ -94,15 +92,18 @@ f 6/11/6 5/10/6 1/1/6 2/13/6
"; ";
impl App<'_> { impl App<'_> {
const ICON: &'static [u8] =
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/Badge.png"));
fn create_main_window(&mut self, event_loop: &ActiveEventLoop) { fn create_main_window(&mut self, event_loop: &ActiveEventLoop) {
let icon = self.load_icon_from_bytes(Self::ICON).unwrap(); let icon = self.load_icon_from_bytes(Self::ICON).unwrap();
let win_attr = Window::default_attributes() let win_attr = Window::default_attributes()
.with_title("Zenyx") .with_title("Zenyx")
.with_min_inner_size(Size::Logical(LogicalSize::new(100.0, 100.0))) .with_min_inner_size(Size::Logical(LogicalSize::new(100.0, 100.0)))
.with_window_icon(icon.clone()) .with_window_icon(icon.clone())
.with_taskbar_icon(icon); .with_taskbar_icon(icon);
match event_loop.create_window(win_attr) { match event_loop.create_window(win_attr) {
Ok(window) => { Ok(window) => {
let window = Arc::new(window); let window = Arc::new(window);
@ -229,14 +230,17 @@ impl App<'_> {
warn!("No window found for fullscreen toggle: {:?}", window_id); warn!("No window found for fullscreen toggle: {:?}", window_id);
} }
} }
fn load_icon_from_bytes(&self, bytes: &[u8]) -> Result<Option<Icon>, String> { fn load_icon_from_bytes(&self, bytes: &[u8]) -> Result<Option<Icon>, String> {
const IMAGE_DIR: &str = env!("CARGO_MANIFEST_DIR");
let cursor = Cursor::new(bytes); let cursor = Cursor::new(bytes);
let format = image::guess_format(bytes).map_err(|_| "Failed to guess image format")?; let format = image::guess_format(bytes).map_err(|_| "Failed to guess image format")?;
let decoder = match format { let decoder = match format {
ImageFormat::Png => image::codecs::png::PngDecoder::new(cursor).map_err(|e| format!("Failed to decode PNG: {}", e))?, ImageFormat::Png => image::codecs::png::PngDecoder::new(cursor)
.map_err(|e| format!("Failed to decode PNG: {}", e))?,
_ => { _ => {
let img = image::load_from_memory(bytes).map_err(|e| format!("Failed to load image: {}", e))?.into_rgba8(); let img = image::load_from_memory(bytes)
.map_err(|e| format!("Failed to load image: {}", e))?
.into_rgba8();
let (width, height) = img.dimensions(); let (width, height) = img.dimensions();
return Icon::from_rgba(img.into_raw(), width, height) return Icon::from_rgba(img.into_raw(), width, height)
.map(Some) .map(Some)
@ -246,14 +250,15 @@ impl App<'_> {
let (width, height) = decoder.dimensions(); let (width, height) = decoder.dimensions();
let mut image_data = vec![0; decoder.total_bytes() as usize]; let mut image_data = vec![0; decoder.total_bytes() as usize];
decoder.read_image(&mut image_data).map_err(|e| format!("Failed to read image data: {}", e))?; decoder
.read_image(&mut image_data)
.map_err(|e| format!("Failed to read image data: {}", e))?;
Icon::from_rgba(image_data, width, height) Icon::from_rgba(image_data, width, height)
.map(Some) .map(Some)
.map_err(|e| format!("Failed to create icon from bytes: {}", e)) .map_err(|e| format!("Failed to create icon from bytes: {}", e))
} }
const ICON: &'static [u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/Badge.png"));
fn spawn_child_window(&mut self, event_loop: &ActiveEventLoop) { fn spawn_child_window(&mut self, event_loop: &ActiveEventLoop) {
if let Some(main_ctx) = self.windows.values().find(|ctx| ctx.is_main_window()) { if let Some(main_ctx) = self.windows.values().find(|ctx| ctx.is_main_window()) {
let title = format!("Zenyx - New Window {}", self.windows.len()); let title = format!("Zenyx - New Window {}", self.windows.len());
@ -265,7 +270,7 @@ impl App<'_> {
.with_min_inner_size(Size::Logical(LogicalSize::new(100.0, 100.0))) .with_min_inner_size(Size::Logical(LogicalSize::new(100.0, 100.0)))
.with_window_icon(icon.clone()) .with_window_icon(icon.clone())
.with_taskbar_icon(icon); .with_taskbar_icon(icon);
match main_ctx.window_handle() { match main_ctx.window_handle() {
Ok(handle) => { Ok(handle) => {
if !cfg!(target_os = "windows") { if !cfg!(target_os = "windows") {
@ -345,6 +350,11 @@ impl App<'_> {
fn handle_resize(&mut self, window_id: WindowId, new_size: winit::dpi::PhysicalSize<u32>) { fn handle_resize(&mut self, window_id: WindowId, new_size: winit::dpi::PhysicalSize<u32>) {
if let Some(window_context) = self.windows.get_mut(&window_id) { if let Some(window_context) = self.windows.get_mut(&window_id) {
// if we dont ignore size 0 this WILL cause a crash. DO NOT REMOVE
if new_size.height == 0 || new_size.width == 0 {
error!("Attempted to resize a window to 0x0!");
return;
}
window_context.ctx.resize(new_size.into()); window_context.ctx.resize(new_size.into());
window_context.window.request_redraw(); window_context.window.request_redraw();
debug!( debug!(
@ -435,7 +445,7 @@ impl ApplicationHandler for App<'_> {
} }
pub fn init_renderer(event_loop: EventLoop<()>) { pub fn init_renderer(event_loop: EventLoop<()>) {
event_loop.set_control_flow(ControlFlow::Wait); event_loop.set_control_flow(ControlFlow::Poll);
let mut app = App::default(); let mut app = App::default();
if let Err(e) = event_loop.run_app(&mut app) { if let Err(e) = event_loop.run_app(&mut app) {
error!("Failed to run application: {}", e); error!("Failed to run application: {}", e);

View file

@ -556,7 +556,7 @@ impl SystemMetadata {
}; };
format!( format!(
"System Metadata:\n\n{}\n\n{}\n\n{}\n\n{}\n\n{}", "System Information:\n\n{}\n\n{}\n\n{}\n\n{}\n\n{}",
self.cpu.verbose_info(), self.cpu.verbose_info(),
main_gpu_info, main_gpu_info,
other_gpu_list, other_gpu_list,