Exemple #1
0
    def signal_two_signals(self):
        threads = self.start_threads(1)
        # try sending two different signals to two threads
        self.test_sequence.add_log_lines([
            "read packet: $vCont;C{0:x}:{1:x};C{2:x}:{3:x}#00".format(
                lldbutil.get_signal_number('SIGUSR1'), threads[0],
                lldbutil.get_signal_number('SIGUSR2'), threads[1]),
            {"direction": "send", "regex": r"^\$E1e#db$"},
        ], True)

        context = self.expect_gdbremote_sequence()
        self.assertIsNotNone(context)
Exemple #2
0
    def test_signal_process_without_tid(self):
        self.build()
        self.set_inferior_startup_launch()

        threads = self.start_threads(1)
        self.send_and_check_signal(
            "C{0:x}".format(lldbutil.get_signal_number('SIGUSR1')), threads)
    def test_signal_minus_one(self):
        self.build()
        self.set_inferior_startup_launch()

        threads = self.start_threads(1)
        self.send_and_check_signal(
            "C{0:x}:-1".format(lldbutil.get_signal_number('SIGUSR1')), threads)
 def test_inferior_seg_fault_received(self):
     self.build()
     if self.platformIsDarwin():
         self.inferior_seg_fault_received(
             self.GDB_REMOTE_STOP_CODE_BAD_ACCESS)
     else:
         self.inferior_seg_fault_received(
             lldbutil.get_signal_number('SIGSEGV'))
Exemple #5
0
    def test_signal_one_thread(self):
        self.build()
        self.set_inferior_startup_launch()

        threads = self.start_threads(1)
        # try sending a signal to one of the two threads
        self.send_and_check_signal(
            "C{0:x}:{1:x};c".format(lldbutil.get_signal_number('SIGUSR1')),
            threads[:1])
Exemple #6
0
    def test_signal_all_threads(self):
        self.build()
        self.set_inferior_startup_launch()

        threads = self.start_threads(1)
        # try sending a signal to two threads (= the process)
        self.send_and_check_signal(
            "C{0:x}:{1:x};C{0:x}:{2:x}".format(
                lldbutil.get_signal_number('SIGUSR1'), *threads), threads)
 def test_default_signals_behavior(self):
     self.build()
     self.set_inferior_startup_launch()
     procs = self.prep_debug_monitor_and_inferior()
     expected_signals = ["SIGSEGV", "SIGUSR1", "SIGUSR2",
         "SIGALRM", "SIGFPE", "SIGBUS", "SIGINT", "SIGHUP"]
     for signal_name in expected_signals:
         signo = lldbutil.get_signal_number(signal_name)
         self.expect_signal(signo)
     self.expect_exit_code(0)
Exemple #8
0
    def test_Hc_then_Csignal_signals_correct_thread_launch(self):
        self.build()
        self.set_inferior_startup_launch()

        if self.platformIsDarwin():
            # Darwin debugserver translates some signals like SIGSEGV into some gdb
            # expectations about fixed signal numbers.
            self.Hc_then_Csignal_signals_correct_thread(self.TARGET_EXC_BAD_ACCESS)
        else:
            self.Hc_then_Csignal_signals_correct_thread(
                lldbutil.get_signal_number('SIGSEGV'))
Exemple #9
0
    def signal_one_thread(self):
        threads = self.start_threads(1)
        # try sending a signal to one of the two threads
        self.test_sequence.add_log_lines([
            "read packet: $vCont;C{0:x}:{1:x};c#00".format(
                lldbutil.get_signal_number('SIGUSR1'), threads[0]),
            {"direction": "send", "regex": r"^\$W00#b7$"},
        ], True)

        context = self.expect_gdbremote_sequence()
        self.assertIsNotNone(context)
 def test_default_signals_behavior(self):
     self.init_llgs_test()
     self.build()
     self.set_inferior_startup_launch()
     procs = self.prep_debug_monitor_and_inferior()
     expected_signals = ["SIGSEGV", "SIGUSR1", "SIGUSR2",
         "SIGALRM", "SIGFPE", "SIGBUS", "SIGINT", "SIGHUP"]
     for signal_name in expected_signals:
         signo = lldbutil.get_signal_number(signal_name)
         self.expect_signal(signo)
     self.expect_exit_code(0)
 def test_q_pass_signals(self):
     self.build()
     self.set_inferior_startup_launch()
     procs = self.prep_debug_monitor_and_inferior()
     expected_signals = ["SIGSEGV",
         "SIGALRM", "SIGFPE", "SIGBUS", "SIGINT", "SIGHUP"]
     signals_to_ignore = ["SIGUSR1", "SIGUSR2"]
     self.ignore_signals(signals_to_ignore)
     for signal_name in expected_signals:
         signo = lldbutil.get_signal_number(signal_name)
         self.expect_signal(signo)
     self.expect_exit_code(len(signals_to_ignore))
Exemple #12
0
    def signal_two_of_three_threads(self):
        threads = self.start_threads(2)
        # try sending a signal to 2 out of 3 threads
        self.test_sequence.add_log_lines([
            "read packet: $vCont;C{0:x}:{1:x};C{0:x}:{2:x};c#00".format(
                lldbutil.get_signal_number('SIGUSR1'),
                threads[1], threads[2]),
            {"direction": "send", "regex": r"^\$E1e#db$"},
        ], True)

        context = self.expect_gdbremote_sequence()
        self.assertIsNotNone(context)
Exemple #13
0
    def test_signal_all_threads(self):
        self.build()
        self.set_inferior_startup_launch()

        threads = self.start_threads(1)
        # try sending a signal to two threads (= the process)
        self.test_sequence.add_log_lines([
            "read packet: $vCont;C{0:x}:{1:x};C{0:x}:{2:x}#00".format(
                lldbutil.get_signal_number('SIGUSR1'),
                threads[0], threads[1]),
            {"direction": "send", "regex": r"^\$W00#b7$"},
        ], True)

        context = self.expect_gdbremote_sequence()
        self.assertIsNotNone(context)
    def test_change_signals_at_runtime(self):
        self.init_llgs_test()
        self.build()
        self.set_inferior_startup_launch()
        procs = self.prep_debug_monitor_and_inferior()
        expected_signals = ["SIGSEGV", "SIGUSR1", "SIGUSR2",
            "SIGALRM", "SIGHUP"]
        signals_to_ignore = ["SIGFPE", "SIGBUS", "SIGINT"]

        for signal_name in expected_signals:
            signo = lldbutil.get_signal_number(signal_name)
            self.expect_signal(signo)
            if signal_name == "SIGALRM":
                self.ignore_signals(signals_to_ignore)
        self.expect_exit_code(len(signals_to_ignore))
    def inferior_abort_received(self):
        procs = self.prep_debug_monitor_and_inferior(inferior_args=["abort"])
        self.assertIsNotNone(procs)

        self.test_sequence.add_log_lines([
            "read packet: $vCont;c#a8",
            {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2}).*#[0-9a-fA-F]{2}$", "capture":{ 1:"hex_exit_code"} },
            ], True)

        context = self.expect_gdbremote_sequence()
        self.assertIsNotNone(context)

        hex_exit_code = context.get("hex_exit_code")
        self.assertIsNotNone(hex_exit_code)
        self.assertEqual(int(hex_exit_code, 16),
                          lldbutil.get_signal_number('SIGABRT'))
Exemple #16
0
    def run_and_check_name(self, expected_name):
        self.test_sequence.add_log_lines(["read packet: $vCont;c#a8",
                                          {"direction": "send",
                                           "regex":
                                           r"^\$T([0-9a-fA-F]{2})([^#]+)#[0-9a-fA-F]{2}$",
                                           "capture": {
                                               1: "signal",
                                               2: "key_vals_text"}},
                                          ],
                                         True)

        context = self.expect_gdbremote_sequence()
        self.assertIsNotNone(context)

        sigint = lldbutil.get_signal_number("SIGINT")
        self.assertEqual(sigint, int(context.get("signal"), 16))
        kv_dict = self.parse_key_val_dict(context.get("key_vals_text"))
        self.assertEqual(expected_name, kv_dict.get("name"))
    def run_and_check_name(self, expected_name):
        self.test_sequence.add_log_lines(["read packet: $vCont;c#a8",
                                          {"direction": "send",
                                           "regex":
                                           r"^\$T([0-9a-fA-F]{2})([^#]+)#[0-9a-fA-F]{2}$",
                                           "capture": {
                                               1: "signal",
                                               2: "key_vals_text"}},
                                          ],
                                         True)

        context = self.expect_gdbremote_sequence()
        self.assertIsNotNone(context)

        sigint = lldbutil.get_signal_number("SIGINT")
        self.assertEqual(sigint, int(context.get("signal"), 16))
        kv_dict = self.parse_key_val_dict(context.get("key_vals_text"))
        self.assertEqual(expected_name, kv_dict.get("name"))
Exemple #18
0
    def inferior_abort_received(self):
        procs = self.prep_debug_monitor_and_inferior(inferior_args=["abort"])
        self.assertIsNotNone(procs)

        self.test_sequence.add_log_lines(["read packet: $vCont;c#a8",
                                          {"direction": "send",
                                           "regex": r"^\$T([0-9a-fA-F]{2}).*#[0-9a-fA-F]{2}$",
                                           "capture": {1: "hex_exit_code"}},
                                          ],
                                         True)

        context = self.expect_gdbremote_sequence()
        self.assertIsNotNone(context)

        hex_exit_code = context.get("hex_exit_code")
        self.assertIsNotNone(hex_exit_code)
        self.assertEqual(int(hex_exit_code, 16),
                         lldbutil.get_signal_number('SIGABRT'))
 def signal_name_to_hex(signame):
     return format(lldbutil.get_signal_number(signame), 'x')
 def test_inferior_seg_fault_received_llgs(self):
     self.build()
     self.inferior_seg_fault_received(lldbutil.get_signal_number('SIGSEGV'))
    def test_with_run_command(self):
        """Test that lldb command 'process signal SIGUSR1' sends a signal to the inferior process."""
        self.build()
        exe = self.getBuildArtifact("a.out")

        # Create a target by the debugger.
        target = self.dbg.CreateTarget(exe)
        self.assertTrue(target, VALID_TARGET)

        # Now create a breakpoint on main.c by name 'c'.
        breakpoint = target.BreakpointCreateByLocation('main.c', self.line)
        self.assertTrue(breakpoint and
                        breakpoint.GetNumLocations() == 1,
                        VALID_BREAKPOINT)

        # Get the breakpoint location from breakpoint after we verified that,
        # indeed, it has one location.
        location = breakpoint.GetLocationAtIndex(0)
        self.assertTrue(location and
                        location.IsEnabled(),
                        VALID_BREAKPOINT_LOCATION)

        # Now launch the process, no arguments & do not stop at entry point.
        launch_info = target.GetLaunchInfo()
        launch_info.SetWorkingDirectory(self.get_process_working_directory())

        process_listener = lldb.SBListener("signal_test_listener")
        launch_info.SetListener(process_listener)
        error = lldb.SBError()
        process = target.Launch(launch_info, error)
        self.assertTrue(process, PROCESS_IS_VALID)

        self.runCmd("process handle -n False -p True -s True SIGUSR1")

        thread = lldbutil.get_stopped_thread(
            process, lldb.eStopReasonBreakpoint)
        self.assertTrue(thread.IsValid(), "We hit the first breakpoint.")

        # After resuming the process, send it a SIGUSR1 signal.

        self.setAsync(True)

        self.assertTrue(
            process_listener.IsValid(),
            "Got a good process listener")

        # Disable our breakpoint, we don't want to hit it anymore...
        breakpoint.SetEnabled(False)

        # Now continue:
        process.Continue()

        # If running remote test, there should be a connected event
        if lldb.remote_platform:
            self.match_state(process_listener, lldb.eStateConnected)

        self.match_state(process_listener, lldb.eStateRunning)

        # Now signal the process, and make sure it stops:
        process.Signal(lldbutil.get_signal_number('SIGUSR1'))

        self.match_state(process_listener, lldb.eStateStopped)

        # Now make sure the thread was stopped with a SIGUSR1:
        threads = lldbutil.get_stopped_threads(process, lldb.eStopReasonSignal)
        self.assertEquals(len(threads), 1, "One thread stopped for a signal.")
        thread = threads[0]

        self.assertTrue(
            thread.GetStopReasonDataCount() >= 1,
            "There was data in the event.")
        self.assertTrue(
            thread.GetStopReasonDataAtIndex(0) == lldbutil.get_signal_number('SIGUSR1'),
            "The stop signal was SIGUSR1")
 def test_inferior_seg_fault_received_llgs(self):
     self.init_llgs_test()
     self.build()
     self.inferior_seg_fault_received(lldbutil.get_signal_number('SIGSEGV'))
Exemple #23
0
    def breakpoint_set_and_remove_work(self, want_hardware):
        # Start up the inferior.
        procs = self.prep_debug_monitor_and_inferior(inferior_args=[
            "get-code-address-hex:hello", "sleep:1", "call-function:hello"
        ])

        # Run the process
        self.add_register_info_collection_packets()
        self.add_process_info_collection_packets()
        self.test_sequence.add_log_lines(
            [  # Start running after initial stop.
                "read packet: $c#63",
                # Match output line that prints the memory address of the function call entry point.
                # Note we require launch-only testing so we can get inferior otuput.
                {
                    "type":
                    "output_match",
                    "regex":
                    self.maybe_strict_output_regex(
                        r"code address: 0x([0-9a-fA-F]+)\r\n"),
                    "capture": {
                        1: "function_address"
                    }
                },
                # Now stop the inferior.
                "read packet: {}".format(chr(3)),
                # And wait for the stop notification.
                {
                    "direction": "send",
                    "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);",
                    "capture": {
                        1: "stop_signo",
                        2: "stop_thread_id"
                    }
                }
            ],
            True)

        # Run the packet stream.
        context = self.expect_gdbremote_sequence()
        self.assertIsNotNone(context)

        # Gather process info - we need endian of target to handle register
        # value conversions.
        process_info = self.parse_process_info_response(context)
        endian = process_info.get("endian")
        self.assertIsNotNone(endian)

        # Gather register info entries.
        reg_infos = self.parse_register_info_packets(context)
        (pc_lldb_reg_index, pc_reg_info) = self.find_pc_reg_info(reg_infos)
        self.assertIsNotNone(pc_lldb_reg_index)
        self.assertIsNotNone(pc_reg_info)

        # Grab the function address.
        self.assertIsNotNone(context.get("function_address"))
        function_address = int(context.get("function_address"), 16)

        # Get current target architecture
        target_arch = self.getArchitecture()

        # Set the breakpoint.
        if (target_arch == "arm") or (target_arch == "aarch64"):
            # TODO: Handle case when setting breakpoint in thumb code
            BREAKPOINT_KIND = 4
        else:
            BREAKPOINT_KIND = 1

        # Set default packet type to Z0 (software breakpoint)
        z_packet_type = 0

        # If hardware breakpoint is requested set packet type to Z1
        if want_hardware == True:
            z_packet_type = 1

        self.reset_test_sequence()
        self.add_set_breakpoint_packets(function_address,
                                        z_packet_type,
                                        do_continue=True,
                                        breakpoint_kind=BREAKPOINT_KIND)

        # Run the packet stream.
        context = self.expect_gdbremote_sequence()
        self.assertIsNotNone(context)

        # Verify the stop signal reported was the breakpoint signal number.
        stop_signo = context.get("stop_signo")
        self.assertIsNotNone(stop_signo)
        self.assertEqual(int(stop_signo, 16),
                         lldbutil.get_signal_number('SIGTRAP'))

        # Ensure we did not receive any output.  If the breakpoint was not set, we would
        # see output (from a launched process with captured stdio) printing a hello, world message.
        # That would indicate the breakpoint didn't take.
        self.assertEqual(len(context["O_content"]), 0)

        # Verify that the PC for the main thread is where we expect it - right at the breakpoint address.
        # This acts as a another validation on the register reading code.
        self.reset_test_sequence()
        self.test_sequence.add_log_lines(
            [
                # Print the PC.  This should match the breakpoint address.
                "read packet: $p{0:x}#00".format(pc_lldb_reg_index),
                # Capture $p results.
                {
                    "direction": "send",
                    "regex": r"^\$([0-9a-fA-F]+)#",
                    "capture": {
                        1: "p_response"
                    }
                },
            ],
            True)

        context = self.expect_gdbremote_sequence()
        self.assertIsNotNone(context)

        # Verify the PC is where we expect.  Note response is in endianness of
        # the inferior.
        p_response = context.get("p_response")
        self.assertIsNotNone(p_response)

        # Convert from target endian to int.
        returned_pc = lldbgdbserverutils.unpack_register_hex_unsigned(
            endian, p_response)
        self.assertEqual(returned_pc, function_address)

        # Verify that a breakpoint remove and continue gets us the expected
        # output.
        self.reset_test_sequence()

        # Add breakpoint remove packets
        self.add_remove_breakpoint_packets(function_address,
                                           z_packet_type,
                                           breakpoint_kind=BREAKPOINT_KIND)

        self.test_sequence.add_log_lines(
            [
                # Continue running.
                "read packet: $c#63",
                # We should now receive the output from the call.
                {
                    "type": "output_match",
                    "regex": r"^hello, world\r\n$"
                },
                # And wait for program completion.
                {
                    "direction": "send",
                    "regex": r"^\$W00(.*)#[0-9a-fA-F]{2}$"
                },
            ],
            True)

        context = self.expect_gdbremote_sequence()
        self.assertIsNotNone(context)
 def signal_name_to_hex(signame):
     return format(lldbutil.get_signal_number(signame), 'x')
    def test_with_run_command(self):
        """Test that lldb command 'process signal SIGUSR1' sends a signal to the inferior process."""
        self.build()
        exe = os.path.join(os.getcwd(), "a.out")

        # Create a target by the debugger.
        target = self.dbg.CreateTarget(exe)
        self.assertTrue(target, VALID_TARGET)

        # Now create a breakpoint on main.c by name 'c'.
        breakpoint = target.BreakpointCreateByLocation('main.c', self.line)
        self.assertTrue(breakpoint and
                        breakpoint.GetNumLocations() == 1,
                        VALID_BREAKPOINT)

        # Get the breakpoint location from breakpoint after we verified that,
        # indeed, it has one location.
        location = breakpoint.GetLocationAtIndex(0)
        self.assertTrue(location and
                        location.IsEnabled(),
                        VALID_BREAKPOINT_LOCATION)

        # Now launch the process, no arguments & do not stop at entry point.
        launch_info = lldb.SBLaunchInfo([exe])
        launch_info.SetWorkingDirectory(self.get_process_working_directory())

        process_listener = lldb.SBListener("signal_test_listener")
        launch_info.SetListener(process_listener)
        error = lldb.SBError()
        process = target.Launch(launch_info, error)
        self.assertTrue(process, PROCESS_IS_VALID)

        self.runCmd("process handle -n False -p True -s True SIGUSR1")

        thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
        self.assertTrue(thread.IsValid(), "We hit the first breakpoint.")

        # After resuming the process, send it a SIGUSR1 signal.

        self.setAsync(True)

        self.assertTrue(process_listener.IsValid(), "Got a good process listener")

        # Disable our breakpoint, we don't want to hit it anymore...
        breakpoint.SetEnabled(False)

        # Now continue:
        process.Continue()

        # If running remote test, there should be a connected event
        if lldb.remote_platform:
            self.match_state(process_listener, lldb.eStateConnected)

        self.match_state(process_listener, lldb.eStateRunning)

        # Now signal the process, and make sure it stops:
        process.Signal(lldbutil.get_signal_number('SIGUSR1'))

        self.match_state(process_listener, lldb.eStateStopped)

        # Now make sure the thread was stopped with a SIGUSR1:
        threads = lldbutil.get_stopped_threads(process, lldb.eStopReasonSignal)
        self.assertTrue(len(threads) == 1, "One thread stopped for a signal.")
        thread = threads[0]

        self.assertTrue(thread.GetStopReasonDataCount() >= 1, "There was data in the event.")
        self.assertTrue(thread.GetStopReasonDataAtIndex(0) == lldbutil.get_signal_number('SIGUSR1'),
                "The stop signal was SIGUSR1")
Exemple #26
0
    def Hc_then_Csignal_signals_correct_thread(self, segfault_signo):
        # NOTE only run this one in inferior-launched mode: we can't grab inferior stdout when running attached,
        # and the test requires getting stdout from the exe.

        NUM_THREADS = 3

        # Startup the inferior with three threads (main + NUM_THREADS-1 worker threads).
        # inferior_args=["thread:print-ids"]
        inferior_args = ["thread:segfault"]
        for i in range(NUM_THREADS - 1):
            # if i > 0:
            # Give time between thread creation/segfaulting for the handler to work.
            # inferior_args.append("sleep:1")
            inferior_args.append("thread:new")
        inferior_args.append("sleep:10")

        # Launch/attach.  (In our case, this should only ever be launched since
        # we need inferior stdout/stderr).
        procs = self.prep_debug_monitor_and_inferior(
            inferior_args=inferior_args)
        self.test_sequence.add_log_lines(["read packet: $c#63"], True)
        context = self.expect_gdbremote_sequence()

        # Let the inferior process have a few moments to start up the thread when launched.
        # context = self.run_process_then_stop(run_seconds=1)

        # Wait at most x seconds for all threads to be present.
        # threads = self.wait_for_thread_count(NUM_THREADS)
        # self.assertEquals(len(threads), NUM_THREADS)

        signaled_tids = {}
        print_thread_ids = {}

        # Switch to each thread, deliver a signal, and verify signal delivery
        for i in range(NUM_THREADS - 1):
            # Run until SIGSEGV comes in.
            self.reset_test_sequence()
            self.test_sequence.add_log_lines([{
                "direction": "send",
                "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);",
                "capture": {
                    1: "signo",
                    2: "thread_id"
                }
            }], True)

            context = self.expect_gdbremote_sequence()
            self.assertIsNotNone(context)
            signo = context.get("signo")
            self.assertEqual(int(signo, 16), segfault_signo)

            # Ensure we haven't seen this tid yet.
            thread_id = int(context.get("thread_id"), 16)
            self.assertFalse(thread_id in signaled_tids)
            signaled_tids[thread_id] = 1

            # Send SIGUSR1 to the thread that signaled the SIGSEGV.
            self.reset_test_sequence()
            self.test_sequence.add_log_lines(
                [
                    # Set the continue thread.
                    # Set current thread.
                    "read packet: $Hc{0:x}#00".format(thread_id),
                    "send packet: $OK#00",

                    # Continue sending the signal number to the continue thread.
                    # The commented out packet is a way to do this same operation without using
                    # a $Hc (but this test is testing $Hc, so we'll stick with the former).
                    "read packet: $C{0:x}#00".format(
                        lldbutil.get_signal_number('SIGUSR1')),
                    # "read packet: $vCont;C{0:x}:{1:x};c#00".format(lldbutil.get_signal_number('SIGUSR1'), thread_id),

                    # FIXME: Linux does not report the thread stop on the delivered signal (SIGUSR1 here).  MacOSX debugserver does.
                    # But MacOSX debugserver isn't guaranteeing the thread the signal handler runs on, so currently its an XFAIL.
                    # Need to rectify behavior here.  The linux behavior is more intuitive to me since we're essentially swapping out
                    # an about-to-be-delivered signal (for which we already sent a stop packet) to a different signal.
                    # {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} },
                    #  "read packet: $c#63",
                    {
                        "type": "output_match",
                        "regex":
                        r"^received SIGUSR1 on thread id: ([0-9a-fA-F]+)\r\nthread ([0-9a-fA-F]+): past SIGSEGV\r\n",
                        "capture": {
                            1: "print_thread_id",
                            2: "post_handle_thread_id"
                        }
                    },
                ],
                True)

            # Run the sequence.
            context = self.expect_gdbremote_sequence()
            self.assertIsNotNone(context)

            # Ensure the stop signal is the signal we delivered.
            # stop_signo = context.get("stop_signo")
            # self.assertIsNotNone(stop_signo)
            # self.assertEquals(int(stop_signo,16), lldbutil.get_signal_number('SIGUSR1'))

            # Ensure the stop thread is the thread to which we delivered the signal.
            # stop_thread_id = context.get("stop_thread_id")
            # self.assertIsNotNone(stop_thread_id)
            # self.assertEquals(int(stop_thread_id,16), thread_id)

            # Ensure we haven't seen this thread id yet.  The inferior's
            # self-obtained thread ids are not guaranteed to match the stub
            # tids (at least on MacOSX).
            print_thread_id = context.get("print_thread_id")
            self.assertIsNotNone(print_thread_id)
            print_thread_id = int(print_thread_id, 16)
            self.assertFalse(print_thread_id in print_thread_ids)

            # Now remember this print (i.e. inferior-reflected) thread id and
            # ensure we don't hit it again.
            print_thread_ids[print_thread_id] = 1

            # Ensure post signal-handle thread id matches the thread that
            # initially raised the SIGSEGV.
            post_handle_thread_id = context.get("post_handle_thread_id")
            self.assertIsNotNone(post_handle_thread_id)
            post_handle_thread_id = int(post_handle_thread_id, 16)
            self.assertEqual(post_handle_thread_id, print_thread_id)