├── LICENSE ├── etrace.el └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Jane Street Group, LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /etrace.el: -------------------------------------------------------------------------------- 1 | ;;; etrace.el --- Emacs Lisp Tracer -*- lexical-binding: t -*- 2 | ;; Released under the MIT license, Copyright Jane Street Group, LLC 3 | ;; This module modifies the instrumentation profiler included with 4 | ;; Emacs called "elp" to also record trace events for the beginning 5 | ;; and end of function calls, and provides a function to write out 6 | ;; those events in Chromium JSON trace format. 7 | ;; 8 | ;; First use elp commands to instrument the functions you want, then 9 | ;; do the thing you want to trace, then M-x etrace-write RET to write 10 | ;; out a trace to the configurable etrace-output-file. You can now 11 | ;; open chrome://tracing and load the resulting trace file to view it. 12 | 13 | (require 'elp) 14 | 15 | (defcustom etrace-output-file "~/etrace.json" 16 | "When calling etrace-write, write the trace to this file." 17 | :type 'file) 18 | 19 | (defvar etrace--trace nil "Trace events") 20 | 21 | (defun etrace--make-wrapper-advice (orig funsym) 22 | "Advice to make the piece of advice that instruments FUNSYM." 23 | (let ((elp-wrapper (funcall orig funsym))) 24 | (lambda (func &rest args) 25 | "This function has been instrumented for profiling by the ELP. 26 | ELP is the Emacs Lisp Profiler. To restore the function to its 27 | original definition, use \\[elp-restore-function] or \\[elp-restore-all]." 28 | (let ((result)) 29 | (push (list ?B funsym (current-time)) etrace--trace) 30 | (unwind-protect 31 | (setq result (apply elp-wrapper func args)) 32 | (push (list ?E funsym (current-time)) etrace--trace)) 33 | result)))) 34 | 35 | (advice-add #'elp--make-wrapper :around #'etrace--make-wrapper-advice) 36 | 37 | (defun etrace-clear () 38 | "Clear the etrace buffer" 39 | (interactive) 40 | (setq etrace--trace nil)) 41 | 42 | (defun etrace-write () 43 | "Write out trace to etrace-output-file then clear the current trace variable" 44 | (interactive) 45 | (save-window-excursion 46 | (save-excursion 47 | (find-file-literally etrace-output-file) 48 | (erase-buffer) 49 | (insert "[") 50 | (let* ((first-el t) 51 | (trace (reverse etrace--trace)) 52 | (start-time (if etrace--trace (float-time (nth 2 (car trace))) nil))) 53 | (dolist (ev trace) 54 | (if first-el 55 | (setq first-el nil) 56 | (insert ",")) 57 | ;; Intentionally avoid using a proper JSON formatting library, traces can be 58 | ;; multiple megabytes and writing them this way is probably faster and produces 59 | ;; compact JSON but without everything being on one line. 60 | (insert 61 | (format 62 | "{\"name\":\"%s\",\"cat\":\"\",\"ph\":\"%c\",\"ts\":%d,\"pid\":0,\"tid\":0,\"args\":{}}\n" 63 | (nth 1 ev) (nth 0 ev) (truncate (* 1000000 (- (float-time (nth 2 ev)) start-time)))))) 64 | (insert "]") 65 | (save-buffer)))) 66 | (message "Wrote trace to etrace-output-file (%s)!" etrace-output-file) 67 | (etrace-clear)) 68 | 69 | (provide 'etrace) 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `etrace.el` - Performance Tracing For Emacs Lisp 2 | 3 | This package for GNU Emacs allows latency tracing to be performed on 4 | Emacs Lisp code and the results output to files using the Chromium 5 | Catapult Trace Event Format. These trace files can then be loaded 6 | into trace analysis utilities in order to generate *flame graphs* and 7 | other useful visualisations and analyses. For example, here's a flame 8 | graph for (a modified version of) the famous `org-agenda` function: 9 | 10 | ![image](https://user-images.githubusercontent.com/100738/96385726-9e5eb580-118d-11eb-8ded-569cf8faf9b4.png) 11 | 12 | This package is built on top of `elp.el`, which is one of two elisp 13 | profiler's built into emacs. (The other is `profiler.el`; they are 14 | both mentioned in [the Profiling section of the Elisp 15 | manual](https://www.gnu.org/software/emacs/manual/html_node/elisp/Profiling.html).) 16 | It records a separate trace using advice on the normal `elp.el` 17 | recording, since `elp.el` doesn't capture enough info itself. 18 | 19 | ## Usage 20 | 21 | ### Generating a trace file 22 | 23 | 1. Either `M-: (require 'etrace)` or add `(require 'etrace)` to your 24 | Emacs config, or enable the micro-feature for it if you use 25 | micro-features from the non-Spacemacs Emacs config. 26 | 27 | 2. (Optional) Run `M-x customize-variable etrace-output-file` to 28 | change where the trace will be written. It defaults to `~/etrace.json`. 29 | 30 | 3. Run `M-x elp-instrument-package` and type in a function prefix to 31 | instrument all the functions with that prefix. It uses a 32 | completion box so note that by default it will complete to 33 | whatever's selected in there. If you want to complete to a prefix 34 | with no corresponding function, you may be able to press the Up 35 | arrow until the text you typed is selected rather than any 36 | completion, although this depends on which completion interface 37 | you have configured. 38 | 39 | There's also `M-x elp-instrument-function` for individual 40 | functions. 41 | 42 | 4. Run `M-x etrace-clear`. 43 | 44 | 5. Execute the code which you want to analyse. 45 | 46 | 6. Run `M-x etrace-write` to write out the trace file. 47 | 48 | ### Analysing the trace file 49 | 50 | There are several tools which can analyse the resulting trace file. 51 | Here are some suggestions, but if you know of other good alternatives, 52 | please submit them to this repository. 53 | 54 | * https://www.speedscope.app/ 55 | 56 | This is an excellent Open Source interactive web-based flamegraph 57 | visualizer, which has 3 visualisation modes, keyboard shortcuts, 58 | lots of nice features, and good documentation. Simply upload your 59 | trace file and then start analysing. 60 | 61 | * https://ui.perfetto.dev/ 62 | 63 | This tool can show you the total time and percentage of time taken 64 | by different functions in a selection range. 65 | 66 | * [chrome://tracing](chrome://tracing) 67 | 68 | If you are using Chromium, Google Chrome, or a similar derivative, 69 | open this built-in tracing tool, and click the load button to open 70 | the trace file. Use Alt+scroll to zoom. 71 | 72 | If you successfully identify performance bottlenecks and fix them then 73 | repeat from step 4, or from step 3 if any code needs to be 74 | re-instrumented. 75 | 76 | Finally, you can run `M-x elp-restore-all` to un-instrument any 77 | instrumented functions. 78 | 79 | ## History and acknowledgements 80 | 81 | This package was originally written by [Tristan 82 | Hume](https://github.com/trishume) and shared as [a GitHub 83 | gist](https://gist.github.com/trishume/40bf7045a23412d4c016f5e8533437db). 84 | However he didn't have plans to develop it further, so he generously 85 | [handed over 86 | maintenance](https://gist.github.com/trishume/40bf7045a23412d4c016f5e8533437db#gistcomment-3495349). 87 | --------------------------------------------------------------------------------