Skip to content

Commit 6308bb2

Browse files
committed
Remove allocations from the ctru panic hook
1 parent 999dd61 commit 6308bb2

File tree

2 files changed

+69
-7
lines changed

2 files changed

+69
-7
lines changed

ctru-rs/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ libc = { workspace = true, default-features = true }
2525
bitflags = "2.6.0"
2626
macaddr = "1.0.1"
2727
widestring = "1.1.0"
28+
itoa = "1.0.15"
2829

2930
[build-dependencies]
3031
toml = "0.9.4"

ctru-rs/src/applets/error.rs

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
55
use crate::services::{apt::Apt, gfx::Gfx};
66

7-
use ctru_sys::errorConf;
7+
use ctru_sys::{errorConf, errorDisp};
8+
9+
use std::cell::UnsafeCell;
810

911
/// Configuration struct to set up the Error applet.
1012
#[doc(alias = "errorConf")]
@@ -94,12 +96,32 @@ impl PopUp {
9496
}
9597
}
9698

99+
struct PanicHookConfig {
100+
error_app: UnsafeCell<PopUp>,
101+
}
102+
103+
impl PanicHookConfig {
104+
fn new() -> Self {
105+
Self {
106+
error_app: UnsafeCell::new(PopUp::new(WordWrap::Enabled)),
107+
}
108+
}
109+
110+
unsafe fn get(&self) -> *mut errorConf {
111+
unsafe { (*self.error_app.get()).state.as_mut() }
112+
}
113+
}
114+
115+
unsafe impl Sync for PanicHookConfig {}
116+
97117
pub(crate) fn set_panic_hook(call_old_hook: bool) {
98118
use crate::services::gfx::GFX_ACTIVE;
99119
use std::sync::TryLockError;
100120

101121
let old_hook = std::panic::take_hook();
102122

123+
let config = PanicHookConfig::new();
124+
103125
std::panic::set_hook(Box::new(move |panic_info| {
104126
// If we get a `WouldBlock` error, we know that the `Gfx` service has been initialized.
105127
// Otherwise fallback to using the old panic hook.
@@ -108,18 +130,57 @@ pub(crate) fn set_panic_hook(call_old_hook: bool) {
108130
old_hook(panic_info);
109131
}
110132

111-
let thread = std::thread::current();
133+
let error_conf = unsafe { &mut *config.get() };
112134

113-
let name = thread.name().unwrap_or("<unnamed>");
135+
let mut buf1 = itoa::Buffer::new();
114136

115-
let message = format!("thread '{name}' {panic_info}");
137+
let mut buf2 = itoa::Buffer::new();
116138

117-
let mut popup = PopUp::new(WordWrap::Enabled);
139+
let thread = std::thread::current();
140+
141+
let name = thread.name().unwrap_or("<unnamed>");
118142

119-
popup.set_text(&message);
143+
let location = panic_info.location().unwrap();
144+
145+
let file = location.file();
146+
147+
let line = buf1.format(location.line());
148+
149+
let column = buf2.format(location.column());
150+
151+
let payload = if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
152+
s
153+
} else if let Some(s) = panic_info.payload().downcast_ref::<String>() {
154+
s.as_str()
155+
} else {
156+
""
157+
};
158+
159+
let message = [
160+
"thread '",
161+
name,
162+
"' panicked at ",
163+
file,
164+
":",
165+
line,
166+
":",
167+
column,
168+
":",
169+
payload,
170+
];
171+
172+
for (idx, code_unit) in message
173+
.into_iter()
174+
.flat_map(str::encode_utf16)
175+
.take(error_conf.Text.len() - 1)
176+
.chain(std::iter::once(0))
177+
.enumerate()
178+
{
179+
error_conf.Text[idx] = code_unit;
180+
}
120181

121182
unsafe {
122-
let _ = popup.launch_unchecked();
183+
errorDisp(error_conf);
123184
}
124185
} else {
125186
old_hook(panic_info);

0 commit comments

Comments
 (0)