diff --git a/bin/verilator b/bin/verilator index 60abb6cc2..10176f45c 100755 --- a/bin/verilator +++ b/bin/verilator @@ -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 ") - # and not single ('run ') 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 ") + # and not single ('run ') 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; diff --git a/test_regress/driver.py b/test_regress/driver.py index 56ec0604c..ecaaca0a0 100755 --- a/test_regress/driver.py +++ b/test_regress/driver.py @@ -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: diff --git a/test_regress/t/t_runflag_logfile_debugger.py b/test_regress/t/t_runflag_logfile_debugger.py index 71d7ce734..d7b292e0d 100755 --- a/test_regress/t/t_runflag_logfile_debugger.py +++ b/test_regress/t/t_runflag_logfile_debugger.py @@ -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()