diff options
author | Robin Templeton <[email protected]> | 2014-06-25 03:10:37 -0400 |
---|---|---|
committer | Robin Templeton <[email protected]> | 2015-04-20 00:29:01 -0400 |
commit | 943a56c7b3c7ccc192adb371e9d47bc568d15576 (patch) | |
tree | 606fd9f64a5e7ccef49488a483226344a1ea6788 /src | |
parent | 01d383fe6a1d8b505b7c37044fe9ebfa3c06b14f (diff) |
* src/profiler.c: Delete.
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.in | 3 | ||||
-rw-r--r-- | src/emacs.c | 2 | ||||
-rw-r--r-- | src/profiler.c | 590 |
3 files changed, 1 insertions, 594 deletions
diff --git a/src/Makefile.in b/src/Makefile.in index ef3c725c33..9675ac4f7a 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -365,7 +365,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 $(NOTIFY_OBJ) \ - profiler.o decompress.o \ + decompress.o \ guile.o \ $(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \ $(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) @@ -699,7 +699,6 @@ nsmenu.o: nsmenu.x nsselect.o: nsselect.x print.o: print.x process.o: process.x -profiler.o: profiler.x search.o: search.x sound.o: sound.x syntax.o: syntax.x diff --git a/src/emacs.c b/src/emacs.c index 89cdf27f78..22381eb77e 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1576,8 +1576,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem #endif /* HAVE_W32NOTIFY */ #endif /* WINDOWSNT */ - syms_of_profiler (); - keys_of_casefiddle (); keys_of_cmds (); keys_of_buffer (); diff --git a/src/profiler.c b/src/profiler.c deleted file mode 100644 index f1b62450d9..0000000000 --- a/src/profiler.c +++ /dev/null @@ -1,590 +0,0 @@ -/* Profiler implementation. - -Copyright (C) 2012-2014 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 "lisp.h" -#include "syssignal.h" -#include "systime.h" - -/* Return A + B, but return the maximum fixnum if the result would overflow. - Assume A and B are nonnegative and in fixnum range. */ - -static EMACS_INT -saturated_add (EMACS_INT a, EMACS_INT b) -{ - return min (a + b, MOST_POSITIVE_FIXNUM); -} - -/* Logs. */ - -typedef struct Lisp_Hash_Table log_t; - -static Lisp_Object Qautomatic_gc; -static Lisp_Object Qprofiler_backtrace_equal; -static struct hash_table_test hashtest_profiler; - -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 (hashtest_profiler, - make_number (heap_size), - make_float (DEFAULT_REHASH_SIZE), - make_float (DEFAULT_REHASH_THRESHOLD), - 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 (i > 0) - 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. COUNT is the weight of this - current backtrace: interrupt counts for CPU, and the allocation - size for memory. */ - -static void -record_backtrace (log_t *log, EMACS_INT count) -{ - Lisp_Object backtrace; - ptrdiff_t index; - - 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); - get_backtrace (backtrace); - - { /* 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) - { - EMACS_INT old_val = XINT (HASH_VALUE (log, j)); - EMACS_INT new_val = saturated_add (old_val, count); - set_hash_value_slot (log, j, make_number (new_val)); - } - 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. */ - } - } -} - -/* Sampling profiler. */ - -#ifdef PROFILER_CPU_SUPPORT - -/* The profiler timer and whether it was properly initialized, if - POSIX timers are available. */ -#ifdef HAVE_ITIMERSPEC -static timer_t profiler_timer; -static bool profiler_timer_ok; -#endif - -/* Status of sampling profiler. */ -static enum profiler_cpu_running - { NOT_RUNNING, TIMER_SETTIME_RUNNING, SETITIMER_RUNNING } - profiler_cpu_running; - -/* Hash-table log of CPU profiler. */ -static Lisp_Object cpu_log; - -/* Separate counter for the time spent in the GC. */ -static EMACS_INT cpu_gc_count; - -/* The current sampling interval in nanoseconds. */ -static EMACS_INT current_sampling_interval; - -/* Signal handler for sampling profiler. */ - -static void -handle_profiler_signal (int signal) -{ - if (EQ (backtrace_top_function (), Qautomatic_gc)) - /* Special case the time-count inside GC because the hash-table - code is not prepared to be used while the GC is running. - More specifically it uses ASIZE at many places where it does - not expect the ARRAY_MARK_FLAG to be set. We could try and - harden the hash-table code, but it doesn't seem worth the - effort. */ - cpu_gc_count = saturated_add (cpu_gc_count, 1); - else - { - EMACS_INT count = 1; -#ifdef HAVE_ITIMERSPEC - if (profiler_timer_ok) - { - int overruns = timer_getoverrun (profiler_timer); - eassert (overruns >= 0); - count += overruns; - } -#endif - eassert (HASH_TABLE_P (cpu_log)); - record_backtrace (XHASH_TABLE (cpu_log), count); - } -} - -static void -deliver_profiler_signal (int signal) -{ - deliver_process_signal (signal, handle_profiler_signal); -} - -static enum profiler_cpu_running -setup_cpu_timer (Lisp_Object sampling_interval) -{ - struct sigaction action; - struct itimerval timer; - struct timespec interval; - int billion = 1000000000; - - if (! RANGED_INTEGERP (1, sampling_interval, - (TYPE_MAXIMUM (time_t) < EMACS_INT_MAX / billion - ? ((EMACS_INT) TYPE_MAXIMUM (time_t) * billion - + (billion - 1)) - : EMACS_INT_MAX))) - return NOT_RUNNING; - - current_sampling_interval = XINT (sampling_interval); - interval = make_timespec (current_sampling_interval / billion, - current_sampling_interval % billion); - emacs_sigaction_init (&action, deliver_profiler_signal); - sigaction (SIGPROF, &action, 0); - -#ifdef HAVE_ITIMERSPEC - if (! profiler_timer_ok) - { - /* System clocks to try, in decreasing order of desirability. */ - static clockid_t const system_clock[] = { -#ifdef CLOCK_THREAD_CPUTIME_ID - CLOCK_THREAD_CPUTIME_ID, -#endif -#ifdef CLOCK_PROCESS_CPUTIME_ID - CLOCK_PROCESS_CPUTIME_ID, -#endif -#ifdef CLOCK_MONOTONIC - CLOCK_MONOTONIC, -#endif - CLOCK_REALTIME - }; - int i; - struct sigevent sigev; - sigev.sigev_value.sival_ptr = &profiler_timer; - sigev.sigev_signo = SIGPROF; - sigev.sigev_notify = SIGEV_SIGNAL; - - for (i = 0; i < ARRAYELTS (system_clock); i++) - if (timer_create (system_clock[i], &sigev, &profiler_timer) == 0) - { - profiler_timer_ok = 1; - break; - } - } - - if (profiler_timer_ok) - { - struct itimerspec ispec; - ispec.it_value = ispec.it_interval = interval; - if (timer_settime (profiler_timer, 0, &ispec, 0) == 0) - return TIMER_SETTIME_RUNNING; - } -#endif - -#ifdef HAVE_SETITIMER - timer.it_value = timer.it_interval = make_timeval (interval); - if (setitimer (ITIMER_PROF, &timer, 0) == 0) - return SETITIMER_RUNNING; -#endif - - return NOT_RUNNING; -} - -DEFUN ("profiler-cpu-start", Fprofiler_cpu_start, Sprofiler_cpu_start, - 1, 1, 0, - doc: /* Start or restart the cpu profiler. -It takes call-stack samples each SAMPLING-INTERVAL nanoseconds, approximately. -See also `profiler-log-size' and `profiler-max-stack-depth'. */) - (Lisp_Object sampling_interval) -{ - if (profiler_cpu_running) - error ("CPU profiler is already running"); - - if (NILP (cpu_log)) - { - cpu_gc_count = 0; - cpu_log = make_log (profiler_log_size, - profiler_max_stack_depth); - } - - profiler_cpu_running = setup_cpu_timer (sampling_interval); - if (! profiler_cpu_running) - error ("Invalid sampling interval"); - - return Qt; -} - -DEFUN ("profiler-cpu-stop", Fprofiler_cpu_stop, Sprofiler_cpu_stop, - 0, 0, 0, - doc: /* Stop the cpu profiler. The profiler log is not affected. -Return non-nil if the profiler was running. */) - (void) -{ - switch (profiler_cpu_running) - { - case NOT_RUNNING: - return Qnil; - -#ifdef HAVE_ITIMERSPEC - case TIMER_SETTIME_RUNNING: - { - struct itimerspec disable; - memset (&disable, 0, sizeof disable); - timer_settime (profiler_timer, 0, &disable, 0); - } - break; -#endif - -#ifdef HAVE_SETITIMER - case SETITIMER_RUNNING: - { - struct itimerval disable; - memset (&disable, 0, sizeof disable); - setitimer (ITIMER_PROF, &disable, 0); - } - break; -#endif - } - - signal (SIGPROF, SIG_IGN); - profiler_cpu_running = NOT_RUNNING; - return Qt; -} - -DEFUN ("profiler-cpu-running-p", - Fprofiler_cpu_running_p, Sprofiler_cpu_running_p, - 0, 0, 0, - doc: /* Return non-nil if cpu profiler is running. */) - (void) -{ - return profiler_cpu_running ? Qt : Qnil; -} - -DEFUN ("profiler-cpu-log", Fprofiler_cpu_log, Sprofiler_cpu_log, - 0, 0, 0, - doc: /* Return the current cpu profiler log. -The log is a hash-table mapping backtraces to counters which represent -the amount of time spent at those points. Every backtrace is a vector -of functions, where the last few elements may be nil. -Before returning, a new log is allocated for future samples. */) - (void) -{ - Lisp_Object result = cpu_log; - /* Here we're making the log visible to Elisp, so it's not safe any - more for our use afterwards since we can't rely on its special - pre-allocated keys anymore. So we have to allocate a new one. */ - cpu_log = (profiler_cpu_running - ? make_log (profiler_log_size, profiler_max_stack_depth) - : Qnil); - Fputhash (Fmake_vector (make_number (1), Qautomatic_gc), - make_number (cpu_gc_count), - result); - cpu_gc_count = 0; - return result; -} -#endif /* PROFILER_CPU_SUPPORT */ - -/* Memory profiler. */ - -/* True if memory profiler is running. */ -bool profiler_memory_running; - -static Lisp_Object memory_log; - -DEFUN ("profiler-memory-start", Fprofiler_memory_start, Sprofiler_memory_start, - 0, 0, 0, - doc: /* Start/restart the memory profiler. -The memory profiler will take samples of the call-stack whenever a new -allocation takes place. Note that most small allocations only trigger -the profiler occasionally. -See also `profiler-log-size' and `profiler-max-stack-depth'. */) - (void) -{ - if (profiler_memory_running) - error ("Memory profiler is already running"); - - if (NILP (memory_log)) - memory_log = make_log (profiler_log_size, - profiler_max_stack_depth); - - profiler_memory_running = true; - - return Qt; -} - -DEFUN ("profiler-memory-stop", - Fprofiler_memory_stop, Sprofiler_memory_stop, - 0, 0, 0, - doc: /* Stop the memory profiler. The profiler log is not affected. -Return non-nil if the profiler was running. */) - (void) -{ - if (!profiler_memory_running) - return Qnil; - profiler_memory_running = false; - return Qt; -} - -DEFUN ("profiler-memory-running-p", - Fprofiler_memory_running_p, Sprofiler_memory_running_p, - 0, 0, 0, - doc: /* Return non-nil if memory profiler is running. */) - (void) -{ - return profiler_memory_running ? Qt : Qnil; -} - -DEFUN ("profiler-memory-log", - Fprofiler_memory_log, Sprofiler_memory_log, - 0, 0, 0, - doc: /* Return the current memory profiler log. -The log is a hash-table mapping backtraces to counters which represent -the amount of memory allocated at those points. Every backtrace is a vector -of functions, where the last few elements may be nil. -Before returning, a new log is allocated for future samples. */) - (void) -{ - Lisp_Object result = memory_log; - /* Here we're making the log visible to Elisp , so it's not safe any - more for our use afterwards since we can't rely on its special - pre-allocated keys anymore. So we have to allocate a new one. */ - memory_log = (profiler_memory_running - ? make_log (profiler_log_size, profiler_max_stack_depth) - : Qnil); - return result; -} - - -/* Signals and probes. */ - -/* Record that the current backtrace allocated SIZE bytes. */ -void -malloc_probe (size_t size) -{ - eassert (HASH_TABLE_P (memory_log)); - record_backtrace (XHASH_TABLE (memory_log), min (size, MOST_POSITIVE_FIXNUM)); -} - -DEFUN ("function-equal", Ffunction_equal, Sfunction_equal, 2, 2, 0, - doc: /* Return non-nil if F1 and F2 come from the same source. -Used to determine if different closures are just different instances of -the same lambda expression, or are really unrelated function. */) - (Lisp_Object f1, Lisp_Object f2) -{ - bool res; - if (EQ (f1, f2)) - res = true; - else if (COMPILEDP (f1) && COMPILEDP (f2)) - res = EQ (AREF (f1, COMPILED_BYTECODE), AREF (f2, COMPILED_BYTECODE)); - else if (CONSP (f1) && CONSP (f2) && CONSP (XCDR (f1)) && CONSP (XCDR (f2)) - && EQ (Qclosure, XCAR (f1)) - && EQ (Qclosure, XCAR (f2))) - res = EQ (XCDR (XCDR (f1)), XCDR (XCDR (f2))); - else - res = false; - return res ? Qt : Qnil; -} - -static bool -cmpfn_profiler (struct hash_table_test *t, - Lisp_Object bt1, Lisp_Object bt2) -{ - if (VECTORP (bt1) && VECTORP (bt2)) - { - ptrdiff_t i, l = ASIZE (bt1); - if (l != ASIZE (bt2)) - return false; - for (i = 0; i < l; i++) - if (NILP (Ffunction_equal (AREF (bt1, i), AREF (bt2, i)))) - return false; - return true; - } - else - return EQ (bt1, bt2); -} - -static EMACS_UINT -hashfn_profiler (struct hash_table_test *ht, Lisp_Object bt) -{ - if (VECTORP (bt)) - { - EMACS_UINT hash = 0; - ptrdiff_t i, l = ASIZE (bt); - for (i = 0; i < l; i++) - { - Lisp_Object f = AREF (bt, i); - EMACS_UINT hash1 - = (COMPILEDP (f) ? XHASH (AREF (f, COMPILED_BYTECODE)) - : (CONSP (f) && CONSP (XCDR (f)) && EQ (Qclosure, XCAR (f))) - ? XHASH (XCDR (XCDR (f))) : XHASH (f)); - hash = sxhash_combine (hash, hash1); - } - return SXHASH_REDUCE (hash); - } - else - return XHASH (bt); -} - -void -syms_of_profiler (void) -{ -#include "profiler.x" - - DEFVAR_INT ("profiler-max-stack-depth", profiler_max_stack_depth, - doc: /* Number of elements from the call-stack recorded in the log. */); - profiler_max_stack_depth = 16; - DEFVAR_INT ("profiler-log-size", profiler_log_size, - doc: /* Number of distinct call-stacks that can be recorded in a profiler log. -If the log gets full, some of the least-seen call-stacks will be evicted -to make room for new entries. */); - profiler_log_size = 10000; - - DEFSYM (Qautomatic_gc, "Automatic GC"); - DEFSYM (Qprofiler_backtrace_equal, "profiler-backtrace-equal"); - - hashtest_profiler.name = Qprofiler_backtrace_equal; - hashtest_profiler.user_hash_function = Qnil; - hashtest_profiler.user_cmp_function = Qnil; - hashtest_profiler.cmpfn = cmpfn_profiler; - hashtest_profiler.hashfn = hashfn_profiler; - -#ifdef PROFILER_CPU_SUPPORT - profiler_cpu_running = NOT_RUNNING; - cpu_log = Qnil; - staticpro (&cpu_log); -#endif - profiler_memory_running = false; - memory_log = Qnil; - staticpro (&memory_log); -} |