]> code.delx.au - gnu-emacs/blob - lisp/emacs-lisp/elp.el
Convert consecutive FSF copyright years to ranges.
[gnu-emacs] / lisp / emacs-lisp / elp.el
1 ;;; elp.el --- Emacs Lisp Profiler
2
3 ;; Copyright (C) 1994-1995, 1997-1998, 2001-2011 Free Software Foundation, Inc.
4
5 ;; Author: Barry A. Warsaw
6 ;; Maintainer: FSF
7 ;; Created: 26-Feb-1994
8 ;; Keywords: debugging lisp tools
9
10 ;; This file is part of GNU Emacs.
11
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
16
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24
25 ;;; Commentary:
26 ;;
27 ;; If you want to profile a bunch of functions, set elp-function-list
28 ;; to the list of symbols, then do a M-x elp-instrument-list. This
29 ;; hacks those functions so that profiling information is recorded
30 ;; whenever they are called. To print out the current results, use
31 ;; M-x elp-results. If you want output to go to standard-output
32 ;; instead of a separate buffer, setq elp-use-standard-output to
33 ;; non-nil. With elp-reset-after-results set to non-nil, profiling
34 ;; information will be reset whenever the results are displayed. You
35 ;; can also reset all profiling info at any time with M-x
36 ;; elp-reset-all.
37 ;;
38 ;; You can also instrument all functions in a package, provided that
39 ;; the package follows the GNU coding standard of a common textual
40 ;; prefix. Use M-x elp-instrument-package for this.
41 ;;
42 ;; If you want to sort the results, set elp-sort-by-function to some
43 ;; predicate function. The three most obvious choices are predefined:
44 ;; elp-sort-by-call-count, elp-sort-by-average-time, and
45 ;; elp-sort-by-total-time. Also, you can prune from the output, all
46 ;; functions that have been called fewer than a given number of times
47 ;; by setting elp-report-limit.
48 ;;
49 ;; Elp can instrument byte-compiled functions just as easily as
50 ;; interpreted functions, but it cannot instrument macros. However,
51 ;; when you redefine a function (e.g. with eval-defun), you'll need to
52 ;; re-instrument it with M-x elp-instrument-function. This will also
53 ;; reset profiling information for that function. Elp can handle
54 ;; interactive functions (i.e. commands), but of course any time spent
55 ;; idling for user prompts will show up in the timing results.
56 ;;
57 ;; You can also designate a `master' function. Profiling times will
58 ;; be gathered for instrumented functions only during execution of
59 ;; this master function. Thus, if you have some defuns like:
60 ;;
61 ;; (defun foo () (do-something-time-intensive))
62 ;; (defun bar () (foo))
63 ;; (defun baz () (bar) (foo))
64 ;;
65 ;; and you want to find out the amount of time spent in bar and foo,
66 ;; but only during execution of bar, make bar the master. The call of
67 ;; foo from baz will not add to foo's total timing sums. Use M-x
68 ;; elp-set-master and M-x elp-unset-master to utilize this feature.
69 ;; Only one master function can be set at a time.
70
71 ;; You can restore any function's original function definition with
72 ;; elp-restore-function. The other instrument, restore, and reset
73 ;; functions are provided for symmetry.
74
75 ;; Here is a list of variable you can use to customize elp:
76 ;; elp-function-list
77 ;; elp-reset-after-results
78 ;; elp-sort-by-function
79 ;; elp-report-limit
80 ;;
81 ;; Here is a list of the interactive commands you can use:
82 ;; elp-instrument-function
83 ;; elp-restore-function
84 ;; elp-instrument-list
85 ;; elp-restore-list
86 ;; elp-instrument-package
87 ;; elp-restore-all
88 ;; elp-reset-function
89 ;; elp-reset-list
90 ;; elp-reset-all
91 ;; elp-set-master
92 ;; elp-unset-master
93 ;; elp-results
94
95 ;; Note that there are plenty of factors that could make the times
96 ;; reported unreliable, including the accuracy and granularity of your
97 ;; system clock, and the overhead spent in lisp calculating and
98 ;; recording the intervals. I figure the latter is pretty constant,
99 ;; so while the times may not be entirely accurate, I think they'll
100 ;; give you a good feel for the relative amount of work spent in the
101 ;; various lisp routines you are profiling. Note further that times
102 ;; are calculated using wall-clock time, so other system load will
103 ;; affect accuracy too.
104
105 ;;; Background:
106
107 ;; This program was inspired by the only two existing Emacs Lisp
108 ;; profilers that I'm aware of, Boaz Ben-Zvi's profile.el, and Root
109 ;; Boy Jim's profiler.el. Both were written for Emacs 18 and both were
110 ;; pretty good first shots at profiling, but I found that they didn't
111 ;; provide the functionality or interface that I wanted, so I wrote
112 ;; this. I've tested elp in XEmacs 19 and Emacs 19. There's no point
113 ;; in even trying to make this work with Emacs 18.
114
115 ;; Unlike previous profilers, elp uses Emacs 19's built-in function
116 ;; current-time to return interval times. This obviates the need for
117 ;; both an external C program and Emacs processes to communicate with
118 ;; such a program, and thus simplifies the package as a whole.
119
120 ;; TBD:
121 ;; Make this act like a real profiler, so that it records time spent
122 ;; in all branches of execution.
123
124 ;;; Code:
125
126 \f
127 ;; start of user configuration variables
128 ;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
129
130 (defgroup elp nil
131 "Emacs Lisp Profiler."
132 :group 'lisp)
133
134 (defcustom elp-function-list nil
135 "List of functions to profile.
136 Used by the command `elp-instrument-list'."
137 :type '(repeat function)
138 :group 'elp)
139
140 (defcustom elp-reset-after-results t
141 "Non-nil means reset all profiling info after results are displayed.
142 Results are displayed with the `elp-results' command."
143 :type 'boolean
144 :group 'elp)
145
146 (defcustom elp-sort-by-function 'elp-sort-by-total-time
147 "Non-nil specifies ELP results sorting function.
148 These functions are currently available:
149
150 elp-sort-by-call-count -- sort by the highest call count
151 elp-sort-by-total-time -- sort by the highest total time
152 elp-sort-by-average-time -- sort by the highest average times
153
154 You can write your own sort function. It should adhere to the
155 interface specified by the PREDICATE argument for `sort'.
156 Each \"element of LIST\" is really a 4 element vector where element 0 is
157 the call count, element 1 is the total time spent in the function,
158 element 2 is the average time spent in the function, and element 3 is
159 the symbol's name string."
160 :type 'function
161 :group 'elp)
162
163 (defcustom elp-report-limit 1
164 "Prevent some functions from being displayed in the results buffer.
165 If a number, no function that has been called fewer than that number
166 of times will be displayed in the output buffer. If nil, all
167 functions will be displayed."
168 :type '(choice integer
169 (const :tag "Show All" nil))
170 :group 'elp)
171
172 (defcustom elp-use-standard-output nil
173 "If non-nil, output to `standard-output' instead of a buffer."
174 :type 'boolean
175 :group 'elp)
176
177 (defcustom elp-recycle-buffers-p t
178 "If nil, don't recycle the `elp-results-buffer'.
179 In other words, a new unique buffer is create every time you run
180 \\[elp-results]."
181 :type 'boolean
182 :group 'elp)
183
184
185 ;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
186 ;; end of user configuration variables
187
188 \f
189 (defvar elp-results-buffer "*ELP Profiling Results*"
190 "Buffer name for outputting profiling results.")
191
192 (defconst elp-timer-info-property 'elp-info
193 "ELP information property name.")
194
195 (defvar elp-all-instrumented-list nil
196 "List of all functions currently being instrumented.")
197
198 (defvar elp-record-p t
199 "Controls whether functions should record times or not.
200 This variable is set by the master function.")
201
202 (defvar elp-master nil
203 "Master function symbol.")
204
205 (defvar elp-not-profilable
206 ;; First, the functions used inside each instrumented function:
207 '(elp-wrapper called-interactively-p
208 ;; Then the functions used by the above functions. I used
209 ;; (delq nil (mapcar (lambda (x) (and (symbolp x) (fboundp x) x))
210 ;; (aref (symbol-function 'elp-wrapper) 2)))
211 ;; to help me find this list.
212 error call-interactively apply current-time
213 ;; Andreas Politz reports problems profiling these (Bug#4233):
214 + byte-code-function-p functionp byte-code subrp
215 indirect-function fboundp)
216 "List of functions that cannot be profiled.
217 Those functions are used internally by the profiling code and profiling
218 them would thus lead to infinite recursion.")
219
220 (defun elp-profilable-p (fun)
221 (and (symbolp fun)
222 (fboundp fun)
223 (not (or (memq fun elp-not-profilable)
224 (keymapp fun)
225 (memq (car-safe (symbol-function fun)) '(autoload macro))
226 (condition-case nil
227 (when (subrp (indirect-function fun))
228 (eq 'unevalled
229 (cdr (subr-arity (indirect-function fun)))))
230 (error nil))))))
231
232 \f
233 ;;;###autoload
234 (defun elp-instrument-function (funsym)
235 "Instrument FUNSYM for profiling.
236 FUNSYM must be a symbol of a defined function."
237 (interactive "aFunction to instrument: ")
238 ;; restore the function. this is necessary to avoid infinite
239 ;; recursion of already instrumented functions (i.e. elp-wrapper
240 ;; calling elp-wrapper ad infinitum). it is better to simply
241 ;; restore the function than to throw an error. this will work
242 ;; properly in the face of eval-defun because if the function was
243 ;; redefined, only the timer info will be nil'd out since
244 ;; elp-restore-function is smart enough not to trash the new
245 ;; definition.
246 (elp-restore-function funsym)
247 (let* ((funguts (symbol-function funsym))
248 (infovec (vector 0 0 funguts))
249 (newguts '(lambda (&rest args))))
250 ;; we cannot profile macros
251 (and (eq (car-safe funguts) 'macro)
252 (error "ELP cannot profile macro: %s" funsym))
253 ;; TBD: at some point it might be better to load the autoloaded
254 ;; function instead of throwing an error. if we do this, then we
255 ;; probably want elp-instrument-package to be updated with the
256 ;; newly loaded list of functions. i'm not sure it's smart to do
257 ;; the autoload here, since that could have side effects, and
258 ;; elp-instrument-function is similar (in my mind) to defun-ish
259 ;; type functionality (i.e. it shouldn't execute the function).
260 (and (eq (car-safe funguts) 'autoload)
261 (error "ELP cannot profile autoloaded function: %s" funsym))
262 ;; We cannot profile functions used internally during profiling.
263 (unless (elp-profilable-p funsym)
264 (error "ELP cannot profile the function: %s" funsym))
265 ;; put rest of newguts together
266 (if (commandp funsym)
267 (setq newguts (append newguts '((interactive)))))
268 (setq newguts (append newguts `((elp-wrapper
269 (quote ,funsym)
270 ,(when (commandp funsym)
271 '(called-interactively-p 'any))
272 args))))
273 ;; to record profiling times, we set the symbol's function
274 ;; definition so that it runs the elp-wrapper function with the
275 ;; function symbol as an argument. We place the old function
276 ;; definition on the info vector.
277 ;;
278 ;; The info vector data structure is a 3 element vector. The 0th
279 ;; element is the call-count, i.e. the total number of times this
280 ;; function has been entered. This value is bumped up on entry to
281 ;; the function so that non-local exists are still recorded. TBD:
282 ;; I haven't tested non-local exits at all, so no guarantees.
283 ;;
284 ;; The 1st element is the total amount of time in usecs that have
285 ;; been spent inside this function. This number is added to on
286 ;; function exit.
287 ;;
288 ;; The 2nd element is the old function definition list. This gets
289 ;; funcall'd in between start/end time retrievals. I believe that
290 ;; this lets us profile even byte-compiled functions.
291
292 ;; put the info vector on the property list
293 (put funsym elp-timer-info-property infovec)
294
295 ;; Set the symbol's new profiling function definition to run
296 ;; elp-wrapper.
297 (let ((advice-info (get funsym 'ad-advice-info)))
298 (if advice-info
299 (progn
300 ;; If function is advised, don't let Advice change
301 ;; its definition from under us during the `fset'.
302 (put funsym 'ad-advice-info nil)
303 (fset funsym newguts)
304 (put funsym 'ad-advice-info advice-info))
305 (fset funsym newguts)))
306
307 ;; add this function to the instrumentation list
308 (unless (memq funsym elp-all-instrumented-list)
309 (push funsym elp-all-instrumented-list))))
310
311 (defun elp-restore-function (funsym)
312 "Restore an instrumented function to its original definition.
313 Argument FUNSYM is the symbol of a defined function."
314 (interactive "aFunction to restore: ")
315 (let ((info (get funsym elp-timer-info-property)))
316 ;; delete the function from the all instrumented list
317 (setq elp-all-instrumented-list
318 (delq funsym elp-all-instrumented-list))
319
320 ;; if the function was the master, reset the master
321 (if (eq funsym elp-master)
322 (setq elp-master nil
323 elp-record-p t))
324
325 ;; zap the properties
326 (put funsym elp-timer-info-property nil)
327
328 ;; restore the original function definition, but if the function
329 ;; wasn't instrumented do nothing. we do this after the above
330 ;; because its possible the function got un-instrumented due to
331 ;; circumstances beyond our control. Also, check to make sure
332 ;; that the current function symbol points to elp-wrapper. If
333 ;; not, then the user probably did an eval-defun, or loaded a
334 ;; byte-compiled version, while the function was instrumented and
335 ;; we don't want to destroy the new definition. can it ever be
336 ;; the case that a lisp function can be compiled instrumented?
337 (and info
338 (functionp funsym)
339 (not (byte-code-function-p (symbol-function funsym)))
340 (assq 'elp-wrapper (symbol-function funsym))
341 (fset funsym (aref info 2)))))
342
343 ;;;###autoload
344 (defun elp-instrument-list (&optional list)
345 "Instrument, for profiling, all functions in `elp-function-list'.
346 Use optional LIST if provided instead.
347 If called interactively, read LIST using the minibuffer."
348 (interactive "PList of functions to instrument: ")
349 (unless (listp list)
350 (signal 'wrong-type-argument (list 'listp list)))
351 (let ((list (or list elp-function-list)))
352 (mapcar 'elp-instrument-function list)))
353
354 ;;;###autoload
355 (defun elp-instrument-package (prefix)
356 "Instrument for profiling, all functions which start with PREFIX.
357 For example, to instrument all ELP functions, do the following:
358
359 \\[elp-instrument-package] RET elp- RET"
360 (interactive
361 (list (completing-read "Prefix of package to instrument: "
362 obarray 'elp-profilable-p)))
363 (if (zerop (length prefix))
364 (error "Instrumenting all Emacs functions would render Emacs unusable"))
365 (elp-instrument-list
366 (mapcar
367 'intern
368 (all-completions prefix obarray 'elp-profilable-p))))
369
370 (defun elp-restore-list (&optional list)
371 "Restore the original definitions for all functions in `elp-function-list'.
372 Use optional LIST if provided instead."
373 (interactive "PList of functions to restore: ")
374 (let ((list (or list elp-function-list)))
375 (mapcar 'elp-restore-function list)))
376
377 (defun elp-restore-all ()
378 "Restore the original definitions of all functions being profiled."
379 (interactive)
380 (elp-restore-list elp-all-instrumented-list))
381
382 \f
383 (defun elp-reset-function (funsym)
384 "Reset the profiling information for FUNSYM."
385 (interactive "aFunction to reset: ")
386 (let ((info (get funsym elp-timer-info-property)))
387 (or info
388 (error "%s is not instrumented for profiling" funsym))
389 (aset info 0 0) ;reset call counter
390 (aset info 1 0.0) ;reset total time
391 ;; don't muck with aref 2 as that is the old symbol definition
392 ))
393
394 (defun elp-reset-list (&optional list)
395 "Reset the profiling information for all functions in `elp-function-list'.
396 Use optional LIST if provided instead."
397 (interactive "PList of functions to reset: ")
398 (let ((list (or list elp-function-list)))
399 (mapcar 'elp-reset-function list)))
400
401 (defun elp-reset-all ()
402 "Reset the profiling information for all functions being profiled."
403 (interactive)
404 (elp-reset-list elp-all-instrumented-list))
405
406 (defun elp-set-master (funsym)
407 "Set the master function for profiling."
408 (interactive "aMaster function: ")
409 ;; when there's a master function, recording is turned off by
410 ;; default
411 (setq elp-master funsym
412 elp-record-p nil)
413 ;; make sure master function is instrumented
414 (or (memq funsym elp-all-instrumented-list)
415 (elp-instrument-function funsym)))
416
417 (defun elp-unset-master ()
418 "Unset the master function."
419 (interactive)
420 ;; when there's no master function, recording is turned on by default.
421 (setq elp-master nil
422 elp-record-p t))
423
424 \f
425 (defsubst elp-elapsed-time (start end)
426 (+ (* (- (car end) (car start)) 65536.0)
427 (- (car (cdr end)) (car (cdr start)))
428 (/ (- (car (cdr (cdr end))) (car (cdr (cdr start)))) 1000000.0)))
429
430 (defun elp-wrapper (funsym interactive-p args)
431 "This function has been instrumented for profiling by the ELP.
432 ELP is the Emacs Lisp Profiler. To restore the function to its
433 original definition, use \\[elp-restore-function] or \\[elp-restore-all]."
434 ;; turn on recording if this is the master function
435 (if (and elp-master
436 (eq funsym elp-master))
437 (setq elp-record-p t))
438 ;; get info vector and original function symbol
439 (let* ((info (get funsym elp-timer-info-property))
440 (func (aref info 2))
441 result)
442 (or func
443 (error "%s is not instrumented for profiling" funsym))
444 (if (not elp-record-p)
445 ;; when not recording, just call the original function symbol
446 ;; and return the results.
447 (setq result
448 (if interactive-p
449 (call-interactively func)
450 (apply func args)))
451 ;; we are recording times
452 (let (enter-time exit-time)
453 ;; increment the call-counter
454 (aset info 0 (1+ (aref info 0)))
455 ;; now call the old symbol function, checking to see if it
456 ;; should be called interactively. make sure we return the
457 ;; correct value
458 (if interactive-p
459 (setq enter-time (current-time)
460 result (call-interactively func)
461 exit-time (current-time))
462 (setq enter-time (current-time)
463 result (apply func args)
464 exit-time (current-time)))
465 ;; calculate total time in function
466 (aset info 1 (+ (aref info 1) (elp-elapsed-time enter-time exit-time)))
467 ))
468 ;; turn off recording if this is the master function
469 (if (and elp-master
470 (eq funsym elp-master))
471 (setq elp-record-p nil))
472 result))
473
474 \f
475 ;; shut the byte-compiler up
476 (defvar elp-field-len nil)
477 (defvar elp-cc-len nil)
478 (defvar elp-at-len nil)
479 (defvar elp-et-len nil)
480
481 (defun elp-sort-by-call-count (vec1 vec2)
482 ;; sort by highest call count. See `sort'.
483 (>= (aref vec1 0) (aref vec2 0)))
484
485 (defun elp-sort-by-total-time (vec1 vec2)
486 ;; sort by highest total time spent in function. See `sort'.
487 (>= (aref vec1 1) (aref vec2 1)))
488
489 (defun elp-sort-by-average-time (vec1 vec2)
490 ;; sort by highest average time spent in function. See `sort'.
491 (>= (aref vec1 2) (aref vec2 2)))
492
493 (defsubst elp-pack-number (number width)
494 ;; pack the NUMBER string into WIDTH characters, watching out for
495 ;; very small or large numbers
496 (if (<= (length number) width)
497 number
498 ;; check for very large or small numbers
499 (if (string-match "^\\(.*\\)\\(e[+-].*\\)$" number)
500 (concat (substring
501 (match-string 1 number)
502 0
503 (- width (match-end 2) (- (match-beginning 2)) 3))
504 "..."
505 (match-string 2 number))
506 (substring number 0 width))))
507
508 (defun elp-output-result (resultvec)
509 ;; output the RESULTVEC into the results buffer. RESULTVEC is a 4 or
510 ;; more element vector where aref 0 is the call count, aref 1 is the
511 ;; total time spent in the function, aref 2 is the average time
512 ;; spent in the function, and aref 3 is the symbol's string
513 ;; name. All other elements in the vector are ignored.
514 (let* ((cc (aref resultvec 0))
515 (tt (aref resultvec 1))
516 (at (aref resultvec 2))
517 (symname (aref resultvec 3))
518 callcnt totaltime avetime)
519 (setq callcnt (number-to-string cc)
520 totaltime (number-to-string tt)
521 avetime (number-to-string at))
522 ;; possibly prune the results
523 (if (and elp-report-limit
524 (numberp elp-report-limit)
525 (< cc elp-report-limit))
526 nil
527 (elp-output-insert-symname symname)
528 (insert-char 32 (+ elp-field-len (- (length symname)) 2))
529 ;; print stuff out, formatting it nicely
530 (insert callcnt)
531 (insert-char 32 (+ elp-cc-len (- (length callcnt)) 2))
532 (let ((ttstr (elp-pack-number totaltime elp-et-len))
533 (atstr (elp-pack-number avetime elp-at-len)))
534 (insert ttstr)
535 (insert-char 32 (+ elp-et-len (- (length ttstr)) 2))
536 (insert atstr))
537 (insert "\n"))))
538
539 (defvar elp-results-symname-map
540 (let ((map (make-sparse-keymap)))
541 (define-key map [mouse-2] 'elp-results-jump-to-definition)
542 (define-key map [follow-link] 'mouse-face)
543 (define-key map "\C-m" 'elp-results-jump-to-definition)
544 map)
545 "Keymap used on the function name column." )
546
547 (defun elp-results-jump-to-definition (&optional event)
548 "Jump to the definition of the function under the point."
549 (interactive (list last-nonmenu-event))
550 (if event (posn-set-point (event-end event)))
551 (find-function (get-text-property (point) 'elp-symname)))
552
553 (defun elp-output-insert-symname (symname)
554 ;; Insert SYMNAME with text properties.
555 (insert (propertize symname
556 'elp-symname (intern symname)
557 'keymap elp-results-symname-map
558 'mouse-face 'highlight
559 'face 'link
560 'help-echo "mouse-2 or RET jumps to definition")))
561
562 ;;;###autoload
563 (defun elp-results ()
564 "Display current profiling results.
565 If `elp-reset-after-results' is non-nil, then current profiling
566 information for all instrumented functions is reset after results are
567 displayed."
568 (interactive)
569 (let ((curbuf (current-buffer))
570 (resultsbuf (if elp-recycle-buffers-p
571 (get-buffer-create elp-results-buffer)
572 (generate-new-buffer elp-results-buffer))))
573 (set-buffer resultsbuf)
574 (erase-buffer)
575 ;; get the length of the longest function name being profiled
576 (let* ((longest 0)
577 (title "Function Name")
578 (titlelen (length title))
579 (elp-field-len titlelen)
580 (cc-header "Call Count")
581 (elp-cc-len (length cc-header))
582 (et-header "Elapsed Time")
583 (elp-et-len (length et-header))
584 (at-header "Average Time")
585 (elp-at-len (length at-header))
586 (resvec
587 (mapcar
588 (function
589 (lambda (funsym)
590 (let* ((info (get funsym elp-timer-info-property))
591 (symname (format "%s" funsym))
592 (cc (aref info 0))
593 (tt (aref info 1)))
594 (if (not info)
595 (insert "No profiling information found for: "
596 symname)
597 (setq longest (max longest (length symname)))
598 (vector cc tt (if (zerop cc)
599 0.0 ;avoid arithmetic div-by-zero errors
600 (/ (float tt) (float cc)))
601 symname)))))
602 elp-all-instrumented-list))
603 ) ; end let*
604 ;; If printing to stdout, insert the header so it will print.
605 ;; Otherwise use header-line-format.
606 (setq elp-field-len (max titlelen longest))
607 (if (or elp-use-standard-output noninteractive)
608 (progn
609 (insert title)
610 (if (> longest titlelen)
611 (progn
612 (insert-char 32 (- longest titlelen))))
613 (insert " " cc-header " " et-header " " at-header "\n")
614 (insert-char ?= elp-field-len)
615 (insert " ")
616 (insert-char ?= elp-cc-len)
617 (insert " ")
618 (insert-char ?= elp-et-len)
619 (insert " ")
620 (insert-char ?= elp-at-len)
621 (insert "\n"))
622 (let ((column 0))
623 (setq header-line-format
624 (mapconcat
625 (lambda (title)
626 (prog1
627 (concat
628 (propertize " "
629 'display (list 'space :align-to column)
630 'face 'fixed-pitch)
631 title)
632 (setq column (+ column 1
633 (if (= column 0)
634 elp-field-len
635 (length title))))))
636 (list title cc-header et-header at-header) ""))))
637 ;; if sorting is enabled, then sort the results list. in either
638 ;; case, call elp-output-result to output the result in the
639 ;; buffer
640 (if elp-sort-by-function
641 (setq resvec (sort resvec elp-sort-by-function)))
642 (mapc 'elp-output-result resvec))
643 ;; now pop up results buffer
644 (set-buffer curbuf)
645 (pop-to-buffer resultsbuf)
646 ;; copy results to standard-output?
647 (if (or elp-use-standard-output noninteractive)
648 (princ (buffer-substring (point-min) (point-max)))
649 (goto-char (point-min)))
650 ;; reset profiling info if desired
651 (and elp-reset-after-results
652 (elp-reset-all))))
653
654 (defun elp-unload-function ()
655 "Unload the Emacs Lisp Profiler."
656 (elp-restore-all)
657 ;; continue standard unloading
658 nil)
659 \f
660 (provide 'elp)
661
662 ;;; elp.el ends here