29#include "llvm/ADT/STLExtras.h"
30#include "llvm/Support/ConvertUTF.h"
31#include "llvm/Support/Threading.h"
32#include "llvm/Support/raw_ostream.h"
37#ifndef STATUS_WX86_BREAKPOINT
38#define STATUS_WX86_BREAKPOINT 0x4000001FL
48 HMODULE h_kernel32 = LoadLibraryW(L
"kernel32.dll");
50 llvm::Error err = llvm::errorCodeToError(
51 std::error_code(GetLastError(), std::system_category()));
53 "Could not load kernel32: {0}");
58 GetProcAddress(h_kernel32,
"WaitForDebugEventEx"));
73 "WaitForDebugEventEx unavailable, using WaitForDebugEvent instead. "
74 "Unicode strings from OutputDebugStringW might show incorrectly.");
94 "lldb.plugin.process-windows.secondary[?]",
96 if (!secondary_thread) {
98 LLDB_LOG(log,
"couldn't launch debugger thread. {0}", result);
107 LLDB_LOG(log,
"attaching to '{0}'", pid);
111 "lldb.plugin.process-windows.secondary[?]", [
this, pid, attach_info] {
114 if (!secondary_thread) {
116 LLDB_LOG(log,
"couldn't attach to process '{0}'. {1}", pid, result);
126 std::shared_ptr<DebuggerThread> this_ref(shared_from_this());
129 LLDB_LOG(log,
"preparing to launch '{0}' on background thread.",
153 std::shared_ptr<DebuggerThread> this_ref(shared_from_this());
156 LLDB_LOG(log,
"preparing to attach to process '{0}' on background thread.",
159 if (!DebugActiveProcess((DWORD)pid)) {
179 LLDB_LOG(log,
"terminate = {0}, inferior={1}.", terminate, pid);
183 bool expected =
false;
198 BOOL terminate_suceeded = TerminateProcess(handle, 0);
200 "calling TerminateProcess({0}, 0) (inferior={1}), success={2}",
201 handle, pid, terminate_suceeded);
204 "NOT calling TerminateProcess because the inferior is not valid "
205 "({0}, 0) (inferior={1})",
215 LLDB_LOG(log,
"masking active exception");
225 if (!::DebugBreakProcess(
226 GetProcess().GetNativeProcess().GetSystemHandle())) {
231 LLDB_LOG(log,
"waiting for detach from process {0} to complete.", pid);
234 if (wait_result != WAIT_OBJECT_0) {
236 LLDB_LOG(log,
"error: WaitForSingleObject({0}, 5000) returned {1}",
239 LLDB_LOG(log,
"detach from process {0} completed successfully.", pid);
241 if (!
error.Success()) {
242 LLDB_LOG(log,
"encountered an error while trying to stop process {0}. {1}",
253 LLDB_LOG(log,
"broadcasting for inferior process {0}.",
271 DEBUG_EVENT dbe = {};
272 bool should_debug =
true;
274 while (should_debug) {
278 DWORD continue_status = DBG_CONTINUE;
280 switch (dbe.dwDebugEventCode) {
282 llvm_unreachable(
"Unhandle debug event code!");
283 case EXCEPTION_DEBUG_EVENT: {
285 dbe.u.Exception, dbe.dwThreadId, shutting_down);
288 continue_status = DBG_CONTINUE;
290 continue_status = DBG_EXCEPTION_NOT_HANDLED;
294 case CREATE_THREAD_DEBUG_EVENT:
298 case CREATE_PROCESS_DEBUG_EVENT:
302 case EXIT_THREAD_DEBUG_EVENT:
306 case EXIT_PROCESS_DEBUG_EVENT:
309 should_debug =
false;
311 case LOAD_DLL_DEBUG_EVENT:
314 case UNLOAD_DLL_DEBUG_EVENT:
317 case OUTPUT_DEBUG_STRING_EVENT:
318 continue_status =
HandleODSEvent(dbe.u.DebugString, dbe.dwThreadId);
322 if (dbe.u.RipInfo.dwType == SLE_ERROR)
323 should_debug =
false;
328 log,
"calling ContinueDebugEvent({0}, {1}, {2}) on thread {3}.",
329 dbe.dwProcessId, dbe.dwThreadId, continue_status,
330 ::GetCurrentThreadId());
332 ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status);
341 (dbe.u.Exception.ExceptionRecord.ExceptionCode ==
342 EXCEPTION_BREAKPOINT ||
343 dbe.u.Exception.ExceptionRecord.ExceptionCode ==
346 "Breakpoint exception is cue to detach from process {0:x}",
355 continue_status = DBG_CONTINUE;
356 if (dbe.dwDebugEventCode == EXCEPTION_DEBUG_EVENT &&
357 !(dbe.u.Exception.ExceptionRecord.ExceptionCode ==
358 EXCEPTION_BREAKPOINT ||
359 dbe.u.Exception.ExceptionRecord.ExceptionCode ==
361 dbe.u.Exception.ExceptionRecord.ExceptionCode ==
362 EXCEPTION_SINGLE_STEP))
363 continue_status = DBG_EXCEPTION_NOT_HANDLED;
364 ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId,
374 should_debug =
false;
377 LLDB_LOG(log,
"returned FALSE from WaitForDebugEvent. Error = {0}",
380 should_debug =
false;
385 LLDB_LOG(log,
"WaitForDebugEvent loop completed, exiting.");
391 DWORD thread_id,
bool shutting_down) {
395 (info.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT ||
405 bool first_chance = (info.dwFirstChance != 0);
409 LLDB_LOG(log,
"encountered {0} chance exception {1:x} on thread {2:x}",
410 first_chance ?
"first" :
"second",
411 info.ExceptionRecord.ExceptionCode, thread_id);
417 LLDB_LOG(log,
"waiting for ExceptionPred != BreakInDebugger");
429 LLDB_LOG(log,
"Thread {0} spawned in process {1}", thread_id,
432 thread.GetNativeThread().SetOwnsHandle(
false);
441 uint32_t process_id = ::GetProcessId(info.hProcess);
443 LLDB_LOG(log,
"process {0} spawned", process_id);
445 std::string thread_name;
446 llvm::raw_string_ostream name_stream(thread_name);
447 name_stream <<
"lldb.plugin.process-windows.secondary[" << process_id <<
"]";
448 llvm::set_thread_name(thread_name);
468 LLDB_LOG(log,
"Thread {0} exited with code {1} in process {2}", thread_id,
469 info.dwExitCode,
m_process.GetProcessId());
488 DWORD dwFileSizeHi = 0;
489 DWORD dwFileSizeLo = ::GetFileSize(hFile, &dwFileSizeHi);
490 if (dwFileSizeLo == 0 && dwFileSizeHi == 0)
494 ::CreateFileMappingW(hFile,
nullptr, PAGE_READONLY, 0, 1, NULL),
nullptr);
498 auto view_deleter = [](
void *pMem) { ::UnmapViewOfFile(pMem); };
499 std::unique_ptr<void,
decltype(view_deleter)> pMem(
500 ::MapViewOfFile(filemap.
get(), FILE_MAP_READ, 0, 0, 1), view_deleter);
504 std::array<wchar_t, MAX_PATH + 1> mapped_filename;
505 if (!::GetMappedFileNameW(::GetCurrentProcess(), pMem.get(),
506 mapped_filename.data(), mapped_filename.size()))
510 std::array<wchar_t, 512> drive_strings;
511 drive_strings[0] = L
'\0';
512 if (!::GetLogicalDriveStringsW(drive_strings.size(), drive_strings.data()))
515 std::array<wchar_t, 3> drive = {L
"_:"};
516 for (
const wchar_t *it = drive_strings.data(); *it != L
'\0';
517 it += wcslen(it) + 1) {
520 std::array<wchar_t, MAX_PATH> device_name;
521 if (::QueryDosDeviceW(drive.data(), device_name.data(),
522 device_name.size())) {
523 size_t device_name_len = wcslen(device_name.data());
524 if (device_name_len < mapped_filename.size()) {
525 bool match = _wcsnicmp(mapped_filename.data(), device_name.data(),
526 device_name_len) == 0;
527 if (match && mapped_filename[device_name_len] == L
'\\') {
529 std::wstring rebuilt_path(drive.data());
530 rebuilt_path.append(&mapped_filename[device_name_len]);
531 std::string path_utf8;
532 llvm::convertWideToUTF8(rebuilt_path, path_utf8);
545 if (info.hFile ==
nullptr) {
547 LLDB_LOG(log,
"Warning: Inferior {0} has a NULL file handle, returning...",
552 auto on_load_dll = [&](llvm::StringRef path) {
557 LLDB_LOG(log,
"Inferior {0} - DLL '{1}' loaded at address {2:x}...",
558 m_process.GetProcessId(), path, info.lpBaseOfDll);
563 std::vector<wchar_t> buffer(1);
564 DWORD required_size =
565 GetFinalPathNameByHandleW(info.hFile, &buffer[0], 0, VOLUME_NAME_DOS);
566 if (required_size > 0) {
567 buffer.resize(required_size + 1);
568 required_size = GetFinalPathNameByHandleW(info.hFile, &buffer[0],
569 required_size, VOLUME_NAME_DOS);
570 std::string path_str_utf8;
571 llvm::convertWideToUTF8(buffer.data(), path_str_utf8);
572 llvm::StringRef path_str = path_str_utf8;
573 const char *path = path_str.data();
574 if (path_str.starts_with(
"\\\\?\\"))
578 }
else if (std::optional<std::string> path =
584 "Inferior {0} - Error {1} occurred calling GetFinalPathNameByHandle",
585 m_process.GetProcessId(), ::GetLastError());
588 ::CloseHandle(info.hFile);
596 LLDB_LOG(log,
"process {0} unloading DLL at addr {1:x}.",
597 m_process.GetProcessId(), info.lpBaseOfDll);
613 LLDB_LOG(log,
"encountered error {0} (type={1}) in process {2} thread {3}",
614 info.dwError, info.dwType,
m_process.GetProcessId(), thread_id);
static llvm::raw_ostream & error(Stream &strm)
static WaitForDebugEventFn * g_wait_for_debug_event
static std::optional< std::string > GetFileNameFromHandleFallback(HANDLE hFile)
#define STATUS_WX86_BREAKPOINT
BOOL WINAPI WaitForDebugEventFn(LPDEBUG_EVENT, DWORD)
static WaitForDebugEventFn * GetWaitForDebugEventEx()
static void InitializeWaitForDebugEvent()
WaitForDebugEventEx is only available on Windows 10+.
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
#define LLDB_LOG_ERROR(log, error,...)
#define LLDB_LOG_VERBOSE(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::pid_t GetProcessId() const
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.