Subject: bug#39408: closed (Re: bug#39408: Breakpoints don't work with M-x gdb under TRAMP)

From: [email-masked] (GNU bug Tracking System)
Date: Sat, 01 May 2021 10:00:06 +0000

Your bug report

#39408: Breakpoints don't work with M-x gdb under TRAMP

which was filed against the emacs package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
39408: http://debbugs.gnu.org/cgi/bugreport.cgi?bug=39408
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#39408: Breakpoints don't work with M-x gdb under TRAMP
From: Michael Albinus <[email-masked]>
To: Jim Porter <[email-masked]>
Cc: [email-masked]
Date: Sat, 01 May 2021 11:59:07 +0200
List-Id: <debbugs-submit.debbugs.gnu.org>

Version: 27.3

Jim Porter <[email-masked]> writes:

> This is already fixed in Emacs 28 thanks to bug#44173 (a78c614), which
> replaces the old GDB/MI parser (that converted the data to JSON first)
> with a more-robust parser. Attached is a patch to fix this under Emacs
> 27.
>
> The problem is that in `gdb-jsonify-buffer', when replacing the
> "fullname" with a Tramp path, the wildcard was greedy, resulting in
> only one "fullname" being replaced in the buffer. This had the effect
> of rendering it impossible to have more than one breakpoint, as all
> but the first breakpoint would have unmodified "fullname"s.

Pushed to the emacs-27 branch. Closing the bug.

Note: I don't know, whether there will be an Emacs 27.3. So the patch is
pushed just in case of.

Best regards, Michael.

Subject: Breakpoints don't work with M-x gdb under TRAMP
From: Timo Lilja <[email-masked]>
To: [email-masked]
Date: Mon, 3 Feb 2020 21:07:05 +0200
List-Id: <debbugs-submit.debbugs.gnu.org>

*** Environment

- GNU Emacs 26.1 (build 2, x86_64-pc-linux-gnu, GTK+ Version 3.24.5) of
  2019-09-23, modified by Debian
- The code looks identical on the relevant parts in Emacs git master

*** Steps to reproduce

1. Run emacs
   $ emacs -q

2. Start remote debugging session
   M-x gdb RET gdb -i=mi /ssh:vagrant@debug:/vagrant/hello

3. Set a breakpoint in *gud-hello* buffer
   (gdb) break main
   Breakpoint 1 at 0x1149: file hello.c, line 6.

4. Run the program to the breakpoint
   (gdb) run

5. Switch back to source window
   C-x b hello.c RET

6. Add another breakpoint
   C-x C-a c-b

Emacs becomes unresponsive and the minibuffer is flooded with the
following error message:

~File /ssh:vagrant@debug:/vagrant/"/vagrant/hello.c" no longer exists!~

*** Fix

1. Apply patch gdb-tramp-fix.diff
   $ zcat /usr/share/emacs/26.1/lisp/progmodes/gdb-mi.el.gz >gdb-mi.el
   $ zcat /usr/share/emacs/26.1/lisp/progmodes/gud.el.gz >gud.el
   $ patch <gdb-tramp-fix.diff

2. run emacs with modified gdb-mi.el and gud.el
   $ emacs -q -l el/gdb-mi.el -l el/gud.el

3. Setting breakpoints with C-x C-a C-b should work now

The changes in ~gdb-mi.el~ fix the problem with the "no longer exist"
error message.

The change in ~gud.el~ makes the fringe mark work a bit better and also
the source buffer to pop up when a break point is hit.

The problem seems to be that not all filename information goes through
(gdb-jsonify-buffer) but gdb-mi.el and gud.el read them directly from
gdb's buffers. These patches check wheter a gdb buffer is remote, and
wrap the file names accordingly.

There is probably a better way to fix the problem, but my knowledge
of gdb-mi.el is limited.

Relates to bug #23608.

*** Affected functions
(gdb-place-breakpoints)
(gdb-get-location)
(gdb-goto-breakpoint)
(gdb-frame-handler)
(gud-file-name)

*** References
- https://debbugs.gnu.org/cgi/bugreport.cgi?bug=23608
--- gdb-mi.el.orig	2020-01-26 18:46:35.351741525 +0200
+++ gdb-mi.el	2020-01-22 09:58:01.445834007 +0200
@@ -2693,7 +2693,7 @@
     (let ((remote (file-remote-p default-directory)))
       (when remote
         (goto-char (point-min))
-        (while (re-search-forward "[\\[,]fullname=\"\\(.+\\)\"" nil t)
+        (while (re-search-forward ",fullname=\"\\(.+\\)\"" nil t)
           (replace-match (concat remote "\\1") nil nil nil 1))))
     (goto-char (point-min))
     (when fix-key
@@ -2967,6 +2967,13 @@
     (insert (gdb-table-string table " "))
     (gdb-place-breakpoints)))
 
+(defun fix-filename (filename)
+  (let ((remote (file-remote-p default-directory)))
+    (if (and remote filename (not (file-remote-p filename)))
+        (concat remote (replace-regexp-in-string "^\\\"\\(.+\\)\"" "\\1"
+                                                 filename nil))
+      filename)))
+
 ;; Put breakpoint icons in relevant margins (even those set in the GUD buffer).
 (defun gdb-place-breakpoints ()
   ;; Remove all breakpoint-icons in source buffers but not assembler buffer.
@@ -2980,7 +2987,7 @@
                                         ; an associative list
            (line (bindat-get-field breakpoint 'line)))
       (when line
-        (let ((file (bindat-get-field breakpoint 'fullname))
+        (let ((file (fix-filename (bindat-get-field breakpoint 'fullname)))
               (flag (bindat-get-field breakpoint 'enabled))
               (bptno (bindat-get-field breakpoint 'number)))
           (unless (and file (file-exists-p file))
@@ -3019,7 +3026,7 @@
 	(message-box "Cannot find source file for breakpoint location.
 Add directory to search path for source files using the GDB command, dir."))
       (throw 'file-not-found nil))
-    (with-current-buffer (find-file-noselect (match-string 1))
+    (with-current-buffer (find-file-noselect (fix-filename (match-string 1)))
       (gdb-init-buffer)
       ;; only want one breakpoint icon at each location
       (gdb-put-breakpoint-icon (eq flag ?y) bptno (string-to-number line)))))
@@ -3977,7 +3984,7 @@
     (let ((breakpoint (get-text-property (point) 'gdb-breakpoint)))
       (if breakpoint
           (let ((bptno (bindat-get-field breakpoint 'number))
-                (file  (bindat-get-field breakpoint 'fullname))
+                (file  (fix-filename (bindat-get-field breakpoint 'fullname)))
                 (line  (bindat-get-field breakpoint 'line)))
             (save-selected-window
               (let* ((buffer (find-file-noselect
@@ -4373,7 +4380,7 @@
   (let ((frame (bindat-get-field (gdb-json-partial-output) 'frame)))
     (when frame
       (setq gdb-selected-frame (bindat-get-field frame 'func))
-      (setq gdb-selected-file (bindat-get-field frame 'fullname))
+      (setq gdb-selected-file (fix-filename (bindat-get-field frame 'fullname)))
       (setq gdb-frame-number (bindat-get-field frame 'level))
       (setq gdb-frame-address (bindat-get-field frame 'addr))
       (let ((line (bindat-get-field frame 'line)))
--- gud.el.orig	2020-01-26 18:46:41.563789189 +0200
+++ gud.el	2020-01-26 22:29:38.053975346 +0200
@@ -301,7 +301,8 @@
   ;; remote part to f, which is the local file name.  Fortunately,
   ;; `file-remote-p' returns exactly this remote file name part (or
   ;; nil otherwise).
-  (setq f (concat (or (file-remote-p default-directory) "") f))
+  (unless (file-remote-p f)
+    (setq f (concat (or (file-remote-p default-directory) "") f)))
   (if (file-exists-p f) (expand-file-name f)
     (let ((directories (gud-val 'directories))
 	  (result nil))