1 | //===-- DebuggerThread.cpp ------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "DebuggerThread.h" |
10 | #include "ExceptionRecord.h" |
11 | #include "IDebugDelegate.h" |
12 | |
13 | #include "lldb/Core/ModuleSpec.h" |
14 | #include "lldb/Host/ProcessLaunchInfo.h" |
15 | #include "lldb/Host/ThreadLauncher.h" |
16 | #include "lldb/Host/windows/AutoHandle.h" |
17 | #include "lldb/Host/windows/HostProcessWindows.h" |
18 | #include "lldb/Host/windows/HostThreadWindows.h" |
19 | #include "lldb/Host/windows/ProcessLauncherWindows.h" |
20 | #include "lldb/Target/Process.h" |
21 | #include "lldb/Utility/FileSpec.h" |
22 | #include "lldb/Utility/Log.h" |
23 | #include "lldb/Utility/Predicate.h" |
24 | #include "lldb/Utility/Status.h" |
25 | |
26 | #include "Plugins/Process/Windows/Common/ProcessWindowsLog.h" |
27 | |
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" |
32 | |
33 | #include <optional> |
34 | #include <psapi.h> |
35 | |
36 | #ifndef STATUS_WX86_BREAKPOINT |
37 | #define STATUS_WX86_BREAKPOINT 0x4000001FL // For WOW64 |
38 | #endif |
39 | |
40 | using namespace lldb; |
41 | using namespace lldb_private; |
42 | |
43 | DebuggerThread::DebuggerThread(DebugDelegateSP debug_delegate) |
44 | : m_debug_delegate(debug_delegate), m_pid_to_detach(0), |
45 | m_is_shutting_down(false) { |
46 | m_debugging_ended_event = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); |
47 | } |
48 | |
49 | DebuggerThread::~DebuggerThread() { ::CloseHandle(m_debugging_ended_event); } |
50 | |
51 | Status DebuggerThread::DebugLaunch(const ProcessLaunchInfo &launch_info) { |
52 | Log *log = GetLog(mask: WindowsLog::Process); |
53 | LLDB_LOG(log, "launching '{0}'" , launch_info.GetExecutableFile().GetPath()); |
54 | |
55 | Status result; |
56 | llvm::Expected<HostThread> secondary_thread = ThreadLauncher::LaunchThread( |
57 | name: "lldb.plugin.process-windows.secondary[?]" , |
58 | thread_function: [this, launch_info] { return DebuggerThreadLaunchRoutine(launch_info); }); |
59 | if (!secondary_thread) { |
60 | result = Status::FromError(error: secondary_thread.takeError()); |
61 | LLDB_LOG(log, "couldn't launch debugger thread. {0}" , result); |
62 | } |
63 | |
64 | return result; |
65 | } |
66 | |
67 | Status DebuggerThread::DebugAttach(lldb::pid_t pid, |
68 | const ProcessAttachInfo &attach_info) { |
69 | Log *log = GetLog(mask: WindowsLog::Process); |
70 | LLDB_LOG(log, "attaching to '{0}'" , pid); |
71 | |
72 | Status result; |
73 | llvm::Expected<HostThread> secondary_thread = ThreadLauncher::LaunchThread( |
74 | name: "lldb.plugin.process-windows.secondary[?]" , thread_function: [this, pid, attach_info] { |
75 | return DebuggerThreadAttachRoutine(pid, launch_info: attach_info); |
76 | }); |
77 | if (!secondary_thread) { |
78 | result = Status::FromError(error: secondary_thread.takeError()); |
79 | LLDB_LOG(log, "couldn't attach to process '{0}'. {1}" , pid, result); |
80 | } |
81 | |
82 | return result; |
83 | } |
84 | |
85 | lldb::thread_result_t DebuggerThread::DebuggerThreadLaunchRoutine( |
86 | const ProcessLaunchInfo &launch_info) { |
87 | // Grab a shared_ptr reference to this so that we know it won't get deleted |
88 | // until after the thread routine has exited. |
89 | std::shared_ptr<DebuggerThread> this_ref(shared_from_this()); |
90 | |
91 | Log *log = GetLog(mask: WindowsLog::Process); |
92 | LLDB_LOG(log, "preparing to launch '{0}' on background thread." , |
93 | launch_info.GetExecutableFile().GetPath()); |
94 | |
95 | Status error; |
96 | ProcessLauncherWindows launcher; |
97 | HostProcess process(launcher.LaunchProcess(launch_info, error)); |
98 | // If we couldn't create the process, notify waiters immediately. Otherwise |
99 | // enter the debug loop and wait until we get the create process debug |
100 | // notification. Note that if the process was created successfully, we can |
101 | // throw away the process handle we got from CreateProcess because Windows |
102 | // will give us another (potentially more useful?) handle when it sends us |
103 | // the CREATE_PROCESS_DEBUG_EVENT. |
104 | if (error.Success()) |
105 | DebugLoop(); |
106 | else |
107 | m_debug_delegate->OnDebuggerError(error, type: 0); |
108 | |
109 | return {}; |
110 | } |
111 | |
112 | lldb::thread_result_t DebuggerThread::DebuggerThreadAttachRoutine( |
113 | lldb::pid_t pid, const ProcessAttachInfo &attach_info) { |
114 | // Grab a shared_ptr reference to this so that we know it won't get deleted |
115 | // until after the thread routine has exited. |
116 | std::shared_ptr<DebuggerThread> this_ref(shared_from_this()); |
117 | |
118 | Log *log = GetLog(mask: WindowsLog::Process); |
119 | LLDB_LOG(log, "preparing to attach to process '{0}' on background thread." , |
120 | pid); |
121 | |
122 | if (!DebugActiveProcess((DWORD)pid)) { |
123 | Status error(::GetLastError(), eErrorTypeWin32); |
124 | m_debug_delegate->OnDebuggerError(error, type: 0); |
125 | return {}; |
126 | } |
127 | |
128 | // The attach was successful, enter the debug loop. From here on out, this |
129 | // is no different than a create process operation, so all the same comments |
130 | // in DebugLaunch should apply from this point out. |
131 | DebugLoop(); |
132 | |
133 | return {}; |
134 | } |
135 | |
136 | Status DebuggerThread::StopDebugging(bool terminate) { |
137 | Status error; |
138 | |
139 | lldb::pid_t pid = m_process.GetProcessId(); |
140 | |
141 | Log *log = GetLog(mask: WindowsLog::Process); |
142 | LLDB_LOG(log, "terminate = {0}, inferior={1}." , terminate, pid); |
143 | |
144 | // Set m_is_shutting_down to true if it was false. Return if it was already |
145 | // true. |
146 | bool expected = false; |
147 | if (!m_is_shutting_down.compare_exchange_strong(i1&: expected, i2: true)) |
148 | return error; |
149 | |
150 | // Make a copy of the process, since the termination sequence will reset |
151 | // DebuggerThread's internal copy and it needs to remain open for the Wait |
152 | // operation. |
153 | HostProcess process_copy = m_process; |
154 | lldb::process_t handle = m_process.GetNativeProcess().GetSystemHandle(); |
155 | |
156 | if (terminate) { |
157 | if (handle != nullptr && handle != LLDB_INVALID_PROCESS) { |
158 | // Initiate the termination before continuing the exception, so that the |
159 | // next debug event we get is the exit process event, and not some other |
160 | // event. |
161 | BOOL terminate_suceeded = TerminateProcess(handle, 0); |
162 | LLDB_LOG(log, |
163 | "calling TerminateProcess({0}, 0) (inferior={1}), success={2}" , |
164 | handle, pid, terminate_suceeded); |
165 | } else { |
166 | LLDB_LOG(log, |
167 | "NOT calling TerminateProcess because the inferior is not valid " |
168 | "({0}, 0) (inferior={1})" , |
169 | handle, pid); |
170 | } |
171 | } |
172 | |
173 | // If we're stuck waiting for an exception to continue (e.g. the user is at a |
174 | // breakpoint messing around in the debugger), continue it now. But only |
175 | // AFTER calling TerminateProcess to make sure that the very next call to |
176 | // WaitForDebugEvent is an exit process event. |
177 | if (m_active_exception.get()) { |
178 | LLDB_LOG(log, "masking active exception" ); |
179 | ContinueAsyncException(result: ExceptionResult::MaskException); |
180 | } |
181 | |
182 | if (!terminate) { |
183 | // Indicate that we want to detach. |
184 | m_pid_to_detach = GetProcess().GetProcessId(); |
185 | |
186 | // Force a fresh break so that the detach can happen from the debugger |
187 | // thread. |
188 | if (!::DebugBreakProcess( |
189 | GetProcess().GetNativeProcess().GetSystemHandle())) { |
190 | error = Status(::GetLastError(), eErrorTypeWin32); |
191 | } |
192 | } |
193 | |
194 | LLDB_LOG(log, "waiting for detach from process {0} to complete." , pid); |
195 | |
196 | DWORD wait_result = WaitForSingleObject(m_debugging_ended_event, 5000); |
197 | if (wait_result != WAIT_OBJECT_0) { |
198 | error = Status(GetLastError(), eErrorTypeWin32); |
199 | LLDB_LOG(log, "error: WaitForSingleObject({0}, 5000) returned {1}" , |
200 | m_debugging_ended_event, wait_result); |
201 | } else |
202 | LLDB_LOG(log, "detach from process {0} completed successfully." , pid); |
203 | |
204 | if (!error.Success()) { |
205 | LLDB_LOG(log, "encountered an error while trying to stop process {0}. {1}" , |
206 | pid, error); |
207 | } |
208 | return error; |
209 | } |
210 | |
211 | void DebuggerThread::ContinueAsyncException(ExceptionResult result) { |
212 | if (!m_active_exception.get()) |
213 | return; |
214 | |
215 | Log *log = GetLog(mask: WindowsLog::Process | WindowsLog::Exception); |
216 | LLDB_LOG(log, "broadcasting for inferior process {0}." , |
217 | m_process.GetProcessId()); |
218 | |
219 | m_active_exception.reset(); |
220 | m_exception_pred.SetValue(value: result, broadcast_type: eBroadcastAlways); |
221 | } |
222 | |
223 | void DebuggerThread::FreeProcessHandles() { |
224 | m_process = HostProcess(); |
225 | m_main_thread = HostThread(); |
226 | if (m_image_file) { |
227 | ::CloseHandle(m_image_file); |
228 | m_image_file = nullptr; |
229 | } |
230 | } |
231 | |
232 | void DebuggerThread::DebugLoop() { |
233 | Log *log = GetLog(mask: WindowsLog::Event); |
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); |
240 | if (wait_result) { |
241 | DWORD continue_status = DBG_CONTINUE; |
242 | bool shutting_down = m_is_shutting_down; |
243 | switch (dbe.dwDebugEventCode) { |
244 | default: |
245 | llvm_unreachable("Unhandle debug event code!" ); |
246 | case EXCEPTION_DEBUG_EVENT: { |
247 | ExceptionResult status = HandleExceptionEvent( |
248 | dbe.u.Exception, dbe.dwThreadId, shutting_down); |
249 | |
250 | if (status == ExceptionResult::MaskException) |
251 | continue_status = DBG_CONTINUE; |
252 | else if (status == ExceptionResult::SendToApplication) |
253 | continue_status = DBG_EXCEPTION_NOT_HANDLED; |
254 | |
255 | break; |
256 | } |
257 | case CREATE_THREAD_DEBUG_EVENT: |
258 | continue_status = |
259 | HandleCreateThreadEvent(dbe.u.CreateThread, dbe.dwThreadId); |
260 | break; |
261 | case CREATE_PROCESS_DEBUG_EVENT: |
262 | continue_status = |
263 | HandleCreateProcessEvent(dbe.u.CreateProcessInfo, dbe.dwThreadId); |
264 | break; |
265 | case EXIT_THREAD_DEBUG_EVENT: |
266 | continue_status = |
267 | HandleExitThreadEvent(dbe.u.ExitThread, dbe.dwThreadId); |
268 | break; |
269 | case EXIT_PROCESS_DEBUG_EVENT: |
270 | continue_status = |
271 | HandleExitProcessEvent(dbe.u.ExitProcess, dbe.dwThreadId); |
272 | should_debug = false; |
273 | break; |
274 | case LOAD_DLL_DEBUG_EVENT: |
275 | continue_status = HandleLoadDllEvent(dbe.u.LoadDll, dbe.dwThreadId); |
276 | break; |
277 | case UNLOAD_DLL_DEBUG_EVENT: |
278 | continue_status = HandleUnloadDllEvent(dbe.u.UnloadDll, dbe.dwThreadId); |
279 | break; |
280 | case OUTPUT_DEBUG_STRING_EVENT: |
281 | continue_status = HandleODSEvent(dbe.u.DebugString, dbe.dwThreadId); |
282 | break; |
283 | case RIP_EVENT: |
284 | continue_status = HandleRipEvent(dbe.u.RipInfo, dbe.dwThreadId); |
285 | if (dbe.u.RipInfo.dwType == SLE_ERROR) |
286 | should_debug = false; |
287 | break; |
288 | } |
289 | |
290 | LLDB_LOGV(log, "calling ContinueDebugEvent({0}, {1}, {2}) on thread {3}." , |
291 | dbe.dwProcessId, dbe.dwThreadId, continue_status, |
292 | ::GetCurrentThreadId()); |
293 | |
294 | ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status); |
295 | |
296 | // We have to DebugActiveProcessStop after ContinueDebugEvent, otherwise |
297 | // the target process will crash |
298 | if (shutting_down) { |
299 | // A breakpoint that occurs while `m_pid_to_detach` is non-zero is a |
300 | // magic exception that we use simply to wake up the DebuggerThread so |
301 | // that we can close out the debug loop. |
302 | if (m_pid_to_detach != 0 && |
303 | (dbe.u.Exception.ExceptionRecord.ExceptionCode == |
304 | EXCEPTION_BREAKPOINT || |
305 | dbe.u.Exception.ExceptionRecord.ExceptionCode == |
306 | STATUS_WX86_BREAKPOINT)) { |
307 | LLDB_LOG(log, |
308 | "Breakpoint exception is cue to detach from process {0:x}" , |
309 | m_pid_to_detach.load()); |
310 | |
311 | // detaching with leaving breakpoint exception event on the queue may |
312 | // cause target process to crash so process events as possible since |
313 | // target threads are running at this time, there is possibility to |
314 | // have some breakpoint exception between last WaitForDebugEvent and |
315 | // DebugActiveProcessStop but ignore for now. |
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 == |
322 | STATUS_WX86_BREAKPOINT || |
323 | dbe.u.Exception.ExceptionRecord.ExceptionCode == |
324 | EXCEPTION_SINGLE_STEP)) |
325 | continue_status = DBG_EXCEPTION_NOT_HANDLED; |
326 | ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, |
327 | continue_status); |
328 | } |
329 | |
330 | ::DebugActiveProcessStop(m_pid_to_detach); |
331 | m_detached = true; |
332 | } |
333 | } |
334 | |
335 | if (m_detached) { |
336 | should_debug = false; |
337 | } |
338 | } else { |
339 | LLDB_LOG(log, "returned FALSE from WaitForDebugEvent. Error = {0}" , |
340 | ::GetLastError()); |
341 | |
342 | should_debug = false; |
343 | } |
344 | } |
345 | FreeProcessHandles(); |
346 | |
347 | LLDB_LOG(log, "WaitForDebugEvent loop completed, exiting." ); |
348 | ::SetEvent(m_debugging_ended_event); |
349 | } |
350 | |
351 | ExceptionResult |
352 | DebuggerThread::HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, |
353 | DWORD thread_id, bool shutting_down) { |
354 | Log *log = GetLog(mask: WindowsLog::Event | WindowsLog::Exception); |
355 | if (shutting_down) { |
356 | bool is_breakpoint = |
357 | (info.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT || |
358 | info.ExceptionRecord.ExceptionCode == STATUS_WX86_BREAKPOINT); |
359 | |
360 | // Don't perform any blocking operations while we're shutting down. That |
361 | // will cause TerminateProcess -> WaitForSingleObject to time out. |
362 | // We should not send breakpoint exceptions to the application. |
363 | return is_breakpoint ? ExceptionResult::MaskException |
364 | : ExceptionResult::SendToApplication; |
365 | } |
366 | |
367 | bool first_chance = (info.dwFirstChance != 0); |
368 | |
369 | m_active_exception.reset( |
370 | p: new ExceptionRecord(info.ExceptionRecord, thread_id)); |
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); |
374 | |
375 | ExceptionResult result = |
376 | m_debug_delegate->OnDebugException(first_chance, record: *m_active_exception); |
377 | m_exception_pred.SetValue(value: result, broadcast_type: eBroadcastNever); |
378 | |
379 | LLDB_LOG(log, "waiting for ExceptionPred != BreakInDebugger" ); |
380 | result = *m_exception_pred.WaitForValueNotEqualTo( |
381 | value: ExceptionResult::BreakInDebugger); |
382 | |
383 | LLDB_LOG(log, "got ExceptionPred = {0}" , (int)m_exception_pred.GetValue()); |
384 | return result; |
385 | } |
386 | |
387 | DWORD |
388 | DebuggerThread::HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info, |
389 | DWORD thread_id) { |
390 | Log *log = GetLog(mask: WindowsLog::Event | WindowsLog::Thread); |
391 | LLDB_LOG(log, "Thread {0} spawned in process {1}" , thread_id, |
392 | m_process.GetProcessId()); |
393 | HostThread thread(info.hThread); |
394 | thread.GetNativeThread().SetOwnsHandle(false); |
395 | m_debug_delegate->OnCreateThread(thread); |
396 | return DBG_CONTINUE; |
397 | } |
398 | |
399 | DWORD |
400 | DebuggerThread::HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info, |
401 | DWORD thread_id) { |
402 | Log *log = GetLog(mask: WindowsLog::Event | WindowsLog::Process); |
403 | uint32_t process_id = ::GetProcessId(info.hProcess); |
404 | |
405 | LLDB_LOG(log, "process {0} spawned" , process_id); |
406 | |
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); |
411 | |
412 | // info.hProcess and info.hThread are closed automatically by Windows when |
413 | // EXIT_PROCESS_DEBUG_EVENT is received. |
414 | m_process = HostProcess(info.hProcess); |
415 | ((HostProcessWindows &)m_process.GetNativeProcess()).SetOwnsHandle(false); |
416 | m_main_thread = HostThread(info.hThread); |
417 | m_main_thread.GetNativeThread().SetOwnsHandle(false); |
418 | m_image_file = info.hFile; |
419 | |
420 | lldb::addr_t load_addr = reinterpret_cast<lldb::addr_t>(info.lpBaseOfImage); |
421 | m_debug_delegate->OnDebuggerConnected(image_base: load_addr); |
422 | |
423 | return DBG_CONTINUE; |
424 | } |
425 | |
426 | DWORD |
427 | DebuggerThread::HandleExitThreadEvent(const EXIT_THREAD_DEBUG_INFO &info, |
428 | DWORD thread_id) { |
429 | Log *log = GetLog(mask: WindowsLog::Event | WindowsLog::Thread); |
430 | LLDB_LOG(log, "Thread {0} exited with code {1} in process {2}" , thread_id, |
431 | info.dwExitCode, m_process.GetProcessId()); |
432 | m_debug_delegate->OnExitThread(thread_id: thread_id, exit_code: info.dwExitCode); |
433 | return DBG_CONTINUE; |
434 | } |
435 | |
436 | DWORD |
437 | DebuggerThread::HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info, |
438 | DWORD thread_id) { |
439 | Log *log = GetLog(mask: WindowsLog::Event | WindowsLog::Thread); |
440 | LLDB_LOG(log, "process {0} exited with code {1}" , m_process.GetProcessId(), |
441 | info.dwExitCode); |
442 | |
443 | m_debug_delegate->OnExitProcess(exit_code: info.dwExitCode); |
444 | |
445 | return DBG_CONTINUE; |
446 | } |
447 | |
448 | static std::optional<std::string> GetFileNameFromHandleFallback(HANDLE hFile) { |
449 | // Check that file is not empty as we cannot map a file with zero length. |
450 | DWORD dwFileSizeHi = 0; |
451 | DWORD dwFileSizeLo = ::GetFileSize(hFile, &dwFileSizeHi); |
452 | if (dwFileSizeLo == 0 && dwFileSizeHi == 0) |
453 | return std::nullopt; |
454 | |
455 | AutoHandle filemap( |
456 | ::CreateFileMappingW(hFile, nullptr, PAGE_READONLY, 0, 1, NULL), nullptr); |
457 | if (!filemap.IsValid()) |
458 | return std::nullopt; |
459 | |
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); |
463 | if (!pMem) |
464 | return std::nullopt; |
465 | |
466 | std::array<wchar_t, MAX_PATH + 1> mapped_filename; |
467 | if (!::GetMappedFileNameW(::GetCurrentProcess(), pMem.get(), |
468 | mapped_filename.data(), mapped_filename.size())) |
469 | return std::nullopt; |
470 | |
471 | // A series of null-terminated strings, plus an additional null character |
472 | std::array<wchar_t, 512> drive_strings; |
473 | drive_strings[0] = L'\0'; |
474 | if (!::GetLogicalDriveStringsW(drive_strings.size(), drive_strings.data())) |
475 | return std::nullopt; |
476 | |
477 | std::array<wchar_t, 3> drive = {._M_elems: L"_:" }; |
478 | for (const wchar_t *it = drive_strings.data(); *it != L'\0'; |
479 | it += wcslen(s: it) + 1) { |
480 | // Copy the drive letter to the template string |
481 | drive[0] = it[0]; |
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'\\') { |
490 | // Replace device path with its drive letter |
491 | std::wstring rebuilt_path(drive.data()); |
492 | rebuilt_path.append(&mapped_filename[device_name_len]); |
493 | std::string path_utf8; |
494 | llvm::convertWideToUTF8(Source: rebuilt_path, Result&: path_utf8); |
495 | return path_utf8; |
496 | } |
497 | } |
498 | } |
499 | } |
500 | return std::nullopt; |
501 | } |
502 | |
503 | DWORD |
504 | DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info, |
505 | DWORD thread_id) { |
506 | Log *log = GetLog(mask: WindowsLog::Event); |
507 | if (info.hFile == nullptr) { |
508 | // Not sure what this is, so just ignore it. |
509 | LLDB_LOG(log, "Warning: Inferior {0} has a NULL file handle, returning..." , |
510 | m_process.GetProcessId()); |
511 | return DBG_CONTINUE; |
512 | } |
513 | |
514 | auto on_load_dll = [&](llvm::StringRef path) { |
515 | FileSpec file_spec(path); |
516 | ModuleSpec module_spec(file_spec); |
517 | lldb::addr_t load_addr = reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll); |
518 | |
519 | LLDB_LOG(log, "Inferior {0} - DLL '{1}' loaded at address {2:x}..." , |
520 | m_process.GetProcessId(), path, info.lpBaseOfDll); |
521 | |
522 | m_debug_delegate->OnLoadDll(module_spec, module_addr: load_addr); |
523 | }; |
524 | |
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(Source: buffer.data(), Result&: path_str_utf8); |
534 | llvm::StringRef path_str = path_str_utf8; |
535 | const char *path = path_str.data(); |
536 | if (path_str.starts_with(Prefix: "\\\\?\\" )) |
537 | path += 4; |
538 | |
539 | on_load_dll(path); |
540 | } else if (std::optional<std::string> path = |
541 | GetFileNameFromHandleFallback(info.hFile)) { |
542 | on_load_dll(*path); |
543 | } else { |
544 | LLDB_LOG( |
545 | log, |
546 | "Inferior {0} - Error {1} occurred calling GetFinalPathNameByHandle" , |
547 | m_process.GetProcessId(), ::GetLastError()); |
548 | } |
549 | // Windows does not automatically close info.hFile, so we need to do it. |
550 | ::CloseHandle(info.hFile); |
551 | return DBG_CONTINUE; |
552 | } |
553 | |
554 | DWORD |
555 | DebuggerThread::HandleUnloadDllEvent(const UNLOAD_DLL_DEBUG_INFO &info, |
556 | DWORD thread_id) { |
557 | Log *log = GetLog(mask: WindowsLog::Event); |
558 | LLDB_LOG(log, "process {0} unloading DLL at addr {1:x}." , |
559 | m_process.GetProcessId(), info.lpBaseOfDll); |
560 | |
561 | m_debug_delegate->OnUnloadDll( |
562 | module_addr: reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll)); |
563 | return DBG_CONTINUE; |
564 | } |
565 | |
566 | DWORD |
567 | DebuggerThread::HandleODSEvent(const OUTPUT_DEBUG_STRING_INFO &info, |
568 | DWORD thread_id) { |
569 | return DBG_CONTINUE; |
570 | } |
571 | |
572 | DWORD |
573 | DebuggerThread::HandleRipEvent(const RIP_INFO &info, DWORD thread_id) { |
574 | Log *log = GetLog(mask: WindowsLog::Event); |
575 | LLDB_LOG(log, "encountered error {0} (type={1}) in process {2} thread {3}" , |
576 | info.dwError, info.dwType, m_process.GetProcessId(), thread_id); |
577 | |
578 | Status error(info.dwError, eErrorTypeWin32); |
579 | m_debug_delegate->OnDebuggerError(error, type: info.dwType); |
580 | |
581 | return DBG_CONTINUE; |
582 | } |
583 | |