aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorNoam Postavsky2017-06-29 19:42:32 -0400
committerNoam Postavsky2017-06-29 19:42:32 -0400
commit169532b0ebc3acb0b1c943d0b3d8b569cd57ca4b (patch)
tree60d7ae515b92944d1e90772df3ae5dac05b5c715 /lisp
parent138447c3abd749d1c27d99d7089b1b0903352ade (diff)
parentc87c87fcc361494815bbd1d92f450b0b80a3ecbb (diff)
downloademacs-169532b0ebc3acb0b1c943d0b3d8b569cd57ca4b.tar.gz
emacs-169532b0ebc3acb0b1c943d0b3d8b569cd57ca4b.zip
; Merge: Backtrace printing improvements (Bug#6991)
Diffstat (limited to 'lisp')
-rw-r--r--lisp/emacs-lisp/cl-print.el9
-rw-r--r--lisp/emacs-lisp/debug.el181
-rw-r--r--lisp/emacs-lisp/ert.el85
-rw-r--r--lisp/select.el3
-rw-r--r--lisp/subr.el3
-rw-r--r--lisp/term/w32-win.el2
6 files changed, 113 insertions, 170 deletions
diff --git a/lisp/emacs-lisp/cl-print.el b/lisp/emacs-lisp/cl-print.el
index 89a71d1b6c5..824d0b7b4f5 100644
--- a/lisp/emacs-lisp/cl-print.el
+++ b/lisp/emacs-lisp/cl-print.el
@@ -105,10 +105,11 @@ into a button whose action shows the function's disassembly.")
105 (if args 105 (if args
106 (prin1 args stream) 106 (prin1 args stream)
107 (princ "()" stream))) 107 (princ "()" stream)))
108 (let ((doc (documentation object 'raw))) 108 (pcase (help-split-fundoc (documentation object 'raw) object)
109 (when doc 109 ;; Drop args which `help-function-arglist' already printed.
110 (princ " " stream) 110 (`(,_usage . ,(and doc (guard (stringp doc))))
111 (prin1 doc stream))) 111 (princ " " stream)
112 (prin1 doc stream)))
112 (let ((inter (interactive-form object))) 113 (let ((inter (interactive-form object)))
113 (when inter 114 (when inter
114 (princ " " stream) 115 (princ " " stream)
diff --git a/lisp/emacs-lisp/debug.el b/lisp/emacs-lisp/debug.el
index 83456fc31a2..726005af9b1 100644
--- a/lisp/emacs-lisp/debug.el
+++ b/lisp/emacs-lisp/debug.el
@@ -49,6 +49,12 @@ the middle is discarded, and just the beginning and end are displayed."
49 :group 'debugger 49 :group 'debugger
50 :version "21.1") 50 :version "21.1")
51 51
52(defcustom debugger-print-function #'cl-prin1
53 "Function used to print values in the debugger backtraces."
54 :type 'function
55 :options '(cl-prin1 prin1)
56 :version "26.1")
57
52(defcustom debugger-bury-or-kill 'bury 58(defcustom debugger-bury-or-kill 'bury
53 "What to do with the debugger buffer when exiting `debug'. 59 "What to do with the debugger buffer when exiting `debug'.
54The value affects the behavior of operations on any window 60The value affects the behavior of operations on any window
@@ -264,6 +270,43 @@ first will be printed into the backtrace buffer."
264 (setq debug-on-next-call debugger-step-after-exit) 270 (setq debug-on-next-call debugger-step-after-exit)
265 debugger-value))) 271 debugger-value)))
266 272
273
274(defvar cl-print-compiled-button)
275
276(defun debugger-insert-backtrace (frames do-xrefs)
277 "Format and insert the backtrace FRAMES at point.
278Make functions into cross-reference buttons if DO-XREFS is non-nil."
279 (let ((standard-output (current-buffer))
280 (cl-print-compiled-button t)
281 (eval-buffers eval-buffer-list))
282 (require 'help-mode) ; Define `help-function-def' button type.
283 (pcase-dolist (`(,evald ,fun ,args ,flags) frames)
284 (insert (if (plist-get flags :debug-on-exit)
285 "* " " "))
286 (let ((fun-file (and do-xrefs (symbol-file fun 'defun)))
287 (fun-pt (point)))
288 (cond
289 ((and evald (not debugger-stack-frame-as-list))
290 (funcall debugger-print-function fun)
291 (if args (funcall debugger-print-function args) (princ "()")))
292 (t
293 (funcall debugger-print-function (cons fun args))
294 (cl-incf fun-pt)))
295 (when fun-file
296 (make-text-button fun-pt (+ fun-pt (length (symbol-name fun)))
297 :type 'help-function-def
298 'help-args (list fun fun-file))))
299 ;; After any frame that uses eval-buffer, insert a line that
300 ;; states the buffer position it's reading at.
301 (when (and eval-buffers (memq fun '(eval-buffer eval-region)))
302 (insert (format " ; Reading at buffer position %d"
303 ;; This will get the wrong result if there are
304 ;; two nested eval-region calls for the same
305 ;; buffer. That's not a very useful case.
306 (with-current-buffer (pop eval-buffers)
307 (point)))))
308 (insert "\n"))))
309
267(defun debugger-setup-buffer (args) 310(defun debugger-setup-buffer (args)
268 "Initialize the `*Backtrace*' buffer for entry to the debugger. 311 "Initialize the `*Backtrace*' buffer for entry to the debugger.
269That buffer should be current already." 312That buffer should be current already."
@@ -271,27 +314,20 @@ That buffer should be current already."
271 (erase-buffer) 314 (erase-buffer)
272 (set-buffer-multibyte t) ;Why was it nil ? -stef 315 (set-buffer-multibyte t) ;Why was it nil ? -stef
273 (setq buffer-undo-list t) 316 (setq buffer-undo-list t)
274 (let ((standard-output (current-buffer))
275 (print-escape-newlines t)
276 (print-level 8)
277 (print-length 50))
278 ;; FIXME the debugger could pass a custom callback to mapbacktrace
279 ;; instead of manipulating printed results.
280 (mapbacktrace #'backtrace--print-frame 'debug))
281 (goto-char (point-min))
282 (delete-region (point)
283 (progn
284 (forward-line (if (eq (car args) 'debug)
285 ;; Remove debug--implement-debug-on-entry
286 ;; and the advice's `apply' frame.
287 3
288 1))
289 (point)))
290 (insert "Debugger entered") 317 (insert "Debugger entered")
291 ;; lambda is for debug-on-call when a function call is next. 318 (let ((frames (nthcdr
292 ;; debug is for debug-on-entry function called. 319 ;; Remove debug--implement-debug-on-entry and the
293 (let ((pos (point))) 320 ;; advice's `apply' frame.
321 (if (eq (car args) 'debug) 3 1)
322 (backtrace-frames 'debug)))
323 (print-escape-newlines t)
324 (print-escape-control-characters t)
325 (print-level 8)
326 (print-length 50)
327 (pos (point)))
294 (pcase (car args) 328 (pcase (car args)
329 ;; lambda is for debug-on-call when a function call is next.
330 ;; debug is for debug-on-entry function called.
295 ((or `lambda `debug) 331 ((or `lambda `debug)
296 (insert "--entering a function:\n") 332 (insert "--entering a function:\n")
297 (setq pos (1- (point)))) 333 (setq pos (1- (point))))
@@ -300,11 +336,9 @@ That buffer should be current already."
300 (insert "--returning value: ") 336 (insert "--returning value: ")
301 (setq pos (point)) 337 (setq pos (point))
302 (setq debugger-value (nth 1 args)) 338 (setq debugger-value (nth 1 args))
303 (prin1 debugger-value (current-buffer)) 339 (funcall debugger-print-function debugger-value (current-buffer))
304 (insert ?\n) 340 (setf (cl-getf (nth 3 (car frames)) :debug-on-exit) nil)
305 (delete-char 1) 341 (insert ?\n))
306 (insert ? )
307 (beginning-of-line))
308 ;; Watchpoint triggered. 342 ;; Watchpoint triggered.
309 ((and `watchpoint (let `(,symbol ,newval . ,details) (cdr args))) 343 ((and `watchpoint (let `(,symbol ,newval . ,details) (cdr args)))
310 (insert 344 (insert
@@ -327,7 +361,7 @@ That buffer should be current already."
327 (`error 361 (`error
328 (insert "--Lisp error: ") 362 (insert "--Lisp error: ")
329 (setq pos (point)) 363 (setq pos (point))
330 (prin1 (nth 1 args) (current-buffer)) 364 (funcall debugger-print-function (nth 1 args) (current-buffer))
331 (insert ?\n)) 365 (insert ?\n))
332 ;; debug-on-call, when the next thing is an eval. 366 ;; debug-on-call, when the next thing is an eval.
333 (`t 367 (`t
@@ -337,98 +371,15 @@ That buffer should be current already."
337 (_ 371 (_
338 (insert ": ") 372 (insert ": ")
339 (setq pos (point)) 373 (setq pos (point))
340 (prin1 (if (eq (car args) 'nil) 374 (funcall debugger-print-function
341 (cdr args) args) 375 (if (eq (car args) 'nil)
342 (current-buffer)) 376 (cdr args) args)
377 (current-buffer))
343 (insert ?\n))) 378 (insert ?\n)))
379 (debugger-insert-backtrace frames t)
344 ;; Place point on "stack frame 0" (bug#15101). 380 ;; Place point on "stack frame 0" (bug#15101).
345 (goto-char pos)) 381 (goto-char pos)))
346 ;; After any frame that uses eval-buffer, 382
347 ;; insert a line that states the buffer position it's reading at.
348 (save-excursion
349 (let ((tem eval-buffer-list))
350 (while (and tem
351 (re-search-forward "^ eval-\\(buffer\\|region\\)(" nil t))
352 (end-of-line)
353 (insert (format " ; Reading at buffer position %d"
354 ;; This will get the wrong result
355 ;; if there are two nested eval-region calls
356 ;; for the same buffer. That's not a very useful case.
357 (with-current-buffer (car tem)
358 (point))))
359 (pop tem))))
360 (debugger-make-xrefs))
361
362(defun debugger-make-xrefs (&optional buffer)
363 "Attach cross-references to function names in the `*Backtrace*' buffer."
364 (interactive "b")
365 (with-current-buffer (or buffer (current-buffer))
366 (save-excursion
367 (setq buffer (current-buffer))
368 (let ((inhibit-read-only t)
369 (old-end (point-min)) (new-end (point-min)))
370 ;; If we saved an old backtrace, find the common part
371 ;; between the new and the old.
372 ;; Compare line by line, starting from the end,
373 ;; because that's the part that is likely to be unchanged.
374 (if debugger-previous-backtrace
375 (let (old-start new-start (all-match t))
376 (goto-char (point-max))
377 (with-temp-buffer
378 (insert debugger-previous-backtrace)
379 (while (and all-match (not (bobp)))
380 (setq old-end (point))
381 (forward-line -1)
382 (setq old-start (point))
383 (with-current-buffer buffer
384 (setq new-end (point))
385 (forward-line -1)
386 (setq new-start (point)))
387 (if (not (zerop
388 (let ((case-fold-search nil))
389 (compare-buffer-substrings
390 (current-buffer) old-start old-end
391 buffer new-start new-end))))
392 (setq all-match nil))))
393 ;; Now new-end is the position of the start of the
394 ;; unchanged part in the current buffer, and old-end is
395 ;; the position of that same text in the saved old
396 ;; backtrace. But we must subtract (point-min) since strings are
397 ;; indexed in origin 0.
398
399 ;; Replace the unchanged part of the backtrace
400 ;; with the text from debugger-previous-backtrace,
401 ;; since that already has the proper xrefs.
402 ;; With this optimization, we only need to scan
403 ;; the changed part of the backtrace.
404 (delete-region new-end (point-max))
405 (goto-char (point-max))
406 (insert (substring debugger-previous-backtrace
407 (- old-end (point-min))))
408 ;; Make the unchanged part of the backtrace inaccessible
409 ;; so it won't be scanned.
410 (narrow-to-region (point-min) new-end)))
411
412 ;; Scan the new part of the backtrace, inserting xrefs.
413 (goto-char (point-min))
414 (while (progn
415 (goto-char (+ (point) 2))
416 (skip-syntax-forward "^w_")
417 (not (eobp)))
418 (let* ((beg (point))
419 (end (progn (skip-syntax-forward "w_") (point)))
420 (sym (intern-soft (buffer-substring-no-properties
421 beg end)))
422 (file (and sym (symbol-file sym 'defun))))
423 (when file
424 (goto-char beg)
425 ;; help-xref-button needs to operate on something matched
426 ;; by a regexp, so set that up for it.
427 (re-search-forward "\\(\\sw\\|\\s_\\)+")
428 (help-xref-button 0 'help-function-def sym file)))
429 (forward-line 1))
430 (widen))
431 (setq debugger-previous-backtrace (buffer-string)))))
432 383
433(defun debugger-step-through () 384(defun debugger-step-through ()
434 "Proceed, stepping through subexpressions of this expression. 385 "Proceed, stepping through subexpressions of this expression.
diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el
index 2c49a634e35..7edc40188e1 100644
--- a/lisp/emacs-lisp/ert.el
+++ b/lisp/emacs-lisp/ert.el
@@ -670,48 +670,12 @@ and is displayed in front of the value of MESSAGE-FORM."
670(cl-defstruct (ert-test-aborted-with-non-local-exit 670(cl-defstruct (ert-test-aborted-with-non-local-exit
671 (:include ert-test-result))) 671 (:include ert-test-result)))
672 672
673 673(defun ert--print-backtrace (backtrace do-xrefs)
674(defun ert--record-backtrace ()
675 "Record the current backtrace (as a list) and return it."
676 ;; Since the backtrace is stored in the result object, result
677 ;; objects must only be printed with appropriate limits
678 ;; (`print-level' and `print-length') in place. For interactive
679 ;; use, the cost of ensuring this possibly outweighs the advantage
680 ;; of storing the backtrace for
681 ;; `ert-results-pop-to-backtrace-for-test-at-point' given that we
682 ;; already have `ert-results-rerun-test-debugging-errors-at-point'.
683 ;; For batch use, however, printing the backtrace may be useful.
684 (cl-loop
685 ;; 6 is the number of frames our own debugger adds (when
686 ;; compiled; more when interpreted). FIXME: Need to describe a
687 ;; procedure for determining this constant.
688 for i from 6
689 for frame = (backtrace-frame i)
690 while frame
691 collect frame))
692
693(defun ert--print-backtrace (backtrace)
694 "Format the backtrace BACKTRACE to the current buffer." 674 "Format the backtrace BACKTRACE to the current buffer."
695 ;; This is essentially a reimplementation of Fbacktrace
696 ;; (src/eval.c), but for a saved backtrace, not the current one.
697 (let ((print-escape-newlines t) 675 (let ((print-escape-newlines t)
698 (print-level 8) 676 (print-level 8)
699 (print-length 50)) 677 (print-length 50))
700 (dolist (frame backtrace) 678 (debugger-insert-backtrace backtrace do-xrefs)))
701 (pcase-exhaustive frame
702 (`(nil ,special-operator . ,arg-forms)
703 ;; Special operator.
704 (insert
705 (format " %S\n" (cons special-operator arg-forms))))
706 (`(t ,fn . ,args)
707 ;; Function call.
708 (insert (format " %S(" fn))
709 (cl-loop for firstp = t then nil
710 for arg in args do
711 (unless firstp
712 (insert " "))
713 (insert (format "%S" arg)))
714 (insert ")\n"))))))
715 679
716;; A container for the state of the execution of a single test and 680;; A container for the state of the execution of a single test and
717;; environment data needed during its execution. 681;; environment data needed during its execution.
@@ -750,7 +714,19 @@ run. ARGS are the arguments to `debugger'."
750 ((quit) 'quit) 714 ((quit) 'quit)
751 ((ert-test-skipped) 'skipped) 715 ((ert-test-skipped) 'skipped)
752 (otherwise 'failed))) 716 (otherwise 'failed)))
753 (backtrace (ert--record-backtrace)) 717 ;; We store the backtrace in the result object for
718 ;; `ert-results-pop-to-backtrace-for-test-at-point'.
719 ;; This means we have to limit `print-level' and
720 ;; `print-length' when printing result objects. That
721 ;; might not be worth while when we can also use
722 ;; `ert-results-rerun-test-debugging-errors-at-point',
723 ;; (i.e., when running interactively) but having the
724 ;; backtrace ready for printing is important for batch
725 ;; use.
726 ;;
727 ;; Grab the frames starting from `signal', frames below
728 ;; that are all from the debugger.
729 (backtrace (backtrace-frames 'signal))
754 (infos (reverse ert--infos))) 730 (infos (reverse ert--infos)))
755 (setf (ert--test-execution-info-result info) 731 (setf (ert--test-execution-info-result info)
756 (cl-ecase type 732 (cl-ecase type
@@ -1409,8 +1385,9 @@ Returns the stats object."
1409 (ert-test-result-with-condition 1385 (ert-test-result-with-condition
1410 (message "Test %S backtrace:" (ert-test-name test)) 1386 (message "Test %S backtrace:" (ert-test-name test))
1411 (with-temp-buffer 1387 (with-temp-buffer
1412 (ert--print-backtrace (ert-test-result-with-condition-backtrace 1388 (ert--print-backtrace
1413 result)) 1389 (ert-test-result-with-condition-backtrace result)
1390 nil)
1414 (goto-char (point-min)) 1391 (goto-char (point-min))
1415 (while (not (eobp)) 1392 (while (not (eobp))
1416 (let ((start (point)) 1393 (let ((start (point))
@@ -1828,12 +1805,23 @@ EWOC and STATS are arguments for `ert--results-update-stats-display'."
1828 1805
1829BEGIN and END specify a region in the current buffer." 1806BEGIN and END specify a region in the current buffer."
1830 (save-excursion 1807 (save-excursion
1831 (save-restriction 1808 (goto-char begin)
1832 (narrow-to-region begin end) 1809 (while (progn
1833 ;; Inhibit optimization in `debugger-make-xrefs' that would 1810 (goto-char (+ (point) 2))
1834 ;; sometimes insert unrelated backtrace info into our buffer. 1811 (skip-syntax-forward "^w_")
1835 (let ((debugger-previous-backtrace nil)) 1812 (< (point) end))
1836 (debugger-make-xrefs))))) 1813 (let* ((beg (point))
1814 (end (progn (skip-syntax-forward "w_") (point)))
1815 (sym (intern-soft (buffer-substring-no-properties
1816 beg end)))
1817 (file (and sym (symbol-file sym 'defun))))
1818 (when file
1819 (goto-char beg)
1820 ;; help-xref-button needs to operate on something matched
1821 ;; by a regexp, so set that up for it.
1822 (re-search-forward "\\(\\sw\\|\\s_\\)+")
1823 (help-xref-button 0 'help-function-def sym file)))
1824 (forward-line 1))))
1837 1825
1838(defun ert--string-first-line (s) 1826(defun ert--string-first-line (s)
1839 "Return the first line of S, or S if it contains no newlines. 1827 "Return the first line of S, or S if it contains no newlines.
@@ -2420,8 +2408,7 @@ To be used in the ERT results buffer."
2420 ;; Use unibyte because `debugger-setup-buffer' also does so. 2408 ;; Use unibyte because `debugger-setup-buffer' also does so.
2421 (set-buffer-multibyte nil) 2409 (set-buffer-multibyte nil)
2422 (setq truncate-lines t) 2410 (setq truncate-lines t)
2423 (ert--print-backtrace backtrace) 2411 (ert--print-backtrace backtrace t)
2424 (debugger-make-xrefs)
2425 (goto-char (point-min)) 2412 (goto-char (point-min))
2426 (insert (substitute-command-keys "Backtrace for test `")) 2413 (insert (substitute-command-keys "Backtrace for test `"))
2427 (ert-insert-test-name-button (ert-test-name test)) 2414 (ert-insert-test-name-button (ert-test-name test))
diff --git a/lisp/select.el b/lisp/select.el
index 4849d7d515e..579c5c7e2ee 100644
--- a/lisp/select.el
+++ b/lisp/select.el
@@ -475,6 +475,9 @@ two markers or an overlay. Otherwise, it is nil."
475 (t 475 (t
476 (error "Unknown selection type: %S" type))))) 476 (error "Unknown selection type: %S" type)))))
477 477
478 ;; Most programs are unable to handle NUL bytes in strings.
479 (setq str (replace-regexp-in-string "\0" "\\0" str t t))
480
478 (setq next-selection-coding-system nil) 481 (setq next-selection-coding-system nil)
479 (cons type str)))) 482 (cons type str))))
480 483
diff --git a/lisp/subr.el b/lisp/subr.el
index d0c8517c543..a9edff6166f 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -4514,7 +4514,8 @@ EVALD, FUNC, ARGS, FLAGS are as in `mapbacktrace'."
4514(defun backtrace () 4514(defun backtrace ()
4515 "Print a trace of Lisp function calls currently active. 4515 "Print a trace of Lisp function calls currently active.
4516Output stream used is value of `standard-output'." 4516Output stream used is value of `standard-output'."
4517 (let ((print-level (or print-level 8))) 4517 (let ((print-level (or print-level 8))
4518 (print-escape-control-characters t))
4518 (mapbacktrace #'backtrace--print-frame 'backtrace))) 4519 (mapbacktrace #'backtrace--print-frame 'backtrace)))
4519 4520
4520(defun backtrace-frames (&optional base) 4521(defun backtrace-frames (&optional base)
diff --git a/lisp/term/w32-win.el b/lisp/term/w32-win.el
index fda93884c40..be895a040da 100644
--- a/lisp/term/w32-win.el
+++ b/lisp/term/w32-win.el
@@ -396,7 +396,7 @@ See the documentation of `create-fontset-from-fontset-spec' for the format.")
396;;; Fix interface to (X-specific) mouse.el 396;;; Fix interface to (X-specific) mouse.el
397(defun w32--set-selection (type value) 397(defun w32--set-selection (type value)
398 (if (eq type 'CLIPBOARD) 398 (if (eq type 'CLIPBOARD)
399 (w32-set-clipboard-data value) 399 (w32-set-clipboard-data (replace-regexp-in-string "\0" "\\0" value t t))
400 (put 'x-selections (or type 'PRIMARY) value))) 400 (put 'x-selections (or type 'PRIMARY) value)))
401 401
402(defun w32--get-selection (&optional type data-type) 402(defun w32--get-selection (&optional type data-type)