[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp func
From: |
Lars Ingebrigtsen |
Subject: |
bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp functions |
Date: |
Sun, 11 Sep 2022 13:49:51 +0200 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/29.0.50 (gnu/linux) |
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)
- bug#1343: bug#27397: [PATCH] New commands for bulk tracing of elisp functions,
Lars Ingebrigtsen <=