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.
This commit is contained in:
Stefan Monnier 2012-09-24 10:38:10 -04:00
parent 0970d85fef
commit 3d80c99f38
10 changed files with 429 additions and 1124 deletions

View file

@ -1,5 +1,4 @@
((nil . ((tab-width . 8)
(indent-tabs-mode . t)
(sentence-end-double-space . t)
(fill-column . 70)))
(c-mode . ((c-file-style . "GNU")))

View file

@ -1,3 +1,7 @@
2012-09-24 Stefan Monnier <monnier@iro.umontreal.ca>
* .dir-locals.el (indent-tabs-mode): Remove personal preference.
2012-08-21 Paul Eggert <eggert@cs.ucla.edu>
Merge from gnulib, incorporating:

View file

@ -1,3 +1,35 @@
2012-09-24 Stefan Monnier <monnier@iro.umontreal.ca>
* 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.
2012-08-22 Tomohiro Matsuyama <tomo@cx4a.org>
* profiler.el: Switch to cl-lib.
@ -35,8 +67,8 @@
* window.el (window-point-1, set-window-point-1): Remove.
(window-in-direction, record-window-buffer)
(set-window-buffer-start-and-point, split-window-below)
(window--state-get-1, display-buffer-record-window): Replace
calls to window-point-1 and set-window-point-1 by calls to
(window--state-get-1, display-buffer-record-window):
Replace calls to window-point-1 and set-window-point-1 by calls to
window-point and set-window-point respectively.
2012-08-21 Glenn Morris <rgm@gnu.org>
@ -154,8 +186,8 @@
(yank-excluded-properties): Add font-lock-face and category.
(yank): Doc fix.
* subr.el (remove-yank-excluded-properties): Obey
yank-handled-properties. The special handling of font-lock-face
* subr.el (remove-yank-excluded-properties):
Obey yank-handled-properties. The special handling of font-lock-face
and category is now done this way, instead of being hard-coded.
(insert-for-yank-1): Remove font-lock-face handling.
(yank-handle-font-lock-face-property)
@ -169,8 +201,8 @@
2012-08-17 Michael Albinus <michael.albinus@gmx.de>
* net/tramp-sh.el (tramp-sh-handle-start-file-process): Eliminate
superfluous prompt. (Bug#12203)
* net/tramp-sh.el (tramp-sh-handle-start-file-process):
Eliminate superfluous prompt. (Bug#12203)
2012-08-17 Chong Yidong <cyd@gnu.org>
@ -197,8 +229,8 @@
(next-buffer, previous-buffer, split-window, balance-windows-2)
(set-window-text-height, window-buffer-height)
(fit-window-to-buffer, shrink-window-if-larger-than-buffer)
(truncated-partial-width-window-p): Minor code adjustments. In
doc-strings state whether the argument window has to denote a
(truncated-partial-width-window-p): Minor code adjustments.
In doc-strings state whether the argument window has to denote a
live, valid or any window.
2012-08-16 Phil Sainty <psainty@orcon.net.nz> (tiny change)

View file

@ -32,7 +32,11 @@
:group 'lisp
:prefix "profiler-")
(defcustom profiler-sample-interval 1
"Default sample interval in millisecond."
:type 'integer
:group 'profiler)
;;; Utilities
(defun profiler-ensure-string (object)
@ -90,55 +94,34 @@
;;; Entries
(defun profiler-entry= (entry1 entry2)
"Return t if ENTRY1 and ENTRY2 are same."
(or (eq entry1 entry2)
(and (stringp entry1)
(stringp entry2)
(string= entry1 entry2))))
(defun profiler-entry-format (entry)
"Format ENTRY in human readable string. ENTRY would be a
function name of a function itself."
(cond ((and (consp entry)
(or (eq (car entry) 'lambda)
(eq (car entry) 'closure)))
(format "#<closure 0x%x>" (sxhash entry)))
((eq (type-of entry) 'compiled-function)
(cond ((memq (car-safe entry) '(closure lambda))
(format "#<lambda 0x%x>" (sxhash entry)))
((byte-code-function-p entry)
(format "#<compiled 0x%x>" (sxhash entry)))
((subrp entry)
(subr-name entry))
((symbolp entry)
(symbol-name entry))
((stringp entry)
entry)
((or (subrp entry) (symbolp entry) (stringp entry))
(format "%s" entry))
(t
(format "#<unknown 0x%x>" (sxhash entry)))))
;;; Backtrace data structure
(defun profiler-backtrace-reverse (backtrace)
(cl-case (car backtrace)
((t gc)
;; Make sure Others node and GC node always be at top.
(cons (car backtrace)
(reverse (cdr backtrace))))
(t (reverse backtrace))))
;;; Slot data structure
(cl-defstruct (profiler-slot (:type list)
(:constructor profiler-make-slot))
backtrace count elapsed)
;;; Log data structure
(cl-defstruct (profiler-log (:type list)
(:constructor profiler-make-log))
type diff-p timestamp slots)
;; The C code returns the log in the form of a hash-table where the keys are
;; vectors (of size profiler-max-stack-depth, holding truncated
;; backtraces, where the first element is the top of the stack) and
;; the values are integers (which count how many times this backtrace
;; has been seen, multiplied by a "weight factor" which is either the
;; sample-interval or the memory being allocated).
;; We extend it by adding a few other entries to the hash-table, most notably:
;; - Key `type' has a value indicating the kind of log (`memory' or `cpu').
;; - Key `timestamp' has a value giving the time when the log was obtained.
;; - Key `diff-p' indicates if this log represents a diff between two logs.
(defun profiler-log-timestamp (log) (gethash 'timestamp log))
(defun profiler-log-type (log) (gethash 'type log))
(defun profiler-log-diff-p (log) (gethash 'diff-p log))
(defun profiler-log-diff (log1 log2)
"Compare LOG1 with LOG2 and return a diff log. Both logs must
@ -146,16 +129,17 @@ be same type."
(unless (eq (profiler-log-type log1)
(profiler-log-type log2))
(error "Can't compare different type of logs"))
(let ((slots (profiler-log-slots log2)))
(dolist (slot (profiler-log-slots log1))
(push (profiler-make-slot :backtrace (profiler-slot-backtrace slot)
:count (- (profiler-slot-count slot))
:elapsed (- (profiler-slot-elapsed slot)))
slots))
(profiler-make-log :type (profiler-log-type log1)
:diff-p t
:timestamp (current-time)
:slots slots)))
(let ((newlog (make-hash-table :test 'equal)))
;; Make a copy of `log1' into `newlog'.
(maphash (lambda (backtrace count) (puthash backtrace count newlog))
log1)
(puthash 'diff-p t newlog)
(maphash (lambda (backtrace count)
(when (vectorp backtrace)
(puthash backtrace (- (gethash backtrace log1 0) count)
newlog)))
log2)
newlog))
(defun profiler-log-fixup-entry (entry)
(if (symbolp entry)
@ -165,21 +149,16 @@ be same type."
(defun profiler-log-fixup-backtrace (backtrace)
(mapcar 'profiler-log-fixup-entry backtrace))
(defun profiler-log-fixup-slot (slot)
(let ((backtrace (profiler-slot-backtrace slot)))
(profiler-make-slot :backtrace (profiler-log-fixup-backtrace backtrace)
:count (profiler-slot-count slot)
:elapsed (profiler-slot-elapsed slot))))
(defun profiler-log-fixup (log)
"Fixup LOG so that the log could be serialized into file."
(cl-loop for slot in (profiler-log-slots log)
collect (profiler-log-fixup-slot slot) into slots
finally return
(profiler-make-log :type (profiler-log-type log)
:diff-p (profiler-log-diff-p log)
:timestamp (profiler-log-timestamp log)
:slots slots)))
(let ((newlog (make-hash-table :test 'equal)))
(maphash (lambda (backtrace count)
(puthash (if (not (vectorp backtrace))
backtrace
(profiler-log-fixup-backtrace backtrace))
count newlog))
log)
newlog))
(defun profiler-log-write-file (log filename &optional confirm)
"Write LOG into FILENAME."
@ -201,7 +180,6 @@ be same type."
(cl-defstruct (profiler-calltree (:constructor profiler-make-calltree))
entry
(count 0) (count-percent "")
(elapsed 0) (elapsed-percent "")
parent children)
(defun profiler-calltree-leaf-p (tree)
@ -210,25 +188,12 @@ be same type."
(defun profiler-calltree-count< (a b)
(cond ((eq (profiler-calltree-entry a) t) t)
((eq (profiler-calltree-entry b) t) nil)
((eq (profiler-calltree-entry a) 'gc) t)
((eq (profiler-calltree-entry b) 'gc) nil)
(t (< (profiler-calltree-count a)
(profiler-calltree-count b)))))
(defun profiler-calltree-count> (a b)
(not (profiler-calltree-count< a b)))
(defun profiler-calltree-elapsed< (a b)
(cond ((eq (profiler-calltree-entry a) t) t)
((eq (profiler-calltree-entry b) t) nil)
((eq (profiler-calltree-entry a) 'gc) t)
((eq (profiler-calltree-entry b) 'gc) nil)
(t (< (profiler-calltree-elapsed a)
(profiler-calltree-elapsed b)))))
(defun profiler-calltree-elapsed> (a b)
(not (profiler-calltree-elapsed< a b)))
(defun profiler-calltree-depth (tree)
(let ((parent (profiler-calltree-parent tree)))
(if (null parent)
@ -239,58 +204,47 @@ be same type."
"Return a child tree of ENTRY under TREE."
;; OPTIMIZED
(let (result (children (profiler-calltree-children tree)))
;; FIXME: Use `assoc'.
(while (and children (null result))
(let ((child (car children)))
(when (profiler-entry= (profiler-calltree-entry child) entry)
(when (equal (profiler-calltree-entry child) entry)
(setq result child))
(setq children (cdr children))))
result))
(defun profiler-calltree-walk (calltree function &rest args)
(apply function calltree args)
(defun profiler-calltree-walk (calltree function)
(funcall function calltree)
(dolist (child (profiler-calltree-children calltree))
(apply 'profiler-calltree-walk child function args)))
(profiler-calltree-walk child function)))
(defun profiler-calltree-build-1 (tree log &optional reverse)
(dolist (slot (profiler-log-slots log))
(let ((backtrace (profiler-slot-backtrace slot))
(count (profiler-slot-count slot))
(elapsed (profiler-slot-elapsed slot))
(node tree))
(dolist (entry (if reverse
backtrace
(profiler-backtrace-reverse backtrace)))
(let ((child (profiler-calltree-find node entry)))
(unless child
(setq child (profiler-make-calltree :entry entry :parent node))
(push child (profiler-calltree-children node)))
(cl-incf (profiler-calltree-count child) count)
(cl-incf (profiler-calltree-elapsed child) elapsed)
(setq node child))))))
(defun profiler-calltree-compute-percentages-1 (node total-count total-elapsed)
(unless (zerop total-count)
(setf (profiler-calltree-count-percent node)
(profiler-format-percent (profiler-calltree-count node)
total-count)))
(unless (zerop total-elapsed)
(setf (profiler-calltree-elapsed-percent node)
(profiler-format-percent (profiler-calltree-elapsed node)
total-elapsed))))
(maphash
(lambda (backtrace count)
(when (vectorp backtrace)
(let ((node tree)
(max (length backtrace)))
(dotimes (i max)
(let ((entry (aref backtrace (if reverse i (- max i 1)))))
(when entry
(let ((child (profiler-calltree-find node entry)))
(unless child
(setq child (profiler-make-calltree
:entry entry :parent node))
(push child (profiler-calltree-children node)))
(cl-incf (profiler-calltree-count child) count)
(setq node child))))))))
log))
(defun profiler-calltree-compute-percentages (tree)
(let ((total-count 0)
(total-elapsed 0))
(let ((total-count 0))
(dolist (child (profiler-calltree-children tree))
(if (eq (profiler-calltree-entry child) 'gc)
(profiler-calltree-compute-percentages child)
(cl-incf total-count (profiler-calltree-count child))
(cl-incf total-elapsed (profiler-calltree-elapsed child))))
(dolist (child (profiler-calltree-children tree))
(unless (eq (profiler-calltree-entry child) 'gc)
(profiler-calltree-walk
child 'profiler-calltree-compute-percentages-1
total-count total-elapsed)))))
(cl-incf total-count (profiler-calltree-count child)))
(unless (zerop total-count)
(profiler-calltree-walk
tree (lambda (node)
(setf (profiler-calltree-count-percent node)
(profiler-format-percent (profiler-calltree-count node)
total-count)))))))
(cl-defun profiler-calltree-build (log &key reverse)
(let ((tree (profiler-make-calltree)))
@ -332,14 +286,14 @@ be same type."
(19 right ((14 right profiler-format-nbytes)
(5 right)))))
(defvar profiler-report-log nil
(defvar-local profiler-report-log nil
"The current profiler log.")
(defvar profiler-report-reversed nil
(defvar-local profiler-report-reversed nil
"True if calltree is rendered in bottom-up. Do not touch this
variable directly.")
(defvar profiler-report-order nil
(defvar-local profiler-report-order nil
"The value can be `ascending' or `descending'. Do not touch
this variable directly.")
@ -347,8 +301,6 @@ this variable directly.")
(let ((string (cond
((eq entry t)
"Others")
((eq entry 'gc)
"Garbage Collection")
((and (symbolp entry)
(fboundp entry))
(propertize (symbol-name entry)
@ -357,7 +309,7 @@ this variable directly.")
'help-echo "mouse-2 or RET jumps to definition"))
(t
(profiler-entry-format entry)))))
(propertize string 'entry entry)))
(propertize string 'profiler-entry entry)))
(defun profiler-report-make-name-part (tree)
(let* ((entry (profiler-calltree-entry tree))
@ -377,31 +329,18 @@ this variable directly.")
(defun profiler-report-line-format (tree)
(let ((diff-p (profiler-log-diff-p profiler-report-log))
(name-part (profiler-report-make-name-part tree))
(elapsed (profiler-calltree-elapsed tree))
(elapsed-percent (profiler-calltree-elapsed-percent tree))
(count (profiler-calltree-count tree))
(count-percent (profiler-calltree-count-percent tree)))
(cl-ecase (profiler-log-type profiler-report-log)
(sample
(if diff-p
(profiler-format profiler-report-sample-line-format
name-part
(list (if (> elapsed 0)
(format "+%s" elapsed)
elapsed)
""))
(profiler-format profiler-report-sample-line-format
name-part (list elapsed elapsed-percent))))
(memory
(if diff-p
(profiler-format profiler-report-memory-line-format
name-part
(list (if (> count 0)
(format "+%s" count)
count)
""))
(profiler-format profiler-report-memory-line-format
name-part (list count count-percent)))))))
(profiler-format (cl-ecase (profiler-log-type profiler-report-log)
(cpu profiler-report-sample-line-format)
(memory profiler-report-memory-line-format))
name-part
(if diff-p
(list (if (> count 0)
(format "+%s" count)
count)
"")
(list count count-percent)))))
(defun profiler-report-insert-calltree (tree)
(let ((line (profiler-report-line-format tree)))
@ -416,10 +355,13 @@ this variable directly.")
(defvar profiler-report-mode-map
(let ((map (make-sparse-keymap)))
;; FIXME: Add menu.
(define-key map "n" 'profiler-report-next-entry)
(define-key map "p" 'profiler-report-previous-entry)
(define-key map [down] 'profiler-report-next-entry)
(define-key map [up] 'profiler-report-previous-entry)
;; I find it annoying more than helpful to not be able to navigate
;; normally with the cursor keys. --Stef
;; (define-key map [down] 'profiler-report-next-entry)
;; (define-key map [up] 'profiler-report-previous-entry)
(define-key map "\r" 'profiler-report-toggle-entry)
(define-key map "\t" 'profiler-report-toggle-entry)
(define-key map "i" 'profiler-report-toggle-entry)
@ -437,10 +379,9 @@ this variable directly.")
map))
(defun profiler-report-make-buffer-name (log)
(let ((time (format-time-string "%Y-%m-%d %T" (profiler-log-timestamp log))))
(cl-ecase (profiler-log-type log)
(sample (format "*CPU-Profiler-Report %s*" time))
(memory (format "*Memory-Profiler-Report %s*" time)))))
(format "*%s-Profiler-Report %s*"
(cl-ecase (profiler-log-type log) (cpu 'CPU) (memory 'Memory))
(format-time-string "%Y-%m-%d %T" (profiler-log-timestamp log))))
(defun profiler-report-setup-buffer (log)
"Make a buffer for LOG and return it."
@ -455,10 +396,6 @@ this variable directly.")
(define-derived-mode profiler-report-mode special-mode "Profiler-Report"
"Profiler Report Mode."
(make-local-variable 'profiler-report-log)
(make-local-variable 'profiler-report-reversed)
(make-local-variable 'profiler-report-order)
(use-local-map profiler-report-mode-map)
(setq buffer-read-only t
buffer-undo-list t
truncate-lines t))
@ -470,7 +407,8 @@ this variable directly.")
(get-text-property (point) 'calltree))
(defun profiler-report-move-to-entry ()
(let ((point (next-single-property-change (line-beginning-position) 'entry)))
(let ((point (next-single-property-change (line-beginning-position)
'profiler-entry)))
(if point
(goto-char point)
(back-to-indentation))))
@ -496,7 +434,7 @@ this variable directly.")
(line-end-position) t)
(let ((tree (profiler-report-calltree-at-point)))
(when tree
(let ((buffer-read-only nil))
(let ((inhibit-read-only t))
(replace-match (concat profiler-report-open-mark " "))
(forward-line)
(profiler-report-insert-calltree-children tree)
@ -514,7 +452,7 @@ this variable directly.")
(start (line-beginning-position 2))
d)
(when tree
(let ((buffer-read-only nil))
(let ((inhibit-read-only t))
(replace-match (concat profiler-report-closed-mark " "))
(while (and (eq (forward-line) 0)
(let ((child (get-text-property (point) 'calltree)))
@ -549,29 +487,25 @@ otherwise collapse."
(require 'help-fns)
(describe-function entry)))))
(cl-defun profiler-report-render-calltree-1 (log &key reverse (order 'descending))
(cl-defun profiler-report-render-calltree-1
(log &key reverse (order 'descending))
(let ((calltree (profiler-calltree-build profiler-report-log
:reverse reverse)))
(cl-ecase (profiler-log-type log)
(sample
(setq header-line-format
(setq header-line-format
(cl-ecase (profiler-log-type log)
(cpu
(profiler-report-header-line-format
profiler-report-sample-line-format
"Function" (list "Time (ms)" "%")))
(let ((predicate (cl-ecase order
(ascending 'profiler-calltree-elapsed<)
(descending 'profiler-calltree-elapsed>))))
(profiler-calltree-sort calltree predicate)))
(memory
(setq header-line-format
(memory
(profiler-report-header-line-format
profiler-report-memory-line-format
"Function" (list "Bytes" "%")))
(let ((predicate (cl-ecase order
(ascending 'profiler-calltree-count<)
(descending 'profiler-calltree-count>))))
(profiler-calltree-sort calltree predicate))))
(let ((buffer-read-only nil))
"Function" (list "Bytes" "%")))))
(let ((predicate (cl-ecase order
(ascending #'profiler-calltree-count<)
(descending #'profiler-calltree-count>))))
(profiler-calltree-sort calltree predicate))
(let ((inhibit-read-only t))
(erase-buffer)
(profiler-report-insert-calltree-children calltree)
(goto-char (point-min))
@ -632,19 +566,15 @@ otherwise collapse."
;;; Profiler commands
(defcustom profiler-sample-interval 10
"Default sample interval in millisecond."
:type 'integer
:group 'profiler)
;;;###autoload
(defun profiler-start (mode)
"Start/restart profilers. MODE can be one of `cpu', `mem',
and `cpu+mem'. If MODE is `cpu' or `cpu+mem', sample profiler
will be started. Also, if MODE is `mem' or `cpu+mem', then
memory profiler will be started."
"Start/restart profilers.
MODE can be one of `cpu', `mem', or `cpu+mem'.
If MODE is `cpu' or `cpu+mem', time-based profiler will be started.
Also, if MODE is `mem' or `cpu+mem', then memory profiler will be started."
(interactive
(list (intern (completing-read "Mode: " '("cpu" "mem" "cpu+mem")
(list (intern (completing-read "Mode (default cpu): "
'("cpu" "mem" "cpu+mem")
nil t nil nil "cpu"))))
(cl-ecase mode
(cpu
@ -679,25 +609,29 @@ memory profiler will be started."
(defun profiler-reset ()
"Reset profiler log."
(interactive)
(sample-profiler-reset)
(memory-profiler-reset)
(ignore (sample-profiler-log))
(ignore (memory-profiler-log))
t)
(defun sample-profiler-report ()
(let ((sample-log (sample-profiler-log)))
(when sample-log
(profiler-report-log sample-log))))
(defun profiler--report-cpu ()
(let ((log (sample-profiler-log)))
(when log
(puthash 'type 'cpu log)
(puthash 'timestamp (current-time) log)
(profiler-report-log log))))
(defun memory-profiler-report ()
(let ((memory-log (memory-profiler-log)))
(when memory-log
(profiler-report-log memory-log))))
(defun profiler--report-memory ()
(let ((log (memory-profiler-log)))
(when log
(puthash 'type 'memory log)
(puthash 'timestamp (current-time) log)
(profiler-report-log log))))
(defun profiler-report ()
"Report profiling results."
(interactive)
(sample-profiler-report)
(memory-profiler-report))
(profiler--report-cpu)
(profiler--report-memory))
;;;###autoload
(defun profiler-find-log (filename)
@ -709,25 +643,23 @@ memory profiler will be started."
;;; Profiling helpers
(cl-defmacro with-sample-profiling ((&key (interval profiler-sample-interval)) &rest body)
`(progn
(sample-profiler-start ,interval)
(sample-profiler-reset)
(unwind-protect
(progn ,@body)
(sample-profiler-stop)
(sample-profiler-report)
(sample-profiler-reset))))
(cl-defmacro with-sample-profiling ((&key interval) &rest body)
`(unwind-protect
(progn
(ignore (sample-profiler-log))
(sample-profiler-start ,interval)
,@body)
(sample-profiler-stop)
(profiler--report-cpu)))
(cl-defmacro with-memory-profiling (() &rest body)
`(progn
(memory-profiler-start)
(memory-profiler-reset)
(unwind-protect
(progn ,@body)
(memory-profiler-stop)
(memory-profiler-report)
(memory-profiler-reset))))
(defmacro with-memory-profiling (&rest body)
`(unwind-protect
(progn
(ignore (memory-profiler-log))
(memory-profiler-start)
,@body)
(memory-profiler-stop)
(profiler--report-memory)))
(provide 'profiler)
;;; profiler.el ends here

View file

@ -1,3 +1,31 @@
2012-09-24 Stefan Monnier <monnier@iro.umontreal.ca>
* xdisp.c (Qautomatic_redisplay): New constant.
(redisplay_internal): Record itself in backtrace_list.
(syms_of_xdisp): Define Qautomatic_redisplay.
* 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.h (SXHASH_COMBINE): Move back to...
* fns.c (SXHASH_COMBINE): ...here.
* 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.
2012-09-15 Tomohiro Matsuyama <tomo@cx4a.org>
* alloc.c (emacs_blocked_malloc): Remove redundant MALLOC_PROBE.

View file

@ -264,6 +264,7 @@ static Lisp_Object Qintervals;
static Lisp_Object Qbuffers;
static Lisp_Object Qstring_bytes, Qvector_slots, Qheap;
static Lisp_Object Qgc_cons_threshold;
Lisp_Object Qautomatic_gc;
Lisp_Object Qchar_table_extra_slots;
/* Hook run after GC has finished. */
@ -5421,6 +5422,7 @@ See Info node `(elisp)Garbage Collection'. */)
EMACS_TIME start;
Lisp_Object retval = Qnil;
size_t tot_before = 0;
struct backtrace backtrace;
if (abort_on_gc)
abort ();
@ -5430,6 +5432,14 @@ See Info node `(elisp)Garbage Collection'. */)
if (pure_bytes_used_before_overflow)
return Qnil;
/* Record this function, so it appears on the profiler's backtraces. */
backtrace.next = backtrace_list;
backtrace.function = &Qautomatic_gc;
backtrace.args = &Qautomatic_gc;
backtrace.nargs = 0;
backtrace.debug_on_exit = 0;
backtrace_list = &backtrace;
check_cons_list ();
/* Don't keep undo information around forever.
@ -5486,7 +5496,6 @@ See Info node `(elisp)Garbage Collection'. */)
shrink_regexp_cache ();
gc_in_progress = 1;
is_in_trace = 1;
/* Mark all the special slots that serve as the roots of accessibility. */
@ -5538,8 +5547,6 @@ See Info node `(elisp)Garbage Collection'. */)
mark_backtrace ();
#endif
mark_profiler ();
#ifdef HAVE_WINDOW_SYSTEM
mark_fringe_data ();
#endif
@ -5607,7 +5614,6 @@ See Info node `(elisp)Garbage Collection'. */)
check_cons_list ();
gc_in_progress = 0;
is_in_trace = 0;
consing_since_gc = 0;
if (gc_cons_threshold < GC_DEFAULT_THRESHOLD / 10)
@ -5720,24 +5726,19 @@ See Info node `(elisp)Garbage Collection'. */)
gcs_done++;
/* Collect profiling data. */
if (sample_profiler_running || memory_profiler_running)
if (memory_profiler_running)
{
size_t swept = 0;
size_t elapsed = 0;
if (memory_profiler_running)
{
size_t tot_after = total_bytes_of_live_objects ();
if (tot_before > tot_after)
swept = tot_before - tot_after;
}
if (sample_profiler_running)
{
EMACS_TIME since_start = sub_emacs_time (current_emacs_time (), start);
elapsed = EMACS_TIME_TO_DOUBLE (since_start) * 1000;
}
gc_probe (swept, elapsed);
malloc_probe (swept);
}
backtrace_list = backtrace.next;
return retval;
}
@ -6867,6 +6868,7 @@ do hash-consing of the objects allocated to pure space. */);
DEFSYM (Qstring_bytes, "string-bytes");
DEFSYM (Qvector_slots, "vector-slots");
DEFSYM (Qheap, "heap");
DEFSYM (Qautomatic_gc, "Automatic GC");
DEFSYM (Qgc_cons_threshold, "gc-cons-threshold");
DEFSYM (Qchar_table_extra_slots, "char-table-extra-slots");

View file

@ -4096,6 +4096,13 @@ sweep_weak_hash_tables (void)
#define SXHASH_MAX_LEN 7
/* Combine two integers X and Y for hashing. The result might not fit
into a Lisp integer. */
#define SXHASH_COMBINE(X, Y) \
((((EMACS_UINT) (X) << 4) + ((EMACS_UINT) (X) >> (BITS_PER_EMACS_INT - 4))) \
+ (EMACS_UINT) (Y))
/* Hash X, returning a value that fits into a Lisp integer. */
#define SXHASH_REDUCE(X) \
((((X) ^ (X) >> (BITS_PER_EMACS_INT - FIXNUM_BITS))) & INTMASK)

View file

@ -2679,11 +2679,6 @@ extern void init_syntax_once (void);
extern void syms_of_syntax (void);
/* Defined in fns.c */
/* Combine two integers X and Y for hashing. The result might not fit
into a Lisp integer. */
#define SXHASH_COMBINE(X, Y) \
((((EMACS_UINT) (X) << 4) + ((EMACS_UINT) (X) >> (BITS_PER_EMACS_INT - 4))) \
+ (EMACS_UINT) (Y))
extern Lisp_Object QCrehash_size, QCrehash_threshold;
enum { NEXT_ALMOST_PRIME_LIMIT = 11 };
EXFUN (Fidentity, 1) ATTRIBUTE_CONST;
@ -2921,6 +2916,7 @@ build_string (const char *str)
extern Lisp_Object pure_cons (Lisp_Object, Lisp_Object);
extern void make_byte_code (struct Lisp_Vector *);
extern Lisp_Object Qautomatic_gc;
extern Lisp_Object Qchar_table_extra_slots;
extern struct Lisp_Vector *allocate_vector (EMACS_INT);
extern struct Lisp_Vector *allocate_pseudovector (int memlen, int lisplen, int tag);
@ -3532,19 +3528,14 @@ void syms_of_dbusbind (void);
/* Defined in profiler.c */
extern bool sample_profiler_running;
extern bool memory_profiler_running;
extern bool is_in_trace;
extern Lisp_Object Qgc;
extern void malloc_probe (size_t);
extern void gc_probe (size_t, size_t);
#define ENTER_TRACE (is_in_trace = 1)
#define LEAVE_TRACE (is_in_trace = 0)
#define MALLOC_PROBE(size) \
do { \
if (memory_profiler_running) \
malloc_probe (size); \
} while (0)
extern void mark_profiler (void);
extern void syms_of_profiler (void);
#ifdef DOS_NT

File diff suppressed because it is too large Load diff

View file

@ -333,10 +333,10 @@ static Lisp_Object Qinhibit_eval_during_redisplay;
static Lisp_Object Qbuffer_position, Qposition, Qobject;
static Lisp_Object Qright_to_left, Qleft_to_right;
/* Cursor shapes */
/* Cursor shapes. */
Lisp_Object Qbar, Qhbar, Qbox, Qhollow;
/* Pointer shapes */
/* Pointer shapes. */
static Lisp_Object Qarrow, Qhand;
Lisp_Object Qtext;
@ -347,6 +347,7 @@ static Lisp_Object Qfontification_functions;
static Lisp_Object Qwrap_prefix;
static Lisp_Object Qline_prefix;
static Lisp_Object Qautomatic_redisplay;
/* Non-nil means don't actually do any redisplay. */
@ -12931,12 +12932,13 @@ redisplay_internal (void)
struct frame *sf;
int polling_stopped_here = 0;
Lisp_Object old_frame = selected_frame;
struct backtrace backtrace;
/* Non-zero means redisplay has to consider all windows on all
frames. Zero means, only selected_window is considered. */
int consider_all_windows_p;
/* Non-zero means redisplay has to redisplay the miniwindow */
/* Non-zero means redisplay has to redisplay the miniwindow. */
int update_miniwindow_p = 0;
TRACE ((stderr, "redisplay_internal %d\n", redisplaying_p));
@ -12974,6 +12976,14 @@ redisplay_internal (void)
++redisplaying_p;
specbind (Qinhibit_free_realized_faces, Qnil);
/* Record this function, so it appears on the profiler's backtraces. */
backtrace.next = backtrace_list;
backtrace.function = &Qautomatic_redisplay;
backtrace.args = &Qautomatic_redisplay;
backtrace.nargs = 0;
backtrace.debug_on_exit = 0;
backtrace_list = &backtrace;
{
Lisp_Object tail, frame;
@ -13671,6 +13681,7 @@ redisplay_internal (void)
#endif /* HAVE_WINDOW_SYSTEM */
end_of_redisplay:
backtrace_list = backtrace.next;
unbind_to (count, Qnil);
RESUME_POLLING;
}
@ -28696,6 +28707,7 @@ syms_of_xdisp (void)
staticpro (&Vmessage_stack);
DEFSYM (Qinhibit_redisplay, "inhibit-redisplay");
DEFSYM (Qautomatic_redisplay, "Automatic Redisplay");
message_dolog_marker1 = Fmake_marker ();
staticpro (&message_dolog_marker1);