//---------------------------------------------------------------------------- // // AsynchronousKdController.cpp // // An extension of the KDController class that allows running certain commands // (e.g. running target) asynchronously. // // Copyright (c) Microsoft. All rights reserved. // //---------------------------------------------------------------------------- #include "stdafx.h" #include "AsynchronousKDController.h" #include "KDController.h" #include "ExceptionHelpers.h" #include "HandleHelpers.h" using namespace KDControllerLib; AsynchronousKDController *AsynchronousKDController::Create(_In_ LPCTSTR pDebuggingToolsPath, _In_ LPCTSTR pConnectionArguments) { if (pDebuggingToolsPath == nullptr || pConnectionArguments == nullptr) { throw _com_error(E_POINTER); } TCHAR kdExe[MAX_PATH]; TCHAR kdCommandLine[MAX_PATH]; _sntprintf_s(kdExe, _TRUNCATE, _T("%s\\kd.exe"), pDebuggingToolsPath); _sntprintf_s(kdCommandLine, _TRUNCATE, _T("\"%s\\kd.exe\" %s"), pDebuggingToolsPath, pConnectionArguments); HandleWrapper stdInputHandle; HandleWrapper stdOutputHandle; HandleWrapper remoteStdInputHandle; HandleWrapper remoteStdOutputHandle; SECURITY_ATTRIBUTES allowHandleInheritanceAttributes = {sizeof(SECURITY_ATTRIBUTES), nullptr, TRUE}; if (!CreatePipe(&remoteStdInputHandle, &stdInputHandle, &allowHandleInheritanceAttributes, 0)) { throw _com_error(HRESULT_FROM_WIN32(GetLastError())); } if (!CreatePipe(&stdOutputHandle, &remoteStdOutputHandle, &allowHandleInheritanceAttributes, 0)) { throw _com_error(HRESULT_FROM_WIN32(GetLastError())); } STARTUPINFO startupInformation = {sizeof(STARTUPINFO), }; PROCESS_INFORMATION processInformation; startupInformation.dwFlags |= STARTF_USESTDHANDLES; startupInformation.hStdError = INVALID_HANDLE_VALUE; startupInformation.hStdInput = remoteStdInputHandle.Get(); startupInformation.hStdOutput = remoteStdOutputHandle.Get(); if (!CreateProcess(nullptr, kdCommandLine, nullptr, nullptr, TRUE, 0, nullptr, nullptr, &startupInformation, &processInformation)) { throw _com_error(HRESULT_FROM_WIN32(GetLastError())); } HandleWrapper::CloseAndInvalidate(&processInformation.hThread); AsynchronousKDController *pResult = new AsynchronousKDController(processInformation.hProcess, stdInputHandle.Detach(), stdOutputHandle.Detach()); pResult->WaitForInitialPrompt(); return pResult; } AsynchronousKDController::AsynchronousKDController(_In_ HANDLE processHandle, _In_ HANDLE stdInput, _In_ HANDLE stdOutput) : KDController(processHandle, stdInput, stdOutput) , m_asynchronousCommandThread(nullptr) { } AsynchronousKDController::~AsynchronousKDController() { if (IsAsynchronousCommandInProgress()) { ShutdownKD(); WaitForSingleObject(m_asynchronousCommandThread, INFINITE); } if (m_asynchronousCommandThread != nullptr) { CloseHandle(m_asynchronousCommandThread); m_asynchronousCommandThread = nullptr; } } unsigned AsynchronousKDController::CreateCodeBreakpoint(_In_ AddressType address) { unsigned slot = static_cast(-1); for(unsigned i = 0; i < m_breakpointSlots.size(); i++) { if (!m_breakpointSlots[i]) { slot = i; break; } } if (slot == -1) { slot = static_cast(m_breakpointSlots.size()); m_breakpointSlots.push_back(false); } char command[256]; _snprintf_s(command, _TRUNCATE, "bp%d 0x%I64x ; .echo", slot, address); std::string reply = ExecuteCommand(command); //KD does not seem to report any error conditions when setting breakpoints by addresses UNREFERENCED_PARAMETER(reply); m_breakpointSlots[slot] = true; return slot; } void AsynchronousKDController::DeleteCodeBreakpoint(_In_ unsigned breakpointNumber) { if (breakpointNumber >= m_breakpointSlots.size() || !m_breakpointSlots[breakpointNumber]) { throw std::exception("Trying to delete non-existing breakpoint"); } char command[256]; _snprintf_s(command, _TRUNCATE, "bc %d ; .echo", breakpointNumber); ExecuteCommand(command); m_breakpointSlots[breakpointNumber] = false; } std::string AsynchronousKDController::ExecuteCommand(_In_ LPCSTR pCommand) { if (IsAsynchronousCommandInProgress()) { throw std::exception("Cannot execute KD command while an asynchronous command is in progress (e.g. target is running)\r\n"); } return KDController::ExecuteCommand(pCommand); } void AsynchronousKDController::StartAsynchronousCommand(_In_ LPCSTR pCommand) { assert(pCommand != nullptr); if (IsAsynchronousCommandInProgress()) { throw std::exception("Cannot execute KD command while an asynchronous command is in progress (e.g. target is running)."); } if (m_asynchronousCommandThread != nullptr) { CloseHandle(m_asynchronousCommandThread); } //At this point no other thread is using these, so no lock is needed m_currentAsynchronousCommand = pCommand; m_currentAsynchronousCommandResult.clear(); DWORD threadId = 0; m_asynchronousCommandThread = CreateThread(nullptr, 0, AsynchronousCommandThreadBody, this, 0, &threadId); if (m_asynchronousCommandThread == nullptr) { throw std::exception("Failed to start asynchronous command thread."); } } bool AsynchronousKDController::IsAsynchronousCommandInProgress() { return m_asynchronousCommandThread != nullptr && WaitForSingleObject(m_asynchronousCommandThread, 0) != WAIT_OBJECT_0; } bool AsynchronousKDController::GetAsynchronousCommandResult(_In_ DWORD timeoutInMilliseconds, _Out_opt_ std::string *pResult) { if (pResult != nullptr) { pResult->clear(); } if (m_asynchronousCommandThread == nullptr) { throw std::exception("No active asynchronous command is running"); } bool result = false; if (WaitForSingleObject(m_asynchronousCommandThread, timeoutInMilliseconds) == WAIT_OBJECT_0) { result = true; if (pResult != nullptr) { pResult->assign(m_currentAsynchronousCommandResult); } } return result; } DWORD AsynchronousKDController::AsynchronousCommandThreadBody(LPVOID p) { AsynchronousKDController *pController = reinterpret_cast(p); assert(pController != nullptr); pController->m_currentAsynchronousCommandResult = pController->KDController::ExecuteCommand(pController->m_currentAsynchronousCommand.c_str()); return 0; } void AsynchronousKDController::StartStepCommand(unsigned processorNumber) { if (processorNumber != -1) { char processorSelectionCommand[32]; _snprintf_s(processorSelectionCommand, _TRUNCATE, "~%ds ; .echo", processorNumber); ExecuteCommand(processorSelectionCommand); } StartAsynchronousCommand("t"); } void AsynchronousKDController::StartRunCommand() { StartAsynchronousCommand("g"); }