]>
Commit | Line | Data |
---|---|---|
1 | ;;; elpy-profile.el --- Profiling capabilitiss for elpy | |
2 | ||
3 | ;; Copyright (C) 2013-2019 Jorgen Schaefer | |
4 | ||
5 | ;; Author: Gaby Launay <gaby.launay@tutanota.com> | |
6 | ;; URL: https://github.com/jorgenschaefer/elpy | |
7 | ||
8 | ;; This program is free software; you can redistribute it and/or | |
9 | ;; modify it under the terms of the GNU General Public License | |
10 | ;; as published by the Free Software Foundation; either version 3 | |
11 | ;; of the License, or (at your option) any later version. | |
12 | ||
13 | ;; This program is distributed in the hope that it will be useful, | |
14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | ;; GNU General Public License for more details. | |
17 | ||
18 | ;; You should have received a copy of the GNU General Public License | |
19 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | ||
21 | ;;; Commentary: | |
22 | ||
23 | ;; This file serves as an extension to elpy by adding profiling capabilities | |
24 | ||
25 | ;;; Code: | |
26 | ||
27 | ||
28 | ;;;;;;;;;;;;;;;;;;;;;; | |
29 | ;;; User customization | |
30 | ||
31 | (defcustom elpy-profile-visualizer "snakeviz" | |
32 | "Visualizer for elpy profile results." | |
33 | :type '(choice (const :tag "Snakeviz" "snakeviz") | |
34 | (const :tag "RunSnakeRun" "runsnake") | |
35 | (const :tag "pyprof2calltree" "pyprof2calltree -k -i") | |
36 | (string :tag "Other")) | |
37 | :group 'elpy) | |
38 | ||
39 | ;;;;;;;;;;;;;;;;;;;;;; | |
40 | ;;; Helper Functions | |
41 | ||
42 | (defun elpy-profile--display-profiling (file) | |
43 | "Display the profile result FILE using `elpy-profile-visualizer'." | |
44 | (let ((exec (car (split-string elpy-profile-visualizer " " t))) | |
45 | (args (append (cdr (split-string elpy-profile-visualizer " " t)) (list file)))) | |
46 | (if (executable-find exec) | |
47 | (apply 'call-process exec nil 0 nil args) | |
48 | (message "Elpy profile visualizer '%s' not found" exec)))) | |
49 | ||
50 | (defun elpy-profile--sentinel (process string) | |
51 | "Elpy profile sentinel." | |
52 | (let ((filename (file-name-nondirectory (process-get process 'file))) | |
53 | (prof-file (process-get process 'prof-file)) | |
54 | (dont-display (process-get process 'dont-display))) | |
55 | (with-current-buffer "*elpy-profile-log*" | |
56 | (view-mode)) | |
57 | (if (not (string-equal string "finished\n")) | |
58 | (progn | |
59 | (message "[%s] Profiling failed" filename) | |
60 | (display-buffer "*elpy-profile-log*")) | |
61 | (message "[%s] Profiling succeeded" filename) | |
62 | (unless dont-display | |
63 | (elpy-profile--display-profiling prof-file))))) | |
64 | ||
65 | (defun elpy-profile--file (file &optional in-dir dont-display) | |
66 | "Profile asynchronously FILE and display the result using | |
67 | `elpy-profile-visualizer'. | |
68 | ||
69 | If IN-DIR is non nil, profile result is saved in the same | |
70 | directory as the script. | |
71 | If DONT-DISPLAY is non nil, don't display the profile results." | |
72 | (ignore-errors (kill-buffer "*elpy-profile-log*")) | |
73 | (let* ((prof-file (if in-dir | |
74 | (concat (file-name-sans-extension file) ".profile") | |
75 | (concat (make-temp-file "elpy-profile-" nil ".profile")))) | |
76 | (proc-name (format "elpy-profile-%s" file)) | |
77 | (proc-cmd (list elpy-rpc-python-command "-m" "cProfile" "-o" prof-file file)) | |
78 | (proc (make-process :name proc-name | |
79 | :buffer "*elpy-profile-log*" | |
80 | :sentinel 'elpy-profile--sentinel | |
81 | :command proc-cmd))) | |
82 | (message "[%s] Profiling ..." (file-name-nondirectory file)) | |
83 | (process-put proc 'prof-file prof-file) | |
84 | (process-put proc 'file file) | |
85 | (process-put proc 'dont-display dont-display) | |
86 | prof-file)) | |
87 | ||
88 | ;;;;;;;;;;;;;;;;;;;;;; | |
89 | ;;; User Functions | |
90 | ||
91 | (defun elpy-profile-buffer-or-region (&optional in-dir dont-display) | |
92 | "Profile asynchronously the active region or the current buffer | |
93 | and display the result using `elpy-profile-visualizer'. | |
94 | ||
95 | If IN-DIR is non nil, profile result is saved in the same | |
96 | directory as the script. | |
97 | If DONT-DISPLAY is non nil, don't display the profile results." | |
98 | (interactive "P") | |
99 | (let* ((file-name (buffer-name)) | |
100 | (file-dir (file-name-directory (buffer-file-name))) | |
101 | (beg (if (region-active-p) (region-beginning) (point-min))) | |
102 | (end (if (region-active-p) (region-end) (point-max))) | |
103 | (tmp-file-prefix (if (region-active-p) "_region_" "")) | |
104 | (tmp-file (if in-dir | |
105 | (concat file-dir "/" tmp-file-prefix file-name) | |
106 | (concat (make-temp-file "elpy-profile-" t) "/" tmp-file-prefix file-name))) | |
107 | (region (python-shell-buffer-substring beg end))) | |
108 | (with-temp-buffer | |
109 | (insert region) | |
110 | (write-region (point-min) (point-max) tmp-file nil t)) | |
111 | (elpy-profile--file tmp-file t dont-display))) | |
112 | ||
113 | (provide 'elpy-profile) | |
114 | ;;; elpy-profile.el ends here |