From dc42365007cb9138cc6705884b96284181ef0a03 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Thu, 20 Oct 2011 21:00:25 -0700 Subject: [PATCH] vm: fep on SIGINT or SIGQUIT instead of exception It's not robust currently to raise an exception because a lot of our code that isn't already written with exceptions in mind breaks. Also, a signal is likely to be received by an FFI callback installed on the IO multiplexer, which will cause Factor to die since the callback cannot handle the exception. We need a more robust solution to dealing with SIGINT. Also lay some groundwork for counting profile samples and reporting non-interrupting asynchronous signals. --- vm/debug.cpp | 4 +++ vm/errors.cpp | 32 ++++++++++++++++---- vm/os-unix.cpp | 81 ++++++++++++++++++++++++++++++-------------------- vm/vm.cpp | 6 +++- vm/vm.hpp | 11 +++++-- 5 files changed, 92 insertions(+), 42 deletions(-) diff --git a/vm/debug.cpp b/vm/debug.cpp index d00e248e71..7d9381b74a 100755 --- a/vm/debug.cpp +++ b/vm/debug.cpp @@ -352,6 +352,7 @@ void factor_vm::factorbug() } /* open_console(); */ + fep_p = true; std::cout << "Starting low level debugger...\n"; std::cout << " Basic commands:\n"; @@ -446,7 +447,10 @@ void factor_vm::factorbug() else if(strcmp(cmd,"g") == 0) dump_generations(); else if(strcmp(cmd,"q") == 0) + { + fep_p = false; return; + } else if(strcmp(cmd,"x") == 0) exit(1); else if(strcmp(cmd,"im") == 0) diff --git a/vm/errors.cpp b/vm/errors.cpp index 23924df4e5..c57e7832f7 100755 --- a/vm/errors.cpp +++ b/vm/errors.cpp @@ -156,19 +156,39 @@ void fp_signal_handler_impl() current_vm()->fp_signal_handler_impl(); } +void factor_vm::enqueue_safepoint_fep() +{ + if (fep_p) + fatal_error("Low-level debugger interrupted", 0); + safepoint_fep = true; + code->guard_safepoint(); +} + void factor_vm::enqueue_safepoint_signal(cell signal) { - safepoint_signal_number = signal; + sigaddset(&safepoint_signals, signal); code->guard_safepoint(); } +void factor_vm::enqueue_safepoint_sample() +{ + if (!sampling_p) + fatal_error("Received sampling signal while not sampling!", 0); + ++safepoint_sample_count; +} + void factor_vm::handle_safepoint() { - assert(safepoint_signal_number != 0); - code->unguard_safepoint(); - cell signal = safepoint_signal_number; - safepoint_signal_number = 0; - general_error(ERROR_SIGNAL,from_unsigned_cell(signal),false_object); + if (safepoint_fep) { + std::cout << "Interrupted. Entering low-level debugger...\n"; + std::cout << "\n"; + factorbug(); + code->unguard_safepoint(); + safepoint_fep = false; + return; + } + // XXX handle sample count + // XXX handle queued signals } } diff --git a/vm/os-unix.cpp b/vm/os-unix.cpp index efd3e03bb2..0eea9d9698 100755 --- a/vm/os-unix.cpp +++ b/vm/os-unix.cpp @@ -167,7 +167,7 @@ void synchronous_signal_handler(int signal, siginfo_t *siginfo, void *uap) fatal_error("Foreign thread received signal ", signal); } -void next_safepoint_signal_handler(int signal, siginfo_t *siginfo, void *uap) +void enqueue_signal_handler(int signal, siginfo_t *siginfo, void *uap) { factor_vm *vm = current_vm_p(); if (vm) @@ -176,6 +176,24 @@ void next_safepoint_signal_handler(int signal, siginfo_t *siginfo, void *uap) fatal_error("Foreign thread received signal ", signal); } +void fep_signal_handler(int signal, siginfo_t *siginfo, void *uap) +{ + factor_vm *vm = current_vm_p(); + if (vm) + vm->enqueue_safepoint_fep(); + else + fatal_error("Foreign thread received signal ", signal); +} + +void sample_signal_handler(int signal, siginfo_t *siginfo, void *uap) +{ + factor_vm *vm = current_vm_p(); + if (vm) + vm->enqueue_safepoint_sample(); + else + fatal_error("Foreign thread received signal ", signal); +} + void ignore_signal_handler(int signal, siginfo_t *siginfo, void *uap) { } @@ -206,6 +224,15 @@ static void sigaction_safe(int signum, const struct sigaction *act, struct sigac fatal_error("sigaction failed", 0); } +static void init_sigaction_with_handler(struct sigaction *act, + void (*handler)(int, siginfo_t*, void*)) +{ + memset(act, 0, sizeof(struct sigaction)); + sigemptyset(&act->sa_mask); + act->sa_sigaction = handler; + act->sa_flags = SA_SIGINFO | SA_ONSTACK; +} + void factor_vm::unix_init_signals() { /* OpenBSD doesn't support sigaltstack() if we link against @@ -225,53 +252,43 @@ void factor_vm::unix_init_signals() struct sigaction memory_sigaction; struct sigaction synchronous_sigaction; - struct sigaction next_safepoint_sigaction; + struct sigaction enqueue_sigaction; + struct sigaction fep_sigaction; + struct sigaction sample_sigaction; struct sigaction fpe_sigaction; struct sigaction ignore_sigaction; - memset(&memory_sigaction,0,sizeof(struct sigaction)); - sigemptyset(&memory_sigaction.sa_mask); - memory_sigaction.sa_sigaction = memory_signal_handler; - memory_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK; - + init_sigaction_with_handler(&memory_sigaction, memory_signal_handler); sigaction_safe(SIGBUS,&memory_sigaction,NULL); sigaction_safe(SIGSEGV,&memory_sigaction,NULL); sigaction_safe(SIGTRAP,&memory_sigaction,NULL); - memset(&fpe_sigaction,0,sizeof(struct sigaction)); - sigemptyset(&fpe_sigaction.sa_mask); - fpe_sigaction.sa_sigaction = fpe_signal_handler; - fpe_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK; - + init_sigaction_with_handler(&fpe_sigaction, fpe_signal_handler); sigaction_safe(SIGFPE,&fpe_sigaction,NULL); - memset(&synchronous_sigaction,0,sizeof(struct sigaction)); - sigemptyset(&synchronous_sigaction.sa_mask); - synchronous_sigaction.sa_sigaction = synchronous_signal_handler; - synchronous_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK; - + init_sigaction_with_handler(&synchronous_sigaction, synchronous_signal_handler); sigaction_safe(SIGILL,&synchronous_sigaction,NULL); sigaction_safe(SIGABRT,&synchronous_sigaction,NULL); - memset(&next_safepoint_sigaction,0,sizeof(struct sigaction)); - sigemptyset(&next_safepoint_sigaction.sa_mask); - next_safepoint_sigaction.sa_sigaction = next_safepoint_signal_handler; - next_safepoint_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK; - sigaction_safe(SIGALRM,&next_safepoint_sigaction,NULL); - sigaction_safe(SIGVTALRM,&next_safepoint_sigaction,NULL); - sigaction_safe(SIGPROF,&next_safepoint_sigaction,NULL); - sigaction_safe(SIGQUIT,&next_safepoint_sigaction,NULL); - sigaction_safe(SIGINT,&next_safepoint_sigaction,NULL); - sigaction_safe(SIGUSR1,&next_safepoint_sigaction,NULL); - sigaction_safe(SIGUSR2,&next_safepoint_sigaction,NULL); + init_sigaction_with_handler(&enqueue_sigaction, enqueue_signal_handler); + sigaction_safe(SIGUSR1,&enqueue_sigaction,NULL); + sigaction_safe(SIGUSR2,&enqueue_sigaction,NULL); + sigaction_safe(SIGWINCH,&enqueue_sigaction,NULL); +#ifdef SIGINFO + sigaction_safe(SIGINFO,&enqueue_sigaction,NULL); +#endif + + init_sigaction_with_handler(&fep_sigaction, fep_signal_handler); + sigaction_safe(SIGQUIT,&fep_sigaction,NULL); + sigaction_safe(SIGINT,&fep_sigaction,NULL); + + init_sigaction_with_handler(&sample_sigaction, sample_signal_handler); + sigaction_safe(SIGALRM,&sample_sigaction,NULL); /* We don't use SA_IGN here because then the ignore action is inherited by subprocesses, which we don't want. There is a unit test in io.launcher.unix for this. */ - memset(&ignore_sigaction,0,sizeof(struct sigaction)); - sigemptyset(&ignore_sigaction.sa_mask); - ignore_sigaction.sa_sigaction = ignore_signal_handler; - ignore_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK; + init_sigaction_with_handler(&ignore_sigaction, ignore_signal_handler); sigaction_safe(SIGPIPE,&ignore_sigaction,NULL); } diff --git a/vm/vm.cpp b/vm/vm.cpp index 5379e33db9..9476b33afd 100755 --- a/vm/vm.cpp +++ b/vm/vm.cpp @@ -8,15 +8,19 @@ factor_vm::factor_vm() : callback_id(0), c_to_factor_func(NULL), profiling_p(false), - safepoint_signal_number(0), + sampling_p(false), + safepoint_fep(false), + safepoint_sample_count(0), gc_off(false), current_gc(NULL), gc_events(NULL), + fep_p(false), fep_disabled(false), full_output(false), last_nano_count(0), signal_callstack_seg(NULL) { + sigemptyset(&safepoint_signals); primitive_reset_dispatch_stats(); } diff --git a/vm/vm.hpp b/vm/vm.hpp index 88633023d5..f76beb5c96 100755 --- a/vm/vm.hpp +++ b/vm/vm.hpp @@ -51,13 +51,17 @@ struct factor_vm /* Is call counting enabled? */ bool profiling_p; + /* Is sampling profiler enabled? */ + bool sampling_p; /* Global variables used to pass fault handler state from signal handler to VM */ cell signal_number; cell signal_fault_addr; unsigned int signal_fpu_status; - cell safepoint_signal_number; + bool safepoint_fep; + cell safepoint_sample_count; + sigset_t safepoint_signals; /* GC is off during heap walking */ bool gc_off; @@ -89,6 +93,7 @@ struct factor_vm std::vector code_roots; /* Debugger */ + bool fep_p; bool fep_disabled; bool full_output; @@ -182,6 +187,8 @@ struct factor_vm void memory_signal_handler_impl(); void synchronous_signal_handler_impl(); void fp_signal_handler_impl(); + void enqueue_safepoint_fep(); + void enqueue_safepoint_sample(); void enqueue_safepoint_signal(cell signal); void handle_safepoint(); @@ -578,7 +585,6 @@ struct factor_vm template void iterate_callstack_object(callstack *stack_, Iterator &iterator); void check_frame(stack_frame *frame); callstack *allot_callstack(cell size); - stack_frame *fix_callstack_top(stack_frame *top); stack_frame *second_from_top_stack_frame(context *ctx); cell capture_callstack(context *ctx); void primitive_callstack(); @@ -591,7 +597,6 @@ struct factor_vm cell frame_scan(stack_frame *frame); cell frame_offset(stack_frame *frame); void set_frame_offset(stack_frame *frame, cell offset); - void scrub_return_address(); void primitive_callstack_to_array(); stack_frame *innermost_stack_frame(stack_frame *bottom, stack_frame *top); void primitive_innermost_stack_frame_executing();