sly-profiler.el (5482B)
1 ;;; -*- coding: utf-8; lexical-binding: t -*- 2 ;;; 3 ;;; sly-profiler.el -- a navigable dialog of inspectable timing entries 4 ;;; 5 (eval-and-compile 6 (require 'sly) 7 (require 'sly-parse "lib/sly-parse")) 8 9 (define-sly-contrib sly-profiler 10 "Provide an interfactive timing dialog buffer for managing and 11 inspecting details of timing functions. Invoke this dialog with C-c Y." 12 (:authors "João Távora <joaotavora@gmail.com>") 13 (:license "GPL") 14 (:slynk-dependencies slynk/profiler) 15 (:on-load (add-hook 'sly-mode-hook 'sly-profiler-enable)) 16 (:on-unload (remove-hook 'sly-mode-hook 'sly-profiler-enable))) 17 18 19 ;;;; Modes and mode maps 20 ;;; 21 (defvar sly-profiler-mode-map 22 (let ((map (make-sparse-keymap))) 23 (define-key map (kbd "G") 'sly-profiler-fetch-timings) 24 (define-key map (kbd "C-k") 'sly-profiler-clear-fetched-timings) 25 (define-key map (kbd "g") 'sly-profiler-fetch-status) 26 (define-key map (kbd "q") 'quit-window) 27 map)) 28 29 (define-derived-mode sly-profiler-mode fundamental-mode 30 "SLY Timing Dialog" "Mode for controlling SLY's Timing Dialog" 31 (set-syntax-table lisp-mode-syntax-table) 32 (read-only-mode 1)) 33 34 (defvar sly-profiler-shortcut-mode-map 35 (let ((map (make-sparse-keymap))) 36 (define-key map (kbd "C-c Y") 'sly-profiler) 37 (define-key map (kbd "C-c C-y") 'sly-profiler-toggle-timing) 38 map)) 39 40 (define-minor-mode sly-profiler-shortcut-mode 41 "Add keybindings for accessing SLY's Profiler.") 42 43 (defun sly-profiler-enable () (sly-profiler-shortcut-mode 1)) 44 45 46 ;;;; Helpers 47 ;;; 48 (defun sly-profiler--get-buffer () 49 (let* ((name (format "*profiler for %s*" 50 (sly-connection-name sly-default-connection))) 51 (existing (get-buffer name))) 52 (cond ((and existing 53 (buffer-live-p existing) 54 (with-current-buffer existing 55 (memq sly-buffer-connection sly-net-processes))) 56 existing) 57 (t 58 (if existing (kill-buffer existing)) 59 (with-current-buffer (get-buffer-create name) 60 (sly-profiler-mode) 61 (setq sly-buffer-connection sly-default-connection) 62 (pop-to-buffer (current-buffer))))))) 63 64 (defun sly-profiler--clear-local-tree () 65 (erase-buffer) 66 (insert "Cleared timings!")) 67 68 (defun sly-profiler--render-timings (timing-specs) 69 (let ((inhibit-read-only t)) 70 (erase-buffer) 71 (let ((standard-output (current-buffer))) 72 (cl-loop for spec in timing-specs 73 do (princ spec) (terpri))))) 74 75 ;;;; Interactive functions 76 ;;; 77 ;; (defun sly-profiler-fetch-specs () 78 ;; "Refresh just list of timing specs." 79 ;; (interactive) 80 ;; (sly-eval-async `(slynk-profiler:report-specs) 81 ;; #'sly-profiler--open-specs)) 82 83 (defun sly-profiler-clear-fetched-timings (&optional interactive) 84 "Clear local and remote timings collected so far" 85 (interactive "p") 86 (when (or (not interactive) 87 (y-or-n-p "Clear all collected and fetched timings?")) 88 (sly-eval-async 89 '(slynk-profiler:clear-timing-tree) 90 #'sly-profiler--clear-local-tree))) 91 92 (defun sly-profiler-fetch-timings () 93 (interactive) 94 (sly-eval-async `(slynk-profiler:report-latest-timings) 95 #'sly-profiler--render-timings)) 96 97 (defun sly-profiler-fetch-status () 98 (interactive) 99 (sly-profiler-fetch-timings)) 100 101 (defun sly-profiler-toggle-timing (&optional using-context-p) 102 "Toggle the dialog-timing of the spec at point. 103 104 When USING-CONTEXT-P, attempt to decipher lambdas. methods and 105 other complicated function specs." 106 (interactive "P") 107 ;; Notice the use of "spec strings" here as opposed to the 108 ;; proper cons specs we use on the slynk side. 109 ;; 110 ;; Notice the conditional use of `sly-trace-query' found in 111 ;; slynk-fancy-trace.el 112 ;; 113 (let* ((spec-string (if using-context-p 114 (sly-extract-context) 115 (sly-symbol-at-point))) 116 (spec-string (read-from-minibuffer "(Un)time: " (format "%s" spec-string)))) 117 (message "%s" (sly-eval `(slynk-profiler:toggle-timing 118 (slynk::from-string ,spec-string)))))) 119 120 (defun sly-profiler (&optional refresh) 121 "Show timing dialog and refresh timing collection status. 122 123 With optional CLEAR-AND-FETCH prefix arg, clear the current tree 124 and fetch a first batch of timings." 125 (interactive "P") 126 (sly-with-popup-buffer ((sly-buffer-name :profiler :connection sly-default-connection) 127 :mode 'sly-profiler-mode 128 :select t) 129 (when refresh (sly-profiler-fetch-timings)))) 130 131 132 ;;;; Menu 133 ;;; 134 (easy-menu-define sly-profiler--shortcut-menu nil 135 "Menu setting traces from anywhere in SLY." 136 (let* ((in-dialog '(eq major-mode 'sly-profiler-mode)) 137 (_dialog-live `(and ,in-dialog 138 (memq sly-buffer-connection sly-net-processes))) 139 (connected '(sly-connected-p))) 140 `("Profiling" 141 ["(Un)Profile definition" sly-profiler-toggle-timing ,connected] 142 ["Open Profiler Dialog" sly-profiler (and ,connected (not ,in-dialog))]))) 143 144 (easy-menu-add-item sly-menu nil sly-profiler--shortcut-menu "Documentation") 145 146 (defvar sly-profiler--easy-menu 147 (let ((condition '(memq sly-buffer-connection sly-net-processes))) 148 `("Timing" 149 [ "Clear fetched timings" sly-profiler-clear-fetched-timings ,condition] 150 [ "Fetch timings" sly-profiler-fetch-timings ,condition]))) 151 152 (easy-menu-define my-menu sly-profiler-mode-map "Timing" 153 sly-profiler--easy-menu) 154 155 (provide 'sly-profiler)