aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomohiro Matsuyama2012-08-22 21:38:39 +0900
committerTomohiro Matsuyama2012-08-22 21:38:39 +0900
commit12b3895d742e06ba3999773f0f02328ae7d9880f (patch)
tree892a7d66df4967c2682804bac5fa7df5cea0581f
parentce56157e5f8ab1b244a63faf2e09ab8cd7c5ee23 (diff)
downloademacs-12b3895d742e06ba3999773f0f02328ae7d9880f.tar.gz
emacs-12b3895d742e06ba3999773f0f02328ae7d9880f.zip
Add GC profiler.
-rw-r--r--lisp/profiler.el91
-rw-r--r--src/alloc.c53
-rw-r--r--src/lisp.h14
-rw-r--r--src/profiler.c49
4 files changed, 157 insertions, 50 deletions
diff --git a/lisp/profiler.el b/lisp/profiler.el
index 9e94f0d078c..3f10735ccba 100644
--- a/lisp/profiler.el
+++ b/lisp/profiler.el
@@ -44,10 +44,16 @@
44(defun profiler-format (fmt &rest args) 44(defun profiler-format (fmt &rest args)
45 (cl-loop for (width align subfmt) in fmt 45 (cl-loop for (width align subfmt) in fmt
46 for arg in args 46 for arg in args
47 for str = (cl-typecase subfmt 47 for str = (cond
48 (cons (apply 'profiler-format subfmt arg)) 48 ((consp subfmt)
49 (string (format subfmt arg)) 49 (apply 'profiler-format subfmt arg))
50 (t (profiler-ensure-string arg))) 50 ((stringp subfmt)
51 (format subfmt arg))
52 ((and (symbolp subfmt)
53 (fboundp subfmt))
54 (funcall subfmt arg))
55 (t
56 (profiler-ensure-string arg)))
51 for len = (length str) 57 for len = (length str)
52 if (< width len) 58 if (< width len)
53 collect (substring str 0 width) into frags 59 collect (substring str 0 width) into frags
@@ -60,6 +66,30 @@
60 into frags 66 into frags
61 finally return (apply #'concat frags))) 67 finally return (apply #'concat frags)))
62 68
69(defun profiler-format-nbytes (nbytes)
70 (if (and (integerp nbytes) (> nbytes 0))
71 (cl-loop with i = (% (1+ (floor (log10 nbytes))) 3)
72 for c in (append (number-to-string nbytes) nil)
73 if (= i 0)
74 collect ?, into s
75 and do (setq i 3)
76 collect c into s
77 do (cl-decf i)
78 finally return
79 (apply 'string (if (eq (car s) ?,) (cdr s) s)))
80 (profiler-ensure-string nbytes)))
81
82
83
84;;; Backtrace data structure
85
86(defun profiler-backtrace-reverse (backtrace)
87 (cl-case (car backtrace)
88 ((t gc)
89 (cons (car backtrace)
90 (reverse (cdr backtrace))))
91 (t (reverse backtrace))))
92
63 93
64 94
65;;; Slot data structure 95;;; Slot data structure
@@ -105,7 +135,7 @@
105 (format "#<compiled 0x%x>" (sxhash entry))) 135 (format "#<compiled 0x%x>" (sxhash entry)))
106 ((subrp entry) 136 ((subrp entry)
107 (subr-name entry)) 137 (subr-name entry))
108 ((symbolp entry) 138 ((or (symbolp entry) (stringp entry))
109 entry) 139 entry)
110 (t 140 (t
111 (format "#<unknown 0x%x>" (sxhash entry))))))) 141 (format "#<unknown 0x%x>" (sxhash entry)))))))
@@ -129,6 +159,8 @@
129(defun profiler-calltree-count< (a b) 159(defun profiler-calltree-count< (a b)
130 (cond ((eq (profiler-calltree-entry a) t) t) 160 (cond ((eq (profiler-calltree-entry a) t) t)
131 ((eq (profiler-calltree-entry b) t) nil) 161 ((eq (profiler-calltree-entry b) t) nil)
162 ((eq (profiler-calltree-entry a) 'gc) t)
163 ((eq (profiler-calltree-entry b) 'gc) nil)
132 (t (< (profiler-calltree-count a) 164 (t (< (profiler-calltree-count a)
133 (profiler-calltree-count b))))) 165 (profiler-calltree-count b)))))
134 166
@@ -138,6 +170,8 @@
138(defun profiler-calltree-elapsed< (a b) 170(defun profiler-calltree-elapsed< (a b)
139 (cond ((eq (profiler-calltree-entry a) t) t) 171 (cond ((eq (profiler-calltree-entry a) t) t)
140 ((eq (profiler-calltree-entry b) t) nil) 172 ((eq (profiler-calltree-entry b) t) nil)
173 ((eq (profiler-calltree-entry a) 'gc) t)
174 ((eq (profiler-calltree-entry b) 'gc) nil)
141 (t (< (profiler-calltree-elapsed a) 175 (t (< (profiler-calltree-elapsed a)
142 (profiler-calltree-elapsed b))))) 176 (profiler-calltree-elapsed b)))))
143 177
@@ -166,7 +200,9 @@
166 (count (profiler-slot-count slot)) 200 (count (profiler-slot-count slot))
167 (elapsed (profiler-slot-elapsed slot)) 201 (elapsed (profiler-slot-elapsed slot))
168 (node tree)) 202 (node tree))
169 (dolist (entry (if reverse backtrace (reverse backtrace))) 203 (dolist (entry (if reverse
204 backtrace
205 (profiler-backtrace-reverse backtrace)))
170 (let ((child (profiler-calltree-find node entry))) 206 (let ((child (profiler-calltree-find node entry)))
171 (unless child 207 (unless child
172 (setq child (profiler-make-calltree :entry entry :parent node)) 208 (setq child (profiler-make-calltree :entry entry :parent node))
@@ -179,20 +215,27 @@
179 (let ((total-count 0) 215 (let ((total-count 0)
180 (total-elapsed 0)) 216 (total-elapsed 0))
181 (dolist (child (profiler-calltree-children tree)) 217 (dolist (child (profiler-calltree-children tree))
182 (cl-incf total-count (profiler-calltree-count child)) 218 (if (eq (profiler-calltree-entry child) 'gc)
183 (cl-incf total-elapsed (profiler-calltree-elapsed child))) 219 (profiler-calltree-compute-percentages child)
184 (profiler-calltree-walk 220 (cl-incf total-count (profiler-calltree-count child))
185 tree (lambda (node) 221 (cl-incf total-elapsed (profiler-calltree-elapsed child))))
186 (unless (zerop total-count) 222 (dolist (child (profiler-calltree-children tree))
187 (setf (profiler-calltree-count-percent node) 223 (if (eq (profiler-calltree-entry child) 'gc)
188 (format "%s%%" 224 (setf (profiler-calltree-count-percent child) ""
189 (/ (* (profiler-calltree-count node) 100) 225 (profiler-calltree-elapsed-percent child) "")
190 total-count)))) 226 (profiler-calltree-walk
191 (unless (zerop total-elapsed) 227 child
192 (setf (profiler-calltree-elapsed-percent node) 228 (lambda (node)
193 (format "%s%%" 229 (unless (zerop total-count)
194 (/ (* (profiler-calltree-elapsed node) 100) 230 (setf (profiler-calltree-count-percent node)
195 total-elapsed)))))))) 231 (format "%s%%"
232 (/ (* (profiler-calltree-count node) 100)
233 total-count))))
234 (unless (zerop total-elapsed)
235 (setf (profiler-calltree-elapsed-percent node)
236 (format "%s%%"
237 (/ (* (profiler-calltree-elapsed node) 100)
238 total-elapsed))))))))))
196 239
197(cl-defun profiler-calltree-build (log &key reverse) 240(cl-defun profiler-calltree-build (log &key reverse)
198 (let ((tree (profiler-make-calltree))) 241 (let ((tree (profiler-make-calltree)))
@@ -231,8 +274,8 @@
231 (5 right))))) 274 (5 right)))))
232 275
233(defvar profiler-report-memory-line-format 276(defvar profiler-report-memory-line-format
234 '((60 left) 277 '((55 left)
235 (14 right ((9 right) 278 (19 right ((14 right profiler-format-nbytes)
236 (5 right))))) 279 (5 right)))))
237 280
238(defvar profiler-report-log nil) 281(defvar profiler-report-log nil)
@@ -244,6 +287,8 @@
244 (cond 287 (cond
245 ((eq entry t) 288 ((eq entry t)
246 "Others") 289 "Others")
290 ((eq entry 'gc)
291 "Garbage Collection")
247 ((and (symbolp entry) 292 ((and (symbolp entry)
248 (fboundp entry)) 293 (fboundp entry))
249 (propertize (symbol-name entry) 294 (propertize (symbol-name entry)
@@ -462,7 +507,7 @@ otherwise collapse the entry."
462 (setq header-line-format 507 (setq header-line-format
463 (profiler-report-header-line-format 508 (profiler-report-header-line-format
464 profiler-report-memory-line-format 509 profiler-report-memory-line-format
465 "Function" (list "Alloc" "%"))) 510 "Function" (list "Bytes" "%")))
466 (let ((predicate (cl-ecase order 511 (let ((predicate (cl-ecase order
467 (ascending 'profiler-calltree-count<) 512 (ascending 'profiler-calltree-count<)
468 (descending 'profiler-calltree-count>)))) 513 (descending 'profiler-calltree-count>))))
diff --git a/src/alloc.c b/src/alloc.c
index 3a4a8de90f5..389da29a533 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -5380,6 +5380,23 @@ bounded_number (EMACS_INT number)
5380 return make_number (min (MOST_POSITIVE_FIXNUM, number)); 5380 return make_number (min (MOST_POSITIVE_FIXNUM, number));
5381} 5381}
5382 5382
5383/* Calculate total bytes of live objects. */
5384
5385static size_t
5386total_bytes_of_live_objects (void)
5387{
5388 size_t tot = 0;
5389 tot += total_conses * sizeof (struct Lisp_Cons);
5390 tot += total_symbols * sizeof (struct Lisp_Symbol);
5391 tot += total_markers * sizeof (union Lisp_Misc);
5392 tot += total_string_bytes;
5393 tot += total_vector_slots * word_size;
5394 tot += total_floats * sizeof (struct Lisp_Float);
5395 tot += total_intervals * sizeof (struct interval);
5396 tot += total_strings * sizeof (struct Lisp_String);
5397 return tot;
5398}
5399
5383DEFUN ("garbage-collect", Fgarbage_collect, Sgarbage_collect, 0, 0, "", 5400DEFUN ("garbage-collect", Fgarbage_collect, Sgarbage_collect, 0, 0, "",
5384 doc: /* Reclaim storage for Lisp objects no longer needed. 5401 doc: /* Reclaim storage for Lisp objects no longer needed.
5385Garbage collection happens automatically if you cons more than 5402Garbage collection happens automatically if you cons more than
@@ -5405,6 +5422,7 @@ See Info node `(elisp)Garbage Collection'. */)
5405 ptrdiff_t count = SPECPDL_INDEX (); 5422 ptrdiff_t count = SPECPDL_INDEX ();
5406 EMACS_TIME start; 5423 EMACS_TIME start;
5407 Lisp_Object retval = Qnil; 5424 Lisp_Object retval = Qnil;
5425 size_t tot_before = 0;
5408 5426
5409 if (abort_on_gc) 5427 if (abort_on_gc)
5410 abort (); 5428 abort ();
@@ -5421,6 +5439,9 @@ See Info node `(elisp)Garbage Collection'. */)
5421 FOR_EACH_BUFFER (nextb) 5439 FOR_EACH_BUFFER (nextb)
5422 compact_buffer (nextb); 5440 compact_buffer (nextb);
5423 5441
5442 if (memory_profiler_running)
5443 tot_before = total_bytes_of_live_objects ();
5444
5424 start = current_emacs_time (); 5445 start = current_emacs_time ();
5425 5446
5426 /* In case user calls debug_print during GC, 5447 /* In case user calls debug_print during GC,
@@ -5467,6 +5488,7 @@ See Info node `(elisp)Garbage Collection'. */)
5467 shrink_regexp_cache (); 5488 shrink_regexp_cache ();
5468 5489
5469 gc_in_progress = 1; 5490 gc_in_progress = 1;
5491 is_in_trace = 1;
5470 5492
5471 /* Mark all the special slots that serve as the roots of accessibility. */ 5493 /* Mark all the special slots that serve as the roots of accessibility. */
5472 5494
@@ -5587,6 +5609,7 @@ See Info node `(elisp)Garbage Collection'. */)
5587 check_cons_list (); 5609 check_cons_list ();
5588 5610
5589 gc_in_progress = 0; 5611 gc_in_progress = 0;
5612 is_in_trace = 0;
5590 5613
5591 consing_since_gc = 0; 5614 consing_since_gc = 0;
5592 if (gc_cons_threshold < GC_DEFAULT_THRESHOLD / 10) 5615 if (gc_cons_threshold < GC_DEFAULT_THRESHOLD / 10)
@@ -5595,16 +5618,7 @@ See Info node `(elisp)Garbage Collection'. */)
5595 gc_relative_threshold = 0; 5618 gc_relative_threshold = 0;
5596 if (FLOATP (Vgc_cons_percentage)) 5619 if (FLOATP (Vgc_cons_percentage))
5597 { /* Set gc_cons_combined_threshold. */ 5620 { /* Set gc_cons_combined_threshold. */
5598 double tot = 0; 5621 double tot = total_bytes_of_live_objects ();
5599
5600 tot += total_conses * sizeof (struct Lisp_Cons);
5601 tot += total_symbols * sizeof (struct Lisp_Symbol);
5602 tot += total_markers * sizeof (union Lisp_Misc);
5603 tot += total_string_bytes;
5604 tot += total_vector_slots * word_size;
5605 tot += total_floats * sizeof (struct Lisp_Float);
5606 tot += total_intervals * sizeof (struct interval);
5607 tot += total_strings * sizeof (struct Lisp_String);
5608 5622
5609 tot *= XFLOAT_DATA (Vgc_cons_percentage); 5623 tot *= XFLOAT_DATA (Vgc_cons_percentage);
5610 if (0 < tot) 5624 if (0 < tot)
@@ -5707,6 +5721,25 @@ See Info node `(elisp)Garbage Collection'. */)
5707 5721
5708 gcs_done++; 5722 gcs_done++;
5709 5723
5724 /* Collect profiling data. */
5725 if (sample_profiler_running || memory_profiler_running)
5726 {
5727 size_t swept = 0;
5728 size_t elapsed = 0;
5729 if (memory_profiler_running)
5730 {
5731 size_t tot_after = total_bytes_of_live_objects ();
5732 if (tot_before > tot_after)
5733 swept = tot_before - tot_after;
5734 }
5735 if (sample_profiler_running)
5736 {
5737 EMACS_TIME since_start = sub_emacs_time (current_emacs_time (), start);
5738 elapsed = EMACS_TIME_TO_DOUBLE (since_start) * 1000;
5739 }
5740 gc_probe (swept, elapsed);
5741 }
5742
5710 return retval; 5743 return retval;
5711} 5744}
5712 5745
diff --git a/src/lisp.h b/src/lisp.h
index b4cead003c2..a979d45b49f 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -3532,12 +3532,18 @@ void syms_of_dbusbind (void);
3532/* Defined in profiler.c */ 3532/* Defined in profiler.c */
3533extern int sample_profiler_running; 3533extern int sample_profiler_running;
3534extern int memory_profiler_running; 3534extern int memory_profiler_running;
3535extern int is_in_trace;
3536extern Lisp_Object Qgc;
3535extern void malloc_probe (size_t); 3537extern void malloc_probe (size_t);
3536#define MALLOC_PROBE(size) \ 3538extern void gc_probe (size_t, size_t);
3537 do { \ 3539#define ENTER_TRACE (is_in_trace = 1)
3538 if (memory_profiler_running) \ 3540#define LEAVE_TRACE (is_in_trace = 0)
3539 malloc_probe (size); \ 3541#define MALLOC_PROBE(size) \
3542 do { \
3543 if (memory_profiler_running) \
3544 malloc_probe (size); \
3540 } while (0) 3545 } while (0)
3546
3541extern void mark_profiler (void); 3547extern void mark_profiler (void);
3542extern void syms_of_profiler (void); 3548extern void syms_of_profiler (void);
3543 3549
diff --git a/src/profiler.c b/src/profiler.c
index 56458c64b85..c26761148df 100644
--- a/src/profiler.c
+++ b/src/profiler.c
@@ -25,6 +25,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
25#include <setjmp.h> 25#include <setjmp.h>
26#include "lisp.h" 26#include "lisp.h"
27 27
28int is_in_trace;
29Lisp_Object Qgc;
30
28static void sigprof_handler (int, siginfo_t *, void *); 31static void sigprof_handler (int, siginfo_t *, void *);
29static void block_sigprof (void); 32static void block_sigprof (void);
30static void unblock_sigprof (void); 33static void unblock_sigprof (void);
@@ -350,8 +353,8 @@ struct slot
350{ 353{
351 struct slot *next, *prev; 354 struct slot *next, *prev;
352 Lisp_Object backtrace; 355 Lisp_Object backtrace;
353 unsigned int count; 356 size_t count;
354 unsigned int elapsed; 357 size_t elapsed;
355 unsigned char used : 1; 358 unsigned char used : 1;
356}; 359};
357 360
@@ -536,8 +539,8 @@ struct log
536 Lisp_Object backtrace; 539 Lisp_Object backtrace;
537 struct slot_heap *slot_heap; 540 struct slot_heap *slot_heap;
538 struct slot_table *slot_table; 541 struct slot_table *slot_table;
539 unsigned int others_count; 542 size_t others_count;
540 unsigned int others_elapsed; 543 size_t others_elapsed;
541}; 544};
542 545
543static struct log * 546static struct log *
@@ -647,22 +650,23 @@ ensure_slot (struct log *log, Lisp_Object backtrace)
647} 650}
648 651
649static void 652static void
650record_backtrace (struct log *log, unsigned int count, unsigned int elapsed) 653record_backtrace_under (struct log *log, Lisp_Object base,
654 size_t count, size_t elapsed)
651{ 655{
652 int i; 656 int i = 0;
653 Lisp_Object backtrace = log->backtrace; 657 Lisp_Object backtrace = log->backtrace;
654 struct backtrace *backlist = backtrace_list; 658 struct backtrace *backlist = backtrace_list;
655 659
656 if (!apply_filter (backlist)) return; 660 if (!apply_filter (backlist)) return;
657 661
658 for (i = 0; i < ASIZE (backtrace) && backlist; backlist = backlist->next) 662 if (!NILP (base) && ASIZE (backtrace) > 0)
663 ASET (backtrace, i++, base);
664
665 for (; i < ASIZE (backtrace) && backlist; backlist = backlist->next)
659 { 666 {
660 Lisp_Object function = *backlist->function; 667 Lisp_Object function = *backlist->function;
661 if (FUNCTIONP (function)) 668 if (FUNCTIONP (function))
662 { 669 ASET (backtrace, i++, function);
663 ASET (backtrace, i, function);
664 i++;
665 }
666 } 670 }
667 for (; i < ASIZE (backtrace); i++) 671 for (; i < ASIZE (backtrace); i++)
668 ASET (backtrace, i, Qnil); 672 ASET (backtrace, i, Qnil);
@@ -675,6 +679,12 @@ record_backtrace (struct log *log, unsigned int count, unsigned int elapsed)
675 } 679 }
676} 680}
677 681
682static void
683record_backtrace (struct log *log, size_t count, size_t elapsed)
684{
685 record_backtrace_under (log, Qnil, count, elapsed);
686}
687
678static Lisp_Object 688static Lisp_Object
679log_object (struct log *log) 689log_object (struct log *log)
680{ 690{
@@ -892,7 +902,8 @@ DEFUN ("memory-profiler-log",
892static void 902static void
893sigprof_handler (int signal, siginfo_t *info, void *ctx) 903sigprof_handler (int signal, siginfo_t *info, void *ctx)
894{ 904{
895 record_backtrace (sample_log, 1, current_sample_interval); 905 if (!is_in_trace && sample_log)
906 record_backtrace (sample_log, 1, current_sample_interval);
896} 907}
897 908
898static void 909static void
@@ -916,7 +927,17 @@ unblock_sigprof (void)
916void 927void
917malloc_probe (size_t size) 928malloc_probe (size_t size)
918{ 929{
919 record_backtrace (memory_log, size, 0); 930 if (memory_log)
931 record_backtrace (memory_log, size, 0);
932}
933
934void
935gc_probe (size_t size, size_t elapsed)
936{
937 if (sample_log)
938 record_backtrace_under (sample_log, Qgc, 1, elapsed);
939 if (memory_log)
940 record_backtrace_under (memory_log, Qgc, size, elapsed);
920} 941}
921 942
922 943
@@ -942,6 +963,8 @@ mark_profiler (void)
942void 963void
943syms_of_profiler (void) 964syms_of_profiler (void)
944{ 965{
966 DEFSYM (Qgc, "gc");
967
945 DEFVAR_INT ("profiler-max-stack-depth", profiler_max_stack_depth, 968 DEFVAR_INT ("profiler-max-stack-depth", profiler_max_stack_depth,
946 doc: /* FIXME */); 969 doc: /* FIXME */);
947 profiler_max_stack_depth = 16; 970 profiler_max_stack_depth = 16;