;;; swank-sprof.lisp
|
|
;;
|
|
;; Authors: Juho Snellman
|
|
;;
|
|
;; License: MIT
|
|
;;
|
|
|
|
(in-package :swank)
|
|
|
|
#+sbcl
|
|
(eval-when (:compile-toplevel :load-toplevel :execute)
|
|
(require :sb-sprof))
|
|
|
|
#+sbcl(progn
|
|
|
|
(defvar *call-graph* nil)
|
|
(defvar *node-numbers* nil)
|
|
(defvar *number-nodes* nil)
|
|
|
|
(defun frame-name (name)
|
|
(if (consp name)
|
|
(case (first name)
|
|
((sb-c::xep sb-c::tl-xep
|
|
sb-c::&more-processor
|
|
sb-c::top-level-form
|
|
sb-c::&optional-processor)
|
|
(second name))
|
|
(sb-pcl::fast-method
|
|
(cdr name))
|
|
((flet labels lambda)
|
|
(let* ((in (member :in name)))
|
|
(if (stringp (cadr in))
|
|
(append (ldiff name in) (cddr in))
|
|
name)))
|
|
(t
|
|
name))
|
|
name))
|
|
|
|
(defun pretty-name (name)
|
|
(let ((*package* (find-package :common-lisp-user))
|
|
(*print-right-margin* most-positive-fixnum))
|
|
(format nil "~S" (frame-name name))))
|
|
|
|
(defun samples-percent (count)
|
|
(sb-sprof::samples-percent *call-graph* count))
|
|
|
|
(defun node-values (node)
|
|
(values (pretty-name (sb-sprof::node-name node))
|
|
(samples-percent (sb-sprof::node-count node))
|
|
(samples-percent (sb-sprof::node-accrued-count node))))
|
|
|
|
(defun filter-swank-nodes (nodes)
|
|
(let ((swank-packages (load-time-value
|
|
(mapcar #'find-package
|
|
'(swank swank/rpc swank/mop
|
|
swank/match swank/backend)))))
|
|
(remove-if (lambda (node)
|
|
(let ((name (sb-sprof::node-name node)))
|
|
(and (symbolp name)
|
|
(member (symbol-package name) swank-packages
|
|
:test #'eq))))
|
|
nodes)))
|
|
|
|
(defun serialize-call-graph (&key exclude-swank)
|
|
(let ((nodes (sb-sprof::call-graph-flat-nodes *call-graph*)))
|
|
(when exclude-swank
|
|
(setf nodes (filter-swank-nodes nodes)))
|
|
(setf nodes (sort (copy-list nodes) #'>
|
|
;; :key #'sb-sprof::node-count)))
|
|
:key #'sb-sprof::node-accrued-count))
|
|
(setf *number-nodes* (make-hash-table))
|
|
(setf *node-numbers* (make-hash-table))
|
|
(loop for node in nodes
|
|
for i from 1
|
|
with total = 0
|
|
collect (multiple-value-bind (name self cumulative)
|
|
(node-values node)
|
|
(setf (gethash node *node-numbers*) i
|
|
(gethash i *number-nodes*) node)
|
|
(incf total self)
|
|
(list i name self cumulative total)) into list
|
|
finally (return
|
|
(let ((rest (- 100 total)))
|
|
(return (append list
|
|
`((nil "Elsewhere" ,rest nil nil)))))))))
|
|
|
|
(defslimefun swank-sprof-get-call-graph (&key exclude-swank)
|
|
(when (setf *call-graph* (sb-sprof:report :type nil))
|
|
(serialize-call-graph :exclude-swank exclude-swank)))
|
|
|
|
(defslimefun swank-sprof-expand-node (index)
|
|
(let* ((node (gethash index *number-nodes*)))
|
|
(labels ((caller-count (v)
|
|
(loop for e in (sb-sprof::vertex-edges v) do
|
|
(when (eq (sb-sprof::edge-vertex e) node)
|
|
(return-from caller-count (sb-sprof::call-count e))))
|
|
0)
|
|
(serialize-node (node count)
|
|
(etypecase node
|
|
(sb-sprof::cycle
|
|
(list (sb-sprof::cycle-index node)
|
|
(sb-sprof::cycle-name node)
|
|
(samples-percent count)))
|
|
(sb-sprof::node
|
|
(let ((name (node-values node)))
|
|
(list (gethash node *node-numbers*)
|
|
name
|
|
(samples-percent count)))))))
|
|
(list :callers (loop for node in
|
|
(sort (copy-list (sb-sprof::node-callers node)) #'>
|
|
:key #'caller-count)
|
|
collect (serialize-node node
|
|
(caller-count node)))
|
|
:calls (let ((edges (sort (copy-list (sb-sprof::vertex-edges node))
|
|
#'>
|
|
:key #'sb-sprof::call-count)))
|
|
(loop for edge in edges
|
|
collect
|
|
(serialize-node (sb-sprof::edge-vertex edge)
|
|
(sb-sprof::call-count edge))))))))
|
|
|
|
(defslimefun swank-sprof-disassemble (index)
|
|
(let* ((node (gethash index *number-nodes*))
|
|
(debug-info (sb-sprof::node-debug-info node)))
|
|
(with-output-to-string (s)
|
|
(typecase debug-info
|
|
(sb-impl::code-component
|
|
(sb-disassem::disassemble-memory (sb-vm::code-instructions debug-info)
|
|
(sb-vm::%code-code-size debug-info)
|
|
:stream s))
|
|
(sb-di::compiled-debug-fun
|
|
(let ((component (sb-di::compiled-debug-fun-component debug-info)))
|
|
(sb-disassem::disassemble-code-component component :stream s)))
|
|
(t `(:error "No disassembly available"))))))
|
|
|
|
(defslimefun swank-sprof-source-location (index)
|
|
(let* ((node (gethash index *number-nodes*))
|
|
(debug-info (sb-sprof::node-debug-info node)))
|
|
(or (when (typep debug-info 'sb-di::compiled-debug-fun)
|
|
(let* ((component (sb-di::compiled-debug-fun-component debug-info))
|
|
(function (sb-kernel::%code-entry-points component)))
|
|
(when function
|
|
(find-source-location function))))
|
|
`(:error "No source location available"))))
|
|
|
|
(defslimefun swank-sprof-start (&key (mode :cpu))
|
|
(sb-sprof:start-profiling :mode mode))
|
|
|
|
(defslimefun swank-sprof-stop ()
|
|
(sb-sprof:stop-profiling))
|
|
|
|
)
|
|
|
|
(provide :swank-sprof)
|