28#include "llvm/ADT/STLExtras.h"
29#include "llvm/Support/ConvertUTF.h"
30#include "llvm/Support/Threading.h"
31#include "llvm/Support/raw_ostream.h"
36#ifndef STATUS_WX86_BREAKPOINT
37#define STATUS_WX86_BREAKPOINT 0x4000001FL
44 : m_debug_delegate(debug_delegate), m_pid_to_detach(0),
45 m_is_shutting_down(false) {
57 "lldb.plugin.process-windows.secondary[?]",
59 if (!secondary_thread) {
61 LLDB_LOG(log,
"couldn't launch debugger thread. {0}", result);
70 LLDB_LOG(log,
"attaching to '{0}'", pid);
74 "lldb.plugin.process-windows.secondary[?]", [
this, pid, attach_info] {
77 if (!secondary_thread) {
79 LLDB_LOG(log,
"couldn't attach to process '{0}'. {1}", pid, result);
89 std::shared_ptr<DebuggerThread> this_ref(shared_from_this());
92 LLDB_LOG(log,
"preparing to launch '{0}' on background thread.",
116 std::shared_ptr<DebuggerThread> this_ref(shared_from_this());
119 LLDB_LOG(log,
"preparing to attach to process '{0}' on background thread.",
122 if (!DebugActiveProcess((DWORD)pid)) {
142 LLDB_LOG(log,
"terminate = {0}, inferior={1}.", terminate, pid);
146 bool expected =
false;
161 BOOL terminate_suceeded = TerminateProcess(handle, 0);
163 "calling TerminateProcess({0}, 0) (inferior={1}), success={2}",
164 handle, pid, terminate_suceeded);
167 "NOT calling TerminateProcess because the inferior is not valid "
168 "({0}, 0) (inferior={1})",
178 LLDB_LOG(log,
"masking active exception");
188 if (!::DebugBreakProcess(
189 GetProcess().GetNativeProcess().GetSystemHandle())) {
194 LLDB_LOG(log,
"waiting for detach from process {0} to complete.", pid);
197 if (wait_result != WAIT_OBJECT_0) {
199 LLDB_LOG(log,
"error: WaitForSingleObject({0}, 5000) returned {1}",
202 LLDB_LOG(log,
"detach from process {0} completed successfully.", pid);
204 if (!
error.Success()) {
205 LLDB_LOG(log,
"encountered an error while trying to stop process {0}. {1}",
216 LLDB_LOG(log,
"broadcasting for inferior process {0}.",
234 DEBUG_EVENT dbe = {};
235 bool should_debug =
true;
236 LLDB_LOGV(log,
"Entering WaitForDebugEvent loop");
237 while (should_debug) {
238 LLDB_LOGV(log,
"Calling WaitForDebugEvent");
239 BOOL wait_result = WaitForDebugEvent(&dbe, INFINITE);
241 DWORD continue_status = DBG_CONTINUE;
243 switch (dbe.dwDebugEventCode) {
245 llvm_unreachable(
"Unhandle debug event code!");
246 case EXCEPTION_DEBUG_EVENT: {
248 dbe.u.Exception, dbe.dwThreadId, shutting_down);
250 if (status == ExceptionResult::MaskException)
251 continue_status = DBG_CONTINUE;
252 else if (status == ExceptionResult::SendToApplication)
253 continue_status = DBG_EXCEPTION_NOT_HANDLED;
257 case CREATE_THREAD_DEBUG_EVENT:
261 case CREATE_PROCESS_DEBUG_EVENT:
265 case EXIT_THREAD_DEBUG_EVENT:
269 case EXIT_PROCESS_DEBUG_EVENT:
272 should_debug =
false;
274 case LOAD_DLL_DEBUG_EVENT:
277 case UNLOAD_DLL_DEBUG_EVENT:
280 case OUTPUT_DEBUG_STRING_EVENT:
281 continue_status =
HandleODSEvent(dbe.u.DebugString, dbe.dwThreadId);
285 if (dbe.u.RipInfo.dwType == SLE_ERROR)
286 should_debug =
false;
290 LLDB_LOGV(log,
"calling ContinueDebugEvent({0}, {1}, {2}) on thread {3}.",
291 dbe.dwProcessId, dbe.dwThreadId, continue_status,
292 ::GetCurrentThreadId());
294 ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status);
303 (dbe.u.Exception.ExceptionRecord.ExceptionCode ==
304 EXCEPTION_BREAKPOINT ||
305 dbe.u.Exception.ExceptionRecord.ExceptionCode ==
308 "Breakpoint exception is cue to detach from process {0:x}",
316 while (WaitForDebugEvent(&dbe, 0)) {
317 continue_status = DBG_CONTINUE;
318 if (dbe.dwDebugEventCode == EXCEPTION_DEBUG_EVENT &&
319 !(dbe.u.Exception.ExceptionRecord.ExceptionCode ==
320 EXCEPTION_BREAKPOINT ||
321 dbe.u.Exception.ExceptionRecord.ExceptionCode ==
323 dbe.u.Exception.ExceptionRecord.ExceptionCode ==
324 EXCEPTION_SINGLE_STEP))
325 continue_status = DBG_EXCEPTION_NOT_HANDLED;
326 ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId,
336 should_debug =
false;
339 LLDB_LOG(log,
"returned FALSE from WaitForDebugEvent. Error = {0}",
342 should_debug =
false;
347 LLDB_LOG(log,
"WaitForDebugEvent loop completed, exiting.");
353 DWORD thread_id,
bool shutting_down) {
357 (info.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT ||
363 return is_breakpoint ? ExceptionResult::MaskException
364 : ExceptionResult::SendToApplication;
367 bool first_chance = (info.dwFirstChance != 0);
371 LLDB_LOG(log,
"encountered {0} chance exception {1:x} on thread {2:x}",
372 first_chance ?
"first" :
"second",
373 info.ExceptionRecord.ExceptionCode, thread_id);
379 LLDB_LOG(log,
"waiting for ExceptionPred != BreakInDebugger");
381 ExceptionResult::BreakInDebugger);
391 LLDB_LOG(log,
"Thread {0} spawned in process {1}", thread_id,
403 uint32_t process_id = ::GetProcessId(info.hProcess);
405 LLDB_LOG(log,
"process {0} spawned", process_id);
407 std::string thread_name;
408 llvm::raw_string_ostream name_stream(thread_name);
409 name_stream <<
"lldb.plugin.process-windows.secondary[" << process_id <<
"]";
410 llvm::set_thread_name(thread_name);
430 LLDB_LOG(log,
"Thread {0} exited with code {1} in process {2}", thread_id,
450 DWORD dwFileSizeHi = 0;
451 DWORD dwFileSizeLo = ::GetFileSize(hFile, &dwFileSizeHi);
452 if (dwFileSizeLo == 0 && dwFileSizeHi == 0)
456 ::CreateFileMappingW(hFile,
nullptr, PAGE_READONLY, 0, 1, NULL),
nullptr);
460 auto view_deleter = [](
void *pMem) { ::UnmapViewOfFile(pMem); };
461 std::unique_ptr<void,
decltype(view_deleter)> pMem(
462 ::MapViewOfFile(filemap.
get(), FILE_MAP_READ, 0, 0, 1), view_deleter);
466 std::array<wchar_t, MAX_PATH + 1> mapped_filename;
467 if (!::GetMappedFileNameW(::GetCurrentProcess(), pMem.get(),
468 mapped_filename.data(), mapped_filename.size()))
472 std::array<wchar_t, 512> drive_strings;
473 drive_strings[0] = L
'\0';
474 if (!::GetLogicalDriveStringsW(drive_strings.size(), drive_strings.data()))
477 std::array<wchar_t, 3> drive = {L
"_:"};
478 for (
const wchar_t *it = drive_strings.data(); *it != L
'\0';
479 it += wcslen(it) + 1) {
482 std::array<wchar_t, MAX_PATH> device_name;
483 if (::QueryDosDeviceW(drive.data(), device_name.data(),
484 device_name.size())) {
485 size_t device_name_len = wcslen(device_name.data());
486 if (device_name_len < mapped_filename.size()) {
487 bool match = _wcsnicmp(mapped_filename.data(), device_name.data(),
488 device_name_len) == 0;
489 if (match && mapped_filename[device_name_len] == L
'\\') {
491 std::wstring rebuilt_path(drive.data());
492 rebuilt_path.append(&mapped_filename[device_name_len]);
493 std::string path_utf8;
494 llvm::convertWideToUTF8(rebuilt_path, path_utf8);
507 if (info.hFile ==
nullptr) {
509 LLDB_LOG(log,
"Warning: Inferior {0} has a NULL file handle, returning...",
514 auto on_load_dll = [&](llvm::StringRef path) {
519 LLDB_LOG(log,
"Inferior {0} - DLL '{1}' loaded at address {2:x}...",
525 std::vector<wchar_t> buffer(1);
526 DWORD required_size =
527 GetFinalPathNameByHandleW(info.hFile, &buffer[0], 0, VOLUME_NAME_DOS);
528 if (required_size > 0) {
529 buffer.resize(required_size + 1);
530 required_size = GetFinalPathNameByHandleW(info.hFile, &buffer[0],
531 required_size, VOLUME_NAME_DOS);
532 std::string path_str_utf8;
533 llvm::convertWideToUTF8(buffer.data(), path_str_utf8);
534 llvm::StringRef path_str = path_str_utf8;
535 const char *path = path_str.data();
536 if (path_str.starts_with(
"\\\\?\\"))
540 }
else if (std::optional<std::string> path =
546 "Inferior {0} - Error {1} occurred calling GetFinalPathNameByHandle",
550 ::CloseHandle(info.hFile);
558 LLDB_LOG(log,
"process {0} unloading DLL at addr {1:x}.",
575 LLDB_LOG(log,
"encountered error {0} (type={1}) in process {2} thread {3}",
static llvm::raw_ostream & error(Stream &strm)
static std::optional< std::string > GetFileNameFromHandleFallback(HANDLE hFile)
#define STATUS_WX86_BREAKPOINT
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
#define LLDB_LOGV(log,...)
std::atomic< bool > m_is_shutting_down
std::atomic< DWORD > m_pid_to_detach
DWORD HandleExitThreadEvent(const EXIT_THREAD_DEBUG_INFO &info, DWORD thread_id)
DWORD HandleODSEvent(const OUTPUT_DEBUG_STRING_INFO &info, DWORD thread_id)
DWORD HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info, DWORD thread_id)
DWORD HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info, DWORD thread_id)
Status StopDebugging(bool terminate)
DWORD HandleRipEvent(const RIP_INFO &info, DWORD thread_id)
void ContinueAsyncException(ExceptionResult result)
HostProcess GetProcess() const
lldb::thread_result_t DebuggerThreadAttachRoutine(lldb::pid_t pid, const ProcessAttachInfo &launch_info)
DebuggerThread(DebugDelegateSP debug_delegate)
void FreeProcessHandles()
virtual ~DebuggerThread()
DWORD HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info, DWORD thread_id)
ExceptionResult HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thread_id, bool shutting_down)
Status DebugAttach(lldb::pid_t pid, const ProcessAttachInfo &attach_info)
lldb::thread_result_t DebuggerThreadLaunchRoutine(const ProcessLaunchInfo &launch_info)
ExceptionRecordSP m_active_exception
Predicate< ExceptionResult > m_exception_pred
HANDLE m_debugging_ended_event
Status DebugLaunch(const ProcessLaunchInfo &launch_info)
DWORD HandleUnloadDllEvent(const UNLOAD_DLL_DEBUG_INFO &info, DWORD thread_id)
DebugDelegateSP m_debug_delegate
DWORD HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info, DWORD thread_id)
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
lldb::process_t GetSystemHandle() const
HostNativeProcessBase & GetNativeProcess()
lldb::pid_t GetProcessId() const
HostNativeThread & GetNativeThread()
void SetValue(T value, PredicateBroadcastType broadcast_type)
Value set accessor.
T GetValue() const
Value get accessor.
std::optional< T > WaitForValueNotEqualTo(T value, const Timeout< std::micro > &timeout=std::nullopt)
Wait for m_value to not be equal to value.
FileSpec & GetExecutableFile()
HostProcess LaunchProcess(const ProcessLaunchInfo &launch_info, Status &error) override
static Status FromError(llvm::Error error)
Avoid using this in new code. Migrate APIs to llvm::Expected instead.
static llvm::Expected< HostThread > LaunchThread(llvm::StringRef name, std::function< lldb::thread_result_t()> thread_function, size_t min_stack_byte_size=0)
#define LLDB_INVALID_PROCESS
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
@ eBroadcastNever
No broadcast will be sent when the value is modified.
@ eBroadcastAlways
Always send a broadcast when the value is modified.
std::shared_ptr< IDebugDelegate > DebugDelegateSP
@ eErrorTypeWin32
Standard Win32 error codes.