|
|
- ;;; cider-profile.el --- CIDER support for profiling -*- lexical-binding: t; -*-
-
- ;; Copyright © 2014-2019 Edwin Watkeys and CIDER contributors
-
- ;; Author: Edwin Watkeys <edw@poseur.com>
- ;; Juan E. Maya <jmayaalv@gmail.com>
-
- ;; This program is free software; you can redistribute it and/or modify
- ;; it under the terms of the GNU General Public License as published by
- ;; the Free Software Foundation, either version 3 of the License, or
- ;; (at your option) any later version.
-
- ;; This program 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 General Public License for more details.
-
- ;; You should have received a copy of the GNU General Public License
- ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- ;;; Commentary:
-
- ;; Provides coarse-grained interactive profiling support.
- ;; Based on earlier work by Edwin Watkeys (https://github.com/thunknyc/nrepl-profile).
-
- ;;; Code:
-
- (require 'cider-client)
- (require 'cider-popup)
- (require 'cider-eval)
-
- (defconst cider-profile-buffer "*cider-profile*")
-
- (defvar cider-profile-map
- (let ((map (define-prefix-command 'cider-profile-map)))
- (define-key map (kbd "t") #'cider-profile-toggle)
- (define-key map (kbd "c") #'cider-profile-clear)
- (define-key map (kbd "S") #'cider-profile-summary)
- (define-key map (kbd "s") #'cider-profile-var-summary)
- (define-key map (kbd "n") #'cider-profile-ns-toggle)
- (define-key map (kbd "v") #'cider-profile-var-profiled-p)
- (define-key map (kbd "+") #'cider-profile-samples)
- map)
- "CIDER profiler keymap.")
-
- (defconst cider-profile-menu
- '("Profile"
- ["Toggle var profiling" cider-profile-toggle]
- ["Toggle namespace profiling" cider-profile-ns-toggle]
- "--"
- ["Display var profiling status" cider-profile-var-profiled-p]
- ["Display max sample count" cider-profile-samples]
- ["Display summary" cider-profile-summary]
- ["Clear data" cider-profile-clear])
- "CIDER profiling submenu.")
-
- (defun cider-profile--make-response-handler (handler &optional buffer)
- "Make a response handler using value handler HANDLER for connection BUFFER.
-
- Optional argument BUFFER defaults to current buffer."
- (nrepl-make-response-handler
- (or buffer (current-buffer)) handler nil nil nil))
-
- ;;;###autoload
- (defun cider-profile-samples (&optional query)
- "Displays current max-sample-count.
- If optional QUERY is specified, set max-sample-count and display new value."
- (interactive "P")
- (cider-ensure-op-supported "set-max-samples")
- (cider-ensure-op-supported "get-max-samples")
- (if (not (null query))
- (cider-nrepl-send-request
- (let ((max-samples (if (numberp query) query '())))
- (message "query: %s" max-samples)
- `("op" "set-max-samples" "max-samples" ,max-samples))
- (cider-profile--make-response-handler
- (lambda (_buffer value)
- (let ((value (if (zerop (length value)) "unlimited" value)))
- (message "max-sample-count is now %s" value)))))
- (cider-nrepl-send-request
- '("op" "get-max-samples")
- (cider-profile--make-response-handler
- (lambda (_buffer value)
- (let ((value (if (zerop (length value)) "unlimited" value)))
- (message "max-sample-count is now %s" value))))))
- query)
-
- ;;;###autoload
- (defun cider-profile-var-profiled-p (query)
- "Displays the profiling status of var under point.
- Prompts for var if none under point or QUERY is present."
- (interactive "P")
- (cider-ensure-op-supported "is-var-profiled")
- (cider-read-symbol-name
- "Report profiling status for var: "
- (lambda (sym)
- (let ((ns (cider-current-ns)))
- (cider-nrepl-send-request
- `("op" "is-var-profiled"
- "ns" ,ns
- "sym" ,sym)
- (cider-profile--make-response-handler
- (lambda (_buffer value)
- (pcase value
- ("profiled" (message "Profiling is currently enabled for %s/%s" ns sym))
- ("unprofiled" (message "Profiling is currently disabled for %s/%s" ns sym))
- ("unbound" (message "%s/%s is unbound" ns sym)))))))))
- query)
-
- ;;;###autoload
- (defun cider-profile-ns-toggle (&optional query)
- "Toggle profiling for the ns associated with optional QUERY.
-
- If optional argument QUERY is non-nil, prompt for ns. Otherwise use
- current ns."
- (interactive "P")
- (cider-ensure-op-supported "toggle-profile-ns")
- (let ((ns (if query
- (completing-read "Toggle profiling for ns: "
- (cider-sync-request:ns-list))
- (cider-current-ns))))
- (cider-nrepl-send-request
- `("op" "toggle-profile-ns"
- "ns" ,ns)
- (cider-profile--make-response-handler
- (lambda (_buffer value)
- (pcase value
- ("profiled" (message "Profiling enabled for %s" ns))
- ("unprofiled" (message "Profiling disabled for %s" ns)))))))
- query)
-
- ;;;###autoload
- (defun cider-profile-toggle (query)
- "Toggle profiling for the given QUERY.
- Defaults to the symbol at point.
- With prefix arg or no symbol at point, prompts for a var."
- (interactive "P")
- (cider-ensure-op-supported "toggle-profile")
- (cider-read-symbol-name
- "Toggle profiling for var: "
- (lambda (sym)
- (let ((ns (cider-current-ns)))
- (cider-nrepl-send-request
- `("op" "toggle-profile"
- "ns" ,ns
- "sym" ,sym)
- (cider-profile--make-response-handler
- (lambda (_buffer value)
- (pcase value
- ("profiled" (message "Profiling enabled for %s/%s" ns sym))
- ("unprofiled" (message "Profiling disabled for %s/%s" ns sym))
- ("unbound" (message "%s/%s is unbound" ns sym)))))))))
- query)
-
- (defun cider-profile-display-stats (stats-response)
- "Displays the STATS-RESPONSE on `cider-profile-buffer`."
- (let ((table (nrepl-dict-get stats-response "err")))
- (if cider-profile-buffer
- (let ((buffer (cider-make-popup-buffer cider-profile-buffer)))
- (with-current-buffer buffer
- (let ((inhibit-read-only t)) (insert table)))
- (display-buffer buffer)
- (let ((window (get-buffer-window buffer)))
- (set-window-point window 0)
- (select-window window)
- (fit-window-to-buffer window)))
- (cider-emit-interactive-eval-err-output table))))
-
- ;;;###autoload
- (defun cider-profile-summary ()
- "Display a summary of currently collected profile data."
- (interactive)
- (cider-ensure-op-supported "profile-summary")
- (cider-profile-display-stats
- (cider-nrepl-send-sync-request '("op" "profile-summary"))))
-
- ;;;###autoload
- (defun cider-profile-var-summary (query)
- "Display profile data for var under point QUERY.
- Defaults to the symbol at point. With prefix arg or no symbol at point,
- prompts for a var."
- (interactive "P")
- (cider-ensure-op-supported "profile-var-summary")
- (cider-read-symbol-name
- "Profile-summary for var: "
- (lambda (sym)
- (cider-profile-display-stats
- (cider-nrepl-send-sync-request
- `("op" "profile-var-summary"
- "ns" ,(cider-current-ns)
- "sym" ,sym)))))
- query)
-
- ;;;###autoload
- (defun cider-profile-clear ()
- "Clear any collected profile data."
- (interactive)
- (cider-ensure-op-supported "clear-profile")
- (cider-nrepl-send-request
- '("op" "clear-profile")
- (cider-profile--make-response-handler
- (lambda (_buffer value)
- (when (equal value "cleared")
- (message "Cleared profile data"))))))
-
- (provide 'cider-profile)
-
- ;;; cider-profile.el ends here
|