diff --git a/pykd_ext/dbgout.h b/pykd_ext/dbgout.h index d124a4a..238e326 100644 --- a/pykd_ext/dbgout.h +++ b/pykd_ext/dbgout.h @@ -65,6 +65,7 @@ public: BEGIN_PYTHON_METHOD_MAP(DbgOut, "dbgout") PYTHON_METHOD1("write", write, "write"); + PYTHON_METHOD1("writedml", writedml, "writedml"); PYTHON_METHOD0("flush", flush, "flush"); PYTHON_PROPERTY("encoding", encoding, "encoding"); PYTHON_PROPERTY("closed", closed, "closed"); diff --git a/pykd_ext/pyinterpret.cpp b/pykd_ext/pyinterpret.cpp index 78e0d09..6380343 100644 --- a/pykd_ext/pyinterpret.cpp +++ b/pykd_ext/pyinterpret.cpp @@ -333,8 +333,6 @@ HMODULE LoadPythonLibrary(int majorVersion, int minorVersion) } - - std::list getInstalledInterpreter() { std::set interpretSet; diff --git a/pykd_ext/pyinterpret.h b/pykd_ext/pyinterpret.h index 49f10b8..a9fedbb 100644 --- a/pykd_ext/pyinterpret.h +++ b/pykd_ext/pyinterpret.h @@ -31,6 +31,8 @@ inline bool operator < (const InterpreterDesc& d1, const InterpreterDesc& d2) std::list getInstalledInterpreter(); +void getDefaultInterpreter(int majorVersion, int minorVersion); + bool isInterpreterLoaded(int majorVersion, int minorVersion); void stopAllInterpreter(); diff --git a/pykd_ext/windbgext.cpp b/pykd_ext/windbgext.cpp index f827815..b8f4f9d 100644 --- a/pykd_ext/windbgext.cpp +++ b/pykd_ext/windbgext.cpp @@ -17,7 +17,8 @@ void handleException(); std::string getScriptFileName(const std::string &scriptName); -void getPythonVersion(int& majorVersion, int& minorVersion); +void getPythonVersion(int& majorVersion, int& minorVersion); +void getDefaultPythonVersion(int& majorVersion, int& minorVersion); ////////////////////////////////////////////////////////////////////////////// @@ -134,13 +135,23 @@ info( std::list interpreterList = getInstalledInterpreter(); std::stringstream sstr; + int defaultMajor; + int defaultMinor; + + getDefaultPythonVersion(defaultMajor, defaultMinor); + sstr << std::endl << "Installed python" << std::endl << std::endl; - sstr << std::setw(14) << std::left << "Version:" << std::setw(12) << std::left << "Status: " << std::left << "Image:" << std::endl; + sstr << std::setw(16) << std::left << "Version:" << std::setw(12) << std::left << "Status: " << std::left << "Image:" << std::endl; sstr << "------------------------------------------------------------------------------" << std::endl; if (interpreterList.size() > 0) { for (const InterpreterDesc& desc : interpreterList) { + if ( defaultMajor == desc.majorVersion && defaultMinor == desc.minorVersion) + sstr << "* "; + else + sstr << " "; + sstr << std::setw(14) << std::left << make_version(desc.majorVersion, desc.minorVersion); sstr << std::setw(12) << std::left << (isInterpreterLoaded(desc.majorVersion, desc.minorVersion) ? "Loaded" : "Unloaded"); @@ -532,14 +543,17 @@ std::string getScriptFileName(const std::string &scriptName) /////////////////////////////////////////////////////////////////////////////// -void getPythonVersion(int& majorVersion, int& minorVersion) +void getPythonVersion(int& majorVersion, int& minorVersion) { + if (majorVersion == -1 && minorVersion == -1 ) + return getDefaultPythonVersion(majorVersion, minorVersion); + std::list interpreterList = getInstalledInterpreter(); bool found = false; bool anyMinorVersion = minorVersion == -1; bool anyMajorVersion = majorVersion == -1; - + if (anyMajorVersion) { for (auto interpret : interpreterList) @@ -577,777 +591,64 @@ void getPythonVersion(int& majorVersion, int& minorVersion) /////////////////////////////////////////////////////////////////////////////// - - - - - - - - - - - - - - - - - - - -/* -#include - -#include -#include -#include - -#include -namespace python = boost::python; - -#include - -/////////////////////////////////////////////////////////////////////////////// - -class AutoRestorePyState +void getDefaultPythonVersion(int& majorVersion, int& minorVersion) { -public: + std::list interpreterList = getInstalledInterpreter(); - AutoRestorePyState() + bool found = false; + majorVersion = -1; + minorVersion = -1; + + for (auto interpret : interpreterList) { - m_state = PyEval_SaveThread(); - } - - ~AutoRestorePyState() - { - PyEval_RestoreThread(m_state); - } - -private: - - PyThreadState* m_state; -}; - -////////////////////////////////////////////////////////////////////////////// - -class DbgOut -{ -public: - - DbgOut(PDEBUG_CLIENT client) - : m_control(client) - {} - - void write(const std::wstring& str) - { - AutoRestorePyState pystate; - - m_control->ControlledOutputWide( - DEBUG_OUTCTL_THIS_CLIENT, - DEBUG_OUTPUT_NORMAL, - L"%ws", - str.c_str() - ); - - } - - void writedml(const std::wstring& str) - { - AutoRestorePyState pystate; - - m_control->ControlledOutputWide( - DEBUG_OUTCTL_THIS_CLIENT | DEBUG_OUTCTL_DML, - DEBUG_OUTPUT_NORMAL, - L"%ws", - str.c_str() - ); - } - - void flush() { - } - - std::wstring encoding() { - return L"ascii"; - } - - bool closed() { - return false; - } - -private: - - CComQIPtr m_control; -}; - -/////////////////////////////////////////////////////////////////////////////// - -class DbgIn -{ -public: - - DbgIn(PDEBUG_CLIENT client) - : m_control(client) - {} - - std::wstring readline() - { - AutoRestorePyState pystate; - - std::vector inputBuffer(0x10000); - - ULONG read = 0; - m_control->InputWide(&inputBuffer[0], static_cast(inputBuffer.size()), &read); - - std::wstring inputstr = std::wstring(&inputBuffer[0]); - - return inputstr.empty() ? L"\n" : inputstr; - } - - bool closed() { - return false; - } - - -private: - - CComQIPtr m_control; -}; - -/////////////////////////////////////////////////////////////////////////////// - -class PythonInterpreter -{ -public: - - PythonInterpreter() - { - PyThreadState* state = Py_NewInterpreter(); - - PyThreadState_Swap(state); - - m_state = PyEval_SaveThread(); - } - - ~PythonInterpreter() - { - PyEval_RestoreThread(m_state); - - PyInterpreterState *interpreter = m_state->interp; - - while (interpreter->tstate_head != NULL) + if (2 == interpret.majorVersion && 7 == interpret.minorVersion) { - PyThreadState *threadState = (PyThreadState*)(interpreter->tstate_head); - - PyThreadState_Clear(threadState); - - PyThreadState_Swap(NULL); - - PyThreadState_Delete(threadState); - } - - PyInterpreterState_Clear(interpreter); - - PyInterpreterState_Delete(interpreter); - } - - void acivate() - { - PyEval_RestoreThread(m_state); - } - - void deactivate() - { - m_state = PyEval_SaveThread(); - } - -private: - - PyThreadState* m_state; - -}; - -/////////////////////////////////////////////////////////////////////////////// - -class PythonSingleton -{ - -public: - - static PythonSingleton* get() - { - if (!m_instance) - m_instance = new PythonSingleton(); - - return m_instance; - } - - void stop() - { - delete m_globalInterpreter; - m_globalInterpreter = 0; - PyThreadState_Swap(m_globalState); - m_globalState = PyEval_SaveThread(); - } - - void start() - { - PyEval_RestoreThread(m_globalState); - - m_globalInterpreter = new PythonInterpreter(); - - //m_globalInterpreter->acivate(); - - //python::object main = boost::python::import("__main__"); - - //python::object main_namespace = main.attr("__dict__"); - - //m_globalInterpreter->deactivate(); - } - - void acivateGlobal() { - m_globalInterpreter->acivate(); - } - - void deactivateGlobal() { - m_globalInterpreter->deactivate(); - } - - void acivateLocal() { - PyEval_RestoreThread(m_globalState); - m_locallInterpreter = new PythonInterpreter(); - m_locallInterpreter->acivate(); - } - - void deactivateLocal() { - m_locallInterpreter->deactivate(); - delete m_locallInterpreter; - m_locallInterpreter = 0; - PyThreadState_Swap(m_globalState); - m_globalState = PyEval_SaveThread(); - } - - void checkPykd() - { - if (m_pykdInit) + majorVersion = 2; + minorVersion = 7; return; - - python::handle<> pykdHandle(python::allow_null(PyImport_ImportModule("pykd"))); - if (!pykdHandle) - throw std::exception("Pykd package is not installed.You can install it by command \"!pykd.install\""); - - python::object main = python::import("__main__"); - python::object globalScope(main.attr("__dict__")); - python::exec("__import__('pykd').initialize()", globalScope); - - m_pykdInit = true; - } - -private: - - static PythonSingleton* m_instance; - - PythonSingleton() - { - Py_Initialize(); - PyEval_InitThreads(); - - // Python debug output console helper classes - python::class_("dout", "dout", python::no_init) - .def("write", &DbgOut::write) - .def("writedml", &DbgOut::writedml) - .def("flush", &DbgOut::flush) - .add_property("encoding", &DbgOut::encoding) - .add_property("closed", &DbgOut::closed); - - python::class_("din", "din", python::no_init) - .def("readline", &DbgIn::readline) - .add_property("closed", &DbgIn::closed); - - - m_globalState = PyEval_SaveThread(); - } - - PythonInterpreter* m_globalInterpreter; - PythonInterpreter* m_locallInterpreter; - PyThreadState* m_globalState; - - bool m_pykdInit; -}; - -PythonSingleton* PythonSingleton::m_instance = 0; - -////////////////////////////////////////////////////////////////////////////// - -class InterruptWatch -{ -public: - - InterruptWatch(PDEBUG_CLIENT client) - { - m_control = client; - m_stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - m_thread = CreateThread(NULL, 0, threadRoutine, this, 0, NULL); - } - - ~InterruptWatch() - { - SetEvent(m_stopEvent); - WaitForSingleObject(m_thread, INFINITE); - CloseHandle(m_stopEvent); - CloseHandle(m_thread); - } - - bool onInterrupt() - { - HANDLE quitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - PyGILState_STATE state = PyGILState_Ensure(); - Py_AddPendingCall(&quit, (void*)quitEvent); - PyGILState_Release(state); - WaitForSingleObject(quitEvent, INFINITE); - CloseHandle(quitEvent); - return true; - } - - static int quit(void *context) - { - HANDLE quitEvent = (HANDLE)context; - PyErr_SetString(PyExc_SystemExit, "CTRL+BREAK"); - SetEvent(quitEvent); - return -1; - } - -private: - - static DWORD WINAPI threadRoutine(LPVOID lpParameter) { - return static_cast(lpParameter)->interruptWatchRoutine(); - } - - DWORD InterruptWatch::interruptWatchRoutine() - { - while (WAIT_TIMEOUT == WaitForSingleObject(m_stopEvent, 250)) - { - HRESULT hres = m_control->GetInterrupt(); - if (hres == S_OK) - { - HANDLE quitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - PyGILState_STATE state = PyGILState_Ensure(); - Py_AddPendingCall(&quit, (void*)quitEvent); - PyGILState_Release(state); - WaitForSingleObject(quitEvent, INFINITE); - CloseHandle(quitEvent); - } - } - - return 0; - } - - HANDLE m_thread; - - HANDLE m_stopEvent; - - CComQIPtr m_control; -}; - -////////////////////////////////////////////////////////////////////////////// - -typedef std::vector< std::string > ArgsList; -typedef boost::escaped_list_separator char_separator_t; -typedef boost::tokenizer< char_separator_t > char_tokenizer_t; - -ArgsList getArgsList( - PCSTR args - ) -{ - std::string argsStr(args); - - char_tokenizer_t token(argsStr, char_separator_t("", " \t", "\"")); - ArgsList argsList; - - for (char_tokenizer_t::iterator it = token.begin(); it != token.end(); ++it) - { - if (*it != "") - argsList.push_back(*it); - } - - return argsList; -} - -////////////////////////////////////////////////////////////////////////////// - -static const char printUsageMsg[] = - "usage:\n" - "!py [options] [file]\n" - "\tOptions:\n" - "\t-g --global : run code in the common namespace\n" - "\t-l --local : run code in the isolate namespace\n" - "!install\n" - "!upgrade\n"; - -void printUsage(PDEBUG_CLIENT client) -{ - CComQIPtr(client)-> - ControlledOutput( - DEBUG_OUTCTL_THIS_CLIENT, - DEBUG_OUTPUT_NORMAL, - "%s", - printUsageMsg - ); -} - -////////////////////////////////////////////////////////////////////////////// - -extern "C" -HRESULT -CALLBACK -DebugExtensionInitialize( - PULONG Version, - PULONG Flags - ) -{ - PythonSingleton::get()->start(); - return S_OK; -} - -////////////////////////////////////////////////////////////////////////////// - -extern "C" -VOID -CALLBACK -DebugExtensionUninitialize() -{ - PythonSingleton::get()->stop(); -} - -////////////////////////////////////////////////////////////////////////////// - -std::string getScriptFileName(const std::string &scriptName); - -void printException(DbgOut &dbgOut); - -////////////////////////////////////////////////////////////////////////////// - -extern "C" -HRESULT -CALLBACK -py( - PDEBUG_CLIENT client, - PCSTR args - ) -{ - ArgsList argsList = getArgsList(args); - - bool global = false; - bool local = false; - bool clean = false; - - if (!argsList.empty()) - { - if (argsList[0] == "-h" || argsList[0] == "--help") - { - printUsage(client); - return S_OK; - } - else - if (argsList[0] == "-g" || argsList[0] == "--global") - { - global = true; - argsList.erase(argsList.begin()); - } - else - if (argsList[0] == "-l" || argsList[0] == "--local") - { - local = true; - argsList.erase(argsList.begin()); } } - if (argsList.size() > 0) + for (auto interpret : interpreterList) { - global = !(global || local) ? false : global; //set local by default - } - else - { - global = !(global || local) ? true : global; //set global by default - } - - DbgOut dbgOut(client); - DbgOut dbgErr(client); - DbgIn dbgIn(client); - - ULONG oldMask; - client->GetOutputMask(&oldMask); - client->SetOutputMask(DEBUG_OUTPUT_NORMAL | DEBUG_OUTPUT_ERROR); - - - if (global) - PythonSingleton::get()->acivateGlobal(); - else - PythonSingleton::get()->acivateLocal(); - - try { - - InterruptWatch interruptWatch(client); - - python::object sys = python::import("sys"); - - sys.attr("stdout") = python::object(dbgOut); - sys.attr("stderr") = python::object(dbgErr); - sys.attr("stdin") = python::object(dbgIn); - - python::object main = python::import("__main__"); - python::object globalScope(main.attr("__dict__")); - - PythonSingleton::get()->checkPykd(); - - if (argsList.size() == 0) + if (3 == interpret.majorVersion && 5 == interpret.minorVersion) { - - python::exec("import pykd", globalScope); - python::exec("from pykd import *", globalScope); - python::exec("__import__('code').InteractiveConsole(__import__('__main__').__dict__).interact()", globalScope); - } - else - { - - std::string scriptFileName = getScriptFileName(argsList[0]); - if (scriptFileName.empty()) - throw std::invalid_argument("script not found"); - - // устанавиливаем питоновские аргументы - char **pythonArgs = new char*[argsList.size()]; - - pythonArgs[0] = const_cast(scriptFileName.c_str()); - - for (size_t i = 1; i < argsList.size(); ++i) - pythonArgs[i] = const_cast(argsList[i].c_str()); - - PySys_SetArgv((int)argsList.size(), pythonArgs); - - delete[] pythonArgs; - - python::exec_file(scriptFileName.c_str(), globalScope); + majorVersion = 3; + minorVersion = 5; + return; } } - catch (const python::error_already_set&) + + for (auto interpret : interpreterList) { - printException(dbgOut); + if (2 == interpret.majorVersion && + minorVersion <= interpret.minorVersion ) + { + found = true; + majorVersion = 2; + minorVersion = interpret.minorVersion; + } } - catch (const std::exception& invalidArg) + + if (found) + return; + + for (auto interpret : interpreterList) { - _bstr_t bstrInavalidArg(invalidArg.what()); - dbgOut.write(std::wstring(bstrInavalidArg)); + if (3 == interpret.majorVersion && + minorVersion <= interpret.minorVersion ) + { + found = true; + majorVersion = 3; + minorVersion = interpret.minorVersion; + } } - if (global) - PythonSingleton::get()->deactivateGlobal(); - else - PythonSingleton::get()->deactivateLocal(); + if (found) + return; - client->SetOutputMask(oldMask); - - return S_OK; -} - -////////////////////////////////////////////////////////////////////////////// - -extern "C" -HRESULT -CALLBACK -install( - PDEBUG_CLIENT client, - PCSTR args - ) -{ - DbgOut dbgOut(client); - DbgOut dbgErr(client); - DbgIn dbgIn(client); - - PythonSingleton::get()->acivateGlobal(); - - try { - - python::object sys = python::import("sys"); - - sys.attr("stdout") = python::object(dbgOut); - sys.attr("stderr") = python::object(dbgErr); - sys.attr("stdin") = python::object(dbgIn); - - // получаем доступ к глобальному мапу ( нужен для вызова exec_file ) - python::object main = python::import("__main__"); - python::object global(main.attr("__dict__")); - - python::exec("import pip\n", global); - python::exec("pip.logger.consumers = []\n", global); - python::exec("pip.main(['install', 'pykd'])\n", global); - - } - catch (const python::error_already_set&) - { - printException(dbgOut); - } - catch (const std::exception& invalidArg) - { - _bstr_t bstrInavalidArg(invalidArg.what()); - dbgOut.write(std::wstring(bstrInavalidArg)); - } - - PythonSingleton::get()->deactivateGlobal(); - - return S_OK; -} - -////////////////////////////////////////////////////////////////////////////// - -extern "C" -HRESULT -CALLBACK -upgrade( - PDEBUG_CLIENT client, - PCSTR args - ) -{ - DbgOut dbgOut(client); - DbgOut dbgErr(client); - DbgIn dbgIn(client); - - PythonSingleton::get()->acivateGlobal(); - - try { - - python::object sys = python::import("sys"); - - sys.attr("stdout") = python::object(dbgOut); - sys.attr("stderr") = python::object(dbgErr); - sys.attr("stdin") = python::object(dbgIn); - - // получаем доступ к глобальному мапу ( нужен для вызова exec_file ) - python::object main = python::import("__main__"); - python::object global(main.attr("__dict__")); - - python::exec("import pip\n", global); - python::exec("pip.logger.consumers = []\n", global); - python::exec("pip.main(['install', '--upgrade', 'pykd'])\n", global); - - } - catch (const python::error_already_set&) - { - printException(dbgOut); - } - catch (const std::exception& invalidArg) - { - _bstr_t bstrInavalidArg(invalidArg.what()); - dbgOut.write(std::wstring(bstrInavalidArg)); - } - - PythonSingleton::get()->deactivateGlobal(); - - return S_OK; + if (!found) + throw std::exception("failed to find python interpreter\n"); } /////////////////////////////////////////////////////////////////////////////// - -std::string findScript(const std::string &fullFileName) -{ - if (GetFileAttributesA(fullFileName.c_str()) != INVALID_FILE_ATTRIBUTES) - return fullFileName; - - python::object sys = python::import("sys"); - - python::list pathList(sys.attr("path")); - - python::ssize_t n = python::len(pathList); - - for (python::ssize_t i = 0; i < n; i++) - { - std::string path = boost::python::extract(pathList[i]); - - DWORD bufSize = SearchPathA( - path.c_str(), - fullFileName.c_str(), - NULL, - 0, - NULL, - NULL); - - if (bufSize > 0) - { - bufSize += 1; - std::vector fullFileNameCStr(bufSize); - char *partFileNameCStr = NULL; - - bufSize = SearchPathA( - path.c_str(), - fullFileName.c_str(), - NULL, - bufSize, - &fullFileNameCStr[0], - &partFileNameCStr); - - DWORD fileAttr = GetFileAttributesA(&fullFileNameCStr[0]); - - if ((fileAttr & FILE_ATTRIBUTE_DIRECTORY) == 0) - return std::string(&fullFileNameCStr[0]); - } - } - - return ""; -} - -/////////////////////////////////////////////////////////////////////////////// - -std::string getScriptFileName(const std::string &scriptName) -{ - std::string scriptFileName = findScript(scriptName); - - if (scriptFileName.empty()) - { - std::string scriptNameLow; - scriptNameLow.resize(scriptName.size()); - std::transform( - scriptName.begin(), - scriptName.end(), - scriptNameLow.begin(), - ::tolower); - if (scriptNameLow.rfind(".py") != (scriptNameLow.length() - 3)) - scriptFileName = findScript(scriptName + ".py"); - } - - return scriptFileName; -} - -/////////////////////////////////////////////////////////////////////////////// - -void printException(DbgOut &dbgOut) -{ - // ошибка в скрипте - PyObject *errtype = NULL, *errvalue = NULL, *traceback = NULL; - - PyErr_Fetch(&errtype, &errvalue, &traceback); - - PyErr_NormalizeException(&errtype, &errvalue, &traceback); - - if (errtype != PyExc_SystemExit) - { - python::object tracebackModule = python::import("traceback"); - - std::wstringstream sstr; - - python::object lst = - python::object(tracebackModule.attr("format_exception"))( - python::handle<>(errtype), - python::handle<>(python::allow_null(errvalue)), - python::handle<>(python::allow_null(traceback))); - - sstr << std::endl << std::endl; - - for (long i = 0; i < python::len(lst); ++i) - sstr << std::wstring(python::extract(lst[i])) << std::endl; - - dbgOut.write(sstr.str()); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -*/