Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1213 lines
52 KiB

4 years ago
  1. ;;; -*- Mode: LISP; Package: monitor; Syntax: Common-lisp; Base: 10.; -*-
  2. ;;; Tue Jan 25 18:32:28 1994 by Mark Kantrowitz <mkant@GLINDA.OZ.CS.CMU.EDU>
  3. ;;; ****************************************************************
  4. ;;; Metering System ************************************************
  5. ;;; ****************************************************************
  6. ;;;
  7. ;;; The Metering System is a portable Common Lisp code profiling tool.
  8. ;;; It gathers timing and consing statistics for specified functions
  9. ;;; while a program is running.
  10. ;;;
  11. ;;; The Metering System is a combination of
  12. ;;; o the Monitor package written by Chris McConnell
  13. ;;; o the Profile package written by Skef Wholey and Rob MacLachlan
  14. ;;; The two systems were merged and extended by Mark Kantrowitz.
  15. ;;;
  16. ;;; Address: Carnegie Mellon University
  17. ;;; School of Computer Science
  18. ;;; Pittsburgh, PA 15213
  19. ;;;
  20. ;;; This code is in the public domain and is distributed without warranty
  21. ;;; of any kind.
  22. ;;;
  23. ;;; This copy is from SLIME, http://www.common-lisp.net/project/slime/
  24. ;;;
  25. ;;;
  26. ;;; ********************************
  27. ;;; Change Log *********************
  28. ;;; ********************************
  29. ;;;
  30. ;;; 26-JUN-90 mk Merged functionality of Monitor and Profile packages.
  31. ;;; 26-JUN-90 mk Now handles both inclusive and exclusive statistics
  32. ;;; with respect to nested calls. (Allows it to subtract
  33. ;;; total monitoring overhead for each function, not just
  34. ;;; the time spent monitoring the function itself.)
  35. ;;; 26-JUN-90 mk The table is now saved so that one may manipulate
  36. ;;; the data (sorting it, etc.) even after the original
  37. ;;; source of the data has been cleared.
  38. ;;; 25-SEP-90 mk Added get-cons functions for Lucid 3.0, MACL 1.3.2
  39. ;;; required-arguments functions for Lucid 3.0,
  40. ;;; Franz Allegro CL, and MACL 1.3.2.
  41. ;;; 25-JAN-91 mk Now uses fdefinition if available.
  42. ;;; 25-JAN-91 mk Replaced (and :allegro (not :coral)) with :excl.
  43. ;;; Much better solution for the fact that both call
  44. ;;; themselves :allegro.
  45. ;;; 5-JUL-91 mk Fixed warning to occur only when file is loaded
  46. ;;; uncompiled.
  47. ;;; 5-JUL-91 mk When many unmonitored functions, print out number
  48. ;;; instead of whole list.
  49. ;;; 24-MAR-92 mk Updated for CLtL2 compatibility. space measuring
  50. ;;; doesn't work in MCL, but fixed so that timing
  51. ;;; statistics do.
  52. ;;; 26-MAR-92 mk Updated for Lispworks. Replaced :ccl with
  53. ;;; (and :ccl (not :lispworks)).
  54. ;;; 27-MAR-92 mk Added get-cons for Allegro-V4.0.
  55. ;;; 01-JAN-93 mk v2.0 Support for MCL 2.0, CMU CL 16d, Allegro V3.1/4.0/4.1,
  56. ;;; Lucid 4.0, ibcl
  57. ;;; 25-JAN-94 mk v2.1 Patches for CLISP from Bruno Haible.
  58. ;;; 01-APR-05 lgorrie Removed support for all Lisps except CLISP and OpenMCL.
  59. ;;; Purely to cut down on stale code (e.g. #+cltl2) in this
  60. ;;; version that is bundled with SLIME.
  61. ;;; 22-Aug-08 stas Define TIME-TYPE for Clozure CL.
  62. ;;; 07-Aug-12 heller Break lines at 80 columns
  63. ;;;
  64. ;;; ********************************
  65. ;;; To Do **************************
  66. ;;; ********************************
  67. ;;;
  68. ;;; - Need get-cons for Allegro, AKCL.
  69. ;;; - Speed up monitoring code. Replace use of hash tables with an embedded
  70. ;;; offset in an array so that it will be faster than using gethash.
  71. ;;; (i.e., svref/closure reference is usually faster than gethash).
  72. ;;; - Beware of (get-internal-run-time) overflowing. Yikes!
  73. ;;; - Check robustness with respect to profiled functions.
  74. ;;; - Check logic of computing inclusive and exclusive time and consing.
  75. ;;; Especially wrt incf/setf comment below. Should be incf, so we
  76. ;;; sum recursive calls.
  77. ;;; - Add option to record caller statistics -- this would list who
  78. ;;; called which functions and how often.
  79. ;;; - switches to turn timing/CONSING statistics collection on/off.
  80. ;;; ********************************
  81. ;;; Notes **************************
  82. ;;; ********************************
  83. ;;;
  84. ;;; METERING has been tested (successfully) in the following lisps:
  85. ;;; CMU Common Lisp (16d, Python Compiler 1.0 ) :new-compiler
  86. ;;; CMU Common Lisp (M2.9 15-Aug-90, Compiler M1.8 15-Aug-90)
  87. ;;; Macintosh Allegro Common Lisp (1.3.2)
  88. ;;; Macintosh Common Lisp (2.0)
  89. ;;; ExCL (Franz Allegro CL 3.1.12 [DEC 3100] 11/19/90) :allegro-v3.1
  90. ;;; ExCL (Franz Allegro CL 4.0.1 [Sun4] 2/8/91) :allegro-v4.0
  91. ;;; ExCL (Franz Allegro CL 4.1 [SPARC R1] 8/28/92 14:06) :allegro-v4.1
  92. ;;; ExCL (Franz ACL 5.0.1 [Linux/X86] 6/29/99 16:11) :allegro-v5.0.1
  93. ;;; Lucid CL (Version 2.1 6-DEC-87)
  94. ;;; Lucid Common Lisp (3.0)
  95. ;;; Lucid Common Lisp (4.0.1 HP-700 12-Aug-91)
  96. ;;; AKCL (1.86, June 30, 1987 or later)
  97. ;;; Ibuki Common Lisp (Version 2, release 01.027)
  98. ;;; CLISP (January 1994)
  99. ;;;
  100. ;;; METERING needs to be tested in the following lisps:
  101. ;;; Symbolics Common Lisp (8.0)
  102. ;;; KCL (June 3, 1987 or later)
  103. ;;; TI (Release 4.1 or later)
  104. ;;; Golden Common Lisp (3.1 IBM-PC)
  105. ;;; VAXLisp (2.0, 3.1)
  106. ;;; Procyon Common Lisp
  107. ;;; ****************************************************************
  108. ;;; Documentation **************************************************
  109. ;;; ****************************************************************
  110. ;;;
  111. ;;; This system runs in any valid Common Lisp. Four small
  112. ;;; implementation-dependent changes can be made to improve performance
  113. ;;; and prettiness. In the section labelled "Implementation Dependent
  114. ;;; Changes" below, you should tailor the functions REQUIRED-ARGUMENTS,
  115. ;;; GET-CONS, GET-TIME, and TIME-UNITS-PER-SECOND to your implementation
  116. ;;; for the best results. If GET-CONS is not specified for your
  117. ;;; implementation, no consing information will be reported. The other
  118. ;;; functions will default to working forms, albeit inefficient, in
  119. ;;; non-CMU implementations. If you tailor these functions for a particular
  120. ;;; version of Common Lisp, we'd appreciate receiving the code.
  121. ;;;
  122. ;;; ****************************************************************
  123. ;;; Usage Notes ****************************************************
  124. ;;; ****************************************************************
  125. ;;;
  126. ;;; SUGGESTED USAGE:
  127. ;;;
  128. ;;; Start by monitoring big pieces of the program, then carefully choose
  129. ;;; which functions close to, but not in, the inner loop are to be
  130. ;;; monitored next. Don't monitor functions that are called by other
  131. ;;; monitored functions: you will only confuse yourself.
  132. ;;;
  133. ;;; If the per-call time reported is less than 1/10th of a second, then
  134. ;;; consider the clock resolution and profiling overhead before you believe
  135. ;;; the time. It may be that you will need to run your program many times
  136. ;;; in order to average out to a higher resolution.
  137. ;;;
  138. ;;; The easiest way to use this package is to load it and execute either
  139. ;;; (swank-monitor:with-monitoring (names*) ()
  140. ;;; your-forms*)
  141. ;;; or
  142. ;;; (swank-monitor:monitor-form your-form)
  143. ;;; The former allows you to specify which functions will be monitored; the
  144. ;;; latter monitors all functions in the current package. Both automatically
  145. ;;; produce a table of statistics. Other variants can be constructed from
  146. ;;; the monitoring primitives, which are described below, along with a
  147. ;;; fuller description of these two macros.
  148. ;;;
  149. ;;; For best results, compile this file before using.
  150. ;;;
  151. ;;;
  152. ;;; CLOCK RESOLUTION:
  153. ;;;
  154. ;;; Unless you are very lucky, the length of your machine's clock "tick" is
  155. ;;; probably much longer than the time it takes a simple function to run.
  156. ;;; For example, on the IBM RT, the clock resolution is 1/50th of a second.
  157. ;;; This means that if a function is only called a few times, then only the
  158. ;;; first couple of decimal places are really meaningful.
  159. ;;;
  160. ;;;
  161. ;;; MONITORING OVERHEAD:
  162. ;;;
  163. ;;; The added monitoring code takes time to run every time that the monitored
  164. ;;; function is called, which can disrupt the attempt to collect timing
  165. ;;; information. In order to avoid serious inflation of the times for functions
  166. ;;; that take little time to run, an estimate of the overhead due to monitoring
  167. ;;; is subtracted from the times reported for each function.
  168. ;;;
  169. ;;; Although this correction works fairly well, it is not totally accurate,
  170. ;;; resulting in times that become increasingly meaningless for functions
  171. ;;; with short runtimes. For example, subtracting the estimated overhead
  172. ;;; may result in negative times for some functions. This is only a concern
  173. ;;; when the estimated profiling overhead is many times larger than
  174. ;;; reported total CPU time.
  175. ;;;
  176. ;;; If you monitor functions that are called by monitored functions, in
  177. ;;; :inclusive mode the monitoring overhead for the inner function is
  178. ;;; subtracted from the CPU time for the outer function. [We do this by
  179. ;;; counting for each function not only the number of calls to *this*
  180. ;;; function, but also the number of monitored calls while it was running.]
  181. ;;; In :exclusive mode this is not necessary, since we subtract the
  182. ;;; monitoring time of inner functions, overhead & all.
  183. ;;;
  184. ;;; Otherwise, the estimated monitoring overhead is not represented in the
  185. ;;; reported total CPU time. The sum of total CPU time and the estimated
  186. ;;; monitoring overhead should be close to the total CPU time for the
  187. ;;; entire monitoring run (as determined by TIME).
  188. ;;;
  189. ;;; A timing overhead factor is computed at load time. This will be incorrect
  190. ;;; if the monitoring code is run in a different environment than this file
  191. ;;; was loaded in. For example, saving a core image on a high performance
  192. ;;; machine and running it on a low performance one will result in the use
  193. ;;; of an erroneously small overhead factor.
  194. ;;;
  195. ;;;
  196. ;;; If your times vary widely, possible causes are:
  197. ;;; - Garbage collection. Try turning it off, then running your code.
  198. ;;; Be warned that monitoring code will probably cons when it does
  199. ;;; (get-internal-run-time).
  200. ;;; - Swapping. If you have enough memory, execute your form once
  201. ;;; before monitoring so that it will be swapped into memory. Otherwise,
  202. ;;; get a bigger machine!
  203. ;;; - Resolution of internal-time-units-per-second. If this value is
  204. ;;; too low, then the timings become wild. You can try executing more
  205. ;;; of whatever your test is, but that will only work if some of your
  206. ;;; paths do not match the timer resolution.
  207. ;;; internal-time-units-per-second is so coarse -- on a Symbolics it is
  208. ;;; 977, in MACL it is 60.
  209. ;;;
  210. ;;;
  211. ;;; ****************************************************************
  212. ;;; Interface ******************************************************
  213. ;;; ****************************************************************
  214. ;;;
  215. ;;; WITH-MONITORING (&rest functions) [Macro]
  216. ;;; (&optional (nested :exclusive)
  217. ;;; (threshold 0.01)
  218. ;;; (key :percent-time))
  219. ;;; &body body
  220. ;;; The named functions will be set up for monitoring, the body forms executed,
  221. ;;; a table of results printed, and the functions unmonitored. The nested,
  222. ;;; threshold, and key arguments are passed to report-monitoring below.
  223. ;;;
  224. ;;; MONITOR-FORM form [Macro]
  225. ;;; &optional (nested :exclusive)
  226. ;;; (threshold 0.01)
  227. ;;; (key :percent-time)
  228. ;;; All functions in the current package are set up for monitoring while
  229. ;;; the form is executed, and automatically unmonitored after a table of
  230. ;;; results has been printed. The nested, threshold, and key arguments
  231. ;;; are passed to report-monitoring below.
  232. ;;;
  233. ;;; *MONITORED-FUNCTIONS* [Variable]
  234. ;;; This holds a list of all functions that are currently being monitored.
  235. ;;;
  236. ;;; MONITOR &rest names [Macro]
  237. ;;; The named functions will be set up for monitoring by augmenting
  238. ;;; their function definitions with code that gathers statistical information
  239. ;;; about code performance. As with the TRACE macro, the function names are
  240. ;;; not evaluated. Calls the function SWANK-MONITOR::MONITORING-ENCAPSULATE on each
  241. ;;; function name. If no names are specified, returns a list of all
  242. ;;; monitored functions.
  243. ;;;
  244. ;;; If name is not a symbol, it is evaled to return the appropriate
  245. ;;; closure. This allows you to monitor closures stored anywhere like
  246. ;;; in a variable, array or structure. Most other monitoring packages
  247. ;;; can't handle this.
  248. ;;;
  249. ;;; MONITOR-ALL &optional (package *package*) [Function]
  250. ;;; Monitors all functions in the specified package, which defaults to
  251. ;;; the current package.
  252. ;;;
  253. ;;; UNMONITOR &rest names [Macro]
  254. ;;; Removes monitoring code from the named functions. If no names are
  255. ;;; specified, all currently monitored functions are unmonitored.
  256. ;;;
  257. ;;; RESET-MONITORING-INFO name [Function]
  258. ;;; Resets the monitoring statistics for the specified function.
  259. ;;;
  260. ;;; RESET-ALL-MONITORING [Function]
  261. ;;; Resets the monitoring statistics for all monitored functions.
  262. ;;;
  263. ;;; MONITORED name [Function]
  264. ;;; Predicate to test whether a function is monitored.
  265. ;;;
  266. ;;; REPORT-MONITORING &optional names [Function]
  267. ;;; (nested :exclusive)
  268. ;;; (threshold 0.01)
  269. ;;; (key :percent-time)
  270. ;;; Creates a table of monitoring information for the specified list
  271. ;;; of names, and displays the table using display-monitoring-results.
  272. ;;; If names is :all or nil, uses all currently monitored functions.
  273. ;;; Takes the following arguments:
  274. ;;; - NESTED specifies whether nested calls of monitored functions
  275. ;;; are included in the times for monitored functions.
  276. ;;; o If :inclusive, the per-function information is for the entire
  277. ;;; duration of the monitored function, including any calls to
  278. ;;; other monitored functions. If functions A and B are monitored,
  279. ;;; and A calls B, then the accumulated time and consing for A will
  280. ;;; include the time and consing of B. Note: if a function calls
  281. ;;; itself recursively, the time spent in the inner call(s) may
  282. ;;; be counted several times.
  283. ;;; o If :exclusive, the information excludes time attributed to
  284. ;;; calls to other monitored functions. This is the default.
  285. ;;; - THRESHOLD specifies that only functions which have been executed
  286. ;;; more than threshold percent of the time will be reported. Defaults
  287. ;;; to 1%. If a threshold of 0 is specified, all functions are listed,
  288. ;;; even those with 0 or negative running times (see note on overhead).
  289. ;;; - KEY specifies that the table be sorted by one of the following
  290. ;;; sort keys:
  291. ;;; :function alphabetically by function name
  292. ;;; :percent-time by percent of total execution time
  293. ;;; :percent-cons by percent of total consing
  294. ;;; :calls by number of times the function was called
  295. ;;; :time-per-call by average execution time per function
  296. ;;; :cons-per-call by average consing per function
  297. ;;; :time same as :percent-time
  298. ;;; :cons same as :percent-cons
  299. ;;;
  300. ;;; REPORT &key (names :all) [Function]
  301. ;;; (nested :exclusive)
  302. ;;; (threshold 0.01)
  303. ;;; (sort-key :percent-time)
  304. ;;; (ignore-no-calls nil)
  305. ;;;
  306. ;;; Same as REPORT-MONITORING but we use a nicer keyword interface.
  307. ;;;
  308. ;;; DISPLAY-MONITORING-RESULTS &optional (threshold 0.01) [Function]
  309. ;;; (key :percent-time)
  310. ;;; Prints a table showing for each named function:
  311. ;;; - the total CPU time used in that function for all calls
  312. ;;; - the total number of bytes consed in that function for all calls
  313. ;;; - the total number of calls
  314. ;;; - the average amount of CPU time per call
  315. ;;; - the average amount of consing per call
  316. ;;; - the percent of total execution time spent executing that function
  317. ;;; - the percent of total consing spent consing in that function
  318. ;;; Summary totals of the CPU time, consing, and calls columns are printed.
  319. ;;; An estimate of the monitoring overhead is also printed. May be run
  320. ;;; even after unmonitoring all the functions, to play with the data.
  321. ;;;
  322. ;;; SAMPLE TABLE:
  323. #|
  324. Cons
  325. % % Per Total Total
  326. Function Time Cons Calls Sec/Call Call Time Cons
  327. ----------------------------------------------------------------------
  328. FIND-ROLE: 0.58 0.00 136 0.003521 0 0.478863 0
  329. GROUP-ROLE: 0.35 0.00 365 0.000802 0 0.292760 0
  330. GROUP-PROJECTOR: 0.05 0.00 102 0.000408 0 0.041648 0
  331. FEATURE-P: 0.02 0.00 570 0.000028 0 0.015680 0
  332. ----------------------------------------------------------------------
  333. TOTAL: 1173 0.828950 0
  334. Estimated total monitoring overhead: 0.88 seconds
  335. |#
  336. ;;; ****************************************************************
  337. ;;; METERING *******************************************************
  338. ;;; ****************************************************************
  339. ;;; ********************************
  340. ;;; Warn people using the wrong Lisp
  341. ;;; ********************************
  342. #-(or clisp openmcl)
  343. (warn "metering.lisp does not support your Lisp implementation!")
  344. ;;; ********************************
  345. ;;; Packages ***********************
  346. ;;; ********************************
  347. ;;; For CLtL2 compatible lisps
  348. (defpackage "SWANK-MONITOR" (:use "COMMON-LISP")
  349. (:export "*MONITORED-FUNCTIONS*"
  350. "MONITOR" "MONITOR-ALL" "UNMONITOR" "MONITOR-FORM"
  351. "WITH-MONITORING"
  352. "RESET-MONITORING-INFO" "RESET-ALL-MONITORING"
  353. "MONITORED"
  354. "REPORT-MONITORING"
  355. "DISPLAY-MONITORING-RESULTS"
  356. "MONITORING-ENCAPSULATE" "MONITORING-UNENCAPSULATE"
  357. "REPORT"))
  358. (in-package "SWANK-MONITOR")
  359. ;;; Warn user if they're loading the source instead of compiling it first.
  360. (eval-when (eval)
  361. (warn "This file should be compiled before loading for best results."))
  362. ;;; ********************************
  363. ;;; Version ************************
  364. ;;; ********************************
  365. (defparameter *metering-version* "v2.1 25-JAN-94"
  366. "Current version number/date for Metering.")
  367. ;;; ****************************************************************
  368. ;;; Implementation Dependent Definitions ***************************
  369. ;;; ****************************************************************
  370. ;;; ********************************
  371. ;;; Timing Functions ***************
  372. ;;; ********************************
  373. ;;; The get-time function is called to find the total number of ticks since
  374. ;;; the beginning of time. time-units-per-second allows us to convert units
  375. ;;; to seconds.
  376. #-(or clisp openmcl)
  377. (eval-when (compile eval)
  378. (warn
  379. "You may want to supply implementation-specific get-time functions."))
  380. (defconstant time-units-per-second internal-time-units-per-second)
  381. #+openmcl
  382. (progn
  383. (deftype time-type () 'unsigned-byte)
  384. (deftype consing-type () 'unsigned-byte))
  385. (defmacro get-time ()
  386. `(the time-type (get-internal-run-time)))
  387. ;;; NOTE: In Macintosh Common Lisp, CCL::GCTIME returns the number of
  388. ;;; milliseconds spent during GC. We could subtract this from
  389. ;;; the value returned by get-internal-run-time to eliminate
  390. ;;; the effect of GC on the timing values, but we prefer to let
  391. ;;; the user run without GC on. If the application is so big that
  392. ;;; it requires GC to complete, then the GC times are part of the
  393. ;;; cost of doing business, and will average out in the long run.
  394. ;;; If it seems really important to a user that GC times not be
  395. ;;; counted, then uncomment the following three lines and read-time
  396. ;;; conditionalize the definition of get-time above with #-:openmcl.
  397. ;#+openmcl
  398. ;(defmacro get-time ()
  399. ; `(the time-type (- (get-internal-run-time) (ccl:gctime))))
  400. ;;; ********************************
  401. ;;; Consing Functions **************
  402. ;;; ********************************
  403. ;;; The get-cons macro is called to find the total number of bytes
  404. ;;; consed since the beginning of time.
  405. #+clisp
  406. (defun get-cons ()
  407. (multiple-value-bind (real1 real2 run1 run2 gc1 gc2 space1 space2 gccount)
  408. (sys::%%time)
  409. (declare (ignore real1 real2 run1 run2 gc1 gc2 gccount))
  410. (dpb space1 (byte 24 24) space2)))
  411. ;;; Macintosh Common Lisp 2.0
  412. ;;; Note that this includes bytes that were allocated during GC.
  413. ;;; We could subtract this out by advising GC like we did under
  414. ;;; MCL 1.3.2, but I'd rather users ran without GC. If they can't
  415. ;;; run without GC, then the bytes consed during GC are a cost of
  416. ;;; running their program. Metering the code a few times will
  417. ;;; avoid the consing values being too lopsided. If a user really really
  418. ;;; wants to subtract out the consing during GC, replace the following
  419. ;;; two lines with the commented out code.
  420. #+openmcl
  421. (defmacro get-cons () `(the consing-type (ccl::total-bytes-allocated)))
  422. #-(or clisp openmcl)
  423. (progn
  424. (eval-when (compile eval)
  425. (warn "No consing will be reported unless a get-cons function is ~
  426. defined."))
  427. (defmacro get-cons () '(the consing-type 0)))
  428. ;; actually, neither `get-cons' nor `get-time' are used as is,
  429. ;; but only in the following macro `with-time/cons'
  430. #-:clisp
  431. (defmacro with-time/cons ((delta-time delta-cons) form &body post-process)
  432. (let ((start-cons (gensym "START-CONS-"))
  433. (start-time (gensym "START-TIME-")))
  434. `(let ((,start-time (get-time)) (,start-cons (get-cons)))
  435. (declare (type time-type ,start-time)
  436. (type consing-type ,start-cons))
  437. (multiple-value-prog1 ,form
  438. (let ((,delta-time (- (get-time) ,start-time))
  439. (,delta-cons (- (get-cons) ,start-cons)))
  440. ,@post-process)))))
  441. #+clisp
  442. (progn
  443. (defmacro delta4 (nv1 nv2 ov1 ov2 by)
  444. `(- (dpb (- ,nv1 ,ov1) (byte ,by ,by) ,nv2) ,ov2))
  445. (let ((del (find-symbol "DELTA4" "SYS")))
  446. (when del (setf (fdefinition 'delta4) (fdefinition del))))
  447. (if (< internal-time-units-per-second 1000000)
  448. ;; TIME_1: AMIGA, OS/2, UNIX_TIMES
  449. (defmacro delta4-time (new-time1 new-time2 old-time1 old-time2)
  450. `(delta4 ,new-time1 ,new-time2 ,old-time1 ,old-time2 16))
  451. ;; TIME_2: other UNIX, WIN32
  452. (defmacro delta4-time (new-time1 new-time2 old-time1 old-time2)
  453. `(+ (* (- ,new-time1 ,old-time1) internal-time-units-per-second)
  454. (- ,new-time2 ,old-time2))))
  455. (defmacro delta4-cons (new-cons1 new-cons2 old-cons1 old-cons2)
  456. `(delta4 ,new-cons1 ,new-cons2 ,old-cons1 ,old-cons2 24))
  457. ;; avoid consing: when the application conses a lot,
  458. ;; get-cons may return a bignum, so we really should not use it.
  459. (defmacro with-time/cons ((delta-time delta-cons) form &body post-process)
  460. (let ((beg-cons1 (gensym "BEG-CONS1-")) (end-cons1 (gensym "END-CONS1-"))
  461. (beg-cons2 (gensym "BEG-CONS2-")) (end-cons2 (gensym "END-CONS2-"))
  462. (beg-time1 (gensym "BEG-TIME1-")) (end-time1 (gensym "END-TIME1-"))
  463. (beg-time2 (gensym "BEG-TIME2-")) (end-time2 (gensym "END-TIME2-"))
  464. (re1 (gensym)) (re2 (gensym)) (gc1 (gensym)) (gc2 (gensym)))
  465. `(multiple-value-bind (,re1 ,re2 ,beg-time1 ,beg-time2
  466. ,gc1 ,gc2 ,beg-cons1 ,beg-cons2)
  467. (sys::%%time)
  468. (declare (ignore ,re1 ,re2 ,gc1 ,gc2))
  469. (multiple-value-prog1 ,form
  470. (multiple-value-bind (,re1 ,re2 ,end-time1 ,end-time2
  471. ,gc1 ,gc2 ,end-cons1 ,end-cons2)
  472. (sys::%%time)
  473. (declare (ignore ,re1 ,re2 ,gc1 ,gc2))
  474. (let ((,delta-time (delta4-time ,end-time1 ,end-time2
  475. ,beg-time1 ,beg-time2))
  476. (,delta-cons (delta4-cons ,end-cons1 ,end-cons2
  477. ,beg-cons1 ,beg-cons2)))
  478. ,@post-process)))))))
  479. ;;; ********************************
  480. ;;; Required Arguments *************
  481. ;;; ********************************
  482. ;;;
  483. ;;; Required (Fixed) vs Optional Args
  484. ;;;
  485. ;;; To avoid unnecessary consing in the "encapsulation" code, we find out the
  486. ;;; number of required arguments, and use &rest to capture only non-required
  487. ;;; arguments. The function Required-Arguments returns two values: the first
  488. ;;; is the number of required arguments, and the second is T iff there are any
  489. ;;; non-required arguments (e.g. &optional, &rest, &key).
  490. ;;; Lucid, Allegro, and Macintosh Common Lisp
  491. #+openmcl
  492. (defun required-arguments (name)
  493. (let* ((function (symbol-function name))
  494. (args (ccl:arglist function))
  495. (pos (position-if #'(lambda (x)
  496. (and (symbolp x)
  497. (let ((name (symbol-name x)))
  498. (and (>= (length name) 1)
  499. (char= (schar name 0)
  500. #\&)))))
  501. args)))
  502. (if pos
  503. (values pos t)
  504. (values (length args) nil))))
  505. #+clisp
  506. (defun required-arguments (name)
  507. (multiple-value-bind (name req-num opt-num rest-p key-p keywords allow-p)
  508. (sys::function-signature name t)
  509. (if name ; no error
  510. (values req-num (or (/= 0 opt-num) rest-p key-p keywords allow-p))
  511. (values 0 t))))
  512. #-(or clisp openmcl)
  513. (progn
  514. (eval-when (compile eval)
  515. (warn
  516. "You may want to add an implementation-specific ~
  517. Required-Arguments function."))
  518. (eval-when (load eval)
  519. (defun required-arguments (name)
  520. (declare (ignore name))
  521. (values 0 t))))
  522. #|
  523. ;;;Examples
  524. (defun square (x) (* x x))
  525. (defun square2 (x &optional y) (* x x y))
  526. (defun test (x y &optional (z 3)) 3)
  527. (defun test2 (x y &optional (z 3) &rest fred) 3)
  528. (required-arguments 'square) => 1 nil
  529. (required-arguments 'square2) => 1 t
  530. (required-arguments 'test) => 2 t
  531. (required-arguments 'test2) => 2 t
  532. |#
  533. ;;; ****************************************************************
  534. ;;; Main METERING Code *********************************************
  535. ;;; ****************************************************************
  536. ;;; ********************************
  537. ;;; Global Variables ***************
  538. ;;; ********************************
  539. (defvar *MONITOR-TIME-OVERHEAD* nil
  540. "The amount of time an empty monitored function costs.")
  541. (defvar *MONITOR-CONS-OVERHEAD* nil
  542. "The amount of cons an empty monitored function costs.")
  543. (defvar *TOTAL-TIME* 0
  544. "Total amount of time monitored so far.")
  545. (defvar *TOTAL-CONS* 0
  546. "Total amount of consing monitored so far.")
  547. (defvar *TOTAL-CALLS* 0
  548. "Total number of calls monitored so far.")
  549. (proclaim '(type time-type *total-time*))
  550. (proclaim '(type consing-type *total-cons*))
  551. (proclaim '(fixnum *total-calls*))
  552. ;;; ********************************
  553. ;;; Accessor Functions *************
  554. ;;; ********************************
  555. ;;; Perhaps the SYMBOLP should be FBOUNDP? I.e., what about variables
  556. ;;; containing closures.
  557. (defmacro PLACE-FUNCTION (function-place)
  558. "Return the function found at FUNCTION-PLACE. Evals FUNCTION-PLACE
  559. if it isn't a symbol, to allow monitoring of closures located in
  560. variables/arrays/structures."
  561. ;; Note that (fboundp 'fdefinition) returns T even if fdefinition
  562. ;; is a macro, which is what we want.
  563. (if (fboundp 'fdefinition)
  564. `(if (fboundp ,function-place)
  565. (fdefinition ,function-place)
  566. (eval ,function-place))
  567. `(if (symbolp ,function-place)
  568. (symbol-function ,function-place)
  569. (eval ,function-place))))
  570. (defsetf PLACE-FUNCTION (function-place) (function)
  571. "Set the function in FUNCTION-PLACE to FUNCTION."
  572. (if (fboundp 'fdefinition)
  573. ;; If we're conforming to CLtL2, use fdefinition here.
  574. `(if (fboundp ,function-place)
  575. (setf (fdefinition ,function-place) ,function)
  576. (eval '(setf ,function-place ',function)))
  577. `(if (symbolp ,function-place)
  578. (setf (symbol-function ,function-place) ,function)
  579. (eval '(setf ,function-place ',function)))))
  580. #|
  581. ;;; before using fdefinition
  582. (defun PLACE-FUNCTION (function-place)
  583. "Return the function found at FUNCTION-PLACE. Evals FUNCTION-PLACE
  584. if it isn't a symbol, to allow monitoring of closures located in
  585. variables/arrays/structures."
  586. (if (symbolp function-place)
  587. (symbol-function function-place)
  588. (eval function-place)))
  589. (defsetf PLACE-FUNCTION (function-place) (function)
  590. "Set the function in FUNCTION-PLACE to FUNCTION."
  591. `(if (symbolp ,function-place)
  592. (setf (symbol-function ,function-place) ,function)
  593. (eval '(setf ,function-place ',function))))
  594. |#
  595. (defun PLACE-FBOUNDP (function-place)
  596. "Test to see if FUNCTION-PLACE is a function."
  597. ;; probably should be
  598. #|(or (and (symbolp function-place)(fboundp function-place))
  599. (functionp (place-function function-place)))|#
  600. (if (symbolp function-place)
  601. (fboundp function-place)
  602. (functionp (place-function function-place))))
  603. (defun PLACE-MACROP (function-place)
  604. "Test to see if FUNCTION-PLACE is a macro."
  605. (when (symbolp function-place)
  606. (macro-function function-place)))
  607. ;;; ********************************
  608. ;;; Measurement Tables *************
  609. ;;; ********************************
  610. (defvar *monitored-functions* nil
  611. "List of monitored symbols.")
  612. ;;; We associate a METERING-FUNCTIONS structure with each monitored function
  613. ;;; name or other closure. This holds the functions that we call to manipulate
  614. ;;; the closure which implements the encapsulation.
  615. ;;;
  616. (defstruct metering-functions
  617. (name nil)
  618. (old-definition nil :type function)
  619. (new-definition nil :type function)
  620. (read-metering nil :type function)
  621. (reset-metering nil :type function))
  622. ;;; In general using hash tables in time-critical programs is a bad idea,
  623. ;;; because when one has to grow the table and rehash everything, the
  624. ;;; timing becomes grossly inaccurate. In this case it is not an issue
  625. ;;; because all inserting of entries in the hash table occurs before the
  626. ;;; timing commences. The only circumstance in which this could be a
  627. ;;; problem is if the lisp rehashes on the next reference to the table,
  628. ;;; instead of when the entry which forces a rehash was inserted.
  629. ;;;
  630. ;;; Note that a similar kind of problem can occur with GC, which is why
  631. ;;; one should turn off GC when monitoring code.
  632. ;;;
  633. (defvar *monitor* (make-hash-table :test #'equal)
  634. "Hash table in which METERING-FUNCTIONS structures are stored.")
  635. (defun get-monitor-info (name)
  636. (gethash name *monitor*))
  637. (defsetf get-monitor-info (name) (info)
  638. `(setf (gethash ,name *monitor*) ,info))
  639. (defun MONITORED (function-place)
  640. "Test to see if a FUNCTION-PLACE is monitored."
  641. (and (place-fboundp function-place) ; this line necessary?
  642. (get-monitor-info function-place)))
  643. (defun reset-monitoring-info (name)
  644. "Reset the monitoring info for the specified function."
  645. (let ((finfo (get-monitor-info name)))
  646. (when finfo
  647. (funcall (metering-functions-reset-metering finfo)))))
  648. (defun reset-all-monitoring ()
  649. "Reset monitoring info for all functions."
  650. (setq *total-time* 0
  651. *total-cons* 0
  652. *total-calls* 0)
  653. (dolist (symbol *monitored-functions*)
  654. (when (monitored symbol)
  655. (reset-monitoring-info symbol))))
  656. (defun monitor-info-values (name &optional (nested :exclusive) warn)
  657. "Returns monitoring information values for the named function,
  658. adjusted for overhead."
  659. (let ((finfo (get-monitor-info name)))
  660. (if finfo
  661. (multiple-value-bind (inclusive-time inclusive-cons
  662. exclusive-time exclusive-cons
  663. calls nested-calls)
  664. (funcall (metering-functions-read-metering finfo))
  665. (unless (or (null warn)
  666. (eq (place-function name)
  667. (metering-functions-new-definition finfo)))
  668. (warn "Funtion ~S has been redefined, so times may be inaccurate.~@
  669. MONITOR it again to record calls to the new definition."
  670. name))
  671. (case nested
  672. (:exclusive (values calls
  673. nested-calls
  674. (- exclusive-time
  675. (* calls *monitor-time-overhead*))
  676. (- exclusive-cons
  677. (* calls *monitor-cons-overhead*))))
  678. ;; In :inclusive mode, subtract overhead for all the
  679. ;; called functions as well. Nested-calls includes the
  680. ;; calls of the function as well. [Necessary 'cause of
  681. ;; functions which call themselves recursively.]
  682. (:inclusive (values calls
  683. nested-calls
  684. (- inclusive-time
  685. (* nested-calls ;(+ calls)
  686. *monitor-time-overhead*))
  687. (- inclusive-cons
  688. (* nested-calls ;(+ calls)
  689. *monitor-cons-overhead*))))))
  690. (values 0 0 0 0))))
  691. ;;; ********************************
  692. ;;; Encapsulate ********************
  693. ;;; ********************************
  694. (eval-when (compile load eval)
  695. ;; Returns a lambda expression for a function that, when called with the
  696. ;; function name, will set up that function for metering.
  697. ;;
  698. ;; A function is monitored by replacing its definition with a closure
  699. ;; created by the following function. The closure records the monitoring
  700. ;; data, and updates the data with each call of the function.
  701. ;;
  702. ;; Other closures are used to read and reset the data.
  703. (defun make-monitoring-encapsulation (min-args optionals-p)
  704. (let (required-args)
  705. (dotimes (i min-args) (push (gensym) required-args))
  706. `(lambda (name)
  707. (let ((inclusive-time 0)
  708. (inclusive-cons 0)
  709. (exclusive-time 0)
  710. (exclusive-cons 0)
  711. (calls 0)
  712. (nested-calls 0)
  713. (old-definition (place-function name)))
  714. (declare (type time-type inclusive-time)
  715. (type time-type exclusive-time)
  716. (type consing-type inclusive-cons)
  717. (type consing-type exclusive-cons)
  718. (fixnum calls)
  719. (fixnum nested-calls))
  720. (pushnew name *monitored-functions*)
  721. (setf (place-function name)
  722. #'(lambda (,@required-args
  723. ,@(when optionals-p
  724. `(&rest optional-args)))
  725. (let ((prev-total-time *total-time*)
  726. (prev-total-cons *total-cons*)
  727. (prev-total-calls *total-calls*)
  728. ;; (old-time inclusive-time)
  729. ;; (old-cons inclusive-cons)
  730. ;; (old-nested-calls nested-calls)
  731. )
  732. (declare (type time-type prev-total-time)
  733. (type consing-type prev-total-cons)
  734. (fixnum prev-total-calls))
  735. (with-time/cons (delta-time delta-cons)
  736. ;; form
  737. ,(if optionals-p
  738. `(apply old-definition
  739. ,@required-args optional-args)
  740. `(funcall old-definition ,@required-args))
  741. ;; post-processing:
  742. ;; Calls
  743. (incf calls)
  744. (incf *total-calls*)
  745. ;; nested-calls includes this call
  746. (incf nested-calls (the fixnum
  747. (- *total-calls*
  748. prev-total-calls)))
  749. ;; (setf nested-calls (+ old-nested-calls
  750. ;; (- *total-calls*
  751. ;; prev-total-calls)))
  752. ;; Time
  753. ;; Problem with inclusive time is that it
  754. ;; currently doesn't add values from recursive
  755. ;; calls to the same function. Change the
  756. ;; setf to an incf to fix this?
  757. (incf inclusive-time (the time-type delta-time))
  758. ;; (setf inclusive-time (+ delta-time old-time))
  759. (incf exclusive-time (the time-type
  760. (+ delta-time
  761. (- prev-total-time
  762. *total-time*))))
  763. (setf *total-time* (the time-type
  764. (+ delta-time
  765. prev-total-time)))
  766. ;; Consing
  767. (incf inclusive-cons (the consing-type delta-cons))
  768. ;; (setf inclusive-cons (+ delta-cons old-cons))
  769. (incf exclusive-cons (the consing-type
  770. (+ delta-cons
  771. (- prev-total-cons
  772. *total-cons*))))
  773. (setf *total-cons*
  774. (the consing-type
  775. (+ delta-cons prev-total-cons)))))))
  776. (setf (get-monitor-info name)
  777. (make-metering-functions
  778. :name name
  779. :old-definition old-definition
  780. :new-definition (place-function name)
  781. :read-metering #'(lambda ()
  782. (values inclusive-time
  783. inclusive-cons
  784. exclusive-time
  785. exclusive-cons
  786. calls
  787. nested-calls))
  788. :reset-metering #'(lambda ()
  789. (setq inclusive-time 0
  790. inclusive-cons 0
  791. exclusive-time 0
  792. exclusive-cons 0
  793. calls 0
  794. nested-calls 0)
  795. t)))))))
  796. );; End of EVAL-WHEN
  797. ;;; For efficiency reasons, we precompute the encapsulation functions
  798. ;;; for a variety of combinations of argument structures
  799. ;;; (min-args . optional-p). These are stored in the following hash table
  800. ;;; along with any new ones we encounter. Since we're now precomputing
  801. ;;; closure functions for common argument signatures, this eliminates
  802. ;;; the former need to call COMPILE for each monitored function.
  803. (eval-when (compile eval)
  804. (defconstant precomputed-encapsulations 8))
  805. (defvar *existing-encapsulations* (make-hash-table :test #'equal))
  806. (defun find-encapsulation (min-args optionals-p)
  807. (or (gethash (cons min-args optionals-p) *existing-encapsulations*)
  808. (setf (gethash (cons min-args optionals-p) *existing-encapsulations*)
  809. (compile nil
  810. (make-monitoring-encapsulation min-args optionals-p)))))
  811. (macrolet ((frob ()
  812. (let ((res ()))
  813. (dotimes (i precomputed-encapsulations)
  814. (push `(setf (gethash '(,i . nil) *existing-encapsulations*)
  815. #',(make-monitoring-encapsulation i nil))
  816. res)
  817. (push `(setf (gethash '(,i . t) *existing-encapsulations*)
  818. #',(make-monitoring-encapsulation i t))
  819. res))
  820. `(progn ,@res))))
  821. (frob))
  822. (defun monitoring-encapsulate (name &optional warn)
  823. "Monitor the function Name. If already monitored, unmonitor first."
  824. ;; Saves the current definition of name and inserts a new function which
  825. ;; returns the result of evaluating body.
  826. (cond ((not (place-fboundp name)) ; not a function
  827. (when warn
  828. (warn "Ignoring undefined function ~S." name)))
  829. ((place-macrop name) ; a macro
  830. (when warn
  831. (warn "Ignoring macro ~S." name)))
  832. (t ; tis a function
  833. (when (get-monitor-info name) ; monitored
  834. (when warn
  835. (warn "~S already monitored, so unmonitoring it first." name))
  836. (monitoring-unencapsulate name))
  837. (multiple-value-bind (min-args optionals-p)
  838. (required-arguments name)
  839. (funcall (find-encapsulation min-args optionals-p) name)))))
  840. (defun monitoring-unencapsulate (name &optional warn)
  841. "Removes monitoring encapsulation code from around Name."
  842. (let ((finfo (get-monitor-info name)))
  843. (when finfo ; monitored
  844. (remprop name 'metering-functions)
  845. (setq *monitored-functions*
  846. (remove name *monitored-functions* :test #'equal))
  847. (if (eq (place-function name)
  848. (metering-functions-new-definition finfo))
  849. (setf (place-function name)
  850. (metering-functions-old-definition finfo))
  851. (when warn
  852. (warn "Preserving current definition of redefined function ~S."
  853. name))))))
  854. ;;; ********************************
  855. ;;; Main Monitoring Functions ******
  856. ;;; ********************************
  857. (defmacro MONITOR (&rest names)
  858. "Monitor the named functions. As in TRACE, the names are not evaluated.
  859. If a function is already monitored, then unmonitor and remonitor (useful
  860. to notice function redefinition). If a name is undefined, give a warning
  861. and ignore it. See also unmonitor, report-monitoring,
  862. display-monitoring-results and reset-time."
  863. `(progn
  864. ,@(mapcar #'(lambda (name) `(monitoring-encapsulate ',name)) names)
  865. *monitored-functions*))
  866. (defmacro UNMONITOR (&rest names)
  867. "Remove the monitoring on the named functions.
  868. Names defaults to the list of all currently monitored functions."
  869. `(dolist (name ,(if names `',names '*monitored-functions*) (values))
  870. (monitoring-unencapsulate name)))
  871. (defun MONITOR-ALL (&optional (package *package*))
  872. "Monitor all functions in the specified package."
  873. (let ((package (if (packagep package)
  874. package
  875. (find-package package))))
  876. (do-symbols (symbol package)
  877. (when (eq (symbol-package symbol) package)
  878. (monitoring-encapsulate symbol)))))
  879. (defmacro MONITOR-FORM (form
  880. &optional (nested :exclusive) (threshold 0.01)
  881. (key :percent-time))
  882. "Monitor the execution of all functions in the current package
  883. during the execution of FORM. All functions that are executed above
  884. THRESHOLD % will be reported."
  885. `(unwind-protect
  886. (progn
  887. (monitor-all)
  888. (reset-all-monitoring)
  889. (prog1
  890. (time ,form)
  891. (report-monitoring :all ,nested ,threshold ,key :ignore-no-calls)))
  892. (unmonitor)))
  893. (defmacro WITH-MONITORING ((&rest functions)
  894. (&optional (nested :exclusive)
  895. (threshold 0.01)
  896. (key :percent-time))
  897. &body body)
  898. "Monitor the specified functions during the execution of the body."
  899. `(unwind-protect
  900. (progn
  901. (dolist (fun ',functions)
  902. (monitoring-encapsulate fun))
  903. (reset-all-monitoring)
  904. ,@body
  905. (report-monitoring :all ,nested ,threshold ,key))
  906. (unmonitor)))
  907. ;;; ********************************
  908. ;;; Overhead Calculations **********
  909. ;;; ********************************
  910. (defconstant overhead-iterations 5000
  911. "Number of iterations over which the timing overhead is averaged.")
  912. ;;; Perhaps this should return something to frustrate clever compilers.
  913. (defun STUB-FUNCTION (x)
  914. (declare (ignore x))
  915. nil)
  916. (proclaim '(notinline stub-function))
  917. (defun SET-MONITOR-OVERHEAD ()
  918. "Determines the average overhead of monitoring by monitoring the execution
  919. of an empty function many times."
  920. (setq *monitor-time-overhead* 0
  921. *monitor-cons-overhead* 0)
  922. (stub-function nil)
  923. (monitor stub-function)
  924. (reset-all-monitoring)
  925. (let ((overhead-function (symbol-function 'stub-function)))
  926. (dotimes (x overhead-iterations)
  927. (funcall overhead-function overhead-function)))
  928. ; (dotimes (x overhead-iterations)
  929. ; (stub-function nil))
  930. (let ((fiter (float overhead-iterations)))
  931. (multiple-value-bind (calls nested-calls time cons)
  932. (monitor-info-values 'stub-function)
  933. (declare (ignore calls nested-calls))
  934. (setq *monitor-time-overhead* (/ time fiter)
  935. *monitor-cons-overhead* (/ cons fiter))))
  936. (unmonitor stub-function))
  937. (set-monitor-overhead)
  938. ;;; ********************************
  939. ;;; Report Data ********************
  940. ;;; ********************************
  941. (defvar *monitor-results* nil
  942. "A table of monitoring statistics is stored here.")
  943. (defvar *no-calls* nil
  944. "A list of monitored functions which weren't called.")
  945. (defvar *estimated-total-overhead* 0)
  946. ;; (proclaim '(type time-type *estimated-total-overhead*))
  947. (defstruct (monitoring-info
  948. (:conc-name m-info-)
  949. (:constructor make-monitoring-info
  950. (name calls time cons
  951. percent-time percent-cons
  952. time-per-call cons-per-call)))
  953. name
  954. calls
  955. time
  956. cons
  957. percent-time
  958. percent-cons
  959. time-per-call
  960. cons-per-call)
  961. (defun REPORT (&key (names :all)
  962. (nested :exclusive)
  963. (threshold 0.01)
  964. (sort-key :percent-time)
  965. (ignore-no-calls nil))
  966. "Same as REPORT-MONITORING but with a nicer keyword interface"
  967. (declare (type (member :function :percent-time :time :percent-cons
  968. :cons :calls :time-per-call :cons-per-call)
  969. sort-key)
  970. (type (member :inclusive :exclusive) nested))
  971. (report-monitoring names nested threshold sort-key ignore-no-calls))
  972. (defun REPORT-MONITORING (&optional names
  973. (nested :exclusive)
  974. (threshold 0.01)
  975. (key :percent-time)
  976. ignore-no-calls)
  977. "Report the current monitoring state.
  978. The percentage of the total time spent executing unmonitored code
  979. in each function (:exclusive mode), or total time (:inclusive mode)
  980. will be printed together with the number of calls and
  981. the unmonitored time per call. Functions that have been executed
  982. below THRESHOLD % of the time will not be reported. To report on all
  983. functions set NAMES to be either NIL or :ALL."
  984. (when (or (null names) (eq names :all)) (setq names *monitored-functions*))
  985. (let ((total-time 0)
  986. (total-cons 0)
  987. (total-calls 0))
  988. ;; Compute overall time and consing.
  989. (dolist (name names)
  990. (multiple-value-bind (calls nested-calls time cons)
  991. (monitor-info-values name nested :warn)
  992. (declare (ignore nested-calls))
  993. (incf total-calls calls)
  994. (incf total-time time)
  995. (incf total-cons cons)))
  996. ;; Total overhead.
  997. (setq *estimated-total-overhead*
  998. (/ (* *monitor-time-overhead* total-calls)
  999. time-units-per-second))
  1000. ;; Assemble data for only the specified names (all monitored functions)
  1001. (if (zerop total-time)
  1002. (format *trace-output* "Not enough execution time to monitor.")
  1003. (progn
  1004. (setq *monitor-results* nil *no-calls* nil)
  1005. (dolist (name names)
  1006. (multiple-value-bind (calls nested-calls time cons)
  1007. (monitor-info-values name nested)
  1008. (declare (ignore nested-calls))
  1009. (when (minusp time) (setq time 0.0))
  1010. (when (minusp cons) (setq cons 0.0))
  1011. (if (zerop calls)
  1012. (push (if (symbolp name)
  1013. (symbol-name name)
  1014. (format nil "~S" name))
  1015. *no-calls*)
  1016. (push (make-monitoring-info
  1017. (format nil "~S" name) ; name
  1018. calls ; calls
  1019. (/ time (float time-units-per-second)) ; time in secs
  1020. (round cons) ; consing
  1021. (/ time (float total-time)) ; percent-time
  1022. (if (zerop total-cons) 0
  1023. (/ cons (float total-cons))) ; percent-cons
  1024. (/ (/ time (float calls)) ; time-per-call
  1025. time-units-per-second) ; sec/call
  1026. (round (/ cons (float calls)))) ; cons-per-call
  1027. *monitor-results*))))
  1028. (display-monitoring-results threshold key ignore-no-calls)))))
  1029. (defun display-monitoring-results (&optional (threshold 0.01)
  1030. (key :percent-time)
  1031. (ignore-no-calls t))
  1032. (let ((max-length 8) ; Function header size
  1033. (max-cons-length 8)
  1034. (total-time 0.0)
  1035. (total-consed 0)
  1036. (total-calls 0)
  1037. (total-percent-time 0)
  1038. (total-percent-cons 0))
  1039. (sort-results key)
  1040. (dolist (result *monitor-results*)
  1041. (when (or (zerop threshold)
  1042. (> (m-info-percent-time result) threshold))
  1043. (setq max-length
  1044. (max max-length
  1045. (length (m-info-name result))))
  1046. (setq max-cons-length
  1047. (max max-cons-length
  1048. (m-info-cons-per-call result)))))
  1049. (incf max-length 2)
  1050. (setf max-cons-length (+ 2 (ceiling (log max-cons-length 10))))
  1051. (format *trace-output*
  1052. "~%~%~
  1053. ~VT ~VA~
  1054. ~% ~VT % % ~VA ~
  1055. Total Total~
  1056. ~%Function~VT Time Cons Calls Sec/Call ~VA ~
  1057. Time Cons~
  1058. ~%~V,,,'-A"
  1059. max-length
  1060. max-cons-length "Cons"
  1061. max-length
  1062. max-cons-length "Per"
  1063. max-length
  1064. max-cons-length "Call"
  1065. (+ max-length 62 (max 0 (- max-cons-length 5))) "-")
  1066. (dolist (result *monitor-results*)
  1067. (when (or (zerop threshold)
  1068. (> (m-info-percent-time result) threshold))
  1069. (format *trace-output*
  1070. "~%~A:~VT~6,2F ~6,2F ~7D ~,6F ~VD ~8,3F ~10D"
  1071. (m-info-name result)
  1072. max-length
  1073. (* 100 (m-info-percent-time result))
  1074. (* 100 (m-info-percent-cons result))
  1075. (m-info-calls result)
  1076. (m-info-time-per-call result)
  1077. max-cons-length
  1078. (m-info-cons-per-call result)
  1079. (m-info-time result)
  1080. (m-info-cons result))
  1081. (incf total-time (m-info-time result))
  1082. (incf total-consed (m-info-cons result))
  1083. (incf total-calls (m-info-calls result))
  1084. (incf total-percent-time (m-info-percent-time result))
  1085. (incf total-percent-cons (m-info-percent-cons result))))
  1086. (format *trace-output*
  1087. "~%~V,,,'-A~
  1088. ~%TOTAL:~VT~6,2F ~6,2F ~7D ~9@T ~VA ~8,3F ~10D~
  1089. ~%Estimated monitoring overhead: ~5,2F seconds~
  1090. ~%Estimated total monitoring overhead: ~5,2F seconds"
  1091. (+ max-length 62 (max 0 (- max-cons-length 5))) "-"
  1092. max-length
  1093. (* 100 total-percent-time)
  1094. (* 100 total-percent-cons)
  1095. total-calls
  1096. max-cons-length " "
  1097. total-time total-consed
  1098. (/ (* *monitor-time-overhead* total-calls)
  1099. time-units-per-second)
  1100. *estimated-total-overhead*)
  1101. (when (and (not ignore-no-calls) *no-calls*)
  1102. (setq *no-calls* (sort *no-calls* #'string<))
  1103. (let ((num-no-calls (length *no-calls*)))
  1104. (if (> num-no-calls 20)
  1105. (format *trace-output*
  1106. "~%~@(~r~) monitored functions were not called. ~
  1107. ~%See the variable swank-monitor::*no-calls* for a list."
  1108. num-no-calls)
  1109. (format *trace-output*
  1110. "~%The following monitored functions were not called:~
  1111. ~%~{~<~%~:; ~A~>~}~%"
  1112. *no-calls*))))
  1113. (values)))
  1114. (defun sort-results (&optional (key :percent-time))
  1115. (setq *monitor-results*
  1116. (case key
  1117. (:function (sort *monitor-results* #'string>
  1118. :key #'m-info-name))
  1119. ((:percent-time :time) (sort *monitor-results* #'>
  1120. :key #'m-info-time))
  1121. ((:percent-cons :cons) (sort *monitor-results* #'>
  1122. :key #'m-info-cons))
  1123. (:calls (sort *monitor-results* #'>
  1124. :key #'m-info-calls))
  1125. (:time-per-call (sort *monitor-results* #'>
  1126. :key #'m-info-time-per-call))
  1127. (:cons-per-call (sort *monitor-results* #'>
  1128. :key #'m-info-cons-per-call)))))
  1129. ;;; *END OF FILE*