(require 'slime)
|
|
(require 'cl-lib)
|
|
(eval-when-compile (require 'cl)) ; lexical-let*
|
|
|
|
(define-slime-contrib slime-sprof
|
|
"Integration with SBCL's sb-sprof."
|
|
(:authors "Juho Snellman"
|
|
"Stas Boukarev")
|
|
(:license "MIT")
|
|
(:swank-dependencies swank-sprof)
|
|
(:on-load
|
|
(let ((C '(and (slime-connected-p)
|
|
(equal (slime-lisp-implementation-type) "SBCL"))))
|
|
(setf (cdr (last (assoc "Profiling" slime-easy-menu)))
|
|
`("--"
|
|
[ "Start sb-sprof" slime-sprof-start ,C ]
|
|
[ "Stop sb-sprof" slime-sprof-stop ,C ]
|
|
[ "Report sb-sprof" slime-sprof-report ,C ])))))
|
|
|
|
(defvar slime-sprof-exclude-swank nil
|
|
"*Display swank functions in the report.")
|
|
|
|
(define-derived-mode slime-sprof-browser-mode fundamental-mode
|
|
"slprof"
|
|
"Mode for browsing profiler data\
|
|
\\<slime-sprof-browser-mode-map>\
|
|
\\{slime-sprof-browser-mode-map}"
|
|
:syntax-table lisp-mode-syntax-table
|
|
(setq buffer-read-only t))
|
|
|
|
(set-keymap-parent slime-sprof-browser-mode-map slime-parent-map)
|
|
|
|
(slime-define-keys slime-sprof-browser-mode-map
|
|
("h" 'describe-mode)
|
|
("d" 'slime-sprof-browser-disassemble-function)
|
|
("g" 'slime-sprof-browser-go-to)
|
|
("v" 'slime-sprof-browser-view-source)
|
|
("s" 'slime-sprof-toggle-swank-exclusion)
|
|
((kbd "RET") 'slime-sprof-browser-toggle))
|
|
|
|
;; Start / stop profiling
|
|
|
|
(cl-defun slime-sprof-start (&optional (mode :cpu))
|
|
(interactive)
|
|
(slime-eval `(swank:swank-sprof-start :mode ,mode)))
|
|
|
|
(defun slime-sprof-start-alloc ()
|
|
(interactive)
|
|
(slime-sprof-start :alloc))
|
|
|
|
(defun slime-sprof-start-time ()
|
|
(interactive)
|
|
(slime-sprof-start :time))
|
|
|
|
(defun slime-sprof-stop ()
|
|
(interactive)
|
|
(slime-eval `(swank:swank-sprof-stop)))
|
|
|
|
;; Reporting
|
|
|
|
(defun slime-sprof-format (graph)
|
|
(with-current-buffer (slime-buffer-name :sprof)
|
|
(let ((inhibit-read-only t))
|
|
(erase-buffer)
|
|
(insert (format "%4s %-54s %6s %6s %6s\n"
|
|
"Rank"
|
|
"Name"
|
|
"Self%"
|
|
"Cumul%"
|
|
"Total%"))
|
|
(dolist (data graph)
|
|
(slime-sprof-browser-insert-line data 54))))
|
|
(forward-line 2))
|
|
|
|
(cl-defun slime-sprof-update (&optional (exclude-swank slime-sprof-exclude-swank))
|
|
(slime-eval-async `(swank:swank-sprof-get-call-graph
|
|
:exclude-swank ,exclude-swank)
|
|
'slime-sprof-format))
|
|
|
|
(defalias 'slime-sprof-browser 'slime-sprof-report)
|
|
|
|
(defun slime-sprof-report ()
|
|
(interactive)
|
|
(slime-with-popup-buffer ((slime-buffer-name :sprof)
|
|
:connection t
|
|
:select t
|
|
:mode 'slime-sprof-browser-mode)
|
|
(slime-sprof-update)))
|
|
|
|
(defun slime-sprof-toggle-swank-exclusion ()
|
|
(interactive)
|
|
(setq slime-sprof-exclude-swank
|
|
(not slime-sprof-exclude-swank))
|
|
(slime-sprof-update))
|
|
|
|
(defun slime-sprof-browser-insert-line (data name-length)
|
|
(cl-destructuring-bind (index name self cumul total)
|
|
data
|
|
(if index
|
|
(insert (format "%-4d " index))
|
|
(insert " "))
|
|
(slime-insert-propertized
|
|
(slime-sprof-browser-name-properties)
|
|
(format (format "%%-%ds " name-length)
|
|
(slime-sprof-abbreviate-name name name-length)))
|
|
(insert (format "%6.2f " self))
|
|
(when cumul
|
|
(insert (format "%6.2f " cumul))
|
|
(when total
|
|
(insert (format "%6.2f" total))))
|
|
(when index
|
|
(slime-sprof-browser-add-line-text-properties
|
|
`(profile-index ,index expanded nil)))
|
|
(insert "\n")))
|
|
|
|
(defun slime-sprof-abbreviate-name (name max-length)
|
|
(cl-subseq name 0 (min (length name) max-length)))
|
|
|
|
;; Expanding / collapsing
|
|
|
|
(defun slime-sprof-browser-toggle ()
|
|
(interactive)
|
|
(let ((index (get-text-property (point) 'profile-index)))
|
|
(when index
|
|
(save-excursion
|
|
(if (slime-sprof-browser-line-expanded-p)
|
|
(slime-sprof-browser-collapse)
|
|
(slime-sprof-browser-expand))))))
|
|
|
|
(defun slime-sprof-browser-collapse ()
|
|
(let ((inhibit-read-only t))
|
|
(slime-sprof-browser-add-line-text-properties '(expanded nil))
|
|
(forward-line)
|
|
(cl-loop until (or (eobp)
|
|
(get-text-property (point) 'profile-index))
|
|
do
|
|
(delete-region (point-at-bol) (point-at-eol))
|
|
(unless (eobp)
|
|
(delete-char 1)))))
|
|
|
|
(defun slime-sprof-browser-expand ()
|
|
(lexical-let* ((buffer (current-buffer))
|
|
(point (point))
|
|
(index (get-text-property point 'profile-index)))
|
|
(slime-eval-async `(swank:swank-sprof-expand-node ,index)
|
|
(lambda (data)
|
|
(with-current-buffer buffer
|
|
(save-excursion
|
|
(destructuring-bind (&key callers calls)
|
|
data
|
|
(slime-sprof-browser-add-expansion callers
|
|
"Callers"
|
|
0)
|
|
(slime-sprof-browser-add-expansion calls
|
|
"Calls"
|
|
0))))))))
|
|
|
|
(defun slime-sprof-browser-add-expansion (data type nesting)
|
|
(when data
|
|
(let ((inhibit-read-only t))
|
|
(slime-sprof-browser-add-line-text-properties '(expanded t))
|
|
(end-of-line)
|
|
(insert (format "\n %s" type))
|
|
(dolist (node data)
|
|
(cl-destructuring-bind (index name cumul) node
|
|
(insert (format (format "\n%%%ds" (+ 7 (* 2 nesting))) ""))
|
|
(slime-insert-propertized
|
|
(slime-sprof-browser-name-properties)
|
|
(let ((len (- 59 (* 2 nesting))))
|
|
(format (format "%%-%ds " len)
|
|
(slime-sprof-abbreviate-name name len))))
|
|
(slime-sprof-browser-add-line-text-properties
|
|
`(profile-sub-index ,index))
|
|
(insert (format "%6.2f" cumul)))))))
|
|
|
|
(defun slime-sprof-browser-line-expanded-p ()
|
|
(get-text-property (point) 'expanded))
|
|
|
|
(defun slime-sprof-browser-add-line-text-properties (properties)
|
|
(add-text-properties (point-at-bol)
|
|
(point-at-eol)
|
|
properties))
|
|
|
|
(defun slime-sprof-browser-name-properties ()
|
|
'(face sldb-restart-number-face))
|
|
|
|
;; "Go to function"
|
|
|
|
(defun slime-sprof-browser-go-to ()
|
|
(interactive)
|
|
(let ((sub-index (get-text-property (point) 'profile-sub-index)))
|
|
(when sub-index
|
|
(let ((pos (text-property-any
|
|
(point-min) (point-max) 'profile-index sub-index)))
|
|
(when pos (goto-char pos))))))
|
|
|
|
;; Disassembly
|
|
|
|
(defun slime-sprof-browser-disassemble-function ()
|
|
(interactive)
|
|
(let ((index (or (get-text-property (point) 'profile-index)
|
|
(get-text-property (point) 'profile-sub-index))))
|
|
(when index
|
|
(slime-eval-describe `(swank:swank-sprof-disassemble
|
|
,index)))))
|
|
|
|
;; View source
|
|
|
|
(defun slime-sprof-browser-view-source ()
|
|
(interactive)
|
|
(let ((index (or (get-text-property (point) 'profile-index)
|
|
(get-text-property (point) 'profile-sub-index))))
|
|
(when index
|
|
(slime-eval-async
|
|
`(swank:swank-sprof-source-location ,index)
|
|
(lambda (source-location)
|
|
(slime-dcase source-location
|
|
((:error message)
|
|
(message "%s" message)
|
|
(ding))
|
|
(t
|
|
(slime-show-source-location source-location))))))))
|
|
|
|
(provide 'slime-sprof)
|