ErrorCode Platform::TranslateError(DWORD error) { switch (error) { case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: return ds2::kErrorNotFound; case ERROR_TOO_MANY_OPEN_FILES: return ds2::kErrorTooManySystemFiles; case ERROR_ACCESS_DENIED: return ds2::kErrorAccessDenied; case ERROR_INVALID_HANDLE: return ds2::kErrorInvalidHandle; case ERROR_NOT_ENOUGH_MEMORY: case ERROR_OUTOFMEMORY: return ds2::kErrorNoMemory; case ERROR_WRITE_PROTECT: return ds2::kErrorNotWriteable; case ERROR_READ_FAULT: case ERROR_WRITE_FAULT: case ERROR_INVALID_ADDRESS: case ERROR_NOACCESS: return ds2::kErrorInvalidAddress; case ERROR_NOT_SUPPORTED: return ds2::kErrorUnsupported; case ERROR_FILE_EXISTS: return ds2::kErrorAlreadyExist; case ERROR_INVALID_PARAMETER: return ds2::kErrorInvalidArgument; case ERROR_BAD_EXE_FORMAT: return ds2::kErrorInvalidArgument; case ERROR_PARTIAL_COPY: return ds2::kErrorNoSpace; default: DS2BUG("unknown error code: %#lx", error); } }
ErrorCode ProcessBase::resume(int signal, std::set<Thread *> const &excluded) { enumerateThreads([&](Thread *thread) { if (excluded.find(thread) != excluded.end()) return; switch (thread->state()) { case Thread::kInvalid: case Thread::kTerminated: DS2BUG("trying to resume tid %" PRI_PID " in state %s", thread->tid(), Stringify::ThreadState(thread->state())); break; case Thread::kRunning: DS2LOG(Debug, "not resuming tid %" PRI_PID ", already in state %s", thread->tid(), Stringify::ThreadState(thread->state())); break; case Thread::kStopped: case Thread::kStepped: { Architecture::CPUState state; thread->readCPUState(state); DS2LOG(Debug, "resuming tid %" PRI_PID " from pc %" PRI_PTR " with signal %d", thread->tid(), PRI_PTR_CAST(state.pc()), signal); ErrorCode error = thread->resume(signal); if (error != kSuccess) { DS2LOG(Warning, "failed resuming tid %" PRI_PID ", error=%s", thread->tid(), Stringify::Error(error)); } } break; } }); return kSuccess; }
void SoftwareBreakpointManager::getOpcode(uint32_t type, std::string &opcode) const { switch (type) { #if defined(ARCH_ARM) case 2: // udf #1 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ opcode += '\xde'; opcode += '\x01'; #else opcode += '\x01'; opcode += '\xde'; #endif break; case 3: // udf.w #0 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ opcode += '\xa0'; opcode += '\x00'; opcode += '\xf7'; opcode += '\xf0'; #else opcode += '\xf0'; opcode += '\xf7'; opcode += '\x00'; opcode += '\xa0'; #endif break; case 4: // udf #16 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ opcode += '\xe7'; opcode += '\xf0'; opcode += '\x01'; opcode += '\xf0'; #else opcode += '\xf0'; opcode += '\x01'; opcode += '\xf0'; opcode += '\xe7'; #endif break; #elif defined(ARCH_ARM64) case 4: #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ opcode += '\xd4'; opcode += '\x20'; opcode += '\x20'; opcode += '\x00'; #else opcode += '\x00'; opcode += '\x20'; opcode += '\x20'; opcode += '\xd4'; #endif break; #endif default: DS2LOG(Error, "invalid breakpoint type %d", type); DS2BUG("invalid breakpoint type"); break; } }
ErrorCode PTrace::step(ProcessThreadId const &ptid, ProcessInfo const &pinfo, int signal, Address const &address) { #if defined(ARCH_ARM) DS2BUG("single stepping for ARM must be implemented in software"); #endif ErrorCode error = prepareAddressForResume(ptid, pinfo, address); if (error != kSuccess) { return error; } return super::step(ptid, pinfo, signal); }
ErrorCode PTrace::kill(ProcessThreadId const &ptid, int signal) { if (!ptid.valid()) return kErrorInvalidArgument; int rc; if (!(ptid.tid <= kAnyThreadId)) { DS2BUG("not implemented"); } else { rc = ::kill(ptid.pid, signal); } if (rc < 0) return Platform::TranslateError(); return kSuccess; }
ErrorCode ProcessBase::suspend() { std::set<Thread *> threads; enumerateThreads([&](Thread *thread) { threads.insert(thread); }); for (auto thread : threads) { switch (thread->state()) { case Thread::kInvalid: DS2BUG("trying to suspend tid %" PRI_PID " in state %s", thread->tid(), Stringify::ThreadState(thread->state())); break; case Thread::kStepped: case Thread::kStopped: case Thread::kTerminated: DS2LOG(Debug, "not suspending tid %" PRI_PID ", already in state %s", thread->tid(), Stringify::ThreadState(thread->state())); if (thread->state() == Thread::kTerminated) { removeThread(thread->tid()); } break; case Thread::kRunning: { DS2LOG(Debug, "suspending tid %" PRI_PID, thread->tid()); ErrorCode error = thread->suspend(); switch (error) { case kSuccess: break; case kErrorProcessNotFound: DS2LOG(Debug, "tried to suspended tid %" PRI_PID " which is already dead", thread->tid()); removeThread(thread->tid()); return error; default: DS2LOG(Warning, "failed suspending tid %" PRI_PID ", error=%s", thread->tid(), Stringify::Error(error)); return error; } } break; } } return kSuccess; }
ErrorCode Process::wait(int *status, bool hang) { DEBUG_EVENT de; BOOL result = WaitForDebugEvent(&de, hang ? INFINITE : 0); if (!result) return Platform::TranslateError(); switch (de.dwDebugEventCode) { case CREATE_PROCESS_DEBUG_EVENT: #define CHECK_AND_CLOSE(HAN) \ do { \ if ((de.u.CreateProcessInfo.HAN) != NULL) \ CloseHandle(de.u.CreateProcessInfo.HAN); \ } while (0) CHECK_AND_CLOSE(hFile); CHECK_AND_CLOSE(hProcess); CHECK_AND_CLOSE(hThread); #undef CHECK_AND_CLOSE return kSuccess; case EXIT_PROCESS_DEBUG_EVENT: _terminated = true; return kSuccess; case CREATE_THREAD_DEBUG_EVENT: DS2LOG(Fatal, "debug event CREATE_THREAD"); case EXIT_THREAD_DEBUG_EVENT: DS2LOG(Fatal, "debug event EXIT_THREAD"); case RIP_EVENT: DS2LOG(Fatal, "debug event RIP"); case EXCEPTION_DEBUG_EVENT: case LOAD_DLL_DEBUG_EVENT: case UNLOAD_DLL_DEBUG_EVENT: case OUTPUT_DEBUG_STRING_EVENT: { auto threadIt = _threads.find(de.dwThreadId); DS2ASSERT(threadIt != _threads.end()); threadIt->second->updateState(de); } break; default: DS2BUG("unknown debug event code: %d", de.dwDebugEventCode); } return kSuccess; }
ErrorCode Platform::TranslateError(int error) { switch (error) { case EBUSY: return ds2::kErrorBusy; case ESRCH: return ds2::kErrorProcessNotFound; case EFAULT: case EIO: return ds2::kErrorInvalidAddress; case EPERM: return ds2::kErrorNoPermission; case EEXIST: return ds2::kErrorAlreadyExist; default: DS2BUG("unknown error code: %d", error); } }
ErrorCode Platform::TranslateKernError(kern_return_t kret) { switch (kret) { case KERN_SUCCESS: return ds2::kSuccess; case KERN_FAILURE: return ds2::kErrorUnknown; case KERN_MEMORY_FAILURE: case KERN_INVALID_ADDRESS: case KERN_PROTECTION_FAILURE: return ds2::kErrorInvalidAddress; case KERN_NO_ACCESS: return ds2::kErrorNoPermission; case KERN_RESOURCE_SHORTAGE: return ds2::kErrorNoMemory; default: DS2BUG("unknown error code: %d [%s]", kret, mach_error_string(kret)); } }
ErrorCode Process::attach(int waitStatus) { if (waitStatus <= 0) { ErrorCode error = ptrace().attach(_pid); if (error != kSuccess) { return error; } _flags |= kFlagAttachedProcess; error = ptrace().wait(_pid, &waitStatus); if (error != kSuccess) return error; ptrace().traceThat(_pid); } if (_flags & kFlagAttachedProcess) { // // Enumerate all the tasks and create a Thread // object for every entry. // bool keep_going = true; // // Try to find threads of the newly attached process in multiple // rounds so that we avoid race conditions with threads being // created before we stop the creator. // while (keep_going) { keep_going = false; DS2BUG("not implemented"); } } // // Create the main thread, ourselves. // _currentThread = new Thread(this, _pid); _currentThread->updateStopInfo(waitStatus); return kSuccess; }
ErrorCode Thread::resume(int signal, Address const &address) { // TODO(sas): Not sure how to translate the signal concept to Windows yet. // We'll probably have to get rid of these at some point. DS2ASSERT(signal == 0); if (address.valid()) { CHK(modifyRegisters( [&address](Architecture::CPUState &state) { state.setPC(address); })); } switch (_state) { case kInvalid: case kRunning: DS2BUG("trying to suspend tid %" PRI_PID " in state %s", tid(), Stringify::ThreadState(_state)); break; case kTerminated: return kErrorProcessNotFound; case kStopped: case kStepped: { if (_stopInfo.event == StopInfo::kEventStop && _stopInfo.reason == StopInfo::kReasonNone) { DWORD result = ::ResumeThread(_handle); if (result == (DWORD)-1) { return Platform::TranslateError(); } } else { BOOL result = ::ContinueDebugEvent(_process->pid(), _tid, DBG_CONTINUE); if (!result) { return Platform::TranslateError(); } } _state = kRunning; return kSuccess; } } // Silence warnings without using a `default:` case for the above switch. DS2_UNREACHABLE(); }
ErrorCode Process::wait(int *rstatus) { int status, signal; ProcessInfo info; ErrorCode err; pid_t tid; // We have at least one thread when we start waiting on a process. DS2ASSERT(!_threads.empty()); continue_waiting: err = super::wait(&status); if (err != kSuccess) return err; DS2LOG(Debug, "stopped: status=%d", status); if (WIFEXITED(status)) { err = super::wait(&status); DS2LOG(Debug, "exited: status=%d", status); _currentThread->updateStopInfo(status); _terminated = true; if (rstatus != nullptr) { *rstatus = status; } return kSuccess; } DS2BUG("not implemented"); switch (_currentThread->_stopInfo.event) { case StopInfo::kEventNone: switch (_currentThread->_stopInfo.reason) { case StopInfo::kReasonNone: ptrace().resume(ProcessThreadId(_pid, tid), info); goto continue_waiting; default: DS2ASSERT(false); goto continue_waiting; } case StopInfo::kEventExit: case StopInfo::kEventKill: DS2LOG(Debug, "thread %d is exiting", tid); // // Killing the main thread? // // Note(sas): This might be buggy; the main thread exiting // doesn't mean that the process is dying. // if (tid == _pid && _threads.size() == 1) { DS2LOG(Debug, "last thread is exiting"); break; } // // Remove and release the thread associated with this pid. // removeThread(tid); goto continue_waiting; case StopInfo::kEventStop: if (getInfo(info) != kSuccess) { DS2LOG(Error, "couldn't get process info for pid %d", _pid); goto continue_waiting; } signal = _currentThread->_stopInfo.signal; if (signal == SIGSTOP || signal == SIGCHLD) { // // Silently ignore SIGSTOP, SIGCHLD and SIGRTMIN (this // last one used for thread cancellation) and continue. // // Note(oba): The SIGRTMIN defines expands to a glibc // call, this due to the fact the POSIX standard does // not mandate that SIGRT* defines to be user-land // constants. // // Note(sas): This is probably partially dead code as // ptrace().step() doesn't work on ARM. // // Note(sas): Single-step detection should be higher up, not // only for SIGSTOP, SIGCHLD and SIGRTMIN, but for every // signal that we choose to ignore. // bool stepping = (_currentThread->state() == Thread::kStepped); if (signal == SIGSTOP) { signal = 0; } else { DS2LOG(Debug, "%s due to special signal, tid=%d status=%#x signal=%s", stepping ? "stepping" : "resuming", tid, status, strsignal(signal)); } ErrorCode error; if (stepping) { error = ptrace().step(ProcessThreadId(_pid, tid), info, signal); } else { error = ptrace().resume(ProcessThreadId(_pid, tid), info, signal); } if (error != kSuccess) { DS2LOG(Warning, "cannot resume thread %d error=%d", tid, error); } goto continue_waiting; } else if (_passthruSignals.find(signal) != _passthruSignals.end()) { ptrace().resume(ProcessThreadId(_pid, tid), info, signal); goto continue_waiting; } else { // // This is a signal that we want to transmit back to the // debugger. // break; } } if (!(WIFEXITED(status) || WIFSIGNALED(status))) { // // Suspend the process, this must be done after updating // the thread trap info. // suspend(); } if ((WIFEXITED(status) || WIFSIGNALED(status))) { _terminated = true; } if (rstatus != nullptr) { *rstatus = status; } return kSuccess; }
uint64_t MachOProcess::getAuxiliaryVectorValue(uint64_t type) { DS2BUG("not implemented"); }
// // Enumerates the linkmap of this MachO process. // ErrorCode MachOProcess::enumerateSharedLibraries( std::function<void(SharedLibraryInfo const &)> const &cb) { Address address; CHK(getSharedLibraryInfoAddress(address)); DS2BUG("not implemented"); }
void LibProc::EnumerateProcesses( bool allUsers, UserId const &uid, std::function<void(pid_t pid, uid_t uid)> const &cb) { DS2BUG("not implemented"); }
void SoftwareBreakpointManager::getOpcode(uint32_t type, ByteVector &opcode) const { #if defined(OS_WIN32) && defined(ARCH_ARM) if (type == 4) { static const uint32_t WinARMBPType = 2; DS2LOG(Warning, "requesting a breakpoint of size %u on Windows ARM, " "adjusting to type %u", type, WinARMBPType); type = WinARMBPType; } #endif opcode.clear(); // TODO: We shouldn't have preprocessor checks for ARCH_ARM vs ARCH_ARM64 // because we might be an ARM64 binary debugging an ARM inferior. switch (type) { #if defined(ARCH_ARM) case 2: // udf #1 opcode.push_back('\xde'); #if defined(OS_POSIX) opcode.push_back('\x01'); #elif defined(OS_WIN32) opcode.push_back('\xfe'); #endif break; case 3: // udf.w #0 opcode.push_back('\xa0'); opcode.push_back('\x00'); opcode.push_back('\xf7'); opcode.push_back('\xf0'); break; case 4: // udf #16 opcode.push_back('\xe7'); opcode.push_back('\xf0'); opcode.push_back('\x01'); opcode.push_back('\xf0'); break; #elif defined(ARCH_ARM64) case 4: opcode.push_back('\xd4'); opcode.push_back('\x20'); opcode.push_back('\x20'); opcode.push_back('\x00'); break; #endif default: DS2LOG(Error, "invalid breakpoint type %d", type); DS2BUG("invalid breakpoint type"); break; } #if !(defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE)) #error "Target not supported." #endif #if defined(ENDIAN_LITTLE) std::reverse(opcode.begin(), opcode.end()); #endif }
ErrorCode PTrace::writeMemory(ProcessThreadId const &ptid, Address const &address, void const *buffer, size_t length, size_t *count) { DS2BUG("not implemented"); return kErrorUnsupported; }
void Thread::updateState(DEBUG_EVENT const &de) { DS2ASSERT(de.dwThreadId == _tid); switch (de.dwDebugEventCode) { case EXCEPTION_DEBUG_EVENT: _state = kStopped; DS2LOG( Debug, "exception from inferior, tid=%lu, code=%s, address=%" PRI_PTR, tid(), Stringify::ExceptionCode(de.u.Exception.ExceptionRecord.ExceptionCode), PRI_PTR_CAST(de.u.Exception.ExceptionRecord.ExceptionAddress)); switch (de.u.Exception.ExceptionRecord.ExceptionCode) { case STATUS_BREAKPOINT: case STATUS_SINGLE_STEP: _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonBreakpoint; break; case STATUS_ACCESS_VIOLATION: case STATUS_ARRAY_BOUNDS_EXCEEDED: case STATUS_IN_PAGE_ERROR: case STATUS_STACK_OVERFLOW: case STATUS_STACK_BUFFER_OVERRUN: _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonMemoryError; break; case STATUS_DATATYPE_MISALIGNMENT: _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonMemoryAlignment; break; case STATUS_FLOAT_DENORMAL_OPERAND: case STATUS_FLOAT_DIVIDE_BY_ZERO: case STATUS_FLOAT_INEXACT_RESULT: case STATUS_FLOAT_INVALID_OPERATION: case STATUS_FLOAT_OVERFLOW: case STATUS_FLOAT_STACK_CHECK: case STATUS_FLOAT_UNDERFLOW: case STATUS_INTEGER_DIVIDE_BY_ZERO: case STATUS_INTEGER_OVERFLOW: _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonMathError; break; case STATUS_ILLEGAL_INSTRUCTION: case STATUS_PRIVILEGED_INSTRUCTION: _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonInstructionError; break; default: DS2LOG(Warning, "unsupported exception code: %lx", de.u.Exception.ExceptionRecord.ExceptionCode); case STATUS_INVALID_DISPOSITION: case STATUS_NONCONTINUABLE_EXCEPTION: case DS2_EXCEPTION_UNCAUGHT_COM: case DS2_EXCEPTION_UNCAUGHT_USER: case DS2_EXCEPTION_UNCAUGHT_WINRT: _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonInstructionError; break; } break; case LOAD_DLL_DEBUG_EVENT: { #define CHK_SKIP(C) CHK_ACTION(C, goto skip_name) std::wstring name = L"<unknown>"; ProcessInfo pi; CHK_SKIP(process()->getInfo(pi)); if (de.u.LoadDll.lpImageName == nullptr) { goto skip_name; } uint64_t ptr; // ptr needs to be set to 0 or `if (ptr != 0)` might be true even if the // pointer is null, on 32-bit targets. ptr = 0; CHK_SKIP(process()->readMemory( reinterpret_cast<uint64_t>(de.u.LoadDll.lpImageName), &ptr, pi.pointerSize)); if (ptr == 0) { goto skip_name; } // It seems like all strings passed by the kernel here are guaranteed to be // unicode. DS2ASSERT(de.u.LoadDll.fUnicode); name.clear(); wchar_t c; do { if (process()->readMemory(ptr, &c, sizeof(c)) != kSuccess) { break; } name.append(1, c); ptr += sizeof(c); } while (c != '\0'); skip_name: DS2LOG(Debug, "new DLL loaded: %s, base=%" PRI_PTR, Host::Platform::WideToNarrowString(name).c_str(), PRI_PTR_CAST(de.u.LoadDll.lpBaseOfDll)); if (de.u.LoadDll.hFile != NULL) { ::CloseHandle(de.u.LoadDll.hFile); } _state = kStopped; _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonLibraryEvent; } break; case UNLOAD_DLL_DEBUG_EVENT: DS2LOG(Debug, "DLL unloaded, base=%" PRI_PTR, PRI_PTR_CAST(de.u.UnloadDll.lpBaseOfDll)); _state = kStopped; _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonLibraryEvent; break; case EXIT_THREAD_DEBUG_EVENT: _state = kStopped; _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonThreadExit; break; case OUTPUT_DEBUG_STRING_EVENT: { auto const &dsInfo = de.u.DebugString; DS2LOG(Debug, "inferior output a debug string: %" PRI_PTR "[%d]", PRI_PTR_CAST(dsInfo.lpDebugStringData), dsInfo.nDebugStringLength); // The length includes terminating null character. DS2ASSERT(dsInfo.nDebugStringLength >= 1); std::string buffer((dsInfo.fUnicode ? sizeof(WCHAR) : 1) * (dsInfo.nDebugStringLength - 1), 0); CHKV(process()->readMemory( reinterpret_cast<uint64_t>(dsInfo.lpDebugStringData), const_cast<char *>(buffer.c_str()), buffer.size())); _stopInfo.debugString = dsInfo.fUnicode ? Platform::WideToNarrowString(std::wstring( reinterpret_cast<const wchar_t *>(buffer.c_str()), dsInfo.nDebugStringLength - 1)) : std::move(buffer); _state = kStopped; _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonDebugOutput; } break; default: DS2BUG("unknown debug event code: %lu", de.dwDebugEventCode); } }
void Thread::updateState(DEBUG_EVENT const &de) { DS2ASSERT(de.dwThreadId == _tid); switch (de.dwDebugEventCode) { case EXCEPTION_DEBUG_EVENT: _state = kStopped; _trap.event = StopInfo::kEventTrap; _trap.reason = StopInfo::kReasonNone; break; case LOAD_DLL_DEBUG_EVENT: { ErrorCode error; std::wstring name = L"<NONAME>"; ProcessInfo pi; error = process()->getInfo(pi); if (error != kSuccess) goto noname; if (de.u.LoadDll.lpImageName == nullptr) goto noname; uint64_t ptr = 0; error = process()->readMemory( reinterpret_cast<uint64_t>(de.u.LoadDll.lpImageName), &ptr, pi.pointerSize); if (error != kSuccess) goto noname; if (ptr == 0) goto noname; // It seems like all strings passed by the kernel here are guaranteed to be // unicode. DS2ASSERT(de.u.LoadDll.fUnicode); name.clear(); wchar_t c; do { error = process()->readMemory(ptr, &c, sizeof(c)); if (error != kSuccess) break; name.append(1, c); ptr += sizeof(c); } while (c != '\0'); noname: DS2LOG(Debug, "new DLL loaded: %s", Host::Platform::WideToNarrowString(name).c_str()); if (de.u.LoadDll.hFile != NULL) CloseHandle(de.u.LoadDll.hFile); } case UNLOAD_DLL_DEBUG_EVENT: case OUTPUT_DEBUG_STRING_EVENT: _state = kStopped; _trap.event = StopInfo::kEventNone; _trap.reason = StopInfo::kReasonNone; break; default: DS2BUG("unknown debug event code: %d", de.dwDebugEventCode); } }
size_t SoftwareBreakpointManager::chooseBreakpointSize() const { DS2BUG( "Choosing a software breakpoint size on ARM is an unsupported operation"); }
ErrorCode Process::wait(int *status, bool hang) { if (_terminated) return kSuccess; DEBUG_EVENT de; bool keepGoing = true; while (keepGoing) { BOOL result = WaitForDebugEvent(&de, hang ? INFINITE : 0); if (!result) return Platform::TranslateError(); keepGoing = false; switch (de.dwDebugEventCode) { case CREATE_PROCESS_DEBUG_EVENT: #define CHECK_AND_CLOSE(HAN) \ do { \ if ((de.u.CreateProcessInfo.HAN) != NULL) \ CloseHandle(de.u.CreateProcessInfo.HAN); \ } while (0) CHECK_AND_CLOSE(hFile); CHECK_AND_CLOSE(hProcess); CHECK_AND_CLOSE(hThread); #undef CHECK_AND_CLOSE return kSuccess; case EXIT_PROCESS_DEBUG_EVENT: _terminated = true; return kSuccess; case CREATE_THREAD_DEBUG_EVENT: { auto threadHandle = de.u.CreateThread.hThread; auto tid = GetThreadId(threadHandle); // No need to save the new thread pointer, as it gets added automatically // to the process. new Thread(this, tid, threadHandle); resume(); keepGoing = true; } break; case EXIT_THREAD_DEBUG_EVENT: { auto threadIt = _threads.find(de.dwThreadId); DS2ASSERT(threadIt != _threads.end()); auto tid = threadIt->second->tid(); ContinueDebugEvent(_pid, tid, DBG_CONTINUE); removeThread(threadIt->second->tid()); keepGoing = true; } break; case RIP_EVENT: DS2LOG(Fatal, "debug event RIP"); case EXCEPTION_DEBUG_EVENT: case LOAD_DLL_DEBUG_EVENT: case UNLOAD_DLL_DEBUG_EVENT: case OUTPUT_DEBUG_STRING_EVENT: { auto threadIt = _threads.find(de.dwThreadId); DS2ASSERT(threadIt != _threads.end()); threadIt->second->updateState(de); } break; default: DS2BUG("unknown debug event code: %lu", de.dwDebugEventCode); } } return kSuccess; }