Index: packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_case_sensitivity/TestBreakpointCaseSensitivity.py =================================================================== --- packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_case_sensitivity/TestBreakpointCaseSensitivity.py +++ packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_case_sensitivity/TestBreakpointCaseSensitivity.py @@ -25,7 +25,6 @@ self.case_sensitivity_breakpoint(True) @skipIf(oslist=['windows']) # Skip for windows platforms - @expectedFailureAll() # Failing for unknown reason on non-Windows platforms. def test_breakpoint_doesnt_match_file_with_different_case(self): """Set breakpoint on file, shouldn't match files with different case on POSIX systems""" self.build() @@ -86,15 +85,22 @@ lldb.SBFileSpec(file)) else: breakpoint = self.target.BreakpointCreateByLocation(file, self.line) + + # breakpoint should be always created + self.assertTrue(breakpoint, VALID_BREAKPOINT + desc) - self.assertEqual(breakpoint and breakpoint.GetNumLocations() == 1, - should_hit, + # check number of locations + self.assertEqual(breakpoint.GetNumLocations(), + int(should_hit), VALID_BREAKPOINT + desc) + # Get the breakpoint location from breakpoint after we verified that, # indeed, it has one location. location = breakpoint.GetLocationAtIndex(0) - self.assertEqual(location and location.IsEnabled(), + # location has to be converted to bool. If no location is present, GetLocationAtIndex returns None. + # Because None and xxxx = None, test will always fail (None != False and None != True) + self.assertEqual(bool(location) and location.IsEnabled(), should_hit, VALID_BREAKPOINT_LOCATION + desc) @@ -113,9 +119,9 @@ # check the breakpoint was not hit self.assertEqual(lldb.eStateExited, process.GetState()) self.assertEqual(breakpoint.GetHitCount(), 0) - - # let process finish - process.Continue() # cleanup self.target.BreakpointDelete(breakpoint.GetID()) + # let process finish + process.Continue() + \ No newline at end of file Index: packages/Python/lldbsuite/test/python_api/process/TestProcessAPI.py =================================================================== --- packages/Python/lldbsuite/test/python_api/process/TestProcessAPI.py +++ packages/Python/lldbsuite/test/python_api/process/TestProcessAPI.py @@ -284,3 +284,40 @@ if self.TraceOn() and error.Success(): print("Number of supported hardware watchpoints: %d" % num) + @add_test_categories(['pyapi']) + def test_continue_after_process_exit(self): + """Test SBProcess.Continue() API after the process exits.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Launch the process, and do not stop at the entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint") + + frame = thread.GetFrameAtIndex(0) + le = frame.GetLineEntry() + self.assertTrue(le.IsValid(), "There should be valid line entry at breakpoint") + self.assertEqual(self.line, le.GetLine(), "There should be valid line number") + + # Continue the return out of main + err = process.Continue() + self.assertTrue(err.Success(), "Continue after breakpoint should be valid") + + # At this point, the inferior process should have exited. + self.assertEqual(lldb.eStateExited, process.GetState(), PROCESS_EXITED) + + # Continue after proces exited should fail with good message, try it multiple times + for i in range(2): + err = process.Continue() + self.assertTrue(err.Fail(), "Continue after exit shouldn't be valid") + self.assertIn("Process is not alive", err.GetCString()) + \ No newline at end of file Index: source/Target/Process.cpp =================================================================== --- source/Target/Process.cpp +++ source/Target/Process.cpp @@ -1740,14 +1740,23 @@ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); if (log) log->Printf("Process::Resume -- locking run lock"); + + Error error; if (!m_public_run_lock.TrySetRunning()) { - Error error("Resume request failed - process still running."); + error.SetErrorString("Resume request failed - process still running."); if (log) log->Printf ("Process::Resume: -- TrySetRunning failed, not resuming."); return error; } - return PrivateResume(); + + error = PrivateResume(); + if (error.Fail()) + { + // PrivateResume failed, reset the public run lock back + m_public_run_lock.SetStopped(); + } + return error; } Error @@ -1775,6 +1784,11 @@ if (!StateIsStoppedState(state, must_be_alive)) error.SetErrorStringWithFormat("process not in stopped state after synchronous resume: %s", StateAsCString(state)); } + else + { + // PrivateResume failed, reset the public run lock back + m_public_run_lock.SetStopped(); + } // Undo the hijacking of process events... RestoreProcessEvents(); @@ -3543,7 +3557,16 @@ StateAsCString(m_public_state.GetValue()), StateAsCString(m_private_state.GetValue())); - Error error (WillResume()); + Error error; + // Cannot resume already exited process. Public running lock is + // held when PrivateResume is entered + if (!IsAlive()) + { + error.SetErrorString("Process is not alive"); + return error; + } + + error = WillResume(); // Tell the process it is about to resume before the thread list if (error.Success()) {