diff options
| author | Pavel Labath <labath@google.com> | 2015-08-20 09:06:12 +0000 |
|---|---|---|
| committer | Pavel Labath <labath@google.com> | 2015-08-20 09:06:12 +0000 |
| commit | 0e1d729b7541cb90c7e5d6107e96347abd120c39 (patch) | |
| tree | 834b2ee3104745d04828a2c653b6ab1264a5d3f9 /lldb/test/functionalities | |
| parent | cfc1df553e22f44b8b14a43da02f700297e7c52d (diff) | |
| download | bcm5719-llvm-0e1d729b7541cb90c7e5d6107e96347abd120c39.tar.gz bcm5719-llvm-0e1d729b7541cb90c7e5d6107e96347abd120c39.zip | |
[NativeProcessLinux] Fix a bug in instruction-stepping over thread creation
Summary:
There was a bug in NativeProcessLinux, where doing an instruction-level single-step over the
thread-creation syscall resulted in loss of control over the inferior. This happened because
after the inferior entered the thread-creation maintenance stop, we unconditionally performed a
PTRACE_CONT, even though the original intention was to do a PTRACE_SINGLESTEP. This is fixed by
storing the original state of the thread before the stop (stepping or running) and then
performing the appropriate action when resuming.
I also get rid of the callback in the ThreadContext structure, which stored the lambda used to
resume the thread, but which was not used consistently.
A test verifying the correctness of the new behavior is included.
Reviewers: ovyalov, tberghammer
Subscribers: lldb-commits
Differential Revision: http://reviews.llvm.org/D12104
llvm-svn: 245545
Diffstat (limited to 'lldb/test/functionalities')
3 files changed, 129 insertions, 0 deletions
diff --git a/lldb/test/functionalities/thread/create_during_instruction_step/Makefile b/lldb/test/functionalities/thread/create_during_instruction_step/Makefile new file mode 100644 index 00000000000..67aa16625bf --- /dev/null +++ b/lldb/test/functionalities/thread/create_during_instruction_step/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +ENABLE_THREADS := YES +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/thread/create_during_instruction_step/TestCreateDuringInstructionStep.py b/lldb/test/functionalities/thread/create_during_instruction_step/TestCreateDuringInstructionStep.py new file mode 100644 index 00000000000..116cd334ff0 --- /dev/null +++ b/lldb/test/functionalities/thread/create_during_instruction_step/TestCreateDuringInstructionStep.py @@ -0,0 +1,88 @@ +""" +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 + 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 + ]: + 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() diff --git a/lldb/test/functionalities/thread/create_during_instruction_step/main.cpp b/lldb/test/functionalities/thread/create_during_instruction_step/main.cpp new file mode 100644 index 00000000000..c50ce479079 --- /dev/null +++ b/lldb/test/functionalities/thread/create_during_instruction_step/main.cpp @@ -0,0 +1,36 @@ +//===-- 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 <atomic> +#include <thread> + +std::atomic<bool> 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; +} |

