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) {
60 result =
Status(secondary_thread.takeError());
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) {
78 result =
Status(secondary_thread.takeError());
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;
242 switch (dbe.dwDebugEventCode) {
244 llvm_unreachable(
"Unhandle debug event code!");
245 case EXCEPTION_DEBUG_EVENT: {
249 if (status == ExceptionResult::MaskException)
250 continue_status = DBG_CONTINUE;
251 else if (status == ExceptionResult::SendToApplication)
252 continue_status = DBG_EXCEPTION_NOT_HANDLED;
256 case CREATE_THREAD_DEBUG_EVENT:
260 case CREATE_PROCESS_DEBUG_EVENT:
264 case EXIT_THREAD_DEBUG_EVENT:
268 case EXIT_PROCESS_DEBUG_EVENT:
271 should_debug =
false;
273 case LOAD_DLL_DEBUG_EVENT:
276 case UNLOAD_DLL_DEBUG_EVENT:
279 case OUTPUT_DEBUG_STRING_EVENT:
280 continue_status =
HandleODSEvent(dbe.u.DebugString, dbe.dwThreadId);
284 if (dbe.u.RipInfo.dwType == SLE_ERROR)
285 should_debug =
false;
289 LLDB_LOGV(log,
"calling ContinueDebugEvent({0}, {1}, {2}) on thread {3}.",
290 dbe.dwProcessId, dbe.dwThreadId, continue_status,
291 ::GetCurrentThreadId());
293 ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status);
296 should_debug =
false;
299 LLDB_LOG(log,
"returned FALSE from WaitForDebugEvent. Error = {0}",
302 should_debug =
false;
307 LLDB_LOG(log,
"WaitForDebugEvent loop completed, exiting.");
321 (info.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT ||
323 LLDB_LOG(log,
"Breakpoint exception is cue to detach from process {0:x}",
331 return ExceptionResult::SendToApplication;
334 bool first_chance = (info.dwFirstChance != 0);
338 LLDB_LOG(log,
"encountered {0} chance exception {1:x} on thread {2:x}",
339 first_chance ?
"first" :
"second",
340 info.ExceptionRecord.ExceptionCode, thread_id);
346 LLDB_LOG(log,
"waiting for ExceptionPred != BreakInDebugger");
348 ExceptionResult::BreakInDebugger);
358 LLDB_LOG(log,
"Thread {0} spawned in process {1}", thread_id,
370 uint32_t process_id = ::GetProcessId(info.hProcess);
372 LLDB_LOG(log,
"process {0} spawned", process_id);
374 std::string thread_name;
375 llvm::raw_string_ostream name_stream(thread_name);
376 name_stream <<
"lldb.plugin.process-windows.secondary[" << process_id <<
"]";
378 llvm::set_thread_name(thread_name);
398 LLDB_LOG(log,
"Thread {0} exited with code {1} in process {2}", thread_id,
418 DWORD dwFileSizeHi = 0;
419 DWORD dwFileSizeLo = ::GetFileSize(hFile, &dwFileSizeHi);
420 if (dwFileSizeLo == 0 && dwFileSizeHi == 0)
424 ::CreateFileMappingW(hFile,
nullptr, PAGE_READONLY, 0, 1, NULL),
nullptr);
428 auto view_deleter = [](
void *pMem) { ::UnmapViewOfFile(pMem); };
429 std::unique_ptr<void,
decltype(view_deleter)> pMem(
430 ::MapViewOfFile(filemap.
get(), FILE_MAP_READ, 0, 0, 1), view_deleter);
434 std::array<wchar_t, MAX_PATH + 1> mapped_filename;
435 if (!::GetMappedFileNameW(::GetCurrentProcess(), pMem.get(),
436 mapped_filename.data(), mapped_filename.size()))
440 std::array<wchar_t, 512> drive_strings;
441 drive_strings[0] = L
'\0';
442 if (!::GetLogicalDriveStringsW(drive_strings.size(), drive_strings.data()))
445 std::array<wchar_t, 3> drive = {L
"_:"};
446 for (
const wchar_t *it = drive_strings.data(); *it != L
'\0';
447 it += wcslen(it) + 1) {
450 std::array<wchar_t, MAX_PATH> device_name;
451 if (::QueryDosDeviceW(drive.data(), device_name.data(),
452 device_name.size())) {
453 size_t device_name_len = wcslen(device_name.data());
454 if (device_name_len < mapped_filename.size()) {
455 bool match = _wcsnicmp(mapped_filename.data(), device_name.data(),
456 device_name_len) == 0;
457 if (match && mapped_filename[device_name_len] == L
'\\') {
459 std::wstring rebuilt_path(drive.data());
460 rebuilt_path.append(&mapped_filename[device_name_len]);
461 std::string path_utf8;
462 llvm::convertWideToUTF8(rebuilt_path, path_utf8);
475 if (info.hFile ==
nullptr) {
477 LLDB_LOG(log,
"Warning: Inferior {0} has a NULL file handle, returning...",
482 auto on_load_dll = [&](llvm::StringRef path) {
487 LLDB_LOG(log,
"Inferior {0} - DLL '{1}' loaded at address {2:x}...",
493 std::vector<wchar_t> buffer(1);
494 DWORD required_size =
495 GetFinalPathNameByHandleW(info.hFile, &buffer[0], 0, VOLUME_NAME_DOS);
496 if (required_size > 0) {
497 buffer.resize(required_size + 1);
498 required_size = GetFinalPathNameByHandleW(info.hFile, &buffer[0],
499 required_size, VOLUME_NAME_DOS);
500 std::string path_str_utf8;
501 llvm::convertWideToUTF8(buffer.data(), path_str_utf8);
502 llvm::StringRef path_str = path_str_utf8;
503 const char *path = path_str.data();
504 if (path_str.starts_with(
"\\\\?\\"))
508 }
else if (std::optional<std::string> path =
514 "Inferior {0} - Error {1} occurred calling GetFinalPathNameByHandle",
518 ::CloseHandle(info.hFile);
526 LLDB_LOG(log,
"process {0} unloading DLL at addr {1:x}.",
543 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)
ExceptionResult HandleExceptionEvent(const EXCEPTION_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)
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 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.