| """Test that we handle inferiors which change their process group""" | |
| import os | |
| import lldb | |
| from lldbsuite.test.decorators import * | |
| from lldbsuite.test.lldbtest import * | |
| from lldbsuite.test import lldbutil | |
| class ChangeProcessGroupTestCase(TestBase): | |
| NO_DEBUG_INFO_TESTCASE = True | |
| def setUp(self): | |
| # Call super's setUp(). | |
| TestBase.setUp(self) | |
| # Find the line number to break for main.c. | |
| self.line = line_number("main.c", "// Set breakpoint here") | |
| @skipIfFreeBSD # Times out on FreeBSD llvm.org/pr23731 | |
| @skipIfWindows # setpgid call does not exist on Windows | |
| @expectedFailureAndroid("http://llvm.org/pr23762", api_levels=[16]) | |
| @expectedFailureNetBSD | |
| @skipIftvOS # fork not available on tvOS. | |
| @skipIfwatchOS # fork not available on watchOS. | |
| def test_setpgid(self): | |
| self.build() | |
| exe = self.getBuildArtifact("a.out") | |
| # Use a file as a synchronization point between test and inferior. | |
| pid_file_path = lldbutil.append_to_process_working_directory( | |
| self, "pid_file_%d" % (int(time.time())) | |
| ) | |
| self.addTearDownHook( | |
| lambda: self.run_platform_command("rm %s" % (pid_file_path)) | |
| ) | |
| popen = self.spawnSubprocess(exe, [pid_file_path]) | |
| pid = lldbutil.wait_for_file_on_target(self, pid_file_path) | |
| # make sure we cleanup the forked child also | |
| def cleanupChild(): | |
| if lldb.remote_platform: | |
| lldb.remote_platform.Kill(int(pid)) | |
| else: | |
| if os.path.exists("/proc/" + pid): | |
| os.kill(int(pid), signal.SIGKILL) | |
| self.addTearDownHook(cleanupChild) | |
| # Create a target by the debugger. | |
| target = self.dbg.CreateTarget(exe) | |
| self.assertTrue(target, VALID_TARGET) | |
| listener = lldb.SBListener("my.attach.listener") | |
| error = lldb.SBError() | |
| process = target.AttachToProcessWithID(listener, int(pid), error) | |
| self.assertTrue(error.Success() and process, PROCESS_IS_VALID) | |
| # set a breakpoint just before the setpgid() call | |
| lldbutil.run_break_set_by_file_and_line( | |
| self, "main.c", self.line, num_expected_locations=-1 | |
| ) | |
| thread = process.GetSelectedThread() | |
| # release the child from its loop | |
| value = thread.GetSelectedFrame().EvaluateExpression("release_child_flag = 1") | |
| self.assertTrue(value.IsValid()) | |
| self.assertEqual(value.GetValueAsUnsigned(0), 1) | |
| process.Continue() | |
| # make sure the child's process group id is different from its pid | |
| value = thread.GetSelectedFrame().EvaluateExpression("(int)getpgid(0)") | |
| self.assertTrue(value.IsValid()) | |
| self.assertNotEqual(value.GetValueAsUnsigned(0), int(pid)) | |
| # step over the setpgid() call | |
| thread.StepOver() | |
| self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonPlanComplete) | |
| # verify that the process group has been set correctly | |
| # this also checks that we are still in full control of the child | |
| value = thread.GetSelectedFrame().EvaluateExpression("(int)getpgid(0)") | |
| self.assertTrue(value.IsValid()) | |
| self.assertEqual(value.GetValueAsUnsigned(0), int(pid)) | |
| # run to completion | |
| process.Continue() | |
| self.assertState(process.GetState(), lldb.eStateExited) |