MacOS uses regular stacktrace for errors.

This commit is contained in:
Christoffer Lerno
2023-09-21 16:11:56 +02:00
committed by Christoffer Lerno
parent c074e79069
commit c4228e08c5
21 changed files with 291 additions and 24 deletions

View File

@@ -89,7 +89,47 @@ struct CallstackElement
uint line;
}
fn void default_panic(String message, String file, String function, uint line)
fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::DARWIN)
{
@stack_mem(4096; Allocator *mem)
{
BacktraceList! backtrace = darwin::backtrace_load(mem);
if (catch backtrace) return false;
if (backtrace.len() <= backtraces_to_ignore) return false;
(void)io::stderr().print("\nERROR: '");
(void)io::stderr().print(message);
io::printn("'");
foreach (i, &trace : backtrace)
{
if (i < backtraces_to_ignore) continue;
if (trace.is_unknown())
{
(void)io::stderr().printn(" in ???");
continue;
}
if (trace.has_file())
{
(void)io::stderr().printfn(" in %s (%s:%d) [%s]", trace.function, trace.file, trace.line, trace.object_file);
continue;
}
(void)io::stderr().printfn(" in %s (source unavailable) [%s]", trace.function, trace.object_file);
}
return true;
};
}
fn void default_panic(String message, String file, String function, uint line) @if(env::DARWIN)
{
$if $defined(io::stderr) && $defined(Stream.printf):
if (!print_backtrace(message, 2))
{
(void)io::stderr().printfn("\nERROR: '%s', in %s (%s:%d)", message, function, file, line);
return;
}
$endif
$$trap();
}
fn void default_panic(String message, String file, String function, uint line) @if(!env::DARWIN)
{
CallstackElement* stack = $$stacktrace();
$if $defined(io::stderr) && $defined(Stream.printf):
@@ -323,7 +363,12 @@ macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c);
module std::core::builtin @if((env::LINUX || env::DARWIN) && env::COMPILER_SAFE_MODE && env::DEBUG_SYMBOLS);
import libc;
fn void sig_panic(String message)
fn void sig_panic(String message) @if(env::DARWIN)
{
default_panic(message, "???", "???", 0);
}
fn void sig_panic(String message) @if(!env::DARWIN)
{
$if $defined(io::stderr) && $defined(File.printf):
CallstackElement* stack = $$stacktrace();
@@ -353,7 +398,6 @@ fn void sig_bus_error(CInt i)
fn void sig_segmentation_fault(CInt i)
{
sig_panic("Out of bounds memory access.");
$$trap();
}
fn void install_signal_handler(CInt signal, SignalFunction func) @local

View File

@@ -290,7 +290,7 @@ fn usz! String.rindex_of(s, String needle)
fn String ZString.as_str(str)
{
return (String)((char*)str)[:str.len()];
return (String)(str[:str.len()]);
}
fn usz ZString.char_len(str)

73
lib/std/os/backtrace.c3 Normal file
View File

@@ -0,0 +1,73 @@
module std::os::backtrace;
fault BacktraceFault
{
SEGMENT_NOT_FOUND,
EXECUTABLE_PATH_NOT_FOUND,
IMAGE_NOT_FOUND,
NO_BACKTRACE_SYMBOLS,
}
const Backtrace BACKTRACE_UNKNOWN = { 0, "", "", "", 0, null };
struct Backtrace
{
uptr offset;
String function;
String object_file;
String file;
uint line;
Allocator* allocator;
}
fn bool Backtrace.has_file(&self)
{
return self.file.len > 0;
}
fn bool Backtrace.is_unknown(&self)
{
return !self.object_file.len;
}
fn usz! Backtrace.to_format(&self, Formatter* formatter) @dynamic
{
if (self.has_file())
{
return formatter.printf("%s (in %s) (%s:%d)", self.function, self.object_file, self.file, self.line);
}
if (self.is_unknown())
{
return formatter.printf("??? (in unknown)");
}
return formatter.printf("%s (in %s) (source unavailable)", self.function, self.object_file);
}
fn void Backtrace.free(&self)
{
if (!self.allocator) return;
free(self.function, .using = self.allocator);
free(self.object_file, .using = self.allocator);
free(self.file, .using = self.allocator);
}
fn Backtrace* Backtrace.init(&self, uptr offset, String function, String object_file, String file = "", uint line = 0, Allocator* using = mem::heap())
{
if (!using)
{
self.offset = offset;
self.function = function;
self.object_file = object_file;
self.file = file;
self.line = 0;
self.allocator = null;
return self;
}
self.offset = offset;
self.function = function.copy(using);
self.object_file = object_file.copy(using);
self.file = file.copy(using);
self.allocator = using;
self.line = line;
return self;
}

View File

@@ -9,8 +9,8 @@ fn uint num_cpu()
usz len = 4;
uint count;
nm = { macos::CTL_HW, macos::HW_NCPU };
macos::sysctl(&nm, 2, &count, &len, null, 0);
nm = { darwin::CTL_HW, darwin::HW_NCPU };
darwin::sysctl(&nm, 2, &count, &len, null, 0);
if (count < 1) count = 1;
return count;
}

View File

@@ -55,3 +55,12 @@ fn void clear_var(String name)
};
$endif
}
fn String! executable_path(Allocator *using = mem::heap())
{
$if env::DARWIN:
return darwin::executable_path();
$else
return "<Unsupported>";
$endif
}

View File

@@ -1,4 +1,5 @@
module std::os::macos @if(env::DARWIN);
module std::os::darwin @if(env::DARWIN);
import std::collections::list;
const CTL_UNSPEC = 0; /* unused */
const CTL_KERN = 1; /* "high kernel": proc, limits */
@@ -36,3 +37,130 @@ const HW_L3CACHESIZE = 22; /* int: L3 Cache Size in Bytes */
const HW_MAXID = 23; /* number of valid hw ids */
extern fn CInt sysctl(CInt *name, CUInt namelen, void *oldp, usz *oldlenp, void *newp, usz newlen);
extern fn CInt darwin_NSGetExecutablePath(char* buffer, uint *size) @extern("_NSGetExecutablePath") @builtin;
extern fn Darwin_segment_command_64* getsegbyname(ZString segname);
extern fn uint _dyld_image_count();
extern fn ZString _dyld_get_image_name(uint image_index);
extern fn iptr _dyld_get_image_vmaddr_slide(uint image_index);
extern fn CInt dladdr(void* addr, Darwin_Dl_info* info);
struct Darwin_Dl_info
{
ZString dli_fname; /* Pathname of shared object */
void* dli_fbase; /* Base address of shared object */
ZString dli_sname; /* Name of nearest symbol */
void* dli_saddr; /* Address of nearest symbol */
}
struct Darwin_segment_command_64
{
uint cmd; /* LC_SEGMENT_64 */
uint cmdsize; /* includes sizeof section_64 structs */
char[16] segname; /* segment name */
ulong vmaddr; /* memory address of this segment */
ulong vmsize; /* memory size of this segment */
ulong fileoff; /* file offset of this segment */
ulong filesize; /* amount to map from the file */
int maxprot; /* maximum VM protection */
int initprot; /* initial VM protection */
uint nsects; /* number of sections in segment */
uint flags; /* flags */
}
fn String! executable_path(Allocator *using = mem::heap())
{
char[4096] path;
uint len = path.len;
if (darwin_NSGetExecutablePath(&path, &len) < 0) return SearchResult.MISSING?;
return ((ZString)&path).copy(.using = using);
}
def BacktraceList = List(<Backtrace>);
fn uptr! load_address() @local
{
Darwin_segment_command_64* cmd = darwin::getsegbyname("__TEXT");
if (!cmd) return BacktraceFault.SEGMENT_NOT_FOUND?;
String path = env::executable_path(mem::temp()) ?? BacktraceFault.EXECUTABLE_PATH_NOT_FOUND?!;
uint dyld_count = darwin::_dyld_image_count();
for (uint i = 0; i < dyld_count; i++)
{
ZString image_name = darwin::_dyld_get_image_name(i);
if (!image_name) continue;
if (image_name.as_str() != path) continue;
return cmd.vmaddr + darwin::_dyld_get_image_vmaddr_slide(i);
}
return BacktraceFault.IMAGE_NOT_FOUND?;
}
fn Backtrace! backtrace_load_element(String execpath, void* buffer, void* load_address, Allocator* using = mem::heap()) @local
{
@stack_mem(1024; Allocator* mem)
{
if (buffer)
{
SubProcess process = process::create({ "atos",
"-o", execpath, "-arch", env::AARCH64 ? "arm64" : "x86_64", "-l",
string::printf("%p", load_address, .using = mem),
string::printf("%p", buffer, .using = mem),
"-fullPath" })!;
process.join()!;
char[4096] buf;
usz len = process.read_stdout(&buf, buf.len)!;
String s = (String)buf[:len - 1];
String[] parts = s.split(" ", .using = mem);
if (parts.len == 4)
{
String[] path_parts = parts[3].split(":", .using = mem);
return {
.offset = (uptr)buffer,
.function = parts[0].copy(using),
.object_file = parts[2][..^2].copy(using),
.file = path_parts[0][1..].copy(using),
.line = path_parts[1][..^2].to_uint()!,
.allocator = using
};
}
}
Darwin_Dl_info info;
if (!buffer || !darwin::dladdr(buffer, &info)) return backtrace::BACKTRACE_UNKNOWN;
return {
.offset = (uptr)buffer,
.function = info.dli_sname ? info.dli_sname.copy(using) : "???".copy(using),
.object_file = info.dli_fname.copy(using),
.file = "".copy(using),
.line = 0
};
};
}
fn BacktraceList! backtrace_load(Allocator* using = mem::heap())
{
void*[256] bt_buffer;
CInt size = posix::backtrace(&bt_buffer, 256);
void *load_addr = (void *)load_address()!;
BacktraceList list;
list.init(size, .using = using);
defer catch
{
foreach (trace : list)
{
trace.free();
}
list.free();
}
@stack_mem(256; Allocator* mem)
{
String execpath = executable_path(mem)!;
for (usz i = 1; i < size; i++)
{
char[1024] fname;
void* buffer = bt_buffer[i];
Backtrace trace = backtrace_load_element(execpath, buffer, load_addr, .using = using)!;
list.append(trace);
}
};
return list;
}

View File

@@ -124,7 +124,6 @@ void update_build_target_with_opt_level(BuildTarget *target, OptimizationSetting
single_module = true;
break;
case OPT_SETTING_O5:
single_module = true;
optlevel = OPTIMIZATION_AGGRESSIVE;
safety_level = SAFETY_OFF;
fp_opt = FP_FAST;

View File

@@ -23,6 +23,7 @@ double compiler_ir_gen_time;
double compiler_codegen_time;
double compiler_link_time;
const char* c3_suffix_list[3] = { ".c3", ".c3t", ".c3i" };
extern int llvm_version_major;
@@ -1023,4 +1024,5 @@ File *compile_and_invoke(const char *file, const char *args)
error_exit("Error invoking script %s with arguments %s.", file, args);
}
return source_file_text_load(file, out);
}
}

View File

@@ -747,6 +747,7 @@ const char *concat_string_parts(const char **args)
return output;
}
void platform_linker(const char *output_file, const char **files, unsigned file_count)
{
DEBUG_LOG("Using cc linker.");
@@ -763,6 +764,16 @@ void platform_linker(const char *output_file, const char **files, unsigned file_
{
error_exit("Failed to link executable '%s' using command '%s'.\n", output_file, output);
}
if (os_is_apple(platform_target.os) && active_target.debug_info == DEBUG_INFO_FULL)
{
// Create .dSYM
scratch_buffer_clear();
scratch_buffer_printf("dsymutil %s", output_file);
if (system(scratch_buffer_to_string()) != 0)
{
puts("Failed to create .dSYM files, debugging will be impacted.");
}
}
printf("Program linked to executable '%s'.\n", output_file);
}

View File

@@ -1006,7 +1006,9 @@ void llvm_append_function_attributes(GenContext *c, Decl *decl)
{
llvm_attribute_add_string(c, function, "frame-pointer", "all", -1);
}
llvm_attribute_add_string(c, function, "stack-protector-buffer-size", "8", -1);
llvm_attribute_add_string(c, function, "no-trapping-math", "true", -1);
if (prototype->ret_by_ref)
{
ABIArgInfo *info = prototype->ret_by_ref_abi_info;

View File

@@ -138,7 +138,7 @@ INLINE void llvm_emit_unreachable_stmt(GenContext *c, BEValue *result_value, Exp
INLINE void llvm_emit_stacktrace(GenContext *c, BEValue *result_value, Expr *expr)
{
if (!c->debug.enable_stacktrace)
if (!c->debug.emulated_stacktrace)
{
llvm_value_set(result_value, llvm_get_zero(c, type_voidptr), type_voidptr);
return;

View File

@@ -6093,7 +6093,7 @@ static inline void llvm_emit_macro_block(GenContext *c, BEValue *be_value, Expr
if (llvm_use_debug(c))
{
llvm_debug_push_lexical_scope(c, astptr(expr->macro_block.first_stmt)->span);
if (c->debug.enable_stacktrace)
if (c->debug.emulated_stacktrace)
{
restore_at_exit = true;
llvm_emit_update_stack_row(c, expr->span.row);

View File

@@ -466,7 +466,7 @@ static void llvm_emit_stacktrace_definitions(GenContext *c)
void llvm_emit_pop_stacktrace(GenContext *c, Stacktrace *slot)
{
if (!c->debug.enable_stacktrace) return;
if (!c->debug.emulated_stacktrace) return;
if (slot)
{
c->debug.stacktrace = *slot;
@@ -475,7 +475,7 @@ void llvm_emit_pop_stacktrace(GenContext *c, Stacktrace *slot)
}
void llvm_emit_update_stack_row(GenContext *c, uint32_t row)
{
if (!c->debug.enable_stacktrace) return;
if (!c->debug.emulated_stacktrace) return;
if (row == 0) row = 1;
if (c->debug.stacktrace.last_row == row) return;
c->debug.stacktrace.last_row = row;
@@ -520,7 +520,7 @@ void llvm_emit_body(GenContext *c, LLVMValueRef function, FunctionPrototype *pro
LLVMValueRef prev_function = c->function;
LLVMBuilderRef prev_builder = c->builder;
bool use_stacktrace = emit_debug && c->debug.enable_stacktrace;
bool use_stacktrace = emit_debug && c->debug.emulated_stacktrace;
if (use_stacktrace && !c->debug.stack_init_fn)
{
llvm_emit_stacktrace_definitions(c);

View File

@@ -73,6 +73,7 @@ typedef struct
{
unsigned runtime_version : 8;
bool enable_stacktrace : 1;
bool emulated_stacktrace : 1;
LLVMDIBuilderRef builder;
DebugFile *debug_files;
DebugFile file;

View File

@@ -168,6 +168,7 @@ void gencontext_begin_module(GenContext *c)
LLVMStructSetBody(c->debug.stack_type, types, 5, false);
c->debug.current_stack_ptr = NULL;
c->debug.enable_stacktrace = true;
c->debug.emulated_stacktrace = !os_is_apple(platform_target.os);
}
}
c->global_builder = LLVMCreateBuilder();

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.4.653"
#define COMPILER_VERSION "0.4.654"

View File

@@ -13,5 +13,5 @@ target triple = "wasm32-unknown-unknown"
declare i32 @get_abc() #0
define i32 @fgh() #1
attributes #0 = { nounwind "no-trapping-math"="true" "wasm-import-name"="get_abc" }
attributes #1 = { nounwind "no-trapping-math"="true" "wasm-export-name"="fgh" }
attributes #0 = { nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "wasm-import-name"="get_abc" }
attributes #1 = { nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "wasm-export-name"="fgh" }

View File

@@ -53,6 +53,6 @@ entry:
call void @test.testme()
ret void
}
attributes #0 = { noreturn nounwind "no-trapping-math"="true" }
attributes #1 = { noinline nounwind "no-trapping-math"="true" }
attributes #2 = { nounwind "no-trapping-math"="true" }
attributes #0 = { noreturn nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { noinline nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #2 = { nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" }

View File

@@ -12,4 +12,4 @@ entry:
ret void
}
attributes #0 = { naked nounwind "no-trapping-math"="true" }
attributes #0 = { naked nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" }

View File

@@ -227,4 +227,3 @@ entry:
ret i32 0
}
attributes #0 = { nounwind "no-trapping-math"="true" }

View File

@@ -88,5 +88,3 @@ switch.exit: ; preds = %switch.default
loop.exit: ; preds = %loop.cond
ret i32 0
}
attributes #0 = { nounwind "no-trapping-math"="true" }