* bug#27397: [PATCH] New commands for bulk tracing of elisp functions @ 2017-06-16 13:32 Phil Sainty 2017-06-16 14:58 ` Dmitry Gutov ` (2 more replies) 0 siblings, 3 replies; 40+ messages in thread From: Phil Sainty @ 2017-06-16 13:32 UTC (permalink / raw) To: 27397 [-- Attachment #1: Type: text/plain, Size: 1476 bytes --] trace.el currently only has commands for adding a trace to a single function, and for untracing either a single function or ALL traced functions. This patch adds commands for tracing and untracing functions in bulk, either by function name prefix (`trace-package' and `untrace-package') or by regexp (`trace-regexp' and `untrace-regexp'). The use of the term "package" for the prefix-based commands was chosen for consistency with `elp-instrument-package', from which this code is partly derived, and from which I have also maintained a warning and basic protection against attempts to trace too many functions at once (which can indeed render Emacs unusable). It it still possible to do this, but I feel the checks which are currently in place are probably sufficient to protect against accidents. `trace-is-traceable-p' is using Stefan's suggestion in his comment on my initial implementation at https://stackoverflow.com/a/44241058 and I have changed the completing-read filter predicate in `trace--read-args' to also use this (instead of `fboundp'), on the assumption that this makes more sense. (I note that the `trace-is-traced' function does not follow the usual naming convention for predicates. Should this be renamed to `trace-is-traced-p' ?) This patch also removes some outdated docstring comments for `untrace-function' which were clearly remnants of the earlier implementation using advice.el (whereas the current library is based on nadvice.el). -Phil [-- Attachment #2: 0001-New-commands-for-bulk-tracing-of-elisp-functions.patch --] [-- Type: text/x-patch, Size: 5559 bytes --] From c7262056db33722bc84dd94fbadaa71ed8fbf5fc Mon Sep 17 00:00:00 2001 From: Phil Sainty <psainty@orcon.net.nz> Date: Sun, 11 Jun 2017 17:29:53 +1200 Subject: [PATCH] New commands for bulk tracing of elisp functions * lisp/emacs-lisp/trace.el (trace-package, untrace-package) (trace-regexp, untrace-regexp, trace-is-traceable-p): New functions. (trace--read-args): Use new trace-is-traceable-p predicate. (trace-is-traced, untrace-function, untrace-all): Doc updates/fixes. --- lisp/emacs-lisp/trace.el | 98 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 6 deletions(-) diff --git a/lisp/emacs-lisp/trace.el b/lisp/emacs-lisp/trace.el index 1c57d73..5dba151 100644 --- a/lisp/emacs-lisp/trace.el +++ b/lisp/emacs-lisp/trace.el @@ -257,7 +257,12 @@ trace-function-internal (or context (lambda () ""))) `((name . ,trace-advice-name) (depth . -100)))) +(defun trace-is-traceable-p (sym) + "Whether the given symbol is a traceable function." + (or (functionp sym) (macrop sym))) + (defun trace-is-traced (function) + "Whether FUNCTION is currently traced." (advice-member-p trace-advice-name function)) (defun trace--read-args (prompt) @@ -274,7 +279,7 @@ trace--read-args default (if beg (substring prompt beg) ": ")) prompt) - obarray 'fboundp t nil nil + obarray 'trace-is-traceable-p t nil nil (if default (symbol-name default))))) (when current-prefix-arg (list @@ -321,17 +326,98 @@ trace-function-background (defalias 'trace-function 'trace-function-foreground) (defun untrace-function (function) - "Untraces FUNCTION and possibly activates all remaining advice. -Activation is performed with `ad-update', hence remaining advice will get -activated only if the advice of FUNCTION is currently active. If FUNCTION -was not traced this is a noop." + "Remove trace from FUNCTION. If FUNCTION was not traced this is a noop." (interactive (list (intern (completing-read "Untrace function: " obarray #'trace-is-traced t)))) (advice-remove function trace-advice-name)) +;;;###autoload +(defun trace-package (prefix) + "Trace all functions with names starting with PREFIX. +For example, to trace all diff functions, do the following: + +\\[trace-package] RET diff- RET + +Background tracing is used. Switch to the trace output buffer to +view the results. + +See also `untrace-package'." + (interactive ;; derived from `elp-instrument-package'. + (list (completing-read "Prefix of package to trace: " + obarray #'trace-is-traceable-p))) + (when (zerop (length prefix)) + (error "Tracing all Emacs functions would render Emacs unusable")) + (mapc (lambda (name) + (trace-function-background (intern name))) + (all-completions prefix obarray #'trace-is-traceable-p)) + (message + "Tracing to %s. Use %s to untrace a package, or %s to remove all traces." + trace-buffer + (substitute-command-keys "\\[untrace-package]") + (substitute-command-keys "\\[untrace-all]"))) + +(defun untrace-package (prefix) + "Remove traces from all functions with names starting with PREFIX. + +See also `trace-package'." + (interactive + (list (completing-read "Prefix of package to untrace: " + obarray #'trace-is-traced))) + (if (and (zerop (length prefix)) + (y-or-n-p "Remove all function traces?")) + (untrace-all) + (mapc (lambda (name) + (untrace-function (intern name))) + (all-completions prefix obarray #'trace-is-traceable-p)))) + +;;;###autoload +(defun trace-regexp (regexp) + "Trace all functions with names matching REGEXP. +For example, to trace indentation-related functions, you could try: + +\\[trace-regexp] RET indent\\|offset RET + +Warning: Do not attempt to trace all functions. Tracing too many +functions at one time will render Emacs unusable. + +Background tracing is used. Switch to the trace output buffer to +view the results. + +See also `untrace-regexp'." + (interactive + (list (read-regexp "Regexp matching functions to trace: "))) + (when (member regexp '("" "." ".+" ".*")) + ;; Not comprehensive, but it catches the most likely attempts. + (error "Tracing all Emacs functions would render Emacs unusable")) + (mapatoms + (lambda (sym) + (and (trace-is-traceable-p sym) + (string-match-p regexp (symbol-name sym)) + (trace-function-background sym)))) + (message + "Tracing to %s. Use %s to untrace by regexp, or %s to remove all traces." + trace-buffer + (substitute-command-keys "\\[untrace-regexp]") + (substitute-command-keys "\\[untrace-all]"))) + +(defun untrace-regexp (regexp) + "Remove traces from all functions with names matching REGEXP. + +See also `trace-regexp'." + (interactive + (list (read-regexp "Regexp matching functions to untrace: "))) + (if (and (zerop (length regexp)) + (y-or-n-p "Remove all function traces?")) + (untrace-all) + (mapatoms + (lambda (sym) + (and (trace-is-traceable-p sym) + (string-match-p regexp (symbol-name sym)) + (untrace-function sym)))))) + (defun untrace-all () - "Untraces all currently traced functions." + "Remove traces from all currently traced functions." (interactive) (mapatoms #'untrace-function)) -- 2.8.3 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-16 13:32 bug#27397: [PATCH] New commands for bulk tracing of elisp functions Phil Sainty @ 2017-06-16 14:58 ` Dmitry Gutov 2017-06-17 8:43 ` Phil Sainty 2017-06-16 15:43 ` Kaushal Modi 2017-06-17 9:20 ` Phil Sainty 2 siblings, 1 reply; 40+ messages in thread From: Dmitry Gutov @ 2017-06-16 14:58 UTC (permalink / raw) To: Phil Sainty, 27397 On 6/16/17 4:32 PM, Phil Sainty wrote: > This patch adds commands for tracing and untracing functions in bulk, > either by function name prefix (`trace-package' and `untrace-package') > or by regexp (`trace-regexp' and `untrace-regexp'). Looking good. And I've wanted these commands both times I've had a need to use trace.el. This patch could probably use a NEWS entry, though. > (I note that the `trace-is-traced' function does not follow the usual > naming convention for predicates. Should this be renamed to > `trace-is-traced-p' ?) I'm not 100% sure about the protocol here, but including both "-is-" and "-p" in a function name seems too much. So maybe you should do the opposite and rename trace-is-traceable-p to trace-is-traceable. ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-16 14:58 ` Dmitry Gutov @ 2017-06-17 8:43 ` Phil Sainty 2017-06-17 9:13 ` Dmitry Gutov 2017-06-19 7:45 ` Michael Albinus 0 siblings, 2 replies; 40+ messages in thread From: Phil Sainty @ 2017-06-17 8:43 UTC (permalink / raw) To: Dmitry Gutov, 27397 On 17/06/17 02:58, Dmitry Gutov wrote: > This patch could probably use a NEWS entry, though. Yes, that's a good point. I'll add one to the next revision. Something like this? ** Trace *** New commands 'trace-package' and 'trace-regexp' (and their counterparts 'untrace-package' and 'untrace-regexp') allow for the bulk tracing of calls to functions with names matching a specified prefix or regexp. As there's no info node for trace.el, that could perhaps be a +++ entry; although a grep shows me that "(tramp) Traces and Profiles" could be amended to change: (require 'trace) (dolist (elt (all-completions "tramp-" obarray 'functionp)) (trace-function-background (intern elt))) to: (trace-package "tramp-") It would be good to add a node to the elisp manual covering trace.el, but that's not strictly part of this patch. -Phil ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-17 8:43 ` Phil Sainty @ 2017-06-17 9:13 ` Dmitry Gutov 2017-06-19 7:45 ` Michael Albinus 1 sibling, 0 replies; 40+ messages in thread From: Dmitry Gutov @ 2017-06-17 9:13 UTC (permalink / raw) To: Phil Sainty, 27397 On 6/17/17 11:43 AM, Phil Sainty wrote: > On 17/06/17 02:58, Dmitry Gutov wrote: >> This patch could probably use a NEWS entry, though. > > Yes, that's a good point. I'll add one to the next revision. > Something like this? Looks good to me, both proposed changes. ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-17 8:43 ` Phil Sainty 2017-06-17 9:13 ` Dmitry Gutov @ 2017-06-19 7:45 ` Michael Albinus 2017-06-19 9:35 ` Phil Sainty 1 sibling, 1 reply; 40+ messages in thread From: Michael Albinus @ 2017-06-19 7:45 UTC (permalink / raw) To: Phil Sainty; +Cc: 27397, Dmitry Gutov Phil Sainty <psainty@orcon.net.nz> writes: Hi Phil, > As there's no info node for trace.el, that could perhaps be a +++ > entry; although a grep shows me that "(tramp) Traces and Profiles" > could be amended to change: > > (require 'trace) > (dolist (elt (all-completions "tramp-" obarray 'functionp)) > (trace-function-background (intern elt))) > > to: > > (trace-package "tramp-") There's no package "tramp-". Better would be (trace-regexp "^tramp-") Could be changed in the Tramp manual (modulo backwards compatibility). One problem I'm always faced with Tramp are autoloaded functions. The code as given in the Tramp manual instruments only functions, which are either already loaded, or which are marked as to be autoloaded. Functions from a Tramp package, which are loaded later on, are not handled. Could you add this functionality? > -Phil Best regards, Michael. ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-19 7:45 ` Michael Albinus @ 2017-06-19 9:35 ` Phil Sainty 2017-06-19 9:56 ` Michael Albinus 0 siblings, 1 reply; 40+ messages in thread From: Phil Sainty @ 2017-06-19 9:35 UTC (permalink / raw) To: Michael Albinus; +Cc: 27397, Dmitry Gutov On 19/06/17 19:45, Michael Albinus wrote: >> (trace-package "tramp-") > > There's no package "tramp-". Better would be > > (trace-regexp "^tramp-") To be clear, the two are functionally equivalent; but if you'd prefer it written that way then I'm happy to make that change. > One problem I'm always faced with Tramp are autoloaded functions. > The code as given in the Tramp manual instruments only functions > which are either already loaded, or which are marked as to be > autoloaded. Functions from a Tramp package, which are loaded later > on, are not handled. > > Could you add this functionality? I'm not certain what you're asking here. With respect to the manual entry being discussed here, we could trivially show code to `require' all of the tramp-* libraries prior to calling trace-package or trace-regexp. e.g.: (mapc 'require '(tramp tramp-adb tramp-cache tramp-cmds tramp-compat tramp-ftp tramp-gvfs tramp-sh tramp-smb tramp-uu trampver)) (or else something which automatically locates library names starting with "tramp-" and loads them all). Perhaps you're actually be suggesting some kind of `eval-after-load' tracing behaviour, though? -Phil ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-19 9:35 ` Phil Sainty @ 2017-06-19 9:56 ` Michael Albinus 2017-06-19 11:00 ` Phil Sainty 2017-06-19 11:27 ` Dmitry Gutov 0 siblings, 2 replies; 40+ messages in thread From: Michael Albinus @ 2017-06-19 9:56 UTC (permalink / raw) To: Phil Sainty; +Cc: 27397, Dmitry Gutov Phil Sainty <psainty@orcon.net.nz> writes: Hi Phil, >> There's no package "tramp-". Better would be >> >> (trace-regexp "^tramp-") > > To be clear, the two are functionally equivalent; but if you'd prefer > it written that way then I'm happy to make that change. Ahh, my error. I thought that `trace-package' takes a package name (or symbol), and traces only all functions which have been loaded by this package. (trace-package 'tramp) would then trace the function `tramp-file-name-handler (defined in tramp.el) , but not the function `tramp-sh-file-name-handler'. The latter one is defined in tramp-sh.el. > With respect to the manual entry being discussed here, we could > trivially show code to `require' all of the tramp-* libraries prior > to calling trace-package or trace-regexp. e.g.: > > (mapc 'require '(tramp tramp-adb tramp-cache tramp-cmds > tramp-compat tramp-ftp tramp-gvfs tramp-sh > tramp-smb tramp-uu trampver)) > > (or else something which automatically locates library names starting > with "tramp-" and loads them all). That's not what I want. Often, I hunt bugs related to the order of autoloaded functions, and this order shall be kept also when tracing. > Perhaps you're actually be suggesting some kind of `eval-after-load' > tracing behaviour, though? Yes, that's the idea. If `trace-package' uses as argument a package name as proposed above, the instrumentation shall happen in an `eval-after-load' form for that package. `trace-regexp', on the other hand, shall instrument the functions in a form added to `after-load-functions', additonally to the functions already loaded. > -Phil Best regards, Michael. ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-19 9:56 ` Michael Albinus @ 2017-06-19 11:00 ` Phil Sainty 2017-06-19 12:05 ` Michael Albinus 2017-06-19 11:27 ` Dmitry Gutov 1 sibling, 1 reply; 40+ messages in thread From: Phil Sainty @ 2017-06-19 11:00 UTC (permalink / raw) To: Michael Albinus; +Cc: 27397, Dmitry Gutov On 19/06/17 21:56, Michael Albinus wrote: > Ahh, my error. I thought that `trace-package' takes a package name > (or symbol), and traces only all functions which have been loaded by > this package. I concede that the name may cause some confusion, but I chose it for consistency with `elp-instrument-package' which uses the same function-name-prefix meaning, so the name makes sense from that perspective. Obviously ELP pre-dates package.el. YMMV. Maybe `elp-instrument-package' and `trace-package' should become `elp-instrument-prefix' and `trace-prefix' ? (I'm not against that idea.) >> some kind of `eval-after-load' tracing behaviour > > Yes, that's the idea. If `trace-package' uses as argument a package > name as proposed above, the instrumentation shall happen in an > `eval-after-load' form for that package. I think `trace-library' would be the appropriate name? (If `trace-package' were about ELPA packages, then we have multi-file packages to consider, which expands the scope further.) Of course we can't guarantee that library foo.el adheres to a foo-* naming scheme for all its functions (or that other libraries don't define any foo-* functions). Would we just ignore this and trace everything starting with foo- on the assumption that this is good enough? Or would we parse the library in order to trace that library's functions precisely? > `trace-regexp', on the other hand, shall instrument the functions in > a form added to `after-load-functions', additonally to the functions > already loaded. Which could also apply to (the current meaning of) `trace-package'. I suppose this could potentially be an additional y-or-n-p prompt for the extended interactive argument input (after BUFFER and CONTEXT) when using a prefix arg. I think that the equivalent `untrace-*' commands would need to remove such `after-load-functions' entries by default, but perhaps a prefix argument to those would allow users to choose. I can see potential for users to end up with unwanted remnant entries in after-load-functions (e.g. trace-regexp "A\\|B", then untrace-regexp "A" and "B" separately), so I think there could be some fiddly aspects to all of this; although one could argue that anyone using these features in the first place is likely to know what they're doing, and would be able to cope with such situations easily enough; especially if `untrace-all' takes care of the after-load cases. -Phil ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-19 11:00 ` Phil Sainty @ 2017-06-19 12:05 ` Michael Albinus 2017-06-19 12:17 ` Phil Sainty 2017-06-19 12:50 ` Dmitry Gutov 0 siblings, 2 replies; 40+ messages in thread From: Michael Albinus @ 2017-06-19 12:05 UTC (permalink / raw) To: Phil Sainty; +Cc: 27397, Dmitry Gutov Phil Sainty <psainty@orcon.net.nz> writes: > I think `trace-library' would be the appropriate name? As usual, I don't care too much about function names :-) > Of course we can't guarantee that library foo.el adheres to a foo-* > naming scheme for all its functions (or that other libraries don't > define any foo-* functions). Would we just ignore this and trace > everything starting with foo- on the assumption that this is good > enough? Or would we parse the library in order to trace that library's > functions precisely? We cannot assume that library 'foo prefixes all its functions with "foo-". Even Tramp libraries have functions which do not fit this scheme, for historical reasons. So we shall parse indead every function for the library it belongs to. It shouldn't be too hard, based on `autoloadp', `find-lisp-object-file-name' and friends, see the implementation in `describe-function-1'. Something like this is a very rudimentary implementation: (defun my-find-library (fun) "Find library FUN is defined in. In case of subroutines, return t" (when (fboundp fun) (let ((def (symbol-function fun))) (cond ((autoloadp def) (cadr def)) ((subrp def) t) (t (file-name-base (find-lisp-object-file-name fun nil))))))) > -Phil Best regards, Michael. ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-19 12:05 ` Michael Albinus @ 2017-06-19 12:17 ` Phil Sainty 2017-06-19 12:50 ` Dmitry Gutov 1 sibling, 0 replies; 40+ messages in thread From: Phil Sainty @ 2017-06-19 12:17 UTC (permalink / raw) To: Michael Albinus; +Cc: 27397, Dmitry Gutov On 20/06/17 00:05, Michael Albinus wrote: > So we shall parse indead every function for the library it belongs to. It > shouldn't be too hard, based on `autoloadp', `find-lisp-object-file-name' > and friends, see the implementation in `describe-function-1'. Something > like this is a very rudimentary implementation: > > (defun my-find-library (fun) > "Find library FUN is defined in. > In case of subroutines, return t" > (when (fboundp fun) > (let ((def (symbol-function fun))) > (cond > ((autoloadp def) (cadr def)) > ((subrp def) t) > (t (file-name-base (find-lisp-object-file-name fun nil))))))) Meanwhile I was experimenting with load-history along these lines: (require 'seq) (defun trace-library (library) "Trace functions defined by LIBRARY." (mapcar 'trace-function-background (mapcar 'cdr (seq-filter (lambda (x) (and (consp x) (eq (car x) 'defun))) (load-history-filename-element (load-history-regexp library)))))) ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-19 12:05 ` Michael Albinus 2017-06-19 12:17 ` Phil Sainty @ 2017-06-19 12:50 ` Dmitry Gutov 2017-06-19 13:07 ` Michael Albinus 1 sibling, 1 reply; 40+ messages in thread From: Dmitry Gutov @ 2017-06-19 12:50 UTC (permalink / raw) To: Michael Albinus, Phil Sainty; +Cc: 27397 On 6/19/17 3:05 PM, Michael Albinus wrote: > So we shall parse indead every function for the library it belongs to. It > shouldn't be too hard, based on `autoloadp', `find-lisp-object-file-name' > and friends, see the implementation in `describe-function-1'. Something > like this is a very rudimentary implementation: I'm not sure I understand. This find the library to which a function belongs. How do we find the file corresponding to a package name this way? This question aside, I'm not sure we'll always want to ignore "tramp-sh" when tracing "tramp". IOW, that this will be every user's preference. For instance, someone might try to trace-package "cedet", and expect every function belonging to CEDET to be traced, subpackages and all. And likewise for multi-file ELPA packages. In that case, it's understood that a "package" can be spread over several files. ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-19 12:50 ` Dmitry Gutov @ 2017-06-19 13:07 ` Michael Albinus 0 siblings, 0 replies; 40+ messages in thread From: Michael Albinus @ 2017-06-19 13:07 UTC (permalink / raw) To: Dmitry Gutov; +Cc: Phil Sainty, 27397 Dmitry Gutov <dgutov@yandex.ru> writes: Hi Dmitry, > I'm not sure I understand. This find the library to which a function > belongs. How do we find the file corresponding to a package name this > way? Forget it. Phil has a simpler approach, based on `load-history'. > This question aside, I'm not sure we'll always want to ignore > "tramp-sh" when tracing "tramp". IOW, that this will be every user's > preference. Sure. There's still (trace-regexp "^tramp-"), no problem. > For instance, someone might try to trace-package "cedet", and expect > every function belonging to CEDET to be traced, subpackages and all. How do you define you a "subpackage"? In Tramp, I don't know what makes tramp-sh.el a subpackage of tramp.el, except my knowledge as maintainer. > And likewise for multi-file ELPA packages. In that case, it's > understood that a "package" can be spread over several files. Again, `tramp-regexp' is your friend. Best regards, Michael. ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-19 9:56 ` Michael Albinus 2017-06-19 11:00 ` Phil Sainty @ 2017-06-19 11:27 ` Dmitry Gutov 2017-06-19 11:36 ` Michael Albinus 1 sibling, 1 reply; 40+ messages in thread From: Dmitry Gutov @ 2017-06-19 11:27 UTC (permalink / raw) To: Michael Albinus, Phil Sainty; +Cc: 27397 Hi Michael, On 6/19/17 12:56 PM, Michael Albinus wrote: > That's not what I want. Often, I hunt bugs related to the order of > autoloaded functions, and this order shall be kept also when tracing. Your case is surely valid, but sounds a bit niche to me. It shouldn't be too hard to evaluate this, though: (with-eval-after-load 'tramp (trace-package "tramp-")) And the current patch makes it easier. >> Perhaps you're actually be suggesting some kind of `eval-after-load' >> tracing behaviour, though? > > Yes, that's the idea. If `trace-package' uses as argument a package name > as proposed above, the instrumentation shall happen in an > `eval-after-load' form for that package. Considering elp-instrument-package does not do that, I think we should limit the scope of the currently discussed patch, and trace only already loaded functions. If we do what you suggest, it should be a new discussion, and it should improve elp-instrument-package as well. ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-19 11:27 ` Dmitry Gutov @ 2017-06-19 11:36 ` Michael Albinus 2017-06-19 12:04 ` Dmitry Gutov 0 siblings, 1 reply; 40+ messages in thread From: Michael Albinus @ 2017-06-19 11:36 UTC (permalink / raw) To: Dmitry Gutov; +Cc: Phil Sainty, 27397 Dmitry Gutov <dgutov@yandex.ru> writes: > Hi Michael, Hi Dmitry, > Your case is surely valid, but sounds a bit niche to me. It shouldn't > be too hard to evaluate this, though: > > (with-eval-after-load 'tramp > (trace-package "tramp-")) But this would also trace functions from tramp-sh.el, which I don't want. >> Yes, that's the idea. If `trace-package' uses as argument a package name >> as proposed above, the instrumentation shall happen in an >> `eval-after-load' form for that package. > > Considering elp-instrument-package does not do that, I think we should > limit the scope of the currently discussed patch, and trace only > already loaded functions. > > If we do what you suggest, it should be a new discussion, and it > should improve elp-instrument-package as well. I don't believe it will be too hard to implement; why not doing it just now? Improving elp* could be a second step then. Best regards, Michael. ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-19 11:36 ` Michael Albinus @ 2017-06-19 12:04 ` Dmitry Gutov 2017-06-19 12:08 ` Michael Albinus 2017-06-19 12:24 ` Phil Sainty 0 siblings, 2 replies; 40+ messages in thread From: Dmitry Gutov @ 2017-06-19 12:04 UTC (permalink / raw) To: Michael Albinus; +Cc: Phil Sainty, 27397 On 6/19/17 2:36 PM, Michael Albinus wrote: >> (with-eval-after-load 'tramp >> (trace-package "tramp-")) > > But this would also trace functions from tramp-sh.el, which I don't want. What would be the equivalent of your proposal, then? >> If we do what you suggest, it should be a new discussion, and it >> should improve elp-instrument-package as well. > > I don't believe it will be too hard to implement; why not doing it just > now? Because we risk stopping now for a while and having two inconsistent functions in Emacs. It also adds a burden to the contributor who might now have signed up for tackling this particular challenge. There will also be some nuances to work out, I'm sure. ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-19 12:04 ` Dmitry Gutov @ 2017-06-19 12:08 ` Michael Albinus 2017-06-19 12:24 ` Phil Sainty 1 sibling, 0 replies; 40+ messages in thread From: Michael Albinus @ 2017-06-19 12:08 UTC (permalink / raw) To: Dmitry Gutov; +Cc: Phil Sainty, 27397 Dmitry Gutov <dgutov@yandex.ru> writes: > On 6/19/17 2:36 PM, Michael Albinus wrote: > >>> (with-eval-after-load 'tramp >>> (trace-package "tramp-")) >> >> But this would also trace functions from tramp-sh.el, which I don't want. > > What would be the equivalent of your proposal, then? See my other message. >>> If we do what you suggest, it should be a new discussion, and it >>> should improve elp-instrument-package as well. >> >> I don't believe it will be too hard to implement; why not doing it just >> now? > > Because we risk stopping now for a while and having two inconsistent > functions in Emacs. It also adds a burden to the contributor who might > now have signed up for tackling this particular challenge. > > There will also be some nuances to work out, I'm sure. It's up to Phil to decide what will be implemented. But as a power user of trace.el, I thought I bring my experience on table. Best regards, Michael. ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-19 12:04 ` Dmitry Gutov 2017-06-19 12:08 ` Michael Albinus @ 2017-06-19 12:24 ` Phil Sainty 1 sibling, 0 replies; 40+ messages in thread From: Phil Sainty @ 2017-06-19 12:24 UTC (permalink / raw) To: Dmitry Gutov, Michael Albinus; +Cc: 27397 On 20/06/17 00:04, Dmitry Gutov wrote: > Because we risk stopping now for a while and having two inconsistent > functions in Emacs. It also adds a burden to the contributor who might > now have signed up for tackling this particular challenge. > > There will also be some nuances to work out, I'm sure. I'm open to exploring such enhancements. If it looks like a can of worms, I'll be voting to split them out into a separate issue :) but let's keep the discussion going for now. At minimum we can figure out what an ideal end result ought to look like, and it might affect the names we choose in the interim. -Phil ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-16 13:32 bug#27397: [PATCH] New commands for bulk tracing of elisp functions Phil Sainty 2017-06-16 14:58 ` Dmitry Gutov @ 2017-06-16 15:43 ` Kaushal Modi 2017-06-17 8:48 ` Phil Sainty 2017-06-17 9:20 ` Phil Sainty 2 siblings, 1 reply; 40+ messages in thread From: Kaushal Modi @ 2017-06-16 15:43 UTC (permalink / raw) To: 27397, psainty, Dmitry Gutov [-- Attachment #1: Type: text/plain, Size: 458 bytes --] > I note that the `trace-is-traced' function does not follow the usual > naming convention for predicates. Should this be renamed to > `trace-is-traced-p' Based on current examples[1], it is more common to see predicate *functions* end in "-p". So the "-is-" portion is maybe redundant. My suggestion would be something like "trace-traced-p" or "trace-fn-traced-p". [1]: https://debbugs.gnu.org/cgi/bugreport.cgi?att=0;bug=26564;msg=5 -- Kaushal Modi [-- Attachment #2: Type: text/html, Size: 808 bytes --] ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-16 15:43 ` Kaushal Modi @ 2017-06-17 8:48 ` Phil Sainty 0 siblings, 0 replies; 40+ messages in thread From: Phil Sainty @ 2017-06-17 8:48 UTC (permalink / raw) To: Kaushal Modi, 27397, Dmitry Gutov > On 6/16/17 4:32 PM, Phil Sainty wrote: >> (I note that the `trace-is-traced' function does not follow the usual >> naming convention for predicates. Should this be renamed to >> `trace-is-traced-p' ?) On 17/06/17 02:58, Dmitry Gutov wrote: > I'm not 100% sure about the protocol here, but including both "-is-" > and "-p" in a function name seems too much. > > So maybe you should do the opposite and rename trace-is-traceable-p to > trace-is-traceable. On 17/06/17 03:43, Kaushal Modi wrote: > Based on current examples[1], it is more common to see predicate > *functions* end in "-p". So the "-is-" portion is maybe redundant. > > My suggestion would be something like "trace-traced-p" or > "trace-fn-traced-p". > > [1]: https://debbugs.gnu.org/cgi/bugreport.cgi?att=0;bug=26564;msg=5 FWIW, after loading more or less all the lisp in trunk, apropos tells me: 2381 matches for ".*-p$" 126 matches for "-is-" 28 matches for "-is-.*-p$" The -p suffix is certainly what I'm used to seeing, but -is- is entirely readable to my mind, so I'm happy either way. Consistency is good, so some kind of change seemed sensible to me, but I don't especially mind one way or the other. If one of the maintainers wants to make a recommendation, I'll update the code (or leave it as-is) accordingly. -Phil ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-16 13:32 bug#27397: [PATCH] New commands for bulk tracing of elisp functions Phil Sainty 2017-06-16 14:58 ` Dmitry Gutov 2017-06-16 15:43 ` Kaushal Modi @ 2017-06-17 9:20 ` Phil Sainty 2017-06-17 12:31 ` Phil Sainty 2017-06-17 23:03 ` Dmitry Gutov 2 siblings, 2 replies; 40+ messages in thread From: Phil Sainty @ 2017-06-17 9:20 UTC (permalink / raw) To: 27397 Thanks to the merge with https://debbugs.gnu.org/cgi/bugreport.cgi?bug=1343 and https://debbugs.gnu.org/cgi/bugreport.cgi?bug=6464 I see that there was a previous submission of a `trace-package' command which (a) used foreground tracing, and (b) provided `trace-package-background' as a separate command. I should note that I elected not to support foreground tracing for my proposed commands, simply because it seemed to me that foreground tracing could be problematic in too many cases, when the traces are being applied en masse on the basis of function name pattern matching. The current `trace-function-foreground' docstring says: > This function creates BUFFER if it does not exist. This buffer will > popup whenever FUNCTION is called. Do not use this function to trace > functions that switch buffers, or do any other display-oriented > stuff - use ‘trace-function-background’ instead. My compromise was to indicate the `trace-buffer' value in the echo area when the commands are invoked, so that the user would know where the trace output was happening. Actually popping up that buffer when the `trace-package' or `trace-regexp' command is used would be another (perhaps nicer) option? I'm open to recommendations for the most useful approach. Do others think that foreground versions of these commands would be a good idea? A prefix argument could mean "use foreground tracing", but that's inconsistent with `trace--read-args' which uses a prefix arg to prompt the user for the trace buffer and a context expression -- which I now realise is behaviour that my commands should incorporate as well. -Phil ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-17 9:20 ` Phil Sainty @ 2017-06-17 12:31 ` Phil Sainty 2017-06-17 22:59 ` Dmitry Gutov 2017-06-17 23:03 ` Dmitry Gutov 1 sibling, 1 reply; 40+ messages in thread From: Phil Sainty @ 2017-06-17 12:31 UTC (permalink / raw) To: 27397 [-- Attachment #1: Type: text/plain, Size: 861 bytes --] On 17/06/17 21:20, Phil Sainty wrote: > inconsistent with `trace--read-args' which uses a prefix arg to prompt > the user for the trace buffer and a context expression -- which I now > realise is behaviour that my commands should incorporate as well. I've implemented this change, and attached the current WIP patch. In the process I noticed that the existing behaviour of trace--read-args was quite unfriendly if you wanted to set a trace buffer but had no need of a context expression -- typing RET at the context expression prompt triggered an "End of file during parsing" error, as that input string is processed by `read-from-string'. I've changed that code to treat an empty input as "nil" (which is read to `nil'), and to ignore `nil' context expressions entirely (as opposed to printing their evaluated value as "[nil]" in the trace buffer). -Phil [-- Attachment #2: 0001-New-commands-for-bulk-tracing-of-elisp-functions.patch --] [-- Type: text/x-patch, Size: 9590 bytes --] From 4d9ea7ac95b070d55b8e1502c3091168891f433e Mon Sep 17 00:00:00 2001 From: Phil Sainty <psainty@orcon.net.nz> Date: Sun, 11 Jun 2017 17:29:53 +1200 Subject: [PATCH] New commands for bulk tracing of elisp functions * lisp/emacs-lisp/trace.el (trace-package, untrace-package) (trace-regexp, untrace-regexp, trace-is-traceable-p): New functions. (trace--read-extra-args): New function, split from `trace--read-args'. Changed to allow the user to enter an empty string at the context expression prompt (previously an error; now treated as "nil"), and to cause a "nil" context expression to produce no context output in the trace buffer. (trace--read-function-args): New name for `trace--read-args'. Changed to use the new `trace-is-traceable-p' predicate. (trace--read-args): Renamed to `trace--read-function-args' (trace-function-foreground, trace-function-background): Call the renamed `trace--read-function-args'. (trace-is-traced, untrace-function, untrace-all): Doc updates/fixes. * etc/NEWS: Mention the new trace commands. * doc/misc/tramp.texi: Update "(tramp) Traces and Profiles" to use `trace-package'. --- doc/misc/tramp.texi | 4 +- etc/NEWS | 8 +++ lisp/emacs-lisp/trace.el | 133 ++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 123 insertions(+), 22 deletions(-) diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi index 4ca3932..54d5d00 100644 --- a/doc/misc/tramp.texi +++ b/doc/misc/tramp.texi @@ -3672,9 +3672,7 @@ Traces and Profiles @lisp @group -(require 'trace) -(dolist (elt (all-completions "tramp-" obarray 'functionp)) - (trace-function-background (intern elt))) +(trace-package "tramp-") (untrace-function 'tramp-read-passwd) @end group @end lisp diff --git a/etc/NEWS b/etc/NEWS index 7972511..d3d73cc 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -743,6 +743,14 @@ header's value. where the GnuPG home directory (used for signature verification) is located and whether GnuPG's option "--homedir" is used or not. +** Trace + ++++ +*** New commands 'trace-package' and 'trace-regexp' (and their +counterparts 'untrace-package' and 'untrace-regexp') allow for the +bulk tracing of calls to functions with names matching a specified +prefix or regexp. + ** Tramp +++ diff --git a/lisp/emacs-lisp/trace.el b/lisp/emacs-lisp/trace.el index 1c57d73..949bb54 100644 --- a/lisp/emacs-lisp/trace.el +++ b/lisp/emacs-lisp/trace.el @@ -257,10 +257,15 @@ trace-function-internal (or context (lambda () ""))) `((name . ,trace-advice-name) (depth . -100)))) +(defun trace-is-traceable-p (sym) + "Whether the given symbol is a traceable function." + (or (functionp sym) (macrop sym))) + (defun trace-is-traced (function) + "Whether FUNCTION is currently traced." (advice-member-p trace-advice-name function)) -(defun trace--read-args (prompt) +(defun trace--read-function-args (prompt) "Read a function name, prompting with string PROMPT. If `current-prefix-arg' is non-nil, also read a buffer and a \"context\" \(Lisp expression). Return (FUNCTION BUFFER FUNCTION-CONTEXT)." @@ -274,19 +279,25 @@ trace--read-args default (if beg (substring prompt beg) ": ")) prompt) - obarray 'fboundp t nil nil + obarray 'trace-is-traceable-p t nil nil (if default (symbol-name default))))) (when current-prefix-arg - (list - (read-buffer "Output to buffer: " trace-buffer) - (let ((exp - (let ((minibuffer-completing-symbol t)) - (read-from-minibuffer "Context expression: " - nil read-expression-map t - 'read-expression-history)))) - (lambda () - (let ((print-circle t)) - (concat " [" (prin1-to-string (eval exp t)) "]")))))))) + (trace--read-extra-args)))) + +(defun trace--read-extra-args () + "Read a buffer and a \"context\" (Lisp expression). +Return (BUFFER FUNCTION-CONTEXT)." + (list + (read-buffer "Output to buffer: " trace-buffer) + (let ((exp + (let ((minibuffer-completing-symbol t)) + (read-from-minibuffer "Context expression: " + nil read-expression-map t + 'read-expression-history "nil")))) + (and exp + (lambda () + (let ((print-circle t)) + (concat " [" (prin1-to-string (eval exp t)) "]"))))))) ;;;###autoload (defun trace-function-foreground (function &optional buffer context) @@ -306,7 +317,7 @@ trace-function-foreground stuff - use `trace-function-background' instead. To stop tracing a function, use `untrace-function' or `untrace-all'." - (interactive (trace--read-args "Trace function: ")) + (interactive (trace--read-function-args "Trace function: ")) (trace-function-internal function buffer nil context)) ;;;###autoload @@ -314,24 +325,108 @@ trace-function-background "Trace calls to function FUNCTION, quietly. This is like `trace-function-foreground', but without popping up the output buffer or changing the window configuration." - (interactive (trace--read-args "Trace function in background: ")) + (interactive (trace--read-function-args "Trace function in background: ")) (trace-function-internal function buffer t context)) ;;;###autoload (defalias 'trace-function 'trace-function-foreground) (defun untrace-function (function) - "Untraces FUNCTION and possibly activates all remaining advice. -Activation is performed with `ad-update', hence remaining advice will get -activated only if the advice of FUNCTION is currently active. If FUNCTION -was not traced this is a noop." + "Remove trace from FUNCTION. If FUNCTION was not traced this is a noop." (interactive (list (intern (completing-read "Untrace function: " obarray #'trace-is-traced t)))) (advice-remove function trace-advice-name)) +;;;###autoload +(defun trace-package (prefix &optional buffer context) + "Trace all functions with names starting with PREFIX. +For example, to trace all diff functions, do the following: + +\\[trace-package] RET diff- RET + +Background tracing is used. Switch to the trace output buffer to +view the results. + +See also `untrace-package'." + ;; Derived in part from `elp-instrument-package'. + (interactive + (cons (completing-read "Prefix of package to trace: " + obarray #'trace-is-traceable-p) + (and current-prefix-arg (trace--read-extra-args)))) + (when (zerop (length prefix)) + (error "Tracing all Emacs functions would render Emacs unusable")) + (mapc (lambda (name) + (trace-function-background (intern name) buffer context)) + (all-completions prefix obarray #'trace-is-traceable-p)) + (message + "Tracing to %s. Use %s to untrace a package, or %s to remove all traces." + (or buffer trace-buffer) + (substitute-command-keys "\\[untrace-package]") + (substitute-command-keys "\\[untrace-all]"))) + +(defun untrace-package (prefix) + "Remove traces from all functions with names starting with PREFIX. + +See also `trace-package'." + (interactive + (list (completing-read "Prefix of package to untrace: " + obarray #'trace-is-traced))) + (if (and (zerop (length prefix)) + (y-or-n-p "Remove all function traces?")) + (untrace-all) + (mapc (lambda (name) + (untrace-function (intern name))) + (all-completions prefix obarray #'trace-is-traceable-p)))) + +;;;###autoload +(defun trace-regexp (regexp &optional buffer context) + "Trace all functions with names matching REGEXP. +For example, to trace indentation-related functions, you could try: + +\\[trace-regexp] RET indent\\|offset RET + +Warning: Do not attempt to trace all functions. Tracing too many +functions at one time will render Emacs unusable. + +Background tracing is used. Switch to the trace output buffer to +view the results. + +See also `untrace-regexp'." + (interactive + (cons (read-regexp "Regexp matching functions to trace: ") + (and current-prefix-arg (trace--read-extra-args)))) + (when (member regexp '("" "." ".+" ".*")) + ;; Not comprehensive, but it catches the most likely attempts. + (error "Tracing all Emacs functions would render Emacs unusable")) + (mapatoms + (lambda (sym) + (and (trace-is-traceable-p sym) + (string-match-p regexp (symbol-name sym)) + (trace-function-background sym buffer context)))) + (message + "Tracing to %s. Use %s to untrace by regexp, or %s to remove all traces." + (or buffer trace-buffer) + (substitute-command-keys "\\[untrace-regexp]") + (substitute-command-keys "\\[untrace-all]"))) + +(defun untrace-regexp (regexp) + "Remove traces from all functions with names matching REGEXP. + +See also `trace-regexp'." + (interactive + (list (read-regexp "Regexp matching functions to untrace: "))) + (if (and (zerop (length regexp)) + (y-or-n-p "Remove all function traces?")) + (untrace-all) + (mapatoms + (lambda (sym) + (and (trace-is-traceable-p sym) + (string-match-p regexp (symbol-name sym)) + (untrace-function sym)))))) + (defun untrace-all () - "Untraces all currently traced functions." + "Remove traces from all currently traced functions." (interactive) (mapatoms #'untrace-function)) -- 2.8.3 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-17 12:31 ` Phil Sainty @ 2017-06-17 22:59 ` Dmitry Gutov 2017-06-18 1:06 ` Phil Sainty 0 siblings, 1 reply; 40+ messages in thread From: Dmitry Gutov @ 2017-06-17 22:59 UTC (permalink / raw) To: Phil Sainty, 27397 On 6/17/17 3:31 PM, Phil Sainty wrote: > In the process I noticed that the existing behaviour of trace--read-args > was quite unfriendly if you wanted to set a trace buffer but had no need > of a context expression -- typing RET at the context expression prompt > triggered an "End of file during parsing" error, as that input string > is processed by `read-from-string'. > > I've changed that code to treat an empty input as "nil" (which is read > to `nil'), and to ignore `nil' context expressions entirely (as opposed > to printing their evaluated value as "[nil]" in the trace buffer). Sounds good. Why the rename, though? Those are not arguments for the function we're going to trace. trace--read-args sounds as appropriate, if not more. ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-17 22:59 ` Dmitry Gutov @ 2017-06-18 1:06 ` Phil Sainty 2017-06-18 6:32 ` Dmitry Gutov 2017-06-18 11:22 ` Phil Sainty 0 siblings, 2 replies; 40+ messages in thread From: Phil Sainty @ 2017-06-18 1:06 UTC (permalink / raw) To: Dmitry Gutov, 27397 On 18/06/17 10:59, Dmitry Gutov wrote: > Why the rename, though? Those are not arguments for the function we're > going to trace. trace--read-args sounds as appropriate, if not more. That was because the behaviour of `trace--read-args' had been quite specific to the `trace-function*' commands -- its primary purpose was to prompt for a single function -- and I thought the name should reflect that. I agree with you, though -- the new name wasn't ideal either. I've now refactored this like so: * `trace--read-args' has been split into `trace--read-function` and `trace--read-extra-args'. * `trace--read-function` reads only a function. * `The interactive specs for the trace-function*' commands are now similar to those of the new bulk trace commands, in explicitly calling `trace--read-extra-args': (interactive (cons (trace--read-function "Trace function: ") (and current-prefix-arg (trace--read-extra-args)))) I think this name change makes better sense, and the code is now more consistent between commands. -Phil ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-18 1:06 ` Phil Sainty @ 2017-06-18 6:32 ` Dmitry Gutov 2017-06-18 11:22 ` Phil Sainty 1 sibling, 0 replies; 40+ messages in thread From: Dmitry Gutov @ 2017-06-18 6:32 UTC (permalink / raw) To: Phil Sainty, 27397 On 6/18/17 4:06 AM, Phil Sainty wrote: > I've now refactored this like so: > > * `trace--read-args' has been split into `trace--read-function` and > `trace--read-extra-args'. > > * `trace--read-function` reads only a function. That sounds better, thanks! ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-18 1:06 ` Phil Sainty 2017-06-18 6:32 ` Dmitry Gutov @ 2017-06-18 11:22 ` Phil Sainty 2019-06-27 18:01 ` bug#1343: " Lars Ingebrigtsen 1 sibling, 1 reply; 40+ messages in thread From: Phil Sainty @ 2017-06-18 11:22 UTC (permalink / raw) To: 27397 [-- Attachment #1: Type: text/plain, Size: 718 bytes --] Just attaching the current patch. Thanks for the input Dmitry and Kaushal. I'll leave it a while to see whether anyone else wishes to weigh in on the outstanding items: * The inconsistent predicate naming of `trace-is-traced' vs `trace-is-traceable-p'. https://debbugs.gnu.org/cgi/bugreport.cgi?bug=27397#19 * Whether foreground-tracing variants of the new commands are wanted. https://debbugs.gnu.org/cgi/bugreport.cgi?bug=27397#25 https://debbugs.gnu.org/cgi/bugreport.cgi?bug=27397#34 * Whether it's a good idea to display the trace buffer initially when one of the background tracing commands is invoked. (I guess I'll follow up next weekend if no one has anything to add in the interim.) -Phil [-- Attachment #2: 0001-New-commands-for-bulk-tracing-of-elisp-functions.patch --] [-- Type: text/x-patch, Size: 13470 bytes --] From 5ca9108f585a06c2085ab180c390bf85d3657230 Mon Sep 17 00:00:00 2001 From: Phil Sainty <psainty@orcon.net.nz> Date: Sun, 11 Jun 2017 17:29:53 +1200 Subject: [PATCH] New commands for bulk tracing of elisp functions * lisp/emacs-lisp/trace.el (trace-package, untrace-package) (trace-regexp, untrace-regexp, trace-is-traceable-p): New functions. (trace--read-function): New function, split from `trace--read-args'. Changed to use the new `trace-is-traceable-p' predicate. (trace--read-extra-args): New function, split from `trace--read-args'. Changed to allow the user to enter an empty string at the context expression prompt (previously an error; now treated as "nil"), and to cause a "nil" context expression to produce no context output in the trace buffer. (trace--read-args): Removed function. Replaced by the combination of `trace--read-function' and `trace--read-extra-args'. (trace-function-foreground, trace-function-background): Updated interactive specs to use the new functions. (trace-is-traced, untrace-function, untrace-all): Doc updates/fixes. Commentary updated to cover the new commands. Change log updated to cover the main changes since 1993. * etc/NEWS: Mention the new trace commands. * doc/misc/tramp.texi: Update "(tramp) Traces and Profiles" to use `trace-package'. --- doc/misc/tramp.texi | 4 +- etc/NEWS | 8 ++ lisp/emacs-lisp/trace.el | 206 ++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 176 insertions(+), 42 deletions(-) diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi index 4ca3932..54d5d00 100644 --- a/doc/misc/tramp.texi +++ b/doc/misc/tramp.texi @@ -3672,9 +3672,7 @@ Traces and Profiles @lisp @group -(require 'trace) -(dolist (elt (all-completions "tramp-" obarray 'functionp)) - (trace-function-background (intern elt))) +(trace-package "tramp-") (untrace-function 'tramp-read-passwd) @end group @end lisp diff --git a/etc/NEWS b/etc/NEWS index 7972511..d3d73cc 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -743,6 +743,14 @@ header's value. where the GnuPG home directory (used for signature verification) is located and whether GnuPG's option "--homedir" is used or not. +** Trace + ++++ +*** New commands 'trace-package' and 'trace-regexp' (and their +counterparts 'untrace-package' and 'untrace-regexp') allow for the +bulk tracing of calls to functions with names matching a specified +prefix or regexp. + ** Tramp +++ diff --git a/lisp/emacs-lisp/trace.el b/lisp/emacs-lisp/trace.el index 1c57d73..05ee780 100644 --- a/lisp/emacs-lisp/trace.el +++ b/lisp/emacs-lisp/trace.el @@ -52,14 +52,20 @@ ;; Usage: ;; ====== -;; - To trace a function say `M-x trace-function', which will ask you for the +;; - To trace a function use `M-x trace-function', which will ask you for the ;; name of the function/subr/macro to trace. ;; - If you want to trace a function that switches buffers or does other ;; display oriented stuff use `M-x trace-function-background', which will ;; generate the trace output silently in the background without popping ;; up windows and doing other irritating stuff. -;; - To untrace a function say `M-x untrace-function'. -;; - To untrace all currently traced functions say `M-x untrace-all'. +;; - `M-x trace-package' will ask you for a function name prefix, and trace +;; (in the background) all matching functions. +;; - `M-x trace-regexp' will ask you for a function name pattern (regexp), +;; and trace (in the background) all matching functions. +;; - To untrace a function use `M-x untrace-function'. +;; - To untrace multiple functions by prefix use `M-x untrace-package'. +;; - To untrace multiple functions by regexp use `M-x untrace-regexp'. +;; - To untrace all currently traced functions use `M-x untrace-all'. ;; Examples: ;; ========= @@ -120,6 +126,22 @@ ;;; Change Log: +;; 2017-06-17 Phil Sainty +;; * New commands `trace-package', `untrace-package', `trace-regexp', +;; `untrace-regexp' +;; +;; 2012-2014 Stefan Monnier, Glenn Morris +;; * Adapted for nadvice.el +;; * New `context' argument and display in trace buffer +;; * `trace-function' renamed to (and now an alias of) +;; `trace-function-foreground' +;; +;; 2005-02-27 Stefan Monnier +;; * New `inhibit-trace' variable +;; +;; 1998-04-05 Stephen Eglen +;; * New customize group `trace' +;; ;; Revision 2.0 1993/05/18 00:41:16 hans ;; * Adapted for advice.el 2.0; it now also works ;; for GNU Emacs-19 and Lemacs @@ -257,42 +279,48 @@ trace-function-internal (or context (lambda () ""))) `((name . ,trace-advice-name) (depth . -100)))) +(defun trace-is-traceable-p (sym) + "Whether the given symbol is a traceable function." + (or (functionp sym) (macrop sym))) + (defun trace-is-traced (function) + "Whether FUNCTION is currently traced." (advice-member-p trace-advice-name function)) -(defun trace--read-args (prompt) - "Read a function name, prompting with string PROMPT. -If `current-prefix-arg' is non-nil, also read a buffer and a \"context\" -\(Lisp expression). Return (FUNCTION BUFFER FUNCTION-CONTEXT)." - (cons - (let ((default (function-called-at-point)) - (beg (string-match ":[ \t]*\\'" prompt))) - (intern (completing-read (if default - (format - "%s (default %s)%s" - (substring prompt 0 beg) - default - (if beg (substring prompt beg) ": ")) - prompt) - obarray 'fboundp t nil nil - (if default (symbol-name default))))) - (when current-prefix-arg - (list - (read-buffer "Output to buffer: " trace-buffer) - (let ((exp - (let ((minibuffer-completing-symbol t)) - (read-from-minibuffer "Context expression: " - nil read-expression-map t - 'read-expression-history)))) - (lambda () - (let ((print-circle t)) - (concat " [" (prin1-to-string (eval exp t)) "]")))))))) +(defun trace--read-function (prompt) + "Read a function name, prompting with string PROMPT." + (let ((default (function-called-at-point)) + (beg (string-match ":[ \t]*\\'" prompt))) + (intern (completing-read (if default + (format + "%s (default %s)%s" + (substring prompt 0 beg) + default + (if beg (substring prompt beg) ": ")) + prompt) + obarray 'trace-is-traceable-p t nil nil + (if default (symbol-name default)))))) + +(defun trace--read-extra-args () + "Read a buffer and a \"context\" (Lisp expression). +Return (BUFFER CONTEXT)." + (list + (read-buffer "Output to buffer: " trace-buffer) + (let ((exp + (let ((minibuffer-completing-symbol t)) + (read-from-minibuffer "Context expression: " + nil read-expression-map t + 'read-expression-history "nil")))) + (and exp + (lambda () + (let ((print-circle t)) + (concat " [" (prin1-to-string (eval exp t)) "]"))))))) ;;;###autoload (defun trace-function-foreground (function &optional buffer context) "Trace calls to function FUNCTION. -With a prefix argument, also prompt for the trace buffer (default -`trace-buffer'), and a Lisp expression CONTEXT. +With a prefix argument, also prompt for the trace output BUFFER +\(default `trace-buffer'), and a Lisp expression CONTEXT. Tracing a function causes every call to that function to insert into BUFFER Lisp-style trace messages that display the function's @@ -306,7 +334,9 @@ trace-function-foreground stuff - use `trace-function-background' instead. To stop tracing a function, use `untrace-function' or `untrace-all'." - (interactive (trace--read-args "Trace function: ")) + (interactive + (cons (trace--read-function "Trace function: ") + (and current-prefix-arg (trace--read-extra-args)))) (trace-function-internal function buffer nil context)) ;;;###autoload @@ -314,24 +344,122 @@ trace-function-background "Trace calls to function FUNCTION, quietly. This is like `trace-function-foreground', but without popping up the output buffer or changing the window configuration." - (interactive (trace--read-args "Trace function in background: ")) + (interactive + (cons (trace--read-function "Trace function in background: ") + (and current-prefix-arg (trace--read-extra-args)))) (trace-function-internal function buffer t context)) ;;;###autoload (defalias 'trace-function 'trace-function-foreground) (defun untrace-function (function) - "Untraces FUNCTION and possibly activates all remaining advice. -Activation is performed with `ad-update', hence remaining advice will get -activated only if the advice of FUNCTION is currently active. If FUNCTION -was not traced this is a noop." + "Remove trace from FUNCTION. If FUNCTION was not traced this is a noop." (interactive (list (intern (completing-read "Untrace function: " obarray #'trace-is-traced t)))) (advice-remove function trace-advice-name)) +;;;###autoload +(defun trace-package (prefix &optional buffer context) + "Trace all functions with names starting with PREFIX. +For example, to trace all diff functions, do the following: + +\\[trace-package] RET diff- RET + +With a prefix argument, also prompt for the trace output BUFFER +\(default `trace-buffer'), and a Lisp expression CONTEXT. + +Background tracing is used. Switch to the trace output buffer to view +the results. + +See `trace-function-foreground' for details on the optional arguments +and foreground vs background tracing. + +See also `untrace-package'." + ;; Derived in part from `elp-instrument-package'. + (interactive + (cons (completing-read "Prefix of package to trace: " + obarray #'trace-is-traceable-p) + (and current-prefix-arg (trace--read-extra-args)))) + (when (zerop (length prefix)) + (error "Tracing all Emacs functions would render Emacs unusable")) + (mapc (lambda (name) + (trace-function-background (intern name) buffer context)) + (all-completions prefix obarray #'trace-is-traceable-p)) + (message + "Tracing to %s. Use %s to untrace a package, or %s to remove all traces." + (or buffer trace-buffer) + (substitute-command-keys "\\[untrace-package]") + (substitute-command-keys "\\[untrace-all]"))) + +(defun untrace-package (prefix) + "Remove all traces from functions with names starting with PREFIX. + +See also `trace-package'." + (interactive + (list (completing-read "Prefix of package to untrace: " + obarray #'trace-is-traced))) + (if (and (zerop (length prefix)) + (y-or-n-p "Remove all function traces?")) + (untrace-all) + (mapc (lambda (name) + (untrace-function (intern name))) + (all-completions prefix obarray #'trace-is-traceable-p)))) + +;;;###autoload +(defun trace-regexp (regexp &optional buffer context) + "Trace all functions with names matching REGEXP. +For example, to trace indentation-related functions, you could try: + +\\[trace-regexp] RET indent\\|offset RET + +Warning: Do not attempt to trace all functions. Tracing too many +functions at one time will render Emacs unusable. + +With a prefix argument, also prompt for the trace output BUFFER +\(default `trace-buffer'), and a Lisp expression CONTEXT. + +Background tracing is used. Switch to the trace output buffer to view +the results. + +See `trace-function-foreground' for details on the optional arguments +and foreground vs background tracing. + +See also `untrace-regexp'." + (interactive + (cons (read-regexp "Regexp matching functions to trace: ") + (and current-prefix-arg (trace--read-extra-args)))) + (when (member regexp '("" "." ".+" ".*")) + ;; Not comprehensive, but it catches the most likely attempts. + (error "Tracing all Emacs functions would render Emacs unusable")) + (mapatoms + (lambda (sym) + (and (trace-is-traceable-p sym) + (string-match-p regexp (symbol-name sym)) + (trace-function-background sym buffer context)))) + (message + "Tracing to %s. Use %s to untrace by regexp, or %s to remove all traces." + (or buffer trace-buffer) + (substitute-command-keys "\\[untrace-regexp]") + (substitute-command-keys "\\[untrace-all]"))) + +(defun untrace-regexp (regexp) + "Remove all traces from functions with names matching REGEXP. + +See also `trace-regexp'." + (interactive + (list (read-regexp "Regexp matching functions to untrace: "))) + (if (and (zerop (length regexp)) + (y-or-n-p "Remove all function traces?")) + (untrace-all) + (mapatoms + (lambda (sym) + (and (trace-is-traceable-p sym) + (string-match-p regexp (symbol-name sym)) + (untrace-function sym)))))) + (defun untrace-all () - "Untraces all currently traced functions." + "Remove traces from all currently traced functions." (interactive) (mapatoms #'untrace-function)) -- 2.8.3 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-18 11:22 ` Phil Sainty @ 2019-06-27 18:01 ` Lars Ingebrigtsen 2019-06-28 11:25 ` bug#27397: " Phil Sainty 0 siblings, 1 reply; 40+ messages in thread From: Lars Ingebrigtsen @ 2019-06-27 18:01 UTC (permalink / raw) To: Phil Sainty; +Cc: 1343, 27397 Phil Sainty <psainty@orcon.net.nz> writes: > (I guess I'll follow up next weekend if no one has anything to add > in the interim.) The consensus seemed to be that adding commands for tracing a large number of functions was welcome, even if Michael had some usability concerns. But it was never applied? -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2019-06-27 18:01 ` bug#1343: " Lars Ingebrigtsen @ 2019-06-28 11:25 ` Phil Sainty 2021-04-17 5:51 ` bug#1343: [PATCH] trace package Stefan Kangas 2022-07-15 4:08 ` bug#27397: bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp functions Phil Sainty 0 siblings, 2 replies; 40+ messages in thread From: Phil Sainty @ 2019-06-28 11:25 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: 1343, 27397 Hi Lars, On 28/06/19 6:01 AM, Lars Ingebrigtsen wrote: > The consensus seemed to be that adding commands for tracing a large > number of functions was welcome, even if Michael had some usability > concerns. But it was never applied? Correct. This has been languishing on my to-do list for a long time (albeit not forgotten about), as my time has been pretty limited, and I've been spending it on other projects. With so-long.el hopefully out of the way for now, and seeing as how you've raised it, I'll try to get back to this next (I had a bunch of work-in-progress based on the previous discussions at the time I stashed it). -Phil ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#1343: [PATCH] trace package 2019-06-28 11:25 ` bug#27397: " Phil Sainty @ 2021-04-17 5:51 ` Stefan Kangas 2021-04-17 14:01 ` bug#27397: " Phil Sainty 2022-07-15 4:08 ` bug#27397: bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp functions Phil Sainty 1 sibling, 1 reply; 40+ messages in thread From: Stefan Kangas @ 2021-04-17 5:51 UTC (permalink / raw) To: Phil Sainty; +Cc: 1343, Lars Ingebrigtsen, 27397 Hi Phil, Phil Sainty <psainty@orcon.net.nz> writes: > On 28/06/19 6:01 AM, Lars Ingebrigtsen wrote: >> The consensus seemed to be that adding commands for tracing a large >> number of functions was welcome, even if Michael had some usability >> concerns. But it was never applied? > > Correct. This has been languishing on my to-do list for a > long time (albeit not forgotten about), as my time has been > pretty limited, and I've been spending it on other projects. > > With so-long.el hopefully out of the way for now, and seeing > as how you've raised it, I'll try to get back to this next > (I had a bunch of work-in-progress based on the previous > discussions at the time I stashed it). (That was almost 2 years ago.) Any updates here? ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: bug#1343: [PATCH] trace package 2021-04-17 5:51 ` bug#1343: [PATCH] trace package Stefan Kangas @ 2021-04-17 14:01 ` Phil Sainty 2021-10-21 20:29 ` Stefan Kangas 0 siblings, 1 reply; 40+ messages in thread From: Phil Sainty @ 2021-04-17 14:01 UTC (permalink / raw) To: Stefan Kangas; +Cc: 1343, Lars Ingebrigtsen, 27397 Hi Stefan, On 17/04/21 5:51 pm, Stefan Kangas wrote: > (That was almost 2 years ago.) > Any updates here? Unfortunately not. I think I briefly resumed work on that at the time, but it almost immediately got sidelined again. -Phil ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#1343: [PATCH] trace package 2021-04-17 14:01 ` bug#27397: " Phil Sainty @ 2021-10-21 20:29 ` Stefan Kangas 0 siblings, 0 replies; 40+ messages in thread From: Stefan Kangas @ 2021-10-21 20:29 UTC (permalink / raw) To: Phil Sainty; +Cc: 1343, Lars Ingebrigtsen, 27397 Phil Sainty <psainty@orcon.net.nz> writes: > Hi Stefan, > > On 17/04/21 5:51 pm, Stefan Kangas wrote: >> (That was almost 2 years ago.) >> Any updates here? > > Unfortunately not. I think I briefly resumed work on that at > the time, but it almost immediately got sidelined again. Is there anything stopping us from adding what you have so far? ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2019-06-28 11:25 ` bug#27397: " Phil Sainty 2021-04-17 5:51 ` bug#1343: [PATCH] trace package Stefan Kangas @ 2022-07-15 4:08 ` Phil Sainty 2022-07-15 6:23 ` Eli Zaretskii 2022-09-11 11:49 ` Lars Ingebrigtsen 1 sibling, 2 replies; 40+ messages in thread From: Phil Sainty @ 2022-07-15 4:08 UTC (permalink / raw) To: 27397, 1343; +Cc: Lars Ingebrigtsen, Michael Albinus, Dmitry Gutov After a brief hiatus, I've resumed (maybe completed) my work on this. The branch scratch/bulk-tracing contains the updated code for Emacs 29 (rebased over master). NEWS is: *** New commands 'trace-package', 'trace-regexp', and 'trace-library' (and their counterparts 'untrace-package', 'untrace-regexp', and 'untrace-library') allow for the bulk tracing of calls to functions with names matching a specified prefix or regexp, or functions defined by a specified file. New command 'trace-currently-traced' lists the traced function symbols. -Phil ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2022-07-15 4:08 ` bug#27397: bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp functions Phil Sainty @ 2022-07-15 6:23 ` Eli Zaretskii 2022-07-15 11:09 ` Phil Sainty 2022-09-11 11:49 ` Lars Ingebrigtsen 1 sibling, 1 reply; 40+ messages in thread From: Eli Zaretskii @ 2022-07-15 6:23 UTC (permalink / raw) To: Phil Sainty; +Cc: 1343, larsi, 27397, michael.albinus, dgutov > Cc: Lars Ingebrigtsen <larsi@gnus.org>, > Michael Albinus <michael.albinus@gmx.de>, Dmitry Gutov <dgutov@yandex.ru> > Date: Fri, 15 Jul 2022 16:08:46 +1200 > From: Phil Sainty <psainty@orcon.net.nz> > > After a brief hiatus, I've resumed (maybe completed) my work on this. > > The branch scratch/bulk-tracing contains the updated code for Emacs 29 > (rebased over master). > > NEWS is: > > *** New commands 'trace-package', 'trace-regexp', and 'trace-library' > (and their counterparts 'untrace-package', 'untrace-regexp', and > 'untrace-library') allow for the bulk tracing of calls to functions > with names matching a specified prefix or regexp, or functions defined > by a specified file. New command 'trace-currently-traced' lists the > traced function symbols. Thanks, but please add documentation at some point (I mean the manual). ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2022-07-15 6:23 ` Eli Zaretskii @ 2022-07-15 11:09 ` Phil Sainty 0 siblings, 0 replies; 40+ messages in thread From: Phil Sainty @ 2022-07-15 11:09 UTC (permalink / raw) To: Eli Zaretskii; +Cc: 1343, larsi, 27397, michael.albinus, dgutov On 2022-07-15 18:23, Eli Zaretskii wrote: > Thanks, but please add documentation at some point (I mean the > manual). I did document these changes in the elisp manual to the extent that any of the trace functionality is documented (which is minimally). The key documentation would probably be for the functionality which was already present, rather than for the changes I've made here; so anyone familiar with trace.el might add that. I'll try to throw something together from commentary and docstrings in the near future though (including the new changes), assuming no one else does so first. -Phil ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2022-07-15 4:08 ` bug#27397: bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp functions Phil Sainty 2022-07-15 6:23 ` Eli Zaretskii @ 2022-09-11 11:49 ` Lars Ingebrigtsen 2022-09-11 12:33 ` Eli Zaretskii 2022-09-12 13:26 ` Michael Albinus 1 sibling, 2 replies; 40+ messages in thread From: Lars Ingebrigtsen @ 2022-09-11 11:49 UTC (permalink / raw) To: Phil Sainty; +Cc: 1343, 27397, Michael Albinus, Dmitry Gutov Phil Sainty <psainty@orcon.net.nz> writes: > After a brief hiatus, I've resumed (maybe completed) my work on this. > > The branch scratch/bulk-tracing contains the updated code for Emacs 29 > (rebased over master). For reference, I've included the diff between master and the branch below. I have not tried the patch myself -- does anybody have any comments here? It seems like useful functionality to me. diff --git a/doc/lispref/debugging.texi b/doc/lispref/debugging.texi index 058c931954..6bdcf33a95 100644 --- a/doc/lispref/debugging.texi +++ b/doc/lispref/debugging.texi @@ -20,14 +20,10 @@ Debugging You can use Edebug, a source-level debugger for Emacs Lisp. @item -@cindex tracing Lisp programs -You can trace the execution of functions involved in the problem using -the tracing facilities provided by the @file{trace.el} package. This -package provides the functions @code{trace-function-foreground} and -@code{trace-function-background} for tracing function calls, and -@code{trace-values} for adding values of select variables to the -trace. For the details, see the documentation of these facilities in -@file{trace.el}. +You can trace the execution of functions involved in the problem +(logging function calls, their arguments and return values, and other +context values) using the tracing facilities provided by the +@file{trace.el} package. @item If a syntactic problem is preventing Lisp from even reading the @@ -59,6 +55,7 @@ Debugging * Syntax Errors:: How to find syntax errors. * Test Coverage:: Ensuring you have tested all branches in your code. * Profiling:: Measuring the resources that your code uses. +* Tracing:: Log function calls, arguments, and return values. @end menu @node Debugger @@ -1072,3 +1069,327 @@ Profiling debugging Emacs. It actually stops the Lisp-level @kbd{M-x profiler-@dots{}} commands described above from working. @end ifnottex + + +@node Tracing +@section Tracing +@cindex tracing +@cindex trace +@cindex trace functions +@cindex tracing Lisp programs + +You can trace the execution of functions using the tracing facilities +provided by the @file{trace.el} library. Many functions can be traced +at the same time. The commands @code{trace-function-foreground} and +@code{trace-function-background} add a new trace to a single specified +function. The commands @code{trace-package}, @code{trace-regexp}, and +@code{trace-library} enable traces to be added to functions en masse. +Traces can also be added to autoloaded functions -- the associated +function will be traced if and when it is defined. + +@vindex trace-buffer +Calls to traced functions, including the values of their arguments and +their return values, are logged to the @file{*trace-output*} buffer +(or another buffer as specified -- either by the @code{trace-buffer} +user option, or as the @var{buffer} argument to a tracing command). + +@anchor{trace context} +@cindex @code{context} in trace functions +Optional @var{context} expressions are also evaluated, both when the +associated function is called and again when it returns, with the +value logged within square brackets alongside the call-time arguments +or return value respectively. This could be used to track the current +buffer or position of point, for instance. If @var{context} is a +function, it will be called (with no arguments) to obtain the value to +be inserted into the trace output buffer. + +Finally, you may add explicit calls to @code{trace-values} to your +code, to log arbitrary values to the trace buffer at any time. + +@anchor{background and foreground tracing} +@cindex foreground tracing +@cindex background tracing +When using ``foreground'' tracing, the output buffer will be displayed +whenever a traced function is called. When using ``background'' +tracing the output buffer is not forcibly displayed. Because +foreground tracing affects the window configuration, it should not be +used to trace functions that switch buffers, or have other +display-oriented behaviour. To avoid such problems, all bulk tracing +commands use background tracing -- @code{trace-function-foreground} is +the only command providing foreground tracing. + +@menu +* Commands for Tracing:: Commands and variables. +* Restrictions on Tracing:: Limitations on what can be traced. +* Examples of Tracing:: Usage examples. +@end menu + +@node Commands for Tracing +@subsection Commands and variables for tracing functions + +@defopt trace-buffer +This variable defines the buffer where trace output will be logged to +by default. Trace commands can be passed a @var{buffer} argument to +specify a non-default output buffer. +@end defopt + +@defvar inhibit-trace +If this variable is non-@code{nil}, all tracing is temporarily +inhibited (including any calls to @code{trace-values}). +@end defvar + +@deffn Command trace-function-background function &optional buffer context +This function adds a background trace (@pxref{background and +foreground tracing}) to @var{function}. When called interactively, it +prompts for @var{function} in the minibuffer. With a prefix argument, +it also prompts for the trace output @var{buffer} (defaulting to the +value of @code{trace-buffer}), and a Lisp expression @var{context} +(@pxref{trace context}). + +If @var{function} is an autoload, the associated function will be +traced if and when it is defined. + +Calling @code{trace-function-background} for an already-traced +@var{function} will update the optional argument behaviours to respect +the new values (and change to background tracing, if foreground +tracing was previously used). +@end deffn + +@deffn Command trace-function-foreground function &optional buffer context +This function adds a foreground trace (@pxref{background and +foreground tracing}) to @var{function}. When called interactively, it +prompts for @var{function} in the minibuffer. With a prefix argument, +it also prompts for the trace output @var{buffer} (defaulting to the +value of @code{trace-buffer}), and a Lisp expression @var{context} +(@pxref{trace context}). + +If @var{function} is an autoload, the associated function will be +traced if and when it is defined. + +Calling @code{trace-function-foreground} for an already-traced +@var{function} will update the optional argument behaviours to respect +the new values (and change to foreground tracing, if background +tracing was previously used). +@end deffn + +@deffn Command trace-package prefix &optional buffer context after-load +This function calls @code{trace-function-background} for all functions +with names starting with @var{prefix}. + +For any autoload declarations matching @var{prefix}, the associated +function will be traced if and when it is defined. + +With a prefix argument, also prompt for the trace output @var{buffer} +(defaulting to the value of @code{trace-buffer}); a Lisp expression +@var{context} (@pxref{trace context}); and boolean query +@var{after-load}. If @var{after-load} is non-@code{nil} then +re-process @var{prefix} after loading any file. + +Calling @code{trace-package} again for the same @var{prefix} will +update the optional argument behaviours to respect the new values. +@end deffn + +@deffn Command trace-regexp regexp &optional buffer context after-load +This function calls @code{trace-function-background} for all functions +matching in @var{regexp}. + +Background tracing is used. Switch to the trace output buffer to view +the results. For any autoload declarations matching @var{regexp}, the +associated function will be traced if and when it is defined. + +With a prefix argument, also prompt for the trace output @var{buffer} +(defaulting to the value of @code{trace-buffer}); a Lisp expression +@var{context} (@pxref{trace context}); and boolean query +@var{after-load}. If @var{after-load} is non-@code{nil} then +re-process @var{regexp} after loading any file. + +Calling @code{trace-regexp} again for the same @var{regexp} will +update the optional argument behaviours to respect the new values. + +@strong{Warning:} Do not attempt to trace all functions. Tracing too +many functions at one time will render Emacs unusable. +@end deffn + +@deffn Command trace-library library &optional buffer context after-load +This function calls @code{trace-function-background} for all functions +currently defined in @var{library} according to @var{load-history}. + +For any autoload declarations with a file name matching @var{library}, +the associated function will be traced if and when it is defined. +(Autoload file names will not match if @var{library} specifies a +longer, more specific path.) + +With a prefix argument, also prompt for the trace output @var{buffer} +(defaulting to the value of @code{trace-buffer}); a Lisp expression +@var{context} (@pxref{trace context}); and boolean query +@var{after-load}. If @var{after-load} is non-@code{nil} then +re-process @var{library} after loading it, (ensuring that all of its +functions will be traced). + +Calling @code{trace-library} again for the same @var{library} will +update the optional argument behaviours to respect the new values. +@end deffn + +@deffn Command trace-currently-traced &optional display-message +This function returns the list of currently traced function symbols. +When called interactively, or if @var{display-message} is +non-@code{nil}, it displays the list as a message. +@end deffn + +@deffn Command untrace-function function +This function removes the trace on @var{function}. This has no effect +if @var{function} was not being traced. When called interactively, it +prompts for @var{function} in the minibuffer. +@end deffn + +@deffn Command untrace-package prefix +This function calls @code{untrace-function} for all functions with +names starting with @var{prefix}. When called interactively, it +prompts for @var{prefix} in the minibuffer. +@end deffn + +@deffn Command untrace-regexp regexp +This function calls @code{untrace-function} for all functions matching +@var{regexp}. When called interactively, it prompts for @var{regexp} +in the minibuffer. +@end deffn + +@deffn Command untrace-library library +This function calls @code{untrace-function} for all functions defined +in @var{library}. When called interactively, it prompts for +@var{library} in the minibuffer. +@end deffn + +@deffn Command untrace-all +This function calls @code{untrace-function} for all functions. +@end deffn + +@deffn Function trace-values &rest values +This function inserts a message showing @var{values} into the trace +buffer. You can add explicit calls to @code{trace-values} into your +functions in order to provide additional tracing information. +@end deffn + + +@node Restrictions on Tracing +@subsection Limitations on what can be traced + +@itemize @bullet +@item +Only functions/macros/subrs that are called via their function cell +will generate trace output; hence, you won't get trace output for: + +@itemize @bullet +@item +Macros that were expanded during compilation. + +@item +Subrs called directly from other subrs/C-code. + +@item +Byte-compiled calls to subrs that have special byte-codes associated +with them: + +@example +(sort (cl-loop for sym being the symbols + if (and (subrp (symbol-function sym)) + (plist-get (symbol-plist sym) + 'byte-opcode)) + collect sym) + (lambda (s1 s2) + (string< (symbol-name s1) (symbol-name s2)))) +@end example +@end itemize + +@item +Tracing too many functions at one time will render Emacs unusable. Do +not attempt to trace all functions, and take care with the arguments +passed to the bulk tracing commands @code{trace-package} and +@code{trace-regexp}. + +@item +Foreground tracing should not be used to trace functions that switch +buffers, or have other display-oriented behaviour. + +@item +Each function can only be subject to a single trace. When a function +which is already being traced is targeted by any tracing command, the +new trace criteria (including optional argument values) will replace +the previous trace criteria for that function. + +Note that this also means there is no need to un-trace a function in +order to re-trace it with different arguments. + +@item +All the restrictions that apply to @file{nadvice.el} also apply to +tracing (as tracing is implemented using advice). @xref{Advising +Functions}. +@end itemize + +@node Examples of Tracing +@subsection Usage examples for function tracing + +The following is example trace output, including a context list +expression, for a function which also makes a call to +@code{trace-values}. The left hand column indicates the evaluation +depth of the function call. + +@example +@group +1 -> (funcname arg1 arg2) [(context1 context2)] +1 -> (trace-values value1 value2) +1 <- funcname: return [(context1 context2)] +@end group +@end example + +The trace output display of recursion/nesting levels can be +demonstrated by tracing a recursive function, such as a simplistic +factorial implementation: + +@example +@group +(defun fact (n) + "Calculate factorial of N." + (if (eql n 0) 1 + (* n (fact (1- n))))) + @result{} fact + +(trace-function 'fact) + @result{} fact + +Now, evaluating this... + +(fact 4) + @result{} 24 + +...will generate the following in *trace-buffer*: + +1 -> fact: n=4 +| 2 -> fact: n=3 +| | 3 -> fact: n=2 +| | | 4 -> fact: n=1 +| | | | 5 -> fact: n=0 +| | | | 5 <- fact: 1 +| | | 4 <- fact: 1 +| | 3 <- fact: 2 +| 2 <- fact: 6 +1 <- fact: 24 +@end group + +Try the following for some more interesting trace output: + +@group +(defun ack (x y z) + (if (= x 0) + (+ y z) + (if (and (<= x 2) (= z 0)) + (1- x) + (if (and (> x 2) (= z 0)) + y + (ack (1- x) y (ack x y (1- z))))))) + +(trace-function 'ack) + +(ack 3 3 1) +@end group +@end example diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi index a3d1d80408..64f31cdf3d 100644 --- a/doc/lispref/elisp.texi +++ b/doc/lispref/elisp.texi @@ -669,6 +669,7 @@ Top * Syntax Errors:: How to find syntax errors. * Test Coverage:: Ensuring you have tested all branches in your code. * Profiling:: Measuring the resources that your code uses. +* Tracing:: Log function calls, arguments, and return values. The Lisp Debugger diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi index e94093318f..ad2b175afb 100644 --- a/doc/lispref/modes.texi +++ b/doc/lispref/modes.texi @@ -2507,15 +2507,13 @@ %-Constructs @item %% The character @samp{%}---this is how to include a literal @samp{%} in a string in which @code{%}-constructs are allowed. -@end table - -The following @code{%}-construct is still supported, but it is -obsolete, since you can get the same result using the variable -@code{mode-name}. -@table @code @item %m -The value of @code{mode-name}. +Obsolete; use the @code{mode-name} variable instead. The @code{%m} +construct is still supported, but it is inadequate, as it produces an +empty string if the value of the @code{mode-name} variable is a +non-string mode-line construct (for example, in +@code{emacs-lisp-mode}). @end table @node Properties in Mode diff --git a/etc/NEWS b/etc/NEWS index 57845df979..9728edc303 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1918,6 +1918,16 @@ The newly created buffer will be displayed via 'display-buffer', which can be customized through the usual mechanism of 'display-buffer-alist' and friends. +** Trace + ++++ +*** New commands 'trace-package', 'trace-regexp', and 'trace-library' +(and their counterparts 'untrace-package', 'untrace-regexp', and +'untrace-library') allow for the bulk tracing of calls to functions +with names matching a specified prefix or regexp, or functions defined +by a specified file. New command 'trace-currently-traced' lists the +traced function symbols. + ** Tramp --- diff --git a/lisp/emacs-lisp/trace.el b/lisp/emacs-lisp/trace.el index c2f6c16226..a4fcfd50b0 100644 --- a/lisp/emacs-lisp/trace.el +++ b/lisp/emacs-lisp/trace.el @@ -40,8 +40,6 @@ ;; Restrictions: ;; ============= -;; - Traced subrs when called interactively will always show nil as the -;; value of their arguments. ;; - Only functions/macros/subrs that are called via their function cell will ;; generate trace output, hence, you won't get trace output for: ;; + Subrs called directly from other subrs/C-code @@ -52,14 +50,28 @@ ;; Usage: ;; ====== -;; - To trace a function say `M-x trace-function', which will ask you for the +;; - To trace a function use `M-x trace-function', which will ask you for the ;; name of the function/subr/macro to trace. ;; - If you want to trace a function that switches buffers or does other ;; display oriented stuff use `M-x trace-function-background', which will ;; generate the trace output silently in the background without popping ;; up windows and doing other irritating stuff. -;; - To untrace a function say `M-x untrace-function'. -;; - To untrace all currently traced functions say `M-x untrace-all'. +;; - `M-x trace-package' will ask you for a function name prefix, and trace +;; (in the background) all matching functions. +;; - `M-x trace-regexp' will ask you for a function name pattern (regexp), +;; and trace (in the background) all matching functions. +;; - `M-x trace-library' will ask you for a library name, and trace (in the +;; background) all functions defined by that file. +;; - Interactively in all cases, a prefix argument can be used to prompt +;; for the output buffer and context arguments and, for bulk tracing +;; commands, whether or not the traces should be automatically updated +;; after loading lisp files. +;; - To untrace a function use `M-x untrace-function'. +;; - To untrace multiple functions by prefix use `M-x untrace-package'. +;; - To untrace multiple functions by regexp use `M-x untrace-regexp'. +;; - To untrace multiple functions by file use `M-x untrace-library'. +;; - To untrace all currently traced functions use `M-x untrace-all'. +;; - To list all currently traced functions use `M-x trace-currently-traced'. ;; Examples: ;; ========= @@ -120,6 +132,23 @@ ;;; Change Log: +;; 2017-06-17 Phil Sainty +;; * New commands `trace-package', `untrace-package', `trace-regexp', +;; `untrace-regexp', `trace-library', `untrace-library'. +;; * Documentation added to the elisp reference manual. +;; +;; 2012-2014 Stefan Monnier, Glenn Morris +;; * Adapted for nadvice.el +;; * New `context' argument and display in trace buffer +;; * `trace-function' renamed to (and now an alias of) +;; `trace-function-foreground' +;; +;; 2005-02-27 Stefan Monnier +;; * New `inhibit-trace' variable +;; +;; 1998-04-05 Stephen Eglen +;; * New customize group `trace' +;; ;; Revision 2.0 1993/05/18 00:41:16 hans ;; * Adapted for advice.el 2.0; it now also works ;; for GNU Emacs-19 and Lemacs @@ -134,6 +163,8 @@ ;;; Code: +(eval-when-compile (require 'cl-macs)) + (defgroup trace nil "Tracing facility for Emacs Lisp functions." :prefix "trace-" @@ -181,7 +212,7 @@ trace-entry-message ;; FIXME: Make it so we can click the function name to jump to its ;; definition and/or untrace it. (cons function args) - context))) + (if context (format " [%s]" context) "")))) (defun trace-exit-message (function level value context) "Generate a string that describes that FUNCTION has exited. @@ -197,7 +228,7 @@ trace-exit-message function ;; Do this so we'll see strings: value - context))) + (if context (format " [%s]" context) "")))) (defvar trace--timer nil) @@ -218,8 +249,14 @@ trace-make-advice FUNCTION is the name of the traced function. BUFFER is the buffer where the trace should be printed. BACKGROUND if nil means to display BUFFER. -CONTEXT if non-nil should be a function that returns extra info that should -be printed along with the arguments in the trace." +CONTEXT, if non-nil, should be either a function or an expression +that returns extra info, which will be printed after the +arguments or return value in the trace." + (setq context (if context + (if (functionp context) + context + (trace-make-context context)) + (lambda () ""))) (lambda (body &rest args) (let ((trace-level (1+ trace-level)) (trace-buffer (get-buffer-create buffer)) @@ -227,6 +264,7 @@ trace-make-advice (ctx (funcall context))) (unless inhibit-trace (with-current-buffer trace-buffer + (setq-local page-delimiter (format "^%s" (regexp-quote trace-separator))) (setq-local window-point-insertion-type t) (unless background (trace--display-buffer trace-buffer)) (goto-char (point-max)) @@ -255,41 +293,70 @@ trace-function-internal "Add trace advice for FUNCTION." (advice-add function :around - (trace-make-advice function (or buffer trace-buffer) background - (or context (lambda () ""))) + (trace-make-advice function (or buffer trace-buffer) background context) `((name . ,trace-advice-name) (depth . -100)))) -(defun trace-is-traced (function) +(defun trace-is-traceable-p (sym) + "Whether the given symbol is a traceable function. +Autoloaded functions are traceable." + (or (functionp sym) (macrop sym))) + +(defun trace-is-traced-p (function) + "Whether FUNCTION is currently traced." (advice-member-p trace-advice-name function)) -(defun trace--read-args (prompt) - "Read a function name, prompting with string PROMPT. -If `current-prefix-arg' is non-nil, also read a buffer and a \"context\" -\(Lisp expression). Return (FUNCTION BUFFER FUNCTION-CONTEXT)." - (cons - (let ((default (function-called-at-point))) - (intern (completing-read (format-prompt prompt default) - obarray 'fboundp t nil nil - (if default (symbol-name default))))) - (when current-prefix-arg - (list - (read-buffer "Output to buffer" trace-buffer) - (let ((exp - (read-from-minibuffer "Context expression: " - nil read-expression-map t - 'read-expression-history))) - (lambda () - (let ((print-circle t) - (print-escape-newlines t)) - (concat " [" (prin1-to-string (eval exp t)) "]")))))))) +(define-obsolete-function-alias 'trace-is-traced 'trace-is-traced-p "29.1") + +(defun trace-currently-traced (&optional display-message) + "Return the list of currently traced function symbols. +Interactively, display the list as a message." + (interactive "p") + (let ((tracelist (cl-loop for sym being the symbols + if (trace-is-traced-p sym) + collect sym))) + (when display-message + (message "%S" tracelist)) + tracelist)) + +(defun trace--read-function (prompt) + "Read a function name, prompting with string PROMPT." + (let ((default (function-called-at-point))) + (intern (completing-read (format-prompt prompt default) + obarray 'trace-is-traceable-p t nil nil + (if default (symbol-name default)))))) + +(defun trace--read-library (&optional prompt) + "Read a library name, prompting with string PROMPT." + (completing-read + (or prompt "Library: ") + (apply-partially 'locate-file-completion-table + load-path (get-load-suffixes)))) + +(defun trace--read-extra-args () + "Read a buffer and a \"context\" (Lisp expression). +Return (BUFFER CONTEXT)." + (list + (read-buffer "Output to buffer" trace-buffer) + (when-let ((exp (read-from-minibuffer + "Context expression: " + nil read-expression-map t + 'read-expression-history "nil"))) + (trace-make-context exp)))) + +(defun trace-make-context (exp) + "Return a context function for expression EXP." + (lambda () + (let ((print-circle t) + (print-escape-newlines t)) + (prin1-to-string (eval exp t))))) ;;;###autoload (defun trace-function-foreground (function &optional buffer context) "Trace calls to function FUNCTION. -With a prefix argument, also prompt for the trace buffer (default -`trace-buffer'), and a Lisp expression CONTEXT. When called from -Lisp, CONTEXT should be a function of no arguments which returns -a value to insert into BUFFER during the trace. +With a prefix argument, also prompt for the trace output BUFFER +\(default `trace-buffer'), and a Lisp expression CONTEXT. +When called from Lisp, CONTEXT should be a function of no arguments +which returns a value to insert into BUFFER during the trace. Tracing a function causes every call to that function to insert into BUFFER Lisp-style trace messages that display the function's @@ -302,8 +369,14 @@ trace-function-foreground functions that switch buffers, or do any other display-oriented stuff - use `trace-function-background' instead. +Calling `trace-function-foreground' again for the same FUNCTION +will update the optional argument behaviours to respect the new +values. + To stop tracing a function, use `untrace-function' or `untrace-all'." - (interactive (trace--read-args "Trace function")) + (interactive + (cons (trace--read-function "Trace function") + (and current-prefix-arg (trace--read-extra-args)))) (trace-function-internal function buffer nil context)) ;;;###autoload @@ -311,26 +384,290 @@ trace-function-background "Trace calls to function FUNCTION, quietly. This is like `trace-function-foreground', but without popping up the output buffer or changing the window configuration." - (interactive (trace--read-args "Trace function in background")) + (interactive + (cons (trace--read-function "Trace function in background") + (and current-prefix-arg (trace--read-extra-args)))) (trace-function-internal function buffer t context)) ;;;###autoload (defalias 'trace-function 'trace-function-foreground) (defun untrace-function (function) - "Untraces FUNCTION and possibly activates all remaining advice. -Activation is performed with `ad-update', hence remaining advice will get -activated only if the advice of FUNCTION is currently active. If FUNCTION -was not traced this is a noop." + "Remove trace from FUNCTION. If FUNCTION was not traced this is a noop." (interactive (list (intern (completing-read "Untrace function: " - obarray #'trace-is-traced t)))) + obarray #'trace-is-traced-p t)))) (advice-remove function trace-advice-name)) +;;;###autoload +(defun trace-package (prefix &optional buffer context after-load) + "Trace all functions with names starting with PREFIX. +For example, to trace all diff functions, do the following: + +\\[trace-package] RET diff- RET + +Background tracing is used. Switch to the trace output buffer to +view the results. For any autoload declarations matching PREFIX, +the associated function will be traced if and when it is defined. + +With a prefix argument, also prompt for the optional arguments. +If AFTER-LOAD is non-nil then re-process PREFIX after loading any +file. See `trace-function-foreground' for details of BUFFER and +CONTEXT, and of foreground vs background tracing. + +Calling `trace-package' again for the same PREFIX will update the +optional argument behaviours to respect the new values. + +See also `untrace-package'." + ;; Derived in part from `elp-instrument-package'. + (interactive + (cons (completing-read "Prefix of package to trace: " + obarray #'trace-is-traceable-p) + (and current-prefix-arg + (nconc (trace--read-extra-args) + (list (y-or-n-p "Update traces after loading files?")))))) + (when (zerop (length prefix)) + (error "Tracing all Emacs functions would render Emacs unusable")) + (mapc (lambda (name) + (trace-function-background (intern name) buffer context)) + (all-completions prefix obarray #'trace-is-traceable-p)) + (message + "Tracing to %s. Use %s to untrace a package, or %s to remove all traces." + (or buffer trace-buffer) + (substitute-command-keys "\\[untrace-package]") + (substitute-command-keys "\\[untrace-all]")) + ;; Handle `after-load' argument. + (when after-load + (trace--after-load 'prefix prefix buffer context))) + +(defun untrace-package (prefix) + "Remove all traces from functions with names starting with PREFIX. + +See also `trace-package'." + (interactive + (list (completing-read "Prefix of package to untrace: " + obarray #'trace-is-traced-p))) + (if (and (zerop (length prefix)) + (y-or-n-p "Remove all function traces?")) + (untrace-all) + (mapc (lambda (name) + (untrace-function (intern name))) + (all-completions prefix obarray #'trace-is-traced-p))) + ;; Remove any `after-load' behaviour. + (trace--remove-after-load 'prefix prefix)) + +;;;###autoload +(defun trace-regexp (regexp &optional buffer context after-load) + "Trace all functions with names matching REGEXP. +For example, to trace indentation-related functions, you could try: + +\\[trace-regexp] RET indent\\|offset RET + +Warning: Do not attempt to trace all functions. Tracing too many +functions at one time will render Emacs unusable. + +Background tracing is used. Switch to the trace output buffer to +view the results. For any autoload declarations matching REGEXP, +the associated function will be traced if and when it is defined. + +With a prefix argument, also prompt for the optional arguments. +If AFTER-LOAD is non-nil then re-process REGEXP after loading any +file. See `trace-function-foreground' for details of BUFFER and +CONTEXT, and of foreground vs background tracing. + +Calling `trace-regexp' again for the same REGEXP will update the +optional argument behaviours to respect the new values. + +See also `untrace-regexp'." + (interactive + (cons (read-regexp "Regexp matching functions to trace: ") + (and current-prefix-arg + (nconc (trace--read-extra-args) + (list (y-or-n-p "Update traces after loading files?")))))) + (when (member regexp '("" "." ".+" ".*")) + ;; Not comprehensive, but it catches the most likely attempts. + (error "Tracing all Emacs functions would render Emacs unusable")) + (mapatoms + (lambda (sym) + (and (trace-is-traceable-p sym) + (string-match-p regexp (symbol-name sym)) + (trace-function-background sym buffer context)))) + (message + "Tracing to %s. Use %s to untrace by regexp, or %s to remove all traces." + (or buffer trace-buffer) + (substitute-command-keys "\\[untrace-regexp]") + (substitute-command-keys "\\[untrace-all]")) + ;; Handle `after-load' argument. + (when after-load + (trace--after-load 'regexp regexp buffer context))) + +(defun untrace-regexp (regexp) + "Remove all traces from functions with names matching REGEXP. + +See also `trace-regexp'." + (interactive + (list (read-regexp "Regexp matching functions to untrace: "))) + (if (and (zerop (length regexp)) + (y-or-n-p "Remove all function traces?")) + (untrace-all) + (mapatoms + (lambda (sym) + (and (trace-is-traced-p sym) + (string-match-p regexp (symbol-name sym)) + (untrace-function sym))))) + ;; Remove any `after-load' behaviour. + (trace--remove-after-load 'regexp regexp)) + +;;;###autoload +(defun trace-library (library &optional buffer context after-load) + "Trace functions defined by LIBRARY. +For example, to trace tramp.el functions, you could use: + +\\[trace-library] RET tramp RET + +Background tracing is used. Switch to the trace output buffer to +view the results. For any autoload declarations with a file name +matching LIBRARY, the associated function will be traced if and +when it is defined. (Autoload file names will not match if LIBRARY +specifies a longer, more specific path.) + +With a prefix argument, also prompt for the optional arguments. +If AFTER-LOAD is non-nil then re-process LIBRARY after loading it +\(ensuring that all of its functions will be traced). See +`trace-function-foreground' for details of BUFFER and CONTEXT, +and of foreground vs background tracing. + +Calling `trace-library' again for the same LIBRARY will update the +optional argument behaviours to respect the new values. + +See also `untrace-library'." + (interactive + (cons (trace--read-library) + (and current-prefix-arg + (nconc (trace--read-extra-args) + (list (y-or-n-p "Update traces after loading this library?")))))) + ;; Build list of library functions and autoloads. + (let ((defs (nconc (trace--library-defuns library) + (trace--library-autoloads library)))) + ;; Trace each of those definitions. + (mapc (lambda (func) + (trace-function-background func buffer context)) + defs)) + ;; Handle `after-load' argument. + (when after-load + (trace--after-load 'library library buffer context))) + +(defun trace--library-defuns (library) + "Returns a list of loaded function definitions associated with LIBRARY." + (delq nil (mapcar (lambda (x) + (and (consp x) + (eq (car x) 'defun) + (cdr x))) + (cdr (load-history-filename-element + (load-history-regexp library)))))) + +(defun trace--library-autoloads (library) + "Returns a list of all current autoloads associated with LIBRARY. + +Autoload file names will not match if LIBRARY specifies a longer, +more specific path than that of the autoload declaration itself." + (let* ((functions nil) + (filepattern (load-history-regexp library)) + (predicate (apply-partially 'trace--library-provides-autoload-p + filepattern))) + (mapatoms (lambda (sym) + (when (funcall predicate sym) + (push sym functions)))) + functions)) + +(defun trace--library-provides-autoload-p (filepattern sym) + "Whether symbol SYM is an autoload associated with FILEPATTERN. + +FILEPATTERN should be the result of calling `load-history-regexp'." + (when (fboundp sym) + (let ((f (symbol-function sym))) + (and (autoloadp f) + (string-match filepattern (cadr f)))))) + +(defun untrace-library (library) + "Remove all traces from functions defined by LIBRARY. + +See also `trace-library'." + (interactive (list (trace--read-library))) + ;; Remove traces from known LIBRARY defuns. + ;; (Also process autoloads, in case LIBRARY is unloaded.) + (let ((defs (nconc (trace--library-defuns library) + (trace--library-autoloads library)))) + (mapc (lambda (func) + (when (trace-is-traced-p func) + (untrace-function func))) + defs)) + ;; Remove any `after-load' behaviour. + (trace--remove-after-load 'library library)) + +(defvar trace--after-load-alist nil + "List of trace types to update after loading. + +Each list item has the form ((TYPE . VALUE) BUFFER CONTEXT), +where TYPE is one of the symbols `prefix', `regexp', or `library'; +and VALUE is the respective first argument to `trace-package', +`trace-regexp', or `trace-library'; with BUFFER and CONTEXT being +the values of those arguments as they were passed to the same +function.") + +(defun trace--after-load (type value &optional buffer context) + "Arrange to update traces after libraries are loaded. + +TYPE is one of the symbols `prefix', `regexp', or `library'; +VALUE is the respective first argument to `trace-package', +`trace-regexp', or `trace-library'; and BUFFER and CONTEXT are +the values of those arguments as they were passed to the same +function. + +Adds `trace--after-load-function' to `after-load-functions'." + ;; Remove any existing spec for this (TYPE VALUE) key. + (trace--remove-after-load type value) + ;; Add the new spec. + (push (list (cons type value) buffer context) + trace--after-load-alist) + ;; Arrange to call `trace--after-load-function'. + (add-hook 'after-load-functions #'trace--after-load-function)) + +(defun trace--after-load-function (file) + "React to FILE being loaded. Callback for `after-load-functions'. + +See also `trace--after-load'." + (dolist (spec trace--after-load-alist) + (cl-destructuring-bind ((type . value) buffer context) + spec + (cl-case type + (prefix (trace-package value nil buffer context)) + (regexp (trace-regexp value nil buffer context)) + (library (when (string-match (load-history-regexp value) file) + (trace-library value nil buffer context))))))) + +(defun trace--remove-after-load (type value) + "Remove any (TYPE . VALUE) entry from `trace--after-load-alist'. + +Remove `trace--after-load-function' from `after-load-functions' +if it is no longer needed." + (setq trace--after-load-alist + (cl-delete (cons type value) trace--after-load-alist + :key #'car :test #'equal)) + (unless trace--after-load-alist + (remove-hook 'after-load-functions #'trace--after-load-function))) + +(defun trace--remove-after-load-all () + "Reset `trace--after-load-alist'. +Remove `trace--after-load-function' from `after-load-functions'" + (setq trace--after-load-alist nil) + (remove-hook 'after-load-functions #'trace--after-load-function)) + (defun untrace-all () - "Untraces all currently traced functions." + "Remove traces from all currently traced functions." (interactive) - (mapatoms #'untrace-function)) + (mapatoms #'untrace-function) + (trace--remove-after-load-all)) (provide 'trace) ^ permalink raw reply related [flat|nested] 40+ messages in thread
* bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2022-09-11 11:49 ` Lars Ingebrigtsen @ 2022-09-11 12:33 ` Eli Zaretskii 2022-09-12 13:26 ` Michael Albinus 1 sibling, 0 replies; 40+ messages in thread From: Eli Zaretskii @ 2022-09-11 12:33 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: psainty, 1343, 27397, michael.albinus, dgutov > Cc: 1343@debbugs.gnu.org, 27397@debbugs.gnu.org, > Michael Albinus <michael.albinus@gmx.de>, Dmitry Gutov <dgutov@yandex.ru> > From: Lars Ingebrigtsen <larsi@gnus.org> > Date: Sun, 11 Sep 2022 13:49:51 +0200 > > Phil Sainty <psainty@orcon.net.nz> writes: > > > After a brief hiatus, I've resumed (maybe completed) my work on this. > > > > The branch scratch/bulk-tracing contains the updated code for Emacs 29 > > (rebased over master). > > For reference, I've included the diff between master and the branch > below. > > I have not tried the patch myself -- does anybody have any comments > here? It seems like useful functionality to me. Yes, it is useful. The documentation uses UK English spelling ("behaviour"). (Perhaps today this is justified, but since a few days will have passed until this is landed, I think we should use US English conventions nonetheless.) I see lack of important detail where stuff like 'context' and 'trace-value' is described: at the very least the manual should say whether the text will be inserted verbatim, or with something like newlines and/or other decorations added. IMO, the description of trace-value should be before all the untrace-* functions. Why isn't trace-is-traceable-p documented in the manual? It sounds relevant to the discussion of the restrictions. I think the description should be updated fore native-compilation (it only mentions byte-compilation). The hunk from modes.texi seems unrelated. The NEWS entry should have a single-line heading that is a full sentence ending with a period. Please verify that doc strings of functions mention all the arguments. (E.g., trace-currently-traced doesn't.) Thanks. ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2022-09-11 11:49 ` Lars Ingebrigtsen 2022-09-11 12:33 ` Eli Zaretskii @ 2022-09-12 13:26 ` Michael Albinus 2022-09-12 22:17 ` bug#1343: " Phil Sainty 2022-09-13 11:11 ` Lars Ingebrigtsen 1 sibling, 2 replies; 40+ messages in thread From: Michael Albinus @ 2022-09-12 13:26 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: Phil Sainty, 1343, 27397, Dmitry Gutov Lars Ingebrigtsen <larsi@gnus.org> writes: Hi, > I have not tried the patch myself -- does anybody have any comments > here? It seems like useful functionality to me. Yes, and I also agree with the comments of Eli. One additional point: > +@deffn Command trace-package prefix &optional buffer context after-load > +This function calls @code{trace-function-background} for all functions > +with names starting with @var{prefix}. Tramp does something like this already. However, not all Tramp functions shall be traced. For example, there is `tramp-read-passwd', which returns the password retireved from auth-sources, or interactively. Obviously, it shouldn't be traced when asking users for a Tramp trace. There are also chatty functions, which don't need to be traces, like `tramp-message' and friends. They add serious noise to the trace, w/o additional information. For that reason, Tramp marks such functions with a function property, which is to be taken into account when activating traces for all Tramp functions. Like this: --8<---------------cut here---------------start------------->8--- (put #'tramp-read-passwd 'tramp-suppress-trace t) --8<---------------cut here---------------end--------------->8--- I would appreciate if `trace-package' and `trace-library' would know a similar mechanism. It could be a function declaration in the function definition, like --8<---------------cut here---------------start------------->8--- (declare (debug t) inhibit-trace ...) --8<---------------cut here---------------end--------------->8--- Or, dynamically, the global variable inhibit-trace could keep a value of such functions suppressed for trace. Of course there is `untrace-function', but it is inconvenient to teach users calling all of them. And if they forget it, their password will be exposed (for example). > +;;;###autoload > +(defun trace-library (library &optional buffer context after-load) > + "Trace functions defined by LIBRARY. > +For example, to trace tramp.el functions, you could use: > + > +\\[trace-library] RET tramp RET I wouldn't use `tramp' as example for `trace-library'. Tramp consists of a dozen of libraries, and several of them must be activated in order to get useful traces. So as example, another library might be better suited. Best regards, Michael. ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2022-09-12 13:26 ` Michael Albinus @ 2022-09-12 22:17 ` Phil Sainty 2022-09-13 7:34 ` bug#27397: " Michael Albinus 2022-09-13 11:11 ` Lars Ingebrigtsen 1 sibling, 1 reply; 40+ messages in thread From: Phil Sainty @ 2022-09-12 22:17 UTC (permalink / raw) To: Michael Albinus; +Cc: 1343, Lars Ingebrigtsen, 27397, Dmitry Gutov On 2022-09-13 01:26, Michael Albinus wrote: > There are also chatty functions, which ... add serious noise > > For that reason, Tramp marks such functions with a function property, > > Or, dynamically, the global variable inhibit-trace could keep a value > of such functions suppressed for trace. Oddly enough, this had also occurred to me yesterday as a useful improvement. I was thinking of a global list of functions not to be traced, but your symbol property approach seems like a good idea as well. Perhaps "both" is good. For symbol properties I think there would need to be a variable to control whether or not that was respected, to make it relatively easy to force them to be traced regardless. > I wouldn't use `tramp' as example for `trace-library'. No problem. I think the example dates back to the info on tracing which used to appear in (info "(tramp) Traces and Profiles") but I'd noticed that had been deleted when I was rebasing this work. -Phil ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2022-09-12 22:17 ` bug#1343: " Phil Sainty @ 2022-09-13 7:34 ` Michael Albinus 0 siblings, 0 replies; 40+ messages in thread From: Michael Albinus @ 2022-09-13 7:34 UTC (permalink / raw) To: Phil Sainty; +Cc: 1343, Lars Ingebrigtsen, 27397, Dmitry Gutov Phil Sainty <psainty@orcon.net.nz> writes: Hi Phil, >> There are also chatty functions, which ... add serious noise >> For that reason, Tramp marks such functions with a function >> property, >> Or, dynamically, the global variable inhibit-trace could keep a >> value >> of such functions suppressed for trace. > > Oddly enough, this had also occurred to me yesterday as a useful > improvement. So it must be a good idea :-) > No problem. I think the example dates back to the info on tracing > which used to appear in (info "(tramp) Traces and Profiles") but > I'd noticed that had been deleted when I was rebasing this work. Yep. After I had recognized that the instructions in the Tramp manual aren't robust enough, I've moved this into Tramp core. It can be activated now with a proper tramp-verbose. > -Phil Best regards, Michael. ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2022-09-12 13:26 ` Michael Albinus 2022-09-12 22:17 ` bug#1343: " Phil Sainty @ 2022-09-13 11:11 ` Lars Ingebrigtsen 1 sibling, 0 replies; 40+ messages in thread From: Lars Ingebrigtsen @ 2022-09-13 11:11 UTC (permalink / raw) To: Michael Albinus; +Cc: Phil Sainty, 1343, 27397, Dmitry Gutov Michael Albinus <michael.albinus@gmx.de> writes: > I would appreciate if `trace-package' and `trace-library' would know a > similar mechanism. It could be a function declaration in the function > definition, like > > (declare (debug t) inhibit-trace ...) Makes sense to me. ^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#27397: [PATCH] New commands for bulk tracing of elisp functions 2017-06-17 9:20 ` Phil Sainty 2017-06-17 12:31 ` Phil Sainty @ 2017-06-17 23:03 ` Dmitry Gutov 1 sibling, 0 replies; 40+ messages in thread From: Dmitry Gutov @ 2017-06-17 23:03 UTC (permalink / raw) To: Phil Sainty, 27397 On 6/17/17 12:20 PM, Phil Sainty wrote: > Actually popping up that buffer when > the `trace-package' or `trace-regexp' command is used would be another > (perhaps nicer) option? Perhaps. > Do others think that foreground versions of these commands would be > a good idea? I'm fine with having only the background versions, but others are welcome to weigh in. > A prefix argument could mean "use foreground tracing" Nah, if we do want foreground tracing, adding separate commands shouldn't be a problem. ^ permalink raw reply [flat|nested] 40+ messages in thread
end of thread, other threads:[~2022-09-13 11:11 UTC | newest] Thread overview: 40+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2017-06-16 13:32 bug#27397: [PATCH] New commands for bulk tracing of elisp functions Phil Sainty 2017-06-16 14:58 ` Dmitry Gutov 2017-06-17 8:43 ` Phil Sainty 2017-06-17 9:13 ` Dmitry Gutov 2017-06-19 7:45 ` Michael Albinus 2017-06-19 9:35 ` Phil Sainty 2017-06-19 9:56 ` Michael Albinus 2017-06-19 11:00 ` Phil Sainty 2017-06-19 12:05 ` Michael Albinus 2017-06-19 12:17 ` Phil Sainty 2017-06-19 12:50 ` Dmitry Gutov 2017-06-19 13:07 ` Michael Albinus 2017-06-19 11:27 ` Dmitry Gutov 2017-06-19 11:36 ` Michael Albinus 2017-06-19 12:04 ` Dmitry Gutov 2017-06-19 12:08 ` Michael Albinus 2017-06-19 12:24 ` Phil Sainty 2017-06-16 15:43 ` Kaushal Modi 2017-06-17 8:48 ` Phil Sainty 2017-06-17 9:20 ` Phil Sainty 2017-06-17 12:31 ` Phil Sainty 2017-06-17 22:59 ` Dmitry Gutov 2017-06-18 1:06 ` Phil Sainty 2017-06-18 6:32 ` Dmitry Gutov 2017-06-18 11:22 ` Phil Sainty 2019-06-27 18:01 ` bug#1343: " Lars Ingebrigtsen 2019-06-28 11:25 ` bug#27397: " Phil Sainty 2021-04-17 5:51 ` bug#1343: [PATCH] trace package Stefan Kangas 2021-04-17 14:01 ` bug#27397: " Phil Sainty 2021-10-21 20:29 ` Stefan Kangas 2022-07-15 4:08 ` bug#27397: bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp functions Phil Sainty 2022-07-15 6:23 ` Eli Zaretskii 2022-07-15 11:09 ` Phil Sainty 2022-09-11 11:49 ` Lars Ingebrigtsen 2022-09-11 12:33 ` Eli Zaretskii 2022-09-12 13:26 ` Michael Albinus 2022-09-12 22:17 ` bug#1343: " Phil Sainty 2022-09-13 7:34 ` bug#27397: " Michael Albinus 2022-09-13 11:11 ` Lars Ingebrigtsen 2017-06-17 23:03 ` Dmitry Gutov
Code repositories for project(s) associated with this public inbox https://git.savannah.gnu.org/cgit/emacs.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).