Set up the state necessary to collect samples. We still need to add GC support for walking the sample set, and the compiler needs to register GC roots before safepoints as well. We also need primitives to expose the data to Factor for reporting.
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 \
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 \
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 \
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 \
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 \
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()
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();
}
}
#include "run.hpp"
#include "objects.hpp"
#include "counting_profiler.hpp"
+#include "sampling_profiler.hpp"
#include "errors.hpp"
#include "bignumint.hpp"
#include "bignum.hpp"
*/
}
+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();
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;
_(context_object) \
_(context_object_for) \
_(current_callback) \
+ _(counting_profiler) \
_(data_room) \
_(datastack) \
_(datastack_for) \
_(modify_code_heap) \
_(nano_count) \
_(optimized_p) \
- _(counting_profiler) \
_(quot_compiled_p) \
_(quotation_code) \
_(reset_dispatch_stats) \
_(resize_string) \
_(retainstack) \
_(retainstack_for) \
+ _(sampling_profiler) \
_(save_image) \
_(save_image_and_exit) \
_(set_context_object) \
--- /dev/null
+#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()));
+}
+
+
+}
--- /dev/null
+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)
+ {
+ }
+};
+
+}
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
+
}
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),
/* 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
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<profiling_sample> samples;
+ volatile cell safepoint_sample_count;
+ volatile cell safepoint_gc_sample_count;
/* GC is off during heap walking */
bool gc_off;
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);
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();
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)