Commit 3a880af4 authored by Stefan Monnier's avatar Stefan Monnier

Merge profiler branch

parents 9180598c 234148bf
......@@ -678,6 +678,11 @@ are deprecated and will be removed eventually.
* Lisp changes in Emacs 24.3
** New sampling-based Elisp profiler.
Try M-x profiler-start ... M-x profiler-stop; and then M-x profiler-report.
The sampling rate can be based on CPU time (only supported on some
systems), or based on memory allocations.
** CL-style generalized variables are now in core Elisp.
`setf' is autoloaded; `push' and `pop' accept generalized variables.
......
2012-09-26 Tomohiro Matsuyama <tomo@cx4a.org>
Stefan Monnier <monnier@iro.umontreal.ca>
* profiler.el: New file.
2012-09-26 Stefan Monnier <monnier@iro.umontreal.ca>
* emacs-lisp/testcover.el (testcover-after): Add gv-expander.
......
This diff is collapsed.
2012-09-26 Tomohiro Matsuyama <tomo@cx4a.org>
Stefan Monnier <monnier@iro.umontreal.ca>
Juanma Barranquero <lekktu@gmail.com>
* profiler.c: New file.
* Makefile.in (base_obj): Add profiler.o.
* makefile.w32-in (OBJ2, GLOBAL_SOURCES): Add profiler.c.
($(BLD)/profiler.$(O)): New target.
* emacs.c (main): Call syms_of_profiler.
* alloc.c (Qautomatic_gc): New constant.
(MALLOC_PROBE): New macro.
(xmalloc, xzalloc, xrealloc, lisp_malloc, lisp_align_malloc): Use it.
(total_bytes_of_live_objects): New function.
(Fgarbage_collect): Use it. Record itself in backtrace_list.
Call malloc_probe for the memory profiler.
(syms_of_alloc): Define Qautomatic_gc.
* eval.c (eval_sub, Ffuncall): Reorder assignments to avoid
race condition.
(struct backtrace): Move definition...
* lisp.h (struct backtrace): ..here.
(Qautomatic_gc, profiler_memory_running): Declare vars.
(malloc_probe, syms_of_profiler): Declare functions.
* xdisp.c (Qautomatic_redisplay): New constant.
(redisplay_internal): Record itself in backtrace_list.
(syms_of_xdisp): Define Qautomatic_redisplay.
2012-09-25 Juanma Barranquero <lekktu@gmail.com>
* makefile.w32-in ($(BLD)/callproc.$(O)): Update dependencies.
......@@ -291,8 +317,8 @@
(reinvoke_input_signal): Remove. All uses replaced by
handle_async_input.
(quit_count): Now volatile, since a signal handler uses it.
(handle_interrupt): Now takes bool IN_SIGNAL_HANDLER as arg. All
callers changed. Block SIGINT only if not already blocked.
(handle_interrupt): Now takes bool IN_SIGNAL_HANDLER as arg.
All callers changed. Block SIGINT only if not already blocked.
Clear sigmask reliably, even if Fsignal returns, which it can.
Omit unnecessary accesses to volatile var.
(quit_throw_to_read_char): No need to restore sigmask.
......@@ -392,8 +418,8 @@
if it is defined. Arguments and return value changed.
(valid_image_p, make_image): Callers changed.
(xbm_type, xpm_type, pbm_type, png_type, jpeg_type, tiff_type)
(gif_type, imagemagick_type, svg_type, gs_type): Add
initialization functions.
(gif_type, imagemagick_type, svg_type, gs_type):
Add initialization functions.
(Finit_image_library): Call lookup_image_type.
(CHECK_LIB_AVAILABLE): Macro deleted.
(lookup_image_type): Call define_image_type here, rather than via
......@@ -415,8 +441,8 @@
* window.c (Fsplit_window_internal): Handle only Qt value of
Vwindow_combination_limit separately.
(Qtemp_buffer_resize): New symbol.
(Vwindow_combination_limit): New default value. Rewrite
doc-string.
(Vwindow_combination_limit): New default value.
Rewrite doc-string.
2012-09-22 Eli Zaretskii <eliz@gnu.org>
......@@ -515,7 +541,7 @@
(Fx_create_frame): Call x_set_offset to correctly interpret
top_pos in geometry.
* frame.c (read_integer, XParseGeometry): Moved from w32xfns.c.
* frame.c (read_integer, XParseGeometry): Move from w32xfns.c.
(Fx_parse_geometry): If there is a space in string, call
Qns_parse_geometry, otherwise do as on other terms (Bug#12368).
......@@ -616,8 +642,8 @@
2012-09-16 Martin Rudalics <rudalics@gmx.at>
* window.c (Fwindow_parameter, Fset_window_parameter): Accept
any window as argument (Bug#12452).
* window.c (Fwindow_parameter, Fset_window_parameter):
Accept any window as argument (Bug#12452).
2012-09-16 Jan Djärv <jan.h.d@swipnet.se>
......@@ -692,8 +718,8 @@
2012-09-14 Dmitry Antipov <dmantipov@yandex.ru>
Avoid out-of-range marker position (Bug#12426).
* insdel.c (replace_range, replace_range_2): Adjust
markers before overlays, as suggested by comments.
* insdel.c (replace_range, replace_range_2):
Adjust markers before overlays, as suggested by comments.
(insert_1_both, insert_from_buffer_1, adjust_after_replace):
Remove redundant check before calling offset_intervals.
......@@ -992,8 +1018,8 @@
in the internal border.
(x_set_window_size): Remove static variables and their usage.
(ns_redraw_scroll_bars): Fix NSTRACE arg.
(ns_after_update_window_line, ns_draw_fringe_bitmap): Remove
fringe/internal border adjustment (Bug#11052).
(ns_after_update_window_line, ns_draw_fringe_bitmap):
Remove fringe/internal border adjustment (Bug#11052).
(ns_draw_fringe_bitmap): Make code more like other terms (xterm.c).
(ns_draw_window_cursor): Remove fringe/internal border adjustment.
(ns_fix_rect_ibw): Remove.
......@@ -1210,8 +1236,8 @@
(init_signals) [FORWARD_SIGNAL_TO_MAIN_THREAD]: Initialize it;
code moved here from emacs.c's main function.
* sysdep.c, syssignal.h (handle_on_main_thread): New function,
replacing the old SIGNAL_THREAD_CHECK. All uses changed. This
lets callers save and restore errno properly.
replacing the old SIGNAL_THREAD_CHECK. All uses changed.
This lets callers save and restore errno properly.
2012-09-05 Dmitry Antipov <dmantipov@yandex.ru>
......@@ -1520,8 +1546,8 @@
* process.c: Include TERM_HEADER instead of listing all possible
window-system headers.
* nsterm.h: Remove declarations now in frame.h. Define
FRAME_X_SCREEN, FRAME_X_VISUAL.
* nsterm.h: Remove declarations now in frame.h.
Define FRAME_X_SCREEN, FRAME_X_VISUAL.
* menu.c: Include TERM_HEADER instead of listing all possible
window-system headers.
......@@ -1717,8 +1743,8 @@
* nsterm.h (NSPanel): New class variable dialog_return.
* nsmenu.m (initWithContentRect:styleMask:backing:defer:): Initialize
dialog_return.
* nsmenu.m (initWithContentRect:styleMask:backing:defer:):
Initialize dialog_return.
(windowShouldClose:): Use stop instead of stopModalWithCode.
(clicked:): Ditto, and also set dialog_return (Bug#12258).
(timeout_handler:): Use stop instead of abortModal. Send a dummy
......
......@@ -339,6 +339,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \
process.o gnutls.o callproc.o \
region-cache.o sound.o atimer.o \
doprnt.o intervals.o textprop.o composite.o xml.o \
profiler.o \
$(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \
$(WINDOW_SYSTEM_OBJ)
obj = $(base_obj) $(NS_OBJC_OBJ)
......
......@@ -205,6 +205,7 @@ static Lisp_Object Qintervals;
static Lisp_Object Qbuffers;
static Lisp_Object Qstring_bytes, Qvector_slots, Qheap;
static Lisp_Object Qgc_cons_threshold;
Lisp_Object Qautomatic_gc;
Lisp_Object Qchar_table_extra_slots;
/* Hook run after GC has finished. */
......@@ -648,6 +649,13 @@ malloc_unblock_input (void)
# define MALLOC_UNBLOCK_INPUT ((void) 0)
#endif
#define MALLOC_PROBE(size) \
do { \
if (profiler_memory_running) \
malloc_probe (size); \
} while (0)
/* Like malloc but check for no memory and block interrupt input.. */
void *
......@@ -661,6 +669,7 @@ xmalloc (size_t size)
if (!val && size)
memory_full (size);
MALLOC_PROBE (size);
return val;
}
......@@ -678,6 +687,7 @@ xzalloc (size_t size)
if (!val && size)
memory_full (size);
memset (val, 0, size);
MALLOC_PROBE (size);
return val;
}
......@@ -699,6 +709,7 @@ xrealloc (void *block, size_t size)
if (!val && size)
memory_full (size);
MALLOC_PROBE (size);
return val;
}
......@@ -888,6 +899,7 @@ lisp_malloc (size_t nbytes, enum mem_type type)
MALLOC_UNBLOCK_INPUT;
if (!val && nbytes)
memory_full (nbytes);
MALLOC_PROBE (nbytes);
return val;
}
......@@ -1093,6 +1105,8 @@ lisp_align_malloc (size_t nbytes, enum mem_type type)
MALLOC_UNBLOCK_INPUT;
MALLOC_PROBE (nbytes);
eassert (0 == ((uintptr_t) val) % BLOCK_ALIGN);
return val;
}
......@@ -5043,6 +5057,23 @@ bounded_number (EMACS_INT number)
return make_number (min (MOST_POSITIVE_FIXNUM, number));
}
/* Calculate total bytes of live objects. */
static size_t
total_bytes_of_live_objects (void)
{
size_t tot = 0;
tot += total_conses * sizeof (struct Lisp_Cons);
tot += total_symbols * sizeof (struct Lisp_Symbol);
tot += total_markers * sizeof (union Lisp_Misc);
tot += total_string_bytes;
tot += total_vector_slots * word_size;
tot += total_floats * sizeof (struct Lisp_Float);
tot += total_intervals * sizeof (struct interval);
tot += total_strings * sizeof (struct Lisp_String);
return tot;
}
DEFUN ("garbage-collect", Fgarbage_collect, Sgarbage_collect, 0, 0, "",
doc: /* Reclaim storage for Lisp objects no longer needed.
Garbage collection happens automatically if you cons more than
......@@ -5068,6 +5099,8 @@ See Info node `(elisp)Garbage Collection'. */)
ptrdiff_t count = SPECPDL_INDEX ();
EMACS_TIME start;
Lisp_Object retval = Qnil;
size_t tot_before = 0;
struct backtrace backtrace;
if (abort_on_gc)
emacs_abort ();
......@@ -5077,6 +5110,14 @@ See Info node `(elisp)Garbage Collection'. */)
if (pure_bytes_used_before_overflow)
return Qnil;
/* Record this function, so it appears on the profiler's backtraces. */
backtrace.next = backtrace_list;
backtrace.function = &Qautomatic_gc;
backtrace.args = &Qautomatic_gc;
backtrace.nargs = 0;
backtrace.debug_on_exit = 0;
backtrace_list = &backtrace;
check_cons_list ();
/* Don't keep undo information around forever.
......@@ -5084,6 +5125,9 @@ See Info node `(elisp)Garbage Collection'. */)
FOR_EACH_BUFFER (nextb)
compact_buffer (nextb);
if (profiler_memory_running)
tot_before = total_bytes_of_live_objects ();
start = current_emacs_time ();
/* In case user calls debug_print during GC,
......@@ -5255,16 +5299,7 @@ See Info node `(elisp)Garbage Collection'. */)
gc_relative_threshold = 0;
if (FLOATP (Vgc_cons_percentage))
{ /* Set gc_cons_combined_threshold. */
double tot = 0;
tot += total_conses * sizeof (struct Lisp_Cons);
tot += total_symbols * sizeof (struct Lisp_Symbol);
tot += total_markers * sizeof (union Lisp_Misc);
tot += total_string_bytes;
tot += total_vector_slots * word_size;
tot += total_floats * sizeof (struct Lisp_Float);
tot += total_intervals * sizeof (struct interval);
tot += total_strings * sizeof (struct Lisp_String);
double tot = total_bytes_of_live_objects ();
tot *= XFLOAT_DATA (Vgc_cons_percentage);
if (0 < tot)
......@@ -5367,6 +5402,17 @@ See Info node `(elisp)Garbage Collection'. */)
gcs_done++;
/* Collect profiling data. */
if (profiler_memory_running)
{
size_t swept = 0;
size_t tot_after = total_bytes_of_live_objects ();
if (tot_before > tot_after)
swept = tot_before - tot_after;
malloc_probe (swept);
}
backtrace_list = backtrace.next;
return retval;
}
......@@ -6527,6 +6573,7 @@ do hash-consing of the objects allocated to pure space. */);
DEFSYM (Qstring_bytes, "string-bytes");
DEFSYM (Qvector_slots, "vector-slots");
DEFSYM (Qheap, "heap");
DEFSYM (Qautomatic_gc, "Automatic GC");
DEFSYM (Qgc_cons_threshold, "gc-cons-threshold");
DEFSYM (Qchar_table_extra_slots, "char-table-extra-slots");
......
......@@ -1419,6 +1419,8 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
syms_of_ntterm ();
#endif /* WINDOWSNT */
syms_of_profiler ();
keys_of_casefiddle ();
keys_of_cmds ();
keys_of_buffer ();
......
......@@ -31,17 +31,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include "xterm.h"
#endif
struct backtrace
{
struct backtrace *next;
Lisp_Object *function;
Lisp_Object *args; /* Points to vector of args. */
ptrdiff_t nargs; /* Length of vector. */
/* Nonzero means call value of debugger when done with this operation. */
unsigned int debug_on_exit : 1;
};
static struct backtrace *backtrace_list;
struct backtrace *backtrace_list;
#if !BYTE_MARK_STACK
static
......@@ -2055,11 +2045,11 @@ eval_sub (Lisp_Object form)
original_args = XCDR (form);
backtrace.next = backtrace_list;
backtrace_list = &backtrace;
backtrace.function = &original_fun; /* This also protects them from gc. */
backtrace.args = &original_args;
backtrace.nargs = UNEVALLED;
backtrace.debug_on_exit = 0;
backtrace_list = &backtrace;
if (debug_on_next_call)
do_debug_on_call (Qt);
......@@ -2730,11 +2720,11 @@ usage: (funcall FUNCTION &rest ARGUMENTS) */)
}
backtrace.next = backtrace_list;
backtrace_list = &backtrace;
backtrace.function = &args[0];
backtrace.args = &args[1]; /* This also GCPROs them. */
backtrace.nargs = nargs - 1;
backtrace.debug_on_exit = 0;
backtrace_list = &backtrace;
/* Call GC after setting up the backtrace, so the latter GCPROs the args. */
maybe_gc ();
......
......@@ -2031,6 +2031,18 @@ extern ptrdiff_t specpdl_size;
#define SPECPDL_INDEX() (specpdl_ptr - specpdl)
struct backtrace
{
struct backtrace *next;
Lisp_Object *function;
Lisp_Object *args; /* Points to vector of args. */
ptrdiff_t nargs; /* Length of vector. */
/* Nonzero means call value of debugger when done with this operation. */
unsigned int debug_on_exit : 1;
};
extern struct backtrace *backtrace_list;
/* Everything needed to describe an active condition case.
Members are volatile if their values need to survive _longjmp when
......@@ -2916,6 +2928,7 @@ build_string (const char *str)
extern Lisp_Object pure_cons (Lisp_Object, Lisp_Object);
extern void make_byte_code (struct Lisp_Vector *);
extern Lisp_Object Qautomatic_gc;
extern Lisp_Object Qchar_table_extra_slots;
extern struct Lisp_Vector *allocate_vector (EMACS_INT);
extern struct Lisp_Vector *allocate_pseudovector (int memlen, int lisplen, int tag);
......@@ -3534,6 +3547,13 @@ extern int have_menus_p (void);
void syms_of_dbusbind (void);
#endif
/* Defined in profiler.c. */
extern bool profiler_memory_running;
extern void malloc_probe (size_t);
extern void syms_of_profiler (void);
#ifdef DOS_NT
/* Defined in msdos.c, w32.c. */
extern char *emacs_root_dir (void);
......
......@@ -125,6 +125,7 @@ OBJ2 = $(BLD)/sysdep.$(O) \
$(BLD)/terminal.$(O) \
$(BLD)/menu.$(O) \
$(BLD)/xml.$(O) \
$(BLD)/profiler.$(O) \
$(BLD)/w32term.$(O) \
$(BLD)/w32xfns.$(O) \
$(BLD)/w32fns.$(O) \
......@@ -222,7 +223,7 @@ GLOBAL_SOURCES = dosfns.c msdos.c \
process.c callproc.c unexw32.c \
region-cache.c sound.c atimer.c \
doprnt.c intervals.c textprop.c composite.c \
gnutls.c xml.c
gnutls.c xml.c profiler.c
SOME_MACHINE_OBJECTS = dosfns.o msdos.o \
xterm.o xfns.o xmenu.o xselect.o xrdb.o xsmfns.o dbusbind.o
obj = $(GLOBAL_SOURCES:.c=.o)
......@@ -973,6 +974,11 @@ $(BLD)/xml.$(O) : \
$(CONFIG_H) \
$(LISP_H)
$(BLD)/profiler.$(O) : \
$(SRC)/profiler.c \
$(CONFIG_H) \
$(LISP_H)
$(BLD)/image.$(O) : \
$(SRC)/image.c \
$(SRC)/blockinput.h \
......
/* Profiler implementation.
Copyright (C) 2012 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <stdio.h>
#include <limits.h>
#include <sys/time.h>
#include <signal.h>
#include <setjmp.h>
#include "lisp.h"
/* Logs. */
typedef struct Lisp_Hash_Table log_t;
static Lisp_Object
make_log (int heap_size, int max_stack_depth)
{
/* We use a standard Elisp hash-table object, but we use it in
a special way. This is OK as long as the object is not exposed
to Elisp, i.e. until it is returned by *-profiler-log, after which
it can't be used any more. */
Lisp_Object log = make_hash_table (Qequal, make_number (heap_size),
make_float (DEFAULT_REHASH_SIZE),
make_float (DEFAULT_REHASH_THRESHOLD),
Qnil, Qnil, Qnil);
struct Lisp_Hash_Table *h = XHASH_TABLE (log);
/* What is special about our hash-tables is that the keys are pre-filled
with the vectors we'll put in them. */
int i = ASIZE (h->key_and_value) / 2;
while (0 < i)
set_hash_key_slot (h, --i,
Fmake_vector (make_number (max_stack_depth), Qnil));
return log;
}
/* Evict the least used half of the hash_table.
When the table is full, we have to evict someone.
The easiest and most efficient is to evict the value we're about to add
(i.e. once the table is full, stop sampling).
We could also pick the element with the lowest count and evict it,
but finding it is O(N) and for that amount of work we get very
little in return: for the next sample, this latest sample will have
count==1 and will hence be a prime candidate for eviction :-(
So instead, we take O(N) time to eliminate more or less half of the
entries (the half with the lowest counts). So we get an amortized
cost of O(1) and we get O(N) time for a new entry to grow larger
than the other least counts before a new round of eviction. */
static EMACS_INT approximate_median (log_t *log,
ptrdiff_t start, ptrdiff_t size)
{
eassert (size > 0);
if (size < 2)
return XINT (HASH_VALUE (log, start));
if (size < 3)
/* Not an actual median, but better for our application than
choosing either of the two numbers. */
return ((XINT (HASH_VALUE (log, start))
+ XINT (HASH_VALUE (log, start + 1)))
/ 2);
else
{
ptrdiff_t newsize = size / 3;
ptrdiff_t start2 = start + newsize;
EMACS_INT i1 = approximate_median (log, start, newsize);
EMACS_INT i2 = approximate_median (log, start2, newsize);
EMACS_INT i3 = approximate_median (log, start2 + newsize,
size - 2 * newsize);
return (i1 < i2
? (i2 < i3 ? i2 : (i1 < i3 ? i3 : i1))
: (i1 < i3 ? i1 : (i2 < i3 ? i3 : i2)));
}
}
static void evict_lower_half (log_t *log)
{
ptrdiff_t size = ASIZE (log->key_and_value) / 2;
EMACS_INT median = approximate_median (log, 0, size);
ptrdiff_t i;
for (i = 0; i < size; i++)
/* Evict not only values smaller but also values equal to the median,
so as to make sure we evict something no matter what. */
if (XINT (HASH_VALUE (log, i)) <= median)
{
Lisp_Object key = HASH_KEY (log, i);
{ /* FIXME: we could make this more efficient. */
Lisp_Object tmp;
XSET_HASH_TABLE (tmp, log); /* FIXME: Use make_lisp_ptr. */
Fremhash (key, tmp);
}
eassert (EQ (log->next_free, make_number (i)));
{
int j;
eassert (VECTORP (key));
for (j = 0; j < ASIZE (key); j++)
ASET (key, j, Qnil);
}
set_hash_key_slot (log, i, key);
}
}
/* Record the current backtrace in LOG. BASE is a special name for
describing which the backtrace come from. BASE can be nil. COUNT is
a number how many times the profiler sees the backtrace at the
time. ELAPSED is a elapsed time in millisecond that the backtrace
took. */
static void
record_backtrace (log_t *log, size_t count)
{
struct backtrace *backlist = backtrace_list;
Lisp_Object backtrace;
ptrdiff_t index, i = 0;
ptrdiff_t asize;
if (!INTEGERP (log->next_free))
/* FIXME: transfer the evicted counts to a special entry rather
than dropping them on the floor. */
evict_lower_half (log);
index = XINT (log->next_free);
/* Get a "working memory" vector. */
backtrace = HASH_KEY (log, index);
asize = ASIZE (backtrace);
/* Copy the backtrace contents into working memory. */
for (; i < asize && backlist; i++, backlist = backlist->next)
/* FIXME: For closures we should ignore the environment. */
ASET (backtrace, i, *backlist->function);
/* Make sure that unused space of working memory is filled with nil. */
for (; i < asize; i++)
ASET (backtrace, i, Qnil);
{ /* We basically do a `gethash+puthash' here, except that we have to be
careful to avoid memory allocation since we're in a signal
handler, and we optimize the code to try and avoid computing the
hash+lookup twice. See fns.c:Fputhash for reference. */
EMACS_UINT hash;
ptrdiff_t j = hash_lookup (log, backtrace, &hash);
if (j >= 0)
set_hash_value_slot (log, j,
make_number (count + XINT (HASH_VALUE (log, j))));
else
{ /* BEWARE! hash_put in general can allocate memory.
But currently it only does that if log->next_free is nil. */
int j;
eassert (!NILP (log->next_free));
j = hash_put (log, backtrace, make_number (count), hash);
/* Let's make sure we've put `backtrace' right where it
already was to start with. */
eassert (index == j);
/* FIXME: If the hash-table is almost full, we should set
some global flag so that some Elisp code can offload its
data elsewhere, so as to avoid the eviction code.
There are 2 ways to do that, AFAICT:
- Set a flag checked in QUIT, such that QUIT can then call
Fprofiler_cpu_log and stash the full log for later use.
- Set a flag check in post-gc-hook, so that Elisp code can call
profiler-cpu-log. That gives us more flexibility since that
Elisp code can then do all kinds of fun stuff like write
the log to disk. Or turn it right away into a call tree.
Of course, using Elisp is generally preferable, but it may
take longer until we get a chance to run the Elisp code, so
there's more risk that the table will get full before we
get there. */
}
}
}
/* Sample profiler. */
/* FIXME: Add support for the CPU profiler in W32. */
/* FIXME: the sigprof_handler suffers from race-conditions if the signal
is delivered to a thread other than the main Emacs thread. */
#if defined SIGPROF && defined HAVE_SETITIMER
#define PROFILER_CPU_SUPPORT
/* True if sampling profiler is running. */
static bool profiler_cpu_running;
static Lisp_Object cpu_log;
/* Separate counter for the time spent in the GC. */
static EMACS_INT cpu_gc_count;
/* The current sample interval in millisecond. */
static int current_sample_interval;
/* Signal handler for sample profiler. */