emacs-lldb-voltron
lldb interface in emacs hosted in a vterm. The source/stack etc is displayed using a tmux session set up to run voltron scripts which get updated dynamically by lldb. The scripts used are maintained in a sister project located here.
Images
Video
A quick video showing a small test project, overflow, its .lldbinit and then launching the lldb interface from the overflow.c file using the keybindings documented below. We hop over to the tmux hosting terminal and swap tmux windows and panes configured here
https://www.youtube.com/watch?v=6-2_pi-FuU8
lldb-voltron.el
link : lldb-voltron.el
Code to launch a lldb session in a vterm and a terminal with a tmux session holding various voltron snippets which talk to lldb for the debugger output UI.
customisation
(defgroup rgr/llvm nil
"llvm options"
:group 'rgr)
(defcustom rgr/lldb-ui-command "lldb-ui"
"the ui/voltron command"
:type 'string
:group 'rgr/llvm)
(defcustom rgr/lldb-voltron-buffer-name "vterm-lldb-voltron"
"the ui/voltron buffer base name"
:type 'string
:group 'rgr/llvm)
rgr/lldb-mode
minor mode to talk to an llvm console in vterm
(define-minor-mode rgr/lldb-mode "my lldb mode" :lighter "lldb"
:keymap '(
( [f10] . (lambda()(interactive)(process-send-string (current-buffer) "thread step-over\n")))
( [f11] . (lambda()(interactive)(process-send-string (current-buffer) "thread step-in\n")))
( [S-f11] . (lambda()(interactive)(process-send-string (current-buffer) "thread step-out\n")))
( [f12] . (lambda()(interactive)(process-send-string (current-buffer) "thread step-inst\n")))))
rgr/lldb-voltron-vterm
(defun rgr/lldb-voltron-vterm(dir)
"Run a vterm with lldb for the current buffer's directory, default DIR. Launch a lldb-ui instance unless prefix arg."
(interactive "DDirectory:")
(let* ((session-name (file-name-nondirectory(directory-file-name dir)))
(lldb-ui-command (format "%s %s %s &" rgr/lldb-ui-command dir session-name))
(vterm-buffer-name (format "*%s:%s*" rgr/lldb-voltron-buffer-name session-name)))
(if (get-buffer vterm-buffer-name)
(switch-to-buffer vterm-buffer-name)
(progn
(with-current-buffer (vterm)
(process-send-string (current-buffer) (format "lldb && tmux kill-session -t %s && exit\n" session-name))
(unless current-prefix-arg
(call-process-shell-command lldb-ui-command)
(process-send-string (current-buffer) "lv\n"))
(rgr/lldb-mode))))))
(use-package projectile
:bind
(:map projectile-command-map ("D" . rgr/lldb-voltron-vterm))
)
provide
(provide 'lldb-voltron)
support scripts
voltron support scripts
https://github.com/rileyrg/linux-init/tree/master/directories/bin/llvm
-
voltron-source
#!/usr/bin/env bash # Maintained in linux-config.org voltron v c 'source list -a $rip -c '"${1:-32}"''
-
voltron-disassembly
#!/usr/bin/env bash # Maintained in linux-config.org voltron v c 'disassemble --pc --context '"${1:-4}"' --count '"${2:-4}"''
-
voltron-disassembly-mixed
#!/usr/bin/env bash # Maintained in linux-config.org voltron v c 'disassemble --mixed --pc --context '"${1:-1}"' --count '"${2:-32}"''
-
voltron-locals
#!/usr/bin/env bash # Maintained in linux-config.org voltron v c 'frame variable' --lexer c
-
voltron-breakpoints
#!/usr/bin/env bash # Maintained in linux-config.org voltron v c 'breakpoint list'
-
voltron-registers
#!/usr/bin/env bash # Maintained in linux-config.org voltron v registers
-
voltron-backtrace
#!/usr/bin/env bash # Maintained in linux-config.org voltron v c 'thread backtrace'
-
voltron-stack
#!/usr/bin/env bash # Maintained in linux-config.org voltron v stack
lldb init and loading voltron
https://github.com/rileyrg/linux-init/blob/master/DotFiles/.lldbinit
The important bit is loading voltron support into lldb. This can be done via a .lldbinit file. At time of writing this init line create an lv alias to load voltron.
command alias lv command script import "/home/rgr/.local/lib/python3.9/site-packages/voltron/entry.py"
This alias is triggered in the elisp above.
The full .lldbinit file I use
# Maintained in linux-config.org
#settings write -f .lldb-settings-local-start
#settings read -f .lldb-settings-local
settings set target.load-cwd-lldbinit true
settings set interpreter.prompt-on-quit false
settings set target.x86-disassembly-flavor intel
command alias bfl breakpoint set -f %1 -l %2
command alias lv command script import "/home/rgr/.local/lib/python3.9/site-packages/voltron/entry.py"
command alias sl source list -a $rip
command alias so thread step-out
command regex srcb 's/([0-9]+)/settings set stop-line-count-before %1/'
srcb 0
command regex srca 's/([0-9]+)/settings set stop-line-count-after %1/'
srca 0
settings set stop-disassembly-display no-debuginfo
break set -p "inspect_"
#alias vtty = shell tmux-pane-tty voltron 4
#define voltron-source-tty
#shell tmux-pane-tty
#end
lldb-ui-session
lldb-ui-session creates a tmux session which you later attach to a terminal: It utilises a suite of small voltron snippets.
#!/usr/bin/env bash
# Maintained in linux-config.org
# create a lldb debug session unless it already exists.
# the -d to new session says "dont attach to current terminal"
# there is a bug where the splt panes split that of a tmux session in the terminal
# we issue the command from. No idea why or how.
# directory="$(realpath -s "${1:-`pwd`}")"
directory="${1:-`pwd`}"
session="${2:-"voltron-$(basename "$directory")"}"
if ! TMUX= tmux has-session -t "$session" &> /dev/null; then
tmux new-session -d -c "$directory" -s "$session" 'voltron-source 32'
firstPane=$(tmux display-message -p "#{pane_id}")
firstWindow=$(tmux display-message -p "#{window_id}")
srcPane="$firstPane"
tmux splitw -h -p 70 -t "$srcPane" voltron-disassembly-mixed
disassPane=$(tmux display-message -p "#{pane_id}")
tmux splitw -v -p 30 -t "$srcPane" voltron-locals
localsPane=$(tmux display-message -p "#{pane_id}")
tmux new-window voltron-source &> /dev/null
sourcePane=$(tmux display-message -p "#{pane_id}")
tmux splitw -v -p 30 -t "$sourcePane" voltron-locals
localsPane=$(tmux display-message -p "#{pane_id}")
tmux splitw -h -p 70 -t "$sourcePane" voltron-registers
registersPane=$(tmux display-message -p "#{pane_id}")
tmux splitw -h -p 70 -t "$localsPane" voltron-backtrace
backTracePane=$(tmux display-message -p "#{pane_id}")
tmux select-window -t "$firstWindow"
tmux select-pane -t "$firstPane"
fi
echo "$session"
lldb-ui
lldb-ui attaches to a lldb-ui-session:
#!/usr/bin/env bash
# Maintained in linux-config.org
directory="${1:-`pwd`}"
session="$(lldb-ui-session "${directory}" "$2")"
ONETERM_TITLE="dbg:lldb-$session" oneterminal "$session"
I use OneTerminal which moves to an existing window if already open. Its coded to use sway but obviously you can replace this with whatever.