From 9f9772c99a2feecd0afc539481ae127090b62bd8 Mon Sep 17 00:00:00 2001 From: Peter Bex Date: Sat, 2 Jan 2016 17:20:58 +0100 Subject: [PATCH 2/3] Support profiling on Windows with native timers In MingW there's no setitimer support, so we'll have to use a native Windows API. This API unfortunately only supports millisecond precision. The CreateWaitableTimer API seems like it supports better precision, but it requires that you wait for it using WaitFor{Single,Multiple}Object. --- runtime.c | 100 ++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 59 insertions(+), 41 deletions(-) diff --git a/runtime.c b/runtime.c index 6286ec8..8e171cf 100644 --- a/runtime.c +++ b/runtime.c @@ -63,15 +63,6 @@ # define EOVERFLOW 0 #endif -/* ITIMER_PROF is more precise, but Cygwin doesn't support it... */ -#ifdef __CYGWIN__ -# define C_PROFILE_SIGNAL SIGALRM -# define C_PROFILE_TIMER ITIMER_REAL -#else -# define C_PROFILE_SIGNAL SIGPROF -# define C_PROFILE_TIMER ITIMER_PROF -#endif - /* TODO: Include sys/select.h? Windows doesn't seem to have it... */ #ifndef NO_POSIX_POLL # include @@ -83,8 +74,19 @@ # include # include +/* ITIMER_PROF is more precise, but Cygwin doesn't support it... */ +# ifdef __CYGWIN__ +# define C_PROFILE_SIGNAL SIGALRM +# define C_PROFILE_TIMER ITIMER_REAL +# else +# define C_PROFILE_SIGNAL SIGPROF +# define C_PROFILE_TIMER ITIMER_PROF +# endif + #else +# define C_PROFILE_SIGNAL -1 /* Stupid way to avoid error */ + #ifdef ECOS #include static C_TLS int timezone; @@ -571,6 +573,7 @@ static void C_fcall really_remark(C_word *x) C_regparm; static C_word C_fcall intern0(C_char *name) C_regparm; static void C_fcall update_locative_table(int mode) C_regparm; static LF_LIST *find_module_handle(C_char *name); +static void set_profile_timer(C_uword freq); static void take_profile_sample(); static C_cpsproc(call_cc_wrapper) C_noret; @@ -863,10 +866,12 @@ int CHICKEN_initialize(int heap, int stack, int symbols, void *toplevel) (void)C_randomize(C_fix(time(NULL))); if (profiling) { -#ifdef HAVE_SIGACTION +#ifndef C_NONUNIX +# ifdef HAVE_SIGACTION C_sigaction(C_PROFILE_SIGNAL, &sa, NULL); -#else +# else C_signal(C_PROFILE_SIGNAL, global_signal_handler); +# endif #endif profile_table = (PROFILE_BUCKET **)C_malloc(PROFILE_TABLE_SIZE * sizeof(PROFILE_BUCKET *)); @@ -1553,17 +1558,7 @@ C_word CHICKEN_run(void *toplevel) chicken_is_running = chicken_ran_once = 1; return_to_host = 0; - if(profiling) { - struct itimerval itv; - - itv.it_value.tv_sec = profile_frequency / 1000000; - itv.it_value.tv_usec = profile_frequency % 1000000; - itv.it_interval.tv_sec = itv.it_value.tv_sec; - itv.it_interval.tv_usec = itv.it_value.tv_usec; - - if (setitimer(C_PROFILE_TIMER, &itv, NULL) == -1) - panic(C_text("error setting timer for profiling")); - } + if(profiling) set_profile_timer(profile_frequency); #if C_STACK_GROWS_DOWNWARD C_stack_limit = (C_word *)((C_byte *)C_stack_pointer - stack_size); @@ -1594,17 +1589,7 @@ C_word CHICKEN_run(void *toplevel) ((C_proc)C_restart_trampoline)(C_restart_c, p); } - if(profiling) { - struct itimerval itv; - - itv.it_value.tv_sec = 0; - itv.it_value.tv_usec = 0; - itv.it_interval.tv_sec = itv.it_value.tv_sec; - itv.it_interval.tv_usec = itv.it_value.tv_usec; - - if (setitimer(C_PROFILE_TIMER, &itv, NULL) == -1) - panic(C_text("error clearing timer for profiling")); - } + if(profiling) set_profile_timer(0); chicken_is_running = 0; return C_restore; @@ -4285,6 +4270,46 @@ C_regparm void *C_fcall C_retrieve2_symbol_proc(C_word val, char *name) return C_fast_retrieve_proc(val); } +#ifdef C_NONUNIX +VOID CALLBACK win_timer(PVOID data_ignored, BOOLEAN wait_or_fired) +{ + if (profiling) take_profile_sample(); +} +#endif + +static void set_profile_timer(C_uword freq) +{ +#ifdef C_NONUNIX + static HANDLE timer = NULL; + + if (freq == 0) { + assert(timer != NULL); + if (!DeleteTimerQueueTimer(NULL, timer, NULL)) goto error; + timer = NULL; + } else if (freq < 1000) { + panic(C_text("On Windows, sampling can only be done in milliseconds")); + } else { + if (!CreateTimerQueueTimer(&timer, NULL, win_timer, NULL, 0, freq/1000, 0)) + goto error; + } +#else + struct itimerval itv; + + itv.it_value.tv_sec = freq / 1000000; + itv.it_value.tv_usec = freq % 1000000; + itv.it_interval.tv_sec = itv.it_value.tv_sec; + itv.it_interval.tv_usec = itv.it_value.tv_usec; + + if (setitimer(C_PROFILE_TIMER, &itv, NULL) == -1) goto error; +#endif + + return; + +error: + if (freq == 0) panic(C_text("error clearing timer for profiling")); + else panic(C_text("error setting timer for profiling")); +} + /* Bump profile count for current top of trace buffer */ static void take_profile_sample() { @@ -13186,18 +13211,11 @@ C_word C_i_dump_statistical_profile() C_char *k1, *k2 = NULL; int n; double ms; - struct itimerval itv; assert(profiling); assert(profile_table != NULL); - itv.it_value.tv_sec = 0; - itv.it_value.tv_usec = 0; - itv.it_interval.tv_sec = itv.it_value.tv_sec; - itv.it_interval.tv_usec = itv.it_value.tv_usec; - - if (setitimer(C_PROFILE_TIMER, &itv, NULL) == -1) - panic(C_text("error clearing timer for profiling")); + set_profile_timer(0); profiling = 0; /* In case a SIGPROF is delivered late */ bp = profile_table; -- 2.1.4