aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorStefan Monnier2012-09-26 11:19:10 -0400
committerStefan Monnier2012-09-26 11:19:10 -0400
commit3a880af4a79688e90da45311a8d85bae2d59a811 (patch)
tree178e2f4ac5889ad1de54fc02c967f7acb377ce64 /src
parent9180598cb164cf32daf0e1761a8143e720460987 (diff)
parent234148bf943ffce55121aefc8694889eb08b0daa (diff)
downloademacs-3a880af4a79688e90da45311a8d85bae2d59a811.tar.gz
emacs-3a880af4a79688e90da45311a8d85bae2d59a811.zip
Merge profiler branch
Diffstat (limited to 'src')
-rw-r--r--src/ChangeLog64
-rw-r--r--src/Makefile.in1
-rw-r--r--src/alloc.c67
-rw-r--r--src/emacs.c2
-rw-r--r--src/eval.c16
-rw-r--r--src/lisp.h20
-rw-r--r--src/makefile.w32-in8
-rw-r--r--src/profiler.c426
-rw-r--r--src/xdisp.c20
9 files changed, 577 insertions, 47 deletions
diff --git a/src/ChangeLog b/src/ChangeLog
index 3e999f3f699..47e2b7a7fea 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,29 @@
12012-09-26 Tomohiro Matsuyama <tomo@cx4a.org>
2 Stefan Monnier <monnier@iro.umontreal.ca>
3 Juanma Barranquero <lekktu@gmail.com>
4
5 * profiler.c: New file.
6 * Makefile.in (base_obj): Add profiler.o.
7 * makefile.w32-in (OBJ2, GLOBAL_SOURCES): Add profiler.c.
8 ($(BLD)/profiler.$(O)): New target.
9 * emacs.c (main): Call syms_of_profiler.
10 * alloc.c (Qautomatic_gc): New constant.
11 (MALLOC_PROBE): New macro.
12 (xmalloc, xzalloc, xrealloc, lisp_malloc, lisp_align_malloc): Use it.
13 (total_bytes_of_live_objects): New function.
14 (Fgarbage_collect): Use it. Record itself in backtrace_list.
15 Call malloc_probe for the memory profiler.
16 (syms_of_alloc): Define Qautomatic_gc.
17 * eval.c (eval_sub, Ffuncall): Reorder assignments to avoid
18 race condition.
19 (struct backtrace): Move definition...
20 * lisp.h (struct backtrace): ..here.
21 (Qautomatic_gc, profiler_memory_running): Declare vars.
22 (malloc_probe, syms_of_profiler): Declare functions.
23 * xdisp.c (Qautomatic_redisplay): New constant.
24 (redisplay_internal): Record itself in backtrace_list.
25 (syms_of_xdisp): Define Qautomatic_redisplay.
26
12012-09-25 Juanma Barranquero <lekktu@gmail.com> 272012-09-25 Juanma Barranquero <lekktu@gmail.com>
2 28
3 * makefile.w32-in ($(BLD)/callproc.$(O)): Update dependencies. 29 * makefile.w32-in ($(BLD)/callproc.$(O)): Update dependencies.
@@ -291,8 +317,8 @@
291 (reinvoke_input_signal): Remove. All uses replaced by 317 (reinvoke_input_signal): Remove. All uses replaced by
292 handle_async_input. 318 handle_async_input.
293 (quit_count): Now volatile, since a signal handler uses it. 319 (quit_count): Now volatile, since a signal handler uses it.
294 (handle_interrupt): Now takes bool IN_SIGNAL_HANDLER as arg. All 320 (handle_interrupt): Now takes bool IN_SIGNAL_HANDLER as arg.
295 callers changed. Block SIGINT only if not already blocked. 321 All callers changed. Block SIGINT only if not already blocked.
296 Clear sigmask reliably, even if Fsignal returns, which it can. 322 Clear sigmask reliably, even if Fsignal returns, which it can.
297 Omit unnecessary accesses to volatile var. 323 Omit unnecessary accesses to volatile var.
298 (quit_throw_to_read_char): No need to restore sigmask. 324 (quit_throw_to_read_char): No need to restore sigmask.
@@ -392,8 +418,8 @@
392 if it is defined. Arguments and return value changed. 418 if it is defined. Arguments and return value changed.
393 (valid_image_p, make_image): Callers changed. 419 (valid_image_p, make_image): Callers changed.
394 (xbm_type, xpm_type, pbm_type, png_type, jpeg_type, tiff_type) 420 (xbm_type, xpm_type, pbm_type, png_type, jpeg_type, tiff_type)
395 (gif_type, imagemagick_type, svg_type, gs_type): Add 421 (gif_type, imagemagick_type, svg_type, gs_type):
396 initialization functions. 422 Add initialization functions.
397 (Finit_image_library): Call lookup_image_type. 423 (Finit_image_library): Call lookup_image_type.
398 (CHECK_LIB_AVAILABLE): Macro deleted. 424 (CHECK_LIB_AVAILABLE): Macro deleted.
399 (lookup_image_type): Call define_image_type here, rather than via 425 (lookup_image_type): Call define_image_type here, rather than via
@@ -415,8 +441,8 @@
415 * window.c (Fsplit_window_internal): Handle only Qt value of 441 * window.c (Fsplit_window_internal): Handle only Qt value of
416 Vwindow_combination_limit separately. 442 Vwindow_combination_limit separately.
417 (Qtemp_buffer_resize): New symbol. 443 (Qtemp_buffer_resize): New symbol.
418 (Vwindow_combination_limit): New default value. Rewrite 444 (Vwindow_combination_limit): New default value.
419 doc-string. 445 Rewrite doc-string.
420 446
4212012-09-22 Eli Zaretskii <eliz@gnu.org> 4472012-09-22 Eli Zaretskii <eliz@gnu.org>
422 448
@@ -515,7 +541,7 @@
515 (Fx_create_frame): Call x_set_offset to correctly interpret 541 (Fx_create_frame): Call x_set_offset to correctly interpret
516 top_pos in geometry. 542 top_pos in geometry.
517 543
518 * frame.c (read_integer, XParseGeometry): Moved from w32xfns.c. 544 * frame.c (read_integer, XParseGeometry): Move from w32xfns.c.
519 (Fx_parse_geometry): If there is a space in string, call 545 (Fx_parse_geometry): If there is a space in string, call
520 Qns_parse_geometry, otherwise do as on other terms (Bug#12368). 546 Qns_parse_geometry, otherwise do as on other terms (Bug#12368).
521 547
@@ -616,8 +642,8 @@
616 642
6172012-09-16 Martin Rudalics <rudalics@gmx.at> 6432012-09-16 Martin Rudalics <rudalics@gmx.at>
618 644
619 * window.c (Fwindow_parameter, Fset_window_parameter): Accept 645 * window.c (Fwindow_parameter, Fset_window_parameter):
620 any window as argument (Bug#12452). 646 Accept any window as argument (Bug#12452).
621 647
6222012-09-16 Jan Djärv <jan.h.d@swipnet.se> 6482012-09-16 Jan Djärv <jan.h.d@swipnet.se>
623 649
@@ -692,8 +718,8 @@
6922012-09-14 Dmitry Antipov <dmantipov@yandex.ru> 7182012-09-14 Dmitry Antipov <dmantipov@yandex.ru>
693 719
694 Avoid out-of-range marker position (Bug#12426). 720 Avoid out-of-range marker position (Bug#12426).
695 * insdel.c (replace_range, replace_range_2): Adjust 721 * insdel.c (replace_range, replace_range_2):
696 markers before overlays, as suggested by comments. 722 Adjust markers before overlays, as suggested by comments.
697 (insert_1_both, insert_from_buffer_1, adjust_after_replace): 723 (insert_1_both, insert_from_buffer_1, adjust_after_replace):
698 Remove redundant check before calling offset_intervals. 724 Remove redundant check before calling offset_intervals.
699 725
@@ -992,8 +1018,8 @@
992 in the internal border. 1018 in the internal border.
993 (x_set_window_size): Remove static variables and their usage. 1019 (x_set_window_size): Remove static variables and their usage.
994 (ns_redraw_scroll_bars): Fix NSTRACE arg. 1020 (ns_redraw_scroll_bars): Fix NSTRACE arg.
995 (ns_after_update_window_line, ns_draw_fringe_bitmap): Remove 1021 (ns_after_update_window_line, ns_draw_fringe_bitmap):
996 fringe/internal border adjustment (Bug#11052). 1022 Remove fringe/internal border adjustment (Bug#11052).
997 (ns_draw_fringe_bitmap): Make code more like other terms (xterm.c). 1023 (ns_draw_fringe_bitmap): Make code more like other terms (xterm.c).
998 (ns_draw_window_cursor): Remove fringe/internal border adjustment. 1024 (ns_draw_window_cursor): Remove fringe/internal border adjustment.
999 (ns_fix_rect_ibw): Remove. 1025 (ns_fix_rect_ibw): Remove.
@@ -1210,8 +1236,8 @@
1210 (init_signals) [FORWARD_SIGNAL_TO_MAIN_THREAD]: Initialize it; 1236 (init_signals) [FORWARD_SIGNAL_TO_MAIN_THREAD]: Initialize it;
1211 code moved here from emacs.c's main function. 1237 code moved here from emacs.c's main function.
1212 * sysdep.c, syssignal.h (handle_on_main_thread): New function, 1238 * sysdep.c, syssignal.h (handle_on_main_thread): New function,
1213 replacing the old SIGNAL_THREAD_CHECK. All uses changed. This 1239 replacing the old SIGNAL_THREAD_CHECK. All uses changed.
1214 lets callers save and restore errno properly. 1240 This lets callers save and restore errno properly.
1215 1241
12162012-09-05 Dmitry Antipov <dmantipov@yandex.ru> 12422012-09-05 Dmitry Antipov <dmantipov@yandex.ru>
1217 1243
@@ -1520,8 +1546,8 @@
1520 * process.c: Include TERM_HEADER instead of listing all possible 1546 * process.c: Include TERM_HEADER instead of listing all possible
1521 window-system headers. 1547 window-system headers.
1522 1548
1523 * nsterm.h: Remove declarations now in frame.h. Define 1549 * nsterm.h: Remove declarations now in frame.h.
1524 FRAME_X_SCREEN, FRAME_X_VISUAL. 1550 Define FRAME_X_SCREEN, FRAME_X_VISUAL.
1525 1551
1526 * menu.c: Include TERM_HEADER instead of listing all possible 1552 * menu.c: Include TERM_HEADER instead of listing all possible
1527 window-system headers. 1553 window-system headers.
@@ -1717,8 +1743,8 @@
1717 1743
1718 * nsterm.h (NSPanel): New class variable dialog_return. 1744 * nsterm.h (NSPanel): New class variable dialog_return.
1719 1745
1720 * nsmenu.m (initWithContentRect:styleMask:backing:defer:): Initialize 1746 * nsmenu.m (initWithContentRect:styleMask:backing:defer:):
1721 dialog_return. 1747 Initialize dialog_return.
1722 (windowShouldClose:): Use stop instead of stopModalWithCode. 1748 (windowShouldClose:): Use stop instead of stopModalWithCode.
1723 (clicked:): Ditto, and also set dialog_return (Bug#12258). 1749 (clicked:): Ditto, and also set dialog_return (Bug#12258).
1724 (timeout_handler:): Use stop instead of abortModal. Send a dummy 1750 (timeout_handler:): Use stop instead of abortModal. Send a dummy
diff --git a/src/Makefile.in b/src/Makefile.in
index 37da170edbd..e43f83e1172 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -339,6 +339,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \
339 process.o gnutls.o callproc.o \ 339 process.o gnutls.o callproc.o \
340 region-cache.o sound.o atimer.o \ 340 region-cache.o sound.o atimer.o \
341 doprnt.o intervals.o textprop.o composite.o xml.o \ 341 doprnt.o intervals.o textprop.o composite.o xml.o \
342 profiler.o \
342 $(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \ 343 $(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \
343 $(WINDOW_SYSTEM_OBJ) 344 $(WINDOW_SYSTEM_OBJ)
344obj = $(base_obj) $(NS_OBJC_OBJ) 345obj = $(base_obj) $(NS_OBJC_OBJ)
diff --git a/src/alloc.c b/src/alloc.c
index 923e8736a86..46c9a10c725 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -205,6 +205,7 @@ static Lisp_Object Qintervals;
205static Lisp_Object Qbuffers; 205static Lisp_Object Qbuffers;
206static Lisp_Object Qstring_bytes, Qvector_slots, Qheap; 206static Lisp_Object Qstring_bytes, Qvector_slots, Qheap;
207static Lisp_Object Qgc_cons_threshold; 207static Lisp_Object Qgc_cons_threshold;
208Lisp_Object Qautomatic_gc;
208Lisp_Object Qchar_table_extra_slots; 209Lisp_Object Qchar_table_extra_slots;
209 210
210/* Hook run after GC has finished. */ 211/* Hook run after GC has finished. */
@@ -648,6 +649,13 @@ malloc_unblock_input (void)
648# define MALLOC_UNBLOCK_INPUT ((void) 0) 649# define MALLOC_UNBLOCK_INPUT ((void) 0)
649#endif 650#endif
650 651
652#define MALLOC_PROBE(size) \
653 do { \
654 if (profiler_memory_running) \
655 malloc_probe (size); \
656 } while (0)
657
658
651/* Like malloc but check for no memory and block interrupt input.. */ 659/* Like malloc but check for no memory and block interrupt input.. */
652 660
653void * 661void *
@@ -661,6 +669,7 @@ xmalloc (size_t size)
661 669
662 if (!val && size) 670 if (!val && size)
663 memory_full (size); 671 memory_full (size);
672 MALLOC_PROBE (size);
664 return val; 673 return val;
665} 674}
666 675
@@ -678,6 +687,7 @@ xzalloc (size_t size)
678 if (!val && size) 687 if (!val && size)
679 memory_full (size); 688 memory_full (size);
680 memset (val, 0, size); 689 memset (val, 0, size);
690 MALLOC_PROBE (size);
681 return val; 691 return val;
682} 692}
683 693
@@ -699,6 +709,7 @@ xrealloc (void *block, size_t size)
699 709
700 if (!val && size) 710 if (!val && size)
701 memory_full (size); 711 memory_full (size);
712 MALLOC_PROBE (size);
702 return val; 713 return val;
703} 714}
704 715
@@ -888,6 +899,7 @@ lisp_malloc (size_t nbytes, enum mem_type type)
888 MALLOC_UNBLOCK_INPUT; 899 MALLOC_UNBLOCK_INPUT;
889 if (!val && nbytes) 900 if (!val && nbytes)
890 memory_full (nbytes); 901 memory_full (nbytes);
902 MALLOC_PROBE (nbytes);
891 return val; 903 return val;
892} 904}
893 905
@@ -1093,6 +1105,8 @@ lisp_align_malloc (size_t nbytes, enum mem_type type)
1093 1105
1094 MALLOC_UNBLOCK_INPUT; 1106 MALLOC_UNBLOCK_INPUT;
1095 1107
1108 MALLOC_PROBE (nbytes);
1109
1096 eassert (0 == ((uintptr_t) val) % BLOCK_ALIGN); 1110 eassert (0 == ((uintptr_t) val) % BLOCK_ALIGN);
1097 return val; 1111 return val;
1098} 1112}
@@ -5043,6 +5057,23 @@ bounded_number (EMACS_INT number)
5043 return make_number (min (MOST_POSITIVE_FIXNUM, number)); 5057 return make_number (min (MOST_POSITIVE_FIXNUM, number));
5044} 5058}
5045 5059
5060/* Calculate total bytes of live objects. */
5061
5062static size_t
5063total_bytes_of_live_objects (void)
5064{
5065 size_t tot = 0;
5066 tot += total_conses * sizeof (struct Lisp_Cons);
5067 tot += total_symbols * sizeof (struct Lisp_Symbol);
5068 tot += total_markers * sizeof (union Lisp_Misc);
5069 tot += total_string_bytes;
5070 tot += total_vector_slots * word_size;
5071 tot += total_floats * sizeof (struct Lisp_Float);
5072 tot += total_intervals * sizeof (struct interval);
5073 tot += total_strings * sizeof (struct Lisp_String);
5074 return tot;
5075}
5076
5046DEFUN ("garbage-collect", Fgarbage_collect, Sgarbage_collect, 0, 0, "", 5077DEFUN ("garbage-collect", Fgarbage_collect, Sgarbage_collect, 0, 0, "",
5047 doc: /* Reclaim storage for Lisp objects no longer needed. 5078 doc: /* Reclaim storage for Lisp objects no longer needed.
5048Garbage collection happens automatically if you cons more than 5079Garbage collection happens automatically if you cons more than
@@ -5068,6 +5099,8 @@ See Info node `(elisp)Garbage Collection'. */)
5068 ptrdiff_t count = SPECPDL_INDEX (); 5099 ptrdiff_t count = SPECPDL_INDEX ();
5069 EMACS_TIME start; 5100 EMACS_TIME start;
5070 Lisp_Object retval = Qnil; 5101 Lisp_Object retval = Qnil;
5102 size_t tot_before = 0;
5103 struct backtrace backtrace;
5071 5104
5072 if (abort_on_gc) 5105 if (abort_on_gc)
5073 emacs_abort (); 5106 emacs_abort ();
@@ -5077,6 +5110,14 @@ See Info node `(elisp)Garbage Collection'. */)
5077 if (pure_bytes_used_before_overflow) 5110 if (pure_bytes_used_before_overflow)
5078 return Qnil; 5111 return Qnil;
5079 5112
5113 /* Record this function, so it appears on the profiler's backtraces. */
5114 backtrace.next = backtrace_list;
5115 backtrace.function = &Qautomatic_gc;
5116 backtrace.args = &Qautomatic_gc;
5117 backtrace.nargs = 0;
5118 backtrace.debug_on_exit = 0;
5119 backtrace_list = &backtrace;
5120
5080 check_cons_list (); 5121 check_cons_list ();
5081 5122
5082 /* Don't keep undo information around forever. 5123 /* Don't keep undo information around forever.
@@ -5084,6 +5125,9 @@ See Info node `(elisp)Garbage Collection'. */)
5084 FOR_EACH_BUFFER (nextb) 5125 FOR_EACH_BUFFER (nextb)
5085 compact_buffer (nextb); 5126 compact_buffer (nextb);
5086 5127
5128 if (profiler_memory_running)
5129 tot_before = total_bytes_of_live_objects ();
5130
5087 start = current_emacs_time (); 5131 start = current_emacs_time ();
5088 5132
5089 /* In case user calls debug_print during GC, 5133 /* In case user calls debug_print during GC,
@@ -5255,16 +5299,7 @@ See Info node `(elisp)Garbage Collection'. */)
5255 gc_relative_threshold = 0; 5299 gc_relative_threshold = 0;
5256 if (FLOATP (Vgc_cons_percentage)) 5300 if (FLOATP (Vgc_cons_percentage))
5257 { /* Set gc_cons_combined_threshold. */ 5301 { /* Set gc_cons_combined_threshold. */
5258 double tot = 0; 5302 double tot = total_bytes_of_live_objects ();
5259
5260 tot += total_conses * sizeof (struct Lisp_Cons);
5261 tot += total_symbols * sizeof (struct Lisp_Symbol);
5262 tot += total_markers * sizeof (union Lisp_Misc);
5263 tot += total_string_bytes;
5264 tot += total_vector_slots * word_size;
5265 tot += total_floats * sizeof (struct Lisp_Float);
5266 tot += total_intervals * sizeof (struct interval);
5267 tot += total_strings * sizeof (struct Lisp_String);
5268 5303
5269 tot *= XFLOAT_DATA (Vgc_cons_percentage); 5304 tot *= XFLOAT_DATA (Vgc_cons_percentage);
5270 if (0 < tot) 5305 if (0 < tot)
@@ -5367,6 +5402,17 @@ See Info node `(elisp)Garbage Collection'. */)
5367 5402
5368 gcs_done++; 5403 gcs_done++;
5369 5404
5405 /* Collect profiling data. */
5406 if (profiler_memory_running)
5407 {
5408 size_t swept = 0;
5409 size_t tot_after = total_bytes_of_live_objects ();
5410 if (tot_before > tot_after)
5411 swept = tot_before - tot_after;
5412 malloc_probe (swept);
5413 }
5414
5415 backtrace_list = backtrace.next;
5370 return retval; 5416 return retval;
5371} 5417}
5372 5418
@@ -6527,6 +6573,7 @@ do hash-consing of the objects allocated to pure space. */);
6527 DEFSYM (Qstring_bytes, "string-bytes"); 6573 DEFSYM (Qstring_bytes, "string-bytes");
6528 DEFSYM (Qvector_slots, "vector-slots"); 6574 DEFSYM (Qvector_slots, "vector-slots");
6529 DEFSYM (Qheap, "heap"); 6575 DEFSYM (Qheap, "heap");
6576 DEFSYM (Qautomatic_gc, "Automatic GC");
6530 6577
6531 DEFSYM (Qgc_cons_threshold, "gc-cons-threshold"); 6578 DEFSYM (Qgc_cons_threshold, "gc-cons-threshold");
6532 DEFSYM (Qchar_table_extra_slots, "char-table-extra-slots"); 6579 DEFSYM (Qchar_table_extra_slots, "char-table-extra-slots");
diff --git a/src/emacs.c b/src/emacs.c
index 5aae812b869..05affeefde7 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -1419,6 +1419,8 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
1419 syms_of_ntterm (); 1419 syms_of_ntterm ();
1420#endif /* WINDOWSNT */ 1420#endif /* WINDOWSNT */
1421 1421
1422 syms_of_profiler ();
1423
1422 keys_of_casefiddle (); 1424 keys_of_casefiddle ();
1423 keys_of_cmds (); 1425 keys_of_cmds ();
1424 keys_of_buffer (); 1426 keys_of_buffer ();
diff --git a/src/eval.c b/src/eval.c
index 25a41486279..d984331ec41 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -31,17 +31,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
31#include "xterm.h" 31#include "xterm.h"
32#endif 32#endif
33 33
34struct backtrace 34struct backtrace *backtrace_list;
35{
36 struct backtrace *next;
37 Lisp_Object *function;
38 Lisp_Object *args; /* Points to vector of args. */
39 ptrdiff_t nargs; /* Length of vector. */
40 /* Nonzero means call value of debugger when done with this operation. */
41 unsigned int debug_on_exit : 1;
42};
43
44static struct backtrace *backtrace_list;
45 35
46#if !BYTE_MARK_STACK 36#if !BYTE_MARK_STACK
47static 37static
@@ -2055,11 +2045,11 @@ eval_sub (Lisp_Object form)
2055 original_args = XCDR (form); 2045 original_args = XCDR (form);
2056 2046
2057 backtrace.next = backtrace_list; 2047 backtrace.next = backtrace_list;
2058 backtrace_list = &backtrace;
2059 backtrace.function = &original_fun; /* This also protects them from gc. */ 2048 backtrace.function = &original_fun; /* This also protects them from gc. */
2060 backtrace.args = &original_args; 2049 backtrace.args = &original_args;
2061 backtrace.nargs = UNEVALLED; 2050 backtrace.nargs = UNEVALLED;
2062 backtrace.debug_on_exit = 0; 2051 backtrace.debug_on_exit = 0;
2052 backtrace_list = &backtrace;
2063 2053
2064 if (debug_on_next_call) 2054 if (debug_on_next_call)
2065 do_debug_on_call (Qt); 2055 do_debug_on_call (Qt);
@@ -2730,11 +2720,11 @@ usage: (funcall FUNCTION &rest ARGUMENTS) */)
2730 } 2720 }
2731 2721
2732 backtrace.next = backtrace_list; 2722 backtrace.next = backtrace_list;
2733 backtrace_list = &backtrace;
2734 backtrace.function = &args[0]; 2723 backtrace.function = &args[0];
2735 backtrace.args = &args[1]; /* This also GCPROs them. */ 2724 backtrace.args = &args[1]; /* This also GCPROs them. */
2736 backtrace.nargs = nargs - 1; 2725 backtrace.nargs = nargs - 1;
2737 backtrace.debug_on_exit = 0; 2726 backtrace.debug_on_exit = 0;
2727 backtrace_list = &backtrace;
2738 2728
2739 /* Call GC after setting up the backtrace, so the latter GCPROs the args. */ 2729 /* Call GC after setting up the backtrace, so the latter GCPROs the args. */
2740 maybe_gc (); 2730 maybe_gc ();
diff --git a/src/lisp.h b/src/lisp.h
index 35efa67e707..21ac55c1063 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -2031,6 +2031,18 @@ extern ptrdiff_t specpdl_size;
2031 2031
2032#define SPECPDL_INDEX() (specpdl_ptr - specpdl) 2032#define SPECPDL_INDEX() (specpdl_ptr - specpdl)
2033 2033
2034struct backtrace
2035{
2036 struct backtrace *next;
2037 Lisp_Object *function;
2038 Lisp_Object *args; /* Points to vector of args. */
2039 ptrdiff_t nargs; /* Length of vector. */
2040 /* Nonzero means call value of debugger when done with this operation. */
2041 unsigned int debug_on_exit : 1;
2042};
2043
2044extern struct backtrace *backtrace_list;
2045
2034/* Everything needed to describe an active condition case. 2046/* Everything needed to describe an active condition case.
2035 2047
2036 Members are volatile if their values need to survive _longjmp when 2048 Members are volatile if their values need to survive _longjmp when
@@ -2916,6 +2928,7 @@ build_string (const char *str)
2916 2928
2917extern Lisp_Object pure_cons (Lisp_Object, Lisp_Object); 2929extern Lisp_Object pure_cons (Lisp_Object, Lisp_Object);
2918extern void make_byte_code (struct Lisp_Vector *); 2930extern void make_byte_code (struct Lisp_Vector *);
2931extern Lisp_Object Qautomatic_gc;
2919extern Lisp_Object Qchar_table_extra_slots; 2932extern Lisp_Object Qchar_table_extra_slots;
2920extern struct Lisp_Vector *allocate_vector (EMACS_INT); 2933extern struct Lisp_Vector *allocate_vector (EMACS_INT);
2921extern struct Lisp_Vector *allocate_pseudovector (int memlen, int lisplen, int tag); 2934extern struct Lisp_Vector *allocate_pseudovector (int memlen, int lisplen, int tag);
@@ -3534,6 +3547,13 @@ extern int have_menus_p (void);
3534void syms_of_dbusbind (void); 3547void syms_of_dbusbind (void);
3535#endif 3548#endif
3536 3549
3550
3551/* Defined in profiler.c. */
3552extern bool profiler_memory_running;
3553extern void malloc_probe (size_t);
3554extern void syms_of_profiler (void);
3555
3556
3537#ifdef DOS_NT 3557#ifdef DOS_NT
3538/* Defined in msdos.c, w32.c. */ 3558/* Defined in msdos.c, w32.c. */
3539extern char *emacs_root_dir (void); 3559extern char *emacs_root_dir (void);
diff --git a/src/makefile.w32-in b/src/makefile.w32-in
index bed6f215711..6f228ed0bb3 100644
--- a/src/makefile.w32-in
+++ b/src/makefile.w32-in
@@ -125,6 +125,7 @@ OBJ2 = $(BLD)/sysdep.$(O) \
125 $(BLD)/terminal.$(O) \ 125 $(BLD)/terminal.$(O) \
126 $(BLD)/menu.$(O) \ 126 $(BLD)/menu.$(O) \
127 $(BLD)/xml.$(O) \ 127 $(BLD)/xml.$(O) \
128 $(BLD)/profiler.$(O) \
128 $(BLD)/w32term.$(O) \ 129 $(BLD)/w32term.$(O) \
129 $(BLD)/w32xfns.$(O) \ 130 $(BLD)/w32xfns.$(O) \
130 $(BLD)/w32fns.$(O) \ 131 $(BLD)/w32fns.$(O) \
@@ -222,7 +223,7 @@ GLOBAL_SOURCES = dosfns.c msdos.c \
222 process.c callproc.c unexw32.c \ 223 process.c callproc.c unexw32.c \
223 region-cache.c sound.c atimer.c \ 224 region-cache.c sound.c atimer.c \
224 doprnt.c intervals.c textprop.c composite.c \ 225 doprnt.c intervals.c textprop.c composite.c \
225 gnutls.c xml.c 226 gnutls.c xml.c profiler.c
226SOME_MACHINE_OBJECTS = dosfns.o msdos.o \ 227SOME_MACHINE_OBJECTS = dosfns.o msdos.o \
227 xterm.o xfns.o xmenu.o xselect.o xrdb.o xsmfns.o dbusbind.o 228 xterm.o xfns.o xmenu.o xselect.o xrdb.o xsmfns.o dbusbind.o
228obj = $(GLOBAL_SOURCES:.c=.o) 229obj = $(GLOBAL_SOURCES:.c=.o)
@@ -973,6 +974,11 @@ $(BLD)/xml.$(O) : \
973 $(CONFIG_H) \ 974 $(CONFIG_H) \
974 $(LISP_H) 975 $(LISP_H)
975 976
977$(BLD)/profiler.$(O) : \
978 $(SRC)/profiler.c \
979 $(CONFIG_H) \
980 $(LISP_H)
981
976$(BLD)/image.$(O) : \ 982$(BLD)/image.$(O) : \
977 $(SRC)/image.c \ 983 $(SRC)/image.c \
978 $(SRC)/blockinput.h \ 984 $(SRC)/blockinput.h \
diff --git a/src/profiler.c b/src/profiler.c
new file mode 100644
index 00000000000..f8fa697d79d
--- /dev/null
+++ b/src/profiler.c
@@ -0,0 +1,426 @@
1/* Profiler implementation.
2
3Copyright (C) 2012 Free Software Foundation, Inc.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation, either version 3 of the License, or
10(at your option) any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
19
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"
27
28/* Logs. */
29
30typedef struct Lisp_Hash_Table log_t;
31
32static Lisp_Object
33make_log (int heap_size, int max_stack_depth)
34{
35 /* We use a standard Elisp hash-table object, but we use it in
36 a special way. This is OK as long as the object is not exposed
37 to Elisp, i.e. until it is returned by *-profiler-log, after which
38 it can't be used any more. */
39 Lisp_Object log = make_hash_table (Qequal, make_number (heap_size),
40 make_float (DEFAULT_REHASH_SIZE),
41 make_float (DEFAULT_REHASH_THRESHOLD),
42 Qnil, Qnil, Qnil);
43 struct Lisp_Hash_Table *h = XHASH_TABLE (log);
44
45 /* What is special about our hash-tables is that the keys are pre-filled
46 with the vectors we'll put in them. */
47 int i = ASIZE (h->key_and_value) / 2;
48 while (0 < i)
49 set_hash_key_slot (h, --i,
50 Fmake_vector (make_number (max_stack_depth), Qnil));
51 return log;
52}
53
54/* Evict the least used half of the hash_table.
55
56 When the table is full, we have to evict someone.
57 The easiest and most efficient is to evict the value we're about to add
58 (i.e. once the table is full, stop sampling).
59
60 We could also pick the element with the lowest count and evict it,
61 but finding it is O(N) and for that amount of work we get very
62 little in return: for the next sample, this latest sample will have
63 count==1 and will hence be a prime candidate for eviction :-(
64
65 So instead, we take O(N) time to eliminate more or less half of the
66 entries (the half with the lowest counts). So we get an amortized
67 cost of O(1) and we get O(N) time for a new entry to grow larger
68 than the other least counts before a new round of eviction. */
69
70static EMACS_INT approximate_median (log_t *log,
71 ptrdiff_t start, ptrdiff_t size)
72{
73 eassert (size > 0);
74 if (size < 2)
75 return XINT (HASH_VALUE (log, start));
76 if (size < 3)
77 /* Not an actual median, but better for our application than
78 choosing either of the two numbers. */
79 return ((XINT (HASH_VALUE (log, start))
80 + XINT (HASH_VALUE (log, start + 1)))
81 / 2);
82 else
83 {
84 ptrdiff_t newsize = size / 3;
85 ptrdiff_t start2 = start + newsize;
86 EMACS_INT i1 = approximate_median (log, start, newsize);
87 EMACS_INT i2 = approximate_median (log, start2, newsize);
88 EMACS_INT i3 = approximate_median (log, start2 + newsize,
89 size - 2 * newsize);
90 return (i1 < i2
91 ? (i2 < i3 ? i2 : (i1 < i3 ? i3 : i1))
92 : (i1 < i3 ? i1 : (i2 < i3 ? i3 : i2)));
93 }
94}
95
96static void evict_lower_half (log_t *log)
97{
98 ptrdiff_t size = ASIZE (log->key_and_value) / 2;
99 EMACS_INT median = approximate_median (log, 0, size);
100 ptrdiff_t i;
101
102 for (i = 0; i < size; i++)
103 /* Evict not only values smaller but also values equal to the median,
104 so as to make sure we evict something no matter what. */
105 if (XINT (HASH_VALUE (log, i)) <= median)
106 {
107 Lisp_Object key = HASH_KEY (log, i);
108 { /* FIXME: we could make this more efficient. */
109 Lisp_Object tmp;
110 XSET_HASH_TABLE (tmp, log); /* FIXME: Use make_lisp_ptr. */
111 Fremhash (key, tmp);
112 }
113 eassert (EQ (log->next_free, make_number (i)));
114 {
115 int j;
116 eassert (VECTORP (key));
117 for (j = 0; j < ASIZE (key); j++)
118 ASET (key, j, Qnil);
119 }
120 set_hash_key_slot (log, i, key);
121 }
122}
123
124/* Record the current backtrace in LOG. BASE is a special name for
125 describing which the backtrace come from. BASE can be nil. COUNT is
126 a number how many times the profiler sees the backtrace at the
127 time. ELAPSED is a elapsed time in millisecond that the backtrace
128 took. */
129
130static void
131record_backtrace (log_t *log, size_t count)
132{
133 struct backtrace *backlist = backtrace_list;
134 Lisp_Object backtrace;
135 ptrdiff_t index, i = 0;
136 ptrdiff_t asize;
137
138 if (!INTEGERP (log->next_free))
139 /* FIXME: transfer the evicted counts to a special entry rather
140 than dropping them on the floor. */
141 evict_lower_half (log);
142 index = XINT (log->next_free);
143
144 /* Get a "working memory" vector. */
145 backtrace = HASH_KEY (log, index);
146 asize = ASIZE (backtrace);
147
148 /* Copy the backtrace contents into working memory. */
149 for (; i < asize && backlist; i++, backlist = backlist->next)
150 /* FIXME: For closures we should ignore the environment. */
151 ASET (backtrace, i, *backlist->function);
152
153 /* Make sure that unused space of working memory is filled with nil. */
154 for (; i < asize; i++)
155 ASET (backtrace, i, Qnil);
156
157 { /* We basically do a `gethash+puthash' here, except that we have to be
158 careful to avoid memory allocation since we're in a signal
159 handler, and we optimize the code to try and avoid computing the
160 hash+lookup twice. See fns.c:Fputhash for reference. */
161 EMACS_UINT hash;
162 ptrdiff_t j = hash_lookup (log, backtrace, &hash);
163 if (j >= 0)
164 set_hash_value_slot (log, j,
165 make_number (count + XINT (HASH_VALUE (log, j))));
166 else
167 { /* BEWARE! hash_put in general can allocate memory.
168 But currently it only does that if log->next_free is nil. */
169 int j;
170 eassert (!NILP (log->next_free));
171 j = hash_put (log, backtrace, make_number (count), hash);
172 /* Let's make sure we've put `backtrace' right where it
173 already was to start with. */
174 eassert (index == j);
175
176 /* FIXME: If the hash-table is almost full, we should set
177 some global flag so that some Elisp code can offload its
178 data elsewhere, so as to avoid the eviction code.
179 There are 2 ways to do that, AFAICT:
180 - Set a flag checked in QUIT, such that QUIT can then call
181 Fprofiler_cpu_log and stash the full log for later use.
182 - Set a flag check in post-gc-hook, so that Elisp code can call
183 profiler-cpu-log. That gives us more flexibility since that
184 Elisp code can then do all kinds of fun stuff like write
185 the log to disk. Or turn it right away into a call tree.
186 Of course, using Elisp is generally preferable, but it may
187 take longer until we get a chance to run the Elisp code, so
188 there's more risk that the table will get full before we
189 get there. */
190 }
191 }
192}
193
194/* Sample profiler. */
195
196/* FIXME: Add support for the CPU profiler in W32. */
197/* FIXME: the sigprof_handler suffers from race-conditions if the signal
198 is delivered to a thread other than the main Emacs thread. */
199
200#if defined SIGPROF && defined HAVE_SETITIMER
201#define PROFILER_CPU_SUPPORT
202
203/* True if sampling profiler is running. */
204static bool profiler_cpu_running;
205
206static Lisp_Object cpu_log;
207/* Separate counter for the time spent in the GC. */
208static EMACS_INT cpu_gc_count;
209
210/* The current sample interval in millisecond. */
211
212static int current_sample_interval;
213
214/* Signal handler for sample profiler. */
215
216static void
217sigprof_handler (int signal, siginfo_t *info, void *ctx)
218{
219 eassert (HASH_TABLE_P (cpu_log));
220 if (backtrace_list && EQ (*backtrace_list->function, Qautomatic_gc))
221 /* Special case the time-count inside GC because the hash-table
222 code is not prepared to be used while the GC is running.
223 More specifically it uses ASIZE at many places where it does
224 not expect the ARRAY_MARK_FLAG to be set. We could try and
225 harden the hash-table code, but it doesn't seem worth the
226 effort. */
227 cpu_gc_count += current_sample_interval;
228 else
229 record_backtrace (XHASH_TABLE (cpu_log), current_sample_interval);
230}
231
232DEFUN ("profiler-cpu-start", Fprofiler_cpu_start, Sprofiler_cpu_start,
233 1, 1, 0,
234 doc: /* Start or restart the cpu profiler.
235The cpu profiler will take call-stack samples each SAMPLE-INTERVAL (expressed in milliseconds).
236See also `profiler-log-size' and `profiler-max-stack-depth'. */)
237 (Lisp_Object sample_interval)
238{
239 struct sigaction sa;
240 struct itimerval timer;
241
242 if (profiler_cpu_running)
243 error ("Sample profiler is already running");
244
245 if (NILP (cpu_log))
246 {
247 cpu_gc_count = 0;
248 cpu_log = make_log (profiler_log_size,
249 profiler_max_stack_depth);
250 }
251
252 current_sample_interval = XINT (sample_interval);
253
254 sa.sa_sigaction = sigprof_handler;
255 sa.sa_flags = SA_RESTART | SA_SIGINFO;
256 sigemptyset (&sa.sa_mask);
257 sigaction (SIGPROF, &sa, 0);
258
259 timer.it_interval.tv_sec = 0;
260 timer.it_interval.tv_usec = current_sample_interval * 1000;
261 timer.it_value = timer.it_interval;
262 setitimer (ITIMER_PROF, &timer, 0);
263
264 profiler_cpu_running = true;
265
266 return Qt;
267}
268
269DEFUN ("profiler-cpu-stop", Fprofiler_cpu_stop, Sprofiler_cpu_stop,
270 0, 0, 0,
271 doc: /* Stop the cpu profiler. The profiler log is not affected.
272Return non-nil if the profiler was running. */)
273 (void)
274{
275 if (!profiler_cpu_running)
276 return Qnil;
277 profiler_cpu_running = false;
278
279 setitimer (ITIMER_PROF, 0, 0);
280
281 return Qt;
282}
283
284DEFUN ("profiler-cpu-running-p",
285 Fprofiler_cpu_running_p, Sprofiler_cpu_running_p,
286 0, 0, 0,
287 doc: /* Return non-nil iff cpu profiler is running. */)
288 (void)
289{
290 return profiler_cpu_running ? Qt : Qnil;
291}
292
293DEFUN ("profiler-cpu-log", Fprofiler_cpu_log, Sprofiler_cpu_log,
294 0, 0, 0,
295 doc: /* Return the current cpu profiler log.
296The log is a hash-table mapping backtraces to counters which represent
297the amount of time spent at those points. Every backtrace is a vector
298of functions, where the last few elements may be nil.
299Before returning, a new log is allocated for future samples. */)
300 (void)
301{
302 Lisp_Object result = cpu_log;
303 /* Here we're making the log visible to Elisp , so it's not safe any
304 more for our use afterwards since we can't rely on its special
305 pre-allocated keys anymore. So we have to allocate a new one. */
306 cpu_log = (profiler_cpu_running
307 ? make_log (profiler_log_size, profiler_max_stack_depth)
308 : Qnil);
309 Fputhash (Fmake_vector (make_number (1), Qautomatic_gc),
310 make_number (cpu_gc_count),
311 result);
312 cpu_gc_count = 0;
313 return result;
314}
315#endif /* not defined PROFILER_CPU_SUPPORT */
316
317/* Memory profiler. */
318
319/* True if memory profiler is running. */
320bool profiler_memory_running;
321
322static Lisp_Object memory_log;
323
324DEFUN ("profiler-memory-start", Fprofiler_memory_start, Sprofiler_memory_start,
325 0, 0, 0,
326 doc: /* Start/restart the memory profiler.
327The memory profiler will take samples of the call-stack whenever a new
328allocation takes place. Note that most small allocations only trigger
329the profiler occasionally.
330See also `profiler-log-size' and `profiler-max-stack-depth'. */)
331 (void)
332{
333 if (profiler_memory_running)
334 error ("Memory profiler is already running");
335
336 if (NILP (memory_log))
337 memory_log = make_log (profiler_log_size,
338 profiler_max_stack_depth);
339
340 profiler_memory_running = true;
341
342 return Qt;
343}
344
345DEFUN ("profiler-memory-stop",
346 Fprofiler_memory_stop, Sprofiler_memory_stop,
347 0, 0, 0,
348 doc: /* Stop the memory profiler. The profiler log is not affected.
349Return non-nil if the profiler was running. */)
350 (void)
351{
352 if (!profiler_memory_running)
353 return Qnil;
354 profiler_memory_running = false;
355 return Qt;
356}
357
358DEFUN ("profiler-memory-running-p",
359 Fprofiler_memory_running_p, Sprofiler_memory_running_p,
360 0, 0, 0,
361 doc: /* Return non-nil if memory profiler is running. */)
362 (void)
363{
364 return profiler_memory_running ? Qt : Qnil;
365}
366
367DEFUN ("profiler-memory-log",
368 Fprofiler_memory_log, Sprofiler_memory_log,
369 0, 0, 0,
370 doc: /* Return the current memory profiler log.
371The log is a hash-table mapping backtraces to counters which represent
372the amount of memory allocated at those points. Every backtrace is a vector
373of functions, where the last few elements may be nil.
374Before returning, a new log is allocated for future samples. */)
375 (void)
376{
377 Lisp_Object result = memory_log;
378 /* Here we're making the log visible to Elisp , so it's not safe any
379 more for our use afterwards since we can't rely on its special
380 pre-allocated keys anymore. So we have to allocate a new one. */
381 memory_log = (profiler_memory_running
382 ? make_log (profiler_log_size, profiler_max_stack_depth)
383 : Qnil);
384 return result;
385}
386
387
388/* Signals and probes. */
389
390/* Record that the current backtrace allocated SIZE bytes. */
391void
392malloc_probe (size_t size)
393{
394 eassert (HASH_TABLE_P (memory_log));
395 record_backtrace (XHASH_TABLE (memory_log), size);
396}
397
398void
399syms_of_profiler (void)
400{
401 DEFVAR_INT ("profiler-max-stack-depth", profiler_max_stack_depth,
402 doc: /* Number of elements from the call-stack recorded in the log. */);
403 profiler_max_stack_depth = 16;
404 DEFVAR_INT ("profiler-log-size", profiler_log_size,
405 doc: /* Number of distinct call-stacks that can be recorded in a profiler log.
406If the log gets full, some of the least-seen call-stacks will be evicted
407to make room for new entries. */);
408 profiler_log_size = 10000;
409
410#ifdef PROFILER_CPU_SUPPORT
411 profiler_cpu_running = false;
412 cpu_log = Qnil;
413 staticpro (&cpu_log);
414 defsubr (&Sprofiler_cpu_start);
415 defsubr (&Sprofiler_cpu_stop);
416 defsubr (&Sprofiler_cpu_running_p);
417 defsubr (&Sprofiler_cpu_log);
418#endif
419 profiler_memory_running = false;
420 memory_log = Qnil;
421 staticpro (&memory_log);
422 defsubr (&Sprofiler_memory_start);
423 defsubr (&Sprofiler_memory_stop);
424 defsubr (&Sprofiler_memory_running_p);
425 defsubr (&Sprofiler_memory_log);
426}
diff --git a/src/xdisp.c b/src/xdisp.c
index 0f02997be22..fa6460d7be2 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -333,10 +333,10 @@ static Lisp_Object Qinhibit_eval_during_redisplay;
333static Lisp_Object Qbuffer_position, Qposition, Qobject; 333static Lisp_Object Qbuffer_position, Qposition, Qobject;
334static Lisp_Object Qright_to_left, Qleft_to_right; 334static Lisp_Object Qright_to_left, Qleft_to_right;
335 335
336/* Cursor shapes */ 336/* Cursor shapes. */
337Lisp_Object Qbar, Qhbar, Qbox, Qhollow; 337Lisp_Object Qbar, Qhbar, Qbox, Qhollow;
338 338
339/* Pointer shapes */ 339/* Pointer shapes. */
340static Lisp_Object Qarrow, Qhand; 340static Lisp_Object Qarrow, Qhand;
341Lisp_Object Qtext; 341Lisp_Object Qtext;
342 342
@@ -347,6 +347,7 @@ static Lisp_Object Qfontification_functions;
347 347
348static Lisp_Object Qwrap_prefix; 348static Lisp_Object Qwrap_prefix;
349static Lisp_Object Qline_prefix; 349static Lisp_Object Qline_prefix;
350static Lisp_Object Qautomatic_redisplay;
350 351
351/* Non-nil means don't actually do any redisplay. */ 352/* Non-nil means don't actually do any redisplay. */
352 353
@@ -12929,12 +12930,13 @@ redisplay_internal (void)
12929 struct frame *sf; 12930 struct frame *sf;
12930 int polling_stopped_here = 0; 12931 int polling_stopped_here = 0;
12931 Lisp_Object old_frame = selected_frame; 12932 Lisp_Object old_frame = selected_frame;
12933 struct backtrace backtrace;
12932 12934
12933 /* Non-zero means redisplay has to consider all windows on all 12935 /* Non-zero means redisplay has to consider all windows on all
12934 frames. Zero means, only selected_window is considered. */ 12936 frames. Zero means, only selected_window is considered. */
12935 int consider_all_windows_p; 12937 int consider_all_windows_p;
12936 12938
12937 /* Non-zero means redisplay has to redisplay the miniwindow */ 12939 /* Non-zero means redisplay has to redisplay the miniwindow. */
12938 int update_miniwindow_p = 0; 12940 int update_miniwindow_p = 0;
12939 12941
12940 TRACE ((stderr, "redisplay_internal %d\n", redisplaying_p)); 12942 TRACE ((stderr, "redisplay_internal %d\n", redisplaying_p));
@@ -12971,6 +12973,14 @@ redisplay_internal (void)
12971 redisplaying_p = 1; 12973 redisplaying_p = 1;
12972 specbind (Qinhibit_free_realized_faces, Qnil); 12974 specbind (Qinhibit_free_realized_faces, Qnil);
12973 12975
12976 /* Record this function, so it appears on the profiler's backtraces. */
12977 backtrace.next = backtrace_list;
12978 backtrace.function = &Qautomatic_redisplay;
12979 backtrace.args = &Qautomatic_redisplay;
12980 backtrace.nargs = 0;
12981 backtrace.debug_on_exit = 0;
12982 backtrace_list = &backtrace;
12983
12974 { 12984 {
12975 Lisp_Object tail, frame; 12985 Lisp_Object tail, frame;
12976 12986
@@ -13668,6 +13678,7 @@ redisplay_internal (void)
13668#endif /* HAVE_WINDOW_SYSTEM */ 13678#endif /* HAVE_WINDOW_SYSTEM */
13669 13679
13670 end_of_redisplay: 13680 end_of_redisplay:
13681 backtrace_list = backtrace.next;
13671 unbind_to (count, Qnil); 13682 unbind_to (count, Qnil);
13672 RESUME_POLLING; 13683 RESUME_POLLING;
13673} 13684}
@@ -28683,6 +28694,7 @@ syms_of_xdisp (void)
28683 staticpro (&Vmessage_stack); 28694 staticpro (&Vmessage_stack);
28684 28695
28685 DEFSYM (Qinhibit_redisplay, "inhibit-redisplay"); 28696 DEFSYM (Qinhibit_redisplay, "inhibit-redisplay");
28697 DEFSYM (Qautomatic_redisplay, "Automatic Redisplay");
28686 28698
28687 message_dolog_marker1 = Fmake_marker (); 28699 message_dolog_marker1 = Fmake_marker ();
28688 staticpro (&message_dolog_marker1); 28700 staticpro (&message_dolog_marker1);
@@ -29349,7 +29361,7 @@ init_xdisp (void)
29349 the following three functions in w32fns.c. */ 29361 the following three functions in w32fns.c. */
29350#ifndef WINDOWSNT 29362#ifndef WINDOWSNT
29351 29363
29352/* Platform-independent portion of hourglass implementation. */ 29364/* Platform-independent portion of hourglass implementation. */
29353 29365
29354/* Cancel a currently active hourglass timer, and start a new one. */ 29366/* Cancel a currently active hourglass timer, and start a new one. */
29355void 29367void