aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarl Heuer1998-10-21 07:01:32 +0000
committerKarl Heuer1998-10-21 07:01:32 +0000
commitc7f8bd788cf417c76ef75b0a5f8ad18768e01a11 (patch)
tree1c4ba0b034d4c3ab1efd68c38bcba520018778a7
parentb946fbad3930f39d8afd3ab0f2dbe5927cf9b6ea (diff)
downloademacs-c7f8bd788cf417c76ef75b0a5f8ad18768e01a11.tar.gz
emacs-c7f8bd788cf417c76ef75b0a5f8ad18768e01a11.zip
(profile-timer-program): Var deleted.
(profile-timer-process, profile-temp-result-, profile-time): Likewise. (profile-filter, profile-reset-timer): Functions deleted. (profile-check-zero-init-times, profile-get-time): Likewise. (profile-find-function, profile-quit): Likewise. (profile-distinct, profile-call-stack, profile-last-time): New vars. (profile-time-list, profile-init-list): Doc fix. (profile-functions): Simplify. (profile-print): Use float. Make output include space separators. (profile-add-time): New helper function. (profile-function-prolog): Renamed from profile-start-function. Handle profile-distinct. (profile-function-epilog): Renamed from profile-update-function. Handle profile-distinct. (profile-a-function): If the function to be profiled is an autoload form, load it. If it's lazy-loaded, fetch it. (profile-fix-fun): Simplify profiling wrapper, and unwind-protect it. (profile-restore-fun): Arg FUN is now a function symbol, as was documented, rather than a one-element list. (profile-finish): Call profile-restore-fun properly.
-rw-r--r--lisp/emacs-lisp/profile.el295
1 files changed, 128 insertions, 167 deletions
diff --git a/lisp/emacs-lisp/profile.el b/lisp/emacs-lisp/profile.el
index d5fc7882fcd..f8b7571aebc 100644
--- a/lisp/emacs-lisp/profile.el
+++ b/lisp/emacs-lisp/profile.el
@@ -1,6 +1,6 @@
1;;; profile.el --- generate run time measurements of Emacs Lisp functions 1;;; profile.el --- generate run time measurements of Emacs Lisp functions
2 2
3;; Copyright (C) 1992, 1994 Free Software Foundation, Inc. 3;; Copyright (C) 1992, 1994, 1998 Free Software Foundation, Inc.
4 4
5;; Author: Boaz Ben-Zvi <boaz@lcs.mit.edu> 5;; Author: Boaz Ben-Zvi <boaz@lcs.mit.edu>
6;; Created: 07 Feb 1992 6;; Created: 07 Feb 1992
@@ -30,25 +30,24 @@
30;; DESCRIPTION: 30;; DESCRIPTION:
31;; ------------ 31;; ------------
32;; This program can be used to monitor running time performance of Emacs Lisp 32;; This program can be used to monitor running time performance of Emacs Lisp
33;; functions. It takes a list of functions and report the real time spent 33;; functions. It takes a list of functions and report the real time spent
34;; inside these functions. It runs a process with a separate timer program. 34;; inside these functions. (Actually, for each function it reports the amount
35;; Caveat: the C code in ../lib-src/profile.c requires BSD-compatible 35;; of time spent while at least one instance of that function is on the call
36;; time-of-day functions. If you're running an AT&T version prior to SVr4, 36;; stack. So if profiled function FOO calls profiled function BAR, the time
37;; you may have difficulty getting it to work. Your X library may supply 37;; spent inside BAR is credited to both functions.)
38;; the required routines if the standard C library does not.
39 38
40;; HOW TO USE: 39;; HOW TO USE:
41;; ----------- 40;; -----------
42;; Set the variable profile-functions-list to the list of functions 41;; Set the variable profile-functions-list to the list of functions
43;; (as symbols) You want to profile. Call M-x profile-functions to set 42;; (as symbols) You want to profile. Call M-x profile-functions to set
44;; this list on and start using your program. Note that profile-functions 43;; this list on and start using your program. Note that profile-functions
45;; MUST be called AFTER all the functions in profile-functions-list have 44;; MUST be called AFTER all the functions in profile-functions-list have
46;; been loaded !! (This call modifies the code of the profiled functions. 45;; been loaded !! (This call modifies the code of the profiled functions.
47;; Hence if you reload these functions, you need to call profile-functions 46;; Hence if you reload these functions, you need to call profile-functions
48;; again! ). 47;; again! ).
49;; To display the results do M-x profile-results . For example: 48;; To display the results do M-x profile-results . For example:
50;;------------------------------------------------------------------- 49;;-------------------------------------------------------------------
51;; (setq profile-functions-list '(sokoban-set-mode-line sokoban-load-game 50;; (setq profile-functions-list '(sokoban-set-mode-line sokoban-load-game
52;; sokoban-move-vertical sokoban-move)) 51;; sokoban-move-vertical sokoban-move))
53;; (load "sokoban") 52;; (load "sokoban")
54;; M-x profile-functions 53;; M-x profile-functions
@@ -62,49 +61,46 @@
62;; sokoban-load-game 0.453235 61;; sokoban-load-game 0.453235
63;; sokoban-set-mode-line 1.949203 62;; sokoban-set-mode-line 1.949203
64;;----------------------------------------------------- 63;;-----------------------------------------------------
65;; To clear all the settings to profile use profile-finish. 64;; To clear all the settings to profile use profile-finish.
66;; To set one function at a time (instead of or in addition to setting the 65;; To set one function at a time (instead of or in addition to setting the
67;; above list and M-x profile-functions) use M-x profile-a-function. 66;; above list and M-x profile-functions) use M-x profile-a-function.
68 67
69;;; Code: 68;;; Code:
70 69
71(defgroup profile nil
72 "Generate run time measurements of Emacs Lisp functions."
73 :prefix "profile-"
74 :group 'lisp)
75
76;;; 70;;;
77;;; User modifiable VARIABLES 71;;; User modifiable VARIABLES
78;;; 72;;;
79 73
80(defcustom profile-functions-list nil 74(defvar profile-functions-list nil "*List of functions to profile.")
81 "*List of functions to profile." 75(defvar profile-buffer "*profile*"
82 :type '(repeat function) 76 "Name of profile buffer.")
83 :group 'profile) 77(defvar profile-distinct nil
84 78 "If non-nil, each time slice gets credited to at most one function.
85(defcustom profile-timer-program 79\(Namely, the most recent one in the call stack.) If nil, then the
86 (concat exec-directory "profile") 80time reported for a function includes the entire time from beginning
87 "*Name of the profile timer program." 81to end, even if it called some other function that was also profiled.")
88 :type 'file
89 :group 'profile)
90 82
91;;; 83;;;
92;;; V A R I A B L E S 84;;; V A R I A B L E S
93;;; 85;;;
94 86
95(defvar profile-timer-process nil "Process running the timer.") 87(defvar profile-time-list nil
96(defvar profile-time-list nil 88 "List of cumulative calls and time for each profiled function.
97 "List of cumulative calls and time for each profiled function.") 89Each element looks like (FUN NCALLS SEC . USEC).")
98(defvar profile-init-list nil 90(defvar profile-init-list nil
99 "List of entry time for each function. 91 "List of entry time for each function.
100Both how many times invoked and real time of start.") 92Both how many times invoked and real time of start.
101(defvar profile-max-fun-name 0 "Max length of name of any function profiled.") 93Each element looks like (FUN DEPTH HISEC LOSEC USEC), where DEPTH is
102(defvar profile-temp-result- nil "Should NOT be used anywhere else.") 94the current recursion depth, and HISEC, LOSEC, and USEC represent the
103(defvar profile-time (cons 0 0) "Used to return result from a filter.") 95starting time of the call (or of the outermost recursion).")
104(defcustom profile-buffer "*profile*" 96(defvar profile-max-fun-name 0
105 "Name of profile buffer." 97 "Max length of name of any function profiled.")
106 :type 'string 98(defvar profile-call-stack nil
107 :group 'profile) 99 "A list of the profiled functions currently executing.
100Used only when profile-distinct is non-nil.")
101(defvar profile-last-time nil
102 "The start time of the current time slice.
103Used only when profile-distinct is non-nil.")
108 104
109(defconst profile-million 1000000) 105(defconst profile-million 1000000)
110 106
@@ -116,36 +112,23 @@ Both how many times invoked and real time of start.")
116 "Profile all the functions listed in `profile-functions-list'. 112 "Profile all the functions listed in `profile-functions-list'.
117With argument FLIST, use the list FLIST instead." 113With argument FLIST, use the list FLIST instead."
118 (interactive "P") 114 (interactive "P")
119 (if (null flist) (setq flist profile-functions-list)) 115 (mapcar 'profile-a-function (or flist profile-functions-list)))
120 (mapcar 'profile-a-function flist))
121
122(defun profile-filter (process input)
123 "Filter for the timer process. Sets `profile-time' to the returned time."
124 (if (zerop (string-match "\\." input))
125 (error "Bad output from %s" profile-timer-program)
126 (setcar profile-time
127 (string-to-int (substring input 0 (match-beginning 0))))
128 (setcdr profile-time
129 (string-to-int (substring input (match-end 0))))))
130
131 116
132(defun profile-print (entry) 117(defun profile-print (entry)
133 "Print one ENTRY (from `profile-time-list')." 118 "Print one ENTRY (from `profile-time-list')."
134 (let* ((calls (car (cdr entry))) 119 (let* ((calls (car (cdr entry)))
135 (timec (cdr (cdr entry))) 120 (timec (cdr (cdr entry)))
136 (time (+ (car timec) (/ (cdr timec) (float profile-million)))) 121 (avgtime (and (not (zerop calls))
137 (avgtime 0.0)) 122 (/ (+ (car timec)
123 (/ (cdr timec) (float profile-million)))
124 calls))))
138 (insert (format (concat "%-" 125 (insert (format (concat "%-"
139 (int-to-string profile-max-fun-name) 126 (int-to-string profile-max-fun-name)
140 "s%8d%11d.%06d") 127 "s %7d %10d.%06d")
141 (car entry) calls (car timec) (cdr timec)) 128 (car entry) calls (car timec) (cdr timec))
142 (if (zerop calls) 129 (if (null avgtime)
143 "\n" 130 "\n"
144 (format "%12d.%06d\n" 131 (format " %18.6f\n" avgtime)))))
145 (truncate (setq avgtime (/ time calls)))
146 (truncate (* (- avgtime (ftruncate avgtime))
147 profile-million))))
148 )))
149 132
150(defun profile-results () 133(defun profile-results ()
151 "Display profiling results in the buffer `*profile*'. 134 "Display profiling results in the buffer `*profile*'.
@@ -158,82 +141,63 @@ With argument FLIST, use the list FLIST instead."
158 (insert (make-string profile-max-fun-name ?=) " ") 141 (insert (make-string profile-max-fun-name ?=) " ")
159 (insert "====== ================ =================\n") 142 (insert "====== ================ =================\n")
160 (mapcar 'profile-print profile-time-list)) 143 (mapcar 'profile-print profile-time-list))
161
162(defun profile-reset-timer ()
163 (process-send-string profile-timer-process "z\n"))
164
165(defun profile-check-zero-init-times (entry)
166 "If ENTRY has non zero time, give an error."
167 (let ((time (cdr (cdr entry))))
168 (if (and (zerop (car time)) (zerop (cdr time))) nil ; OK
169 (error "Process timer died while making performance profile."))))
170
171(defun profile-get-time ()
172 "Get time from timer process into `profile-time'."
173 ;; first time or if process dies
174 (if (and (processp profile-timer-process)
175 (eq 'run (process-status profile-timer-process))) nil
176 (setq profile-timer-process;; [re]start the timer process
177 (start-process "timer"
178 (get-buffer-create profile-buffer)
179 profile-timer-program))
180 (set-process-filter profile-timer-process 'profile-filter)
181 (process-kill-without-query profile-timer-process)
182 (profile-reset-timer)
183 ;; check if timer died during time measurement
184 (mapcar 'profile-check-zero-init-times profile-init-list))
185 ;; make timer process return current time
186 (process-send-string profile-timer-process "p\n")
187 (accept-process-output))
188 144
189(defun profile-find-function (fun flist) 145(defun profile-add-time (dest now prev)
190 "Linear search for FUN in FLIST." 146 "Add to DEST the difference between timestamps NOW and PREV.
191 (if (null flist) nil 147DEST is a pair (SEC . USEC) which is modified in place.
192 (if (eq fun (car (car flist))) (cdr (car flist)) 148NOW and PREV are triples as returned by `current-time'."
193 (profile-find-function fun (cdr flist))))) 149 (let ((sec (+ (car dest)
194 150 (* 65536 (- (car now) (car prev)))
195(defun profile-start-function (fun) 151 (- (cadr now) (cadr prev))))
196 "On entry, keep current time for function FUN." 152 (usec (+ (cdr dest)
197 ;; assumes that profile-time contains the current time 153 (- (car (cddr now)) (car (cddr prev))))))
198 (let ((init-time (profile-find-function fun profile-init-list))) 154 (if (< usec 0)
199 (if (null init-time) (error "Function %s missing from list" fun)) 155 (setq sec (1- sec)
200 (if (not (zerop (car init-time)));; is it a recursive call ? 156 usec (+ usec profile-million))
201 (setcar init-time (1+ (car init-time))) 157 (if (>= usec profile-million)
202 (setcar init-time 1) ; mark first entry 158 (setq sec (1+ sec)
203 (setq init-time (cdr init-time)) 159 usec (- usec profile-million))))
204 (setcar init-time (car profile-time)) 160 (setcar dest sec)
205 (setcdr init-time (cdr profile-time))) 161 (setcdr dest usec)))
206 )) 162
207 163(defun profile-function-prolog (fun)
208(defun profile-update-function (fun) 164 "Mark the beginning of a call to function FUN."
209 "When the call to the function FUN is finished, add its run time." 165 (if profile-distinct
210 ;; assumes that profile-time contains the current time 166 (let ((profile-time (current-time)))
211 (let ((init-time (profile-find-function fun profile-init-list)) 167 (if profile-call-stack
212 (accum (profile-find-function fun profile-time-list)) 168 (profile-add-time (cdr (cdr (assq (car profile-call-stack)
213 calls time sec usec) 169 profile-time-list)))
214 (if (or (null init-time) 170 profile-time profile-last-time))
215 (null accum)) (error "Function %s missing from list" fun)) 171 (setq profile-call-stack (cons fun profile-call-stack)
216 (setq calls (car accum)) 172 profile-last-time profile-time))
217 (setq time (cdr accum)) 173 (let ((profile-time (current-time))
218 (setcar init-time (1- (car init-time))) ; pop one level in recursion 174 (init-time (cdr (assq fun profile-init-list))))
219 (if (not (zerop (car init-time))) 175 (if (null init-time) (error "Function %s missing from list" fun))
220 nil ; in some recursion level, 176 (if (not (zerop (car init-time)));; is it a recursive call ?
221 ; do not update cumulated time 177 (setcar init-time (1+ (car init-time)))
222 (setcar accum (1+ calls)) 178 (setcar init-time 1) ; mark first entry
223 (setq init-time (cdr init-time)) 179 (setcdr init-time profile-time)))))
224 (setq sec (- (car profile-time) (car init-time)) 180
225 usec (- (cdr profile-time) (cdr init-time))) 181(defun profile-function-epilog (fun)
226 (setcar init-time 0) ; reset time to check for error 182 "Mark the end of a call to function FUN."
227 (setcdr init-time 0) ; in case timer process dies 183 (if profile-distinct
228 (if (>= usec 0) nil 184 (let ((profile-time (current-time))
229 (setq usec (+ usec profile-million)) 185 (accum (cdr (assq fun profile-time-list))))
230 (setq sec (1- sec))) 186 (setcar accum (1+ (car accum)))
231 (setcar time (+ sec (car time))) 187 (profile-add-time (cdr accum) profile-time profile-last-time)
232 (setcdr time (+ usec (cdr time))) 188 (setq profile-call-stack (cdr profile-call-stack)
233 (if (< (cdr time) profile-million) nil 189 profile-last-time profile-time))
234 (setcar time (1+ (car time))) 190 (let ((profile-time (current-time))
235 (setcdr time (- (cdr time) profile-million))) 191 (init-time (cdr (assq fun profile-init-list)))
236 ))) 192 (accum (cdr (assq fun profile-time-list))))
193 (if (or (null init-time)
194 (null accum))
195 (error "Function %s missing from list" fun))
196 (setcar init-time (1- (car init-time))) ; pop one level in recursion
197 ;; Update only if we've finished the outermost recursive call
198 (when (zerop (car init-time))
199 (setcar accum (1+ (car accum)))
200 (profile-add-time (cdr accum) profile-time (cdr init-time))))))
237 201
238(defun profile-convert-byte-code (function) 202(defun profile-convert-byte-code (function)
239 (let ((defn (symbol-function function))) 203 (let ((defn (symbol-function function)))
@@ -255,14 +219,19 @@ With argument FLIST, use the list FLIST instead."
255(defun profile-a-function (fun) 219(defun profile-a-function (fun)
256 "Profile the function FUN." 220 "Profile the function FUN."
257 (interactive "aFunction to profile: ") 221 (interactive "aFunction to profile: ")
222 (let ((def (symbol-function fun)))
223 (when (eq (car-safe def) 'autoload)
224 (load (car (cdr def)))
225 (setq def (symbol-function fun)))
226 (fetch-bytecode def))
258 (profile-convert-byte-code fun) 227 (profile-convert-byte-code fun)
259 (let ((def (symbol-function fun)) (funlen (length (symbol-name fun)))) 228 (let ((def (symbol-function fun)) (funlen (length (symbol-name fun))))
260 (if (eq (car def) 'lambda) nil 229 (or (eq (car def) 'lambda)
261 (error "To profile: %s must be a user-defined function" fun)) 230 (error "To profile: %s must be a user-defined function" fun))
262 (setq profile-time-list ; add a new entry 231 (setq profile-time-list ; add a new entry
263 (cons (cons fun (cons 0 (cons 0 0))) profile-time-list)) 232 (cons (cons fun (cons 0 (cons 0 0))) profile-time-list))
264 (setq profile-init-list ; add a new entry 233 (setq profile-init-list ; add a new entry
265 (cons (cons fun (cons 0 (cons 0 0))) profile-init-list)) 234 (cons (cons fun (cons 0 nil)) profile-init-list))
266 (if (< profile-max-fun-name funlen) (setq profile-max-fun-name funlen)) 235 (if (< profile-max-fun-name funlen) (setq profile-max-fun-name funlen))
267 (fset fun (profile-fix-fun fun def)))) 236 (fset fun (profile-fix-fun fun def))))
268 237
@@ -270,7 +239,7 @@ With argument FLIST, use the list FLIST instead."
270 "Take function FUN and return it fixed for profiling. 239 "Take function FUN and return it fixed for profiling.
271DEF is (symbol-function FUN)." 240DEF is (symbol-function FUN)."
272 (if (< (length def) 3) 241 (if (< (length def) 3)
273 def ; nothing to see 242 def ; nothing to change
274 (let ((prefix (list (car def) (car (cdr def)))) 243 (let ((prefix (list (car def) (car (cdr def))))
275 (suffix (cdr (cdr def)))) 244 (suffix (cdr (cdr def))))
276 ;; Skip the doc string, if there is a string 245 ;; Skip the doc string, if there is a string
@@ -281,51 +250,43 @@ DEF is (symbol-function FUN)."
281 suffix (cdr suffix))) 250 suffix (cdr suffix)))
282 ;; Check for an interactive spec. 251 ;; Check for an interactive spec.
283 ;; If found, put it into PREFIX and skip it. 252 ;; If found, put it into PREFIX and skip it.
284 (if (and (listp (car suffix)) 253 (if (and (listp (car suffix))
285 (eq (car (car suffix)) 'interactive)) 254 (eq (car (car suffix)) 'interactive))
286 (setq prefix (nconc prefix (list (car suffix))) 255 (setq prefix (nconc prefix (list (car suffix)))
287 suffix (cdr suffix))) 256 suffix (cdr suffix)))
288 (if (equal (car suffix) '(profile-get-time)) 257 (if (eq (car-safe (car suffix)) 'profile-function-prolog)
289 def ; already profiled 258 def ; already profiled
290 ;; Prepare new function definition. 259 ;; Prepare new function definition.
260 ;; If you change this structure, also change profile-restore-fun.
291 (nconc prefix 261 (nconc prefix
292 (list '(profile-get-time) ; read time 262 (list (list 'profile-function-prolog
293 (list 'profile-start-function
294 (list 'quote fun))
295 (list 'setq 'profile-temp-result-
296 (cons 'progn suffix))
297 '(profile-get-time) ; read time
298 (list 'profile-update-function
299 (list 'quote fun)) 263 (list 'quote fun))
300 'profile-temp-result-)))))) 264 (list 'unwind-protect
265 (cons 'progn suffix)
266 (list 'profile-function-epilog
267 (list 'quote fun)))))))))
301 268
302(defun profile-restore-fun (fun) 269(defun profile-restore-fun (fun)
303 "Restore profiled function FUN to its original state." 270 "Restore profiled function FUN to its original state."
304 (let ((def (symbol-function (car fun))) body index) 271 (let ((def (symbol-function fun)) body index)
305 ;; move index beyond header 272 ;; move index beyond header
306 (setq index (cdr def)) 273 (setq index (cdr-safe def))
307 (if (stringp (car (cdr index))) (setq index (cdr index))) 274 (if (stringp (car (cdr index)))
308 (if (and (listp (car (cdr index)))
309 (eq (car (car (cdr index))) 'interactive))
310 (setq index (cdr index))) 275 (setq index (cdr index)))
311 (setq body (car (nthcdr 3 index))) 276 (if (eq (car-safe (car (cdr index))) 'interactive)
312 (if (and (listp body) ; the right element ? 277 (setq index (cdr index)))
313 (eq (car (cdr body)) 'profile-temp-result-)) 278 (if (eq (car-safe (car (cdr index))) 'profile-function-prolog)
314 (setcdr index (cdr (car (cdr (cdr body)))))))) 279 (setcdr index (cdr (car (cdr (car (cdr (cdr index))))))))))
315 280
316(defun profile-finish () 281(defun profile-finish ()
317 "Stop profiling functions. Clear all the settings." 282 "Stop profiling functions. Clear all the settings."
318 (interactive) 283 (interactive)
319 (mapcar 'profile-restore-fun profile-time-list) 284 (while profile-time-list
285 (profile-restore-fun (car (car profile-time-list)))
286 (setq profile-time-list (cdr profile-time-list)))
320 (setq profile-max-fun-name 0) 287 (setq profile-max-fun-name 0)
321 (setq profile-time-list nil)
322 (setq profile-init-list nil)) 288 (setq profile-init-list nil))
323 289
324(defun profile-quit ()
325 "Kill the timer process."
326 (interactive)
327 (process-send-string profile-timer-process "q\n"))
328
329(provide 'profile) 290(provide 'profile)
330 291
331;;; profile.el ends here 292;;; profile.el ends here