diff options
| author | Stefan Monnier | 2012-09-26 11:19:10 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2012-09-26 11:19:10 -0400 |
| commit | 3a880af4a79688e90da45311a8d85bae2d59a811 (patch) | |
| tree | 178e2f4ac5889ad1de54fc02c967f7acb377ce64 /src | |
| parent | 9180598cb164cf32daf0e1761a8143e720460987 (diff) | |
| parent | 234148bf943ffce55121aefc8694889eb08b0daa (diff) | |
| download | emacs-3a880af4a79688e90da45311a8d85bae2d59a811.tar.gz emacs-3a880af4a79688e90da45311a8d85bae2d59a811.zip | |
Merge profiler branch
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 64 | ||||
| -rw-r--r-- | src/Makefile.in | 1 | ||||
| -rw-r--r-- | src/alloc.c | 67 | ||||
| -rw-r--r-- | src/emacs.c | 2 | ||||
| -rw-r--r-- | src/eval.c | 16 | ||||
| -rw-r--r-- | src/lisp.h | 20 | ||||
| -rw-r--r-- | src/makefile.w32-in | 8 | ||||
| -rw-r--r-- | src/profiler.c | 426 | ||||
| -rw-r--r-- | src/xdisp.c | 20 |
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 @@ | |||
| 1 | 2012-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 | |||
| 1 | 2012-09-25 Juanma Barranquero <lekktu@gmail.com> | 27 | 2012-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 | ||
| 421 | 2012-09-22 Eli Zaretskii <eliz@gnu.org> | 447 | 2012-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 | ||
| 617 | 2012-09-16 Martin Rudalics <rudalics@gmx.at> | 643 | 2012-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 | ||
| 622 | 2012-09-16 Jan Djärv <jan.h.d@swipnet.se> | 648 | 2012-09-16 Jan Djärv <jan.h.d@swipnet.se> |
| 623 | 649 | ||
| @@ -692,8 +718,8 @@ | |||
| 692 | 2012-09-14 Dmitry Antipov <dmantipov@yandex.ru> | 718 | 2012-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 | ||
| 1216 | 2012-09-05 Dmitry Antipov <dmantipov@yandex.ru> | 1242 | 2012-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) |
| 344 | obj = $(base_obj) $(NS_OBJC_OBJ) | 345 | obj = $(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; | |||
| 205 | static Lisp_Object Qbuffers; | 205 | static Lisp_Object Qbuffers; |
| 206 | static Lisp_Object Qstring_bytes, Qvector_slots, Qheap; | 206 | static Lisp_Object Qstring_bytes, Qvector_slots, Qheap; |
| 207 | static Lisp_Object Qgc_cons_threshold; | 207 | static Lisp_Object Qgc_cons_threshold; |
| 208 | Lisp_Object Qautomatic_gc; | ||
| 208 | Lisp_Object Qchar_table_extra_slots; | 209 | Lisp_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 | ||
| 653 | void * | 661 | void * |
| @@ -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 | |||
| 5062 | static size_t | ||
| 5063 | total_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 | |||
| 5046 | DEFUN ("garbage-collect", Fgarbage_collect, Sgarbage_collect, 0, 0, "", | 5077 | DEFUN ("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. |
| 5048 | Garbage collection happens automatically if you cons more than | 5079 | Garbage 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 | ||
| 34 | struct backtrace | 34 | struct 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 | |||
| 44 | static struct backtrace *backtrace_list; | ||
| 45 | 35 | ||
| 46 | #if !BYTE_MARK_STACK | 36 | #if !BYTE_MARK_STACK |
| 47 | static | 37 | static |
| @@ -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 | ||
| 2034 | struct 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 | |||
| 2044 | extern 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 | ||
| 2917 | extern Lisp_Object pure_cons (Lisp_Object, Lisp_Object); | 2929 | extern Lisp_Object pure_cons (Lisp_Object, Lisp_Object); |
| 2918 | extern void make_byte_code (struct Lisp_Vector *); | 2930 | extern void make_byte_code (struct Lisp_Vector *); |
| 2931 | extern Lisp_Object Qautomatic_gc; | ||
| 2919 | extern Lisp_Object Qchar_table_extra_slots; | 2932 | extern Lisp_Object Qchar_table_extra_slots; |
| 2920 | extern struct Lisp_Vector *allocate_vector (EMACS_INT); | 2933 | extern struct Lisp_Vector *allocate_vector (EMACS_INT); |
| 2921 | extern struct Lisp_Vector *allocate_pseudovector (int memlen, int lisplen, int tag); | 2934 | extern struct Lisp_Vector *allocate_pseudovector (int memlen, int lisplen, int tag); |
| @@ -3534,6 +3547,13 @@ extern int have_menus_p (void); | |||
| 3534 | void syms_of_dbusbind (void); | 3547 | void syms_of_dbusbind (void); |
| 3535 | #endif | 3548 | #endif |
| 3536 | 3549 | ||
| 3550 | |||
| 3551 | /* Defined in profiler.c. */ | ||
| 3552 | extern bool profiler_memory_running; | ||
| 3553 | extern void malloc_probe (size_t); | ||
| 3554 | extern 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. */ |
| 3539 | extern char *emacs_root_dir (void); | 3559 | extern 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 |
| 226 | SOME_MACHINE_OBJECTS = dosfns.o msdos.o \ | 227 | SOME_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 |
| 228 | obj = $(GLOBAL_SOURCES:.c=.o) | 229 | obj = $(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 | |||
| 3 | Copyright (C) 2012 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | This file is part of GNU Emacs. | ||
| 6 | |||
| 7 | GNU Emacs is free software: you can redistribute it and/or modify | ||
| 8 | it under the terms of the GNU General Public License as published by | ||
| 9 | the Free Software Foundation, either version 3 of the License, or | ||
| 10 | (at your option) any later version. | ||
| 11 | |||
| 12 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | GNU General Public License for more details. | ||
| 16 | |||
| 17 | You should have received a copy of the GNU General Public License | ||
| 18 | along 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 | |||
| 30 | typedef struct Lisp_Hash_Table log_t; | ||
| 31 | |||
| 32 | static Lisp_Object | ||
| 33 | make_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 | |||
| 70 | static 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 | |||
| 96 | static 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 | |||
| 130 | static void | ||
| 131 | record_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. */ | ||
| 204 | static bool profiler_cpu_running; | ||
| 205 | |||
| 206 | static Lisp_Object cpu_log; | ||
| 207 | /* Separate counter for the time spent in the GC. */ | ||
| 208 | static EMACS_INT cpu_gc_count; | ||
| 209 | |||
| 210 | /* The current sample interval in millisecond. */ | ||
| 211 | |||
| 212 | static int current_sample_interval; | ||
| 213 | |||
| 214 | /* Signal handler for sample profiler. */ | ||
| 215 | |||
| 216 | static void | ||
| 217 | sigprof_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 | |||
| 232 | DEFUN ("profiler-cpu-start", Fprofiler_cpu_start, Sprofiler_cpu_start, | ||
| 233 | 1, 1, 0, | ||
| 234 | doc: /* Start or restart the cpu profiler. | ||
| 235 | The cpu profiler will take call-stack samples each SAMPLE-INTERVAL (expressed in milliseconds). | ||
| 236 | See 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 | |||
| 269 | DEFUN ("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. | ||
| 272 | Return 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 | |||
| 284 | DEFUN ("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 | |||
| 293 | DEFUN ("profiler-cpu-log", Fprofiler_cpu_log, Sprofiler_cpu_log, | ||
| 294 | 0, 0, 0, | ||
| 295 | doc: /* Return the current cpu profiler log. | ||
| 296 | The log is a hash-table mapping backtraces to counters which represent | ||
| 297 | the amount of time spent at those points. Every backtrace is a vector | ||
| 298 | of functions, where the last few elements may be nil. | ||
| 299 | Before 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. */ | ||
| 320 | bool profiler_memory_running; | ||
| 321 | |||
| 322 | static Lisp_Object memory_log; | ||
| 323 | |||
| 324 | DEFUN ("profiler-memory-start", Fprofiler_memory_start, Sprofiler_memory_start, | ||
| 325 | 0, 0, 0, | ||
| 326 | doc: /* Start/restart the memory profiler. | ||
| 327 | The memory profiler will take samples of the call-stack whenever a new | ||
| 328 | allocation takes place. Note that most small allocations only trigger | ||
| 329 | the profiler occasionally. | ||
| 330 | See 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 | |||
| 345 | DEFUN ("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. | ||
| 349 | Return 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 | |||
| 358 | DEFUN ("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 | |||
| 367 | DEFUN ("profiler-memory-log", | ||
| 368 | Fprofiler_memory_log, Sprofiler_memory_log, | ||
| 369 | 0, 0, 0, | ||
| 370 | doc: /* Return the current memory profiler log. | ||
| 371 | The log is a hash-table mapping backtraces to counters which represent | ||
| 372 | the amount of memory allocated at those points. Every backtrace is a vector | ||
| 373 | of functions, where the last few elements may be nil. | ||
| 374 | Before 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. */ | ||
| 391 | void | ||
| 392 | malloc_probe (size_t size) | ||
| 393 | { | ||
| 394 | eassert (HASH_TABLE_P (memory_log)); | ||
| 395 | record_backtrace (XHASH_TABLE (memory_log), size); | ||
| 396 | } | ||
| 397 | |||
| 398 | void | ||
| 399 | syms_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. | ||
| 406 | If the log gets full, some of the least-seen call-stacks will be evicted | ||
| 407 | to 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; | |||
| 333 | static Lisp_Object Qbuffer_position, Qposition, Qobject; | 333 | static Lisp_Object Qbuffer_position, Qposition, Qobject; |
| 334 | static Lisp_Object Qright_to_left, Qleft_to_right; | 334 | static Lisp_Object Qright_to_left, Qleft_to_right; |
| 335 | 335 | ||
| 336 | /* Cursor shapes */ | 336 | /* Cursor shapes. */ |
| 337 | Lisp_Object Qbar, Qhbar, Qbox, Qhollow; | 337 | Lisp_Object Qbar, Qhbar, Qbox, Qhollow; |
| 338 | 338 | ||
| 339 | /* Pointer shapes */ | 339 | /* Pointer shapes. */ |
| 340 | static Lisp_Object Qarrow, Qhand; | 340 | static Lisp_Object Qarrow, Qhand; |
| 341 | Lisp_Object Qtext; | 341 | Lisp_Object Qtext; |
| 342 | 342 | ||
| @@ -347,6 +347,7 @@ static Lisp_Object Qfontification_functions; | |||
| 347 | 347 | ||
| 348 | static Lisp_Object Qwrap_prefix; | 348 | static Lisp_Object Qwrap_prefix; |
| 349 | static Lisp_Object Qline_prefix; | 349 | static Lisp_Object Qline_prefix; |
| 350 | static 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. */ |
| 29355 | void | 29367 | void |