forked from OSchip/llvm-project
parent
d04893fa36
commit
610e52912d
|
@ -127,6 +127,9 @@ public:
|
|||
virtual void
|
||||
Error(const char *fmt, ...) __attribute__((format(printf, 2, 3)));
|
||||
|
||||
virtual void
|
||||
VAError(const char *format, va_list args) __attribute__((format(printf, 2, 3)));
|
||||
|
||||
virtual void
|
||||
FatalError(int err, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
|
||||
|
||||
|
|
|
@ -208,11 +208,18 @@ Log::LogIf(uint32_t bits, const char *format, ...)
|
|||
void
|
||||
Log::Error(const char *format, ...)
|
||||
{
|
||||
char *arg_msg = nullptr;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
::vasprintf(&arg_msg, format, args);
|
||||
VAError(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Log::VAError(const char *format, va_list args)
|
||||
{
|
||||
char *arg_msg = nullptr;
|
||||
::vasprintf(&arg_msg, format, args);
|
||||
|
||||
if (arg_msg == nullptr)
|
||||
return;
|
||||
|
@ -221,6 +228,7 @@ Log::Error(const char *format, ...)
|
|||
free(arg_msg);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Printing of errors that ARE fatal. Exit with ERR exit code
|
||||
// immediately.
|
||||
|
|
|
@ -171,6 +171,10 @@ SystemInitializerCommon::Terminate()
|
|||
PlatformDarwinKernel::Terminate();
|
||||
#endif
|
||||
|
||||
#if defined(__WIN32__)
|
||||
ProcessWindowsLog::Terminate();
|
||||
#endif
|
||||
|
||||
#ifndef LLDB_DISABLE_PYTHON
|
||||
OperatingSystemPython::Terminate();
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include "lldb/Host/windows/ProcessLauncherWindows.h"
|
||||
#include "lldb/Target/ProcessLaunchInfo.h"
|
||||
|
||||
#include "Plugins/Process/Windows/ProcessWindowsLog.h"
|
||||
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
|
@ -56,10 +58,19 @@ DebuggerThread::~DebuggerThread()
|
|||
Error
|
||||
DebuggerThread::DebugLaunch(const ProcessLaunchInfo &launch_info)
|
||||
{
|
||||
Error error;
|
||||
WINLOG_IFALL(WINDOWS_LOG_PROCESS,
|
||||
"DebuggerThread::DebugLaunch launching '%s'", launch_info.GetExecutableFile().GetPath().c_str());
|
||||
|
||||
Error error;
|
||||
DebugLaunchContext *context = new DebugLaunchContext(this, launch_info);
|
||||
HostThread slave_thread(ThreadLauncher::LaunchThread("lldb.plugin.process-windows.slave[?]", DebuggerThreadRoutine, context, &error));
|
||||
HostThread slave_thread(ThreadLauncher::LaunchThread("lldb.plugin.process-windows.slave[?]",
|
||||
DebuggerThreadRoutine, context, &error));
|
||||
|
||||
if (!error.Success())
|
||||
{
|
||||
WINERR_IFALL(WINDOWS_LOG_PROCESS,
|
||||
"DebugLaunch couldn't launch debugger thread. %s", error.AsCString());
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@ -80,6 +91,10 @@ DebuggerThread::DebuggerThreadRoutine(const ProcessLaunchInfo &launch_info)
|
|||
// thread routine has exited.
|
||||
std::shared_ptr<DebuggerThread> this_ref(shared_from_this());
|
||||
|
||||
WINLOG_IFALL(WINDOWS_LOG_PROCESS,
|
||||
"DebuggerThread preparing to launch '%s'.",
|
||||
launch_info.GetExecutableFile().GetPath().c_str());
|
||||
|
||||
Error error;
|
||||
ProcessLauncherWindows launcher;
|
||||
HostProcess process(launcher.LaunchProcess(launch_info, error));
|
||||
|
@ -101,29 +116,51 @@ DebuggerThread::StopDebugging(bool terminate)
|
|||
{
|
||||
Error error;
|
||||
|
||||
lldb::pid_t pid = m_process.GetProcessId();
|
||||
|
||||
WINLOG_IFALL(WINDOWS_LOG_PROCESS,
|
||||
"StopDebugging('%s') called (inferior=%I64u).",
|
||||
(terminate ? "true" : "false"), pid);
|
||||
|
||||
if (terminate)
|
||||
{
|
||||
// Make a copy of the process, since the termination sequence will reset DebuggerThread's
|
||||
// internal copy and it needs to remain open for us to perform the Wait operation.
|
||||
// Make a copy of the process, since the termination sequence will reset
|
||||
// DebuggerThread's internal copy and it needs to remain open for the Wait operation.
|
||||
HostProcess process_copy = m_process;
|
||||
lldb::process_t handle = process_copy.GetNativeProcess().GetSystemHandle();
|
||||
lldb::process_t handle = m_process.GetNativeProcess().GetSystemHandle();
|
||||
|
||||
// Initiate the termination before continuing the exception, so that the next debug event
|
||||
// we get is the exit process event, and not some other event.
|
||||
// Initiate the termination before continuing the exception, so that the next debug
|
||||
// event we get is the exit process event, and not some other event.
|
||||
BOOL terminate_suceeded = TerminateProcess(handle, 0);
|
||||
WINLOG_IFALL(WINDOWS_LOG_PROCESS,
|
||||
"StopDebugging called TerminateProcess(0x%p, 0) (inferior=%I64u), success='%s'",
|
||||
handle, pid, (terminate_suceeded ? "true" : "false"));
|
||||
|
||||
|
||||
// If we're stuck waiting for an exception to continue, continue it now. But only
|
||||
// AFTER setting the termination event, to make sure that we don't race and enter
|
||||
// another wait for another debug event.
|
||||
if (m_active_exception.get())
|
||||
{
|
||||
WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_EXCEPTION,
|
||||
"StopDebugging masking active exception");
|
||||
|
||||
ContinueAsyncException(ExceptionResult::MaskException);
|
||||
}
|
||||
|
||||
// Don't return until the process has exited.
|
||||
if (terminate_suceeded)
|
||||
{
|
||||
WINLOG_IFALL(WINDOWS_LOG_PROCESS,
|
||||
"StopDebugging waiting for termination of process %u to complete.", pid);
|
||||
|
||||
DWORD wait_result = ::WaitForSingleObject(handle, 5000);
|
||||
if (wait_result != WAIT_OBJECT_0)
|
||||
terminate_suceeded = false;
|
||||
|
||||
WINLOG_IFALL(WINDOWS_LOG_PROCESS,
|
||||
"StopDebugging WaitForSingleObject(0x%p, 5000) returned %u",
|
||||
handle, wait_result);
|
||||
}
|
||||
|
||||
if (!terminate_suceeded)
|
||||
|
@ -134,6 +171,13 @@ DebuggerThread::StopDebugging(bool terminate)
|
|||
error.SetErrorString("Detach not yet supported on Windows.");
|
||||
// TODO: Implement detach.
|
||||
}
|
||||
|
||||
if (!error.Success())
|
||||
{
|
||||
WINERR_IFALL(WINDOWS_LOG_PROCESS,
|
||||
"StopDebugging encountered an error while trying to stop process %u. %s",
|
||||
pid, error.AsCString());
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -143,6 +187,10 @@ DebuggerThread::ContinueAsyncException(ExceptionResult result)
|
|||
if (!m_active_exception.get())
|
||||
return;
|
||||
|
||||
WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_EXCEPTION,
|
||||
"ContinueAsyncException called for inferior process %I64u, broadcasting.",
|
||||
m_process.GetProcessId());
|
||||
|
||||
m_active_exception.reset();
|
||||
m_exception_pred.SetValue(result, eBroadcastAlways);
|
||||
}
|
||||
|
@ -164,51 +212,68 @@ DebuggerThread::DebugLoop()
|
|||
{
|
||||
DEBUG_EVENT dbe = {0};
|
||||
bool should_debug = true;
|
||||
while (should_debug && WaitForDebugEvent(&dbe, INFINITE))
|
||||
WINLOG_IFALL(WINDOWS_LOG_EVENT, "Entering WaitForDebugEvent loop");
|
||||
while (should_debug)
|
||||
{
|
||||
DWORD continue_status = DBG_CONTINUE;
|
||||
switch (dbe.dwDebugEventCode)
|
||||
WINLOGD_IFALL(WINDOWS_LOG_EVENT, "Calling WaitForDebugEvent");
|
||||
BOOL wait_result = WaitForDebugEvent(&dbe, INFINITE);
|
||||
if (wait_result)
|
||||
{
|
||||
case EXCEPTION_DEBUG_EVENT:
|
||||
DWORD continue_status = DBG_CONTINUE;
|
||||
switch (dbe.dwDebugEventCode)
|
||||
{
|
||||
ExceptionResult status = HandleExceptionEvent(dbe.u.Exception, dbe.dwThreadId);
|
||||
case EXCEPTION_DEBUG_EVENT:
|
||||
{
|
||||
ExceptionResult status = HandleExceptionEvent(dbe.u.Exception, dbe.dwThreadId);
|
||||
|
||||
if (status == ExceptionResult::MaskException)
|
||||
continue_status = DBG_CONTINUE;
|
||||
else if (status == ExceptionResult::SendToApplication)
|
||||
continue_status = DBG_EXCEPTION_NOT_HANDLED;
|
||||
break;
|
||||
}
|
||||
case CREATE_THREAD_DEBUG_EVENT:
|
||||
continue_status = HandleCreateThreadEvent(dbe.u.CreateThread, dbe.dwThreadId);
|
||||
break;
|
||||
case CREATE_PROCESS_DEBUG_EVENT:
|
||||
continue_status = HandleCreateProcessEvent(dbe.u.CreateProcessInfo, dbe.dwThreadId);
|
||||
break;
|
||||
case EXIT_THREAD_DEBUG_EVENT:
|
||||
continue_status = HandleExitThreadEvent(dbe.u.ExitThread, dbe.dwThreadId);
|
||||
break;
|
||||
case EXIT_PROCESS_DEBUG_EVENT:
|
||||
continue_status = HandleExitProcessEvent(dbe.u.ExitProcess, dbe.dwThreadId);
|
||||
should_debug = false;
|
||||
break;
|
||||
case LOAD_DLL_DEBUG_EVENT:
|
||||
continue_status = HandleLoadDllEvent(dbe.u.LoadDll, dbe.dwThreadId);
|
||||
break;
|
||||
case UNLOAD_DLL_DEBUG_EVENT:
|
||||
continue_status = HandleUnloadDllEvent(dbe.u.UnloadDll, dbe.dwThreadId);
|
||||
break;
|
||||
case OUTPUT_DEBUG_STRING_EVENT:
|
||||
continue_status = HandleODSEvent(dbe.u.DebugString, dbe.dwThreadId);
|
||||
break;
|
||||
case RIP_EVENT:
|
||||
continue_status = HandleRipEvent(dbe.u.RipInfo, dbe.dwThreadId);
|
||||
if (dbe.u.RipInfo.dwType == SLE_ERROR)
|
||||
if (status == ExceptionResult::MaskException)
|
||||
continue_status = DBG_CONTINUE;
|
||||
else if (status == ExceptionResult::SendToApplication)
|
||||
continue_status = DBG_EXCEPTION_NOT_HANDLED;
|
||||
break;
|
||||
}
|
||||
case CREATE_THREAD_DEBUG_EVENT:
|
||||
continue_status = HandleCreateThreadEvent(dbe.u.CreateThread, dbe.dwThreadId);
|
||||
break;
|
||||
case CREATE_PROCESS_DEBUG_EVENT:
|
||||
continue_status = HandleCreateProcessEvent(dbe.u.CreateProcessInfo, dbe.dwThreadId);
|
||||
break;
|
||||
case EXIT_THREAD_DEBUG_EVENT:
|
||||
continue_status = HandleExitThreadEvent(dbe.u.ExitThread, dbe.dwThreadId);
|
||||
break;
|
||||
case EXIT_PROCESS_DEBUG_EVENT:
|
||||
continue_status = HandleExitProcessEvent(dbe.u.ExitProcess, dbe.dwThreadId);
|
||||
should_debug = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case LOAD_DLL_DEBUG_EVENT:
|
||||
continue_status = HandleLoadDllEvent(dbe.u.LoadDll, dbe.dwThreadId);
|
||||
break;
|
||||
case UNLOAD_DLL_DEBUG_EVENT:
|
||||
continue_status = HandleUnloadDllEvent(dbe.u.UnloadDll, dbe.dwThreadId);
|
||||
break;
|
||||
case OUTPUT_DEBUG_STRING_EVENT:
|
||||
continue_status = HandleODSEvent(dbe.u.DebugString, dbe.dwThreadId);
|
||||
break;
|
||||
case RIP_EVENT:
|
||||
continue_status = HandleRipEvent(dbe.u.RipInfo, dbe.dwThreadId);
|
||||
if (dbe.u.RipInfo.dwType == SLE_ERROR)
|
||||
should_debug = false;
|
||||
break;
|
||||
}
|
||||
|
||||
::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status);
|
||||
WINLOGD_IFALL(WINDOWS_LOG_EVENT, "DebugLoop calling ContinueDebugEvent(%u, %u, %u) on thread %u.",
|
||||
dbe.dwProcessId, dbe.dwThreadId, continue_status, ::GetCurrentThreadId());
|
||||
|
||||
::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status);
|
||||
}
|
||||
else
|
||||
{
|
||||
WINERR_IFALL(WINDOWS_LOG_EVENT,
|
||||
"DebugLoop returned FALSE from WaitForDebugEvent. Error = %u",
|
||||
::GetCurrentThreadId(), ::GetLastError());
|
||||
|
||||
should_debug = false;
|
||||
}
|
||||
}
|
||||
FreeProcessHandles();
|
||||
}
|
||||
|
@ -219,26 +284,46 @@ DebuggerThread::HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thr
|
|||
bool first_chance = (info.dwFirstChance != 0);
|
||||
|
||||
m_active_exception.reset(new ExceptionRecord(info.ExceptionRecord));
|
||||
WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_EXCEPTION,
|
||||
"HandleExceptionEvent encountered %s chance exception 0x%x on thread %u",
|
||||
first_chance ? "first" : "second", info.ExceptionRecord.ExceptionCode, thread_id);
|
||||
|
||||
ExceptionResult result = m_debug_delegate->OnDebugException(first_chance, *m_active_exception);
|
||||
ExceptionResult result = m_debug_delegate->OnDebugException(first_chance,
|
||||
*m_active_exception);
|
||||
m_exception_pred.SetValue(result, eBroadcastNever);
|
||||
|
||||
WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_EXCEPTION,
|
||||
"DebuggerThread::HandleExceptionEvent waiting for ExceptionPred != BreakInDebugger");
|
||||
|
||||
m_exception_pred.WaitForValueNotEqualTo(ExceptionResult::BreakInDebugger, result);
|
||||
|
||||
WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_EXCEPTION,
|
||||
"DebuggerThread::HandleExceptionEvent got ExceptionPred = %u",
|
||||
m_exception_pred.GetValue());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return DBG_CONTINUE;
|
||||
}
|
||||
|
||||
DWORD
|
||||
DebuggerThread::HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info, DWORD thread_id)
|
||||
{
|
||||
uint32_t process_id = ::GetProcessId(info.hProcess);
|
||||
|
||||
WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_PROCESS, "HandleCreateProcessEvent process %u spawned", process_id);
|
||||
|
||||
std::string thread_name;
|
||||
llvm::raw_string_ostream name_stream(thread_name);
|
||||
name_stream << "lldb.plugin.process-windows.slave[" << m_process.GetProcessId() << "]";
|
||||
name_stream << "lldb.plugin.process-windows.slave[" << process_id << "]";
|
||||
name_stream.flush();
|
||||
ThisThread::SetName(thread_name.c_str());
|
||||
|
||||
|
@ -259,12 +344,20 @@ DebuggerThread::HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info,
|
|||
DWORD
|
||||
DebuggerThread::HandleExitThreadEvent(const EXIT_THREAD_DEBUG_INFO &info, DWORD thread_id)
|
||||
{
|
||||
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());
|
||||
|
||||
return DBG_CONTINUE;
|
||||
}
|
||||
|
||||
DWORD
|
||||
DebuggerThread::HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info, DWORD thread_id)
|
||||
{
|
||||
WINLOG_IFANY(WINDOWS_LOG_EVENT|WINDOWS_LOG_THREAD,
|
||||
"HandleExitProcessEvent process %I64u exited with code %u",
|
||||
m_process.GetProcessId(), info.dwExitCode);
|
||||
|
||||
FreeProcessHandles();
|
||||
|
||||
m_debug_delegate->OnExitProcess(info.dwExitCode);
|
||||
|
@ -277,6 +370,8 @@ DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info, DWORD thread
|
|||
if (info.hFile == nullptr)
|
||||
{
|
||||
// Not sure what this is, so just ignore it.
|
||||
WINWARN_IFALL(WINDOWS_LOG_EVENT, "Inferior %I64u - HandleLoadDllEvent has a NULL file handle, returning...",
|
||||
m_process.GetProcessId());
|
||||
return DBG_CONTINUE;
|
||||
}
|
||||
|
||||
|
@ -294,11 +389,17 @@ DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info, DWORD thread
|
|||
FileSpec file_spec(path, false);
|
||||
ModuleSpec module_spec(file_spec);
|
||||
lldb::addr_t load_addr = reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll);
|
||||
|
||||
WINLOG_IFALL(WINDOWS_LOG_EVENT, "Inferior %I64u - HandleLoadDllEvent DLL '%s' loaded at address 0x%p...",
|
||||
m_process.GetProcessId(), path, info.lpBaseOfDll);
|
||||
|
||||
m_debug_delegate->OnLoadDll(module_spec, load_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// An unknown error occurred getting the path name.
|
||||
WINERR_IFALL(WINDOWS_LOG_EVENT,
|
||||
"Inferior %I64u - HandleLoadDllEvent Error %u occurred calling GetFinalPathNameByHandle",
|
||||
m_process.GetProcessId(), ::GetLastError());
|
||||
}
|
||||
// Windows does not automatically close info.hFile, so we need to do it.
|
||||
::CloseHandle(info.hFile);
|
||||
|
@ -308,6 +409,10 @@ DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info, DWORD thread
|
|||
DWORD
|
||||
DebuggerThread::HandleUnloadDllEvent(const UNLOAD_DLL_DEBUG_INFO &info, DWORD thread_id)
|
||||
{
|
||||
WINLOG_IFALL(WINDOWS_LOG_EVENT,
|
||||
"HandleUnloadDllEvent process %I64u unloading DLL at addr 0x%p.",
|
||||
m_process.GetProcessId(), info.lpBaseOfDll);
|
||||
|
||||
m_debug_delegate->OnUnloadDll(reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll));
|
||||
return DBG_CONTINUE;
|
||||
}
|
||||
|
@ -321,6 +426,10 @@ DebuggerThread::HandleODSEvent(const OUTPUT_DEBUG_STRING_INFO &info, DWORD threa
|
|||
DWORD
|
||||
DebuggerThread::HandleRipEvent(const RIP_INFO &info, DWORD thread_id)
|
||||
{
|
||||
WINERR_IFALL(WINDOWS_LOG_EVENT,
|
||||
"HandleRipEvent encountered error %u (type=%u) in process %I64u thread %u",
|
||||
info.dwError, info.dwType, m_process.GetProcessId(), thread_id);
|
||||
|
||||
Error error(info.dwError, eErrorTypeWin32);
|
||||
m_debug_delegate->OnDebuggerError(error, info.dwType);
|
||||
|
||||
|
|
|
@ -37,17 +37,22 @@
|
|||
#include "lldb/Target/StopInfo.h"
|
||||
#include "lldb/Target/Target.h"
|
||||
|
||||
#include "Plugins/Process/Windows/ProcessWindowsLog.h"
|
||||
|
||||
#include "DebuggerThread.h"
|
||||
#include "ExceptionRecord.h"
|
||||
#include "LocalDebugDelegate.h"
|
||||
#include "ProcessWindows.h"
|
||||
#include "TargetThreadWindows.h"
|
||||
|
||||
#include "llvm/Support/Format.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
|
||||
#define BOOL_STR(b) ((b) ? "true" : "false")
|
||||
|
||||
namespace lldb_private
|
||||
{
|
||||
|
||||
|
@ -152,13 +157,32 @@ ProcessWindows::PutSTDIN(const char *buf, size_t buf_size, Error &error)
|
|||
Error
|
||||
ProcessWindows::EnableBreakpointSite(BreakpointSite *bp_site)
|
||||
{
|
||||
return EnableSoftwareBreakpoint(bp_site);
|
||||
WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS, "EnableBreakpointSite called with bp_site 0x%p "
|
||||
"(id=%d, addr=0x%x)",
|
||||
bp_site->GetID(), bp_site->GetLoadAddress());
|
||||
|
||||
Error error = EnableSoftwareBreakpoint(bp_site);
|
||||
if (!error.Success())
|
||||
{
|
||||
WINERR_IFALL(WINDOWS_LOG_BREAKPOINTS, "EnableBreakpointSite failed. %s", error.AsCString());
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
Error
|
||||
ProcessWindows::DisableBreakpointSite(BreakpointSite *bp_site)
|
||||
{
|
||||
return DisableSoftwareBreakpoint(bp_site);
|
||||
WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS, "DisableBreakpointSite called with bp_site 0x%p "
|
||||
"(id=%d, addr=0x%x)",
|
||||
bp_site->GetID(), bp_site->GetLoadAddress());
|
||||
|
||||
Error error = DisableSoftwareBreakpoint(bp_site);
|
||||
|
||||
if (!error.Success())
|
||||
{
|
||||
WINERR_IFALL(WINDOWS_LOG_BREAKPOINTS, "DisableBreakpointSite failed. %s", error.AsCString());
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -167,6 +191,10 @@ ProcessWindows::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_th
|
|||
// Add all the threads that were previously running and for which we did not detect a thread
|
||||
// exited event.
|
||||
int new_size = 0;
|
||||
int continued_threads = 0;
|
||||
int exited_threads = 0;
|
||||
int new_threads = 0;
|
||||
|
||||
for (ThreadSP old_thread : old_thread_list.Threads())
|
||||
{
|
||||
lldb::tid_t old_thread_id = old_thread->GetID();
|
||||
|
@ -175,18 +203,32 @@ ProcessWindows::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_th
|
|||
{
|
||||
new_thread_list.AddThread(old_thread);
|
||||
++new_size;
|
||||
++continued_threads;
|
||||
WINLOGV_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - Thread %u was running and is still running.",
|
||||
old_thread_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
WINLOGV_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - Thread %u was running and has exited.",
|
||||
old_thread_id);
|
||||
++exited_threads;
|
||||
}
|
||||
}
|
||||
|
||||
// Also add all the threads that are new since the last time we broke into the debugger.
|
||||
for (auto iter = m_session_data->m_new_threads.begin(); iter != m_session_data->m_new_threads.end(); ++iter)
|
||||
for (const auto &thread_info : m_session_data->m_new_threads)
|
||||
{
|
||||
ThreadSP thread(new TargetThreadWindows(*this, iter->second));
|
||||
thread->SetID(iter->first);
|
||||
ThreadSP thread(new TargetThreadWindows(*this, thread_info.second));
|
||||
thread->SetID(thread_info.first);
|
||||
new_thread_list.AddThread(thread);
|
||||
++new_size;
|
||||
++new_threads;
|
||||
WINLOGV_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - Thread %u is new since last update.", thread_info.first);
|
||||
}
|
||||
|
||||
WINLOG_IFALL(WINDOWS_LOG_THREAD, "UpdateThreadList - %d new threads, %d old threads, %d exited threads.",
|
||||
new_threads, continued_threads, exited_threads);
|
||||
|
||||
m_session_data->m_new_threads.clear();
|
||||
m_session_data->m_exited_threads.clear();
|
||||
|
||||
|
@ -204,7 +246,13 @@ ProcessWindows::DoLaunch(Module *exe_module,
|
|||
Error result;
|
||||
if (!launch_info.GetFlags().Test(eLaunchFlagDebug))
|
||||
{
|
||||
result.SetErrorString("ProcessWindows can only be used to launch processes for debugging.");
|
||||
StreamString stream;
|
||||
stream.Printf("ProcessWindows unable to launch '%s'. ProcessWindows can only be used for debug launches.",
|
||||
launch_info.GetExecutableFile().GetPath().c_str());
|
||||
std::string message = stream.GetString();
|
||||
result.SetErrorString(message.c_str());
|
||||
|
||||
WINERR_IFALL(WINDOWS_LOG_PROCESS, message.c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -221,6 +269,9 @@ ProcessWindows::DoLaunch(Module *exe_module,
|
|||
HostProcess process;
|
||||
if (result.Success())
|
||||
{
|
||||
WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch started asynchronous launch of '%s'. Waiting for initial stop.",
|
||||
launch_info.GetExecutableFile().GetPath().c_str());
|
||||
|
||||
// Block this function until we receive the initial stop from the process.
|
||||
if (::WaitForSingleObject(m_session_data->m_initial_stop_event, INFINITE) == WAIT_OBJECT_0)
|
||||
{
|
||||
|
@ -232,8 +283,17 @@ ProcessWindows::DoLaunch(Module *exe_module,
|
|||
result.SetError(::GetLastError(), eErrorTypeWin32);
|
||||
}
|
||||
|
||||
if (!result.Success())
|
||||
if (result.Success())
|
||||
{
|
||||
WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch successfully launched '%s'",
|
||||
launch_info.GetExecutableFile().GetPath().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoLaunch failed launching '%s'. %s",
|
||||
launch_info.GetExecutableFile().GetPath().c_str(), result.AsCString());
|
||||
return result;
|
||||
}
|
||||
|
||||
// We've hit the initial stop. The private state should already be set to stopped as a result
|
||||
// of encountering the breakpoint exception in ProcessWindows::OnDebugException.
|
||||
|
@ -247,27 +307,42 @@ Error
|
|||
ProcessWindows::DoResume()
|
||||
{
|
||||
llvm::sys::ScopedLock lock(m_mutex);
|
||||
|
||||
Error error;
|
||||
if (GetPrivateState() == eStateStopped || GetPrivateState() == eStateCrashed)
|
||||
|
||||
StateType private_state = GetPrivateState();
|
||||
if (private_state == eStateStopped || private_state == eStateCrashed)
|
||||
{
|
||||
ExceptionRecordSP active_exception = m_session_data->m_debugger->GetActiveException().lock();
|
||||
WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoResume called for process %I64u while state is %u. Resuming...",
|
||||
m_session_data->m_debugger->GetProcess().GetProcessId(), GetPrivateState());
|
||||
|
||||
ExceptionRecordSP active_exception =
|
||||
m_session_data->m_debugger->GetActiveException().lock();
|
||||
if (active_exception)
|
||||
{
|
||||
// Resume the process and continue processing debug events. Mask the exception so that
|
||||
// from the process's view, there is no indication that anything happened.
|
||||
m_session_data->m_debugger->ContinueAsyncException(ExceptionResult::MaskException);
|
||||
// Resume the process and continue processing debug events. Mask
|
||||
// the exception so that from the process's view, there is no
|
||||
// indication that anything happened.
|
||||
m_session_data->m_debugger->ContinueAsyncException(
|
||||
ExceptionResult::MaskException);
|
||||
}
|
||||
|
||||
WINLOG_IFANY(WINDOWS_LOG_PROCESS | WINDOWS_LOG_THREAD, "DoResume resuming %u threads.",
|
||||
m_thread_list.GetSize());
|
||||
|
||||
for (int i = 0; i < m_thread_list.GetSize(); ++i)
|
||||
{
|
||||
typedef std::shared_ptr<TargetThreadWindows> TargetThreadWindowsSP;
|
||||
TargetThreadWindowsSP thread = std::static_pointer_cast<TargetThreadWindows>(m_thread_list.GetThreadAtIndex(i));
|
||||
auto thread = std::static_pointer_cast<TargetThreadWindows>(
|
||||
m_thread_list.GetThreadAtIndex(i));
|
||||
thread->DoResume();
|
||||
}
|
||||
|
||||
SetPrivateState(eStateRunning);
|
||||
}
|
||||
else
|
||||
{
|
||||
WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoResume called for process %I64u but state is %u. Returning...",
|
||||
m_session_data->m_debugger->GetProcess().GetProcessId(), GetPrivateState());
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -300,12 +375,29 @@ ProcessWindows::DoDestroy()
|
|||
llvm::sys::ScopedLock lock(m_mutex);
|
||||
|
||||
Error error;
|
||||
if (GetPrivateState() != eStateExited && GetPrivateState() != eStateDetached && m_session_data)
|
||||
StateType private_state = GetPrivateState();
|
||||
if (!m_session_data)
|
||||
{
|
||||
DebuggerThread &debugger = *m_session_data->m_debugger;
|
||||
error = debugger.StopDebugging(true);
|
||||
WINWARN_IFALL(WINDOWS_LOG_PROCESS, "DoDestroy called while state = %u, but there is no active session.",
|
||||
private_state);
|
||||
return error;
|
||||
}
|
||||
m_session_data.reset();
|
||||
|
||||
DebuggerThread &debugger = *m_session_data->m_debugger;
|
||||
if (private_state != eStateExited && private_state != eStateDetached)
|
||||
{
|
||||
WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoDestroy called for process 0x%I64u while state = %u. Shutting down...",
|
||||
debugger.GetProcess().GetNativeProcess().GetSystemHandle(), private_state);
|
||||
error = debugger.StopDebugging(true);
|
||||
m_session_data.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
WINERR_IFALL(WINDOWS_LOG_PROCESS,
|
||||
"DoDestroy called for process %I64u while state = %u, but cannot destroy in this state.",
|
||||
debugger.GetProcess().GetNativeProcess().GetSystemHandle(), private_state);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -315,28 +407,56 @@ ProcessWindows::RefreshStateAfterStop()
|
|||
llvm::sys::ScopedLock lock(m_mutex);
|
||||
|
||||
if (!m_session_data)
|
||||
{
|
||||
WINWARN_IFALL(WINDOWS_LOG_PROCESS, "RefreshStateAfterStop called with no active session. Returning...");
|
||||
return;
|
||||
}
|
||||
|
||||
m_thread_list.RefreshStateAfterStop();
|
||||
|
||||
std::weak_ptr<ExceptionRecord> exception_record = m_session_data->m_debugger->GetActiveException();
|
||||
ExceptionRecordSP active_exception = exception_record.lock();
|
||||
if (!active_exception)
|
||||
{
|
||||
WINERR_IFALL(WINDOWS_LOG_PROCESS, "RefreshStateAfterStop called for process %I64u but there is no "
|
||||
"active exception. Why is the process stopped?",
|
||||
m_session_data->m_debugger->GetProcess().GetProcessId());
|
||||
return;
|
||||
}
|
||||
|
||||
StopInfoSP stop_info;
|
||||
ThreadSP stop_thread = m_thread_list.GetSelectedThread();
|
||||
RegisterContextSP register_context = stop_thread->GetRegisterContext();
|
||||
|
||||
uint64_t pc = register_context->GetPC();
|
||||
// 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)
|
||||
{
|
||||
// TODO(zturner): The current EIP is AFTER the BP opcode, which is one byte.
|
||||
BreakpointSiteSP site(GetBreakpointSiteList().FindByAddress(pc - 1));
|
||||
if (site && site->ValidForThisThread(stop_thread.get()))
|
||||
BreakpointSiteSP site(GetBreakpointSiteList().FindByAddress(pc));
|
||||
|
||||
if (site)
|
||||
{
|
||||
stop_info = StopInfo::CreateStopReasonWithBreakpointSiteID(*stop_thread, site->GetID());
|
||||
register_context->SetPC(pc - 1);
|
||||
WINLOG_IFANY(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION,
|
||||
"RefreshStateAfterStop detected breakpoint in process %I64u at "
|
||||
"address 0x%I64x with breakpoint site %d",
|
||||
m_session_data->m_debugger->GetProcess().GetProcessId(), pc, site->GetID());
|
||||
|
||||
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());
|
||||
|
||||
stop_info = StopInfo::CreateStopReasonWithBreakpointSiteID(
|
||||
*stop_thread, site->GetID());
|
||||
register_context->SetPC(pc);
|
||||
}
|
||||
else
|
||||
{
|
||||
WINLOG_IFALL(WINDOWS_LOG_BREAKPOINTS | WINDOWS_LOG_EXCEPTION,
|
||||
"Breakpoint site %d is not valid for this thread, creating empty stop info.",
|
||||
site->GetID());
|
||||
}
|
||||
}
|
||||
stop_thread->SetStopInfo(stop_info);
|
||||
}
|
||||
|
@ -344,14 +464,18 @@ ProcessWindows::RefreshStateAfterStop()
|
|||
{
|
||||
stop_info = StopInfo::CreateStopReasonToTrace(*stop_thread);
|
||||
stop_thread->SetStopInfo(stop_info);
|
||||
WINLOG_IFANY(WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_STEP, "RefreshStateAfterStop single stepping thread %u",
|
||||
stop_thread->GetID());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string desc;
|
||||
llvm::raw_string_ostream desc_stream(desc);
|
||||
desc_stream << "Exception " << active_exception->GetExceptionCode() << " encountered at address " << pc;
|
||||
desc_stream << "Exception 0x" << llvm::format_hex(active_exception->GetExceptionCode(), 8)
|
||||
<< " encountered at address 0x" << 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -384,7 +508,11 @@ ProcessWindows::DoHalt(bool &caused_stop)
|
|||
llvm::sys::ScopedLock lock(m_mutex);
|
||||
caused_stop = ::DebugBreakProcess(m_session_data->m_debugger->GetProcess().GetNativeProcess().GetSystemHandle());
|
||||
if (!caused_stop)
|
||||
error.SetError(GetLastError(), eErrorTypeWin32);
|
||||
{
|
||||
error.SetError(::GetLastError(), eErrorTypeWin32);
|
||||
WINERR_IFALL(WINDOWS_LOG_PROCESS, "DoHalt called DebugBreakProcess, but it failed with error %u",
|
||||
error.GetError());
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
@ -410,11 +538,16 @@ ProcessWindows::DoReadMemory(lldb::addr_t vm_addr,
|
|||
if (!m_session_data)
|
||||
return 0;
|
||||
|
||||
WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoReadMemory attempting to read %u bytes from address 0x%I64x", size, vm_addr);
|
||||
|
||||
HostProcess process = m_session_data->m_debugger->GetProcess();
|
||||
void *addr = reinterpret_cast<void *>(vm_addr);
|
||||
SIZE_T bytes_read = 0;
|
||||
if (!ReadProcessMemory(process.GetNativeProcess().GetSystemHandle(), addr, buf, size, &bytes_read))
|
||||
{
|
||||
error.SetError(GetLastError(), eErrorTypeWin32);
|
||||
WINERR_IFALL(WINDOWS_LOG_MEMORY, "DoReadMemory failed with error code %u", error.GetError());
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
|
@ -426,6 +559,8 @@ ProcessWindows::DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size
|
|||
if (!m_session_data)
|
||||
return 0;
|
||||
|
||||
WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoWriteMemory attempting to write %u bytes into address 0x%I64x", size, vm_addr);
|
||||
|
||||
HostProcess process = m_session_data->m_debugger->GetProcess();
|
||||
void *addr = reinterpret_cast<void *>(vm_addr);
|
||||
SIZE_T bytes_written = 0;
|
||||
|
@ -433,7 +568,10 @@ ProcessWindows::DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size
|
|||
if (WriteProcessMemory(handle, addr, buf, size, &bytes_written))
|
||||
FlushInstructionCache(handle, addr, bytes_written);
|
||||
else
|
||||
{
|
||||
error.SetError(GetLastError(), eErrorTypeWin32);
|
||||
WINLOG_IFALL(WINDOWS_LOG_MEMORY, "DoWriteMemory failed with error code %u", error.GetError());
|
||||
}
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
|
@ -445,7 +583,8 @@ ProcessWindows::GetMemoryRegionInfo(lldb::addr_t vm_addr, MemoryRegionInfo &info
|
|||
|
||||
if (!m_session_data)
|
||||
{
|
||||
error.SetErrorString("ProcessWindows::GetMemoryRegionInfo called with no debugging session.");
|
||||
error.SetErrorString("GetMemoryRegionInfo called with no debugging session.");
|
||||
WINERR_IFALL(WINDOWS_LOG_MEMORY, error.AsCString());
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -453,25 +592,33 @@ ProcessWindows::GetMemoryRegionInfo(lldb::addr_t vm_addr, MemoryRegionInfo &info
|
|||
lldb::process_t handle = process.GetNativeProcess().GetSystemHandle();
|
||||
if (handle == nullptr || handle == LLDB_INVALID_PROCESS)
|
||||
{
|
||||
error.SetErrorString("ProcessWindows::GetMemoryRegionInfo called with an invalid target process.");
|
||||
error.SetErrorString("GetMemoryRegionInfo called with an invalid target process.");
|
||||
WINERR_IFALL(WINDOWS_LOG_MEMORY, error.AsCString());
|
||||
return error;
|
||||
}
|
||||
|
||||
WINLOG_IFALL(WINDOWS_LOG_MEMORY, "GetMemoryRegionInfo getting info for address 0x%I64x", vm_addr);
|
||||
|
||||
void *addr = reinterpret_cast<void *>(vm_addr);
|
||||
MEMORY_BASIC_INFORMATION mem_info = {0};
|
||||
SIZE_T result = ::VirtualQueryEx(handle, addr, &mem_info, sizeof(mem_info));
|
||||
if (result == 0)
|
||||
{
|
||||
error.SetError(::GetLastError(), eErrorTypeWin32);
|
||||
WINERR_IFALL(WINDOWS_LOG_MEMORY,
|
||||
"VirtualQueryEx returned error %u while getting memory region info for address 0x%I64x",
|
||||
error.GetError(), vm_addr);
|
||||
return error;
|
||||
}
|
||||
|
||||
bool readable = !(mem_info.Protect & PAGE_NOACCESS);
|
||||
bool executable = mem_info.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY);
|
||||
bool writable = mem_info.Protect & (PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY | PAGE_READWRITE | PAGE_WRITECOPY);
|
||||
info.SetReadable(readable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo);
|
||||
info.SetExecutable(executable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo);
|
||||
info.SetWritable(writable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo);
|
||||
error.SetError(::GetLastError(), eErrorTypeWin32);
|
||||
WINLOGV_IFALL(WINDOWS_LOG_MEMORY, "Memory region info for address 0x%I64u: readable=%s, executable=%s, writable=%s",
|
||||
BOOL_STR(readable), BOOL_STR(executable), BOOL_STR(writable));
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -504,6 +651,7 @@ void
|
|||
ProcessWindows::OnExitProcess(uint32_t exit_code)
|
||||
{
|
||||
// No need to acquire the lock since m_session_data isn't accessed.
|
||||
WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Process %u exited with code %u", GetID(), exit_code);
|
||||
|
||||
ModuleSP executable_module = GetTarget().GetExecutableModule();
|
||||
ModuleList unloaded_modules;
|
||||
|
@ -517,6 +665,11 @@ ProcessWindows::OnExitProcess(uint32_t exit_code)
|
|||
void
|
||||
ProcessWindows::OnDebuggerConnected(lldb::addr_t image_base)
|
||||
{
|
||||
DebuggerThreadSP debugger = m_session_data->m_debugger;
|
||||
|
||||
WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Debugger established connected to process %I64u. Image base = 0x%I64x",
|
||||
debugger->GetProcess().GetProcessId(), image_base);
|
||||
|
||||
// Either we successfully attached to an existing process, or we successfully launched a new
|
||||
// process under the debugger.
|
||||
ModuleSP module = GetTarget().GetExecutableModule();
|
||||
|
@ -531,7 +684,6 @@ ProcessWindows::OnDebuggerConnected(lldb::addr_t image_base)
|
|||
|
||||
llvm::sys::ScopedLock lock(m_mutex);
|
||||
|
||||
DebuggerThreadSP debugger = m_session_data->m_debugger;
|
||||
const HostThreadWindows &wmain_thread = debugger->GetMainThread().GetNativeThread();
|
||||
m_session_data->m_new_threads[wmain_thread.GetThreadId()] = debugger->GetMainThread();
|
||||
}
|
||||
|
@ -547,7 +699,12 @@ ProcessWindows::OnDebugException(bool first_chance, const ExceptionRecord &recor
|
|||
// this, we probably need to first figure allow the test suite to print out full
|
||||
// lldb logs, and then add logging to the process plugin.
|
||||
if (!m_session_data)
|
||||
{
|
||||
WINERR_IFANY(WINDOWS_LOG_EXCEPTION,
|
||||
"Debugger thread reported exception 0x%u at address 0x%I64x, but there is no session.",
|
||||
record.GetExceptionCode(), record.GetExceptionAddress());
|
||||
return ExceptionResult::SendToApplication;
|
||||
}
|
||||
|
||||
if (!first_chance)
|
||||
{
|
||||
|
@ -574,6 +731,9 @@ ProcessWindows::OnDebugException(bool first_chance, const ExceptionRecord &recor
|
|||
SetPrivateState(eStateStopped);
|
||||
break;
|
||||
default:
|
||||
WINLOG_IFANY(WINDOWS_LOG_EXCEPTION,
|
||||
"Debugger thread reported exception 0x%u 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)
|
||||
result = ExceptionResult::SendToApplication;
|
||||
|
@ -588,7 +748,6 @@ void
|
|||
ProcessWindows::OnCreateThread(const HostThread &new_thread)
|
||||
{
|
||||
llvm::sys::ScopedLock lock(m_mutex);
|
||||
|
||||
const HostThreadWindows &wnew_thread = new_thread.GetNativeThread();
|
||||
m_session_data->m_new_threads[wnew_thread.GetThreadId()] = new_thread;
|
||||
}
|
||||
|
@ -649,16 +808,22 @@ ProcessWindows::OnDebuggerError(const Error &error, uint32_t type)
|
|||
{
|
||||
llvm::sys::ScopedLock lock(m_mutex);
|
||||
|
||||
if (!m_session_data->m_initial_stop_received)
|
||||
if (m_session_data->m_initial_stop_received)
|
||||
{
|
||||
// This happened while debugging. Do we shutdown the debugging session, try to continue,
|
||||
// or do something else?
|
||||
WINERR_IFALL(WINDOWS_LOG_PROCESS, "Error %u occurred during debugging. Unexpected behavior may result. %s",
|
||||
error.GetError(), error.AsCString());
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we haven't actually launched the process yet, this was an error launching the
|
||||
// process. Set the internal error and signal the initial stop event so that the DoLaunch
|
||||
// method wakes up and returns a failure.
|
||||
m_session_data->m_launch_error = error;
|
||||
::SetEvent(m_session_data->m_initial_stop_event);
|
||||
WINERR_IFALL(WINDOWS_LOG_PROCESS, "Error %u occurred launching the process before the initial stop. %s",
|
||||
error.GetError(), error.AsCString());
|
||||
return;
|
||||
}
|
||||
|
||||
// This happened while debugging. Do we shutdown the debugging session, try to continue,
|
||||
// or do something else?
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
|
||||
#include <mutex>
|
||||
|
||||
#include "lldb/Interpreter/Args.h"
|
||||
#include "lldb/Core/StreamFile.h"
|
||||
#include "lldb/Interpreter/Args.h"
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
|
||||
#include "ProcessWindows.h"
|
||||
|
@ -26,14 +26,7 @@ using namespace lldb_private;
|
|||
// that will construct the static g_log_sp the first time this function is
|
||||
// called.
|
||||
static bool g_log_enabled = false;
|
||||
static Log * g_log = NULL;
|
||||
static Log *
|
||||
GetLog()
|
||||
{
|
||||
if (!g_log_enabled)
|
||||
return NULL;
|
||||
return g_log;
|
||||
}
|
||||
static Log * g_log = nullptr;
|
||||
|
||||
static llvm::ManagedStatic<std::once_flag> g_once_flag;
|
||||
|
||||
|
@ -54,34 +47,44 @@ ProcessWindowsLog::Initialize()
|
|||
});
|
||||
}
|
||||
|
||||
Log *
|
||||
ProcessWindowsLog::GetLogIfAllCategoriesSet(uint32_t mask)
|
||||
void
|
||||
ProcessWindowsLog::Terminate()
|
||||
{
|
||||
Log *log(GetLog());
|
||||
if (log && mask)
|
||||
{
|
||||
uint32_t log_mask = log->GetMask().Get();
|
||||
if ((log_mask & mask) != mask)
|
||||
return NULL;
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
Log *
|
||||
ProcessWindowsLog::GetLog()
|
||||
{
|
||||
return (g_log_enabled) ? g_log : nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessWindowsLog::TestLogFlags(uint32_t mask, LogMaskReq req)
|
||||
{
|
||||
Log *log = GetLog();
|
||||
if (!log)
|
||||
return false;
|
||||
|
||||
uint32_t log_mask = log->GetMask().Get();
|
||||
if (req == LogMaskReq::All)
|
||||
return ((log_mask & mask) == mask);
|
||||
else
|
||||
return (log_mask & mask);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
GetFlagBits(const char *arg)
|
||||
{
|
||||
if (::strcasecmp (arg, "all") == 0 ) return WINDOWS_LOG_ALL;
|
||||
else if (::strcasecmp (arg, "async") == 0 ) return WINDOWS_LOG_ASYNC;
|
||||
else if (::strncasecmp (arg, "break", 5) == 0 ) return WINDOWS_LOG_BREAKPOINTS;
|
||||
else if (::strcasecmp (arg, "default") == 0 ) return WINDOWS_LOG_DEFAULT;
|
||||
else if (::strcasecmp (arg, "memory") == 0 ) return WINDOWS_LOG_MEMORY;
|
||||
else if (::strcasecmp (arg, "data-short") == 0 ) return WINDOWS_LOG_MEMORY_DATA_SHORT;
|
||||
else if (::strcasecmp (arg, "data-long") == 0 ) return WINDOWS_LOG_MEMORY_DATA_LONG;
|
||||
else if (::strcasecmp (arg, "process") == 0 ) return WINDOWS_LOG_PROCESS;
|
||||
else if (::strcasecmp (arg, "registers") == 0 ) return WINDOWS_LOG_REGISTERS;
|
||||
else if (::strcasecmp (arg, "step") == 0 ) return WINDOWS_LOG_STEP;
|
||||
else if (::strcasecmp (arg, "thread") == 0 ) return WINDOWS_LOG_THREAD;
|
||||
else if (::strcasecmp (arg, "verbose") == 0 ) return WINDOWS_LOG_VERBOSE;
|
||||
if (::strcasecmp(arg, "all") == 0 ) return WINDOWS_LOG_ALL;
|
||||
else if (::strcasecmp(arg, "break") == 0 ) return WINDOWS_LOG_BREAKPOINTS;
|
||||
else if (::strcasecmp(arg, "event") == 0 ) return WINDOWS_LOG_EVENT;
|
||||
else if (::strcasecmp(arg, "exception") == 0 ) return WINDOWS_LOG_EXCEPTION;
|
||||
else if (::strcasecmp(arg, "memory") == 0 ) return WINDOWS_LOG_MEMORY;
|
||||
else if (::strcasecmp(arg, "process") == 0 ) return WINDOWS_LOG_PROCESS;
|
||||
else if (::strcasecmp(arg, "registers") == 0 ) return WINDOWS_LOG_REGISTERS;
|
||||
else if (::strcasecmp(arg, "step") == 0 ) return WINDOWS_LOG_STEP;
|
||||
else if (::strcasecmp(arg, "thread") == 0 ) return WINDOWS_LOG_THREAD;
|
||||
else if (::strcasecmp(arg, "verbose") == 0 ) return WINDOWS_LOG_VERBOSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -159,7 +162,7 @@ ProcessWindowsLog::EnableLog(StreamSP &log_stream_sp, uint32_t log_options, cons
|
|||
}
|
||||
}
|
||||
if (flag_bits == 0)
|
||||
flag_bits = WINDOWS_LOG_DEFAULT;
|
||||
flag_bits = WINDOWS_LOG_ALL;
|
||||
g_log->GetMask().Reset(flag_bits);
|
||||
g_log->GetOptions().Reset(log_options);
|
||||
g_log_enabled = true;
|
||||
|
@ -170,35 +173,18 @@ ProcessWindowsLog::EnableLog(StreamSP &log_stream_sp, uint32_t log_options, cons
|
|||
void
|
||||
ProcessWindowsLog::ListLogCategories(Stream *strm)
|
||||
{
|
||||
strm->Printf ("Logging categories for '%s':\n"
|
||||
" all - turn on all available logging categories\n"
|
||||
" async - log asynchronous activity\n"
|
||||
" break - log breakpoints\n"
|
||||
" default - enable the default set of logging categories for liblldb\n"
|
||||
" memory - log memory reads and writes\n"
|
||||
" data-short - log memory bytes for memory reads and writes for short transactions only\n"
|
||||
" data-long - log memory bytes for memory reads and writes for all transactions\n"
|
||||
" process - log process events and activities\n"
|
||||
" registers - log register read/writes\n"
|
||||
" thread - log thread events and activities\n"
|
||||
" step - log step related activities\n"
|
||||
" verbose - enable verbose logging\n",
|
||||
ProcessWindowsLog::m_pluginname);
|
||||
strm->Printf("Logging categories for '%s':\n"
|
||||
" all - turn on all available logging categories\n"
|
||||
" break - log breakpoints\n"
|
||||
" event - log low level debugger events\n"
|
||||
" exception - log exception information\n"
|
||||
" memory - log memory reads and writes\n"
|
||||
" process - log process events and activities\n"
|
||||
" registers - log register read/writes\n"
|
||||
" thread - log thread events and activities\n"
|
||||
" step - log step related activities\n"
|
||||
" verbose - enable verbose logging\n",
|
||||
ProcessWindowsLog::m_pluginname);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ProcessWindowsLog::LogIf(uint32_t mask, const char *format, ...)
|
||||
{
|
||||
Log *log = GetLogIfAllCategoriesSet(mask);
|
||||
if (log)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
log->VAPrintf(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
int ProcessWindowsLog::m_nestinglevel = 0;
|
||||
const char *ProcessWindowsLog::m_pluginname = "";
|
||||
|
|
|
@ -12,25 +12,25 @@
|
|||
|
||||
#include "lldb/Core/Log.h"
|
||||
|
||||
#define WINDOWS_LOG_VERBOSE (1u << 0)
|
||||
#define WINDOWS_LOG_PROCESS (1u << 1)
|
||||
#define WINDOWS_LOG_THREAD (1u << 2)
|
||||
#define WINDOWS_LOG_MEMORY (1u << 3) // Log memory reads/writes calls
|
||||
#define WINDOWS_LOG_MEMORY_DATA_SHORT (1u << 4) // Log short memory reads/writes bytes
|
||||
#define WINDOWS_LOG_MEMORY_DATA_LONG (1u << 5) // Log all memory reads/writes bytes
|
||||
#define WINDOWS_LOG_BREAKPOINTS (1u << 6)
|
||||
#define WINDOWS_LOG_STEP (1u << 7)
|
||||
#define WINDOWS_LOG_ASYNC (1u << 8)
|
||||
#define WINDOWS_LOG_REGISTERS (1u << 9)
|
||||
#define WINDOWS_LOG_ALL (UINT32_MAX)
|
||||
#define WINDOWS_LOG_DEFAULT WINDOWS_LOG_ASYNC
|
||||
#define WINDOWS_LOG_VERBOSE (1u << 0)
|
||||
#define WINDOWS_LOG_PROCESS (1u << 1) // Log process operations
|
||||
#define WINDOWS_LOG_EXCEPTION (1u << 1) // Log exceptions
|
||||
#define WINDOWS_LOG_THREAD (1u << 2) // Log thread operations
|
||||
#define WINDOWS_LOG_MEMORY (1u << 3) // Log memory reads/writes calls
|
||||
#define WINDOWS_LOG_BREAKPOINTS (1u << 4) // Log breakpoint operations
|
||||
#define WINDOWS_LOG_STEP (1u << 5) // Log step operations
|
||||
#define WINDOWS_LOG_REGISTERS (1u << 6) // Log register operations
|
||||
#define WINDOWS_LOG_EVENT (1u << 7) // Low level debug events
|
||||
#define WINDOWS_LOG_ALL (UINT32_MAX)
|
||||
|
||||
// The size which determines "short memory reads/writes".
|
||||
#define WINDOWS_LOG_MEMORY_SHORT_BYTES (4 * sizeof(ptrdiff_t))
|
||||
enum class LogMaskReq
|
||||
{
|
||||
All,
|
||||
Any
|
||||
};
|
||||
|
||||
class ProcessWindowsLog
|
||||
{
|
||||
static int m_nestinglevel;
|
||||
static const char *m_pluginname;
|
||||
|
||||
public:
|
||||
|
@ -40,6 +40,9 @@ public:
|
|||
static void
|
||||
Initialize();
|
||||
|
||||
static void
|
||||
Terminate();
|
||||
|
||||
static void
|
||||
RegisterPluginName(const char *pluginName)
|
||||
{
|
||||
|
@ -52,8 +55,11 @@ public:
|
|||
m_pluginname = pluginName.GetCString();
|
||||
}
|
||||
|
||||
static bool
|
||||
TestLogFlags(uint32_t mask, LogMaskReq req);
|
||||
|
||||
static lldb_private::Log *
|
||||
GetLogIfAllCategoriesSet(uint32_t mask = 0);
|
||||
GetLog();
|
||||
|
||||
static void
|
||||
DisableLog(const char **args, lldb_private::Stream *feedback_strm);
|
||||
|
@ -64,10 +70,27 @@ public:
|
|||
|
||||
static void
|
||||
ListLogCategories(lldb_private::Stream *strm);
|
||||
|
||||
static void
|
||||
LogIf(uint32_t mask, const char *format, ...);
|
||||
|
||||
};
|
||||
|
||||
#define WINLOGF_IF(Flags, Req, Method, ...) \
|
||||
{ \
|
||||
if (ProcessWindowsLog::TestLogFlags(Flags, Req)) \
|
||||
{ \
|
||||
Log *log = ProcessWindowsLog::GetLog(); \
|
||||
if (log) \
|
||||
log->Method(__VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define WINLOG_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Printf, __VA_ARGS__)
|
||||
#define WINLOG_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Printf, __VA_ARGS__)
|
||||
#define WINLOGV_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Verbose, __VA_ARGS__)
|
||||
#define WINLOGV_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Verbose, __VA_ARGS__)
|
||||
#define WINLOGD_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Debug, __VA_ARGS__)
|
||||
#define WINLOGD_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Debug, __VA_ARGS__)
|
||||
#define WINERR_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Error, __VA_ARGS__)
|
||||
#define WINERR_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Error, __VA_ARGS__)
|
||||
#define WINWARN_IFANY(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::Any, Warning, __VA_ARGS__)
|
||||
#define WINWARN_IFALL(Flags, ...) WINLOGF_IF(Flags, LogMaskReq::All, Warning, __VA_ARGS__)
|
||||
|
||||
#endif // liblldb_ProcessWindowsLog_h_
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "lldb/Host/windows/HostThreadWindows.h"
|
||||
#include "lldb/Host/windows/windows.h"
|
||||
|
||||
#include "ProcessWindowsLog.h"
|
||||
#include "RegisterContextWindows.h"
|
||||
#include "TargetThreadWindows.h"
|
||||
|
||||
|
@ -143,7 +144,12 @@ RegisterContextWindows::CacheAllRegisterValues()
|
|||
memset(&m_context, 0, sizeof(m_context));
|
||||
m_context.ContextFlags = kWinContextFlags;
|
||||
if (!::GetThreadContext(wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context))
|
||||
{
|
||||
WINERR_IFALL(WINDOWS_LOG_REGISTERS, "GetThreadContext failed with error %u while caching register values.",
|
||||
::GetLastError());
|
||||
return false;
|
||||
}
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "GetThreadContext successfully updated the register values.", ::GetLastError());
|
||||
m_context_stale = false;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "lldb/Host/windows/windows.h"
|
||||
|
||||
#include "lldb-x86-register-enums.h"
|
||||
#include "ProcessWindowsLog.h"
|
||||
#include "RegisterContext_x86.h"
|
||||
#include "RegisterContextWindows_x86.h"
|
||||
#include "TargetThreadWindows.h"
|
||||
|
@ -127,38 +128,52 @@ RegisterContextWindows_x86::ReadRegister(const RegisterInfo *reg_info, RegisterV
|
|||
if (!CacheAllRegisterValues())
|
||||
return false;
|
||||
|
||||
switch (reg_info->kinds[eRegisterKindLLDB])
|
||||
uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
|
||||
switch (reg)
|
||||
{
|
||||
case lldb_eax_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EAX", m_context.Eax);
|
||||
reg_value.SetUInt32(m_context.Eax);
|
||||
break;
|
||||
case lldb_ebx_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EBX", m_context.Ebx);
|
||||
reg_value.SetUInt32(m_context.Ebx);
|
||||
break;
|
||||
case lldb_ecx_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ECX", m_context.Ecx);
|
||||
reg_value.SetUInt32(m_context.Ecx);
|
||||
break;
|
||||
case lldb_edx_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EDX", m_context.Edx);
|
||||
reg_value.SetUInt32(m_context.Edx);
|
||||
break;
|
||||
case lldb_edi_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EDI", m_context.Edi);
|
||||
reg_value.SetUInt32(m_context.Edi);
|
||||
break;
|
||||
case lldb_esi_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ESI", m_context.Esi);
|
||||
reg_value.SetUInt32(m_context.Esi);
|
||||
break;
|
||||
case lldb_ebp_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EBP", m_context.Ebp);
|
||||
reg_value.SetUInt32(m_context.Ebp);
|
||||
break;
|
||||
case lldb_esp_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from ESP", m_context.Esp);
|
||||
reg_value.SetUInt32(m_context.Esp);
|
||||
break;
|
||||
case lldb_eip_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EIP", m_context.Eip);
|
||||
reg_value.SetUInt32(m_context.Eip);
|
||||
break;
|
||||
case lldb_eflags_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Read value 0x%x from EFLAGS", m_context.EFlags);
|
||||
reg_value.SetUInt32(m_context.EFlags);
|
||||
break;
|
||||
default:
|
||||
WINWARN_IFALL(WINDOWS_LOG_REGISTERS, "Requested unknown register %u", reg);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -172,38 +187,52 @@ RegisterContextWindows_x86::WriteRegister(const RegisterInfo *reg_info, const Re
|
|||
if (!CacheAllRegisterValues())
|
||||
return false;
|
||||
|
||||
switch (reg_info->kinds[eRegisterKindLLDB])
|
||||
uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
|
||||
switch (reg)
|
||||
{
|
||||
case lldb_eax_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EAX", reg_value.GetAsUInt32());
|
||||
m_context.Eax = reg_value.GetAsUInt32();
|
||||
break;
|
||||
case lldb_ebx_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EBX", reg_value.GetAsUInt32());
|
||||
m_context.Ebx = reg_value.GetAsUInt32();
|
||||
break;
|
||||
case lldb_ecx_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to ECX", reg_value.GetAsUInt32());
|
||||
m_context.Ecx = reg_value.GetAsUInt32();
|
||||
break;
|
||||
case lldb_edx_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EDX", reg_value.GetAsUInt32());
|
||||
m_context.Edx = reg_value.GetAsUInt32();
|
||||
break;
|
||||
case lldb_edi_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EDI", reg_value.GetAsUInt32());
|
||||
m_context.Edi = reg_value.GetAsUInt32();
|
||||
break;
|
||||
case lldb_esi_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to ESI", reg_value.GetAsUInt32());
|
||||
m_context.Esi = reg_value.GetAsUInt32();
|
||||
break;
|
||||
case lldb_ebp_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EBP", reg_value.GetAsUInt32());
|
||||
m_context.Ebp = reg_value.GetAsUInt32();
|
||||
break;
|
||||
case lldb_esp_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to ESP", reg_value.GetAsUInt32());
|
||||
m_context.Esp = reg_value.GetAsUInt32();
|
||||
break;
|
||||
case lldb_eip_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EIP", reg_value.GetAsUInt32());
|
||||
m_context.Eip = reg_value.GetAsUInt32();
|
||||
break;
|
||||
case lldb_eflags_i386:
|
||||
WINLOG_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to EFLAGS", reg_value.GetAsUInt32());
|
||||
m_context.EFlags = reg_value.GetAsUInt32();
|
||||
break;
|
||||
default:
|
||||
WINWARN_IFALL(WINDOWS_LOG_REGISTERS, "Write value 0x%x to unknown register %u", reg_value.GetAsUInt32(),
|
||||
reg);
|
||||
}
|
||||
|
||||
// Physically update the registers in the target process.
|
||||
|
|
Loading…
Reference in New Issue