;;; 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)