diff options
| author | Stefan Monnier | 2012-09-24 10:38:10 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2012-09-24 10:38:10 -0400 |
| commit | 3d80c99f3817bf5eccd6acc6a79498a4fde979a4 (patch) | |
| tree | 5377692a9d9b96157a42b8ae693a8f7d18a8bc85 /lisp | |
| parent | 0970d85fef9830e3b8e5cbfbdc04dbf00cc4c027 (diff) | |
| download | emacs-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/ChangeLog | 48 | ||||
| -rw-r--r-- | lisp/profiler.el | 366 |
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 @@ | |||
| 1 | 2012-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 | |||
| 1 | 2012-08-22 Tomohiro Matsuyama <tomo@cx4a.org> | 33 | 2012-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 | ||
| 42 | 2012-08-21 Glenn Morris <rgm@gnu.org> | 74 | 2012-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 | ||
| 170 | 2012-08-17 Michael Albinus <michael.albinus@gmx.de> | 202 | 2012-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 | ||
| 175 | 2012-08-17 Chong Yidong <cyd@gnu.org> | 207 | 2012-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 | ||
| 204 | 2012-08-16 Phil Sainty <psainty@orcon.net.nz> (tiny change) | 236 | 2012-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 |
| 102 | function name of a function itself." | 99 | function 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 |
| 340 | variable directly.") | 294 | variable 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 |
| 344 | this variable directly.") | 298 | this 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. |
| 643 | and `cpu+mem'. If MODE is `cpu' or `cpu+mem', sample profiler | 572 | MODE can be one of `cpu', `mem', or `cpu+mem'. |
| 644 | will be started. Also, if MODE is `mem' or `cpu+mem', then | 573 | If MODE is `cpu' or `cpu+mem', time-based profiler will be started. |
| 645 | memory profiler will be started." | 574 | Also, 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 |