1 | //===-- NativeProcessWindows.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 "lldb/Host/windows/windows.h" |
10 | #include <psapi.h> |
11 | |
12 | #include "NativeProcessWindows.h" |
13 | #include "NativeThreadWindows.h" |
14 | #include "lldb/Host/FileSystem.h" |
15 | #include "lldb/Host/HostNativeProcessBase.h" |
16 | #include "lldb/Host/HostProcess.h" |
17 | #include "lldb/Host/ProcessLaunchInfo.h" |
18 | #include "lldb/Host/windows/AutoHandle.h" |
19 | #include "lldb/Host/windows/HostThreadWindows.h" |
20 | #include "lldb/Host/windows/ProcessLauncherWindows.h" |
21 | #include "lldb/Target/MemoryRegionInfo.h" |
22 | #include "lldb/Target/Process.h" |
23 | #include "lldb/Utility/State.h" |
24 | #include "llvm/Support/ConvertUTF.h" |
25 | #include "llvm/Support/Errc.h" |
26 | #include "llvm/Support/Error.h" |
27 | #include "llvm/Support/Format.h" |
28 | #include "llvm/Support/Threading.h" |
29 | #include "llvm/Support/raw_ostream.h" |
30 | |
31 | #include "DebuggerThread.h" |
32 | #include "ExceptionRecord.h" |
33 | #include "ProcessWindowsLog.h" |
34 | |
35 | #include <tlhelp32.h> |
36 | |
37 | #pragma warning(disable : 4005) |
38 | #include "winternl.h" |
39 | #include <ntstatus.h> |
40 | |
41 | using namespace lldb; |
42 | using namespace lldb_private; |
43 | using namespace llvm; |
44 | |
45 | namespace lldb_private { |
46 | |
47 | NativeProcessWindows::NativeProcessWindows(ProcessLaunchInfo &launch_info, |
48 | NativeDelegate &delegate, |
49 | llvm::Error &E) |
50 | : NativeProcessProtocol(LLDB_INVALID_PROCESS_ID, |
51 | launch_info.GetPTY().ReleasePrimaryFileDescriptor(), |
52 | delegate), |
53 | ProcessDebugger(), m_arch(launch_info.GetArchitecture()) { |
54 | ErrorAsOutParameter EOut(&E); |
55 | DebugDelegateSP delegate_sp(new NativeDebugDelegate(*this)); |
56 | E = LaunchProcess(launch_info, delegate: delegate_sp).ToError(); |
57 | if (E) |
58 | return; |
59 | |
60 | SetID(GetDebuggedProcessId()); |
61 | } |
62 | |
63 | NativeProcessWindows::NativeProcessWindows(lldb::pid_t pid, int terminal_fd, |
64 | NativeDelegate &delegate, |
65 | llvm::Error &E) |
66 | : NativeProcessProtocol(pid, terminal_fd, delegate), ProcessDebugger() { |
67 | ErrorAsOutParameter EOut(&E); |
68 | DebugDelegateSP delegate_sp(new NativeDebugDelegate(*this)); |
69 | ProcessAttachInfo attach_info; |
70 | attach_info.SetProcessID(pid); |
71 | E = AttachProcess(pid, attach_info, delegate: delegate_sp).ToError(); |
72 | if (E) |
73 | return; |
74 | |
75 | SetID(GetDebuggedProcessId()); |
76 | |
77 | ProcessInstanceInfo info; |
78 | if (!Host::GetProcessInfo(pid, proc_info&: info)) { |
79 | E = createStringError(EC: inconvertibleErrorCode(), |
80 | Msg: "Cannot get process information" ); |
81 | return; |
82 | } |
83 | m_arch = info.GetArchitecture(); |
84 | } |
85 | |
86 | Status NativeProcessWindows::Resume(const ResumeActionList &resume_actions) { |
87 | Log *log = GetLog(mask: WindowsLog::Process); |
88 | Status error; |
89 | llvm::sys::ScopedLock lock(m_mutex); |
90 | |
91 | StateType state = GetState(); |
92 | if (state == eStateStopped || state == eStateCrashed) { |
93 | LLDB_LOG(log, "process {0} is in state {1}. Resuming..." , |
94 | GetDebuggedProcessId(), state); |
95 | LLDB_LOG(log, "resuming {0} threads." , m_threads.size()); |
96 | |
97 | bool failed = false; |
98 | for (uint32_t i = 0; i < m_threads.size(); ++i) { |
99 | auto thread = static_cast<NativeThreadWindows *>(m_threads[i].get()); |
100 | const ResumeAction *const action = |
101 | resume_actions.GetActionForThread(tid: thread->GetID(), default_ok: true); |
102 | if (action == nullptr) |
103 | continue; |
104 | |
105 | switch (action->state) { |
106 | case eStateRunning: |
107 | case eStateStepping: { |
108 | Status result = thread->DoResume(resume_state: action->state); |
109 | if (result.Fail()) { |
110 | failed = true; |
111 | LLDB_LOG(log, |
112 | "Trying to resume thread at index {0}, but failed with " |
113 | "error {1}." , |
114 | i, result); |
115 | } |
116 | break; |
117 | } |
118 | case eStateSuspended: |
119 | case eStateStopped: |
120 | break; |
121 | |
122 | default: |
123 | return Status( |
124 | "NativeProcessWindows::%s (): unexpected state %s specified " |
125 | "for pid %" PRIu64 ", tid %" PRIu64, |
126 | __FUNCTION__, StateAsCString(state: action->state), GetID(), |
127 | thread->GetID()); |
128 | } |
129 | } |
130 | |
131 | if (failed) { |
132 | error.SetErrorString("NativeProcessWindows::DoResume failed" ); |
133 | } else { |
134 | SetState(state: eStateRunning); |
135 | } |
136 | |
137 | // Resume the debug loop. |
138 | ExceptionRecordSP active_exception = |
139 | m_session_data->m_debugger->GetActiveException().lock(); |
140 | if (active_exception) { |
141 | // Resume the process and continue processing debug events. Mask the |
142 | // exception so that from the process's view, there is no indication that |
143 | // anything happened. |
144 | m_session_data->m_debugger->ContinueAsyncException( |
145 | result: ExceptionResult::MaskException); |
146 | } |
147 | } else { |
148 | LLDB_LOG(log, "error: process {0} is in state {1}. Returning..." , |
149 | GetDebuggedProcessId(), GetState()); |
150 | } |
151 | |
152 | return error; |
153 | } |
154 | |
155 | NativeThreadWindows * |
156 | NativeProcessWindows::GetThreadByID(lldb::tid_t thread_id) { |
157 | return static_cast<NativeThreadWindows *>( |
158 | NativeProcessProtocol::GetThreadByID(tid: thread_id)); |
159 | } |
160 | |
161 | Status NativeProcessWindows::Halt() { |
162 | bool caused_stop = false; |
163 | StateType state = GetState(); |
164 | if (state != eStateStopped) |
165 | return HaltProcess(caused_stop); |
166 | return Status(); |
167 | } |
168 | |
169 | Status NativeProcessWindows::Detach() { |
170 | Status error; |
171 | Log *log = GetLog(mask: WindowsLog::Process); |
172 | StateType state = GetState(); |
173 | if (state != eStateExited && state != eStateDetached) { |
174 | error = DetachProcess(); |
175 | if (error.Success()) |
176 | SetState(state: eStateDetached); |
177 | else |
178 | LLDB_LOG(log, "Detaching process error: {0}" , error); |
179 | } else { |
180 | error.SetErrorStringWithFormatv(format: "error: process {0} in state = {1}, but " |
181 | "cannot detach it in this state." , |
182 | args: GetID(), args&: state); |
183 | LLDB_LOG(log, "error: {0}" , error); |
184 | } |
185 | return error; |
186 | } |
187 | |
188 | Status NativeProcessWindows::Signal(int signo) { |
189 | Status error; |
190 | error.SetErrorString("Windows does not support sending signals to processes" ); |
191 | return error; |
192 | } |
193 | |
194 | Status NativeProcessWindows::Interrupt() { return Halt(); } |
195 | |
196 | Status NativeProcessWindows::Kill() { |
197 | StateType state = GetState(); |
198 | return DestroyProcess(process_state: state); |
199 | } |
200 | |
201 | Status NativeProcessWindows::IgnoreSignals(llvm::ArrayRef<int> signals) { |
202 | return Status(); |
203 | } |
204 | |
205 | Status NativeProcessWindows::GetMemoryRegionInfo(lldb::addr_t load_addr, |
206 | MemoryRegionInfo &range_info) { |
207 | return ProcessDebugger::GetMemoryRegionInfo(load_addr, range_info); |
208 | } |
209 | |
210 | Status NativeProcessWindows::ReadMemory(lldb::addr_t addr, void *buf, |
211 | size_t size, size_t &bytes_read) { |
212 | return ProcessDebugger::ReadMemory(addr, buf, size, bytes_read); |
213 | } |
214 | |
215 | Status NativeProcessWindows::WriteMemory(lldb::addr_t addr, const void *buf, |
216 | size_t size, size_t &bytes_written) { |
217 | return ProcessDebugger::WriteMemory(addr, buf, size, bytes_written); |
218 | } |
219 | |
220 | llvm::Expected<lldb::addr_t> |
221 | NativeProcessWindows::AllocateMemory(size_t size, uint32_t permissions) { |
222 | lldb::addr_t addr; |
223 | Status ST = ProcessDebugger::AllocateMemory(size, permissions, addr); |
224 | if (ST.Success()) |
225 | return addr; |
226 | return ST.ToError(); |
227 | } |
228 | |
229 | llvm::Error NativeProcessWindows::DeallocateMemory(lldb::addr_t addr) { |
230 | return ProcessDebugger::DeallocateMemory(addr).ToError(); |
231 | } |
232 | |
233 | lldb::addr_t NativeProcessWindows::GetSharedLibraryInfoAddress() { return 0; } |
234 | |
235 | bool NativeProcessWindows::IsAlive() const { |
236 | StateType state = GetState(); |
237 | switch (state) { |
238 | case eStateCrashed: |
239 | case eStateDetached: |
240 | case eStateExited: |
241 | case eStateInvalid: |
242 | case eStateUnloaded: |
243 | return false; |
244 | default: |
245 | return true; |
246 | } |
247 | } |
248 | |
249 | void NativeProcessWindows::SetStopReasonForThread(NativeThreadWindows &thread, |
250 | lldb::StopReason reason, |
251 | std::string description) { |
252 | SetCurrentThreadID(thread.GetID()); |
253 | |
254 | ThreadStopInfo stop_info; |
255 | stop_info.reason = reason; |
256 | // No signal support on Windows but required to provide a 'valid' signum. |
257 | stop_info.signo = SIGTRAP; |
258 | |
259 | if (reason == StopReason::eStopReasonException) { |
260 | stop_info.details.exception.type = 0; |
261 | stop_info.details.exception.data_count = 0; |
262 | } |
263 | |
264 | thread.SetStopReason(stop_info, description); |
265 | } |
266 | |
267 | void NativeProcessWindows::StopThread(lldb::tid_t thread_id, |
268 | lldb::StopReason reason, |
269 | std::string description) { |
270 | NativeThreadWindows *thread = GetThreadByID(thread_id); |
271 | if (!thread) |
272 | return; |
273 | |
274 | for (uint32_t i = 0; i < m_threads.size(); ++i) { |
275 | auto t = static_cast<NativeThreadWindows *>(m_threads[i].get()); |
276 | Status error = t->DoStop(); |
277 | if (error.Fail()) |
278 | exit(status: 1); |
279 | } |
280 | SetStopReasonForThread(thread&: *thread, reason, description); |
281 | } |
282 | |
283 | size_t NativeProcessWindows::UpdateThreads() { return m_threads.size(); } |
284 | |
285 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> |
286 | NativeProcessWindows::GetAuxvData() const { |
287 | // Not available on this target. |
288 | return llvm::errc::not_supported; |
289 | } |
290 | |
291 | llvm::Expected<llvm::ArrayRef<uint8_t>> |
292 | NativeProcessWindows::GetSoftwareBreakpointTrapOpcode(size_t size_hint) { |
293 | static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x3e, 0xd4}; // brk #0xf000 |
294 | static const uint8_t g_thumb_opcode[] = {0xfe, 0xde}; // udf #0xfe |
295 | |
296 | switch (GetArchitecture().GetMachine()) { |
297 | case llvm::Triple::aarch64: |
298 | return llvm::ArrayRef(g_aarch64_opcode); |
299 | |
300 | case llvm::Triple::arm: |
301 | case llvm::Triple::thumb: |
302 | return llvm::ArrayRef(g_thumb_opcode); |
303 | |
304 | default: |
305 | return NativeProcessProtocol::GetSoftwareBreakpointTrapOpcode(size_hint); |
306 | } |
307 | } |
308 | |
309 | size_t NativeProcessWindows::GetSoftwareBreakpointPCOffset() { |
310 | // Windows always reports an incremented PC after a breakpoint is hit, |
311 | // even on ARM. |
312 | return cantFail(ValOrErr: GetSoftwareBreakpointTrapOpcode(size_hint: 0)).size(); |
313 | } |
314 | |
315 | bool NativeProcessWindows::FindSoftwareBreakpoint(lldb::addr_t addr) { |
316 | auto it = m_software_breakpoints.find(x: addr); |
317 | if (it == m_software_breakpoints.end()) |
318 | return false; |
319 | return true; |
320 | } |
321 | |
322 | Status NativeProcessWindows::SetBreakpoint(lldb::addr_t addr, uint32_t size, |
323 | bool hardware) { |
324 | if (hardware) |
325 | return SetHardwareBreakpoint(addr, size); |
326 | return SetSoftwareBreakpoint(addr, size_hint: size); |
327 | } |
328 | |
329 | Status NativeProcessWindows::RemoveBreakpoint(lldb::addr_t addr, |
330 | bool hardware) { |
331 | if (hardware) |
332 | return RemoveHardwareBreakpoint(addr); |
333 | return RemoveSoftwareBreakpoint(addr); |
334 | } |
335 | |
336 | Status NativeProcessWindows::CacheLoadedModules() { |
337 | Status error; |
338 | if (!m_loaded_modules.empty()) |
339 | return Status(); |
340 | |
341 | // Retrieve loaded modules by a Target/Module free implemenation. |
342 | AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetID())); |
343 | if (snapshot.IsValid()) { |
344 | MODULEENTRY32W me; |
345 | me.dwSize = sizeof(MODULEENTRY32W); |
346 | if (Module32FirstW(snapshot.get(), &me)) { |
347 | do { |
348 | std::string path; |
349 | if (!llvm::convertWideToUTF8(Source: me.szExePath, Result&: path)) |
350 | continue; |
351 | |
352 | FileSpec file_spec(path); |
353 | FileSystem::Instance().Resolve(file_spec); |
354 | m_loaded_modules[file_spec] = (addr_t)me.modBaseAddr; |
355 | } while (Module32Next(snapshot.get(), &me)); |
356 | } |
357 | |
358 | if (!m_loaded_modules.empty()) |
359 | return Status(); |
360 | } |
361 | |
362 | error.SetError(::err: GetLastError(), type: lldb::ErrorType::eErrorTypeWin32); |
363 | return error; |
364 | } |
365 | |
366 | Status NativeProcessWindows::GetLoadedModuleFileSpec(const char *module_path, |
367 | FileSpec &file_spec) { |
368 | Status error = CacheLoadedModules(); |
369 | if (error.Fail()) |
370 | return error; |
371 | |
372 | FileSpec module_file_spec(module_path); |
373 | FileSystem::Instance().Resolve(file_spec&: module_file_spec); |
374 | for (auto &it : m_loaded_modules) { |
375 | if (it.first == module_file_spec) { |
376 | file_spec = it.first; |
377 | return Status(); |
378 | } |
379 | } |
380 | return Status("Module (%s) not found in process %" PRIu64 "!" , |
381 | module_file_spec.GetPath().c_str(), GetID()); |
382 | } |
383 | |
384 | Status |
385 | NativeProcessWindows::GetFileLoadAddress(const llvm::StringRef &file_name, |
386 | lldb::addr_t &load_addr) { |
387 | Status error = CacheLoadedModules(); |
388 | if (error.Fail()) |
389 | return error; |
390 | |
391 | load_addr = LLDB_INVALID_ADDRESS; |
392 | FileSpec file_spec(file_name); |
393 | FileSystem::Instance().Resolve(file_spec); |
394 | for (auto &it : m_loaded_modules) { |
395 | if (it.first == file_spec) { |
396 | load_addr = it.second; |
397 | return Status(); |
398 | } |
399 | } |
400 | return Status("Can't get loaded address of file (%s) in process %" PRIu64 "!" , |
401 | file_spec.GetPath().c_str(), GetID()); |
402 | } |
403 | |
404 | void NativeProcessWindows::OnExitProcess(uint32_t exit_code) { |
405 | Log *log = GetLog(mask: WindowsLog::Process); |
406 | LLDB_LOG(log, "Process {0} exited with code {1}" , GetID(), exit_code); |
407 | |
408 | ProcessDebugger::OnExitProcess(exit_code); |
409 | |
410 | // No signal involved. It is just an exit event. |
411 | WaitStatus wait_status(WaitStatus::Exit, exit_code); |
412 | SetExitStatus(status: wait_status, bNotifyStateChange: true); |
413 | |
414 | // Notify the native delegate. |
415 | SetState(state: eStateExited, notify_delegates: true); |
416 | } |
417 | |
418 | void NativeProcessWindows::OnDebuggerConnected(lldb::addr_t image_base) { |
419 | Log *log = GetLog(mask: WindowsLog::Process); |
420 | LLDB_LOG(log, "Debugger connected to process {0}. Image base = {1:x}" , |
421 | GetDebuggedProcessId(), image_base); |
422 | |
423 | // This is the earliest chance we can resolve the process ID and |
424 | // architecture if we don't know them yet. |
425 | if (GetID() == LLDB_INVALID_PROCESS_ID) |
426 | SetID(GetDebuggedProcessId()); |
427 | |
428 | if (GetArchitecture().GetMachine() == llvm::Triple::UnknownArch) { |
429 | ProcessInstanceInfo process_info; |
430 | if (!Host::GetProcessInfo(pid: GetDebuggedProcessId(), proc_info&: process_info)) { |
431 | LLDB_LOG(log, "Cannot get process information during debugger connecting " |
432 | "to process" ); |
433 | return; |
434 | } |
435 | SetArchitecture(process_info.GetArchitecture()); |
436 | } |
437 | |
438 | // The very first one shall always be the main thread. |
439 | assert(m_threads.empty()); |
440 | m_threads.push_back(x: std::make_unique<NativeThreadWindows>( |
441 | args&: *this, args: m_session_data->m_debugger->GetMainThread())); |
442 | } |
443 | |
444 | ExceptionResult |
445 | NativeProcessWindows::OnDebugException(bool first_chance, |
446 | const ExceptionRecord &record) { |
447 | Log *log = GetLog(mask: WindowsLog::Exception); |
448 | llvm::sys::ScopedLock lock(m_mutex); |
449 | |
450 | // Let the debugger establish the internal status. |
451 | ProcessDebugger::OnDebugException(first_chance, record); |
452 | |
453 | static bool initial_stop = false; |
454 | if (!first_chance) { |
455 | SetState(state: eStateStopped, notify_delegates: false); |
456 | } |
457 | |
458 | ExceptionResult result = ExceptionResult::SendToApplication; |
459 | switch (record.GetExceptionCode()) { |
460 | case DWORD(STATUS_SINGLE_STEP): |
461 | case STATUS_WX86_SINGLE_STEP: { |
462 | uint32_t wp_id = LLDB_INVALID_INDEX32; |
463 | if (NativeThreadWindows *thread = GetThreadByID(thread_id: record.GetThreadID())) { |
464 | NativeRegisterContextWindows ®_ctx = thread->GetRegisterContext(); |
465 | Status error = |
466 | reg_ctx.GetWatchpointHitIndex(wp_index&: wp_id, trap_addr: record.GetExceptionAddress()); |
467 | if (error.Fail()) |
468 | LLDB_LOG(log, |
469 | "received error while checking for watchpoint hits, pid = " |
470 | "{0}, error = {1}" , |
471 | thread->GetID(), error); |
472 | if (wp_id != LLDB_INVALID_INDEX32) { |
473 | addr_t wp_addr = reg_ctx.GetWatchpointAddress(wp_index: wp_id); |
474 | addr_t wp_hit_addr = reg_ctx.GetWatchpointHitAddress(wp_index: wp_id); |
475 | std::string desc = |
476 | formatv(Fmt: "{0} {1} {2}" , Vals&: wp_addr, Vals&: wp_id, Vals&: wp_hit_addr).str(); |
477 | StopThread(thread_id: record.GetThreadID(), reason: StopReason::eStopReasonWatchpoint, |
478 | description: desc); |
479 | } |
480 | } |
481 | if (wp_id == LLDB_INVALID_INDEX32) |
482 | StopThread(thread_id: record.GetThreadID(), reason: StopReason::eStopReasonTrace); |
483 | |
484 | SetState(state: eStateStopped, notify_delegates: true); |
485 | |
486 | // Continue the debugger. |
487 | return ExceptionResult::MaskException; |
488 | } |
489 | case DWORD(STATUS_BREAKPOINT): |
490 | case STATUS_WX86_BREAKPOINT: |
491 | if (FindSoftwareBreakpoint(addr: record.GetExceptionAddress())) { |
492 | LLDB_LOG(log, "Hit non-loader breakpoint at address {0:x}." , |
493 | record.GetExceptionAddress()); |
494 | |
495 | StopThread(thread_id: record.GetThreadID(), reason: StopReason::eStopReasonBreakpoint); |
496 | |
497 | if (NativeThreadWindows *stop_thread = |
498 | GetThreadByID(thread_id: record.GetThreadID())) { |
499 | auto ®ister_context = stop_thread->GetRegisterContext(); |
500 | uint32_t breakpoint_size = GetSoftwareBreakpointPCOffset(); |
501 | // The current PC is AFTER the BP opcode, on all architectures. |
502 | uint64_t pc = register_context.GetPC() - breakpoint_size; |
503 | register_context.SetPC(pc); |
504 | } |
505 | |
506 | SetState(state: eStateStopped, notify_delegates: true); |
507 | return ExceptionResult::MaskException; |
508 | } |
509 | |
510 | if (!initial_stop) { |
511 | initial_stop = true; |
512 | LLDB_LOG(log, |
513 | "Hit loader breakpoint at address {0:x}, setting initial stop " |
514 | "event." , |
515 | record.GetExceptionAddress()); |
516 | |
517 | // We are required to report the reason for the first stop after |
518 | // launching or being attached. |
519 | if (NativeThreadWindows *thread = GetThreadByID(thread_id: record.GetThreadID())) |
520 | SetStopReasonForThread(thread&: *thread, reason: StopReason::eStopReasonBreakpoint); |
521 | |
522 | // Do not notify the native delegate (e.g. llgs) since at this moment |
523 | // the program hasn't returned from Manager::Launch() and the delegate |
524 | // might not have an valid native process to operate on. |
525 | SetState(state: eStateStopped, notify_delegates: false); |
526 | |
527 | // Hit the initial stop. Continue the application. |
528 | return ExceptionResult::BreakInDebugger; |
529 | } |
530 | |
531 | [[fallthrough]]; |
532 | default: |
533 | LLDB_LOG(log, |
534 | "Debugger thread reported exception {0:x} at address {1:x} " |
535 | "(first_chance={2})" , |
536 | record.GetExceptionCode(), record.GetExceptionAddress(), |
537 | first_chance); |
538 | |
539 | { |
540 | std::string desc; |
541 | llvm::raw_string_ostream desc_stream(desc); |
542 | desc_stream << "Exception " |
543 | << llvm::format_hex(N: record.GetExceptionCode(), Width: 8) |
544 | << " encountered at address " |
545 | << llvm::format_hex(N: record.GetExceptionAddress(), Width: 8); |
546 | StopThread(thread_id: record.GetThreadID(), reason: StopReason::eStopReasonException, |
547 | description: desc_stream.str().c_str()); |
548 | |
549 | SetState(state: eStateStopped, notify_delegates: true); |
550 | } |
551 | |
552 | // For non-breakpoints, give the application a chance to handle the |
553 | // exception first. |
554 | if (first_chance) |
555 | result = ExceptionResult::SendToApplication; |
556 | else |
557 | result = ExceptionResult::BreakInDebugger; |
558 | } |
559 | |
560 | return result; |
561 | } |
562 | |
563 | void NativeProcessWindows::OnCreateThread(const HostThread &new_thread) { |
564 | llvm::sys::ScopedLock lock(m_mutex); |
565 | |
566 | auto thread = std::make_unique<NativeThreadWindows>(args&: *this, args: new_thread); |
567 | thread->GetRegisterContext().ClearAllHardwareWatchpoints(); |
568 | for (const auto &pair : GetWatchpointMap()) { |
569 | const NativeWatchpoint &wp = pair.second; |
570 | thread->SetWatchpoint(addr: wp.m_addr, size: wp.m_size, watch_flags: wp.m_watch_flags, |
571 | hardware: wp.m_hardware); |
572 | } |
573 | |
574 | m_threads.push_back(x: std::move(thread)); |
575 | } |
576 | |
577 | void NativeProcessWindows::OnExitThread(lldb::tid_t thread_id, |
578 | uint32_t exit_code) { |
579 | llvm::sys::ScopedLock lock(m_mutex); |
580 | NativeThreadWindows *thread = GetThreadByID(thread_id); |
581 | if (!thread) |
582 | return; |
583 | |
584 | for (auto t = m_threads.begin(); t != m_threads.end();) { |
585 | if ((*t)->GetID() == thread_id) { |
586 | t = m_threads.erase(position: t); |
587 | } else { |
588 | ++t; |
589 | } |
590 | } |
591 | } |
592 | |
593 | void NativeProcessWindows::OnLoadDll(const ModuleSpec &module_spec, |
594 | lldb::addr_t module_addr) { |
595 | // Simply invalidate the cached loaded modules. |
596 | if (!m_loaded_modules.empty()) |
597 | m_loaded_modules.clear(); |
598 | } |
599 | |
600 | void NativeProcessWindows::OnUnloadDll(lldb::addr_t module_addr) { |
601 | if (!m_loaded_modules.empty()) |
602 | m_loaded_modules.clear(); |
603 | } |
604 | |
605 | llvm::Expected<std::unique_ptr<NativeProcessProtocol>> |
606 | NativeProcessWindows::Manager::Launch( |
607 | ProcessLaunchInfo &launch_info, |
608 | NativeProcessProtocol::NativeDelegate &native_delegate) { |
609 | Error E = Error::success(); |
610 | auto process_up = std::unique_ptr<NativeProcessWindows>( |
611 | new NativeProcessWindows(launch_info, native_delegate, E)); |
612 | if (E) |
613 | return std::move(E); |
614 | return std::move(process_up); |
615 | } |
616 | |
617 | llvm::Expected<std::unique_ptr<NativeProcessProtocol>> |
618 | NativeProcessWindows::Manager::Attach( |
619 | lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate) { |
620 | Error E = Error::success(); |
621 | // Set pty primary fd invalid since it is not available. |
622 | auto process_up = std::unique_ptr<NativeProcessWindows>( |
623 | new NativeProcessWindows(pid, -1, native_delegate, E)); |
624 | if (E) |
625 | return std::move(E); |
626 | return std::move(process_up); |
627 | } |
628 | } // namespace lldb_private |
629 | |