2009-12-21 00:01:13 +01:00
|
|
|
|
;;;; (statprof) -- a statistical profiler for Guile
|
|
|
|
|
|
;;;; -*-scheme-*-
|
|
|
|
|
|
;;;;
|
2020-01-23 21:46:28 +01:00
|
|
|
|
;;;; Copyright (C) 2009, 2010, 2011, 2013-2018, 2020 Free Software Foundation, Inc.
|
2009-12-21 00:01:13 +01:00
|
|
|
|
;;;; Copyright (C) 2004, 2009 Andy Wingo <wingo at pobox dot com>
|
|
|
|
|
|
;;;; Copyright (C) 2001 Rob Browning <rlb at defaultvalue dot org>
|
|
|
|
|
|
;;;;
|
|
|
|
|
|
;;;; This library is free software; you can redistribute it and/or
|
|
|
|
|
|
;;;; modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
|
;;;; License as published by the Free Software Foundation; either
|
|
|
|
|
|
;;;; version 3 of the License, or (at your option) any later version.
|
|
|
|
|
|
;;;;
|
|
|
|
|
|
;;;; This library is distributed in the hope that it will be useful,
|
|
|
|
|
|
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
|
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
|
;;;; Lesser General Public License for more details.
|
|
|
|
|
|
;;;;
|
|
|
|
|
|
;;;; You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
|
;;;; License along with this library; if not, write to the Free Software
|
|
|
|
|
|
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
|
|
;;;;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
2014-02-21 19:20:16 +01:00
|
|
|
|
;;;
|
2016-02-01 14:58:34 +01:00
|
|
|
|
;;; @code{(statprof)} is a statistical profiler for Guile. See the
|
|
|
|
|
|
;;; "Statprof" section in the manual, for more information.
|
2014-02-21 19:20:16 +01:00
|
|
|
|
;;;
|
2009-12-21 00:01:13 +01:00
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
|
|
|
|
(define-module (statprof)
|
|
|
|
|
|
#:use-module (srfi srfi-1)
|
2014-02-21 21:01:10 +01:00
|
|
|
|
#:use-module (srfi srfi-9)
|
2014-02-22 15:09:54 +01:00
|
|
|
|
#:use-module (srfi srfi-9 gnu)
|
2016-01-07 16:23:26 +01:00
|
|
|
|
#:use-module (ice-9 format)
|
|
|
|
|
|
#:use-module (ice-9 match)
|
|
|
|
|
|
#:use-module (ice-9 vlist)
|
2010-01-14 22:52:07 +01:00
|
|
|
|
#:use-module (system vm vm)
|
|
|
|
|
|
#:use-module (system vm frame)
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
#:use-module (system vm debug)
|
2010-01-14 22:52:07 +01:00
|
|
|
|
#:use-module (system vm program)
|
2009-12-21 00:01:13 +01:00
|
|
|
|
#:export (statprof-active?
|
|
|
|
|
|
statprof-start
|
|
|
|
|
|
statprof-stop
|
|
|
|
|
|
statprof-reset
|
|
|
|
|
|
|
|
|
|
|
|
statprof-accumulated-time
|
|
|
|
|
|
statprof-sample-count
|
|
|
|
|
|
statprof-fold-call-data
|
|
|
|
|
|
statprof-proc-call-data
|
|
|
|
|
|
statprof-call-data-name
|
|
|
|
|
|
statprof-call-data-calls
|
|
|
|
|
|
statprof-call-data-cum-samples
|
|
|
|
|
|
statprof-call-data-self-samples
|
|
|
|
|
|
statprof-call-data->stats
|
|
|
|
|
|
|
|
|
|
|
|
statprof-stats-proc-name
|
2014-03-01 12:59:58 +01:00
|
|
|
|
statprof-stats-proc-source
|
2009-12-21 00:01:13 +01:00
|
|
|
|
statprof-stats-%-time-in-proc
|
|
|
|
|
|
statprof-stats-cum-secs-in-proc
|
|
|
|
|
|
statprof-stats-self-secs-in-proc
|
|
|
|
|
|
statprof-stats-calls
|
|
|
|
|
|
statprof-stats-self-secs-per-call
|
|
|
|
|
|
statprof-stats-cum-secs-per-call
|
|
|
|
|
|
|
|
|
|
|
|
statprof-display
|
2014-02-28 11:06:55 +01:00
|
|
|
|
statprof-display-anomalies
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
|
|
|
|
|
statprof-fetch-stacks
|
|
|
|
|
|
statprof-fetch-call-tree
|
|
|
|
|
|
|
2010-01-14 22:52:07 +01:00
|
|
|
|
statprof
|
2011-05-05 10:08:29 +02:00
|
|
|
|
gcprof))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
2014-03-01 15:54:47 +01:00
|
|
|
|
;;; ~ Implementation notes ~
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; Statprof can be divided into two pieces: data collection and data
|
|
|
|
|
|
;;; analysis.
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; The data collection runs concurrently with the program, and is
|
|
|
|
|
|
;;; designed to be as cheap as possible. The main data collection
|
|
|
|
|
|
;;; instrument is the stack sampler, driven by SIGPROF signals that are
|
|
|
|
|
|
;;; scheduled with periodic setitimer calls. The stack sampler simply
|
|
|
|
|
|
;;; looks at every frame on the stack, and writes a representation of
|
|
|
|
|
|
;;; the frame's procedure into a growable buffer.
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; For most frames, this representation is the instruction pointer of
|
|
|
|
|
|
;;; that frame, because it's cheap to get and you can map from
|
|
|
|
|
|
;;; instruction pointer to procedure fairly cheaply. This won't
|
|
|
|
|
|
;;; distinguish between different closures which share the same code,
|
|
|
|
|
|
;;; but that is usually what we want anyway.
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; The other part of data collection is the exact call counter, which
|
|
|
|
|
|
;;; uses the VM's "apply" hook to record each procedure call.
|
|
|
|
|
|
;;; Naturally, this is quite expensive, and it is off by default.
|
|
|
|
|
|
;;; Running code at every procedure call effectively penalizes procedure
|
|
|
|
|
|
;;; calls. Still, it's useful sometimes. If the profiler state has a
|
|
|
|
|
|
;;; call-counts table, then calls will be counted. As with the stack
|
2018-07-29 15:36:07 +02:00
|
|
|
|
;;; counter, the key in the hash table is the code pointer of the
|
|
|
|
|
|
;;; procedure being called. The call counter can also see calls of
|
|
|
|
|
|
;;; non-programs, for example in the case of applicable structs. In
|
|
|
|
|
|
;;; that case the key is the procedure itself.
|
2014-03-01 15:54:47 +01:00
|
|
|
|
;;;
|
|
|
|
|
|
;;; After collection is finished, the data can be analyzed. The first
|
|
|
|
|
|
;;; step is usually to run over the stack traces, tabulating sample
|
|
|
|
|
|
;;; counts by procedure; the stack-samples->procedure-data does that.
|
|
|
|
|
|
;;; The result of stack-samples->procedure-data is a hash table mapping
|
|
|
|
|
|
;;; procedures to "call data" records. The call data values are exposed
|
|
|
|
|
|
;;; to users via the statprof-fold-call-data procedure.
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; Usually all the analysis is triggered by calling statprof-display,
|
|
|
|
|
|
;;; or having the statprof procedure call it for you.
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; The other thing we can do is to look at the stacks themselves, for
|
|
|
|
|
|
;;; example via statprof-fetch-call-tree.
|
|
|
|
|
|
;;;
|
|
|
|
|
|
|
|
|
|
|
|
;;; ~ Threads and state ~
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; The state of the profiler is contained in a <state> record, which is
|
|
|
|
|
|
;;; bound to a thread-local parameter. The accurate call counter uses
|
|
|
|
|
|
;;; the VM apply hook, which is also local to the current thread, so all
|
|
|
|
|
|
;;; is good there.
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; The problem comes in the statistical stack sampler's use of
|
|
|
|
|
|
;;; `setitimer' and SIGPROF. The timer manipulated by setitimer is a
|
|
|
|
|
|
;;; whole-process timer, so it decrements as other threads execute,
|
|
|
|
|
|
;;; which is the wrong thing if you want to profile just one thread. On
|
|
|
|
|
|
;;; the other hand, SIGPROF is delivered to the process as a whole,
|
|
|
|
|
|
;;; which is fine given Guile's signal-handling thread, but then only
|
|
|
|
|
|
;;; delivered to the thread running statprof, which isn't the right
|
|
|
|
|
|
;;; thing if you want to profile the whole system.
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; The summary is that statprof works more or less well as a per-thread
|
|
|
|
|
|
;;; profiler if no other threads are running on their own when
|
|
|
|
|
|
;;; profiling. If the other threads are running on behalf of the thread
|
|
|
|
|
|
;;; being profiled (as via futures or parallel marking) things still
|
|
|
|
|
|
;;; mostly work as expected. You can run statprof in one thread,
|
|
|
|
|
|
;;; finish, and then run statprof in another thread, and the profile
|
|
|
|
|
|
;;; runs won't affect each other. But if you want true per-thread
|
|
|
|
|
|
;;; profiles when other things are happening in the process, including
|
|
|
|
|
|
;;; other statprof runs, or whole-process profiles with per-thread
|
|
|
|
|
|
;;; breakdowns, the use of setitimer currently prevents that.
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; The solution would be to switch to POSIX.1-2001's timer_create(2),
|
|
|
|
|
|
;;; and to add some more threading-related API to statprof. Some other
|
|
|
|
|
|
;;; day.
|
|
|
|
|
|
;;;
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
2014-02-21 21:01:10 +01:00
|
|
|
|
(define-record-type <state>
|
|
|
|
|
|
(make-state accumulated-time last-start-time sample-count
|
2014-02-25 22:40:32 +01:00
|
|
|
|
sampling-period remaining-prof-time profile-level
|
2014-02-28 19:31:46 +01:00
|
|
|
|
call-counts gc-time-taken inside-profiler?
|
2014-03-01 16:09:30 +01:00
|
|
|
|
prev-sigprof-handler outer-cut buffer buffer-pos)
|
2014-02-21 21:01:10 +01:00
|
|
|
|
state?
|
|
|
|
|
|
;; Total time so far.
|
|
|
|
|
|
(accumulated-time accumulated-time set-accumulated-time!)
|
|
|
|
|
|
;; Start-time when timer is active.
|
|
|
|
|
|
(last-start-time last-start-time set-last-start-time!)
|
|
|
|
|
|
;; Total count of sampler calls.
|
|
|
|
|
|
(sample-count sample-count set-sample-count!)
|
2014-02-25 22:40:32 +01:00
|
|
|
|
;; Microseconds.
|
|
|
|
|
|
(sampling-period sampling-period set-sampling-period!)
|
2014-02-21 21:01:10 +01:00
|
|
|
|
;; Time remaining when prof suspended.
|
|
|
|
|
|
(remaining-prof-time remaining-prof-time set-remaining-prof-time!)
|
|
|
|
|
|
;; For user start/stop nesting.
|
|
|
|
|
|
(profile-level profile-level set-profile-level!)
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
;; Hash table mapping ip -> call count, or #f if not counting calls.
|
|
|
|
|
|
(call-counts call-counts set-call-counts!)
|
2014-02-21 21:01:10 +01:00
|
|
|
|
;; GC time between statprof-start and statprof-stop.
|
|
|
|
|
|
(gc-time-taken gc-time-taken set-gc-time-taken!)
|
2014-02-21 21:25:50 +01:00
|
|
|
|
;; True if we are inside the profiler.
|
2014-02-28 11:17:37 +01:00
|
|
|
|
(inside-profiler? inside-profiler? set-inside-profiler?!)
|
2014-03-01 16:09:30 +01:00
|
|
|
|
;; Previous sigprof handler.
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(prev-sigprof-handler prev-sigprof-handler set-prev-sigprof-handler!)
|
2014-03-01 16:09:30 +01:00
|
|
|
|
;; Outer stack cut, or 0.
|
|
|
|
|
|
(outer-cut outer-cut)
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
;; Stack samples.
|
|
|
|
|
|
(buffer buffer set-buffer!)
|
|
|
|
|
|
(buffer-pos buffer-pos set-buffer-pos!))
|
2014-02-21 21:01:10 +01:00
|
|
|
|
|
|
|
|
|
|
(define profiler-state (make-parameter #f))
|
|
|
|
|
|
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(define (fresh-buffer)
|
|
|
|
|
|
(make-vector 1024 #f))
|
|
|
|
|
|
|
|
|
|
|
|
(define (expand-buffer buf)
|
|
|
|
|
|
(let* ((size (vector-length buf))
|
|
|
|
|
|
(new (make-vector (* size 2) #f)))
|
|
|
|
|
|
(vector-move-left! buf 0 (vector-length buf) new 0)
|
|
|
|
|
|
new))
|
|
|
|
|
|
|
2014-02-21 21:56:01 +01:00
|
|
|
|
(define* (fresh-profiler-state #:key (count-calls? #f)
|
2014-03-01 16:09:30 +01:00
|
|
|
|
(sampling-period 10000)
|
|
|
|
|
|
(outer-cut 0))
|
2014-02-28 19:31:46 +01:00
|
|
|
|
(make-state 0 #f 0
|
|
|
|
|
|
sampling-period 0 0
|
|
|
|
|
|
(and count-calls? (make-hash-table)) 0 #f
|
2014-03-01 16:09:30 +01:00
|
|
|
|
#f outer-cut (fresh-buffer) 0))
|
2014-02-21 21:56:01 +01:00
|
|
|
|
|
2014-02-21 21:01:10 +01:00
|
|
|
|
(define (ensure-profiler-state)
|
|
|
|
|
|
(or (profiler-state)
|
2014-02-21 21:56:01 +01:00
|
|
|
|
(let ((state (fresh-profiler-state)))
|
2014-02-21 21:01:10 +01:00
|
|
|
|
(profiler-state state)
|
|
|
|
|
|
state)))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
2014-02-21 21:43:39 +01:00
|
|
|
|
(define (existing-profiler-state)
|
|
|
|
|
|
(or (profiler-state)
|
|
|
|
|
|
(error "expected there to be a profiler state")))
|
|
|
|
|
|
|
2014-02-21 21:01:10 +01:00
|
|
|
|
(define (accumulate-time state stop-time)
|
|
|
|
|
|
(set-accumulated-time! state
|
|
|
|
|
|
(+ (accumulated-time state)
|
|
|
|
|
|
(- stop-time (last-start-time state)))))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
;; SIGPROF handler
|
|
|
|
|
|
|
2014-02-22 15:09:54 +01:00
|
|
|
|
(define (sample-stack-procs state stack)
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(set-sample-count! state (+ (sample-count state) 1))
|
|
|
|
|
|
|
|
|
|
|
|
(let lp ((frame (stack-ref stack 0))
|
2014-03-01 16:09:30 +01:00
|
|
|
|
(len (stack-length stack))
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(buffer (buffer state))
|
|
|
|
|
|
(pos (buffer-pos state)))
|
|
|
|
|
|
(define (write-sample sample)
|
|
|
|
|
|
(vector-set! buffer pos sample))
|
|
|
|
|
|
(define (continue pos)
|
2014-03-01 16:09:30 +01:00
|
|
|
|
(lp (frame-previous frame) (1- len) buffer pos))
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(define (write-sample-and-continue sample)
|
|
|
|
|
|
(write-sample sample)
|
|
|
|
|
|
(continue (1+ pos)))
|
|
|
|
|
|
(cond
|
|
|
|
|
|
((= pos (vector-length buffer))
|
2014-03-01 16:09:30 +01:00
|
|
|
|
(lp frame len (expand-buffer buffer) pos))
|
|
|
|
|
|
((or (zero? len) (not frame))
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(write-sample #f)
|
|
|
|
|
|
(set-buffer! state buffer)
|
|
|
|
|
|
(set-buffer-pos! state (1+ pos)))
|
|
|
|
|
|
(else
|
2018-07-29 15:36:07 +02:00
|
|
|
|
(write-sample-and-continue (frame-instruction-pointer frame))))))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
2014-02-25 22:40:32 +01:00
|
|
|
|
(define (reset-sigprof-timer usecs)
|
2014-02-28 10:36:21 +01:00
|
|
|
|
;; Guile's setitimer binding is terrible.
|
|
|
|
|
|
(let ((prev (setitimer ITIMER_PROF 0 0 0 usecs)))
|
|
|
|
|
|
(+ (* (caadr prev) #e1e6) (cdadr prev))))
|
2014-02-25 22:40:32 +01:00
|
|
|
|
|
2014-04-16 19:12:43 +02:00
|
|
|
|
(define profile-signal-handler
|
|
|
|
|
|
(let ()
|
|
|
|
|
|
(define (profile-signal-handler sig)
|
|
|
|
|
|
(define state (existing-profiler-state))
|
2014-02-21 21:01:10 +01:00
|
|
|
|
|
2014-04-16 19:12:43 +02:00
|
|
|
|
(set-inside-profiler?! state #t)
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
2014-04-16 19:12:43 +02:00
|
|
|
|
(when (positive? (profile-level state))
|
|
|
|
|
|
(let* ((stop-time (get-internal-run-time))
|
|
|
|
|
|
;; Cut down to the signal handler. Note that this will
|
|
|
|
|
|
;; only work if statprof.scm is compiled; otherwise we
|
|
|
|
|
|
;; get `eval' on the stack instead, because if it's not
|
|
|
|
|
|
;; compiled, profile-signal-handler is a thunk that
|
|
|
|
|
|
;; tail-calls eval. For the same reason we define the
|
|
|
|
|
|
;; handler in an inner letrec, so that the compiler sees
|
|
|
|
|
|
;; the inner reference to profile-signal-handler as the
|
|
|
|
|
|
;; same as the procedure, and therefore keeps slot 0
|
2017-03-09 10:42:58 +01:00
|
|
|
|
;; alive. Nastiness, that. Finally we cut one more
|
|
|
|
|
|
;; inner frame, corresponding to the handle-interrupts
|
|
|
|
|
|
;; trampoline.
|
2014-04-16 19:12:43 +02:00
|
|
|
|
(stack
|
2017-03-09 10:42:58 +01:00
|
|
|
|
(or (make-stack #t profile-signal-handler (outer-cut state) 1)
|
2014-04-16 19:12:43 +02:00
|
|
|
|
(pk 'what! (make-stack #t)))))
|
2014-02-22 17:02:53 +01:00
|
|
|
|
|
2014-04-16 19:12:43 +02:00
|
|
|
|
(sample-stack-procs state stack)
|
|
|
|
|
|
(accumulate-time state stop-time)
|
|
|
|
|
|
(set-last-start-time! state (get-internal-run-time))
|
2014-02-22 17:02:53 +01:00
|
|
|
|
|
2014-04-16 19:12:43 +02:00
|
|
|
|
(reset-sigprof-timer (sampling-period state))))
|
|
|
|
|
|
|
|
|
|
|
|
(set-inside-profiler?! state #f))
|
|
|
|
|
|
profile-signal-handler))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
;; Count total calls.
|
|
|
|
|
|
|
2010-01-14 22:52:07 +01:00
|
|
|
|
(define (count-call frame)
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(let ((state (existing-profiler-state)))
|
|
|
|
|
|
(unless (inside-profiler? state)
|
2018-07-29 15:36:07 +02:00
|
|
|
|
(let* ((key (frame-instruction-pointer frame))
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(handle (hashv-create-handle! (call-counts state) key 0)))
|
2016-02-01 11:27:14 +01:00
|
|
|
|
(set-cdr! handle (1+ (cdr handle)))))))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
|
|
|
|
|
|
(define (statprof-active?)
|
|
|
|
|
|
"Returns @code{#t} if @code{statprof-start} has been called more times
|
|
|
|
|
|
than @code{statprof-stop}, @code{#f} otherwise."
|
2014-02-21 21:43:39 +01:00
|
|
|
|
(define state (profiler-state))
|
|
|
|
|
|
(and state (positive? (profile-level state))))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
|
|
|
|
|
;; Do not call this from statprof internal functions -- user only.
|
2014-02-28 11:27:56 +01:00
|
|
|
|
(define* (statprof-start #:optional (state (ensure-profiler-state)))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
"Start the profiler.@code{}"
|
|
|
|
|
|
;; After some head-scratching, I don't *think* I need to mask/unmask
|
|
|
|
|
|
;; signals here, but if I'm wrong, please let me know.
|
2014-02-21 21:01:10 +01:00
|
|
|
|
(set-profile-level! state (+ (profile-level state) 1))
|
2014-02-22 14:54:17 +01:00
|
|
|
|
(when (= (profile-level state) 1)
|
2014-02-25 22:40:32 +01:00
|
|
|
|
(let ((rpt (remaining-prof-time state)))
|
|
|
|
|
|
(set-remaining-prof-time! state 0)
|
2014-02-22 14:54:17 +01:00
|
|
|
|
;; FIXME: Use per-thread run time.
|
|
|
|
|
|
(set-last-start-time! state (get-internal-run-time))
|
2014-02-22 16:31:31 +01:00
|
|
|
|
(set-gc-time-taken! state (assq-ref (gc-stats) 'gc-time-taken))
|
2014-02-28 11:17:37 +01:00
|
|
|
|
(let ((prev (sigaction SIGPROF profile-signal-handler)))
|
|
|
|
|
|
(set-prev-sigprof-handler! state (car prev)))
|
2014-02-25 22:40:32 +01:00
|
|
|
|
(reset-sigprof-timer (if (zero? rpt) (sampling-period state) rpt))
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(when (call-counts state)
|
2018-09-14 08:42:41 +02:00
|
|
|
|
(vm-add-apply-hook! count-call)
|
2014-04-14 17:06:05 +02:00
|
|
|
|
(set-vm-trace-level! (1+ (vm-trace-level))))
|
2014-02-22 14:54:17 +01:00
|
|
|
|
#t)))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
|
|
|
|
|
;; Do not call this from statprof internal functions -- user only.
|
2014-02-28 11:27:56 +01:00
|
|
|
|
(define* (statprof-stop #:optional (state (ensure-profiler-state)))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
"Stop the profiler.@code{}"
|
|
|
|
|
|
;; After some head-scratching, I don't *think* I need to mask/unmask
|
|
|
|
|
|
;; signals here, but if I'm wrong, please let me know.
|
2014-02-21 21:01:10 +01:00
|
|
|
|
(set-profile-level! state (- (profile-level state) 1))
|
2014-02-22 14:54:17 +01:00
|
|
|
|
(when (zero? (profile-level state))
|
2014-04-14 17:06:05 +02:00
|
|
|
|
(when (call-counts state)
|
|
|
|
|
|
(set-vm-trace-level! (1- (vm-trace-level)))
|
2018-09-14 08:42:41 +02:00
|
|
|
|
(vm-remove-apply-hook! count-call))
|
2014-02-22 14:54:17 +01:00
|
|
|
|
(set-gc-time-taken! state
|
2014-02-22 16:31:31 +01:00
|
|
|
|
(- (assq-ref (gc-stats) 'gc-time-taken)
|
2014-02-22 14:54:17 +01:00
|
|
|
|
(gc-time-taken state)))
|
|
|
|
|
|
;; I believe that we need to do this before getting the time
|
|
|
|
|
|
;; (unless we want to make things even more complicated).
|
2014-02-25 22:40:32 +01:00
|
|
|
|
(set-remaining-prof-time! state (reset-sigprof-timer 0))
|
2014-02-22 14:54:17 +01:00
|
|
|
|
(accumulate-time state (get-internal-run-time))
|
2014-02-28 11:17:37 +01:00
|
|
|
|
(sigaction SIGPROF (prev-sigprof-handler state))
|
|
|
|
|
|
(set-prev-sigprof-handler! state #f)
|
2014-02-22 14:54:17 +01:00
|
|
|
|
(set-last-start-time! state #f)))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
2011-05-05 09:59:59 +02:00
|
|
|
|
(define* (statprof-reset sample-seconds sample-microseconds count-calls?
|
|
|
|
|
|
#:optional full-stacks?)
|
2009-12-21 00:01:13 +01:00
|
|
|
|
"Reset the statprof sampler interval to @var{sample-seconds} and
|
|
|
|
|
|
@var{sample-microseconds}. If @var{count-calls?} is true, arrange to
|
|
|
|
|
|
instrument procedure calls as well as collecting statistical profiling
|
2014-02-28 19:31:46 +01:00
|
|
|
|
data. (The optional @var{full-stacks?} argument is deprecated; statprof
|
|
|
|
|
|
always collects full stacks.)"
|
2014-02-21 22:12:47 +01:00
|
|
|
|
(when (statprof-active?)
|
2014-02-21 21:56:01 +01:00
|
|
|
|
(error "Can't reset profiler while profiler is running."))
|
2014-02-28 11:17:37 +01:00
|
|
|
|
(profiler-state
|
|
|
|
|
|
(fresh-profiler-state #:count-calls? count-calls?
|
|
|
|
|
|
#:sampling-period (+ (* sample-seconds #e1e6)
|
2014-02-28 19:31:46 +01:00
|
|
|
|
sample-microseconds)))
|
2014-02-28 11:27:56 +01:00
|
|
|
|
(values))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(define-record-type call-data
|
2014-03-01 12:59:58 +01:00
|
|
|
|
(make-call-data name printable source
|
2014-02-28 19:42:04 +01:00
|
|
|
|
call-count cum-sample-count self-sample-count)
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
call-data?
|
|
|
|
|
|
(name call-data-name)
|
|
|
|
|
|
(printable call-data-printable)
|
2014-03-01 12:59:58 +01:00
|
|
|
|
(source call-data-source)
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(call-count call-data-call-count set-call-data-call-count!)
|
|
|
|
|
|
(cum-sample-count call-data-cum-sample-count set-call-data-cum-sample-count!)
|
|
|
|
|
|
(self-sample-count call-data-self-sample-count set-call-data-self-sample-count!))
|
|
|
|
|
|
|
|
|
|
|
|
(define (source->string source)
|
|
|
|
|
|
(format #f "~a:~a:~a"
|
|
|
|
|
|
(or (source-file source) "<current input>")
|
|
|
|
|
|
(source-line-for-user source)
|
|
|
|
|
|
(source-column source)))
|
|
|
|
|
|
|
|
|
|
|
|
(define (addr->pdi addr cache)
|
|
|
|
|
|
(cond
|
|
|
|
|
|
((hashv-get-handle cache addr) => cdr)
|
|
|
|
|
|
(else
|
|
|
|
|
|
(let ((data (find-program-debug-info addr)))
|
|
|
|
|
|
(hashv-set! cache addr data)
|
|
|
|
|
|
data))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (addr->printable addr pdi)
|
2014-03-01 12:59:58 +01:00
|
|
|
|
(or (and=> (and=> pdi program-debug-info-name) symbol->string)
|
2020-06-04 11:46:34 +02:00
|
|
|
|
(and=> (primitive-code-name addr) symbol->string)
|
2014-03-01 12:59:58 +01:00
|
|
|
|
(string-append "anon #x" (number->string addr 16))))
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
|
|
|
|
|
|
(define (inc-call-data-cum-sample-count! cd)
|
|
|
|
|
|
(set-call-data-cum-sample-count! cd (1+ (call-data-cum-sample-count cd))))
|
|
|
|
|
|
(define (inc-call-data-self-sample-count! cd)
|
|
|
|
|
|
(set-call-data-self-sample-count! cd (1+ (call-data-self-sample-count cd))))
|
|
|
|
|
|
|
2016-02-01 11:27:14 +01:00
|
|
|
|
(define (skip-count-call buffer start len)
|
|
|
|
|
|
;; If we are counting all procedure calls, count-call might be on the
|
|
|
|
|
|
;; stack. If it is, skip that part of the stack.
|
|
|
|
|
|
(match (program-address-range count-call)
|
|
|
|
|
|
((lo . hi)
|
|
|
|
|
|
(let lp ((pos start))
|
|
|
|
|
|
(if (< pos len)
|
|
|
|
|
|
(let ((key (vector-ref buffer pos)))
|
|
|
|
|
|
(cond
|
|
|
|
|
|
((not key)
|
|
|
|
|
|
;; End of stack; count-call not on the stack.
|
|
|
|
|
|
start)
|
|
|
|
|
|
((and (number? key) (<= lo key) (< key hi))
|
|
|
|
|
|
;; Found count-call.
|
|
|
|
|
|
(1+ pos))
|
|
|
|
|
|
(else
|
|
|
|
|
|
;; Otherwise keep going.
|
|
|
|
|
|
(lp (1+ pos)))))
|
|
|
|
|
|
start)))))
|
|
|
|
|
|
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(define (stack-samples->procedure-data state)
|
|
|
|
|
|
(let ((table (make-hash-table))
|
|
|
|
|
|
(addr-cache (make-hash-table))
|
|
|
|
|
|
(call-counts (call-counts state))
|
|
|
|
|
|
(buffer (buffer state))
|
|
|
|
|
|
(len (buffer-pos state)))
|
|
|
|
|
|
(define (addr->call-data addr)
|
|
|
|
|
|
(let* ((pdi (addr->pdi addr addr-cache))
|
|
|
|
|
|
(entry (if pdi (program-debug-info-addr pdi) addr)))
|
|
|
|
|
|
(or (hashv-ref table entry)
|
|
|
|
|
|
(let ((data (make-call-data (and=> pdi program-debug-info-name)
|
|
|
|
|
|
(addr->printable entry pdi)
|
2014-03-01 12:59:58 +01:00
|
|
|
|
(find-source-for-addr entry)
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(and call-counts
|
|
|
|
|
|
(hashv-ref call-counts entry))
|
|
|
|
|
|
0
|
|
|
|
|
|
0)))
|
|
|
|
|
|
(hashv-set! table entry data)
|
|
|
|
|
|
data))))
|
|
|
|
|
|
|
|
|
|
|
|
(when call-counts
|
|
|
|
|
|
(hash-for-each (lambda (callee count)
|
2018-07-29 15:36:07 +02:00
|
|
|
|
(unless (number? callee)
|
|
|
|
|
|
(error "unexpected callee" callee))
|
|
|
|
|
|
(addr->call-data callee))
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
call-counts))
|
|
|
|
|
|
|
|
|
|
|
|
(let visit-stacks ((pos 0))
|
|
|
|
|
|
(cond
|
|
|
|
|
|
((< pos len)
|
2016-02-01 11:27:14 +01:00
|
|
|
|
(let ((pos (if call-counts
|
2018-07-29 15:36:07 +02:00
|
|
|
|
(skip-count-call buffer pos len)
|
|
|
|
|
|
pos)))
|
2016-02-01 11:27:14 +01:00
|
|
|
|
(inc-call-data-self-sample-count!
|
2018-07-29 15:36:07 +02:00
|
|
|
|
(addr->call-data (vector-ref buffer pos)))
|
2016-02-01 11:27:14 +01:00
|
|
|
|
(let visit-stack ((pos pos))
|
|
|
|
|
|
(cond
|
|
|
|
|
|
((vector-ref buffer pos)
|
2018-07-29 15:36:07 +02:00
|
|
|
|
=> (lambda (ip)
|
|
|
|
|
|
(inc-call-data-cum-sample-count! (addr->call-data ip))
|
2016-02-01 11:27:14 +01:00
|
|
|
|
(visit-stack (1+ pos))))
|
|
|
|
|
|
(else
|
|
|
|
|
|
(visit-stacks (1+ pos)))))))
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(else table)))))
|
|
|
|
|
|
|
2014-02-28 19:31:46 +01:00
|
|
|
|
(define (stack-samples->callee-lists state)
|
|
|
|
|
|
(let ((buffer (buffer state))
|
|
|
|
|
|
(len (buffer-pos state)))
|
|
|
|
|
|
(let visit-stacks ((pos 0) (out '()))
|
|
|
|
|
|
(cond
|
|
|
|
|
|
((< pos len)
|
2016-02-01 11:27:14 +01:00
|
|
|
|
(let visit-stack ((pos (if (call-counts state)
|
|
|
|
|
|
(skip-count-call buffer pos len)
|
|
|
|
|
|
pos))
|
|
|
|
|
|
(stack '()))
|
2014-02-28 19:31:46 +01:00
|
|
|
|
(cond
|
|
|
|
|
|
((vector-ref buffer pos)
|
|
|
|
|
|
=> (lambda (callee)
|
|
|
|
|
|
(visit-stack (1+ pos) (cons callee stack))))
|
|
|
|
|
|
(else
|
|
|
|
|
|
(visit-stacks (1+ pos) (cons (reverse stack) out))))))
|
|
|
|
|
|
(else (reverse out))))))
|
|
|
|
|
|
|
2014-04-14 16:54:51 +02:00
|
|
|
|
(define* (statprof-fold-call-data proc init #:optional
|
|
|
|
|
|
(state (existing-profiler-state)))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
"Fold @var{proc} over the call-data accumulated by statprof. Cannot be
|
|
|
|
|
|
called while statprof is active. @var{proc} should take two arguments,
|
|
|
|
|
|
@code{(@var{call-data} @var{prior-result})}.
|
|
|
|
|
|
|
|
|
|
|
|
Note that a given proc-name may appear multiple times, but if it does,
|
|
|
|
|
|
it represents different functions with the same name."
|
2014-02-21 22:12:47 +01:00
|
|
|
|
(when (statprof-active?)
|
|
|
|
|
|
(error "Can't call statprof-fold-call-data while profiler is running."))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
(hash-fold
|
|
|
|
|
|
(lambda (key value prior-result)
|
|
|
|
|
|
(proc value prior-result))
|
|
|
|
|
|
init
|
2014-04-14 16:54:51 +02:00
|
|
|
|
(stack-samples->procedure-data state)))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
2014-04-14 16:54:51 +02:00
|
|
|
|
(define* (statprof-proc-call-data proc #:optional
|
|
|
|
|
|
(state (existing-profiler-state)))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
"Returns the call-data associated with @var{proc}, or @code{#f} if
|
|
|
|
|
|
none is available."
|
2014-02-21 22:12:47 +01:00
|
|
|
|
(when (statprof-active?)
|
|
|
|
|
|
(error "Can't call statprof-proc-call-data while profiler is running."))
|
2015-11-27 11:44:11 +01:00
|
|
|
|
(unless (program? proc)
|
|
|
|
|
|
(error "statprof-call-data only works for VM programs"))
|
2018-08-06 13:34:48 +02:00
|
|
|
|
(hashv-ref (stack-samples->procedure-data state) (program-code proc)))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
;; Stats
|
|
|
|
|
|
|
2014-02-28 19:42:04 +01:00
|
|
|
|
(define-record-type stats
|
2014-03-01 12:59:58 +01:00
|
|
|
|
(make-stats proc-name proc-source
|
|
|
|
|
|
%-time-in-proc cum-secs-in-proc self-secs-in-proc
|
2016-02-01 14:58:34 +01:00
|
|
|
|
calls)
|
2014-02-28 19:42:04 +01:00
|
|
|
|
stats?
|
|
|
|
|
|
(proc-name statprof-stats-proc-name)
|
2014-03-01 12:59:58 +01:00
|
|
|
|
(proc-source statprof-stats-proc-source)
|
2014-02-28 19:42:04 +01:00
|
|
|
|
(%-time-in-proc statprof-stats-%-time-in-proc)
|
|
|
|
|
|
(cum-secs-in-proc statprof-stats-cum-secs-in-proc)
|
|
|
|
|
|
(self-secs-in-proc statprof-stats-self-secs-in-proc)
|
2016-02-01 14:58:34 +01:00
|
|
|
|
(calls statprof-stats-calls))
|
|
|
|
|
|
|
|
|
|
|
|
(define (statprof-stats-self-secs-per-call stats)
|
|
|
|
|
|
(let ((calls (statprof-stats-calls stats)))
|
|
|
|
|
|
(and calls
|
|
|
|
|
|
(/ (statprof-stats-self-secs-in-proc stats)
|
|
|
|
|
|
calls))))
|
|
|
|
|
|
|
|
|
|
|
|
(define (statprof-stats-cum-secs-per-call stats)
|
|
|
|
|
|
(let ((calls (statprof-stats-calls stats)))
|
|
|
|
|
|
(and calls
|
|
|
|
|
|
(/ (statprof-stats-cum-secs-in-proc stats)
|
|
|
|
|
|
;; `calls' might be 0 if we entered statprof during the
|
|
|
|
|
|
;; dynamic extent of the call.
|
|
|
|
|
|
(max calls 1)))))
|
2014-02-28 19:42:04 +01:00
|
|
|
|
|
2009-12-21 00:01:13 +01:00
|
|
|
|
(define (statprof-call-data->stats call-data)
|
|
|
|
|
|
"Returns an object of type @code{statprof-stats}."
|
2014-02-21 21:43:39 +01:00
|
|
|
|
(define state (existing-profiler-state))
|
2014-02-21 21:01:10 +01:00
|
|
|
|
|
2014-03-01 12:59:58 +01:00
|
|
|
|
(let* ((proc-name (call-data-name call-data))
|
|
|
|
|
|
(proc-source (and=> (call-data-source call-data) source->string))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
(self-samples (call-data-self-sample-count call-data))
|
|
|
|
|
|
(cum-samples (call-data-cum-sample-count call-data))
|
2014-04-14 16:54:51 +02:00
|
|
|
|
(all-samples (statprof-sample-count state))
|
|
|
|
|
|
(secs-per-sample (/ (statprof-accumulated-time state)
|
|
|
|
|
|
(statprof-sample-count state)))
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(num-calls (and (call-counts state)
|
|
|
|
|
|
(statprof-call-data-calls call-data))))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
2014-03-01 12:59:58 +01:00
|
|
|
|
(make-stats (or proc-name
|
|
|
|
|
|
;; If there is no name and no source, fall back to
|
|
|
|
|
|
;; printable.
|
|
|
|
|
|
(and (not proc-source) (call-data-printable call-data)))
|
|
|
|
|
|
proc-source
|
2014-02-28 19:42:04 +01:00
|
|
|
|
(* (/ self-samples all-samples) 100.0)
|
|
|
|
|
|
(* cum-samples secs-per-sample 1.0)
|
|
|
|
|
|
(* self-samples secs-per-sample 1.0)
|
2016-02-01 14:58:34 +01:00
|
|
|
|
num-calls)))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
|
|
|
|
|
|
(define (stats-sorter x y)
|
|
|
|
|
|
(let ((diff (- (statprof-stats-self-secs-in-proc x)
|
|
|
|
|
|
(statprof-stats-self-secs-in-proc y))))
|
|
|
|
|
|
(positive?
|
|
|
|
|
|
(if (= diff 0)
|
|
|
|
|
|
(- (statprof-stats-cum-secs-in-proc x)
|
|
|
|
|
|
(statprof-stats-cum-secs-in-proc y))
|
|
|
|
|
|
diff))))
|
|
|
|
|
|
|
2016-01-07 16:23:26 +01:00
|
|
|
|
(define* (statprof-display/flat port state)
|
2009-12-21 00:01:13 +01:00
|
|
|
|
"Displays a gprof-like summary of the statistics collected. Unless an
|
|
|
|
|
|
optional @var{port} argument is passed, uses the current output port."
|
|
|
|
|
|
(cond
|
2014-04-14 16:54:51 +02:00
|
|
|
|
((zero? (statprof-sample-count state))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
(format port "No samples recorded.\n"))
|
|
|
|
|
|
(else
|
|
|
|
|
|
(let* ((stats-list (statprof-fold-call-data
|
|
|
|
|
|
(lambda (data prior-value)
|
|
|
|
|
|
(cons (statprof-call-data->stats data)
|
|
|
|
|
|
prior-value))
|
2014-04-14 16:54:51 +02:00
|
|
|
|
'()
|
|
|
|
|
|
state))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
(sorted-stats (sort stats-list stats-sorter)))
|
|
|
|
|
|
|
|
|
|
|
|
(define (display-stats-line stats)
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(format port "~6,2f ~9,2f ~9,2f"
|
|
|
|
|
|
(statprof-stats-%-time-in-proc stats)
|
|
|
|
|
|
(statprof-stats-cum-secs-in-proc stats)
|
|
|
|
|
|
(statprof-stats-self-secs-in-proc stats))
|
|
|
|
|
|
(if (call-counts state)
|
|
|
|
|
|
(if (statprof-stats-calls stats)
|
2016-02-01 14:58:34 +01:00
|
|
|
|
(format port " ~7d "
|
|
|
|
|
|
(statprof-stats-calls stats))
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(format port " "))
|
|
|
|
|
|
(display " " port))
|
2014-03-01 12:59:58 +01:00
|
|
|
|
(let ((source (statprof-stats-proc-source stats))
|
|
|
|
|
|
(name (statprof-stats-proc-name stats)))
|
|
|
|
|
|
(when source
|
|
|
|
|
|
(display source port)
|
|
|
|
|
|
(when name
|
|
|
|
|
|
(display ":" port)))
|
|
|
|
|
|
(when name
|
|
|
|
|
|
(display name port))
|
|
|
|
|
|
(newline port)))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(if (call-counts state)
|
2009-12-21 00:01:13 +01:00
|
|
|
|
(begin
|
2016-02-01 14:58:34 +01:00
|
|
|
|
(format port "~5a ~10a ~7a ~8a\n"
|
|
|
|
|
|
"% " "cumulative" "self" "")
|
|
|
|
|
|
(format port "~5a ~9a ~8a ~7a ~a\n"
|
|
|
|
|
|
"time" "seconds" "seconds" "calls" "procedure"))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
(begin
|
2014-03-01 12:59:58 +01:00
|
|
|
|
(format port "~5a ~10a ~7a ~8a\n"
|
2009-12-21 00:01:13 +01:00
|
|
|
|
"%" "cumulative" "self" "")
|
2014-03-01 12:59:58 +01:00
|
|
|
|
(format port "~5a ~10a ~7a ~a\n"
|
|
|
|
|
|
"time" "seconds" "seconds" "procedure")))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
|
|
|
|
|
(for-each display-stats-line sorted-stats)
|
|
|
|
|
|
|
|
|
|
|
|
(display "---\n" port)
|
2017-03-19 16:15:24 +01:00
|
|
|
|
(format port "Sample count: ~A\n" (statprof-sample-count state))
|
|
|
|
|
|
(format port "Total time: ~A seconds (~A seconds in GC)\n"
|
2016-01-07 16:23:26 +01:00
|
|
|
|
(statprof-accumulated-time state)
|
|
|
|
|
|
(/ (gc-time-taken state)
|
|
|
|
|
|
1.0 internal-time-units-per-second))))))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
2014-02-28 11:06:55 +01:00
|
|
|
|
(define* (statprof-display-anomalies #:optional (state
|
|
|
|
|
|
(existing-profiler-state)))
|
|
|
|
|
|
"A sanity check that attempts to detect anomalies in statprof's
|
2009-12-21 00:01:13 +01:00
|
|
|
|
statistics.@code{}"
|
|
|
|
|
|
(statprof-fold-call-data
|
|
|
|
|
|
(lambda (data prior-value)
|
Statprof always stores full stack traces
* module/statprof.scm (<state>): Instead of a boolean count-calls?,
treat the presence of a call-counts hash table as indicating a need to
count calls. That hash table maps callees to call counts. A "callee"
is either the IP of the entry of a program, the symbolic name of a
primitive, or the identity of a non-program.
New members "buffer" and "buffer-pos" replace "procedure-data".
We try to avoid analyzing things at runtime, instead just recording
the stack traces into a buffer. This will let us do smarter things
when post-processing.
(fresh-buffer, expand-buffer): New helpers.
(fresh-profiler-state): Adapt to <state> changes.
(sample-stack-procs): Instead of updating the procedure-data
table (which no longer exists), instead trace the stack into the
buffer.
(count-call): Update to update the call-counts table instead of the
procedure-data table.
(statprof-start, statprof-start): Adapt to call-counts change.
(call-data): Move lower in the file. Add "name" and "printable"
members, and no longer store a proc.
(source->string, program-debug-info-printable, addr->pdi)
(addr->printable): New helpers.
(stack-samples->procedure-data): New procedure to process stack trace
buffer into a hash table of the same format as the old procedure-data
table.
(statprof-fold-call-data, statprof-proc-call-data): Use
stack-samples->procedure-data instead of procedure-data.
(statprof-call-data->stats): Adapt to count-calls change.
(statprof-display, statprof-display-anomalies): Adapt.
2014-02-28 18:35:25 +01:00
|
|
|
|
(when (and (call-counts state)
|
2014-02-22 14:54:17 +01:00
|
|
|
|
(zero? (call-data-call-count data))
|
|
|
|
|
|
(positive? (call-data-cum-sample-count data)))
|
2016-01-07 16:23:26 +01:00
|
|
|
|
(format #t
|
|
|
|
|
|
"==[~A ~A ~A]\n"
|
|
|
|
|
|
(call-data-name data)
|
|
|
|
|
|
(call-data-call-count data)
|
|
|
|
|
|
(call-data-cum-sample-count data))))
|
2014-04-14 16:54:51 +02:00
|
|
|
|
#f
|
|
|
|
|
|
state)
|
2016-01-07 16:23:26 +01:00
|
|
|
|
(format #t "Total time: ~A\n" (statprof-accumulated-time state))
|
|
|
|
|
|
(format #t "Sample count: ~A\n" (statprof-sample-count state)))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
2014-02-28 11:06:55 +01:00
|
|
|
|
(define* (statprof-accumulated-time #:optional (state
|
|
|
|
|
|
(existing-profiler-state)))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
"Returns the time accumulated during the last statprof run.@code{}"
|
2014-02-28 11:06:55 +01:00
|
|
|
|
(/ (accumulated-time state) 1.0 internal-time-units-per-second))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
2014-02-28 11:06:55 +01:00
|
|
|
|
(define* (statprof-sample-count #:optional (state (existing-profiler-state)))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
"Returns the number of samples taken during the last statprof run.@code{}"
|
2014-02-28 11:06:55 +01:00
|
|
|
|
(sample-count state))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
|
|
|
|
|
(define statprof-call-data-name call-data-name)
|
|
|
|
|
|
(define statprof-call-data-calls call-data-call-count)
|
|
|
|
|
|
(define statprof-call-data-cum-samples call-data-cum-sample-count)
|
|
|
|
|
|
(define statprof-call-data-self-samples call-data-self-sample-count)
|
|
|
|
|
|
|
2014-02-28 11:06:55 +01:00
|
|
|
|
(define* (statprof-fetch-stacks #:optional (state (existing-profiler-state)))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
"Returns a list of stacks, as they were captured since the last call
|
2014-02-28 19:31:46 +01:00
|
|
|
|
to @code{statprof-reset}."
|
|
|
|
|
|
(stack-samples->callee-lists state))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
|
|
|
|
|
;; tree ::= (car n . tree*)
|
|
|
|
|
|
|
|
|
|
|
|
(define (lists->trees lists equal?)
|
|
|
|
|
|
(let lp ((in lists) (n-terminal 0) (tails '()))
|
|
|
|
|
|
(cond
|
|
|
|
|
|
((null? in)
|
|
|
|
|
|
(let ((trees (map (lambda (tail)
|
|
|
|
|
|
(cons (car tail)
|
|
|
|
|
|
(lists->trees (cdr tail) equal?)))
|
|
|
|
|
|
tails)))
|
|
|
|
|
|
(cons (apply + n-terminal (map cadr trees))
|
|
|
|
|
|
(sort trees
|
|
|
|
|
|
(lambda (a b) (> (cadr a) (cadr b)))))))
|
|
|
|
|
|
((null? (car in))
|
|
|
|
|
|
(lp (cdr in) (1+ n-terminal) tails))
|
|
|
|
|
|
((find (lambda (x) (equal? (car x) (caar in)))
|
|
|
|
|
|
tails)
|
|
|
|
|
|
=> (lambda (tail)
|
|
|
|
|
|
(lp (cdr in)
|
|
|
|
|
|
n-terminal
|
|
|
|
|
|
(assq-set! tails
|
|
|
|
|
|
(car tail)
|
|
|
|
|
|
(cons (cdar in) (cdr tail))))))
|
|
|
|
|
|
(else
|
|
|
|
|
|
(lp (cdr in)
|
|
|
|
|
|
n-terminal
|
|
|
|
|
|
(acons (caar in) (list (cdar in)) tails))))))
|
|
|
|
|
|
|
2016-01-07 16:23:26 +01:00
|
|
|
|
(define (collect-cycles items)
|
|
|
|
|
|
(define (find-cycle item stack)
|
|
|
|
|
|
(match (vhash-assoc item stack)
|
|
|
|
|
|
(#f #f)
|
|
|
|
|
|
((_ . pos)
|
|
|
|
|
|
(let ((size (- (vlist-length stack) pos)))
|
|
|
|
|
|
(and (<= (1- (* size 2)) (vlist-length stack))
|
|
|
|
|
|
(let lp ((i 0))
|
|
|
|
|
|
(if (= i (1- size))
|
|
|
|
|
|
size
|
|
|
|
|
|
(and (equal? (car (vlist-ref stack i))
|
|
|
|
|
|
(car (vlist-ref stack (+ i size))))
|
|
|
|
|
|
(lp (1+ i))))))))))
|
|
|
|
|
|
(define (collect-cycle stack size)
|
|
|
|
|
|
(vlist-fold-right (lambda (pair cycle)
|
|
|
|
|
|
(cons (car pair) cycle))
|
|
|
|
|
|
'()
|
|
|
|
|
|
(vlist-take stack size)))
|
|
|
|
|
|
(define (detect-cycle items stack)
|
|
|
|
|
|
(match items
|
|
|
|
|
|
(() stack)
|
|
|
|
|
|
((item . items)
|
|
|
|
|
|
(let* ((cycle-size (find-cycle item stack)))
|
|
|
|
|
|
(if cycle-size
|
|
|
|
|
|
(chomp-cycles (collect-cycle stack cycle-size)
|
|
|
|
|
|
items
|
|
|
|
|
|
(vlist-drop stack (1- (* cycle-size 2))))
|
|
|
|
|
|
(chomp-cycles (list item) items stack))))))
|
|
|
|
|
|
(define (skip-cycles cycle items)
|
|
|
|
|
|
(let lp ((a cycle) (b items))
|
|
|
|
|
|
(match a
|
|
|
|
|
|
(() (skip-cycles cycle b))
|
|
|
|
|
|
((a . a*)
|
|
|
|
|
|
(match b
|
|
|
|
|
|
(() items)
|
|
|
|
|
|
((b . b*)
|
|
|
|
|
|
(if (equal? a b)
|
|
|
|
|
|
(lp a* b*)
|
|
|
|
|
|
items)))))))
|
|
|
|
|
|
(define (chomp-cycles cycle items stack)
|
|
|
|
|
|
(detect-cycle (skip-cycles cycle items)
|
|
|
|
|
|
(vhash-cons (match cycle
|
|
|
|
|
|
((item) item)
|
|
|
|
|
|
(cycle cycle))
|
|
|
|
|
|
(vlist-length stack)
|
|
|
|
|
|
stack)))
|
|
|
|
|
|
(vlist-fold
|
|
|
|
|
|
(lambda (pair out)
|
|
|
|
|
|
(cons (car pair) out))
|
|
|
|
|
|
'()
|
|
|
|
|
|
(detect-cycle items vlist-null)))
|
|
|
|
|
|
|
2016-01-07 16:56:39 +01:00
|
|
|
|
(define* (statprof-fetch-call-tree #:optional (state (existing-profiler-state))
|
|
|
|
|
|
#:key precise?)
|
2009-12-21 00:01:13 +01:00
|
|
|
|
"Return a call tree for the previous statprof run.
|
|
|
|
|
|
|
|
|
|
|
|
The return value is a list of nodes, each of which is of the type:
|
|
|
|
|
|
@code
|
|
|
|
|
|
node ::= (@var{proc} @var{count} . @var{nodes})
|
|
|
|
|
|
@end code"
|
2016-01-07 16:56:39 +01:00
|
|
|
|
(define-syntax-rule (define-memoized (fn arg) body)
|
|
|
|
|
|
(define fn
|
|
|
|
|
|
(let ((table (make-hash-table)))
|
|
|
|
|
|
(lambda (arg)
|
|
|
|
|
|
(cond
|
|
|
|
|
|
((hash-get-handle table arg) => cdr)
|
|
|
|
|
|
(else
|
|
|
|
|
|
(let ((res body))
|
|
|
|
|
|
(hash-set! table arg res)
|
|
|
|
|
|
res)))))))
|
|
|
|
|
|
(define-memoized (callee->printable callee)
|
2014-02-28 19:31:46 +01:00
|
|
|
|
(cond
|
|
|
|
|
|
((number? callee)
|
2016-01-07 16:23:26 +01:00
|
|
|
|
(let* ((pdi (find-program-debug-info callee))
|
|
|
|
|
|
(name (or (and=> (and pdi (program-debug-info-name pdi))
|
|
|
|
|
|
symbol->string)
|
|
|
|
|
|
(string-append "#x" (number->string callee 16))))
|
2016-01-07 16:56:39 +01:00
|
|
|
|
(loc (and=> (find-source-for-addr
|
|
|
|
|
|
(or (and (not precise?)
|
|
|
|
|
|
(and=> pdi program-debug-info-addr))
|
|
|
|
|
|
callee))
|
|
|
|
|
|
source->string)))
|
2016-01-07 16:23:26 +01:00
|
|
|
|
(if loc
|
|
|
|
|
|
(string-append name " at " loc)
|
|
|
|
|
|
name)))
|
2014-02-28 19:31:46 +01:00
|
|
|
|
(else
|
|
|
|
|
|
(with-output-to-string (lambda () (write callee))))))
|
2016-01-07 16:56:39 +01:00
|
|
|
|
(define (munge-stack stack)
|
|
|
|
|
|
;; We collect the sample in newest-to-oldest
|
|
|
|
|
|
;; order. Change to have the oldest first.
|
|
|
|
|
|
(let ((stack (reverse stack)))
|
|
|
|
|
|
(define (cycle->printable item)
|
|
|
|
|
|
(if (string? item)
|
|
|
|
|
|
item
|
|
|
|
|
|
(string-join (map cycle->printable item) ", ")))
|
|
|
|
|
|
(map cycle->printable (collect-cycles (map callee->printable stack)))))
|
|
|
|
|
|
(let ((stacks (map munge-stack (stack-samples->callee-lists state))))
|
|
|
|
|
|
(cons #t (lists->trees stacks equal?))))
|
2009-12-21 00:01:13 +01:00
|
|
|
|
|
2016-01-07 16:23:26 +01:00
|
|
|
|
(define (statprof-display/tree port state)
|
|
|
|
|
|
(match (statprof-fetch-call-tree state)
|
|
|
|
|
|
((#t total-count . trees)
|
|
|
|
|
|
(define (print-tree tree indent)
|
|
|
|
|
|
(define (print-subtree tree) (print-tree tree (+ indent 2)))
|
|
|
|
|
|
(match tree
|
|
|
|
|
|
((callee count . trees)
|
|
|
|
|
|
(format port "~vt~,1f% ~a\n" indent (* 100. (/ count total-count))
|
|
|
|
|
|
callee)
|
|
|
|
|
|
(for-each print-subtree trees))))
|
|
|
|
|
|
(for-each (lambda (tree) (print-tree tree 0)) trees)))
|
|
|
|
|
|
(display "---\n" port)
|
|
|
|
|
|
(format port "Sample count: ~A\n" (statprof-sample-count state))
|
|
|
|
|
|
(format port "Total time: ~A seconds (~A seconds in GC)\n"
|
|
|
|
|
|
(statprof-accumulated-time state)
|
|
|
|
|
|
(/ (gc-time-taken state)
|
|
|
|
|
|
1.0 internal-time-units-per-second)))
|
|
|
|
|
|
|
|
|
|
|
|
(define* (statprof-display #:optional (port (current-output-port))
|
|
|
|
|
|
(state (existing-profiler-state))
|
|
|
|
|
|
#:key (style 'flat))
|
|
|
|
|
|
"Displays a summary of the statistics collected. Unless an optional
|
|
|
|
|
|
@var{port} argument is passed, uses the current output port."
|
|
|
|
|
|
(case style
|
|
|
|
|
|
((flat) (statprof-display/flat port state))
|
|
|
|
|
|
((anomalies)
|
|
|
|
|
|
(with-output-to-port port
|
|
|
|
|
|
(lambda ()
|
|
|
|
|
|
(statprof-display-anomalies state))))
|
|
|
|
|
|
((tree) (statprof-display/tree port state))
|
|
|
|
|
|
(else (error "Unknown statprof display style" style))))
|
|
|
|
|
|
|
2014-03-01 16:09:30 +01:00
|
|
|
|
(define (call-thunk thunk)
|
2015-01-22 14:37:18 +01:00
|
|
|
|
(call-with-values (lambda () (thunk))
|
|
|
|
|
|
(lambda results
|
|
|
|
|
|
(apply values results))))
|
2014-03-01 16:09:30 +01:00
|
|
|
|
|
2020-01-23 21:46:28 +01:00
|
|
|
|
;; Prevent inlining of 'call-thunk' so that it can be used to determine
|
|
|
|
|
|
;; the stack boundaries passed to 'make-stack'.
|
|
|
|
|
|
(set! call-thunk call-thunk)
|
|
|
|
|
|
|
2010-01-14 22:52:07 +01:00
|
|
|
|
(define* (statprof thunk #:key (loop 1) (hz 100) (count-calls? #f)
|
2016-01-07 16:23:26 +01:00
|
|
|
|
(port (current-output-port)) full-stacks?
|
|
|
|
|
|
(display-style 'flat))
|
2015-01-11 20:44:36 +01:00
|
|
|
|
"Profile the execution of @var{thunk}, and return its return values.
|
2010-01-14 22:52:07 +01:00
|
|
|
|
|
2015-01-11 20:44:36 +01:00
|
|
|
|
The stack will be sampled @var{hz} times per second, and the thunk
|
|
|
|
|
|
itself will be called @var{loop} times.
|
2010-01-14 22:52:07 +01:00
|
|
|
|
|
|
|
|
|
|
If @var{count-calls?} is true, all procedure calls will be recorded. This
|
2014-02-28 19:31:46 +01:00
|
|
|
|
operation is somewhat expensive."
|
2010-01-14 22:52:07 +01:00
|
|
|
|
|
2014-02-28 11:27:56 +01:00
|
|
|
|
(let ((state (fresh-profiler-state #:count-calls? count-calls?
|
|
|
|
|
|
#:sampling-period
|
2014-03-01 16:09:30 +01:00
|
|
|
|
(inexact->exact (round (/ 1e6 hz)))
|
2014-05-01 14:26:20 +02:00
|
|
|
|
#:outer-cut
|
|
|
|
|
|
(program-address-range call-thunk))))
|
2014-02-25 22:16:49 +01:00
|
|
|
|
(parameterize ((profiler-state state))
|
|
|
|
|
|
(dynamic-wind
|
|
|
|
|
|
(lambda ()
|
2014-02-28 11:27:56 +01:00
|
|
|
|
(statprof-start state))
|
2014-02-25 22:16:49 +01:00
|
|
|
|
(lambda ()
|
|
|
|
|
|
(let lp ((i loop))
|
2015-01-22 14:37:18 +01:00
|
|
|
|
(unless (= i 1)
|
2014-03-01 16:09:30 +01:00
|
|
|
|
(call-thunk thunk)
|
2015-01-22 14:37:18 +01:00
|
|
|
|
(lp (1- i))))
|
|
|
|
|
|
(call-thunk thunk))
|
2014-02-25 22:16:49 +01:00
|
|
|
|
(lambda ()
|
2014-02-28 11:27:56 +01:00
|
|
|
|
(statprof-stop state)
|
2016-01-07 16:23:26 +01:00
|
|
|
|
(statprof-display port state #:style display-style))))))
|
2010-01-14 22:52:07 +01:00
|
|
|
|
|
2014-04-14 16:54:51 +02:00
|
|
|
|
(define* (gcprof thunk #:key (loop 1) full-stacks? (port (current-output-port)))
|
2011-05-05 10:08:29 +02:00
|
|
|
|
"Do an allocation profile of the execution of @var{thunk}.
|
|
|
|
|
|
|
|
|
|
|
|
The stack will be sampled soon after every garbage collection, yielding
|
|
|
|
|
|
an approximate idea of what is causing allocation in your program.
|
|
|
|
|
|
|
|
|
|
|
|
Since GC does not occur very frequently, you may need to use the
|
|
|
|
|
|
@var{loop} parameter, to cause @var{thunk} to be called @var{loop}
|
2014-02-28 19:31:46 +01:00
|
|
|
|
times."
|
2011-05-05 10:08:29 +02:00
|
|
|
|
|
2014-05-01 14:26:20 +02:00
|
|
|
|
(let ((state (fresh-profiler-state #:outer-cut
|
|
|
|
|
|
(program-address-range call-thunk))))
|
2014-02-25 22:16:49 +01:00
|
|
|
|
(parameterize ((profiler-state state))
|
|
|
|
|
|
(define (gc-callback)
|
2014-02-28 10:48:41 +01:00
|
|
|
|
(unless (inside-profiler? state)
|
2014-02-25 22:16:49 +01:00
|
|
|
|
(set-inside-profiler?! state #t)
|
|
|
|
|
|
|
|
|
|
|
|
(let ((stop-time (get-internal-run-time))
|
2017-03-09 10:48:02 +01:00
|
|
|
|
;; Cut down to gc-callback, and then two more (the
|
|
|
|
|
|
;; after-gc async and the handle-interrupts trampoline).
|
|
|
|
|
|
;; See the note in profile-signal-handler also.
|
|
|
|
|
|
(stack (or (make-stack #t gc-callback (outer-cut state) 2)
|
2014-02-25 22:16:49 +01:00
|
|
|
|
(pk 'what! (make-stack #t)))))
|
|
|
|
|
|
(sample-stack-procs state stack)
|
|
|
|
|
|
(accumulate-time state stop-time)
|
|
|
|
|
|
(set-last-start-time! state (get-internal-run-time)))
|
2014-02-28 19:31:46 +01:00
|
|
|
|
|
2014-02-28 10:48:41 +01:00
|
|
|
|
(set-inside-profiler?! state #f)))
|
2014-02-25 22:16:49 +01:00
|
|
|
|
|
|
|
|
|
|
(dynamic-wind
|
|
|
|
|
|
(lambda ()
|
2014-02-28 10:48:41 +01:00
|
|
|
|
(set-profile-level! state 1)
|
|
|
|
|
|
(set-last-start-time! state (get-internal-run-time))
|
|
|
|
|
|
(set-gc-time-taken! state (assq-ref (gc-stats) 'gc-time-taken))
|
|
|
|
|
|
(add-hook! after-gc-hook gc-callback))
|
2014-02-25 22:16:49 +01:00
|
|
|
|
(lambda ()
|
|
|
|
|
|
(let lp ((i loop))
|
|
|
|
|
|
(unless (zero? i)
|
2014-03-01 16:09:30 +01:00
|
|
|
|
(call-thunk thunk)
|
2014-02-25 22:16:49 +01:00
|
|
|
|
(lp (1- i)))))
|
|
|
|
|
|
(lambda ()
|
2014-02-28 10:48:41 +01:00
|
|
|
|
(remove-hook! after-gc-hook gc-callback)
|
|
|
|
|
|
(set-gc-time-taken! state
|
|
|
|
|
|
(- (assq-ref (gc-stats) 'gc-time-taken)
|
|
|
|
|
|
(gc-time-taken state)))
|
|
|
|
|
|
(accumulate-time state (get-internal-run-time))
|
|
|
|
|
|
(set-profile-level! state 0)
|
2014-04-14 16:54:51 +02:00
|
|
|
|
(statprof-display port state))))))
|