aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorStefan Monnier2012-09-24 10:38:10 -0400
committerStefan Monnier2012-09-24 10:38:10 -0400
commit3d80c99f3817bf5eccd6acc6a79498a4fde979a4 (patch)
tree5377692a9d9b96157a42b8ae693a8f7d18a8bc85 /lisp
parent0970d85fef9830e3b8e5cbfbdc04dbf00cc4c027 (diff)
downloademacs-3d80c99f3817bf5eccd6acc6a79498a4fde979a4.tar.gz
emacs-3d80c99f3817bf5eccd6acc6a79498a4fde979a4.zip
Rewrite sampler to use Elisp hash-tables.
* src/profiler.c: Remove filtering functionality. (is_in_trace, Qgc): Remove vars. (make_log, record_backtrace, Fsample_profiler_log): Rewrite, using Elisp hash-tables. (approximate_median, evict_lower_half): New functions. (cpu_log): Rename from sample_log. (cpu_gc_count): New var. (Fsample_profiler_reset, Fmemory_profiler_reset): Remove. (sigprof_handler): Add count to cpu_gc_count during GC, detected via backtrace_list. (block_sigprof, unblock_sigprof): Remove. (gc_probe, mark_profiler): Remove functions. (syms_of_profiler): Staticpro cpu_log and memory_log. * lisp/profiler.el (profiler-sample-interval): Move before first use. Change default to 1ms. (profiler-entry=, profiler-backtrace-reverse, profiler-log-fixup-slot) (profiler-calltree-elapsed<, profiler-calltree-elapsed>): Remove functions. (profiler-entry-format): Don't use type-of. (profiler-slot, profiler-log): Remove structs. (profiler-log-timestamp, profiler-log-type, profiler-log-diff-p): Redefine for new log representation. (profiler-log-diff, profiler-log-fixup, profiler-calltree-build-1): Rewrite for new log representation. (profiler-calltree): Remove `elapsed' fields. (profiler-calltree-count<, profiler-report-make-entry-part): Remove gc special case. (profiler-calltree-find): Use equal. (profiler-calltree-walk): Remove `args'; rely on closures instead. (profiler-calltree-compute-percentages-1): Remove; inlined. (profiler-calltree-compute-percentages): Simplify. (profiler-report-log, profiler-report-reversed) (profiler-report-order): Use defvar-local. (profiler-report-line-format): Remove `elapsed', do a bit of CSE. (profiler-report-mode-map): Remove up/down bindings. (profiler-report-make-buffer-name): Simplify by CSE. (profiler-report-mode): Remove redundant code. (profiler-report-expand-entry, profiler-report-collapse-entry): Use inhibit-read-only. (profiler-report-render-calltree-1): Simplify by CSE. (profiler-reset): Rewrite for new subroutines. (profiler--report-cpu): Rename from sample-profiler-report. (profiler--report-memory): Rename from memory-profiler-report. * src/alloc.c (Fgarbage_collect): Record itself in backtrace_list. Don't set is_in_trace any more. Don't call mark_profiler. Only call gc_probe for the memory profiler. (syms_of_alloc): Define Qautomatic_gc. * src/lisp.h (SXHASH_COMBINE): Move back to... * src/fns.c (SXHASH_COMBINE): ...here. * src/xdisp.c (Qautomatic_redisplay): New constant. (redisplay_internal): Record itself in backtrace_list. (syms_of_xdisp): Define Qautomatic_redisplay. * .dir-locals.el (indent-tabs-mode): Remove personal preference.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/ChangeLog48
-rw-r--r--lisp/profiler.el366
2 files changed, 189 insertions, 225 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index d8134f2c046..64fb7e2ffc7 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,35 @@
12012-09-24 Stefan Monnier <monnier@iro.umontreal.ca>
2
3 * profiler.el (profiler-sample-interval): Move before first use.
4 Change default to 1ms.
5 (profiler-entry=, profiler-backtrace-reverse, profiler-log-fixup-slot)
6 (profiler-calltree-elapsed<, profiler-calltree-elapsed>): Remove functions.
7 (profiler-entry-format): Don't use type-of.
8 (profiler-slot, profiler-log): Remove structs.
9 (profiler-log-timestamp, profiler-log-type, profiler-log-diff-p):
10 Redefine for new log representation.
11 (profiler-log-diff, profiler-log-fixup, profiler-calltree-build-1):
12 Rewrite for new log representation.
13 (profiler-calltree): Remove `elapsed' fields.
14 (profiler-calltree-count<, profiler-report-make-entry-part):
15 Remove gc special case.
16 (profiler-calltree-find): Use equal.
17 (profiler-calltree-walk): Remove `args'; rely on closures instead.
18 (profiler-calltree-compute-percentages-1): Remove; inlined.
19 (profiler-calltree-compute-percentages): Simplify.
20 (profiler-report-log, profiler-report-reversed)
21 (profiler-report-order): Use defvar-local.
22 (profiler-report-line-format): Remove `elapsed', do a bit of CSE.
23 (profiler-report-mode-map): Remove up/down bindings.
24 (profiler-report-make-buffer-name): Simplify by CSE.
25 (profiler-report-mode): Remove redundant code.
26 (profiler-report-expand-entry, profiler-report-collapse-entry):
27 Use inhibit-read-only.
28 (profiler-report-render-calltree-1): Simplify by CSE.
29 (profiler-reset): Rewrite for new subroutines.
30 (profiler--report-cpu): Rename from sample-profiler-report.
31 (profiler--report-memory): Rename from memory-profiler-report.
32
12012-08-22 Tomohiro Matsuyama <tomo@cx4a.org> 332012-08-22 Tomohiro Matsuyama <tomo@cx4a.org>
2 34
3 * profiler.el: Switch to cl-lib. 35 * profiler.el: Switch to cl-lib.
@@ -35,8 +67,8 @@
35 * window.el (window-point-1, set-window-point-1): Remove. 67 * window.el (window-point-1, set-window-point-1): Remove.
36 (window-in-direction, record-window-buffer) 68 (window-in-direction, record-window-buffer)
37 (set-window-buffer-start-and-point, split-window-below) 69 (set-window-buffer-start-and-point, split-window-below)
38 (window--state-get-1, display-buffer-record-window): Replace 70 (window--state-get-1, display-buffer-record-window):
39 calls to window-point-1 and set-window-point-1 by calls to 71 Replace calls to window-point-1 and set-window-point-1 by calls to
40 window-point and set-window-point respectively. 72 window-point and set-window-point respectively.
41 73
422012-08-21 Glenn Morris <rgm@gnu.org> 742012-08-21 Glenn Morris <rgm@gnu.org>
@@ -154,8 +186,8 @@
154 (yank-excluded-properties): Add font-lock-face and category. 186 (yank-excluded-properties): Add font-lock-face and category.
155 (yank): Doc fix. 187 (yank): Doc fix.
156 188
157 * subr.el (remove-yank-excluded-properties): Obey 189 * subr.el (remove-yank-excluded-properties):
158 yank-handled-properties. The special handling of font-lock-face 190 Obey yank-handled-properties. The special handling of font-lock-face
159 and category is now done this way, instead of being hard-coded. 191 and category is now done this way, instead of being hard-coded.
160 (insert-for-yank-1): Remove font-lock-face handling. 192 (insert-for-yank-1): Remove font-lock-face handling.
161 (yank-handle-font-lock-face-property) 193 (yank-handle-font-lock-face-property)
@@ -169,8 +201,8 @@
169 201
1702012-08-17 Michael Albinus <michael.albinus@gmx.de> 2022012-08-17 Michael Albinus <michael.albinus@gmx.de>
171 203
172 * net/tramp-sh.el (tramp-sh-handle-start-file-process): Eliminate 204 * net/tramp-sh.el (tramp-sh-handle-start-file-process):
173 superfluous prompt. (Bug#12203) 205 Eliminate superfluous prompt. (Bug#12203)
174 206
1752012-08-17 Chong Yidong <cyd@gnu.org> 2072012-08-17 Chong Yidong <cyd@gnu.org>
176 208
@@ -197,8 +229,8 @@
197 (next-buffer, previous-buffer, split-window, balance-windows-2) 229 (next-buffer, previous-buffer, split-window, balance-windows-2)
198 (set-window-text-height, window-buffer-height) 230 (set-window-text-height, window-buffer-height)
199 (fit-window-to-buffer, shrink-window-if-larger-than-buffer) 231 (fit-window-to-buffer, shrink-window-if-larger-than-buffer)
200 (truncated-partial-width-window-p): Minor code adjustments. In 232 (truncated-partial-width-window-p): Minor code adjustments.
201 doc-strings state whether the argument window has to denote a 233 In doc-strings state whether the argument window has to denote a
202 live, valid or any window. 234 live, valid or any window.
203 235
2042012-08-16 Phil Sainty <psainty@orcon.net.nz> (tiny change) 2362012-08-16 Phil Sainty <psainty@orcon.net.nz> (tiny change)
diff --git a/lisp/profiler.el b/lisp/profiler.el
index 1777fc00bde..00ee99a6132 100644
--- a/lisp/profiler.el
+++ b/lisp/profiler.el
@@ -32,7 +32,11 @@
32 :group 'lisp 32 :group 'lisp
33 :prefix "profiler-") 33 :prefix "profiler-")
34 34
35 35(defcustom profiler-sample-interval 1
36 "Default sample interval in millisecond."
37 :type 'integer
38 :group 'profiler)
39
36;;; Utilities 40;;; Utilities
37 41
38(defun profiler-ensure-string (object) 42(defun profiler-ensure-string (object)
@@ -90,55 +94,34 @@
90 94
91;;; Entries 95;;; Entries
92 96
93(defun profiler-entry= (entry1 entry2)
94 "Return t if ENTRY1 and ENTRY2 are same."
95 (or (eq entry1 entry2)
96 (and (stringp entry1)
97 (stringp entry2)
98 (string= entry1 entry2))))
99
100(defun profiler-entry-format (entry) 97(defun profiler-entry-format (entry)
101 "Format ENTRY in human readable string. ENTRY would be a 98 "Format ENTRY in human readable string. ENTRY would be a
102function name of a function itself." 99function name of a function itself."
103 (cond ((and (consp entry) 100 (cond ((memq (car-safe entry) '(closure lambda))
104 (or (eq (car entry) 'lambda) 101 (format "#<lambda 0x%x>" (sxhash entry)))
105 (eq (car entry) 'closure))) 102 ((byte-code-function-p entry)
106 (format "#<closure 0x%x>" (sxhash entry)))
107 ((eq (type-of entry) 'compiled-function)
108 (format "#<compiled 0x%x>" (sxhash entry))) 103 (format "#<compiled 0x%x>" (sxhash entry)))
109 ((subrp entry) 104 ((or (subrp entry) (symbolp entry) (stringp entry))
110 (subr-name entry)) 105 (format "%s" entry))
111 ((symbolp entry)
112 (symbol-name entry))
113 ((stringp entry)
114 entry)
115 (t 106 (t
116 (format "#<unknown 0x%x>" (sxhash entry))))) 107 (format "#<unknown 0x%x>" (sxhash entry)))))
117 108
118
119;;; Backtrace data structure
120
121(defun profiler-backtrace-reverse (backtrace)
122 (cl-case (car backtrace)
123 ((t gc)
124 ;; Make sure Others node and GC node always be at top.
125 (cons (car backtrace)
126 (reverse (cdr backtrace))))
127 (t (reverse backtrace))))
128
129
130;;; Slot data structure
131
132(cl-defstruct (profiler-slot (:type list)
133 (:constructor profiler-make-slot))
134 backtrace count elapsed)
135
136
137;;; Log data structure 109;;; Log data structure
138 110
139(cl-defstruct (profiler-log (:type list) 111;; The C code returns the log in the form of a hash-table where the keys are
140 (:constructor profiler-make-log)) 112;; vectors (of size profiler-max-stack-depth, holding truncated
141 type diff-p timestamp slots) 113;; backtraces, where the first element is the top of the stack) and
114;; the values are integers (which count how many times this backtrace
115;; has been seen, multiplied by a "weight factor" which is either the
116;; sample-interval or the memory being allocated).
117;; We extend it by adding a few other entries to the hash-table, most notably:
118;; - Key `type' has a value indicating the kind of log (`memory' or `cpu').
119;; - Key `timestamp' has a value giving the time when the log was obtained.
120;; - Key `diff-p' indicates if this log represents a diff between two logs.
121
122(defun profiler-log-timestamp (log) (gethash 'timestamp log))
123(defun profiler-log-type (log) (gethash 'type log))
124(defun profiler-log-diff-p (log) (gethash 'diff-p log))
142 125
143(defun profiler-log-diff (log1 log2) 126(defun profiler-log-diff (log1 log2)
144 "Compare LOG1 with LOG2 and return a diff log. Both logs must 127 "Compare LOG1 with LOG2 and return a diff log. Both logs must
@@ -146,16 +129,17 @@ be same type."
146 (unless (eq (profiler-log-type log1) 129 (unless (eq (profiler-log-type log1)
147 (profiler-log-type log2)) 130 (profiler-log-type log2))
148 (error "Can't compare different type of logs")) 131 (error "Can't compare different type of logs"))
149 (let ((slots (profiler-log-slots log2))) 132 (let ((newlog (make-hash-table :test 'equal)))
150 (dolist (slot (profiler-log-slots log1)) 133 ;; Make a copy of `log1' into `newlog'.
151 (push (profiler-make-slot :backtrace (profiler-slot-backtrace slot) 134 (maphash (lambda (backtrace count) (puthash backtrace count newlog))
152 :count (- (profiler-slot-count slot)) 135 log1)
153 :elapsed (- (profiler-slot-elapsed slot))) 136 (puthash 'diff-p t newlog)
154 slots)) 137 (maphash (lambda (backtrace count)
155 (profiler-make-log :type (profiler-log-type log1) 138 (when (vectorp backtrace)
156 :diff-p t 139 (puthash backtrace (- (gethash backtrace log1 0) count)
157 :timestamp (current-time) 140 newlog)))
158 :slots slots))) 141 log2)
142 newlog))
159 143
160(defun profiler-log-fixup-entry (entry) 144(defun profiler-log-fixup-entry (entry)
161 (if (symbolp entry) 145 (if (symbolp entry)
@@ -165,21 +149,16 @@ be same type."
165(defun profiler-log-fixup-backtrace (backtrace) 149(defun profiler-log-fixup-backtrace (backtrace)
166 (mapcar 'profiler-log-fixup-entry backtrace)) 150 (mapcar 'profiler-log-fixup-entry backtrace))
167 151
168(defun profiler-log-fixup-slot (slot)
169 (let ((backtrace (profiler-slot-backtrace slot)))
170 (profiler-make-slot :backtrace (profiler-log-fixup-backtrace backtrace)
171 :count (profiler-slot-count slot)
172 :elapsed (profiler-slot-elapsed slot))))
173
174(defun profiler-log-fixup (log) 152(defun profiler-log-fixup (log)
175 "Fixup LOG so that the log could be serialized into file." 153 "Fixup LOG so that the log could be serialized into file."
176 (cl-loop for slot in (profiler-log-slots log) 154 (let ((newlog (make-hash-table :test 'equal)))
177 collect (profiler-log-fixup-slot slot) into slots 155 (maphash (lambda (backtrace count)
178 finally return 156 (puthash (if (not (vectorp backtrace))
179 (profiler-make-log :type (profiler-log-type log) 157 backtrace
180 :diff-p (profiler-log-diff-p log) 158 (profiler-log-fixup-backtrace backtrace))
181 :timestamp (profiler-log-timestamp log) 159 count newlog))
182 :slots slots))) 160 log)
161 newlog))
183 162
184(defun profiler-log-write-file (log filename &optional confirm) 163(defun profiler-log-write-file (log filename &optional confirm)
185 "Write LOG into FILENAME." 164 "Write LOG into FILENAME."
@@ -201,7 +180,6 @@ be same type."
201(cl-defstruct (profiler-calltree (:constructor profiler-make-calltree)) 180(cl-defstruct (profiler-calltree (:constructor profiler-make-calltree))
202 entry 181 entry
203 (count 0) (count-percent "") 182 (count 0) (count-percent "")
204 (elapsed 0) (elapsed-percent "")
205 parent children) 183 parent children)
206 184
207(defun profiler-calltree-leaf-p (tree) 185(defun profiler-calltree-leaf-p (tree)
@@ -210,25 +188,12 @@ be same type."
210(defun profiler-calltree-count< (a b) 188(defun profiler-calltree-count< (a b)
211 (cond ((eq (profiler-calltree-entry a) t) t) 189 (cond ((eq (profiler-calltree-entry a) t) t)
212 ((eq (profiler-calltree-entry b) t) nil) 190 ((eq (profiler-calltree-entry b) t) nil)
213 ((eq (profiler-calltree-entry a) 'gc) t)
214 ((eq (profiler-calltree-entry b) 'gc) nil)
215 (t (< (profiler-calltree-count a) 191 (t (< (profiler-calltree-count a)
216 (profiler-calltree-count b))))) 192 (profiler-calltree-count b)))))
217 193
218(defun profiler-calltree-count> (a b) 194(defun profiler-calltree-count> (a b)
219 (not (profiler-calltree-count< a b))) 195 (not (profiler-calltree-count< a b)))
220 196
221(defun profiler-calltree-elapsed< (a b)
222 (cond ((eq (profiler-calltree-entry a) t) t)
223 ((eq (profiler-calltree-entry b) t) nil)
224 ((eq (profiler-calltree-entry a) 'gc) t)
225 ((eq (profiler-calltree-entry b) 'gc) nil)
226 (t (< (profiler-calltree-elapsed a)
227 (profiler-calltree-elapsed b)))))
228
229(defun profiler-calltree-elapsed> (a b)
230 (not (profiler-calltree-elapsed< a b)))
231
232(defun profiler-calltree-depth (tree) 197(defun profiler-calltree-depth (tree)
233 (let ((parent (profiler-calltree-parent tree))) 198 (let ((parent (profiler-calltree-parent tree)))
234 (if (null parent) 199 (if (null parent)
@@ -239,58 +204,47 @@ be same type."
239 "Return a child tree of ENTRY under TREE." 204 "Return a child tree of ENTRY under TREE."
240 ;; OPTIMIZED 205 ;; OPTIMIZED
241 (let (result (children (profiler-calltree-children tree))) 206 (let (result (children (profiler-calltree-children tree)))
207 ;; FIXME: Use `assoc'.
242 (while (and children (null result)) 208 (while (and children (null result))
243 (let ((child (car children))) 209 (let ((child (car children)))
244 (when (profiler-entry= (profiler-calltree-entry child) entry) 210 (when (equal (profiler-calltree-entry child) entry)
245 (setq result child)) 211 (setq result child))
246 (setq children (cdr children)))) 212 (setq children (cdr children))))
247 result)) 213 result))
248 214
249(defun profiler-calltree-walk (calltree function &rest args) 215(defun profiler-calltree-walk (calltree function)
250 (apply function calltree args) 216 (funcall function calltree)
251 (dolist (child (profiler-calltree-children calltree)) 217 (dolist (child (profiler-calltree-children calltree))
252 (apply 'profiler-calltree-walk child function args))) 218 (profiler-calltree-walk child function)))
253 219
254(defun profiler-calltree-build-1 (tree log &optional reverse) 220(defun profiler-calltree-build-1 (tree log &optional reverse)
255 (dolist (slot (profiler-log-slots log)) 221 (maphash
256 (let ((backtrace (profiler-slot-backtrace slot)) 222 (lambda (backtrace count)
257 (count (profiler-slot-count slot)) 223 (when (vectorp backtrace)
258 (elapsed (profiler-slot-elapsed slot)) 224 (let ((node tree)
259 (node tree)) 225 (max (length backtrace)))
260 (dolist (entry (if reverse 226 (dotimes (i max)
261 backtrace 227 (let ((entry (aref backtrace (if reverse i (- max i 1)))))
262 (profiler-backtrace-reverse backtrace))) 228 (when entry
263 (let ((child (profiler-calltree-find node entry))) 229 (let ((child (profiler-calltree-find node entry)))
264 (unless child 230 (unless child
265 (setq child (profiler-make-calltree :entry entry :parent node)) 231 (setq child (profiler-make-calltree
266 (push child (profiler-calltree-children node))) 232 :entry entry :parent node))
267 (cl-incf (profiler-calltree-count child) count) 233 (push child (profiler-calltree-children node)))
268 (cl-incf (profiler-calltree-elapsed child) elapsed) 234 (cl-incf (profiler-calltree-count child) count)
269 (setq node child)))))) 235 (setq node child))))))))
270 236 log))
271(defun profiler-calltree-compute-percentages-1 (node total-count total-elapsed)
272 (unless (zerop total-count)
273 (setf (profiler-calltree-count-percent node)
274 (profiler-format-percent (profiler-calltree-count node)
275 total-count)))
276 (unless (zerop total-elapsed)
277 (setf (profiler-calltree-elapsed-percent node)
278 (profiler-format-percent (profiler-calltree-elapsed node)
279 total-elapsed))))
280 237
281(defun profiler-calltree-compute-percentages (tree) 238(defun profiler-calltree-compute-percentages (tree)
282 (let ((total-count 0) 239 (let ((total-count 0))
283 (total-elapsed 0))
284 (dolist (child (profiler-calltree-children tree)) 240 (dolist (child (profiler-calltree-children tree))
285 (if (eq (profiler-calltree-entry child) 'gc) 241 (cl-incf total-count (profiler-calltree-count child)))
286 (profiler-calltree-compute-percentages child) 242 (unless (zerop total-count)
287 (cl-incf total-count (profiler-calltree-count child)) 243 (profiler-calltree-walk
288 (cl-incf total-elapsed (profiler-calltree-elapsed child)))) 244 tree (lambda (node)
289 (dolist (child (profiler-calltree-children tree)) 245 (setf (profiler-calltree-count-percent node)
290 (unless (eq (profiler-calltree-entry child) 'gc) 246 (profiler-format-percent (profiler-calltree-count node)
291 (profiler-calltree-walk 247 total-count)))))))
292 child 'profiler-calltree-compute-percentages-1
293 total-count total-elapsed)))))
294 248
295(cl-defun profiler-calltree-build (log &key reverse) 249(cl-defun profiler-calltree-build (log &key reverse)
296 (let ((tree (profiler-make-calltree))) 250 (let ((tree (profiler-make-calltree)))
@@ -332,14 +286,14 @@ be same type."
332 (19 right ((14 right profiler-format-nbytes) 286 (19 right ((14 right profiler-format-nbytes)
333 (5 right))))) 287 (5 right)))))
334 288
335(defvar profiler-report-log nil 289(defvar-local profiler-report-log nil
336 "The current profiler log.") 290 "The current profiler log.")
337 291
338(defvar profiler-report-reversed nil 292(defvar-local profiler-report-reversed nil
339 "True if calltree is rendered in bottom-up. Do not touch this 293 "True if calltree is rendered in bottom-up. Do not touch this
340variable directly.") 294variable directly.")
341 295
342(defvar profiler-report-order nil 296(defvar-local profiler-report-order nil
343 "The value can be `ascending' or `descending'. Do not touch 297 "The value can be `ascending' or `descending'. Do not touch
344this variable directly.") 298this variable directly.")
345 299
@@ -347,8 +301,6 @@ this variable directly.")
347 (let ((string (cond 301 (let ((string (cond
348 ((eq entry t) 302 ((eq entry t)
349 "Others") 303 "Others")
350 ((eq entry 'gc)
351 "Garbage Collection")
352 ((and (symbolp entry) 304 ((and (symbolp entry)
353 (fboundp entry)) 305 (fboundp entry))
354 (propertize (symbol-name entry) 306 (propertize (symbol-name entry)
@@ -357,7 +309,7 @@ this variable directly.")
357 'help-echo "mouse-2 or RET jumps to definition")) 309 'help-echo "mouse-2 or RET jumps to definition"))
358 (t 310 (t
359 (profiler-entry-format entry))))) 311 (profiler-entry-format entry)))))
360 (propertize string 'entry entry))) 312 (propertize string 'profiler-entry entry)))
361 313
362(defun profiler-report-make-name-part (tree) 314(defun profiler-report-make-name-part (tree)
363 (let* ((entry (profiler-calltree-entry tree)) 315 (let* ((entry (profiler-calltree-entry tree))
@@ -377,31 +329,18 @@ this variable directly.")
377(defun profiler-report-line-format (tree) 329(defun profiler-report-line-format (tree)
378 (let ((diff-p (profiler-log-diff-p profiler-report-log)) 330 (let ((diff-p (profiler-log-diff-p profiler-report-log))
379 (name-part (profiler-report-make-name-part tree)) 331 (name-part (profiler-report-make-name-part tree))
380 (elapsed (profiler-calltree-elapsed tree))
381 (elapsed-percent (profiler-calltree-elapsed-percent tree))
382 (count (profiler-calltree-count tree)) 332 (count (profiler-calltree-count tree))
383 (count-percent (profiler-calltree-count-percent tree))) 333 (count-percent (profiler-calltree-count-percent tree)))
384 (cl-ecase (profiler-log-type profiler-report-log) 334 (profiler-format (cl-ecase (profiler-log-type profiler-report-log)
385 (sample 335 (cpu profiler-report-sample-line-format)
386 (if diff-p 336 (memory profiler-report-memory-line-format))
387 (profiler-format profiler-report-sample-line-format 337 name-part
388 name-part 338 (if diff-p
389 (list (if (> elapsed 0) 339 (list (if (> count 0)
390 (format "+%s" elapsed) 340 (format "+%s" count)
391 elapsed) 341 count)
392 "")) 342 "")
393 (profiler-format profiler-report-sample-line-format 343 (list count count-percent)))))
394 name-part (list elapsed elapsed-percent))))
395 (memory
396 (if diff-p
397 (profiler-format profiler-report-memory-line-format
398 name-part
399 (list (if (> count 0)
400 (format "+%s" count)
401 count)
402 ""))
403 (profiler-format profiler-report-memory-line-format
404 name-part (list count count-percent)))))))
405 344
406(defun profiler-report-insert-calltree (tree) 345(defun profiler-report-insert-calltree (tree)
407 (let ((line (profiler-report-line-format tree))) 346 (let ((line (profiler-report-line-format tree)))
@@ -416,10 +355,13 @@ this variable directly.")
416 355
417(defvar profiler-report-mode-map 356(defvar profiler-report-mode-map
418 (let ((map (make-sparse-keymap))) 357 (let ((map (make-sparse-keymap)))
358 ;; FIXME: Add menu.
419 (define-key map "n" 'profiler-report-next-entry) 359 (define-key map "n" 'profiler-report-next-entry)
420 (define-key map "p" 'profiler-report-previous-entry) 360 (define-key map "p" 'profiler-report-previous-entry)
421 (define-key map [down] 'profiler-report-next-entry) 361 ;; I find it annoying more than helpful to not be able to navigate
422 (define-key map [up] 'profiler-report-previous-entry) 362 ;; normally with the cursor keys. --Stef
363 ;; (define-key map [down] 'profiler-report-next-entry)
364 ;; (define-key map [up] 'profiler-report-previous-entry)
423 (define-key map "\r" 'profiler-report-toggle-entry) 365 (define-key map "\r" 'profiler-report-toggle-entry)
424 (define-key map "\t" 'profiler-report-toggle-entry) 366 (define-key map "\t" 'profiler-report-toggle-entry)
425 (define-key map "i" 'profiler-report-toggle-entry) 367 (define-key map "i" 'profiler-report-toggle-entry)
@@ -437,10 +379,9 @@ this variable directly.")
437 map)) 379 map))
438 380
439(defun profiler-report-make-buffer-name (log) 381(defun profiler-report-make-buffer-name (log)
440 (let ((time (format-time-string "%Y-%m-%d %T" (profiler-log-timestamp log)))) 382 (format "*%s-Profiler-Report %s*"
441 (cl-ecase (profiler-log-type log) 383 (cl-ecase (profiler-log-type log) (cpu 'CPU) (memory 'Memory))
442 (sample (format "*CPU-Profiler-Report %s*" time)) 384 (format-time-string "%Y-%m-%d %T" (profiler-log-timestamp log))))
443 (memory (format "*Memory-Profiler-Report %s*" time)))))
444 385
445(defun profiler-report-setup-buffer (log) 386(defun profiler-report-setup-buffer (log)
446 "Make a buffer for LOG and return it." 387 "Make a buffer for LOG and return it."
@@ -455,10 +396,6 @@ this variable directly.")
455 396
456(define-derived-mode profiler-report-mode special-mode "Profiler-Report" 397(define-derived-mode profiler-report-mode special-mode "Profiler-Report"
457 "Profiler Report Mode." 398 "Profiler Report Mode."
458 (make-local-variable 'profiler-report-log)
459 (make-local-variable 'profiler-report-reversed)
460 (make-local-variable 'profiler-report-order)
461 (use-local-map profiler-report-mode-map)
462 (setq buffer-read-only t 399 (setq buffer-read-only t
463 buffer-undo-list t 400 buffer-undo-list t
464 truncate-lines t)) 401 truncate-lines t))
@@ -470,7 +407,8 @@ this variable directly.")
470 (get-text-property (point) 'calltree)) 407 (get-text-property (point) 'calltree))
471 408
472(defun profiler-report-move-to-entry () 409(defun profiler-report-move-to-entry ()
473 (let ((point (next-single-property-change (line-beginning-position) 'entry))) 410 (let ((point (next-single-property-change (line-beginning-position)
411 'profiler-entry)))
474 (if point 412 (if point
475 (goto-char point) 413 (goto-char point)
476 (back-to-indentation)))) 414 (back-to-indentation))))
@@ -496,7 +434,7 @@ this variable directly.")
496 (line-end-position) t) 434 (line-end-position) t)
497 (let ((tree (profiler-report-calltree-at-point))) 435 (let ((tree (profiler-report-calltree-at-point)))
498 (when tree 436 (when tree
499 (let ((buffer-read-only nil)) 437 (let ((inhibit-read-only t))
500 (replace-match (concat profiler-report-open-mark " ")) 438 (replace-match (concat profiler-report-open-mark " "))
501 (forward-line) 439 (forward-line)
502 (profiler-report-insert-calltree-children tree) 440 (profiler-report-insert-calltree-children tree)
@@ -514,7 +452,7 @@ this variable directly.")
514 (start (line-beginning-position 2)) 452 (start (line-beginning-position 2))
515 d) 453 d)
516 (when tree 454 (when tree
517 (let ((buffer-read-only nil)) 455 (let ((inhibit-read-only t))
518 (replace-match (concat profiler-report-closed-mark " ")) 456 (replace-match (concat profiler-report-closed-mark " "))
519 (while (and (eq (forward-line) 0) 457 (while (and (eq (forward-line) 0)
520 (let ((child (get-text-property (point) 'calltree))) 458 (let ((child (get-text-property (point) 'calltree)))
@@ -549,29 +487,25 @@ otherwise collapse."
549 (require 'help-fns) 487 (require 'help-fns)
550 (describe-function entry))))) 488 (describe-function entry)))))
551 489
552(cl-defun profiler-report-render-calltree-1 (log &key reverse (order 'descending)) 490(cl-defun profiler-report-render-calltree-1
491 (log &key reverse (order 'descending))
553 (let ((calltree (profiler-calltree-build profiler-report-log 492 (let ((calltree (profiler-calltree-build profiler-report-log
554 :reverse reverse))) 493 :reverse reverse)))
555 (cl-ecase (profiler-log-type log) 494 (setq header-line-format
556 (sample 495 (cl-ecase (profiler-log-type log)
557 (setq header-line-format 496 (cpu
558 (profiler-report-header-line-format 497 (profiler-report-header-line-format
559 profiler-report-sample-line-format 498 profiler-report-sample-line-format
560 "Function" (list "Time (ms)" "%"))) 499 "Function" (list "Time (ms)" "%")))
561 (let ((predicate (cl-ecase order 500 (memory
562 (ascending 'profiler-calltree-elapsed<)
563 (descending 'profiler-calltree-elapsed>))))
564 (profiler-calltree-sort calltree predicate)))
565 (memory
566 (setq header-line-format
567 (profiler-report-header-line-format 501 (profiler-report-header-line-format
568 profiler-report-memory-line-format 502 profiler-report-memory-line-format
569 "Function" (list "Bytes" "%"))) 503 "Function" (list "Bytes" "%")))))
570 (let ((predicate (cl-ecase order 504 (let ((predicate (cl-ecase order
571 (ascending 'profiler-calltree-count<) 505 (ascending #'profiler-calltree-count<)
572 (descending 'profiler-calltree-count>)))) 506 (descending #'profiler-calltree-count>))))
573 (profiler-calltree-sort calltree predicate)))) 507 (profiler-calltree-sort calltree predicate))
574 (let ((buffer-read-only nil)) 508 (let ((inhibit-read-only t))
575 (erase-buffer) 509 (erase-buffer)
576 (profiler-report-insert-calltree-children calltree) 510 (profiler-report-insert-calltree-children calltree)
577 (goto-char (point-min)) 511 (goto-char (point-min))
@@ -632,19 +566,15 @@ otherwise collapse."
632 566
633;;; Profiler commands 567;;; Profiler commands
634 568
635(defcustom profiler-sample-interval 10
636 "Default sample interval in millisecond."
637 :type 'integer
638 :group 'profiler)
639
640;;;###autoload 569;;;###autoload
641(defun profiler-start (mode) 570(defun profiler-start (mode)
642 "Start/restart profilers. MODE can be one of `cpu', `mem', 571 "Start/restart profilers.
643and `cpu+mem'. If MODE is `cpu' or `cpu+mem', sample profiler 572MODE can be one of `cpu', `mem', or `cpu+mem'.
644will be started. Also, if MODE is `mem' or `cpu+mem', then 573If MODE is `cpu' or `cpu+mem', time-based profiler will be started.
645memory profiler will be started." 574Also, if MODE is `mem' or `cpu+mem', then memory profiler will be started."
646 (interactive 575 (interactive
647 (list (intern (completing-read "Mode: " '("cpu" "mem" "cpu+mem") 576 (list (intern (completing-read "Mode (default cpu): "
577 '("cpu" "mem" "cpu+mem")
648 nil t nil nil "cpu")))) 578 nil t nil nil "cpu"))))
649 (cl-ecase mode 579 (cl-ecase mode
650 (cpu 580 (cpu
@@ -679,25 +609,29 @@ memory profiler will be started."
679(defun profiler-reset () 609(defun profiler-reset ()
680 "Reset profiler log." 610 "Reset profiler log."
681 (interactive) 611 (interactive)
682 (sample-profiler-reset) 612 (ignore (sample-profiler-log))
683 (memory-profiler-reset) 613 (ignore (memory-profiler-log))
684 t) 614 t)
685 615
686(defun sample-profiler-report () 616(defun profiler--report-cpu ()
687 (let ((sample-log (sample-profiler-log))) 617 (let ((log (sample-profiler-log)))
688 (when sample-log 618 (when log
689 (profiler-report-log sample-log)))) 619 (puthash 'type 'cpu log)
620 (puthash 'timestamp (current-time) log)
621 (profiler-report-log log))))
690 622
691(defun memory-profiler-report () 623(defun profiler--report-memory ()
692 (let ((memory-log (memory-profiler-log))) 624 (let ((log (memory-profiler-log)))
693 (when memory-log 625 (when log
694 (profiler-report-log memory-log)))) 626 (puthash 'type 'memory log)
627 (puthash 'timestamp (current-time) log)
628 (profiler-report-log log))))
695 629
696(defun profiler-report () 630(defun profiler-report ()
697 "Report profiling results." 631 "Report profiling results."
698 (interactive) 632 (interactive)
699 (sample-profiler-report) 633 (profiler--report-cpu)
700 (memory-profiler-report)) 634 (profiler--report-memory))
701 635
702;;;###autoload 636;;;###autoload
703(defun profiler-find-log (filename) 637(defun profiler-find-log (filename)
@@ -709,25 +643,23 @@ memory profiler will be started."
709 643
710;;; Profiling helpers 644;;; Profiling helpers
711 645
712(cl-defmacro with-sample-profiling ((&key (interval profiler-sample-interval)) &rest body) 646(cl-defmacro with-sample-profiling ((&key interval) &rest body)
713 `(progn 647 `(unwind-protect
714 (sample-profiler-start ,interval) 648 (progn
715 (sample-profiler-reset) 649 (ignore (sample-profiler-log))
716 (unwind-protect 650 (sample-profiler-start ,interval)
717 (progn ,@body) 651 ,@body)
718 (sample-profiler-stop) 652 (sample-profiler-stop)
719 (sample-profiler-report) 653 (profiler--report-cpu)))
720 (sample-profiler-reset)))) 654
721 655(defmacro with-memory-profiling (&rest body)
722(cl-defmacro with-memory-profiling (() &rest body) 656 `(unwind-protect
723 `(progn 657 (progn
724 (memory-profiler-start) 658 (ignore (memory-profiler-log))
725 (memory-profiler-reset) 659 (memory-profiler-start)
726 (unwind-protect 660 ,@body)
727 (progn ,@body) 661 (memory-profiler-stop)
728 (memory-profiler-stop) 662 (profiler--report-memory)))
729 (memory-profiler-report)
730 (memory-profiler-reset))))
731 663
732(provide 'profiler) 664(provide 'profiler)
733;;; profiler.el ends here 665;;; profiler.el ends here