comparison lisp/emacs-lisp/profile.el @ 23512:1cd5555e67a9

(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.
author Karl Heuer <kwzh@gnu.org>
date Wed, 21 Oct 1998 07:01:32 +0000
parents 25eb153a9d4c
children 70fadf968ae4
comparison
equal deleted inserted replaced
23511:8b4cdcbaf071 23512:1cd5555e67a9
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
7 ;; Version: 1.0 7 ;; Version: 1.0
8 ;; Adapted-By: ESR 8 ;; Adapted-By: ESR
28 ;;; Commentary: 28 ;;; Commentary:
29 29
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
55 ;; ... I play the sokoban game .......... 54 ;; ... I play the sokoban game ..........
56 ;; M-x profile-results 55 ;; M-x profile-results
60 ;; sokoban-move 0.539088 59 ;; sokoban-move 0.539088
61 ;; sokoban-move-vertical 0.410130 60 ;; sokoban-move-vertical 0.410130
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") 80 time reported for a function includes the entire time from beginning
87 "*Name of the profile timer program." 81 to 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.") 89 Each 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.
100 Both how many times invoked and real time of start.") 92 Both how many times invoked and real time of start.
101 (defvar profile-max-fun-name 0 "Max length of name of any function profiled.") 93 Each element looks like (FUN DEPTH HISEC LOSEC USEC), where DEPTH is
102 (defvar profile-temp-result- nil "Should NOT be used anywhere else.") 94 the current recursion depth, and HISEC, LOSEC, and USEC represent the
103 (defvar profile-time (cons 0 0) "Used to return result from a filter.") 95 starting 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.
100 Used only when profile-distinct is non-nil.")
101 (defvar profile-last-time nil
102 "The start time of the current time slice.
103 Used only when profile-distinct is non-nil.")
108 104
109 (defconst profile-million 1000000) 105 (defconst profile-million 1000000)
110 106
111 ;;; 107 ;;;
112 ;;; F U N C T I O N S 108 ;;; F U N C T I O N S
114 110
115 (defun profile-functions (&optional flist) 111 (defun profile-functions (&optional flist)
116 "Profile all the functions listed in `profile-functions-list'. 112 "Profile all the functions listed in `profile-functions-list'.
117 With argument FLIST, use the list FLIST instead." 113 With 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*'.
152 \(The buffer name comes from `profile-buffer'.)" 135 \(The buffer name comes from `profile-buffer'.)"
153 (interactive) 136 (interactive)
156 (insert "Function" (make-string (- profile-max-fun-name 6) ? )) 139 (insert "Function" (make-string (- profile-max-fun-name 6) ? ))
157 (insert " Calls Total time (sec) Avg time per call\n") 140 (insert " Calls Total time (sec) Avg time per call\n")
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 144
162 (defun profile-reset-timer () 145 (defun profile-add-time (dest now prev)
163 (process-send-string profile-timer-process "z\n")) 146 "Add to DEST the difference between timestamps NOW and PREV.
164 147 DEST is a pair (SEC . USEC) which is modified in place.
165 (defun profile-check-zero-init-times (entry) 148 NOW and PREV are triples as returned by `current-time'."
166 "If ENTRY has non zero time, give an error." 149 (let ((sec (+ (car dest)
167 (let ((time (cdr (cdr entry)))) 150 (* 65536 (- (car now) (car prev)))
168 (if (and (zerop (car time)) (zerop (cdr time))) nil ; OK 151 (- (cadr now) (cadr prev))))
169 (error "Process timer died while making performance profile.")))) 152 (usec (+ (cdr dest)
170 153 (- (car (cddr now)) (car (cddr prev))))))
171 (defun profile-get-time () 154 (if (< usec 0)
172 "Get time from timer process into `profile-time'." 155 (setq sec (1- sec)
173 ;; first time or if process dies 156 usec (+ usec profile-million))
174 (if (and (processp profile-timer-process) 157 (if (>= usec profile-million)
175 (eq 'run (process-status profile-timer-process))) nil 158 (setq sec (1+ sec)
176 (setq profile-timer-process;; [re]start the timer process 159 usec (- usec profile-million))))
177 (start-process "timer" 160 (setcar dest sec)
178 (get-buffer-create profile-buffer) 161 (setcdr dest usec)))
179 profile-timer-program)) 162
180 (set-process-filter profile-timer-process 'profile-filter) 163 (defun profile-function-prolog (fun)
181 (process-kill-without-query profile-timer-process) 164 "Mark the beginning of a call to function FUN."
182 (profile-reset-timer) 165 (if profile-distinct
183 ;; check if timer died during time measurement 166 (let ((profile-time (current-time)))
184 (mapcar 'profile-check-zero-init-times profile-init-list)) 167 (if profile-call-stack
185 ;; make timer process return current time 168 (profile-add-time (cdr (cdr (assq (car profile-call-stack)
186 (process-send-string profile-timer-process "p\n") 169 profile-time-list)))
187 (accept-process-output)) 170 profile-time profile-last-time))
188 171 (setq profile-call-stack (cons fun profile-call-stack)
189 (defun profile-find-function (fun flist) 172 profile-last-time profile-time))
190 "Linear search for FUN in FLIST." 173 (let ((profile-time (current-time))
191 (if (null flist) nil 174 (init-time (cdr (assq fun profile-init-list))))
192 (if (eq fun (car (car flist))) (cdr (car flist)) 175 (if (null init-time) (error "Function %s missing from list" fun))
193 (profile-find-function fun (cdr flist))))) 176 (if (not (zerop (car init-time)));; is it a recursive call ?
194 177 (setcar init-time (1+ (car init-time)))
195 (defun profile-start-function (fun) 178 (setcar init-time 1) ; mark first entry
196 "On entry, keep current time for function FUN." 179 (setcdr init-time profile-time)))))
197 ;; assumes that profile-time contains the current time 180
198 (let ((init-time (profile-find-function fun profile-init-list))) 181 (defun profile-function-epilog (fun)
199 (if (null init-time) (error "Function %s missing from list" fun)) 182 "Mark the end of a call to function FUN."
200 (if (not (zerop (car init-time)));; is it a recursive call ? 183 (if profile-distinct
201 (setcar init-time (1+ (car init-time))) 184 (let ((profile-time (current-time))
202 (setcar init-time 1) ; mark first entry 185 (accum (cdr (assq fun profile-time-list))))
203 (setq init-time (cdr init-time)) 186 (setcar accum (1+ (car accum)))
204 (setcar init-time (car profile-time)) 187 (profile-add-time (cdr accum) profile-time profile-last-time)
205 (setcdr init-time (cdr profile-time))) 188 (setq profile-call-stack (cdr profile-call-stack)
206 )) 189 profile-last-time profile-time))
207 190 (let ((profile-time (current-time))
208 (defun profile-update-function (fun) 191 (init-time (cdr (assq fun profile-init-list)))
209 "When the call to the function FUN is finished, add its run time." 192 (accum (cdr (assq fun profile-time-list))))
210 ;; assumes that profile-time contains the current time 193 (if (or (null init-time)
211 (let ((init-time (profile-find-function fun profile-init-list)) 194 (null accum))
212 (accum (profile-find-function fun profile-time-list)) 195 (error "Function %s missing from list" fun))
213 calls time sec usec) 196 (setcar init-time (1- (car init-time))) ; pop one level in recursion
214 (if (or (null init-time) 197 ;; Update only if we've finished the outermost recursive call
215 (null accum)) (error "Function %s missing from list" fun)) 198 (when (zerop (car init-time))
216 (setq calls (car accum)) 199 (setcar accum (1+ (car accum)))
217 (setq time (cdr accum)) 200 (profile-add-time (cdr accum) profile-time (cdr init-time))))))
218 (setcar init-time (1- (car init-time))) ; pop one level in recursion
219 (if (not (zerop (car init-time)))
220 nil ; in some recursion level,
221 ; do not update cumulated time
222 (setcar accum (1+ calls))
223 (setq init-time (cdr init-time))
224 (setq sec (- (car profile-time) (car init-time))
225 usec (- (cdr profile-time) (cdr init-time)))
226 (setcar init-time 0) ; reset time to check for error
227 (setcdr init-time 0) ; in case timer process dies
228 (if (>= usec 0) nil
229 (setq usec (+ usec profile-million))
230 (setq sec (1- sec)))
231 (setcar time (+ sec (car time)))
232 (setcdr time (+ usec (cdr time)))
233 (if (< (cdr time) profile-million) nil
234 (setcar time (1+ (car time)))
235 (setcdr time (- (cdr time) profile-million)))
236 )))
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)))
240 (if (byte-code-function-p defn) 204 (if (byte-code-function-p defn)
241 ;; It is a compiled code object. 205 ;; It is a compiled code object.
253 (fset function (cons 'lambda (cons (car contents) body))))))) 217 (fset function (cons 'lambda (cons (car contents) body)))))))
254 218
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
269 (defun profile-fix-fun (fun def) 238 (defun profile-fix-fun (fun def)
270 "Take function FUN and return it fixed for profiling. 239 "Take function FUN and return it fixed for profiling.
271 DEF is (symbol-function FUN)." 240 DEF 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
277 ;; which serves only as a doc string, 246 ;; which serves only as a doc string,
278 ;; and put it in PREFIX. 247 ;; and put it in PREFIX.
279 (if (and (stringp (car suffix)) (cdr suffix)) 248 (if (and (stringp (car suffix)) (cdr suffix))
280 (setq prefix (nconc prefix (list (car suffix))) 249 (setq prefix (nconc prefix (list (car suffix)))
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)) 263 (list 'quote fun))
295 (list 'setq 'profile-temp-result- 264 (list 'unwind-protect
296 (cons 'progn suffix)) 265 (cons 'progn suffix)
297 '(profile-get-time) ; read time 266 (list 'profile-function-epilog
298 (list 'profile-update-function 267 (list 'quote fun)))))))))
299 (list 'quote fun))
300 'profile-temp-result-))))))
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