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