Index: lldb/trunk/test/functionalities/thread/create_during_instruction_step/Makefile =================================================================== --- lldb/trunk/test/functionalities/thread/create_during_instruction_step/Makefile +++ lldb/trunk/test/functionalities/thread/create_during_instruction_step/Makefile @@ -1,5 +0,0 @@ -LEVEL = ../../../make - -CXX_SOURCES := main.cpp -ENABLE_THREADS := YES -include $(LEVEL)/Makefile.rules Index: lldb/trunk/test/functionalities/thread/create_during_instruction_step/TestCreateDuringInstructionStep.py =================================================================== --- lldb/trunk/test/functionalities/thread/create_during_instruction_step/TestCreateDuringInstructionStep.py +++ lldb/trunk/test/functionalities/thread/create_during_instruction_step/TestCreateDuringInstructionStep.py @@ -1,91 +0,0 @@ -""" -This tests that we do not lose control of the inferior, while doing an instruction-level step -over a thread creation instruction. -""" - -import os -import unittest2 -import lldb -from lldbtest import * -import lldbutil - -class CreateDuringInstructionStepTestCase(TestBase): - - mydir = TestBase.compute_mydir(__file__) - - def setUp(self): - # Call super's setUp(). - TestBase.setUp(self) - # Find the line numbers to break and continue. - self.breakpoint = line_number('main.cpp', '// Set breakpoint here') - - @dsym_test - def test_step_inst_with_dsym(self): - self.buildDsym(dictionary=self.getBuildFlags()) - self.create_during_step_inst_test() - - @dwarf_test - @skipIfTargetAndroid(archs=['aarch64']) - @expectedFailureAndroid("llvm.org/pr23944", archs=['aarch64']) # We are unable to step through std::thread::_M_start_thread - def test_step_inst_with_dwarf(self): - self.buildDwarf(dictionary=self.getBuildFlags()) - self.create_during_step_inst_test() - - def create_during_step_inst_test(self): - exe = os.path.join(os.getcwd(), "a.out") - target = self.dbg.CreateTarget(exe) - self.assertTrue(target and target.IsValid(), "Target is valid") - - # This should create a breakpoint in the stepping thread. - self.bp_num = lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=-1) - - # Run the program. - process = target.LaunchSimple(None, None, self.get_process_working_directory()) - self.assertTrue(process and process.IsValid(), PROCESS_IS_VALID) - - # The stop reason of the thread should be breakpoint. - self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) - self.assertEqual(lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint).IsValid(), 1, - STOPPED_DUE_TO_BREAKPOINT) - - # Get the number of threads - num_threads = process.GetNumThreads() - - # Make sure we see only one threads - self.assertEqual(num_threads, 1, 'Number of expected threads and actual threads do not match.') - - thread = process.GetThreadAtIndex(0) - self.assertTrue(thread and thread.IsValid(), "Thread is valid") - - # Keep stepping until we see the thread creation - while process.GetNumThreads() < 2: - # This skips some functions we have trouble stepping into. Testing stepping - # through these is not the purpose of this test. We just want to find the - # instruction, which creates the thread. - if thread.GetFrameAtIndex(0).GetFunctionName() in [ - '__sync_fetch_and_add_4', # Android arm: unable to set a breakpoint for software single-step - 'pthread_mutex_lock', # Android arm: function contains atomic instruction sequences - 'pthread_mutex_unlock' # Android arm: function contains atomic instruction sequences - ]: - thread.StepOut() - else: - thread.StepInstruction(False) - self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) - self.assertEqual(thread.GetStopReason(), lldb.eStopReasonPlanComplete, "Step operation succeeded") - if self.TraceOn(): - self.runCmd("disassemble --pc") - - if self.TraceOn(): - self.runCmd("thread list") - - # We have successfully caught thread creation. Now just run to completion - process.Continue() - - # At this point, the inferior process should have exited. - self.assertEqual(process.GetState(), lldb.eStateExited, PROCESS_EXITED) - -if __name__ == '__main__': - import atexit - lldb.SBDebugger.Initialize() - atexit.register(lambda: lldb.SBDebugger.Terminate()) - unittest2.main() Index: lldb/trunk/test/functionalities/thread/create_during_instruction_step/main.cpp =================================================================== --- lldb/trunk/test/functionalities/thread/create_during_instruction_step/main.cpp +++ lldb/trunk/test/functionalities/thread/create_during_instruction_step/main.cpp @@ -1,36 +0,0 @@ -//===-- main.cpp ------------------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include -#include - -std::atomic flag(false); - -void do_nothing() -{ - while (flag) - ; -} - -int main () -{ - // Instruction-level stepping over a creation of the first thread takes a very long time, so - // we give the threading machinery a chance to initialize all its data structures. - // This way, stepping over the second thread will be much faster. - std::thread dummy(do_nothing); - dummy.join(); - - // Make sure the new thread does not exit before we get a chance to notice the main thread stopped - flag = true; - - std::thread thread(do_nothing); // Set breakpoint here - flag = false; // Release the new thread. - thread.join(); - return 0; -} Index: lldb/trunk/test/linux/thread/create_during_instruction_step/Makefile =================================================================== --- lldb/trunk/test/linux/thread/create_during_instruction_step/Makefile +++ lldb/trunk/test/linux/thread/create_during_instruction_step/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules Index: lldb/trunk/test/linux/thread/create_during_instruction_step/TestCreateDuringInstructionStep.py =================================================================== --- lldb/trunk/test/linux/thread/create_during_instruction_step/TestCreateDuringInstructionStep.py +++ lldb/trunk/test/linux/thread/create_during_instruction_step/TestCreateDuringInstructionStep.py @@ -0,0 +1,76 @@ +""" +This tests that we do not lose control of the inferior, while doing an instruction-level step +over a thread creation instruction. +""" + +import os +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class CreateDuringInstructionStepTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + @dsym_test + def test_step_inst_with_dsym(self): + self.buildDsym(dictionary=self.getBuildFlags()) + self.create_during_step_inst_test() + + @dwarf_test + def test_step_inst_with_dwarf(self): + self.buildDwarf(dictionary=self.getBuildFlags()) + self.create_during_step_inst_test() + + def create_during_step_inst_test(self): + exe = os.path.join(os.getcwd(), "a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target and target.IsValid(), "Target is valid") + + # This should create a breakpoint in the stepping thread. + breakpoint = target.BreakpointCreateByName("main") + self.assertTrue(breakpoint and breakpoint.IsValid(), "Breakpoint is valid") + + # Run the program. + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertTrue(process and process.IsValid(), PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + + threads = lldbutil.get_threads_stopped_at_breakpoint(process, breakpoint) + self.assertEquals(len(threads), 1, STOPPED_DUE_TO_BREAKPOINT) + + thread = threads[0] + self.assertTrue(thread and thread.IsValid(), "Thread is valid") + + # Make sure we see only one threads + self.assertEqual(process.GetNumThreads(), 1, 'Number of expected threads and actual threads do not match.') + + # Keep stepping until we see the thread creation + while process.GetNumThreads() < 2: + thread.StepInstruction(False) + self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) + self.assertEqual(thread.GetStopReason(), lldb.eStopReasonPlanComplete, "Step operation succeeded") + if self.TraceOn(): + self.runCmd("disassemble --pc") + + if self.TraceOn(): + self.runCmd("thread list") + + # We have successfully caught thread creation. Now just run to completion + process.Continue() + + # At this point, the inferior process should have exited. + self.assertEqual(process.GetState(), lldb.eStateExited, PROCESS_EXITED) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() Index: lldb/trunk/test/linux/thread/create_during_instruction_step/main.cpp =================================================================== --- lldb/trunk/test/linux/thread/create_during_instruction_step/main.cpp +++ lldb/trunk/test/linux/thread/create_during_instruction_step/main.cpp @@ -0,0 +1,55 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This file deliberately uses low level linux-specific API for thread creation because: +// - instruction-stepping over thread creation using higher-level functions was very slow +// - it was also unreliable due to single-stepping bugs unrelated to this test +// - some threading libraries do not create or destroy threads when we would expect them to + +#include + +#include +#include + +enum { STACK_SIZE = 0x2000 }; + +static uint8_t child_stack[STACK_SIZE]; + +pid_t child_tid; + +std::atomic flag(false); + +int thread_main(void *) +{ + while (! flag) // Make sure the thread does not exit prematurely + ; + + return 0; +} + +int main () +{ + int ret = clone(thread_main, + child_stack + STACK_SIZE/2, // Don't care whether the stack grows up or down, + // just point to the middle + CLONE_CHILD_CLEARTID | CLONE_FILES | CLONE_FS | CLONE_PARENT_SETTID | CLONE_SETTLS | + CLONE_SIGHAND | CLONE_SYSVSEM | CLONE_THREAD | CLONE_VM, + nullptr, // thread_main argument + &child_tid); + + if (ret == -1) + { + perror("clone"); + return 1; + } + + flag = true; + + return 0; +}