Subject: bug#66575: [PATCH] Gud lldb support

From: Gerd Möllmann <[email-masked]>
Date: Mon, 16 Oct 2023 16:09:38 +0200

Mattias Engdegård <[email-masked]> writes:

>> No MI support, because lldb-mi is no longer part of LLVM.
>
> So what is the currently blessed way to communicate with LLDB?

Don't really know, I'm afraid.

> I only have comments about trivialities here; someone else should deal
> with the serious stuff.
>
>> +(eval-when-compile (require 'rx))
>
> There is usually no need to import rx explicitly. The necessary macros
> and functions are autoloaded.
>
>> +;; because gud-symbol uses their values if they are present.  Tehir
>> +;; names are deducded from the minor-mode name.
>
> 'Their', 'deduced'.
>
>> +         ;;     frame #0: ...) at emacs.c:1310:9 [opt]
>> +         (string-match (rx (and line-start (0+ blank) "frame"
>> +                                (0+ not-newline) " at "
>> +                                (group (1+ (not (any ":"))))
>> +                                ":"
>
>
> (You can write (not ":") instead of (not (any ":")) if you like.)

Both Fixed in the attached patch.  I also found another spelling error
in an option passed to 'breakpoint set -joint...'.

> If thñe file name can have an absolute directory part, then it may run
> into trouble on Windows (C:\Some\Dir\File.c).

I guess I'll leave that as an exercise for someone having Windows :-).

>
> Sad that we throw away the column number here, but perhaps that's just
> a limitation of gud.el.

Indeed.

>
> Anyway, a big step up from not having any support at all. Thank you!

👍

>
> Would you include a NEWS entry as well?

When I get this into master, yes of course.  I'm not yet convinced of
that :-).

>From 8cbbe82f715876a26736f1380908b0ebb3356f19 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gerd=20M=C3=B6llmann?= <[email-masked]>
Date: Mon, 16 Oct 2023 13:54:02 +0200
Subject: [PATCH] Gud lldb support

* lisp/progmodes/gud.el (lldb): New function.
---
 lisp/progmodes/gud.el | 142 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 142 insertions(+)

diff --git a/lisp/progmodes/gud.el b/lisp/progmodes/gud.el
index d4b954a7203..cff2d7034e4 100644
--- a/lisp/progmodes/gud.el
+++ b/lisp/progmodes/gud.el
@@ -973,6 +973,7 @@ gud-gdb-fetch-lines-filter
       (setq gud-gdb-fetch-lines-string string)
       "")))
 
+
 ;; gdb speedbar functions
 
 ;; Part of the macro expansion of dframe-with-attached-buffer.
@@ -3840,6 +3841,147 @@ gud-tooltip-tips
 		  (gud-basic-call cmd))
 		expr))))))))
 
+
+;; 'gud-lldb-history' and 'gud-gud-lldb-command-name' are required
+;; because gud-symbol uses their values if they are present.  Their
+;; names are deduced from the minor-mode name.
+(defvar gud-lldb-history nil)
+
+(defcustom gud-gud-lldb-command-name "lldb"
+  "Default command to run an executable under LLDB in text command mode."
+  :type 'string)
+
+(defun gud-lldb-marker-filter (string)
+  "Deduce interesting stuff from output STRING."
+  (cond (;; Process 72668 stopped
+         ;; * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
+         ;;     frame #0: ...) at emacs.c:1310:9 [opt]
+         (string-match (rx (and line-start (0+ blank) "frame"
+                                (0+ not-newline) " at "
+                                (group (1+ (not ":")))
+                                ":"
+                                (group (1+ digit))))
+                       string)
+         (setq gud-last-frame
+               (cons (match-string 1 string)
+                     (string-to-number (match-string 2 string)))))
+        (;; Process 72874 exited with status = 9 (0x00000009) killed
+         (string-match (rx "Process " (1+ digit) " exited with status")
+                       string)
+         (setq gud-last-last-frame nil)
+         (setq gud-overlay-arrow-position nil)))
+  string)
+
+;;;###autoload
+(defun lldb (command-line)
+  "Run lldb passing it COMMAND-LINE as arguments.
+If COMMAND-LINE names a program FILE to debug, lldb will run in
+a buffer named *gud-FILE*, and the directory containing FILE
+becomes the initial working directory and source-file directory
+for your debugger.  If you don't want `default-directory' to
+change to the directory of FILE, specify FILE without leading
+directories, in which case FILE should reside either in the
+directory of the buffer from which this command is invoked, or
+it can be found by searching PATH.
+
+If COMMAND-LINE requests that lldb attaches to a process PID, lldb
+will run in *gud-PID*, otherwise it will run in *gud*; in these
+cases the initial working directory is the `default-directory' of
+the buffer in which this command was invoked."
+  (interactive (list (gud-query-cmdline 'lldb)))
+
+  (when (and gud-comint-buffer
+	     (buffer-name gud-comint-buffer)
+	     (get-buffer-process gud-comint-buffer)
+	     (with-current-buffer gud-comint-buffer (eq gud-minor-mode 'gud-lldb)))
+    (gdb-restore-windows)
+    ;; FIXME: Copied from gud-gdb, but what does that even say?
+    (error "Multiple debugging requires restarting in text command mode"))
+
+  (gud-common-init command-line nil 'gud-lldb-marker-filter)
+  (setq-local gud-minor-mode 'lldb)
+
+  (gud-def gud-break
+           "breakpoint set --joint-specifier %f:%l"
+           "\C-b"
+           "Set breakpoint at current line.")
+  (gud-def gud-tbreak
+           "_regexp-break %f:%l"
+           "\C-t"
+	   "Set temporary breakpoint at current line.")
+  (gud-def gud-remove
+           "breakpoint clear  --line %l --file %f"
+           "\C-d"
+           "Remove breakpoint at current line")
+  (gud-def gud-step "thread step-in --count %p"
+           "\C-s"
+           "Step one source line with display.")
+  (gud-def gud-stepi
+           "thread step-inst --count %p"
+           "\C-i"
+           "Step one instruction with display.")
+  (gud-def gud-next
+           "thread step-over --count %p"
+           "\C-n"
+           "Step one line (skip functions).")
+  (gud-def gud-nexti
+           "thread step-inst-over --count %p"
+           nil
+           "Step one instruction (skip functions).")
+  (gud-def gud-cont
+           "process continue --ignore-count %p"
+           "\C-r"
+           "Continue with display.")
+  (gud-def gud-finish
+           "thread step-out"
+           "\C-f"
+           "Finish executing current function.")
+  (gud-def gud-jump
+	   (progn
+             (gud-call "_regexp-break %f:%l" arg)
+             (gud-call "_regexp-jump %f:%l"))
+	   "\C-j"
+           "Set execution address to current line.")
+  (gud-def gud-up
+           "_regexp-up up %p"
+           "<"
+           "Up N stack frames (numeric arg).")
+  (gud-def gud-down
+           "_regexp-down %p"
+           ">"
+           "Down N stack frames (numeric arg).")
+  (gud-def gud-print
+           "dwim-print %e"
+           "\C-p"
+           "Evaluate C expression at point.")
+  (gud-def gud-pstar
+           "dwim-print *%e"
+           nil
+	   "Evaluate C dereferenced pointer expression at point.")
+
+  ;; For debugging Emacs only.
+  (gud-def gud-pv
+           "xprint %e"
+           "\C-v"
+           "Print the value of the lisp variable.")
+
+  (gud-def gud-until
+           "thread until %l"
+           "\C-u"
+           "Continue to current line.")
+  (gud-def gud-run
+           ;; Extension for process launch --tty?
+           "process launch -X true"
+	   nil
+           "Run the program.")
+
+  (gud-set-repeat-map-property 'gud-gdb-repeat-map)
+  (setq comint-prompt-regexp "^(lldb) *")
+  (setq paragraph-start comint-prompt-regexp)
+  (setq gud-running nil)
+  (setq gud-filter-pending-text nil)
+  (run-hooks 'gud-lldb-mode-hook))
+
 (provide 'gud)
 
 ;;; gud.el ends here
-- 
2.42.0