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(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(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.SetError(::err: GetLastError(), type: 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.SetError(err: GetLastError(), type: 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 | switch (dbe.dwDebugEventCode) { |
243 | default: |
244 | llvm_unreachable("Unhandle debug event code!" ); |
245 | case EXCEPTION_DEBUG_EVENT: { |
246 | ExceptionResult status = |
247 | HandleExceptionEvent(dbe.u.Exception, dbe.dwThreadId); |
248 | |
249 | if (status == ExceptionResult::MaskException) |
250 | continue_status = DBG_CONTINUE; |
251 | else if (status == ExceptionResult::SendToApplication) |
252 | continue_status = DBG_EXCEPTION_NOT_HANDLED; |
253 | |
254 | break; |
255 | } |
256 | case CREATE_THREAD_DEBUG_EVENT: |
257 | continue_status = |
258 | HandleCreateThreadEvent(dbe.u.CreateThread, dbe.dwThreadId); |
259 | break; |
260 | case CREATE_PROCESS_DEBUG_EVENT: |
261 | continue_status = |
262 | HandleCreateProcessEvent(dbe.u.CreateProcessInfo, dbe.dwThreadId); |
263 | break; |
264 | case EXIT_THREAD_DEBUG_EVENT: |
265 | continue_status = |
266 | HandleExitThreadEvent(dbe.u.ExitThread, dbe.dwThreadId); |
267 | break; |
268 | case EXIT_PROCESS_DEBUG_EVENT: |
269 | continue_status = |
270 | HandleExitProcessEvent(dbe.u.ExitProcess, dbe.dwThreadId); |
271 | should_debug = false; |
272 | break; |
273 | case LOAD_DLL_DEBUG_EVENT: |
274 | continue_status = HandleLoadDllEvent(dbe.u.LoadDll, dbe.dwThreadId); |
275 | break; |
276 | case UNLOAD_DLL_DEBUG_EVENT: |
277 | continue_status = HandleUnloadDllEvent(dbe.u.UnloadDll, dbe.dwThreadId); |
278 | break; |
279 | case OUTPUT_DEBUG_STRING_EVENT: |
280 | continue_status = HandleODSEvent(dbe.u.DebugString, dbe.dwThreadId); |
281 | break; |
282 | case RIP_EVENT: |
283 | continue_status = HandleRipEvent(dbe.u.RipInfo, dbe.dwThreadId); |
284 | if (dbe.u.RipInfo.dwType == SLE_ERROR) |
285 | should_debug = false; |
286 | break; |
287 | } |
288 | |
289 | LLDB_LOGV(log, "calling ContinueDebugEvent({0}, {1}, {2}) on thread {3}." , |
290 | dbe.dwProcessId, dbe.dwThreadId, continue_status, |
291 | ::GetCurrentThreadId()); |
292 | |
293 | ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status); |
294 | |
295 | if (m_detached) { |
296 | should_debug = false; |
297 | } |
298 | } else { |
299 | LLDB_LOG(log, "returned FALSE from WaitForDebugEvent. Error = {0}" , |
300 | ::GetLastError()); |
301 | |
302 | should_debug = false; |
303 | } |
304 | } |
305 | FreeProcessHandles(); |
306 | |
307 | LLDB_LOG(log, "WaitForDebugEvent loop completed, exiting." ); |
308 | ::SetEvent(m_debugging_ended_event); |
309 | } |
310 | |
311 | ExceptionResult |
312 | DebuggerThread::HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, |
313 | DWORD thread_id) { |
314 | Log *log = GetLog(mask: WindowsLog::Event | WindowsLog::Exception); |
315 | if (m_is_shutting_down) { |
316 | // A breakpoint that occurs while `m_pid_to_detach` is non-zero is a magic |
317 | // exception that |
318 | // we use simply to wake up the DebuggerThread so that we can close out the |
319 | // debug loop. |
320 | if (m_pid_to_detach != 0 && |
321 | (info.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT || |
322 | info.ExceptionRecord.ExceptionCode == STATUS_WX86_BREAKPOINT)) { |
323 | LLDB_LOG(log, "Breakpoint exception is cue to detach from process {0:x}" , |
324 | m_pid_to_detach.load()); |
325 | ::DebugActiveProcessStop(m_pid_to_detach); |
326 | m_detached = true; |
327 | } |
328 | |
329 | // Don't perform any blocking operations while we're shutting down. That |
330 | // will cause TerminateProcess -> WaitForSingleObject to time out. |
331 | return ExceptionResult::SendToApplication; |
332 | } |
333 | |
334 | bool first_chance = (info.dwFirstChance != 0); |
335 | |
336 | m_active_exception.reset( |
337 | p: new ExceptionRecord(info.ExceptionRecord, thread_id)); |
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); |
341 | |
342 | ExceptionResult result = |
343 | m_debug_delegate->OnDebugException(first_chance, record: *m_active_exception); |
344 | m_exception_pred.SetValue(value: result, broadcast_type: eBroadcastNever); |
345 | |
346 | LLDB_LOG(log, "waiting for ExceptionPred != BreakInDebugger" ); |
347 | result = *m_exception_pred.WaitForValueNotEqualTo( |
348 | value: ExceptionResult::BreakInDebugger); |
349 | |
350 | LLDB_LOG(log, "got ExceptionPred = {0}" , (int)m_exception_pred.GetValue()); |
351 | return result; |
352 | } |
353 | |
354 | DWORD |
355 | DebuggerThread::HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info, |
356 | DWORD thread_id) { |
357 | Log *log = GetLog(mask: WindowsLog::Event | WindowsLog::Thread); |
358 | LLDB_LOG(log, "Thread {0} spawned in process {1}" , thread_id, |
359 | m_process.GetProcessId()); |
360 | HostThread thread(info.hThread); |
361 | thread.GetNativeThread().SetOwnsHandle(false); |
362 | m_debug_delegate->OnCreateThread(thread); |
363 | return DBG_CONTINUE; |
364 | } |
365 | |
366 | DWORD |
367 | DebuggerThread::HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info, |
368 | DWORD thread_id) { |
369 | Log *log = GetLog(mask: WindowsLog::Event | WindowsLog::Process); |
370 | uint32_t process_id = ::GetProcessId(info.hProcess); |
371 | |
372 | LLDB_LOG(log, "process {0} spawned" , process_id); |
373 | |
374 | std::string thread_name; |
375 | llvm::raw_string_ostream name_stream(thread_name); |
376 | name_stream << "lldb.plugin.process-windows.secondary[" << process_id << "]" ; |
377 | name_stream.flush(); |
378 | llvm::set_thread_name(thread_name); |
379 | |
380 | // info.hProcess and info.hThread are closed automatically by Windows when |
381 | // EXIT_PROCESS_DEBUG_EVENT is received. |
382 | m_process = HostProcess(info.hProcess); |
383 | ((HostProcessWindows &)m_process.GetNativeProcess()).SetOwnsHandle(false); |
384 | m_main_thread = HostThread(info.hThread); |
385 | m_main_thread.GetNativeThread().SetOwnsHandle(false); |
386 | m_image_file = info.hFile; |
387 | |
388 | lldb::addr_t load_addr = reinterpret_cast<lldb::addr_t>(info.lpBaseOfImage); |
389 | m_debug_delegate->OnDebuggerConnected(image_base: load_addr); |
390 | |
391 | return DBG_CONTINUE; |
392 | } |
393 | |
394 | DWORD |
395 | DebuggerThread::HandleExitThreadEvent(const EXIT_THREAD_DEBUG_INFO &info, |
396 | DWORD thread_id) { |
397 | Log *log = GetLog(mask: WindowsLog::Event | WindowsLog::Thread); |
398 | LLDB_LOG(log, "Thread {0} exited with code {1} in process {2}" , thread_id, |
399 | info.dwExitCode, m_process.GetProcessId()); |
400 | m_debug_delegate->OnExitThread(thread_id: thread_id, exit_code: info.dwExitCode); |
401 | return DBG_CONTINUE; |
402 | } |
403 | |
404 | DWORD |
405 | DebuggerThread::HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info, |
406 | DWORD thread_id) { |
407 | Log *log = GetLog(mask: WindowsLog::Event | WindowsLog::Thread); |
408 | LLDB_LOG(log, "process {0} exited with code {1}" , m_process.GetProcessId(), |
409 | info.dwExitCode); |
410 | |
411 | m_debug_delegate->OnExitProcess(exit_code: info.dwExitCode); |
412 | |
413 | return DBG_CONTINUE; |
414 | } |
415 | |
416 | static std::optional<std::string> GetFileNameFromHandleFallback(HANDLE hFile) { |
417 | // Check that file is not empty as we cannot map a file with zero length. |
418 | DWORD dwFileSizeHi = 0; |
419 | DWORD dwFileSizeLo = ::GetFileSize(hFile, &dwFileSizeHi); |
420 | if (dwFileSizeLo == 0 && dwFileSizeHi == 0) |
421 | return std::nullopt; |
422 | |
423 | AutoHandle filemap( |
424 | ::CreateFileMappingW(hFile, nullptr, PAGE_READONLY, 0, 1, NULL), nullptr); |
425 | if (!filemap.IsValid()) |
426 | return std::nullopt; |
427 | |
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); |
431 | if (!pMem) |
432 | return std::nullopt; |
433 | |
434 | std::array<wchar_t, MAX_PATH + 1> mapped_filename; |
435 | if (!::GetMappedFileNameW(::GetCurrentProcess(), pMem.get(), |
436 | mapped_filename.data(), mapped_filename.size())) |
437 | return std::nullopt; |
438 | |
439 | // A series of null-terminated strings, plus an additional null character |
440 | std::array<wchar_t, 512> drive_strings; |
441 | drive_strings[0] = L'\0'; |
442 | if (!::GetLogicalDriveStringsW(drive_strings.size(), drive_strings.data())) |
443 | return std::nullopt; |
444 | |
445 | std::array<wchar_t, 3> drive = {._M_elems: L"_:" }; |
446 | for (const wchar_t *it = drive_strings.data(); *it != L'\0'; |
447 | it += wcslen(s: it) + 1) { |
448 | // Copy the drive letter to the template string |
449 | drive[0] = it[0]; |
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'\\') { |
458 | // Replace device path with its drive letter |
459 | std::wstring rebuilt_path(drive.data()); |
460 | rebuilt_path.append(&mapped_filename[device_name_len]); |
461 | std::string path_utf8; |
462 | llvm::convertWideToUTF8(Source: rebuilt_path, Result&: path_utf8); |
463 | return path_utf8; |
464 | } |
465 | } |
466 | } |
467 | } |
468 | return std::nullopt; |
469 | } |
470 | |
471 | DWORD |
472 | DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info, |
473 | DWORD thread_id) { |
474 | Log *log = GetLog(mask: WindowsLog::Event); |
475 | if (info.hFile == nullptr) { |
476 | // Not sure what this is, so just ignore it. |
477 | LLDB_LOG(log, "Warning: Inferior {0} has a NULL file handle, returning..." , |
478 | m_process.GetProcessId()); |
479 | return DBG_CONTINUE; |
480 | } |
481 | |
482 | auto on_load_dll = [&](llvm::StringRef path) { |
483 | FileSpec file_spec(path); |
484 | ModuleSpec module_spec(file_spec); |
485 | lldb::addr_t load_addr = reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll); |
486 | |
487 | LLDB_LOG(log, "Inferior {0} - DLL '{1}' loaded at address {2:x}..." , |
488 | m_process.GetProcessId(), path, info.lpBaseOfDll); |
489 | |
490 | m_debug_delegate->OnLoadDll(module_spec, module_addr: load_addr); |
491 | }; |
492 | |
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(Source: buffer.data(), Result&: path_str_utf8); |
502 | llvm::StringRef path_str = path_str_utf8; |
503 | const char *path = path_str.data(); |
504 | if (path_str.starts_with(Prefix: "\\\\?\\" )) |
505 | path += 4; |
506 | |
507 | on_load_dll(path); |
508 | } else if (std::optional<std::string> path = |
509 | GetFileNameFromHandleFallback(info.hFile)) { |
510 | on_load_dll(*path); |
511 | } else { |
512 | LLDB_LOG( |
513 | log, |
514 | "Inferior {0} - Error {1} occurred calling GetFinalPathNameByHandle" , |
515 | m_process.GetProcessId(), ::GetLastError()); |
516 | } |
517 | // Windows does not automatically close info.hFile, so we need to do it. |
518 | ::CloseHandle(info.hFile); |
519 | return DBG_CONTINUE; |
520 | } |
521 | |
522 | DWORD |
523 | DebuggerThread::HandleUnloadDllEvent(const UNLOAD_DLL_DEBUG_INFO &info, |
524 | DWORD thread_id) { |
525 | Log *log = GetLog(mask: WindowsLog::Event); |
526 | LLDB_LOG(log, "process {0} unloading DLL at addr {1:x}." , |
527 | m_process.GetProcessId(), info.lpBaseOfDll); |
528 | |
529 | m_debug_delegate->OnUnloadDll( |
530 | module_addr: reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll)); |
531 | return DBG_CONTINUE; |
532 | } |
533 | |
534 | DWORD |
535 | DebuggerThread::HandleODSEvent(const OUTPUT_DEBUG_STRING_INFO &info, |
536 | DWORD thread_id) { |
537 | return DBG_CONTINUE; |
538 | } |
539 | |
540 | DWORD |
541 | DebuggerThread::HandleRipEvent(const RIP_INFO &info, DWORD thread_id) { |
542 | Log *log = GetLog(mask: WindowsLog::Event); |
543 | LLDB_LOG(log, "encountered error {0} (type={1}) in process {2} thread {3}" , |
544 | info.dwError, info.dwType, m_process.GetProcessId(), thread_id); |
545 | |
546 | Status error(info.dwError, eErrorTypeWin32); |
547 | m_debug_delegate->OnDebuggerError(error, type: info.dwType); |
548 | |
549 | return DBG_CONTINUE; |
550 | } |
551 | |