Index: lldb/packages/Python/lldbsuite/test/lldbutil.py =================================================================== --- lldb/packages/Python/lldbsuite/test/lldbutil.py +++ lldb/packages/Python/lldbsuite/test/lldbutil.py @@ -235,7 +235,7 @@ elif enum == lldb.eStateSuspended: return "suspended" else: - raise Exception("Unknown StateType enum") + raise Exception("Unknown StateType enum: " + str(enum)) def stop_reason_to_str(enum): @@ -860,11 +860,28 @@ def run_to_breakpoint_do_run(test, target, bkpt, launch_info = None, only_one_thread = True, extra_images = None): + # The (local) file path that the process will dump its stderr output in. + recorded_stderr_path = None + # Launch the process, and do not stop at the entry point. if not launch_info: launch_info = target.GetLaunchInfo() launch_info.SetWorkingDirectory(test.get_process_working_directory()) + # If we're not in remote mode, record the stderr of the process. + # Getting this to work in remote requires also transferring the file + # back afterwards. Windows also doesn't support redirecting stderr. + # + # Don't do this if the test specified its own launch_info as we don't + # know if this unintentionally overwrite an action of the test. + if not lldb.remote_platform and test.getPlatform() != "windows": + stderr_fd = 2 + recorded_stderr_path = test.getBuildArtifact("recorded_stderr.txt") + readMode = False + writeMode = True + launch_info.AddOpenFileAction(stderr_fd, recorded_stderr_path, + readMode, writeMode) + if extra_images: environ = test.registerSharedLibrariesWithTarget(target, extra_images) launch_info.SetEnvironmentEntries(environ, True) @@ -889,7 +906,25 @@ test.assertFalse(error.Fail(), "Process launch failed: %s" % (error.GetCString())) - test.assertEqual(process.GetState(), lldb.eStateStopped) + # If we asked the process to dump its stderr output, then read it now so + # the output can be appended to the errors below. + recorded_stderr = None + if recorded_stderr_path: + with open(recorded_stderr_path, 'r') as f: + recorded_stderr = f.read() + + def processStateInfo(process): + # If 'exited', print exit code and extracted description. + if process.state == lldb.eStateExited: + return ("Exit code/status: " + str(process.GetExitStatus()) + ". " + + "Exit description: " + str(process.exit_description)) + return "" + + if process.state != lldb.eStateStopped: + test.fail("Test process is not stopped at breakpoint, but instead in" + + " state '" + state_type_to_str(process.state) + "'. " + + processStateInfo(process) + ".\nstderr of inferior:\n" + + str(recorded_stderr)) # Frame #0 should be at our breakpoint. threads = get_threads_stopped_at_breakpoint( Index: lldb/test/API/lldbutil-tests/failed-to-hit-breakpoint/Makefile =================================================================== --- /dev/null +++ lldb/test/API/lldbutil-tests/failed-to-hit-breakpoint/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules Index: lldb/test/API/lldbutil-tests/failed-to-hit-breakpoint/TestLLDBUtilFailedToHitBreakpoint.py =================================================================== --- /dev/null +++ lldb/test/API/lldbutil-tests/failed-to-hit-breakpoint/TestLLDBUtilFailedToHitBreakpoint.py @@ -0,0 +1,30 @@ +""" +Tests lldbutil's behaviour when running to a source breakpoint fails. +""" + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from textwrap import dedent + + +class TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + NO_DEBUG_INFO_TESTCASE = True + + def test_error_message(self): + """ + Tests that run_to_source_breakpoint prints the right error message + when failing to hit the wanted breakpoint. + """ + self.build() + try: + lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.c")) + except AssertionError as e: + self.assertIn("Test process is not stopped at breakpoint, but " + + "instead in state 'exited'. Exit code/status: 0. " + + "Exit description: None.\nstderr of inferior:\n" + + "stderr_needle\n", str(e)) + else: + self.fail("Hit breakpoint in unreachable code path.") Index: lldb/test/API/lldbutil-tests/failed-to-hit-breakpoint/main.c =================================================================== --- /dev/null +++ lldb/test/API/lldbutil-tests/failed-to-hit-breakpoint/main.c @@ -0,0 +1,13 @@ +#include <stdio.h> + +int main(int argc, char **argv) { + // Prin the string that the test looks for to make sure stderr got recorded. + fprintf(stderr, "stderr_needle\n"); + // This is unreachable during normal test execution as we don't pass any + // (or +100) arguments. This still needs to be theoretically reachable code + // so that the compiler will generate code for this (that we can set a + // breakpoint on). + if (argc > 100) + return 1; // break here + return 0; +}