aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2012-09-29 23:19:33 -0700
committerPaul Eggert2012-09-29 23:19:33 -0700
commitd89460ed93e67852a95a597a7a1a7983e8828fb9 (patch)
tree349021cb4c7dd7546ae3e354bee1ab2d0b4b48a5 /src
parent65788e2e93a48015493514b7e8b712700d31a7a2 (diff)
downloademacs-d89460ed93e67852a95a597a7a1a7983e8828fb9.tar.gz
emacs-d89460ed93e67852a95a597a7a1a7983e8828fb9.zip
Profiler improvements: more-accurate timers, overflow checks.
* profiler.c: Don't include stdio.h, limits.h, sys/time.h, signal.h, setjmp.h. Include systime.h instead. (saturated_add): New function. (record_backtrace, current_sample_interval): Use EMACS_INT, not size_t. (record_backtrace, handle_profiler_signal): Saturate on fixnum overflow. (profiler_timer, profiler_timer_ok) [HAVE_TIMER_SETTIME]: New static vars. (enum profiler_cpu_running): New enumn. (profiler_cpu_running): Now of that enum type, not bool. All uses changed to store the new value. (handle_profiler_signal): Rename from sigprof_handler_1, for consistency with other handlers. Do not check whether cpu_log is a hash-table if garbage collecting, since it doesn't matter in that case. (deliver_profiler_signal): Rename from sigprof_handler, for consistency with other handlers. (setup_cpu_timer): New function, with much of what used to be in Fprofiler_cpu_start. Check for out-of-range argument. Prefer timer_settime if available, and prefer thread cputime clocks, then process cputime clocks, then monotonic clocks, to the old realtime clock. Use make_timeval to round more-correctly when falling back to setitimer. (Fprofiler_cpu_start): Use it. (Fprofiler_cpu_stop): Prefer timer_settime if available. Don't assume that passing NULL as the 2nd argument of setitimer is the same as passing a pointer to all-zero storage. Ignore SIGPROF afterwards. (malloc_probe): Saturate at MOST_POSITIVE_FIXNUM. * sysdep.c (emacs_sigaction_init): Also mask out SIGPROF in non-fatal signal handlers. Ignore SIGPROF on startup. * syssignal.h (PROFILER_CPU_SUPPORT): Define this macro here, not in profiler.c, since sysdep.c now uses it.
Diffstat (limited to 'src')
-rw-r--r--src/ChangeLog34
-rw-r--r--src/profiler.c189
-rw-r--r--src/sysdep.c5
-rw-r--r--src/syssignal.h4
4 files changed, 178 insertions, 54 deletions
diff --git a/src/ChangeLog b/src/ChangeLog
index fd4319eb4b0..97f955e7136 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,5 +1,39 @@
12012-09-30 Paul Eggert <eggert@cs.ucla.edu> 12012-09-30 Paul Eggert <eggert@cs.ucla.edu>
2 2
3 Profiler improvements: more-accurate timers, overflow checks.
4 * profiler.c: Don't include stdio.h, limits.h, sys/time.h,
5 signal.h, setjmp.h. Include systime.h instead.
6 (saturated_add): New function.
7 (record_backtrace, current_sample_interval): Use EMACS_INT, not size_t.
8 (record_backtrace, handle_profiler_signal): Saturate on fixnum overflow.
9 (profiler_timer, profiler_timer_ok) [HAVE_TIMER_SETTIME]:
10 New static vars.
11 (enum profiler_cpu_running): New enumn.
12 (profiler_cpu_running): Now of that enum type, not bool.
13 All uses changed to store the new value.
14 (handle_profiler_signal): Rename from sigprof_handler_1,
15 for consistency with other handlers. Do not check whether
16 cpu_log is a hash-table if garbage collecting, since it
17 doesn't matter in that case.
18 (deliver_profiler_signal): Rename from sigprof_handler,
19 for consistency with other handlers.
20 (setup_cpu_timer): New function, with much of what used to be in
21 Fprofiler_cpu_start. Check for out-of-range argument.
22 Prefer timer_settime if available, and prefer
23 thread cputime clocks, then process cputime clocks, then
24 monotonic clocks, to the old realtime clock. Use make_timeval
25 to round more-correctly when falling back to setitimer.
26 (Fprofiler_cpu_start): Use it.
27 (Fprofiler_cpu_stop): Prefer timer_settime if available.
28 Don't assume that passing NULL as the 2nd argument of setitimer
29 is the same as passing a pointer to all-zero storage.
30 Ignore SIGPROF afterwards.
31 (malloc_probe): Saturate at MOST_POSITIVE_FIXNUM.
32 * sysdep.c (emacs_sigaction_init): Also mask out SIGPROF in
33 non-fatal signal handlers. Ignore SIGPROF on startup.
34 * syssignal.h (PROFILER_CPU_SUPPORT): Define this macro here, not
35 in profiler.c, since sysdep.c now uses it.
36
3 * sysdep.c (handle_fatal_signal): Bump backtrace size to 40. 37 * sysdep.c (handle_fatal_signal): Bump backtrace size to 40.
4 Suggested by Eli Zaretskii in 38 Suggested by Eli Zaretskii in
5 <http://lists.gnu.org/archive/html/emacs-devel/2012-09/msg00796.html>. 39 <http://lists.gnu.org/archive/html/emacs-devel/2012-09/msg00796.html>.
diff --git a/src/profiler.c b/src/profiler.c
index 44a12fc159c..489c68b7291 100644
--- a/src/profiler.c
+++ b/src/profiler.c
@@ -18,13 +18,18 @@ You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ 18along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
19 19
20#include <config.h> 20#include <config.h>
21#include <stdio.h>
22#include <limits.h>
23#include <sys/time.h>
24#include <signal.h>
25#include <setjmp.h>
26#include "lisp.h" 21#include "lisp.h"
27#include "syssignal.h" 22#include "syssignal.h"
23#include "systime.h"
24
25/* Return A + B, but return the maximum fixnum if the result would overflow.
26 Assume A and B are nonnegative and in fixnum range. */
27
28static EMACS_INT
29saturated_add (EMACS_INT a, EMACS_INT b)
30{
31 return min (a + b, MOST_POSITIVE_FIXNUM);
32}
28 33
29/* Logs. */ 34/* Logs. */
30 35
@@ -122,14 +127,12 @@ static void evict_lower_half (log_t *log)
122 } 127 }
123} 128}
124 129
125/* Record the current backtrace in LOG. BASE is a special name for 130/* Record the current backtrace in LOG. COUNT is the weight of this
126 describing which the backtrace come from. BASE can be nil. COUNT is 131 current backtrace: milliseconds for CPU counts, and the allocation
127 a number how many times the profiler sees the backtrace at the 132 size for memory logs. */
128 time. ELAPSED is a elapsed time in millisecond that the backtrace
129 took. */
130 133
131static void 134static void
132record_backtrace (log_t *log, size_t count) 135record_backtrace (log_t *log, EMACS_INT count)
133{ 136{
134 struct backtrace *backlist = backtrace_list; 137 struct backtrace *backlist = backtrace_list;
135 Lisp_Object backtrace; 138 Lisp_Object backtrace;
@@ -162,8 +165,11 @@ record_backtrace (log_t *log, size_t count)
162 EMACS_UINT hash; 165 EMACS_UINT hash;
163 ptrdiff_t j = hash_lookup (log, backtrace, &hash); 166 ptrdiff_t j = hash_lookup (log, backtrace, &hash);
164 if (j >= 0) 167 if (j >= 0)
165 set_hash_value_slot (log, j, 168 {
166 make_number (count + XINT (HASH_VALUE (log, j)))); 169 EMACS_INT old_val = XINT (HASH_VALUE (log, j));
170 EMACS_INT new_val = saturated_add (old_val, count);
171 set_hash_value_slot (log, j, make_number (new_val));
172 }
167 else 173 else
168 { /* BEWARE! hash_put in general can allocate memory. 174 { /* BEWARE! hash_put in general can allocate memory.
169 But currently it only does that if log->next_free is nil. */ 175 But currently it only does that if log->next_free is nil. */
@@ -195,29 +201,35 @@ record_backtrace (log_t *log, size_t count)
195/* Sample profiler. */ 201/* Sample profiler. */
196 202
197/* FIXME: Add support for the CPU profiler in W32. */ 203/* FIXME: Add support for the CPU profiler in W32. */
198/* FIXME: the sigprof_handler suffers from race-conditions if the signal
199 is delivered to a thread other than the main Emacs thread. */
200 204
201#if defined SIGPROF && defined HAVE_SETITIMER 205#ifdef PROFILER_CPU_SUPPORT
202#define PROFILER_CPU_SUPPORT 206
207/* The profiler timer and whether it was properly initialized, if
208 POSIX timers are available. */
209#ifdef HAVE_TIMER_SETTIME
210static timer_t profiler_timer;
211static bool profiler_timer_ok;
212#endif
203 213
204/* True if sampling profiler is running. */ 214/* Status of sampling profiler. */
205static bool profiler_cpu_running; 215static enum profiler_cpu_running
216 { NOT_RUNNING, TIMER_SETTIME_RUNNING, SETITIMER_RUNNING }
217 profiler_cpu_running;
206 218
219/* Hash-table log of CPU profiler. */
207static Lisp_Object cpu_log; 220static Lisp_Object cpu_log;
221
208/* Separate counter for the time spent in the GC. */ 222/* Separate counter for the time spent in the GC. */
209static EMACS_INT cpu_gc_count; 223static EMACS_INT cpu_gc_count;
210 224
211/* The current sample interval in millisecond. */ 225/* The current sample interval in milliseconds. */
212 226static EMACS_INT current_sample_interval;
213static int current_sample_interval;
214 227
215/* Signal handler for sample profiler. */ 228/* Signal handler for sample profiler. */
216 229
217static void 230static void
218sigprof_handler_1 (int signal) 231handle_profiler_signal (int signal)
219{ 232{
220 eassert (HASH_TABLE_P (cpu_log));
221 if (backtrace_list && EQ (backtrace_list->function, Qautomatic_gc)) 233 if (backtrace_list && EQ (backtrace_list->function, Qautomatic_gc))
222 /* Special case the time-count inside GC because the hash-table 234 /* Special case the time-count inside GC because the hash-table
223 code is not prepared to be used while the GC is running. 235 code is not prepared to be used while the GC is running.
@@ -225,27 +237,90 @@ sigprof_handler_1 (int signal)
225 not expect the ARRAY_MARK_FLAG to be set. We could try and 237 not expect the ARRAY_MARK_FLAG to be set. We could try and
226 harden the hash-table code, but it doesn't seem worth the 238 harden the hash-table code, but it doesn't seem worth the
227 effort. */ 239 effort. */
228 cpu_gc_count += current_sample_interval; 240 cpu_gc_count = saturated_add (cpu_gc_count, current_sample_interval);
229 else 241 else
230 record_backtrace (XHASH_TABLE (cpu_log), current_sample_interval); 242 {
243 eassert (HASH_TABLE_P (cpu_log));
244 record_backtrace (XHASH_TABLE (cpu_log), current_sample_interval);
245 }
231} 246}
232 247
233static void 248static void
234sigprof_handler (int signal) 249deliver_profiler_signal (int signal)
250{
251 deliver_process_signal (signal, handle_profiler_signal);
252}
253
254static enum profiler_cpu_running
255setup_cpu_timer (Lisp_Object sample_interval)
235{ 256{
236 deliver_process_signal (signal, sigprof_handler_1); 257 struct sigaction action;
258 struct itimerval timer;
259 struct timespec interval;
260
261 if (! RANGED_INTEGERP (1, sample_interval,
262 (TYPE_MAXIMUM (time_t) < EMACS_INT_MAX / 1000
263 ? (EMACS_INT) TYPE_MAXIMUM (time_t) * 1000 + 999
264 : EMACS_INT_MAX)))
265 return NOT_RUNNING;
266
267 current_sample_interval = XINT (sample_interval);
268 interval = make_emacs_time (current_sample_interval / 1000,
269 current_sample_interval % 1000 * 1000000);
270 emacs_sigaction_init (&action, deliver_profiler_signal);
271 sigaction (SIGPROF, &action, 0);
272
273#ifdef HAVE_TIMER_SETTIME
274 if (! profiler_timer_ok)
275 {
276 /* System clocks to try, in decreasing order of desirability. */
277 static clockid_t const system_clock[] = {
278#ifdef CLOCK_THREAD_CPUTIME_ID
279 CLOCK_THREAD_CPUTIME_ID,
280#endif
281#ifdef CLOCK_PROCESS_CPUTIME_ID
282 CLOCK_PROCESS_CPUTIME_ID,
283#endif
284#ifdef CLOCK_MONOTONIC
285 CLOCK_MONOTONIC,
286#endif
287 CLOCK_REALTIME
288 };
289 int i;
290 struct sigevent sigev;
291 sigev.sigev_value.sival_ptr = &profiler_timer;
292 sigev.sigev_signo = SIGPROF;
293 sigev.sigev_notify = SIGEV_SIGNAL;
294
295 for (i = 0; i < sizeof system_clock / sizeof *system_clock; i++)
296 if (timer_create (system_clock[i], &sigev, &profiler_timer) == 0)
297 {
298 profiler_timer_ok = 1;
299 break;
300 }
301 }
302
303 if (profiler_timer_ok)
304 {
305 struct itimerspec ispec;
306 ispec.it_value = ispec.it_interval = interval;
307 timer_settime (profiler_timer, 0, &ispec, 0);
308 return TIMER_SETTIME_RUNNING;
309 }
310#endif
311
312 timer.it_value = timer.it_interval = make_timeval (interval);
313 setitimer (ITIMER_PROF, &timer, 0);
314 return SETITIMER_RUNNING;
237} 315}
238 316
239DEFUN ("profiler-cpu-start", Fprofiler_cpu_start, Sprofiler_cpu_start, 317DEFUN ("profiler-cpu-start", Fprofiler_cpu_start, Sprofiler_cpu_start,
240 1, 1, 0, 318 1, 1, 0,
241 doc: /* Start or restart the cpu profiler. 319 doc: /* Start or restart the cpu profiler.
242The cpu profiler will take call-stack samples each SAMPLE-INTERVAL (expressed in milliseconds). 320It takes call-stack samples each SAMPLE-INTERVAL milliseconds.
243See also `profiler-log-size' and `profiler-max-stack-depth'. */) 321See also `profiler-log-size' and `profiler-max-stack-depth'. */)
244 (Lisp_Object sample_interval) 322 (Lisp_Object sample_interval)
245{ 323{
246 struct sigaction sa;
247 struct itimerval timer;
248
249 if (profiler_cpu_running) 324 if (profiler_cpu_running)
250 error ("Sample profiler is already running"); 325 error ("Sample profiler is already running");
251 326
@@ -256,19 +331,9 @@ See also `profiler-log-size' and `profiler-max-stack-depth'. */)
256 profiler_max_stack_depth); 331 profiler_max_stack_depth);
257 } 332 }
258 333
259 current_sample_interval = XINT (sample_interval); 334 profiler_cpu_running = setup_cpu_timer (sample_interval);
260 335 if (! profiler_cpu_running)
261 sa.sa_handler = sigprof_handler; 336 error ("Invalid sample interval");
262 sa.sa_flags = SA_RESTART;
263 sigemptyset (&sa.sa_mask);
264 sigaction (SIGPROF, &sa, 0);
265
266 timer.it_interval.tv_sec = 0;
267 timer.it_interval.tv_usec = current_sample_interval * 1000;
268 timer.it_value = timer.it_interval;
269 setitimer (ITIMER_PROF, &timer, 0);
270
271 profiler_cpu_running = true;
272 337
273 return Qt; 338 return Qt;
274} 339}
@@ -279,12 +344,30 @@ DEFUN ("profiler-cpu-stop", Fprofiler_cpu_stop, Sprofiler_cpu_stop,
279Return non-nil if the profiler was running. */) 344Return non-nil if the profiler was running. */)
280 (void) 345 (void)
281{ 346{
282 if (!profiler_cpu_running) 347 switch (profiler_cpu_running)
283 return Qnil; 348 {
284 profiler_cpu_running = false; 349 case NOT_RUNNING:
350 return Qnil;
351
352 case TIMER_SETTIME_RUNNING:
353 {
354 struct itimerspec disable;
355 memset (&disable, 0, sizeof disable);
356 timer_settime (profiler_timer, 0, &disable, 0);
357 }
358 break;
285 359
286 setitimer (ITIMER_PROF, 0, 0); 360 case SETITIMER_RUNNING:
361 {
362 struct itimerval disable;
363 memset (&disable, 0, sizeof disable);
364 setitimer (ITIMER_PROF, &disable, 0);
365 }
366 break;
367 }
287 368
369 signal (SIGPROF, SIG_IGN);
370 profiler_cpu_running = NOT_RUNNING;
288 return Qt; 371 return Qt;
289} 372}
290 373
@@ -307,7 +390,7 @@ Before returning, a new log is allocated for future samples. */)
307 (void) 390 (void)
308{ 391{
309 Lisp_Object result = cpu_log; 392 Lisp_Object result = cpu_log;
310 /* Here we're making the log visible to Elisp , so it's not safe any 393 /* Here we're making the log visible to Elisp, so it's not safe any
311 more for our use afterwards since we can't rely on its special 394 more for our use afterwards since we can't rely on its special
312 pre-allocated keys anymore. So we have to allocate a new one. */ 395 pre-allocated keys anymore. So we have to allocate a new one. */
313 cpu_log = (profiler_cpu_running 396 cpu_log = (profiler_cpu_running
@@ -319,7 +402,7 @@ Before returning, a new log is allocated for future samples. */)
319 cpu_gc_count = 0; 402 cpu_gc_count = 0;
320 return result; 403 return result;
321} 404}
322#endif /* not defined PROFILER_CPU_SUPPORT */ 405#endif /* PROFILER_CPU_SUPPORT */
323 406
324/* Memory profiler. */ 407/* Memory profiler. */
325 408
@@ -399,7 +482,7 @@ void
399malloc_probe (size_t size) 482malloc_probe (size_t size)
400{ 483{
401 eassert (HASH_TABLE_P (memory_log)); 484 eassert (HASH_TABLE_P (memory_log));
402 record_backtrace (XHASH_TABLE (memory_log), size); 485 record_backtrace (XHASH_TABLE (memory_log), min (size, MOST_POSITIVE_FIXNUM));
403} 486}
404 487
405void 488void
@@ -415,7 +498,7 @@ to make room for new entries. */);
415 profiler_log_size = 10000; 498 profiler_log_size = 10000;
416 499
417#ifdef PROFILER_CPU_SUPPORT 500#ifdef PROFILER_CPU_SUPPORT
418 profiler_cpu_running = false; 501 profiler_cpu_running = NOT_RUNNING;
419 cpu_log = Qnil; 502 cpu_log = Qnil;
420 staticpro (&cpu_log); 503 staticpro (&cpu_log);
421 defsubr (&Sprofiler_cpu_start); 504 defsubr (&Sprofiler_cpu_start);
diff --git a/src/sysdep.c b/src/sysdep.c
index 4deb022fb90..b7141011d05 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -1447,6 +1447,9 @@ emacs_sigaction_init (struct sigaction *action, signal_handler_t handler)
1447#ifdef SIGDANGER 1447#ifdef SIGDANGER
1448 sigaddset (&action->sa_mask, SIGDANGER); 1448 sigaddset (&action->sa_mask, SIGDANGER);
1449#endif 1449#endif
1450#ifdef PROFILER_CPU_SUPPORT
1451 sigaddset (&action->sa_mask, SIGPROF);
1452#endif
1450#ifdef SIGWINCH 1453#ifdef SIGWINCH
1451 sigaddset (&action->sa_mask, SIGWINCH); 1454 sigaddset (&action->sa_mask, SIGWINCH);
1452#endif 1455#endif
@@ -1837,7 +1840,7 @@ init_signals (bool dumping)
1837#endif 1840#endif
1838 sigaction (SIGTERM, &process_fatal_action, 0); 1841 sigaction (SIGTERM, &process_fatal_action, 0);
1839#ifdef SIGPROF 1842#ifdef SIGPROF
1840 sigaction (SIGPROF, &process_fatal_action, 0); 1843 signal (SIGPROF, SIG_IGN);
1841#endif 1844#endif
1842#ifdef SIGVTALRM 1845#ifdef SIGVTALRM
1843 sigaction (SIGVTALRM, &process_fatal_action, 0); 1846 sigaction (SIGVTALRM, &process_fatal_action, 0);
diff --git a/src/syssignal.h b/src/syssignal.h
index 5c0edaa591d..0bc856769ba 100644
--- a/src/syssignal.h
+++ b/src/syssignal.h
@@ -29,6 +29,10 @@ extern void init_signals (bool);
29#define FORWARD_SIGNAL_TO_MAIN_THREAD 29#define FORWARD_SIGNAL_TO_MAIN_THREAD
30#endif 30#endif
31 31
32#if defined SIGPROF && (defined HAVE_TIMER_SETTIME || defined HAVE_SETITIMER)
33# define PROFILER_CPU_SUPPORT
34#endif
35
32extern sigset_t empty_mask; 36extern sigset_t empty_mask;
33 37
34typedef void (*signal_handler_t) (int); 38typedef void (*signal_handler_t) (int);