1.0.5.39: sb-sprof call counting
[sbcl.git] / contrib / sb-sprof / sb-sprof.lisp
1 ;;; Copyright (C) 2003 Gerd Moellmann <gerd.moellmann@t-online.de>
2 ;;; All rights reserved.
3 ;;;
4 ;;; Redistribution and use in source and binary forms, with or without
5 ;;; modification, are permitted provided that the following conditions
6 ;;; are met:
7 ;;;
8 ;;; 1. Redistributions of source code must retain the above copyright
9 ;;;    notice, this list of conditions and the following disclaimer.
10 ;;; 2. Redistributions in binary form must reproduce the above copyright
11 ;;;    notice, this list of conditions and the following disclaimer in the
12 ;;;    documentation and/or other materials provided with the distribution.
13 ;;; 3. The name of the author may not be used to endorse or promote
14 ;;;    products derived from this software without specific prior written
15 ;;;    permission.
16 ;;;
17 ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
18 ;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 ;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 ;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
21 ;;; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 ;;; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
23 ;;; OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 ;;; BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 ;;; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 ;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
27 ;;; USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
28 ;;; DAMAGE.
29
30 ;;; Statistical profiler.
31
32 ;;; Overview:
33 ;;;
34 ;;; This profiler arranges for SIGPROF interrupts to interrupt a
35 ;;; running program at regular intervals.  Each time a SIGPROF occurs,
36 ;;; the current program counter and return address is recorded in a
37 ;;; vector, until a configurable maximum number of samples have been
38 ;;; taken.
39 ;;;
40 ;;; A profiling report is generated from the samples array by
41 ;;; determining the Lisp functions corresponding to the recorded
42 ;;; addresses.  Each program counter/return address pair forms one
43 ;;; edge in a call graph.
44
45 ;;; Problems:
46 ;;;
47 ;;; The code being generated on x86 makes determining callers reliably
48 ;;; something between extremely difficult and impossible.  Example:
49 ;;;
50 ;;; 10979F00:       .entry eval::eval-stack-args(arg-count)
51 ;;;       18:       pop     dword ptr [ebp-8]
52 ;;;       1B:       lea     esp, [ebp-32]
53 ;;;       1E:       mov     edi, edx
54 ;;;
55 ;;;       20:       cmp     ecx, 4
56 ;;;       23:       jne     L4
57 ;;;       29:       mov     [ebp-12], edi
58 ;;;       2C:       mov     dword ptr [ebp-16], #x28F0000B ; nil
59 ;;;                                              ; No-arg-parsing entry point
60 ;;;       33:       mov     dword ptr [ebp-20], 0
61 ;;;       3A:       jmp     L3
62 ;;;       3C: L0:   mov     edx, esp
63 ;;;       3E:       sub     esp, 12
64 ;;;       41:       mov     eax, [#x10979EF8]    ; #<FDEFINITION object for eval::eval-stack-pop>
65 ;;;       47:       xor     ecx, ecx
66 ;;;       49:       mov     [edx-4], ebp
67 ;;;       4C:       mov     ebp, edx
68 ;;;       4E:       call    dword ptr [eax+5]
69 ;;;       51:       mov     esp, ebx
70 ;;;
71 ;;; Suppose this function is interrupted by SIGPROF at 4E.  At that
72 ;;; point, the frame pointer EBP has been modified so that the
73 ;;; original return address of the caller of eval-stack-args is no
74 ;;; longer where it can be found by x86-call-context, and the new
75 ;;; return address, for the call to eval-stack-pop, is not yet on the
76 ;;; stack.  The effect is that x86-call-context returns something
77 ;;; bogus, which leads to wrong edges in the call graph.
78 ;;;
79 ;;; One thing that one might try is filtering cases where the program
80 ;;; is interrupted at a call instruction.  But since the above example
81 ;;; of an interrupt at a call instruction isn't the only case where
82 ;;; the stack is something x86-call-context can't really cope with,
83 ;;; this is not a general solution.
84 ;;;
85 ;;; Random ideas for implementation:
86 ;;;
87 ;;; * Space profiler.  Sample when new pages are allocated instead of
88 ;;; at SIGPROF.
89 ;;;
90 ;;; * Record a configurable number of callers up the stack.  That
91 ;;; could give a more complete graph when there are many small
92 ;;; functions.
93 ;;;
94 ;;; * Print help strings for reports, include hints to the problem
95 ;;; explained above.
96 ;;;
97 ;;; * Make flat report the default since call-graph isn't that
98 ;;; reliable?
99
100 (defpackage #:sb-sprof
101   (:use #:cl #:sb-ext #:sb-unix #:sb-alien #:sb-sys)
102   (:export #:*sample-interval* #:*max-samples* #:*alloc-interval*
103            #:start-sampling #:stop-sampling #:with-sampling
104            #:with-profiling #:start-profiling #:stop-profiling
105            #:profile-call-counts #:unprofile-call-counts
106            #:reset #:report))
107
108 (in-package #:sb-sprof)
109
110 \f
111 ;;;; Graph Utilities
112
113 (defstruct (vertex (:constructor make-vertex)
114                    (:constructor make-scc (scc-vertices edges)))
115   (visited     nil :type boolean)
116   (root        nil :type (or null vertex))
117   (dfn           0 :type fixnum)
118   (edges        () :type list)
119   (scc-vertices () :type list))
120
121 (defstruct edge
122   (vertex (sb-impl::missing-arg) :type vertex))
123
124 (defstruct graph
125   (vertices () :type list))
126
127 (declaim (inline scc-p))
128 (defun scc-p (vertex)
129   (not (null (vertex-scc-vertices vertex))))
130
131 (defmacro do-vertices ((vertex graph) &body body)
132   `(dolist (,vertex (graph-vertices ,graph))
133      ,@body))
134
135 (defmacro do-edges ((edge edge-to vertex) &body body)
136   `(dolist (,edge (vertex-edges ,vertex))
137      (let ((,edge-to (edge-vertex ,edge)))
138        ,@body)))
139
140 (defun self-cycle-p (vertex)
141   (do-edges (e to vertex)
142     (when (eq to vertex)
143       (return t))))
144
145 (defun map-vertices (fn vertices)
146   (dolist (v vertices)
147     (setf (vertex-visited v) nil))
148   (dolist (v vertices)
149     (unless (vertex-visited v)
150       (funcall fn v))))
151
152 ;;; Eeko Nuutila, Eljas Soisalon-Soininen, around 1992.  Improves on
153 ;;; Tarjan's original algorithm by not using the stack when processing
154 ;;; trivial components.  Trivial components should appear frequently
155 ;;; in a call-graph such as ours, I think.  Same complexity O(V+E) as
156 ;;; Tarjan.
157 (defun strong-components (vertices)
158   (let ((in-component (make-array (length vertices)
159                                   :element-type 'boolean
160                                   :initial-element nil))
161         (stack ())
162         (components ())
163         (dfn -1))
164     (labels ((min-root (x y)
165                (let ((rx (vertex-root x))
166                      (ry (vertex-root y)))
167                  (if (< (vertex-dfn rx) (vertex-dfn ry))
168                      rx
169                      ry)))
170              (in-component (v)
171                (aref in-component (vertex-dfn v)))
172              ((setf in-component) (in v)
173                (setf (aref in-component (vertex-dfn v)) in))
174              (vertex-> (x y)
175                (> (vertex-dfn x) (vertex-dfn y)))
176              (visit (v)
177                (setf (vertex-dfn v) (incf dfn)
178                      (in-component v) nil
179                      (vertex-root v) v
180                      (vertex-visited v) t)
181                (do-edges (e w v)
182                  (unless (vertex-visited w)
183                    (visit w))
184                  (unless (in-component w)
185                    (setf (vertex-root v) (min-root v w))))
186                (if (eq v (vertex-root v))
187                    (loop while (and stack (vertex-> (car stack) v))
188                          as w = (pop stack)
189                          collect w into this-component
190                          do (setf (in-component w) t)
191                          finally
192                            (setf (in-component v) t)
193                            (push (cons v this-component) components))
194                    (push v stack))))
195       (map-vertices #'visit vertices)
196       components)))
197
198 ;;; Given a dag as a list of vertices, return the list sorted
199 ;;; topologically, children first.
200 (defun topological-sort (dag)
201   (let ((sorted ())
202         (dfn -1))
203     (labels ((rec-sort (v)
204                (setf (vertex-visited v) t)
205                (setf (vertex-dfn v) (incf dfn))
206                (dolist (e (vertex-edges v))
207                  (unless (vertex-visited (edge-vertex e))
208                    (rec-sort (edge-vertex e))))
209                (push v sorted)))
210       (map-vertices #'rec-sort dag)
211       (nreverse sorted))))
212
213 ;;; Reduce graph G to a dag by coalescing strongly connected components
214 ;;; into vertices.  Sort the result topologically.
215 (defun reduce-graph (graph &optional (scc-constructor #'make-scc))
216   (sb-int:collect ((sccs) (trivial))
217     (dolist (c (strong-components (graph-vertices graph)))
218       (if (or (cdr c) (self-cycle-p (car c)))
219           (sb-int:collect ((outgoing))
220             (dolist (v c)
221               (do-edges (e w v)
222                 (unless (member w c)
223                   (outgoing e))))
224             (sccs (funcall scc-constructor c (outgoing))))
225           (trivial (car c))))
226     (dolist (scc (sccs))
227       (dolist (v (trivial))
228         (do-edges (e w v)
229           (when (member w (vertex-scc-vertices scc))
230             (setf (edge-vertex e) scc)))))
231     (setf (graph-vertices graph)
232           (topological-sort (nconc (sccs) (trivial))))))
233 \f
234 ;;;; The Profiler
235
236 (deftype address ()
237   "Type used for addresses, for instance, program counters,
238    code start/end locations etc."
239   '(unsigned-byte #.sb-vm::n-machine-word-bits))
240
241 (defconstant +unknown-address+ 0
242   "Constant representing an address that cannot be determined.")
243
244 ;;; A call graph.  Vertices are NODE structures, edges are CALL
245 ;;; structures.
246 (defstruct (call-graph (:include graph)
247                        (:constructor %make-call-graph))
248   ;; the value of *SAMPLE-INTERVAL* or *ALLOC-INTERVAL* at the time
249   ;; the graph was created (depending on the current allocation mode)
250   (sample-interval (sb-impl::missing-arg) :type number)
251   ;; the sampling-mode that was used for the profiling run
252   (sampling-mode (sb-impl::missing-arg) :type (member :cpu :alloc))
253   ;; number of samples taken
254   (nsamples (sb-impl::missing-arg) :type sb-int:index)
255   ;; sample count for samples not in any function
256   (elsewhere-count (sb-impl::missing-arg) :type sb-int:index)
257   ;; a flat list of NODEs, sorted by sample count
258   (flat-nodes () :type list))
259
260 ;;; A node in a call graph, representing a function that has been
261 ;;; sampled.  The edges of a node are CALL structures that represent
262 ;;; functions called from a given node.
263 (defstruct (node (:include vertex)
264                  (:constructor %make-node))
265   ;; A numeric label for the node.  The most frequently called function
266   ;; gets label 1.  This is just for identification purposes in the
267   ;; profiling report.
268   (index 0 :type fixnum)
269   ;; Start and end address of the function's code. Depending on the
270   ;; debug-info, this might be either as absolute addresses for things
271   ;; that won't move around in memory, or as relative offsets from
272   ;; some point for things that might move.
273   (start-pc-or-offset 0 :type address)
274   (end-pc-or-offset 0 :type address)
275   ;; the name of the function
276   (name nil :type t)
277   ;; sample count for this function
278   (count 0 :type fixnum)
279   ;; count including time spent in functions called from this one
280   (accrued-count 0 :type fixnum)
281   ;; the debug-info that this node was created from
282   (debug-info nil :type t)
283   ;; list of NODEs for functions calling this one
284   (callers () :type list)
285   ;; the call count for the function that corresponds to this node (or NIL
286   ;; if call counting wasn't enabled for this function)
287   (call-count nil :type (or null integer)))
288
289 ;;; A cycle in a call graph.  The functions forming the cycle are
290 ;;; found in the SCC-VERTICES slot of the VERTEX structure.
291 (defstruct (cycle (:include node)))
292
293 ;;; An edge in a call graph.  EDGE-VERTEX is the function being
294 ;;; called.
295 (defstruct (call (:include edge)
296                  (:constructor make-call (vertex)))
297   ;; number of times the call was sampled
298   (count 1 :type sb-int:index))
299
300 ;;; Encapsulate all the information about a sampling run
301 (defstruct (samples)
302   ;; When this vector fills up, we allocate a new one and copy over
303   ;; the old contents.
304   (vector (make-array (* *max-samples*
305                          ;; Arbitrary guess at how many samples we'll be
306                          ;; taking for each trace. The exact amount doesn't
307                          ;; matter, this is just to decrease the amount of
308                          ;; re-allocation that will need to be done.
309                          10
310                          ;; Each sample takes two cells in the vector
311                          2))
312           :type simple-vector)
313   (trace-count 0 :type sb-int:index)
314   (index 0 :type sb-int:index)
315   (mode nil :type (member :cpu :alloc))
316   (sample-interval *sample-interval* :type number)
317   (alloc-interval *alloc-interval* :type number)
318   (max-depth most-positive-fixnum :type number)
319   (max-samples *max-samples* :type sb-int:index))
320
321 (defmethod print-object ((call-graph call-graph) stream)
322   (print-unreadable-object (call-graph stream :type t :identity t)
323     (format stream "~d samples" (call-graph-nsamples call-graph))))
324
325 (defmethod print-object ((node node) stream)
326   (print-unreadable-object (node stream :type t :identity t)
327     (format stream "~s [~d]" (node-name node) (node-index node))))
328
329 (defmethod print-object ((call call) stream)
330   (print-unreadable-object (call stream :type t :identity t)
331     (format stream "~s [~d]" (node-name (call-vertex call))
332             (node-index (call-vertex call)))))
333
334 (deftype report-type ()
335   '(member nil :flat :graph))
336
337 (defvar *sampling-mode* :cpu
338   "Default sampling mode. :CPU for cpu profiling, :ALLOC for allocation
339 profiling")
340 (declaim (type (member :cpu :alloc) *sampling-mode*))
341
342 (defvar *sample-interval* 0.01
343   "Default number of seconds between samples.")
344 (declaim (number *sample-interval*))
345
346 (defvar *alloc-region-size*
347   #-gencgc
348   4096
349   ;; This hardcoded 2 matches the one in gc_find_freeish_pages. It's not
350   ;; really worth genesifying.
351   #+gencgc
352   (* 2 sb-vm:gencgc-page-size))
353 (declaim (number *alloc-region-size*))
354
355 (defvar *alloc-interval* 4
356   "Default number of allocation region openings between samples.")
357 (declaim (number *alloc-interval*))
358
359 (defvar *max-samples* 50000
360   "Default number of traces taken. This variable is somewhat misnamed:
361 each trace may actually consist of an arbitrary number of samples, depending
362 on the depth of the call stack.")
363 (declaim (type sb-int:index *max-samples*))
364
365 (defvar *samples* nil)
366 (declaim (type (or null samples) *samples*))
367
368 (defvar *profiling* nil)
369 (defvar *sampling* nil)
370 (declaim (type boolean *profiling* *sampling*))
371
372 (defvar *show-progress* nil)
373
374 (defvar *old-sampling* nil)
375
376 ;; Call count encapsulation information
377 (defvar *encapsulations* (make-hash-table :test 'equal))
378
379 (defun turn-off-sampling ()
380   (setq *old-sampling* *sampling*)
381   (setq *sampling* nil))
382
383 (defun turn-on-sampling ()
384   (setq *sampling* *old-sampling*))
385
386 (defun show-progress (format-string &rest args)
387   (when *show-progress*
388     (apply #'format t format-string args)
389     (finish-output)))
390
391 (defun start-sampling ()
392   "Switch on statistical sampling."
393   (setq *sampling* t))
394
395 (defun stop-sampling ()
396   "Switch off statistical sampling."
397   (setq *sampling* nil))
398
399 (defmacro with-sampling ((&optional (on t)) &body body)
400   "Evaluate body with statistical sampling turned on or off."
401   `(let ((*sampling* ,on)
402          (sb-vm:*alloc-signal* sb-vm:*alloc-signal*))
403      ,@body))
404
405 ;;; Return something serving as debug info for address PC.
406 (declaim (inline debug-info))
407 (defun debug-info (pc)
408   (declare (type system-area-pointer pc)
409            (muffle-conditions compiler-note))
410   (let ((ptr (sb-di::component-ptr-from-pc pc)))
411     (cond ((sap= ptr (int-sap 0))
412            (let ((name (sap-foreign-symbol pc)))
413              (if name
414                  (values (format nil "foreign function ~a" name)
415                          (sap-int pc))
416                  (values nil (sap-int pc)))))
417           (t
418            (let* ((code (sb-di::component-from-component-ptr ptr))
419                   (code-header-len (* (sb-kernel:get-header-data code)
420                                       sb-vm:n-word-bytes))
421                   (pc-offset (- (sap-int pc)
422                                 (- (sb-kernel:get-lisp-obj-address code)
423                                    sb-vm:other-pointer-lowtag)
424                                 code-header-len))
425                   (df (sb-di::debug-fun-from-pc code pc-offset)))
426              (cond ((typep df 'sb-di::bogus-debug-fun)
427                     (values code (sap-int pc)))
428                    (df
429                     ;; The code component might be moved by the GC. Store
430                     ;; a PC offset, and reconstruct the data in
431                     ;; SAMPLE-PC-FROM-PC-OR-OFFSET.
432                     (values df pc-offset))
433                    (t
434                     (values nil 0))))))))
435
436 (defun ensure-samples-vector (samples)
437   (let ((vector (samples-vector samples))
438         (index (samples-index samples)))
439     ;; Allocate a new sample vector if the old one is full
440     (if (= (length vector) index)
441         (let ((new-vector (make-array (* 2 index))))
442           (format *trace-output* "Profiler sample vector full (~a traces / ~a samples), doubling the size~%"
443                   (samples-trace-count samples)
444                   (truncate index 2))
445           (replace new-vector vector)
446           (setf (samples-vector samples) new-vector))
447         vector)))
448
449 (declaim (inline record))
450 (defun record (samples pc)
451   (declare (type system-area-pointer pc)
452            (muffle-conditions compiler-note))
453   (multiple-value-bind (info pc-or-offset)
454       (debug-info pc)
455     (let ((vector (ensure-samples-vector samples))
456           (index (samples-index samples)))
457       (declare (type simple-vector vector))
458       ;; Allocate a new sample vector if the old one is full
459       (when (= (length vector) index)
460         (let ((new-vector (make-array (* 2 index))))
461           (format *trace-output* "Profiler sample vector full (~a traces / ~a samples), doubling the size~%"
462                   (samples-trace-count samples)
463                   (truncate index 2))
464           (replace new-vector vector)
465           (setf vector new-vector
466                 (samples-vector samples) new-vector)))
467       ;; For each sample, store the debug-info and the PC/offset into
468       ;; adjacent cells.
469       (setf (aref vector index) info
470             (aref vector (1+ index)) pc-or-offset)))
471   (incf (samples-index samples) 2))
472
473 (defun record-trace-start (samples)
474   ;; Mark the start of the trace.
475   (let ((vector (ensure-samples-vector samples)))
476     (declare (type simple-vector vector))
477     (setf (aref vector (samples-index samples))
478           'trace-start))
479   (incf (samples-index samples) 2))
480
481 ;;; Ensure that only one thread at a time will be executing sigprof handler.
482 (defvar *sigprof-handler-lock* (sb-thread:make-mutex :name "SIGPROF handler"))
483
484 ;;; SIGPROF handler.  Record current PC and return address in
485 ;;; *SAMPLES*.
486 #+(or x86 x86-64)
487 (defun sigprof-handler (signal code scp)
488   (declare (ignore signal code)
489            (optimize speed (space 0))
490            (muffle-conditions compiler-note)
491            (disable-package-locks sb-di::x86-call-context)
492            (type system-area-pointer scp))
493   (sb-sys:without-interrupts
494     (let ((sb-vm:*alloc-signal* nil)
495           (samples *samples*))
496       (when (and *sampling*
497                  samples
498                  (< (samples-trace-count samples)
499                     (samples-max-samples samples)))
500         (sb-sys:without-gcing
501           (sb-thread:with-mutex (*sigprof-handler-lock*)
502             (with-alien ((scp (* os-context-t) :local scp))
503               (let* ((pc-ptr (sb-vm:context-pc scp))
504                      (fp (sb-vm::context-register scp #.sb-vm::ebp-offset)))
505                 ;; For some reason completely bogus small values for the
506                 ;; frame pointer are returned every now and then, leading
507                 ;; to segfaults. Try to avoid these cases.
508                 ;;
509                 ;; FIXME: Do a more thorough sanity check on ebp, or figure
510                 ;; out why this is happening.
511                 ;; -- JES, 2005-01-11
512                 (when (< fp 4096)
513                   (return-from sigprof-handler nil))
514                 (incf (samples-trace-count samples))
515                 (let ((fp (int-sap fp))
516                       (ok t))
517                   (declare (type system-area-pointer fp pc-ptr))
518                   ;; FIXME: How annoying. The XC doesn't store enough
519                   ;; type information about SB-DI::X86-CALL-CONTEXT,
520                   ;; even if we declaim the ftype explicitly in
521                   ;; src/code/debug-int. And for some reason that type
522                   ;; information is needed for the inlined version to
523                   ;; be compiled without boxing the returned saps. So
524                   ;; we declare the correct ftype here manually, even
525                   ;; if the compiler should be able to deduce this
526                   ;; exact same information.
527                   (declare (ftype (function (system-area-pointer)
528                                             (values (member nil t)
529                                                     system-area-pointer
530                                                     system-area-pointer))
531                                   sb-di::x86-call-context))
532                   (record-trace-start samples)
533                   (dotimes (i (samples-max-depth samples))
534                     (record samples pc-ptr)
535                     (setf (values ok pc-ptr fp)
536                           (sb-di::x86-call-context fp))
537                     (unless ok
538                       (return))))))))))
539     ;; Reset the allocation counter
540     (when (and sb-vm:*alloc-signal*
541                (<= sb-vm:*alloc-signal* 0))
542       (setf sb-vm:*alloc-signal* (1- *alloc-interval*)))
543     nil))
544
545 ;; FIXME: On non-x86 platforms we don't yet walk the call stack deeper
546 ;; than one level.
547 #-(or x86 x86-64)
548 (defun sigprof-handler (signal code scp)
549   (declare (ignore signal code))
550   (sb-sys:without-interrupts
551     (let ((samples *samples*))
552       (when (and *sampling*
553                  samples
554                  (< (samples-trace-count samples)
555                     (samples-max-samples samples)))
556         (sb-sys:without-gcing
557           (with-alien ((scp (* os-context-t) :local scp))
558             (locally (declare (optimize (inhibit-warnings 2)))
559               (record-trace-start samples)
560               (let* ((pc-ptr (sb-vm:context-pc scp))
561                      (fp (sb-vm::context-register scp #.sb-vm::cfp-offset))
562                      (ra (sap-ref-word
563                           (int-sap fp)
564                           (* sb-vm::lra-save-offset sb-vm::n-word-bytes))))
565                 (record samples pc-ptr)
566                 (record samples (int-sap ra))))))))))
567
568 ;;; Return the start address of CODE.
569 (defun code-start (code)
570   (declare (type sb-kernel:code-component code))
571   (sap-int (sb-kernel:code-instructions code)))
572
573 ;;; Return start and end address of CODE as multiple values.
574 (defun code-bounds (code)
575   (declare (type sb-kernel:code-component code))
576   (let* ((start (code-start code))
577          (end (+ start (sb-kernel:%code-code-size code))))
578     (values start end)))
579
580 (defmacro with-profiling ((&key (sample-interval '*sample-interval*)
581                                 (alloc-interval '*alloc-interval*)
582                                 (max-samples '*max-samples*)
583                                 (reset nil)
584                                 (mode '*sampling-mode*)
585                                 (loop t)
586                                 (max-depth most-positive-fixnum)
587                                 show-progress
588                                 (report nil report-p))
589                           &body body)
590   "Repeatedly evaluate BODY with statistical profiling turned on.
591    In multi-threaded operation, only the thread in which WITH-PROFILING
592    was evaluated will be profiled by default. If you want to profile
593    multiple threads, invoke the profiler with START-PROFILING.
594
595    The following keyword args are recognized:
596
597    :SAMPLE-INTERVAL <n>
598      Take a sample every <n> seconds. Default is *SAMPLE-INTERVAL*.
599
600    :ALLOC-INTERVAL <n>
601      Take a sample every time <n> allocation regions (approximately
602      8kB) have been allocated since the last sample. Default is
603      *ALLOC-INTERVAL*.
604
605    :MODE <mode>
606      If :CPU, run the profiler in CPU profiling mode. If :ALLOC, run
607      the profiler in allocation profiling mode.
608
609    :MAX-SAMPLES <max>
610      Repeat evaluating body until <max> samples are taken.
611      Default is *MAX-SAMPLES*.
612
613    :MAX-DEPTH <max>
614      Maximum call stack depth that the profiler should consider. Only
615      has an effect on x86 and x86-64.
616
617    :REPORT <type>
618      If specified, call REPORT with :TYPE <type> at the end.
619
620    :RESET <bool>
621      It true, call RESET at the beginning.
622 e
623    :LOOP <bool>
624      If true (the default) repeatedly evaluate BODY. If false, evaluate
625      if only once."
626   (declare (type report-type report))
627   `(let* ((*sample-interval* ,sample-interval)
628           (*alloc-interval* ,alloc-interval)
629           (*sampling* nil)
630           (sb-vm:*alloc-signal* nil)
631           (*sampling-mode* ,mode)
632           (*max-samples* ,max-samples))
633      ,@(when reset '((reset)))
634      (unwind-protect
635           (progn
636             (start-profiling :max-depth ',max-depth)
637             (loop
638                (when (>= (samples-trace-count *samples*)
639                          (samples-max-samples *samples*))
640                  (return))
641                ,@(when show-progress
642                        `((format t "~&===> ~d of ~d samples taken.~%"
643                                  (samples-trace-count *samples*)
644                                  (samples-max-samples *samples*))))
645                (let ((.last-index. (samples-index *samples*)))
646                  ,@body
647                  (when (= .last-index. (samples-index *samples*))
648                    (warn "No sampling progress; possibly a profiler bug.")
649                    (return)))
650                (unless ,loop
651                  (return))))
652        (stop-profiling))
653      ,@(when report-p `((report :type ,report)))))
654
655 (defun start-profiling (&key (max-samples *max-samples*)
656                         (mode *sampling-mode*)
657                         (sample-interval *sample-interval*)
658                         (alloc-interval *alloc-interval*)
659                         (max-depth most-positive-fixnum)
660                         (sampling t))
661   "Start profiling statistically if not already profiling.
662    The following keyword args are recognized:
663
664    :SAMPLE-INTERVAL <n>
665      Take a sample every <n> seconds.  Default is *SAMPLE-INTERVAL*.
666
667    :ALLOC-INTERVAL <n>
668      Take a sample every time <n> allocation regions (approximately
669      8kB) have been allocated since the last sample. Default is
670      *ALLOC-INTERVAL*.
671
672    :MODE <mode>
673      If :CPU, run the profiler in CPU profiling mode. If :ALLOC, run
674      the profiler in allocation profiling mode.
675
676    :MAX-SAMPLES <max>
677      Maximum number of samples.  Default is *MAX-SAMPLES*.
678
679    :MAX-DEPTH <max>
680      Maximum call stack depth that the profiler should consider. Only
681      has an effect on x86 and x86-64.
682
683    :SAMPLING <bool>
684      If true, the default, start sampling right away.
685      If false, START-SAMPLING can be used to turn sampling on."
686   #-gencgc
687   (when (eq mode :alloc)
688     (error "Allocation profiling is only supported for builds using the generational garbage collector."))
689   (unless *profiling*
690     (multiple-value-bind (secs usecs)
691         (multiple-value-bind (secs rest)
692             (truncate sample-interval)
693           (values secs (truncate (* rest 1000000))))
694       (setf *sampling* sampling
695             *samples* (make-samples :max-depth max-depth
696                                     :max-samples max-samples
697                                     :mode mode))
698       (enable-call-counting)
699       (sb-sys:enable-interrupt sb-unix:sigprof #'sigprof-handler)
700       (if (eq mode :alloc)
701           (setf sb-vm:*alloc-signal* (1- alloc-interval))
702           (progn
703             (unix-setitimer :profile secs usecs secs usecs)
704             (setf sb-vm:*alloc-signal* nil)))
705       (setq *profiling* t)))
706   (values))
707
708 (defun stop-profiling ()
709   "Stop profiling if profiling."
710   (when *profiling*
711     (unix-setitimer :profile 0 0 0 0)
712     (disable-call-counting)
713     ;; Even with the timer shut down we cannot be sure that there is
714     ;; no undelivered sigprof. Besides, leaving the signal handler
715     ;; installed won't hurt.
716     (setq *sampling* nil)
717     (setq sb-vm:*alloc-signal* nil)
718     (setq *profiling* nil))
719   (values))
720
721 (defun reset ()
722   "Reset the profiler."
723   (stop-profiling)
724   (setq *sampling* nil)
725   (setq *samples* nil)
726   (values))
727
728 ;;; Make a NODE for debug-info INFO.
729 (defun make-node (info)
730   (flet ((clean-name (name)
731            (if (and (consp name)
732                     (member (first name)
733                             '(sb-c::xep sb-c::tl-xep sb-c::&more-processor
734                               sb-c::varargs-entry
735                               sb-c::top-level-form
736                               sb-c::hairy-arg-processor
737                               sb-c::&optional-processor)))
738                (second name)
739                name)))
740     (typecase info
741       (sb-kernel::code-component
742        (multiple-value-bind (start end)
743            (code-bounds info)
744          (values
745           (%make-node :name (or (sb-disassem::find-assembler-routine start)
746                                 (format nil "~a" info))
747                       :debug-info info
748                       :start-pc-or-offset start
749                       :end-pc-or-offset end)
750           info)))
751       (sb-di::compiled-debug-fun
752        (let* ((name (sb-di::debug-fun-name info))
753               (cdf (sb-di::compiled-debug-fun-compiler-debug-fun info))
754               (start-offset (sb-c::compiled-debug-fun-start-pc cdf))
755               (end-offset (sb-c::compiled-debug-fun-elsewhere-pc cdf))
756               (component (sb-di::compiled-debug-fun-component info))
757               (start-pc (code-start component)))
758          ;; Call graphs are mostly useless unless we somehow
759          ;; distinguish a gazillion different (LAMBDA ())'s.
760          (when (equal name '(lambda ()))
761            (setf name (format nil "Unknown component: #x~x" start-pc)))
762          (values (%make-node :name (clean-name name)
763                              :debug-info info
764                              :start-pc-or-offset start-offset
765                              :end-pc-or-offset end-offset)
766                  component)))
767       (sb-di::debug-fun
768        (%make-node :name (clean-name (sb-di::debug-fun-name info))
769                    :debug-info info))
770       (t
771        (%make-node :name (coerce info 'string)
772                    :debug-info info)))))
773
774 ;;; One function can have more than one COMPILED-DEBUG-FUNCTION with
775 ;;; the same name.  Reduce the number of calls to Debug-Info by first
776 ;;; looking for a given PC in a red-black tree.  If not found in the
777 ;;; tree, get debug info, and look for a node in a hash-table by
778 ;;; function name.  If not found in the hash-table, make a new node.
779
780 (defvar *name->node*)
781
782 (defmacro with-lookup-tables (() &body body)
783   `(let ((*name->node* (make-hash-table :test 'equal)))
784      ,@body))
785
786 ;;; Find or make a new node for INFO.  Value is the NODE found or
787 ;;; made; NIL if not enough information exists to make a NODE for INFO.
788 (defun lookup-node (info)
789   (when info
790     (multiple-value-bind (new key)
791         (make-node info)
792       (when (eql (node-name new) 'call-counter)
793         (return-from lookup-node (values nil nil)))
794       (let* ((key (cons (node-name new) key))
795              (found (gethash key *name->node*)))
796         (cond (found
797                (setf (node-start-pc-or-offset found)
798                      (min (node-start-pc-or-offset found)
799                           (node-start-pc-or-offset new)))
800                (setf (node-end-pc-or-offset found)
801                      (max (node-end-pc-or-offset found)
802                           (node-end-pc-or-offset new)))
803                found)
804               (t
805                (let ((call-count-info (gethash (node-name new)
806                                                *encapsulations*)))
807                  (when call-count-info
808                    (setf (node-call-count new)
809                          (car call-count-info))))
810                (setf (gethash key *name->node*) new)
811                new))))))
812
813 ;;; Return a list of all nodes created by LOOKUP-NODE.
814 (defun collect-nodes ()
815   (loop for node being the hash-values of *name->node*
816         collect node))
817
818 ;;; Value is a CALL-GRAPH for the current contents of *SAMPLES*.
819 (defun make-call-graph-1 (max-depth)
820   (let ((elsewhere-count 0)
821         visited-nodes)
822     (with-lookup-tables ()
823       (loop for i below (- (samples-index *samples*) 2) by 2
824             with depth = 0
825             for debug-info = (aref (samples-vector *samples*) i)
826             for next-info = (aref (samples-vector *samples*)
827                                   (+ i 2))
828             do (if (eq debug-info 'trace-start)
829                    (setf depth 0)
830                    (let ((callee (lookup-node debug-info))
831                          (caller (unless (eq next-info 'trace-start)
832                                    (lookup-node next-info))))
833                      (when (< depth max-depth)
834                        (when (zerop depth)
835                          (setf visited-nodes nil)
836                          (cond (callee
837                                 (incf (node-accrued-count callee))
838                                 (incf (node-count callee)))
839                                (t
840                                 (incf elsewhere-count))))
841                        (incf depth)
842                        (when callee
843                          (push callee visited-nodes))
844                        (when caller
845                          (unless (member caller visited-nodes)
846                            (incf (node-accrued-count caller)))
847                          (when callee
848                            (let ((call (find callee (node-edges caller)
849                                              :key #'call-vertex)))
850                              (pushnew caller (node-callers callee))
851                              (if call
852                                  (unless (member caller visited-nodes)
853                                    (incf (call-count call)))
854                                  (push (make-call callee)
855                                        (node-edges caller))))))))))
856       (let ((sorted-nodes (sort (collect-nodes) #'> :key #'node-count)))
857         (loop for node in sorted-nodes and i from 1 do
858               (setf (node-index node) i))
859         (%make-call-graph :nsamples (samples-trace-count *samples*)
860                           :sample-interval (if (eq (samples-mode *samples*)
861                                                    :alloc)
862                                                (samples-alloc-interval *samples*)
863                                                (samples-sample-interval *samples*))
864                           :sampling-mode (samples-mode *samples*)
865                           :elsewhere-count elsewhere-count
866                           :vertices sorted-nodes)))))
867
868 ;;; Reduce CALL-GRAPH to a dag, creating CYCLE structures for call
869 ;;; cycles.
870 (defun reduce-call-graph (call-graph)
871   (let ((cycle-no 0))
872     (flet ((make-one-cycle (vertices edges)
873              (let* ((name (format nil "<Cycle ~d>" (incf cycle-no)))
874                     (count (loop for v in vertices sum (node-count v))))
875                (make-cycle :name name
876                            :index cycle-no
877                            :count count
878                            :scc-vertices vertices
879                            :edges edges))))
880       (reduce-graph call-graph #'make-one-cycle))))
881
882 ;;; For all nodes in CALL-GRAPH, compute times including the time
883 ;;; spent in functions called from them.  Note that the call-graph
884 ;;; vertices are in reverse topological order, children first, so we
885 ;;; will have computed accrued counts of called functions before they
886 ;;; are used to compute accrued counts for callers.
887 (defun compute-accrued-counts (call-graph)
888   (do-vertices (from call-graph)
889     (setf (node-accrued-count from) (node-count from))
890     (do-edges (call to from)
891       (incf (node-accrued-count from)
892             (round (* (/ (call-count call) (node-count to))
893                       (node-accrued-count to)))))))
894
895 ;;; Return a CALL-GRAPH structure for the current contents of
896 ;;; *SAMPLES*.  The result contain a list of nodes sorted by self-time
897 ;;; in the FLAT-NODES slot, and a dag in VERTICES, with call cycles
898 ;;; reduced to CYCLE structures.
899 (defun make-call-graph (max-depth)
900   (stop-profiling)
901   (show-progress "~&Computing call graph ")
902   (let ((call-graph (without-gcing (make-call-graph-1 max-depth))))
903     (setf (call-graph-flat-nodes call-graph)
904           (copy-list (graph-vertices call-graph)))
905     (show-progress "~&Finding cycles")
906     #+nil
907     (reduce-call-graph call-graph)
908     (show-progress "~&Propagating counts")
909     #+nil
910     (compute-accrued-counts call-graph)
911     call-graph))
912
913 \f
914 ;;;; Reporting
915
916 (defun print-separator (&key (length 72) (char #\-))
917   (format t "~&~V,,,V<~>~%" length char))
918
919 (defun samples-percent (call-graph count)
920   (if (> count 0)
921       (* 100.0 (/ count (call-graph-nsamples call-graph)))
922       0))
923
924 (defun print-call-graph-header (call-graph)
925   (let ((nsamples (call-graph-nsamples call-graph))
926         (interval (call-graph-sample-interval call-graph))
927         (ncycles (loop for v in (graph-vertices call-graph)
928                        count (scc-p v))))
929     (if (eq (call-graph-sampling-mode call-graph) :alloc)
930         (format t "~2&Number of samples:     ~d~%~
931                   Sample interval:       ~a regions (approximately ~a kB)~%~
932                   Total sampling amount: ~a regions (approximately ~a kB)~%~
933                   Number of cycles:      ~d~2%"
934                 nsamples
935                 interval
936                 (truncate (* interval *alloc-region-size*) 1024)
937                 (* nsamples interval)
938                 (truncate (* nsamples interval *alloc-region-size*) 1024)
939                 ncycles)
940         (format t "~2&Number of samples:   ~d~%~
941                   Sample interval:     ~f seconds~%~
942                   Total sampling time: ~f seconds~%~
943                   Number of cycles:    ~d~2%"
944                 nsamples
945                 interval
946                 (* nsamples interval)
947                 ncycles))))
948
949 (defun print-flat (call-graph &key (stream *standard-output*) max
950                    min-percent (print-header t))
951   (let ((*standard-output* stream)
952         (*print-pretty* nil)
953         (total-count 0)
954         (total-percent 0)
955         (min-count (if min-percent
956                        (round (* (/ min-percent 100.0)
957                                  (call-graph-nsamples call-graph)))
958                        0)))
959     (when print-header
960       (print-call-graph-header call-graph))
961     (format t "~&           Self        Total        Cumul~%")
962     (format t "~&  Nr  Count     %  Count     %  Count     %    Calls  Function~%")
963     (print-separator)
964     (let ((elsewhere-count (call-graph-elsewhere-count call-graph))
965           (i 0))
966       (dolist (node (call-graph-flat-nodes call-graph))
967         (when (or (and max (> (incf i) max))
968                   (< (node-count node) min-count))
969           (return))
970         (let* ((count (node-count node))
971                (percent (samples-percent call-graph count))
972                (accrued-count (node-accrued-count node))
973                (accrued-percent (samples-percent call-graph accrued-count)))
974           (incf total-count count)
975           (incf total-percent percent)
976           (format t "~&~4d ~6d ~5,1f ~6d ~5,1f ~6d ~5,1f ~8@a  ~s~%"
977                   (node-index node)
978                   count
979                   percent
980                   accrued-count
981                   accrued-percent
982                   total-count
983                   total-percent
984                   (or (node-call-count node) "-")
985                   (node-name node))
986           (finish-output)))
987       (print-separator)
988       (format t "~&     ~6d ~5,1f~36a elsewhere~%"
989               elsewhere-count
990               (samples-percent call-graph elsewhere-count)
991               ""))))
992
993 (defun print-cycles (call-graph)
994   (when (some #'cycle-p (graph-vertices call-graph))
995     (format t "~&                            Cycle~%")
996     (format t "~& Count     %                   Parts~%")
997     (do-vertices (node call-graph)
998       (when (cycle-p node)
999         (flet ((print-info (indent index count percent name)
1000                  (format t "~&~6d ~5,1f ~11@t ~V@t  ~s [~d]~%"
1001                          count percent indent name index)))
1002           (print-separator)
1003           (format t "~&~6d ~5,1f                ~a...~%"
1004                   (node-count node)
1005                   (samples-percent call-graph (cycle-count node))
1006                   (node-name node))
1007           (dolist (v (vertex-scc-vertices node))
1008             (print-info 4 (node-index v) (node-count v)
1009                         (samples-percent call-graph (node-count v))
1010                         (node-name v))))))
1011     (print-separator)
1012     (format t "~2%")))
1013
1014 (defun print-graph (call-graph &key (stream *standard-output*)
1015                     max min-percent)
1016   (let ((*standard-output* stream)
1017         (*print-pretty* nil))
1018     (print-call-graph-header call-graph)
1019     (print-cycles call-graph)
1020     (flet ((find-call (from to)
1021              (find to (node-edges from) :key #'call-vertex))
1022            (print-info (indent index count percent name)
1023              (format t "~&~6d ~5,1f ~11@t ~V@t  ~s [~d]~%"
1024                      count percent indent name index)))
1025       (format t "~&                               Callers~%")
1026       (format t "~&                 Total.     Function~%")
1027       (format t "~& Count     %  Count     %      Callees~%")
1028       (do-vertices (node call-graph)
1029         (print-separator)
1030         ;;
1031         ;; Print caller information.
1032         (dolist (caller (node-callers node))
1033           (let ((call (find-call caller node)))
1034             (print-info 4 (node-index caller)
1035                         (call-count call)
1036                         (samples-percent call-graph (call-count call))
1037                         (node-name caller))))
1038         ;; Print the node itself.
1039         (format t "~&~6d ~5,1f ~6d ~5,1f   ~s [~d]~%"
1040                 (node-count node)
1041                 (samples-percent call-graph (node-count node))
1042                 (node-accrued-count node)
1043                 (samples-percent call-graph (node-accrued-count node))
1044                 (node-name node)
1045                 (node-index node))
1046         ;; Print callees.
1047         (do-edges (call called node)
1048           (print-info 4 (node-index called)
1049                       (call-count call)
1050                       (samples-percent call-graph (call-count call))
1051                       (node-name called))))
1052       (print-separator)
1053       (format t "~2%")
1054       (print-flat call-graph :stream stream :max max
1055                   :min-percent min-percent :print-header nil))))
1056
1057 (defun report (&key (type :graph) max min-percent call-graph
1058                (stream *standard-output*) ((:show-progress *show-progress*)))
1059   "Report statistical profiling results.  The following keyword
1060    args are recognized:
1061
1062    :TYPE <type>
1063       Specifies the type of report to generate.  If :FLAT, show
1064       flat report, if :GRAPH show a call graph and a flat report.
1065       If nil, don't print out a report.
1066
1067    :STREAM <stream>
1068       Specify a stream to print the report on.  Default is
1069       *STANDARD-OUTPUT*.
1070
1071    :MAX <max>
1072       Don't show more than <max> entries in the flat report.
1073
1074    :MIN-PERCENT <min-percent>
1075       Don't show functions taking less than <min-percent> of the
1076       total time in the flat report.
1077
1078    :SHOW-PROGRESS <bool>
1079      If true, print progress messages while generating the call graph.
1080
1081    :CALL-GRAPH <graph>
1082      Print a report from <graph> instead of the latest profiling
1083      results.
1084
1085    Value of this function is a CALL-GRAPH object representing the
1086    resulting call-graph."
1087   (let ((graph (or call-graph (make-call-graph most-positive-fixnum))))
1088     (ecase type
1089       (:flat
1090        (print-flat graph :stream stream :max max :min-percent min-percent))
1091       (:graph
1092        (print-graph graph :stream stream :max max :min-percent min-percent))
1093       ((nil)))
1094     graph))
1095
1096 ;;; Interface to DISASSEMBLE
1097
1098 (defun sample-pc-from-pc-or-offset (sample pc-or-offset)
1099   (etypecase sample
1100     ;; Assembly routines or foreign functions don't move around, so we've
1101     ;; stored a raw PC
1102     ((or sb-kernel:code-component string)
1103      pc-or-offset)
1104     ;; Lisp functions might move, so we've stored a offset from the
1105     ;; start of the code component.
1106     (sb-di::compiled-debug-fun
1107      (let* ((component (sb-di::compiled-debug-fun-component sample))
1108             (start-pc (code-start component)))
1109        (+ start-pc pc-or-offset)))))
1110
1111 (defun add-disassembly-profile-note (chunk stream dstate)
1112   (declare (ignore chunk stream))
1113   (when *samples*
1114     (let* ((location (+ (sb-disassem::seg-virtual-location
1115                          (sb-disassem:dstate-segment dstate))
1116                         (sb-disassem::dstate-cur-offs dstate)))
1117            (samples (loop with index = (samples-index *samples*)
1118                           for x from 0 below (- index 2) by 2
1119                           for last-sample = nil then sample
1120                           for sample = (aref (samples-vector *samples*) x)
1121                           for pc-or-offset = (aref (samples-vector *samples*)
1122                                                    (1+ x))
1123                           when (and sample (eq last-sample 'trace-start))
1124                           count (= location
1125                                    (sample-pc-from-pc-or-offset sample
1126                                                                 pc-or-offset)))))
1127       (unless (zerop samples)
1128         (sb-disassem::note (format nil "~A/~A samples"
1129                                    samples (samples-trace-count *samples*))
1130                            dstate)))))
1131
1132 (pushnew 'add-disassembly-profile-note sb-disassem::*default-dstate-hooks*)
1133
1134 \f
1135 ;;;; Call counting
1136
1137 ;;; The following functions tell sb-sprof to do call count profiling
1138 ;;; for the named functions in addition to normal statistical
1139 ;;; profiling.  The benefit of this over using SB-PROFILE is that this
1140 ;;; encapsulation is a lot more lightweight, due to not needing to
1141 ;;; track cpu usage / consing. (For example, compiling asdf 20 times
1142 ;;; took 13s normally, 15s with call counting for all functions in
1143 ;;; SB-C, and 94s with SB-PROFILE profiling SB-C).
1144
1145 (defun profile-call-counts (&rest names)
1146   "Mark the functions named by NAMES as being subject to call counting
1147 during statistical profiling. If a string is used as a name, it will
1148 be interpreted as a package name. In this case call counting will be
1149 done for all functions with names like X or (SETF X), where X is a symbol
1150 with the package as its home package."
1151   (dolist (name names)
1152     (if (stringp name)
1153         (let ((package (find-package name)))
1154           (do-symbols (symbol package)
1155             (when (eql (symbol-package symbol) package)
1156               (dolist (function-name (list symbol (list 'setf symbol)))
1157                 (profile-call-counts-for-function function-name)))))
1158         (profile-call-counts-for-function name))))
1159
1160 (defun profile-call-counts-for-function (function-name)
1161   (unless (gethash function-name *encapsulations*)
1162     (setf (gethash function-name *encapsulations*) nil)))
1163
1164 (defun unprofile-call-counts ()
1165   "Clear all call counting information. Call counting will be done for no
1166 functions during statistical profiling."
1167   (clrhash *encapsulations*))
1168
1169 ;;; Called when profiling is started to enable the call counting
1170 ;;; encapsulation. Wrap all the call counted functions
1171 (defun enable-call-counting ()
1172   (maphash (lambda (k v)
1173              (declare (ignore v))
1174              (enable-call-counting-for-function k))
1175            *encapsulations*))
1176
1177 ;;; Called when profiling is stopped to disable the encapsulation. Restore
1178 ;;; the original functions.
1179 (defun disable-call-counting ()
1180   (maphash (lambda (k v)
1181              (when v
1182                (assert (cdr v))
1183                (without-package-locks
1184                  (setf (fdefinition k) (cdr v)))
1185                (setf (cdr v) nil)))
1186            *encapsulations*))
1187
1188 (defun enable-call-counting-for-function (function-name)
1189   (let ((info (gethash function-name *encapsulations*)))
1190     ;; We should never try to encapsulate an fdefn multiple times.
1191     (assert (or (null info)
1192                 (null (cdr info))))
1193     (when (and (fboundp function-name)
1194                (or (not (symbolp function-name))
1195                    (and (not (special-operator-p function-name))
1196                         (not (macro-function function-name)))))
1197       (let* ((original-fun (fdefinition function-name))
1198              (info (cons 0 original-fun)))
1199         (setf (gethash function-name *encapsulations*) info)
1200         (without-package-locks
1201           (setf (fdefinition function-name)
1202                 (sb-int:named-lambda call-counter (sb-int:&more more-context more-count)
1203                   (declare (optimize speed (safety 0)))
1204                   ;; 2^59 calls should be enough for anybody, and it
1205                   ;; allows using fixnum arithmetic on x86-64. 2^32
1206                   ;; isn't enough, so we can't do that on 32 bit platforms.
1207                   (incf (the (unsigned-byte 59)
1208                           (car info)))
1209                   (multiple-value-call original-fun
1210                     (sb-c:%more-arg-values more-context
1211                                            0
1212                                            more-count)))))))))
1213
1214 \f
1215 ;;; silly examples
1216
1217 (defun test-0 (n &optional (depth 0))
1218   (declare (optimize (debug 3)))
1219   (when (< depth n)
1220     (dotimes (i n)
1221       (test-0 n (1+ depth))
1222       (test-0 n (1+ depth)))))
1223
1224 (defun test ()
1225   (with-profiling (:reset t :max-samples 1000 :report :graph)
1226     (test-0 7)))
1227
1228
1229 ;;; provision
1230 (provide 'sb-sprof)
1231
1232 ;;; end of file