Support MacOS lldb (#7697)

This commit is contained in:
Tracy Narine 2026-06-02 16:42:42 -04:00 committed by GitHub
parent fe4adfe273
commit ee793669c5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 104 additions and 43 deletions

View File

@ -101,9 +101,15 @@ if (defined $ENV{VERILATOR_ROOT}) {
$ENV{VERILATOR_ROOT} = $verilator_root;
}
if ($opt_gdbbt && !gdb_works()) {
warn "-Info: --gdbbt ignored: gdb doesn't seem to be working\n" if $Debug;
$opt_gdbbt = 0;
my $lldb_selected = 0;
if (($opt_gdb || $opt_gdbbt) && !gdb_works()) {
if (lldb_works()) {
$lldb_selected = 1;
} else {
warn "-Info: --gdb or --gdbbt ignored: gdb doesn't seem to be working\n" if $Debug;
$opt_gdbbt = 0;
$opt_gdb = 0;
}
}
# Determine runtime flags and run
@ -113,22 +119,35 @@ if ($opt_gdbbt && !gdb_works()) {
# then see exactly the contents of @Opt_Verilator_Sw.
my @quoted_sw = map { sh_escape($_) } @Opt_Verilator_Sw;
if ($opt_gdb) {
# Generic GDB interactive
run (ulimit_stack_unlimited()
. aslr(0)
. ($ENV{VERILATOR_GDB} || "gdb")
. " " . verilator_bin()
# Note, uncomment to set breakpoints before running:
# ." -ex 'break main'"
# Note, we must use double-quotes ("run <switches>")
# and not single ('run <switches>') below. Bash swallows
# escapes as you would expect in a double-quoted string.
# That's not true for a single-quoted string, where \'
# actually terminates the string -- not what we want!
. " -ex \"run " . join(' ', @quoted_sw) . "\""
. " -ex 'set width 0'"
. " -ex 'bt'");
# Note, we must use double-quotes ("run <switches>")
# and not single ('run <switches>') below. Bash swallows
# escapes as you would expect in a double-quoted string.
# That's not true for a single-quoted string, where \'
# actually terminates the string -- not what we want!
if ($lldb_selected) {
# Generic lldb interactive
run (ulimit_stack_unlimited()
. aslr(0)
. ($ENV{VERILATOR_GDB} || "lldb")
. " " . verilator_bin()
# Note, uncomment to set breakpoints before running:
# . " -o 'b main'"
. " -o 'b exit'" # Need break to keep process active for bt
. " -o \"run " . join(' ', @quoted_sw) . "\""
. " -o 'settings set term-width 1024'"
. " -o 'bt'");
} else {
# Generic GDB interactive
run (ulimit_stack_unlimited()
. aslr(0)
. ($ENV{VERILATOR_GDB} || "gdb")
. " " . verilator_bin()
# Note, uncomment to set breakpoints before running:
# . " -ex 'break main'"
. " -ex \"run " . join(' ', @quoted_sw) . "\""
. " -ex 'set width 0'"
. " -ex 'bt'");
}
} elsif ($opt_rr) {
# Record with rr
run (ulimit_stack_unlimited()
@ -136,15 +155,27 @@ if ($opt_gdb) {
. "rr record " . verilator_bin()
. " " . join(' ', @quoted_sw));
} elsif ($opt_gdbbt && $Debug) {
# Run under GDB to get gdbbt
run (ulimit_stack_unlimited()
. aslr(0)
. "gdb"
. " " . verilator_bin()
. " --batch --quiet --return-child-result"
. " -ex \"run " . join(' ', @quoted_sw)."\""
. " -ex 'set width 0'"
. " -ex 'bt' -ex 'quit'");
# Run under debugger to get gdbbt
if ($lldb_selected) {
run (ulimit_stack_unlimited()
. aslr(0)
. ($ENV{VERILATOR_GDB} || "lldb")
. " " . verilator_bin()
. " -o 'b exit'" # Need break to keep process active for bt
. " --batch --source-quietly"
. " -o \"run " . join(' ', @quoted_sw)."\""
. " -o 'settings set term-width 1024'"
. " -o 'bt' -o 'quit'");
} else {
run (ulimit_stack_unlimited()
. aslr(0)
. ($ENV{VERILATOR_GDB} || "gdb")
. " " . verilator_bin()
. " --batch --quiet --return-child-result"
. " -ex \"run " . join(' ', @quoted_sw)."\""
. " -ex 'set width 0'"
. " -ex 'bt' -ex 'quit'");
}
} elsif ($opt_valgrind) {
# Run under valgrind
my $valgrind_bin = ($ENV{VERILATOR_VALGRIND} || "valgrind --error-exitcode=1 --max-stackframe=2815880"
@ -210,6 +241,18 @@ sub gdb_works {
return $status == 0;
}
sub lldb_works {
$! = undef; # Cleanup -x
# macOS system integrity stops attach to system binaries like /bin/sh
system("lldb --version"
. " --batch --source-quietly"
. " -o 'settings set term-width 1024'"
. " -o 'bt'"
. " -o 'quit'");
my $status = $?;
return $status == 0;
}
sub aslr {
my $want_on = shift;
$want_on = $opt_aslr if defined $opt_aslr;

View File

@ -143,6 +143,7 @@ class Capabilities:
_cached_have_dev_asan = None
_cached_have_dev_gcov = None
_cached_have_gdb = None
_cached_have_lldb = None
_cached_have_sc = None
_cached_have_solver = None
_cached_make_version = None
@ -196,6 +197,13 @@ class Capabilities:
Capabilities._cached_have_gdb = bool('Copyright' in out)
return Capabilities._cached_have_gdb
@staticproperty
def have_lldb() -> bool: # pylint: disable=no-method-argument
if Capabilities._cached_have_lldb is None:
out = VtOs.run_capture('lldb --help 2>/dev/null', check=False)
Capabilities._cached_have_lldb = bool('USAGE: lldb' in out)
return Capabilities._cached_have_lldb
@staticproperty
def have_sc() -> bool: # pylint: disable=no-method-argument
if Capabilities._cached_have_sc is None:
@ -237,6 +245,7 @@ class Capabilities:
_ignore = Capabilities.have_dev_asan
_ignore = Capabilities.have_dev_gcov
_ignore = Capabilities.have_gdb
_ignore = Capabilities.have_lldb
_ignore = Capabilities.have_sc
_ignore = Capabilities.have_solver
@ -1541,8 +1550,9 @@ class VlTest:
debugger_exec_cmd_start = ""
debugger_exec_cmd_end = ""
if Args.gdbsim:
debugger = VtOs.getenv_def('VERILATOR_GDB', "gdb") + " "
debugger_exec_cmd_start = " -ex 'run "
use_lldb = not self.have_gdb and self.have_lldb
debugger = VtOs.getenv_def('VERILATOR_GDB', "lldb" if use_lldb else "gdb") + " "
debugger_exec_cmd_start = " -b -o 'run " if use_lldb else " -ex 'run "
debugger_exec_cmd_end = "'"
cmd = [
run_env + debugger + 'vvp', debugger_exec_cmd_start,
@ -1649,14 +1659,20 @@ class VlTest:
if not param['executable']:
param['executable'] = self.obj_dir + "/" + param['vm_prefix']
debugger = ""
run_flags = ""
trailer = ""
if Args.gdbsim:
debugger = VtOs.getenv_def('VERILATOR_GDB', "gdb") + " "
use_lldb = not self.have_gdb and self.have_lldb
debugger = VtOs.getenv_def('VERILATOR_GDB', "lldb" if use_lldb else "gdb") + " "
run_flag = " -o " if use_lldb else " -ex "
run_flags = run_flag + "'run "
trailer = "'"
elif Args.rrsim:
debugger = "rr record "
cmd = [
(run_env + debugger + param['executable'] + (" -ex 'run " if Args.gdbsim else "")),
(run_env + debugger + param['executable'] + run_flags),
*param['all_run_flags'],
("'" if Args.gdbsim else ""),
trailer
]
cmd += self.driver_verilated_flags
self.run(
@ -1772,6 +1788,10 @@ class VlTest:
def have_coroutines(self) -> bool:
return Capabilities.have_coroutines
@property
def have_dbg(self) -> bool:
return Capabilities.have_gdb or Capabilities.have_lldb
@property
def have_dev_asan(self) -> bool:
return Capabilities.have_dev_asan
@ -1784,6 +1804,10 @@ class VlTest:
def have_gdb(self) -> bool:
return Capabilities.have_gdb
@property
def have_lldb(self) -> bool:
return Capabilities.have_lldb
@property
def have_sc(self) -> bool:
return Capabilities.have_sc
@ -2899,10 +2923,6 @@ def run_them() -> None:
if __name__ == '__main__':
os.environ['PYTHONUNBUFFERED'] = "1"
# GDB is broken on macOS
if platform.system() == "Darwin":
os.environ['VERILATOR_TEST_NO_GDB'] = "1"
if ('VERILATOR_ROOT' not in os.environ) and os.path.isfile('../bin/verilator'):
os.environ['VERILATOR_ROOT'] = os.getcwd() + "/.."
if 'MAKE' not in os.environ:

View File

@ -11,12 +11,10 @@ import vltest_bootstrap
test.scenarios('vlt')
have_gdb = 'VERILATOR_GDB' in os.environ
if not have_gdb:
if 'VERILATOR_TEST_NO_GDB' in os.environ:
test.skip("Skipping due to VERILATOR_TEST_NO_GDB")
if not test.have_gdb:
test.skip("No gdb installed")
if 'VERILATOR_TEST_NO_GDB' in os.environ:
test.skip("Skipping due to VERILATOR_TEST_NO_GDB")
if not test.have_dbg:
test.skip("No debugger installed")
test.compile()