mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
MacOS uses regular stacktrace for errors.
This commit is contained in:
committed by
Christoffer Lerno
parent
c074e79069
commit
c4228e08c5
@@ -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
|
||||
|
||||
@@ -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
73
lib/std/os/backtrace.c3
Normal 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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define COMPILER_VERSION "0.4.653"
|
||||
#define COMPILER_VERSION "0.4.654"
|
||||
@@ -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" }
|
||||
|
||||
@@ -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" }
|
||||
@@ -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" }
|
||||
@@ -227,4 +227,3 @@ entry:
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind "no-trapping-math"="true" }
|
||||
|
||||
@@ -88,5 +88,3 @@ switch.exit: ; preds = %switch.default
|
||||
loop.exit: ; preds = %loop.cond
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind "no-trapping-math"="true" }
|
||||
Reference in New Issue
Block a user