@@ -3,6 +3,7 @@ const builtin = @import("builtin");
33
44extern "log" fn __android_log_write (prio : c_int , tag : [* c ]const u8 , text : [* c ]const u8 ) c_int ;
55
6+ /// Alternate panic implementation that calls __android_log_write so that you can see the logging via "adb logcat"
67pub const panic = Panic .panic ;
78
89/// Levels for Android
@@ -23,20 +24,26 @@ pub const Level = enum(u8) {
2324 // verbose = 2, // ANDROID_LOG_VERBOSE
2425 // default = 1, // ANDROID_LOG_DEFAULT
2526
26- /// Returns a string literal of the given level in full text form.
27- pub fn asText (comptime self : Level ) []const u8 {
28- return switch (self ) {
29- .err = > "error" ,
30- .warn = > "warning" ,
31- .info = > "info" ,
32- .debug = > "debug" ,
33- };
34- }
27+ // Returns a string literal of the given level in full text form.
28+ // pub fn asText(comptime self: Level) []const u8 {
29+ // return switch (self) {
30+ // .err => "error",
31+ // .warn => "warning",
32+ // .info => "info",
33+ // .debug => "debug",
34+ // };
35+ // }
3536};
3637
38+ /// Alternate log function implementation that calls __android_log_write so that you can see the logging via "adb logcat"
3739pub fn logFn (
3840 comptime message_level : std.log.Level ,
39- comptime scope : @Type (.EnumLiteral ),
41+ comptime scope : if (builtin .zig_version .minor != 13 )
42+ // Support Zig 0.14.0-dev
43+ @Type (.enum_literal )
44+ else
45+ // Support Zig 0.13.0
46+ @Type (.EnumLiteral ),
4047 comptime format : []const u8 ,
4148 args : anytype ,
4249) void {
@@ -61,9 +68,11 @@ pub fn logFn(
6168 }
6269}
6370
71+ /// LogWriter was was taken basically as is from: https://github.com/ikskuh/ZigAndroidTemplate
6472const LogWriter = struct {
6573 /// name of the application / log scope
66- const tag : [* c ]const u8 = null ; // = "zig-app";
74+ /// if not set, it'll default to the "package" attribute defined in AndroidManifest.xml
75+ const tag : [* c ]const u8 = null ;
6776
6877 level : Level ,
6978
@@ -109,6 +118,13 @@ const LogWriter = struct {
109118 }
110119};
111120
121+ /// Panic is a copy-paste of the panic logic from Zig but replaces usages of getStdErr with our own writer
122+ ///
123+ /// Example output:
124+ /// 09-22 13:08:49.578 3390 3390 F com.zig.minimal: thread 3390 panic: your panic message here
125+ /// 09-22 13:08:49.637 3390 3390 F com.zig.minimal: zig-android-sdk/examples\minimal/src/minimal.zig:33:15: 0x7ccb77b282dc in nativeActivityOnCreate (minimal)
126+ /// 09-22 13:08:49.637 3390 3390 F com.zig.minimal: zig-android-sdk/examples/minimal/src/minimal.zig:84:27: 0x7ccb77b28650 in ANativeActivity_onCreate (minimal)
127+ /// 09-22 13:08:49.637 3390 3390 F com.zig.minimal: ???:?:?: 0x7ccea4021d9c in ??? (libandroid_runtime.so)
112128pub const Panic = struct {
113129 /// Non-zero whenever the program triggered a panic.
114130 /// The counter is incremented/decremented atomically.
@@ -126,17 +142,87 @@ pub const Panic = struct {
126142 panicImpl (stack_trace , first_trace_addr , message );
127143 }
128144
145+ /// Must be called only after adding 1 to `panicking`. There are three callsites.
146+ fn waitForOtherThreadToFinishPanicking () void {
147+ if (panicking .fetchSub (1 , .seq_cst ) != 1 ) {
148+ // Another thread is panicking, wait for the last one to finish
149+ // and call abort()
150+ if (builtin .single_threaded ) unreachable ;
151+
152+ // Sleep forever without hammering the CPU
153+ var futex = std .atomic .Value (u32 ).init (0 );
154+ while (true ) std .Thread .Futex .wait (& futex , 0 );
155+ unreachable ;
156+ }
157+ }
158+
159+ const native_os = builtin .os .tag ;
160+ const updateSegfaultHandler = std .debug .updateSegfaultHandler ;
161+
162+ fn resetSegfaultHandler () void {
163+ // NOTE(jae): 2024-09-22
164+ // Not applicable for Android as it runs on the OS tag Linux
165+ // if (native_os == .windows) {
166+ // if (windows_segfault_handle) |handle| {
167+ // assert(windows.kernel32.RemoveVectoredExceptionHandler(handle) != 0);
168+ // windows_segfault_handle = null;
169+ // }
170+ // return;
171+ // }
172+ var act = posix.Sigaction {
173+ .handler = .{ .handler = posix .SIG .DFL },
174+ .mask = posix .empty_sigset ,
175+ .flags = 0 ,
176+ };
177+ // To avoid a double-panic, do nothing if an error happens here.
178+ if (builtin .zig_version .major == 0 and builtin .zig_version .minor == 13 ) {
179+ // Legacy 0.13.0
180+ updateSegfaultHandler (& act ) catch {};
181+ } else {
182+ // 0.14.0-dev+
183+ updateSegfaultHandler (& act );
184+ }
185+ }
186+
187+ const io = struct {
188+ const tty = struct {
189+ inline fn detectConfig (_ : * LogWriter ) std.io.tty.Config {
190+ return .no_color ;
191+ }
192+ };
193+
194+ var writer = LogWriter {
195+ .level = .fatal ,
196+ };
197+
198+ inline fn getStdErr () * LogWriter {
199+ return & writer ;
200+ }
201+ };
202+
203+ const posix = std .posix ;
204+ const enable_segfault_handler = std .options .enable_segfault_handler ;
205+
206+ /// Panic is a copy-paste of the panic logic from Zig but replaces usages of getStdErr with our own writer
207+ ///
208+ /// - Provide custom "io" namespace so we can easily customize getStdErr() to be our own writer
209+ /// - Provide other functions from std.debug.*
129210 fn panicImpl (trace : ? * const std.builtin.StackTrace , first_trace_addr : ? usize , msg : []const u8 ) noreturn {
130- @setCold (true );
131-
132- // NOTE(jae): 2024-09-15
133- // resetSegfaultHandler is not a public function
134- // if (comptime std.options.enable_segfault_handler) {
135- // // If a segfault happens while panicking, we want it to actually segfault, not trigger
136- // // the handler.
137- // std.debug.resetSegfaultHandler();
211+ // NOTE(jae): 2024-09-22
212+ // Cannot mark this as cold(true) OR setCold() depending on Zig version as we get an invalid builtin function
213+ // comptime {
214+ // if (builtin.zig_version.minor == 13)
215+ // @setCold(true)
216+ // else
217+ // @cold(true);
138218 // }
139219
220+ if (enable_segfault_handler ) {
221+ // If a segfault happens while panicking, we want it to actually segfault, not trigger
222+ // the handler.
223+ resetSegfaultHandler ();
224+ }
225+
140226 // Note there is similar logic in handleSegfaultPosix and handleSegfaultWindowsExtra.
141227 nosuspend switch (panic_stage ) {
142228 0 = > {
@@ -149,69 +235,104 @@ pub const Panic = struct {
149235 panic_mutex .lock ();
150236 defer panic_mutex .unlock ();
151237
152- var logger = LogWriter {
153- .level = .fatal ,
154- };
155- const stderr = logger .writer ();
238+ const stderr = io .getStdErr ().writer ();
156239 if (builtin .single_threaded ) {
157- stderr .print ("panic: " , .{}) catch @trap ();
240+ stderr .print ("panic: " , .{}) catch posix . abort ();
158241 } else {
159242 const current_thread_id = std .Thread .getCurrentId ();
160- stderr .print ("thread {} panic: " , .{current_thread_id }) catch @trap ();
243+ stderr .print ("thread {} panic: " , .{current_thread_id }) catch posix . abort ();
161244 }
162- stderr .print ("{s}\n " , .{msg }) catch @trap ();
245+ stderr .print ("{s}\n " , .{msg }) catch posix . abort ();
163246 if (trace ) | t | {
164- // std.debug.dumpStackTrace(t.*);
165- if (builtin .strip_debug_info ) {
166- stderr .print ("Unable to dump stack trace: debug info stripped\n " , .{}) catch return ;
167- } else {
168- const debug_info = std .debug .getSelfDebugInfo () catch | err | {
169- stderr .print ("Unable to dump stack trace: Unable to open debug info: {s}\n " , .{@errorName (err )}) catch return ;
170- @trap ();
171- };
172- var debug_info_arena_allocator = std .heap .ArenaAllocator .init (std .heap .page_allocator );
173- const allocator = debug_info_arena_allocator .allocator ();
174- std .debug .writeStackTrace (t .* , stderr , allocator , debug_info , .no_color ) catch | err | {
175- stderr .print ("Unable to dump stack trace: {s}\n " , .{@errorName (err )}) catch return ;
176- @trap ();
177- };
178- }
179- }
180- // std.debug.dumpCurrentStackTrace(first_trace_addr);
181- {
182- if (builtin .strip_debug_info ) {
183- stderr .print ("Unable to dump stack trace: debug info stripped\n " , .{}) catch return ;
184- } else {
185- const debug_info = std .debug .getSelfDebugInfo () catch | err | {
186- stderr .print ("Unable to dump stack trace: Unable to open debug info: {s}\n " , .{@errorName (err )}) catch return ;
187- @trap ();
188- };
189- std .debug .writeCurrentStackTrace (stderr , debug_info , .no_color , first_trace_addr ) catch | err | {
190- stderr .print ("Unable to dump stack trace: {s}\n " , .{@errorName (err )}) catch return ;
191- @trap ();
192- };
193- }
247+ dumpStackTrace (t .* );
194248 }
249+ dumpCurrentStackTrace (first_trace_addr );
195250 }
196- // std.debug.waitForOtherThreadToFinishPanicking();
251+
252+ waitForOtherThreadToFinishPanicking ();
197253 },
198254 1 = > {
199255 panic_stage = 2 ;
200256
201257 // A panic happened while trying to print a previous panic message,
202258 // we're still holding the mutex but that's fine as we're going to
203259 // call abort()
204- var logger = LogWriter {
205- .level = .fatal ,
206- };
207- const stderr = logger .writer ();
208- stderr .print ("Panicked during a panic. Aborting.\n " , .{}) catch @trap ();
260+ const stderr = io .getStdErr ().writer ();
261+ stderr .print ("Panicked during a panic. Aborting.\n " , .{}) catch posix .abort ();
209262 },
210263 else = > {
211264 // Panicked while printing "Panicked during a panic."
212265 },
213266 };
214267
215- @trap ();
268+ posix .abort ();
269+ }
270+
271+ const getSelfDebugInfo = std .debug .getSelfDebugInfo ;
272+ const writeStackTrace = std .debug .writeStackTrace ;
273+
274+ // Used for 0.13.0 compatibility, technically this allocator is completely unused by "writeStackTrace"
275+ fn getDebugInfoAllocator () std.mem.Allocator {
276+ return std .heap .page_allocator ;
277+ }
278+
279+ fn dumpStackTrace (stack_trace : std.builtin.StackTrace ) void {
280+ nosuspend {
281+ if (comptime builtin .target .isWasm ()) {
282+ if (native_os == .wasi ) {
283+ const stderr = io .getStdErr ().writer ();
284+ stderr .print ("Unable to dump stack trace: not implemented for Wasm\n " , .{}) catch return ;
285+ }
286+ return ;
287+ }
288+ const stderr = io .getStdErr ().writer ();
289+ if (builtin .strip_debug_info ) {
290+ stderr .print ("Unable to dump stack trace: debug info stripped\n " , .{}) catch return ;
291+ return ;
292+ }
293+ const debug_info = getSelfDebugInfo () catch | err | {
294+ stderr .print ("Unable to dump stack trace: Unable to open debug info: {s}\n " , .{@errorName (err )}) catch return ;
295+ return ;
296+ };
297+ if (builtin .zig_version .major == 0 and builtin .zig_version .minor == 13 ) {
298+ // Legacy 0.13.0
299+ writeStackTrace (stack_trace , stderr , getDebugInfoAllocator (), debug_info , io .tty .detectConfig (io .getStdErr ())) catch | err | {
300+ stderr .print ("Unable to dump stack trace: {s}\n " , .{@errorName (err )}) catch return ;
301+ return ;
302+ };
303+ } else {
304+ // 0.14.0-dev+
305+ writeStackTrace (stack_trace , stderr , debug_info , io .tty .detectConfig (io .getStdErr ())) catch | err | {
306+ stderr .print ("Unable to dump stack trace: {s}\n " , .{@errorName (err )}) catch return ;
307+ return ;
308+ };
309+ }
310+ }
311+ }
312+
313+ const writeCurrentStackTrace = std .debug .writeCurrentStackTrace ;
314+ fn dumpCurrentStackTrace (start_addr : ? usize ) void {
315+ nosuspend {
316+ if (comptime builtin .target .isWasm ()) {
317+ if (native_os == .wasi ) {
318+ const stderr = io .getStdErr ().writer ();
319+ stderr .print ("Unable to dump stack trace: not implemented for Wasm\n " , .{}) catch return ;
320+ }
321+ return ;
322+ }
323+ const stderr = io .getStdErr ().writer ();
324+ if (builtin .strip_debug_info ) {
325+ stderr .print ("Unable to dump stack trace: debug info stripped\n " , .{}) catch return ;
326+ return ;
327+ }
328+ const debug_info = getSelfDebugInfo () catch | err | {
329+ stderr .print ("Unable to dump stack trace: Unable to open debug info: {s}\n " , .{@errorName (err )}) catch return ;
330+ return ;
331+ };
332+ writeCurrentStackTrace (stderr , debug_info , io .tty .detectConfig (io .getStdErr ()), start_addr ) catch | err | {
333+ stderr .print ("Unable to dump stack trace: {s}\n " , .{@errorName (err )}) catch return ;
334+ return ;
335+ };
336+ }
216337 }
217338};
0 commit comments