Enable multithreaded debugging on Windows.
llvm-svn: 237392
This commit is contained in:
parent
c324b92c35
commit
d26b0c4d31
|
@ -283,9 +283,9 @@ DebuggerThread::HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thr
|
|||
{
|
||||
bool first_chance = (info.dwFirstChance != 0);
|
||||
|
||||
m_active_exception.reset(new ExceptionRecord(info.ExceptionRecord));
|
||||
m_active_exception.reset(new ExceptionRecord(info.ExceptionRecord, thread_id));
|
||||
WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_EXCEPTION,
|
||||
"HandleExceptionEvent encountered %s chance exception 0x%x on thread %u",
|
||||
"HandleExceptionEvent encountered %s chance exception 0x%x on thread 0x%x",
|
||||
first_chance ? "first" : "second", info.ExceptionRecord.ExceptionCode, thread_id);
|
||||
|
||||
ExceptionResult result = m_debug_delegate->OnDebugException(first_chance,
|
||||
|
@ -308,9 +308,11 @@ DWORD
|
|||
DebuggerThread::HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info, DWORD thread_id)
|
||||
{
|
||||
WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_THREAD,
|
||||
"HandleCreateThreadEvent Thread %u spawned in process %I64u",
|
||||
m_process.GetProcessId(), thread_id);
|
||||
|
||||
"HandleCreateThreadEvent Thread 0x%x spawned in process %I64u",
|
||||
thread_id, m_process.GetProcessId());
|
||||
HostThread thread(info.hThread);
|
||||
thread.GetNativeThread().SetOwnsHandle(false);
|
||||
m_debug_delegate->OnCreateThread(thread);
|
||||
return DBG_CONTINUE;
|
||||
}
|
||||
|
||||
|
@ -347,7 +349,7 @@ DebuggerThread::HandleExitThreadEvent(const EXIT_THREAD_DEBUG_INFO &info, DWORD
|
|||
WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_THREAD,
|
||||
"HandleExitThreadEvent Thread %u exited with code %u in process %I64u",
|
||||
thread_id, info.dwExitCode, m_process.GetProcessId());
|
||||
|
||||
m_debug_delegate->OnExitThread(thread_id, info.dwExitCode);
|
||||
return DBG_CONTINUE;
|
||||
}
|
||||
|
||||
|
@ -358,9 +360,9 @@ DebuggerThread::HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info, DWOR
|
|||
"HandleExitProcessEvent process %I64u exited with code %u",
|
||||
m_process.GetProcessId(), info.dwExitCode);
|
||||
|
||||
FreeProcessHandles();
|
||||
|
||||
m_debug_delegate->OnExitProcess(info.dwExitCode);
|
||||
|
||||
FreeProcessHandles();
|
||||
return DBG_CONTINUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,13 +30,14 @@ namespace lldb_private
|
|||
class ExceptionRecord
|
||||
{
|
||||
public:
|
||||
explicit ExceptionRecord(const EXCEPTION_RECORD &record)
|
||||
ExceptionRecord(const EXCEPTION_RECORD &record, lldb::tid_t thread_id)
|
||||
{
|
||||
m_code = record.ExceptionCode;
|
||||
m_continuable = (record.ExceptionFlags == 0);
|
||||
if (record.ExceptionRecord)
|
||||
m_next_exception.reset(new ExceptionRecord(*record.ExceptionRecord));
|
||||
m_next_exception.reset(new ExceptionRecord(*record.ExceptionRecord, thread_id));
|
||||
m_exception_addr = reinterpret_cast<lldb::addr_t>(record.ExceptionAddress);
|
||||
m_thread_id = thread_id;
|
||||
m_arguments.assign(record.ExceptionInformation, record.ExceptionInformation + record.NumberParameters);
|
||||
}
|
||||
virtual ~ExceptionRecord() {}
|
||||
|
@ -62,11 +63,18 @@ class ExceptionRecord
|
|||
return m_exception_addr;
|
||||
}
|
||||
|
||||
lldb::tid_t
|
||||
GetThreadID() const
|
||||
{
|
||||
return m_thread_id;
|
||||
}
|
||||
|
||||
private:
|
||||
DWORD m_code;
|
||||
bool m_continuable;
|
||||
std::shared_ptr<ExceptionRecord> m_next_exception;
|
||||
lldb::addr_t m_exception_addr;
|
||||
lldb::tid_t m_thread_id;
|
||||
std::vector<ULONG_PTR> m_arguments;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ class IDebugDelegate
|
|||
virtual void OnDebuggerConnected(lldb::addr_t image_base) = 0;
|
||||
virtual ExceptionResult OnDebugException(bool first_chance, const ExceptionRecord &record) = 0;
|
||||
virtual void OnCreateThread(const HostThread &thread) = 0;
|
||||
virtual void OnExitThread(const HostThread &thread) = 0;
|
||||
virtual void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) = 0;
|
||||
virtual void OnLoadDll(const ModuleSpec &module_spec, lldb::addr_t module_addr) = 0;
|
||||
virtual void OnUnloadDll(lldb::addr_t module_addr) = 0;
|
||||
virtual void OnDebugString(const std::string &string) = 0;
|
||||
|
|
|
@ -43,9 +43,9 @@ LocalDebugDelegate::OnCreateThread(const HostThread &thread)
|
|||
}
|
||||
|
||||
void
|
||||
LocalDebugDelegate::OnExitThread(const HostThread &thread)
|
||||
LocalDebugDelegate::OnExitThread(lldb::tid_t thread_id, uint32_t exit_code)
|
||||
{
|
||||
((ProcessWindows &)*m_process).OnExitThread(thread);
|
||||
((ProcessWindows &)*m_process).OnExitThread(thread_id, exit_code);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -46,7 +46,7 @@ class LocalDebugDelegate : public IDebugDelegate
|
|||
void OnDebuggerConnected(lldb::addr_t image_base) override;
|
||||
ExceptionResult OnDebugException(bool first_chance, const ExceptionRecord &record) override;
|
||||
void OnCreateThread(const HostThread &thread) override;
|
||||
void OnExitThread(const HostThread &thread) override;
|
||||
void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) override;
|
||||
void OnLoadDll(const lldb_private::ModuleSpec &module_spec, lldb::addr_t module_addr) override;
|
||||
void OnUnloadDll(lldb::addr_t module_addr) override;
|
||||
void OnDebugString(const std::string &message) override;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
// C++ Includes
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
// Other libraries and framework includes
|
||||
|
@ -78,7 +79,7 @@ class ProcessWindowsData
|
|||
HANDLE m_initial_stop_event;
|
||||
bool m_initial_stop_received;
|
||||
std::map<lldb::tid_t, HostThread> m_new_threads;
|
||||
std::map<lldb::tid_t, HostThread> m_exited_threads;
|
||||
std::set<lldb::tid_t> m_exited_threads;
|
||||
};
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -425,11 +426,11 @@ ProcessWindows::RefreshStateAfterStop()
|
|||
}
|
||||
|
||||
StopInfoSP stop_info;
|
||||
m_thread_list.SetSelectedThreadByID(active_exception->GetThreadID());
|
||||
ThreadSP stop_thread = m_thread_list.GetSelectedThread();
|
||||
RegisterContextSP register_context = stop_thread->GetRegisterContext();
|
||||
|
||||
// The current EIP is AFTER the BP opcode, which is one byte.
|
||||
// TODO(zturner): Can't we just use active_exception->GetExceptionAddress()?
|
||||
uint64_t pc = register_context->GetPC() - 1;
|
||||
if (active_exception->GetExceptionCode() == EXCEPTION_BREAKPOINT)
|
||||
{
|
||||
|
@ -445,7 +446,8 @@ ProcessWindows::RefreshStateAfterStop()
|
|||
if (site->ValidForThisThread(stop_thread.get()))
|
||||
{
|
||||
WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION,
|
||||
"Breakpoint site %d is valid for this thread, creating stop info.", site->GetID());
|
||||
"Breakpoint site %d is valid for this thread (0x%I64x), creating stop info.",
|
||||
site->GetID(), stop_thread->GetID());
|
||||
|
||||
stop_info = StopInfo::CreateStopReasonWithBreakpointSiteID(
|
||||
*stop_thread, site->GetID());
|
||||
|
@ -471,8 +473,8 @@ ProcessWindows::RefreshStateAfterStop()
|
|||
{
|
||||
std::string desc;
|
||||
llvm::raw_string_ostream desc_stream(desc);
|
||||
desc_stream << "Exception 0x" << llvm::format_hex(active_exception->GetExceptionCode(), 8)
|
||||
<< " encountered at address 0x" << llvm::format_hex(pc, 8);
|
||||
desc_stream << "Exception " << llvm::format_hex(active_exception->GetExceptionCode(), 8)
|
||||
<< " encountered at address " << llvm::format_hex(pc, 8);
|
||||
stop_info = StopInfo::CreateStopReasonWithException(*stop_thread, desc_stream.str().c_str());
|
||||
stop_thread->SetStopInfo(stop_info);
|
||||
WINLOG_IFALL(WINDOWS_LOG_EXCEPTION, desc_stream.str().c_str());
|
||||
|
@ -701,7 +703,7 @@ ProcessWindows::OnDebugException(bool first_chance, const ExceptionRecord &recor
|
|||
if (!m_session_data)
|
||||
{
|
||||
WINERR_IFANY(WINDOWS_LOG_EXCEPTION,
|
||||
"Debugger thread reported exception 0x%u at address 0x%I64x, but there is no session.",
|
||||
"Debugger thread reported exception 0x%x at address 0x%I64x, but there is no session.",
|
||||
record.GetExceptionCode(), record.GetExceptionAddress());
|
||||
return ExceptionResult::SendToApplication;
|
||||
}
|
||||
|
@ -732,7 +734,7 @@ ProcessWindows::OnDebugException(bool first_chance, const ExceptionRecord &recor
|
|||
break;
|
||||
default:
|
||||
WINLOG_IFANY(WINDOWS_LOG_EXCEPTION,
|
||||
"Debugger thread reported exception 0x%u at address 0x%I64x (first_chance=%s)",
|
||||
"Debugger thread reported exception 0x%x at address 0x%I64x (first_chance=%s)",
|
||||
record.GetExceptionCode(), record.GetExceptionAddress(), BOOL_STR(first_chance));
|
||||
// For non-breakpoints, give the application a chance to handle the exception first.
|
||||
if (first_chance)
|
||||
|
@ -753,18 +755,22 @@ ProcessWindows::OnCreateThread(const HostThread &new_thread)
|
|||
}
|
||||
|
||||
void
|
||||
ProcessWindows::OnExitThread(const HostThread &exited_thread)
|
||||
ProcessWindows::OnExitThread(lldb::tid_t thread_id, uint32_t exit_code)
|
||||
{
|
||||
llvm::sys::ScopedLock lock(m_mutex);
|
||||
|
||||
// On a forced termination, we may get exit thread events after the session
|
||||
// data has been cleaned up.
|
||||
if (!m_session_data)
|
||||
return;
|
||||
|
||||
// A thread may have started and exited before the debugger stopped allowing a refresh.
|
||||
// Just remove it from the new threads list in that case.
|
||||
const HostThreadWindows &wexited_thread = exited_thread.GetNativeThread();
|
||||
auto iter = m_session_data->m_new_threads.find(wexited_thread.GetThreadId());
|
||||
auto iter = m_session_data->m_new_threads.find(thread_id);
|
||||
if (iter != m_session_data->m_new_threads.end())
|
||||
m_session_data->m_new_threads.erase(iter);
|
||||
else
|
||||
m_session_data->m_exited_threads[wexited_thread.GetThreadId()] = exited_thread;
|
||||
m_session_data->m_exited_threads.insert(thread_id);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -109,7 +109,7 @@ public:
|
|||
void OnDebuggerConnected(lldb::addr_t image_base) override;
|
||||
ExceptionResult OnDebugException(bool first_chance, const lldb_private::ExceptionRecord &record) override;
|
||||
void OnCreateThread(const lldb_private::HostThread &thread) override;
|
||||
void OnExitThread(const lldb_private::HostThread &thread) override;
|
||||
void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) override;
|
||||
void OnLoadDll(const lldb_private::ModuleSpec &module_spec, lldb::addr_t module_addr) override;
|
||||
void OnUnloadDll(lldb::addr_t module_addr) override;
|
||||
void OnDebugString(const std::string &string) override;
|
||||
|
|
|
@ -247,8 +247,8 @@ ThreadList::ShouldStop (Event *event_ptr)
|
|||
// figuring out whether the thread plan conditions are met. So we don't want
|
||||
// to keep the ThreadList locked the whole time we are doing this.
|
||||
// FIXME: It is possible that running code could cause new threads
|
||||
// to be created. If that happens we will miss asking them whether
|
||||
// then should stop. This is not a big deal, since we haven't had
|
||||
// to be created. If that happens, we will miss asking them whether
|
||||
// they should stop. This is not a big deal since we haven't had
|
||||
// a chance to hang any interesting operations on those threads yet.
|
||||
|
||||
collection threads_copy;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
LEVEL = ../../make
|
||||
|
||||
C_SOURCES := main.c
|
||||
CXX_SOURCES := main.cpp
|
||||
ENABLE_THREADS := YES
|
||||
include $(LEVEL)/Makefile.rules
|
||||
|
|
|
@ -29,7 +29,7 @@ class NumberOfThreadsTestCase(TestBase):
|
|||
# Call super's setUp().
|
||||
TestBase.setUp(self)
|
||||
# Find the line number to break inside main().
|
||||
self.line = line_number('main.c', '// Set break point at this line.')
|
||||
self.line = line_number('main.cpp', '// Set break point at this line.')
|
||||
|
||||
def number_of_threads_test(self):
|
||||
"""Test number of threads."""
|
||||
|
@ -37,11 +37,11 @@ class NumberOfThreadsTestCase(TestBase):
|
|||
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
|
||||
|
||||
# This should create a breakpoint with 1 location.
|
||||
lldbutil.run_break_set_by_file_and_line (self, "main.c", self.line, num_expected_locations=1)
|
||||
lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1)
|
||||
|
||||
# The breakpoint list should show 3 locations.
|
||||
self.expect("breakpoint list -f", "Breakpoint location shown correctly",
|
||||
substrs = ["1: file = 'main.c', line = %d, locations = 1" % self.line])
|
||||
substrs = ["1: file = 'main.cpp', line = %d, locations = 1" % self.line])
|
||||
|
||||
# Run the program.
|
||||
self.runCmd("run", RUN_SUCCEEDED)
|
||||
|
@ -57,7 +57,9 @@ class NumberOfThreadsTestCase(TestBase):
|
|||
# Get the number of threads
|
||||
num_threads = process.GetNumThreads()
|
||||
|
||||
self.assertTrue(num_threads == 4, 'Number of expected threads and actual threads do not match.')
|
||||
# Using std::thread may involve extra threads, so we assert that there are
|
||||
# at least 4 rather than exactly 4.
|
||||
self.assertTrue(num_threads >= 4, 'Number of expected threads and actual threads do not match.')
|
||||
|
||||
if __name__ == '__main__':
|
||||
import atexit
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
#include <pthread.h>
|
||||
|
||||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
void *
|
||||
thread3 (void *input)
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
pthread_cond_signal(&cond); // Set break point at this line.
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *
|
||||
thread2 (void *input)
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
pthread_cond_signal(&cond);
|
||||
pthread_cond_wait(&cond, &mutex);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *
|
||||
thread1 (void *input)
|
||||
{
|
||||
pthread_t thread_2;
|
||||
pthread_create (&thread_2, NULL, thread2, NULL);
|
||||
|
||||
pthread_join(thread_2, NULL);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
pthread_t thread_1;
|
||||
pthread_t thread_3;
|
||||
|
||||
pthread_mutex_lock (&mutex);
|
||||
|
||||
pthread_create (&thread_1, NULL, thread1, NULL);
|
||||
|
||||
pthread_cond_wait (&cond, &mutex);
|
||||
|
||||
pthread_create (&thread_3, NULL, thread3, NULL);
|
||||
|
||||
pthread_cond_wait (&cond, &mutex);
|
||||
|
||||
pthread_mutex_unlock (&mutex);
|
||||
|
||||
pthread_join (thread_1, NULL);
|
||||
pthread_join (thread_3, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
std::mutex mutex;
|
||||
std::condition_variable cond;
|
||||
|
||||
void *
|
||||
thread3(void *input)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cond.notify_all(); // Set break point at this line.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *
|
||||
thread2(void *input)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cond.notify_all();
|
||||
cond.wait(lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *
|
||||
thread1(void *input)
|
||||
{
|
||||
std::thread thread_2(thread2, nullptr);
|
||||
thread_2.join();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
|
||||
std::thread thread_1(thread1, nullptr);
|
||||
cond.wait(lock);
|
||||
|
||||
std::thread thread_3(thread3, nullptr);
|
||||
cond.wait(lock);
|
||||
|
||||
lock.unlock();
|
||||
|
||||
thread_1.join();
|
||||
thread_3.join();
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue