Subject: bug#8789: 23.3; debug backtrace buffer changes window on step-through

From: martin rudalics <[email-masked]>
Date: Wed, 29 Feb 2012 09:47:08 +0100

> Here it comes, produced today with "GNU Emacs 24.0.93.1
 > (i486-pc-linux-gnu, GTK+ Version 3.2.3)\n of 2012-02-22 on zelenka,
 > modified by Debian".

Thank you.  Is it true that you can reproduce this with the given recipe
at will?  The combination of

 > Content of *Backtrace* before the crash:
 >
 > * (set-window-configuration wconfig)

and

 > (gdb) bt
 > #0  0xb7fe2424 in __kernel_vsyscall ()
 > #1  0xb6a65c16 in kill () from /lib/i386-linux-gnu/i686/cmov/libc.so.6
 > #2  0x08139d78 in abort () at emacs.c:394
 > #3  0x0819441e in mark_object (arg=139036542) at alloc.c:5706

seems to indicate a collector problem when recovering from a window
excursion.  Reproducing a collector crash, however, is usually very
difficult.

Meanwhile please try the patch attached to check whether it (1) solves
the original problem and (2) results in this crash as well.

Thanks, martin
=== modified file 'lisp/emacs-lisp/debug.el'
--- lisp/emacs-lisp/debug.el	2012-01-19 07:21:25 +0000
+++ lisp/emacs-lisp/debug.el	2012-02-26 18:55:02 +0000
@@ -194,7 +194,8 @@
 		  ;; Place an extra debug-on-exit for macro's.
 		  (when (eq 'lambda (car-safe (cadr (backtrace-frame 4))))
 		    (backtrace-debug 5 t)))
-                (pop-to-buffer debugger-buffer)
+                (pop-to-buffer
+		 debugger-buffer '((display-buffer-reuse-window display-buffer-in-previous-window) . nil))
 		(debugger-mode)
 		(debugger-setup-buffer debugger-args)
 		(when noninteractive
@@ -245,7 +246,8 @@
                     ;; best to do that.
                     (bury-buffer))))
             (unless debugger-previous-state
-              (kill-buffer debugger-buffer)))
+              ;; (kill-buffer debugger-buffer)
+	      ))
           ;; Restore the previous state of the debugger-buffer, in case we were
           ;; in a recursive invocation of the debugger.
           (when (buffer-live-p debugger-buffer)

=== modified file 'lisp/window.el'
--- lisp/window.el	2012-02-12 05:10:30 +0000
+++ lisp/window.el	2012-02-26 19:09:54 +0000
@@ -2482,69 +2482,6 @@
 
 ;;; Windows and buffers.
 
-;; `prev-buffers' and `next-buffers' are two reserved window slots used
-;; for (1) determining which buffer to show in the window when its
-;; buffer shall be buried or killed and (2) which buffer to show for
-;; `switch-to-prev-buffer' and `switch-to-next-buffer'.
-
-;; `prev-buffers' consists of <buffer, window-start, window-point>
-;; triples.  The entries on this list are ordered by the time their
-;; buffer has been removed from the window, the most recently removed
-;; buffer's entry being first.  The window-start and window-point
-;; components are `window-start' and `window-point' at the time the
-;; buffer was removed from the window which implies that the entry must
-;; be added when `set-window-buffer' removes the buffer from the window.
-
-;; `next-buffers' is the list of buffers that have been replaced
-;; recently by `switch-to-prev-buffer'.  These buffers are the least
-;; preferred candidates of `switch-to-prev-buffer' and the preferred
-;; candidates of `switch-to-next-buffer' to switch to.  This list is
-;; reset to nil by any action changing the window's buffer with the
-;; exception of `switch-to-prev-buffer' and `switch-to-next-buffer'.
-;; `switch-to-prev-buffer' pushes the buffer it just replaced on it,
-;; `switch-to-next-buffer' pops the last pushed buffer from it.
-
-;; Both `prev-buffers' and `next-buffers' may reference killed buffers
-;; if such a buffer was killed while the window was hidden within a
-;; window configuration.  Such killed buffers get removed whenever
-;; `switch-to-prev-buffer' or `switch-to-next-buffer' encounter them.
-
-;; The following function is called by `set-window-buffer' _before_ it
-;; replaces the buffer of the argument window with the new buffer.
-(defun record-window-buffer (&optional window)
-  "Record WINDOW's buffer.
-WINDOW must be a live window and defaults to the selected one."
-  (let* ((window (window-normalize-window window t))
-	 (buffer (window-buffer window))
-	 (entry (assq buffer (window-prev-buffers window))))
-    ;; Reset WINDOW's next buffers.  If needed, they are resurrected by
-    ;; `switch-to-prev-buffer' and `switch-to-next-buffer'.
-    (set-window-next-buffers window nil)
-
-    (when entry
-      ;; Remove all entries for BUFFER from WINDOW's previous buffers.
-      (set-window-prev-buffers
-       window (assq-delete-all buffer (window-prev-buffers window))))
-
-    ;; Don't record insignificant buffers.
-    (unless (eq (aref (buffer-name buffer) 0) ?\s)
-      ;; Add an entry for buffer to WINDOW's previous buffers.
-      (with-current-buffer buffer
-	(let ((start (window-start window))
-	      (point (window-point-1 window)))
-	  (setq entry
-		(cons buffer
-		      (if entry
-			  ;; We have an entry, update marker positions.
-			  (list (set-marker (nth 1 entry) start)
-				(set-marker (nth 2 entry) point))
-			;; Make new markers.
-			(list (copy-marker start)
-			      (copy-marker point)))))
-
-	  (set-window-prev-buffers
-	   window (cons entry (window-prev-buffers window))))))))
-
 (defun unrecord-window-buffer (&optional window buffer)
   "Unrecord BUFFER in WINDOW.
 WINDOW must be a live window and defaults to the selected one.
@@ -4846,6 +4783,50 @@
       (and pop-up-windows
 	   (display-buffer-pop-up-window buffer alist))))
 
+(defun display-buffer-in-previous-window (buffer alist)
+  "Display BUFFER in a window previously showing it.
+If ALIST has a non-nil `inhibit-same-window' entry, the selected
+window is not eligible for reuse.
+
+If ALIST contains a `reusable-frames' entry, its value determines
+which frames to search for a reusable window:
+  nil -- the selected frame (actually the last non-minibuffer frame)
+  A frame   -- just that frame
+  `visible' -- all visible frames
+  0   -- all frames on the current terminal
+  t   -- all frames.
+
+If ALIST contains no `reusable-frames' entry, search just the
+selected frame if `display-buffer-reuse-frames' and
+`pop-up-frames' are both nil; search all frames on the current
+terminal if either of those variables is non-nil."
+  (let* ((alist-entry (assq 'reusable-frames alist))
+	 (inhibit-same-window
+	  (cdr (assq 'inhibit-same-window alist)))
+	 (frames (cond
+		  (alist-entry (cdr alist-entry))
+		  ((if (eq pop-up-frames 'graphic-only)
+		       (display-graphic-p)
+		     pop-up-frames)
+		   0)
+		  (display-buffer-reuse-frames 0)
+		  (t (last-nonminibuffer-frame))))
+	 best-window second-best-window window)
+    (when inhibit-same-window (ding))
+    (catch 'best
+      (dolist (window (window-list-1 (frame-first-window) 'nomini frames))
+	(when (and (assq buffer (window-prev-buffers window))
+		   (not (window-dedicated-p window)))
+	  (if (eq window (selected-window))
+	      (unless inhibit-same-window
+		(setq second-best-window window))
+	    (setq best-window window)
+	    (throw 'best t)))))
+
+    (when (setq window (or best-window second-best-window))
+      (display-buffer-record-window 'reuse window buffer)
+      (window--display-buffer-2 buffer window display-buffer-mark-dedicated))))
+
 (defun display-buffer-use-some-window (buffer alist)
   "Display BUFFER in an existing window.
 Search for a usable window, set that window to the buffer, and

=== modified file 'src/window.c'
--- src/window.c	2012-02-23 17:40:33 +0000
+++ src/window.c	2012-02-27 07:43:06 +0000
@@ -51,7 +51,7 @@
 #endif
 
 Lisp_Object Qwindowp, Qwindow_live_p;
-static Lisp_Object Qwindow_configuration_p, Qrecord_window_buffer;
+static Lisp_Object Qwindow_configuration_p;
 static Lisp_Object Qwindow_deletable_p, Qdelete_window, Qdisplay_buffer;
 static Lisp_Object Qreplace_buffer_in_windows, Qget_mru_window;
 static Lisp_Object Qwindow_resize_root_window, Qwindow_resize_root_window_vertically;
@@ -1647,15 +1647,41 @@
 
 DEFUN ("set-window-prev-buffers", Fset_window_prev_buffers,
        Sset_window_prev_buffers, 2, 2, 0,
-       doc: /* Set WINDOW's previous buffers to PREV-BUFFERS.
+       doc:	     /* Set WINDOW's previous buffers to PREV-BUFFERS.
 WINDOW must be a live window and defaults to the selected one.
 
 PREV-BUFFERS should be a list of elements (BUFFER WINDOW-START POS),
 where BUFFER is a buffer, WINDOW-START is the start position of the
 window for that buffer, and POS is a window-specific point value.  */)
-     (Lisp_Object window, Lisp_Object prev_buffers)
+  (Lisp_Object window, Lisp_Object prev_buffers)
 {
-  return decode_window (window)->prev_buffers = prev_buffers;
+  register struct window *w = decode_window (window);
+
+  if (NILP (prev_buffers))
+    return w->prev_buffers = Qnil;
+  else if (!CONSP (prev_buffers))
+    return w->prev_buffers;
+  else
+    {
+      /* Run cycle detection on prev_buffers.  */
+      Lisp_Object tortoise, hare;
+
+      hare = tortoise = prev_buffers;
+      while (CONSP (hare))
+	{
+	  hare = XCDR (hare);
+	  if (!CONSP (hare))
+	    break;
+
+	  hare = XCDR (hare);
+	  tortoise = XCDR (tortoise);
+
+	  if (EQ (hare, tortoise))
+	    return w->prev_buffers;
+	}
+
+      return w->prev_buffers = prev_buffers;
+    }
 }
 
 DEFUN ("window-next-buffers", Fwindow_next_buffers, Swindow_next_buffers,
@@ -1674,7 +1700,33 @@
 NEXT-BUFFERS should be a list of buffers.  */)
      (Lisp_Object window, Lisp_Object next_buffers)
 {
-  return decode_window (window)->next_buffers = next_buffers;
+  register struct window *w = decode_window (window);
+
+  if (NILP (next_buffers))
+    return w->next_buffers = Qnil;
+  else if (!CONSP (next_buffers))
+    return w->next_buffers;
+  else
+    {
+      /* Run cycle detection on next_buffers.  */
+      Lisp_Object tortoise, hare;
+
+      hare = tortoise = next_buffers;
+      while (CONSP (hare))
+	{
+	  hare = XCDR (hare);
+	  if (!CONSP (hare))
+	    break;
+
+	  hare = XCDR (hare);
+	  tortoise = XCDR (tortoise);
+
+	  if (EQ (hare, tortoise))
+	    return w->next_buffers;
+	}
+
+      return w->next_buffers = next_buffers;
+    }
 }
 
 DEFUN ("window-parameters", Fwindow_parameters, Swindow_parameters,
@@ -2940,13 +2992,94 @@
 DEFUN ("run-window-configuration-change-hook", Frun_window_configuration_change_hook,
        Srun_window_configuration_change_hook, 1, 1, 0,
        doc: /* Run `window-configuration-change-hook' for FRAME.  */)
-     (Lisp_Object frame)
+  (Lisp_Object frame)
 {
   CHECK_LIVE_FRAME (frame);
   run_window_configuration_change_hook (XFRAME (frame));
   return Qnil;
 }
 
+/* `prev_buffers' and `next_buffers' are two reserved window slots used
+for determining
+
+(1) which buffer to show in the window when its buffer shall be buried
+    or killed, and
+
+(2) which buffer to show for `switch-to-prev-buffer' and
+    `switch-to-next-buffer'.
+
+`prev_buffers' is built from <buffer, window-start, window-point>
+triples.  The entries on this list are ordered by the time their buffer
+has been removed from the window, the most recently removed buffer's
+entry being first.  The window-start and window-point components are
+`window-start' and `window-point' at the time the buffer was removed
+from the window which implies that the entry must be added when
+Fset_window_buffer removes the buffer from the window.
+
+`next_buffers' is the list of buffers that have been replaced recently
+by `switch-to-prev-buffer'.  These buffers are the least preferred
+candidates of `switch-to-prev-buffer' and the preferred candidates of
+`switch-to-next-buffer' to switch to.  This list is reset to nil by any
+action changing the window's buffer with the exception of
+`switch-to-prev-buffer' and `switch-to-next-buffer'.
+`switch-to-prev-buffer' pushes the buffer it just replaced on it,
+`switch-to-next-buffer' pops the last pushed buffer from it.
+
+Both `prev_buffers' and `next_buffers' may reference killed buffers if
+such a buffer was killed while the window was hidden within a window
+configuration.  Such killed buffers get removed whenever
+`switch-to-prev-buffer' or `switch-to-next-buffer' encounter them.
+
+The following function must be called by Fset_window_buffer and
+Fset_window_configuration _before_ these replace the buffer of the
+argument window with the new buffer.  */
+
+static void
+record_window_buffer (Lisp_Object window)
+{
+  struct window *w = XWINDOW (window);
+  Lisp_Object buffer = w->buffer;
+  struct buffer *b = XBUFFER (buffer);
+  Lisp_Object entry = Fassq (buffer, w->prev_buffers);
+  Lisp_Object start_marker, point_marker, point;
+  int count = SPECPDL_INDEX ();
+
+  /* Return immediately if BUFFER is not a buffer.  */
+  if (!BUFFERP (buffer))
+    return;
+
+  /* Reset WINDOW's next buffers.  If needed, they are resurrected by
+     `switch-to-prev-buffer' and `switch-to-next-buffer'.  */
+  w->next_buffers = Qnil;
+
+  if (!NILP (entry))
+    /* Remove all entries for BUFFER from WINDOW's previous buffers.  */
+    w->prev_buffers = Fdelq (entry, w->prev_buffers);
+
+  if (NILP (BVAR (b, name)) || SREF (BVAR (b, name), 0) == ' ')
+    /* Don't record dead or insignificant buffers.  */
+    return;
+
+  if (current_buffer != b)
+    {
+      /* Make sure Fpoint below gets the right buffer.  */
+      record_unwind_protect (set_buffer_if_live, Fcurrent_buffer ());
+      set_buffer_internal (b);
+    }
+
+  /* Reuse existing markers if possible.  */
+  start_marker = !NILP (entry) ? Fnth (make_number (1), entry) : Fmake_marker ();
+  point_marker = !NILP (entry) ? Fnth (make_number (2), entry) : Fmake_marker ();
+  point = EQ (window, selected_window) ? Fpoint () : w->pointm;
+  set_marker_restricted (start_marker, w->start, buffer);
+  set_marker_restricted (point_marker, point, buffer);
+  w->prev_buffers = Fcons (list3 (buffer, start_marker, point_marker),
+			   w->prev_buffers);
+
+  unbind_to (count, Qnil);
+}
+
+
 /* Make WINDOW display BUFFER as its contents.  RUN_HOOKS_P non-zero
    means it's allowed to run hooks.  See make_frame for a case where
    it's not allowed.  KEEP_MARGINS_P non-zero means that the current
@@ -3094,7 +3227,7 @@
 	       dedication.  */
 	    w->dedicated = Qnil;
 
-	  call1 (Qrecord_window_buffer, window);
+	  record_window_buffer (window);
 	}
 
       unshow_buffer (w);
@@ -5587,6 +5720,8 @@
 	  else if (!NILP (BVAR (XBUFFER (p->buffer), name)))
 	    /* If saved buffer is alive, install it.  */
 	    {
+	      if (!EQ (w->buffer, p->buffer))
+		record_window_buffer (window);
 	      w->buffer = p->buffer;
 	      w->start_at_line_beg = p->start_at_line_beg;
 	      set_marker_restricted (w->start, p->start, w->buffer);
@@ -5620,6 +5755,7 @@
 		   && SCHARS (auto_buffer_name) != 0
 		   && !NILP (w->buffer = Fget_buffer_create (auto_buffer_name)))
 	    {
+	      record_window_buffer (window);
 	      set_marker_restricted (w->start, make_number (0), w->buffer);
 	      set_marker_restricted (w->pointm, make_number (0), w->buffer);
 	      w->start_at_line_beg = Qt;
@@ -5627,6 +5763,7 @@
 	  else
 	    /* Window has no live buffer, get one.  */
 	    {
+	      record_window_buffer (window);
 	      /* Get the buffer via other_buffer_safely in order to
 	      avoid showing an unimportant buffer and, if necessary, to
 	      recreate *scratch* in the course (part of Juanma's bs-show
@@ -6509,7 +6646,6 @@
   DEFSYM (Qsafe, "safe");
   DEFSYM (Qdisplay_buffer, "display-buffer");
   DEFSYM (Qreplace_buffer_in_windows, "replace-buffer-in-windows");
-  DEFSYM (Qrecord_window_buffer, "record-window-buffer");
   DEFSYM (Qget_mru_window, "get-mru-window");
   DEFSYM (Qtemp_buffer_show_hook, "temp-buffer-show-hook");
   DEFSYM (Qabove, "above");