diff --git a/GNUmakefile b/GNUmakefile index c48e3a363e..ea0ee832de 100755 --- a/GNUmakefile +++ b/GNUmakefile @@ -34,6 +34,7 @@ ifdef CONFIG vm/code_heap.o \ vm/compaction.o \ vm/contexts.o \ + vm/counting_profiler.o \ vm/data_heap.o \ vm/data_heap_checker.o \ vm/debug.o \ @@ -56,9 +57,9 @@ ifdef CONFIG vm/object_start_map.o \ vm/objects.o \ vm/primitives.o \ - vm/counting_profiler.o \ vm/quotations.o \ vm/run.o \ + vm/sampling_profiler.o \ vm/strings.o \ vm/to_tenured_collector.o \ vm/tuples.o \ diff --git a/Nmakefile b/Nmakefile index 1aa568b746..a4d2da4503 100755 --- a/Nmakefile +++ b/Nmakefile @@ -21,7 +21,7 @@ PLAF_DLL_OBJS = vm\os-windows-x86.64.obj vm\cpu-x86.obj ML_FLAGS = /nologo /safeseh -EXE_OBJS = vm/main-windows.obj vm\factor.res +EXE_OBJS = vm\main-windows.obj vm\factor.res DLL_OBJS = $(PLAF_DLL_OBJS) \ vm\os-windows.obj \ @@ -37,6 +37,7 @@ DLL_OBJS = $(PLAF_DLL_OBJS) \ vm\code_heap.obj \ vm\compaction.obj \ vm\contexts.obj \ + vm\counting_profiler.obj \ vm\data_heap.obj \ vm\data_heap_checker.obj \ vm\debug.obj \ @@ -60,9 +61,9 @@ DLL_OBJS = $(PLAF_DLL_OBJS) \ vm\object_start_map.obj \ vm\objects.obj \ vm\primitives.obj \ - vm\counting_profiler.obj \ vm\quotations.obj \ vm\run.obj \ + vm\sampling_profiler.obj \ vm\strings.obj \ vm\to_tenured_collector.obj \ vm\tuples.obj \ diff --git a/vm/errors.cpp b/vm/errors.cpp index 340ccbaa1a..7ed28babba 100755 --- a/vm/errors.cpp +++ b/vm/errors.cpp @@ -173,7 +173,11 @@ void factor_vm::enqueue_safepoint_fep() void factor_vm::enqueue_safepoint_sample() { if (sampling_profiler_p) - ++safepoint_sample_count; + { + FACTOR_ATOMIC_ADD(&safepoint_sample_count, 1); + if (current_gc) + FACTOR_ATOMIC_ADD(&safepoint_gc_sample_count, 1); + } } void factor_vm::handle_safepoint() @@ -181,10 +185,15 @@ void factor_vm::handle_safepoint() code->unguard_safepoint(); if (safepoint_fep) { + if (sampling_profiler_p) + end_sampling_profiler(); std::cout << "Interrupted\n"; factorbug(); safepoint_fep = false; - return; + } + else if (sampling_profiler_p) + { + record_sample(); } } diff --git a/vm/master.hpp b/vm/master.hpp index c7f8836c2b..2b51b0575f 100755 --- a/vm/master.hpp +++ b/vm/master.hpp @@ -95,6 +95,7 @@ namespace factor #include "run.hpp" #include "objects.hpp" #include "counting_profiler.hpp" +#include "sampling_profiler.hpp" #include "errors.hpp" #include "bignumint.hpp" #include "bignum.hpp" diff --git a/vm/os-unix.cpp b/vm/os-unix.cpp index 90324de03e..28c697f575 100755 --- a/vm/os-unix.cpp +++ b/vm/os-unix.cpp @@ -153,6 +153,22 @@ void factor_vm::enqueue_safepoint_signal(cell signal) */ } +void factor_vm::start_sampling_profiler_timer() +{ + struct itimerval timer; + memset((void*)&timer, 0, sizeof(struct itimerval)); + timer.it_value.tv_usec = 1000000/FACTOR_PROFILE_SAMPLES_PER_SECOND; + timer.it_interval.tv_usec = 1000000/FACTOR_PROFILE_SAMPLES_PER_SECOND; + setitimer(ITIMER_REAL, &timer, NULL); +} + +void factor_vm::end_sampling_profiler_timer() +{ + struct itimerval timer; + memset((void*)&timer, 0, sizeof(struct itimerval)); + setitimer(ITIMER_REAL, &timer, NULL); +} + void memory_signal_handler(int signal, siginfo_t *siginfo, void *uap) { factor_vm *vm = current_vm(); @@ -239,21 +255,6 @@ static void init_sigaction_with_handler(struct sigaction *act, void factor_vm::unix_init_signals() { - /* OpenBSD doesn't support sigaltstack() if we link against - libpthread. See http://redmine.ruby-lang.org/issues/show/1239 */ - -#ifndef __OpenBSD__ - signal_callstack_seg = new segment(callstack_size,false); - - stack_t signal_callstack; - signal_callstack.ss_sp = (char *)signal_callstack_seg->start; - signal_callstack.ss_size = signal_callstack_seg->size; - signal_callstack.ss_flags = 0; - - if(sigaltstack(&signal_callstack,(stack_t *)NULL) < 0) - fatal_error("sigaltstack() failed",0); -#endif - struct sigaction memory_sigaction; struct sigaction synchronous_sigaction; struct sigaction enqueue_sigaction; diff --git a/vm/primitives.hpp b/vm/primitives.hpp index b3a36b273a..31845a2122 100644 --- a/vm/primitives.hpp +++ b/vm/primitives.hpp @@ -45,6 +45,7 @@ namespace factor _(context_object) \ _(context_object_for) \ _(current_callback) \ + _(counting_profiler) \ _(data_room) \ _(datastack) \ _(datastack_for) \ @@ -100,7 +101,6 @@ namespace factor _(modify_code_heap) \ _(nano_count) \ _(optimized_p) \ - _(counting_profiler) \ _(quot_compiled_p) \ _(quotation_code) \ _(reset_dispatch_stats) \ @@ -109,6 +109,7 @@ namespace factor _(resize_string) \ _(retainstack) \ _(retainstack_for) \ + _(sampling_profiler) \ _(save_image) \ _(save_image_and_exit) \ _(set_context_object) \ diff --git a/vm/sampling_profiler.cpp b/vm/sampling_profiler.cpp new file mode 100644 index 0000000000..08e6427654 --- /dev/null +++ b/vm/sampling_profiler.cpp @@ -0,0 +1,62 @@ +#include "master.hpp" + +namespace factor +{ + +void factor_vm::record_sample() +{ + cell recorded_sample_count; + cell recorded_gc_sample_count; + + recorded_sample_count = safepoint_sample_count; + recorded_gc_sample_count = safepoint_gc_sample_count; + if (recorded_sample_count == 0 && recorded_gc_sample_count == 0) + return; + + /* Another sample signal could be raised while we record these counts */ + FACTOR_ATOMIC_SUB(&safepoint_sample_count, recorded_sample_count); + FACTOR_ATOMIC_SUB(&safepoint_gc_sample_count, recorded_gc_sample_count); + + samples.push_back(profiling_sample( + recorded_sample_count, + recorded_gc_sample_count, + ctx, + capture_callstack(ctx) + )); +} + +void factor_vm::set_sampling_profiler(bool sampling_p) +{ + if (sampling_p == sampling_profiler_p) + return; + + if (sampling_p) + start_sampling_profiler(); + else + end_sampling_profiler(); +} + +void factor_vm::start_sampling_profiler() +{ + safepoint_sample_count = 0; + safepoint_gc_sample_count = 0; + samples.clear(); + samples.reserve(10*FACTOR_PROFILE_SAMPLES_PER_SECOND); + sampling_profiler_p = true; + start_sampling_profiler_timer(); +} + +void factor_vm::end_sampling_profiler() +{ + end_sampling_profiler_timer(); + record_sample(); + sampling_profiler_p = false; +} + +void factor_vm::primitive_sampling_profiler() +{ + set_sampling_profiler(to_boolean(ctx->pop())); +} + + +} diff --git a/vm/sampling_profiler.hpp b/vm/sampling_profiler.hpp new file mode 100644 index 0000000000..540d6b6913 --- /dev/null +++ b/vm/sampling_profiler.hpp @@ -0,0 +1,30 @@ +namespace factor +{ + +#define FACTOR_PROFILE_SAMPLES_PER_SECOND 1000 + +struct profiling_sample +{ + // Number of samples taken before the safepoint that recorded the sample + cell sample_count; + // Number of samples taken during GC + cell gc_sample_count; + // Active context during sample + context *ctx; + // The callstack at safepoint time + cell callstack; + + profiling_sample(cell sample_count, + cell gc_sample_count, + context *ctx, + cell callstack) + : + sample_count(sample_count), + gc_sample_count(gc_sample_count), + ctx(ctx), + callstack(callstack) + { + } +}; + +} diff --git a/vm/utilities.hpp b/vm/utilities.hpp index 5f37644213..2b93a62fbc 100755 --- a/vm/utilities.hpp +++ b/vm/utilities.hpp @@ -53,4 +53,45 @@ vm_char *safe_strdup(const vm_char *str); cell read_cell_hex(); VM_C_API void *factor_memcpy(void *dst, void *src, size_t len); +#if defined(WINDOWS) + + #if defined(FACTOR_64) + + #define FACTOR_ATOMIC_CAS(ptr, old_val, new_val) \ + (InterlockedCompareExchange64(ptr, new_val, old_val) == old_val) + + #define FACTOR_ATOMIC_ADD(ptr, val) \ + InterlockedAdd64(ptr, val) + + #define FACTOR_ATOMIC_SUB(ptr, val) \ + InterlockedSub64(ptr, val) + + #else + + #define FACTOR_ATOMIC_CAS(ptr, old_val, new_val) \ + (InterlockedCompareExchange(ptr, new_val, old_val) == old_val) + + #define FACTOR_ATOMIC_ADD(ptr, val) \ + InterlockedAdd(ptr, val) + + #define FACTOR_ATOMIC_SUB(ptr, val) \ + InterlockedSub(ptr, val) + + #endif + +#elif defined(__GNUC__) || defined(__clang__) + + #define FACTOR_ATOMIC_CAS(ptr, old_val, new_val) \ + __sync_bool_compare_and_swap(ptr, old_val, new_val) + + #define FACTOR_ATOMIC_ADD(ptr, val) \ + __sync_add_and_fetch(ptr, val) + + #define FACTOR_ATOMIC_SUB(ptr, val) \ + __sync_sub_and_fetch(ptr, val) + +#else + #error "Unsupported compiler" +#endif + } diff --git a/vm/vm.cpp b/vm/vm.cpp index a1da97cd6d..47669f615a 100755 --- a/vm/vm.cpp +++ b/vm/vm.cpp @@ -11,6 +11,7 @@ factor_vm::factor_vm() : sampling_profiler_p(false), safepoint_fep(false), safepoint_sample_count(0), + safepoint_gc_sample_count(0), gc_off(false), current_gc(NULL), gc_events(NULL), diff --git a/vm/vm.hpp b/vm/vm.hpp index 4c92caa26f..2c2772e990 100755 --- a/vm/vm.hpp +++ b/vm/vm.hpp @@ -60,9 +60,8 @@ struct factor_vm /* External entry points */ c_to_factor_func_type c_to_factor_func; - /* Is call counting enabled? */ + /* Is profiling enabled? */ bool counting_profiler_p; - /* Is sampling profiler enabled? */ bool sampling_profiler_p; /* Global variables used to pass fault handler state from signal handler @@ -71,8 +70,12 @@ struct factor_vm cell signal_number; cell signal_fault_addr; unsigned int signal_fpu_status; - bool safepoint_fep; - cell safepoint_sample_count; + volatile bool safepoint_fep; + + /* State kept by the sampling profiler */ + std::vector samples; + volatile cell safepoint_sample_count; + volatile cell safepoint_gc_sample_count; /* GC is off during heap walking */ bool gc_off; @@ -186,6 +189,13 @@ struct factor_vm void set_counting_profiler(bool counting_profiler); void primitive_counting_profiler(); + /* Sampling profiler */ + void record_sample(); + void start_sampling_profiler(); + void end_sampling_profiler(); + void set_sampling_profiler(bool sampling); + void primitive_sampling_profiler(); + // errors void general_error(vm_error_type error, cell arg1, cell arg2); void type_error(cell type, cell tagged); @@ -383,7 +393,7 @@ struct factor_vm void find_data_references_step(cell *scan); void find_data_references(cell look_for_); void dump_code_heap(); - void factorbug_usage(); + void factorbug_usage(bool advanced_p); void factorbug(); void primitive_die(); @@ -705,6 +715,8 @@ struct factor_vm void ffi_dlclose(dll *dll); void c_to_factor_toplevel(cell quot); void init_signals(); + void start_sampling_profiler_timer(); + void end_sampling_profiler_timer(); // os-windows #if defined(WINDOWS)