diff --git a/pykd/dbgengine.cpp b/pykd/dbgengine.cpp index de5dff0..0992454 100644 --- a/pykd/dbgengine.cpp +++ b/pykd/dbgengine.cpp @@ -5,27 +5,10 @@ #include "dbgengine.h" #include "variant.h" +#include "pystate.h" namespace pykd { -class AutoRestorePyState -{ -public: - - AutoRestorePyState() - { - m_state = PyEval_SaveThread(); - } - - ~AutoRestorePyState() - { - PyEval_RestoreThread( m_state ); - } - -private: - - PyThreadState* m_state; -}; /////////////////////////////////////////////////////////////////////////////// diff --git a/pykd/pykd.vcxproj b/pykd/pykd.vcxproj index 6d860c1..99977eb 100644 --- a/pykd/pykd.vcxproj +++ b/pykd/pykd.vcxproj @@ -239,6 +239,8 @@ + + diff --git a/pykd/pykd.vcxproj.filters b/pykd/pykd.vcxproj.filters index 874c683..705f138 100644 --- a/pykd/pykd.vcxproj.filters +++ b/pykd/pykd.vcxproj.filters @@ -54,6 +54,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/pykd/pymod.cpp b/pykd/pymod.cpp index ef04f35..dc87732 100644 --- a/pykd/pymod.cpp +++ b/pykd/pymod.cpp @@ -15,6 +15,7 @@ #include "windbgext.h" #include "eventhandler.h" #include "cpucontext.h" +#include "pydbgio.h" using namespace pykd; @@ -132,10 +133,14 @@ BOOST_PYTHON_MODULE( pykd ) "Print out string and insert end of line symbol. If dml = True string is printed with dml highlighting ( only for windbg )" ) ); // Python debug output console helper classes - python::class_( "dout", "dout", python::no_init ) - .def( "write", &kdlib::DbgOut::write ); - python::class_( "din", "din", python::no_init ) - .def( "readline", &kdlib::DbgIn::readline ); + python::class_( "dout", "dout", python::no_init ) + .def( "write", &DbgOut::write ) + .def( "flush", &DbgOut::flush ) + .add_property( "encoding", &DbgOut::encoding ); + + python::class_( "din", "din", python::no_init ) + .def( "readline", &DbgIn::readline ) + .add_property( "encoding", &DbgIn::encoding ); // system properties python::def( "ptrSize", &kdlib::ptrSize, diff --git a/pykd/windbgext.cpp b/pykd/windbgext.cpp index 2c31a65..2a5ebd0 100644 --- a/pykd/windbgext.cpp +++ b/pykd/windbgext.cpp @@ -9,6 +9,7 @@ namespace python = boost::python; #include "windbgext.h" #include "dbgexcept.h" +#include "pydbgio.h" using namespace kdlib; using namespace kdlib::windbg; @@ -32,19 +33,34 @@ void PykdExt::setUp() PyImport_AppendInittab("pykd", initpykd ); - Py_Initialize(); - PyEval_InitThreads(); - python::import( "pykd" ); + Py_Initialize(); + + python::object main = boost::python::import("__main__"); + + python::object main_namespace = main.attr("__dict__"); + + python::object pykd = python::import( "pykd" ); + + // делаем аналог from pykd import * + python::dict pykd_namespace( pykd.attr("__dict__") ); + + python::list iterkeys( pykd_namespace.iterkeys() ); + + for (int i = 0; i < boost::python::len(iterkeys); i++) + { + std::string key = boost::python::extract(iterkeys[i]); + + main_namespace[ key ] = pykd_namespace[ key ]; + } // перенаправление стандартных потоков ВВ python::object sys = python::import("sys"); - sys.attr("stdout") = python::ptr( dbgout ); - sys.attr("stderr") = python::ptr( dbgout ); - sys.attr("stdin") = python::ptr( dbgin ); - + sys.attr("stdout") = python::object( pykd::DbgOut() ); + sys.attr("stderr") = python::object( pykd::DbgOut() ); + sys.attr("stdin") = python::object( pykd::DbgIn() ); python::list pathList(sys.attr("path")); @@ -52,12 +68,16 @@ void PykdExt::setUp() for (python::ssize_t i = 0; i < n ; i++) m_paths.push_back(boost::python::extract(pathList[i])); + + m_pyState = PyEval_SaveThread(); } /////////////////////////////////////////////////////////////////////////////// void PykdExt::tearDown() { + PyEval_RestoreThread( m_pyState ); + Py_Finalize(); WindbgExtension::tearDown(); @@ -67,45 +87,157 @@ void PykdExt::tearDown() KDLIB_EXT_COMMAND_METHOD_IMPL(PykdExt, py) { - const ArgsList& args = getArgs(); + ArgsList args = getArgs(); + + bool global = false; + bool local = false; + bool clean = false; + + ArgsList::iterator foundArg; + + foundArg = std::find( args.begin(), args.end(), "-h" ); + if ( foundArg != args.end() ) + { + printUsage(); + return; + } + + foundArg = std::find( args.begin(), args.end(), "--help" ); + if ( foundArg != args.end() ) + { + printUsage(); + return; + } + + foundArg = std::find( args.begin(), args.end(), "-g" ); + if ( foundArg != args.end() ) + { + global = true; + args.erase( foundArg ); + } + + foundArg = std::find( args.begin(), args.end(), "--global" ); + if ( foundArg != args.end() ) + { + global = true; + args.erase( foundArg ); + } + + foundArg = std::find( args.begin(), args.end(), "-l" ); + if ( foundArg != args.end() ) + { + local = true; + args.erase( foundArg ); + } + + foundArg = std::find( args.begin(), args.end(), "--local" ); + if ( foundArg != args.end() ) + { + local = true; + args.erase( foundArg ); + } + + if ( global & local ) + { + eprintln( L"-g(--global) and -l(--local) cannot be set together" ); + return; + } + + std::string scriptFileName; + if ( args.size() > 0 ) + { + scriptFileName = getScriptFileName( args[0] ); + + if ( scriptFileName.empty() ) + { + eprintln( L"script file not found" ); + return; + } + + global = !(global | local ) ? false : global ; //set local by default + } + else + { + global = !(global | local ) ? true : global ; //set global by default + } + + PyThreadState *localState = NULL; + PyThreadState *globalState = NULL; + + PyEval_RestoreThread( m_pyState ); + + if ( !global ) + { + globalState = PyThreadState_Swap( NULL ); + + localState = Py_NewInterpreter(); + + python::object sys = python::import("sys"); + + sys.attr("stdout") = python::object( pykd::DbgOut() ); + sys.attr("stderr") = python::object( pykd::DbgOut() ); + sys.attr("stdin") = python::object( pykd::DbgIn() ); + } if ( args.size() == 0 ) { startConsole(); - return; } - - std::string scriptFileName = getScriptFileName( args[0] ); - - if ( scriptFileName.empty() ) + else { - eprintln( L"script file not found" ); - return; + std::string scriptFileName = getScriptFileName( args[0] ); + + // устанавиливаем питоновские аргументы + char **pythonArgs = new char* [ args.size() ]; + + pythonArgs[0] = const_cast(scriptFileName.c_str()); + + for ( size_t i = 1; i < args.size(); ++i ) + pythonArgs[i] = const_cast( args[i].c_str() ); + + PySys_SetArgv( (int)args.size(), pythonArgs ); + + delete[] pythonArgs; + + // получаем достпу к глобальному мапу ( нужен для вызова exec_file ) + python::object main = python::import("__main__"); + + python::object global(main.attr("__dict__")); + + try { + PykdInterruptWatch interruptWatch; + python::exec_file( scriptFileName.c_str(), global ); + } + catch( python::error_already_set const & ) + { + printException(); + } } - // устанавиливаем питоновские аргументы - char **pythonArgs = new char* [ args.size() ]; - - for ( size_t i = 0; i < args.size(); ++i ) - pythonArgs[i] = const_cast( args[i].c_str() ); - - PySys_SetArgv( (int)args.size(), pythonArgs ); - - delete[] pythonArgs; - - // получаем достпу к глобальному мапу ( нужен для вызова exec_file ) - python::object main = python::import("__main__"); - - python::object global(main.attr("__dict__")); - - try { - PykdInterruptWatch interruptWatch; - python::exec_file( scriptFileName.c_str(), global ); - } - catch( python::error_already_set const & ) + if ( !global ) { - printException(); + PyInterpreterState *interpreter = localState->interp; + + while( interpreter->tstate_head != NULL ) + { + PyThreadState *threadState = (PyThreadState*)(interpreter->tstate_head); + + PyThreadState_Clear(threadState); + + PyThreadState_Swap( NULL ); + + PyThreadState_Delete(threadState); + } + + PyInterpreterState_Clear(interpreter); + + PyInterpreterState_Delete(interpreter); + + PyThreadState_Swap( globalState ); } + + m_pyState = PyEval_SaveThread(); + } /////////////////////////////////////////////////////////////////////////////// @@ -130,6 +262,16 @@ void PykdExt::startConsole() /////////////////////////////////////////////////////////////////////////////// +void PykdExt::printUsage() +{ + dprintln( L"usage: !py [options] [file]" ); + dprintln( L"Options:" ); + dprintln( L"-g --global : run code in the common namespace" ); + dprintln( L"-l --local : run code in the isolate namespace" ); +} + +/////////////////////////////////////////////////////////////////////////////// + std::string PykdExt::getScriptFileName( const std::string &scriptName ) { bool fileHasPyExt = false; diff --git a/pykd/windbgext.h b/pykd/windbgext.h index b0bb411..876f279 100644 --- a/pykd/windbgext.h +++ b/pykd/windbgext.h @@ -18,6 +18,8 @@ private: void startConsole(); + void printUsage(); + virtual void setUp(); virtual void tearDown(); @@ -25,6 +27,8 @@ private: std::string getScriptFileName( const std::string &scriptName ); std::vector m_paths; + + PyThreadState *m_pyState; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/snippets/ipython.py b/snippets/ipython.py new file mode 100644 index 0000000..c1e9109 --- /dev/null +++ b/snippets/ipython.py @@ -0,0 +1,22 @@ +from IPython.config.loader import Config +from IPython.terminal.embed import InteractiveShellEmbed + +from IPython.terminal.interactiveshell import TerminalInteractiveShell + +import pykd + +cfg = Config() + +cfg.InteractiveShell.colors = 'NoColor' +cfg.InteractiveShell.readline_use = False +cfg.InteractiveShell.autoindent = True + +cfg.PromptManager.in_template = 'In <\\#>: ' +cfg.PromptManager.in2_template = ' .\\D.: ' +cfg.PromptManager.out_template = 'Out<\\#>: ' + +cfg.InteractiveShellApp.extensions = [ 'pykdmagic' ] + +ipshell = InteractiveShellEmbed(config=cfg) + +ipshell() \ No newline at end of file