mirror of
https://github.com/ivellioscolin/pykd.git
synced 2025-04-20 03:23:23 +08:00
[0.1.x] added : simple "simple debugger"
git-svn-id: https://pykd.svn.codeplex.com/svn@75462 9b283d60-5439-405e-af05-b73fd8c4d996
This commit is contained in:
parent
b16dc51fd0
commit
78fbe47e76
242
samples/dbg/PySideKick/Call.py
Normal file
242
samples/dbg/PySideKick/Call.py
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
# Copyright (c) 2009-2010, Cloud Matrix Pty. Ltd.
|
||||||
|
# All rights reserved; available under the terms of the BSD License.
|
||||||
|
"""
|
||||||
|
|
||||||
|
PySideKick.Call: helpers for managing function calls
|
||||||
|
=====================================================
|
||||||
|
|
||||||
|
|
||||||
|
This module defines a collection of helpers for managing function calls in
|
||||||
|
cooperation with the Qt event loop. We have:
|
||||||
|
|
||||||
|
* qCallAfter: call function after current event has been processed
|
||||||
|
* qCallLater: call function after sleeping for some interval
|
||||||
|
* qCallInMainThread: (blockingly) call function in the main GUI thread
|
||||||
|
* qCallInWorkerThread: (nonblockingly) call function in worker thread
|
||||||
|
|
||||||
|
|
||||||
|
There is also a decorator to apply these helpers to all calls to a function:
|
||||||
|
|
||||||
|
* qCallUsing(helper): route all calls to a function through the helper
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import thread
|
||||||
|
import threading
|
||||||
|
from functools import wraps
|
||||||
|
import Queue
|
||||||
|
|
||||||
|
import PySideKick
|
||||||
|
from PySideKick import QtCore, qIsMainThread
|
||||||
|
|
||||||
|
|
||||||
|
class qCallAfter(QtCore.QObject):
|
||||||
|
"""Call the given function on a subsequent iteration of the event loop.
|
||||||
|
|
||||||
|
This helper arranges for the given function to be called on a subsequent
|
||||||
|
iteration of the main event loop. It's most useful inside event handlers,
|
||||||
|
where you may want to defer some work until after the event has finished
|
||||||
|
being processed.
|
||||||
|
|
||||||
|
The implementation is as a singleton QObject subclass. It maintains a
|
||||||
|
queue of functions to the called, and posts an event to itself whenever
|
||||||
|
a new function is queued.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(qCallAfter,self).__init__(None)
|
||||||
|
self.app = None
|
||||||
|
self.event_id = QtCore.QEvent.registerEventType()
|
||||||
|
self.event_type = QtCore.QEvent.Type(self.event_id)
|
||||||
|
self.pending_func_queue = Queue.Queue()
|
||||||
|
self.func_queue = Queue.Queue()
|
||||||
|
|
||||||
|
def customEvent(self,event):
|
||||||
|
if event.type() == self.event_type:
|
||||||
|
self._popCall()
|
||||||
|
|
||||||
|
def __call__(self,func,*args,**kwds):
|
||||||
|
global qCallAfter
|
||||||
|
# If the app is running, dispatch the event directly.
|
||||||
|
if self.app is not None:
|
||||||
|
self.func_queue.put((func,args,kwds))
|
||||||
|
self._postEvent()
|
||||||
|
return
|
||||||
|
# Otherwise, we have some bootstrapping to do!
|
||||||
|
# Before dispatching, there must be a running app and we must
|
||||||
|
# be on the same thread as it.
|
||||||
|
app = QtCore.QCoreApplication.instance()
|
||||||
|
if app is None or not qIsMainThread():
|
||||||
|
self.pending_func_queue.put((func,args,kwds))
|
||||||
|
return
|
||||||
|
# This is the first call with a running app and from the
|
||||||
|
# main thread. If it turns out we're not on the main thread,
|
||||||
|
# replace ourselves with a fresh instance.
|
||||||
|
if hasattr(self,"thread"):
|
||||||
|
if self.thread() is not QtCore.QThread.currentThread():
|
||||||
|
qCallAfter = self.__class__()
|
||||||
|
else:
|
||||||
|
self.app = app
|
||||||
|
else:
|
||||||
|
self.app = app
|
||||||
|
# OK, we now have the official qCallAfter instance.
|
||||||
|
# Flush all pending events.
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
(pfunc,pargs,pkwds) = self.pending_func_queue.get(False)
|
||||||
|
qCallAfter(pfunc,*pargs,**pkwds)
|
||||||
|
except Queue.Empty:
|
||||||
|
pass
|
||||||
|
qCallAfter(func,*args,**kwds)
|
||||||
|
|
||||||
|
def _popCall(self):
|
||||||
|
(func,args,kwds) = self.func_queue.get(False)
|
||||||
|
func(*args,**kwds)
|
||||||
|
|
||||||
|
def _postEvent(self):
|
||||||
|
event = QtCore.QEvent(self.event_type)
|
||||||
|
try:
|
||||||
|
self.app.postEvent(self,event)
|
||||||
|
except RuntimeError:
|
||||||
|
# This can happen if the app has been destroyed.
|
||||||
|
# Immediately empty the queue.
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
self._popCall()
|
||||||
|
except Queue.Empty:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Optimistically create the singleton instance of qCallAfter.
|
||||||
|
# If this module is imported from a non-gui thread then this instance will
|
||||||
|
# eventually be replaced, but usually it'll be the correct one.
|
||||||
|
qCallAfter = qCallAfter()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Future(object):
|
||||||
|
"""Primative "future" class for executing functions in another thread.
|
||||||
|
|
||||||
|
Instances of this class represent a compuation sent to another thread.
|
||||||
|
Call the "get_result" method to wait until completion and get the result
|
||||||
|
(or raise the exception).
|
||||||
|
|
||||||
|
Existing Future objects are recycled to avoid the overhead of allocating
|
||||||
|
a new lock primitive for each async call.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_READY_INSTANCES = []
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.ready = threading.Event()
|
||||||
|
self.result = None
|
||||||
|
self.exception = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_or_create(cls):
|
||||||
|
try:
|
||||||
|
return cls._READY_INSTANCES.pop(0)
|
||||||
|
except IndexError:
|
||||||
|
return cls()
|
||||||
|
|
||||||
|
def recycle(self):
|
||||||
|
self.result = None
|
||||||
|
self.exception = None
|
||||||
|
self.ready.clear()
|
||||||
|
self._READY_INSTANCES.append(self)
|
||||||
|
|
||||||
|
def call_function(self,func,*args,**kwds):
|
||||||
|
try:
|
||||||
|
self.result = func(*args,**kwds)
|
||||||
|
except Exception:
|
||||||
|
self.exception = sys.exc_info()
|
||||||
|
finally:
|
||||||
|
self.ready.set()
|
||||||
|
|
||||||
|
def get_result(self):
|
||||||
|
self.ready.wait()
|
||||||
|
try:
|
||||||
|
if self.exception is None:
|
||||||
|
return self.result
|
||||||
|
else:
|
||||||
|
raise self.exception[0], \
|
||||||
|
self.exception[1], \
|
||||||
|
self.exception[2]
|
||||||
|
finally:
|
||||||
|
self.recycle()
|
||||||
|
|
||||||
|
def is_ready(self):
|
||||||
|
return self.read.isSet()
|
||||||
|
|
||||||
|
|
||||||
|
def qCallInMainThread(func,*args,**kwds):
|
||||||
|
"""Synchronously call the given function in the main thread.
|
||||||
|
|
||||||
|
This helper arranges for the given function to be called in the main
|
||||||
|
event loop, then blocks and waits for the result. It's a simple way to
|
||||||
|
call functions that manipulate the GUI from a non-GUI thread.
|
||||||
|
"""
|
||||||
|
if qIsMainThread():
|
||||||
|
func(*args,**kwds)
|
||||||
|
else:
|
||||||
|
future = Future.get_or_create()
|
||||||
|
qCallAfter(future.call_function,func,*args,**kwds)
|
||||||
|
return future.get_result()
|
||||||
|
|
||||||
|
|
||||||
|
def qCallInWorkerThread(func,*args,**kwds):
|
||||||
|
"""Asynchronously call the given function in a background worker thread.
|
||||||
|
|
||||||
|
This helper arranges for the given function to be executed by a background
|
||||||
|
worker thread. Eventually it'll get a thread pool; for now each task
|
||||||
|
spawns a new background thread.
|
||||||
|
|
||||||
|
If you need to know the result of the function call, this helper returns
|
||||||
|
a Future object; use f.is_ready() to test whether it's ready and call
|
||||||
|
f.get_result() to get the return value or raise the exception.
|
||||||
|
"""
|
||||||
|
future = Future.get_or_create()
|
||||||
|
def runit():
|
||||||
|
future.call_function(func,*args,**kwds)
|
||||||
|
threading.Thread(target=runit).start()
|
||||||
|
return future
|
||||||
|
|
||||||
|
|
||||||
|
def qCallLater(interval,func,*args,**kwds):
|
||||||
|
"""Asynchronously call the given function after a timeout.
|
||||||
|
|
||||||
|
This helper is similar to qCallAfter, but it waits at least 'interval'
|
||||||
|
seconds before executing the function. To cancel the call before the
|
||||||
|
sleep interval has expired, call 'cancel' on the returned object.
|
||||||
|
|
||||||
|
Currently this is a thin wrapper around threading.Timer; eventually it
|
||||||
|
will be integrated with Qt's own timer mechanisms.
|
||||||
|
"""
|
||||||
|
def runit():
|
||||||
|
qCallAfter(func,*args,**kwds)
|
||||||
|
t = threading.Timer(interval,runit)
|
||||||
|
t.start()
|
||||||
|
return t
|
||||||
|
|
||||||
|
|
||||||
|
def qCallUsing(helper):
|
||||||
|
"""Function/method decorator to always apply a function call helper.
|
||||||
|
|
||||||
|
This decorator can be used to ensure that a function is always called
|
||||||
|
using one of the qCall helpers. For example, the following function can
|
||||||
|
be safely called from any thread, as it will transparently apply the
|
||||||
|
qCallInMainThread helper whenever it is called:
|
||||||
|
|
||||||
|
@qCallUsing(qCallInMainThread)
|
||||||
|
def prompt_for_input(msg):
|
||||||
|
# ... pop up a dialog, return input
|
||||||
|
|
||||||
|
"""
|
||||||
|
def decorator(func):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args,**kwds):
|
||||||
|
return helper(func,*args,**kwds)
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
143
samples/dbg/PySideKick/Console.py
Normal file
143
samples/dbg/PySideKick/Console.py
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
# Copyright (c) 2009-2010, Cloud Matrix Pty. Ltd.
|
||||||
|
# All rights reserved; available under the terms of the BSD License.
|
||||||
|
"""
|
||||||
|
PySideKick.Console: a simple embeddable python shell
|
||||||
|
=====================================================
|
||||||
|
|
||||||
|
|
||||||
|
This module provides the call QPythonConsole, a python shell that can be
|
||||||
|
embedded in your GUI.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from code import InteractiveConsole as _InteractiveConsole
|
||||||
|
|
||||||
|
from PySideKick import QtCore, QtGui
|
||||||
|
|
||||||
|
try:
|
||||||
|
from cStringIO import StringIO
|
||||||
|
except ImportError:
|
||||||
|
from StringIO import StringIO
|
||||||
|
|
||||||
|
|
||||||
|
class _QPythonConsoleInterpreter(_InteractiveConsole):
|
||||||
|
"""InteractiveConsole subclass that sends all output to the GUI."""
|
||||||
|
|
||||||
|
def __init__(self,ui,locals=None):
|
||||||
|
_InteractiveConsole.__init__(self,locals)
|
||||||
|
self.ui = ui
|
||||||
|
|
||||||
|
def write(self,data):
|
||||||
|
if data:
|
||||||
|
if data[-1] == "\n":
|
||||||
|
data = data[:-1]
|
||||||
|
self.ui.output.appendPlainText(data)
|
||||||
|
|
||||||
|
def runsource(self,source,filename="<input>",symbol="single"):
|
||||||
|
old_stdout = sys.stdout
|
||||||
|
old_stderr = sys.stderr
|
||||||
|
sys.stdout = sys.stderr = collector = StringIO()
|
||||||
|
try:
|
||||||
|
more = _InteractiveConsole.runsource(self,source,filename,symbol)
|
||||||
|
finally:
|
||||||
|
if sys.stdout is collector:
|
||||||
|
sys.stdout = old_stdout
|
||||||
|
if sys.stderr is collector:
|
||||||
|
sys.stderr = old_stderr
|
||||||
|
self.write(collector.getvalue())
|
||||||
|
return more
|
||||||
|
|
||||||
|
|
||||||
|
class _QPythonConsoleUI(object):
|
||||||
|
"""UI layout container for QPythonConsole."""
|
||||||
|
def __init__(self,parent):
|
||||||
|
if parent.layout() is None:
|
||||||
|
parent.setLayout(QtGui.QHBoxLayout())
|
||||||
|
layout = QtGui.QVBoxLayout()
|
||||||
|
layout.setSpacing(0)
|
||||||
|
# Output console: a fixed-pitch-font text area.
|
||||||
|
self.output = QtGui.QPlainTextEdit(parent)
|
||||||
|
self.output.setReadOnly(True)
|
||||||
|
self.output.setUndoRedoEnabled(False)
|
||||||
|
self.output.setMaximumBlockCount(5000)
|
||||||
|
fmt = QtGui.QTextCharFormat()
|
||||||
|
fmt.setFontFixedPitch(True)
|
||||||
|
self.output.setCurrentCharFormat(fmt)
|
||||||
|
layout.addWidget(self.output)
|
||||||
|
parent.layout().addLayout(layout)
|
||||||
|
# Input console, a prompt displated next to a lineedit
|
||||||
|
layout2 = QtGui.QHBoxLayout()
|
||||||
|
self.prompt = QtGui.QLabel(parent)
|
||||||
|
self.prompt.setText(">>> ")
|
||||||
|
layout2.addWidget(self.prompt)
|
||||||
|
self.input = QtGui.QLineEdit(parent)
|
||||||
|
layout2.addWidget(self.input)
|
||||||
|
layout.addLayout(layout2)
|
||||||
|
|
||||||
|
|
||||||
|
class QPythonConsole(QtGui.QWidget):
|
||||||
|
"""A simple python console to embed in your GUI.
|
||||||
|
|
||||||
|
This widget provides a simple interactive python console that you can
|
||||||
|
embed in your GUI (e.g. for debugging purposes). Use it like so:
|
||||||
|
|
||||||
|
self.debug_window.layout().addWidget(QPythonConsole())
|
||||||
|
|
||||||
|
You can customize the variables that are available in the shell by
|
||||||
|
passing a dict as the "locals" argument.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self,parent=None,locals=None):
|
||||||
|
super(QPythonConsole,self).__init__(parent)
|
||||||
|
self.ui = _QPythonConsoleUI(self)
|
||||||
|
self.interpreter = _QPythonConsoleInterpreter(self.ui,locals)
|
||||||
|
self.ui.input.returnPressed.connect(self._on_enter_line)
|
||||||
|
self.ui.input.installEventFilter(self)
|
||||||
|
self.history = []
|
||||||
|
self.history_pos = 0
|
||||||
|
|
||||||
|
def _on_enter_line(self):
|
||||||
|
line = self.ui.input.text()
|
||||||
|
self.ui.input.setText("")
|
||||||
|
self.interpreter.write(self.ui.prompt.text() + line)
|
||||||
|
more = self.interpreter.push(line)
|
||||||
|
if line:
|
||||||
|
self.history.append(line)
|
||||||
|
self.history_pos = len(self.history)
|
||||||
|
while len(self.history) > 100:
|
||||||
|
self.history = self.history[1:]
|
||||||
|
self.history_pos -= 1
|
||||||
|
if more:
|
||||||
|
self.ui.prompt.setText("... ")
|
||||||
|
else:
|
||||||
|
self.ui.prompt.setText(">>> ")
|
||||||
|
|
||||||
|
def eventFilter(self,obj,event):
|
||||||
|
if event.type() == QtCore.QEvent.KeyPress:
|
||||||
|
if event.key() == QtCore.Qt.Key_Up:
|
||||||
|
self.go_history(-1)
|
||||||
|
elif event.key() == QtCore.Qt.Key_Down:
|
||||||
|
self.go_history(1)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def go_history(self,offset):
|
||||||
|
if offset < 0:
|
||||||
|
self.history_pos = max(0,self.history_pos + offset)
|
||||||
|
elif offset > 0:
|
||||||
|
self.history_pos = min(len(self.history),self.history_pos + offset)
|
||||||
|
try:
|
||||||
|
line = self.history[self.history_pos]
|
||||||
|
except IndexError:
|
||||||
|
line = ""
|
||||||
|
self.ui.input.setText(line)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys, os
|
||||||
|
app = QtGui.QApplication(sys.argv)
|
||||||
|
win = QtGui.QMainWindow()
|
||||||
|
win.setCentralWidget(QPythonConsole())
|
||||||
|
win.show()
|
||||||
|
app.exec_()
|
||||||
|
|
1467
samples/dbg/PySideKick/Hatchet.py
Normal file
1467
samples/dbg/PySideKick/Hatchet.py
Normal file
File diff suppressed because it is too large
Load Diff
56
samples/dbg/PySideKick/__init__.py
Normal file
56
samples/dbg/PySideKick/__init__.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# Copyright (c) 2009-2010, Cloud Matrix Pty. Ltd.
|
||||||
|
# All rights reserved; available under the terms of the BSD License.
|
||||||
|
"""
|
||||||
|
|
||||||
|
PySideKick: helpful utilities for working with PySide
|
||||||
|
======================================================
|
||||||
|
|
||||||
|
|
||||||
|
This package is a rather ad-hoc collection of helpers, utilities and custom
|
||||||
|
widgets for building applications with PySide. So far we have:
|
||||||
|
|
||||||
|
* PySideKick.Call: helpers for calling functions in a variety of ways,
|
||||||
|
e.g. qCallAfter, qCallInMainThread
|
||||||
|
|
||||||
|
* PySideKick.Console: a simple interactive console to embed in your
|
||||||
|
application
|
||||||
|
|
||||||
|
* PySideKick.Hatchet: a tool for hacking frozen PySide apps down to size,
|
||||||
|
by rebuilding PySide with a minimal set of classes
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
__ver_major__ = 0
|
||||||
|
__ver_minor__ = 2
|
||||||
|
__ver_patch__ = 3
|
||||||
|
__ver_sub__ = ""
|
||||||
|
__ver_tuple__ = (__ver_major__,__ver_minor__,__ver_patch__,__ver_sub__)
|
||||||
|
__version__ = "%d.%d.%d%s" % __ver_tuple__
|
||||||
|
|
||||||
|
|
||||||
|
import thread
|
||||||
|
|
||||||
|
from PySide import QtCore, QtGui
|
||||||
|
from PySide.QtCore import Qt
|
||||||
|
|
||||||
|
|
||||||
|
# Older versions of PySide don't expose the 'thread' attribute of QObject.
|
||||||
|
# In this case, assume the thread importing this module is the main thread.
|
||||||
|
if hasattr(QtCore.QCoreApplication,"thread"):
|
||||||
|
def qIsMainThread():
|
||||||
|
app = QtCore.QCoreApplication.instance()
|
||||||
|
if app is None:
|
||||||
|
return False
|
||||||
|
return QtCore.QThread.currentThread() is app.thread()
|
||||||
|
else:
|
||||||
|
_MAIN_THREAD_ID = thread.get_ident()
|
||||||
|
def qIsMainThread():
|
||||||
|
return thread.get_ident() == _MAIN_THREAD_ID
|
||||||
|
|
||||||
|
|
||||||
|
# PySideKick.Call needs to create a singleton object in the main gui thread.
|
||||||
|
# Since this is *usually* the thread that imports this module, loading it here
|
||||||
|
# will provide a small speedup in the common case.
|
||||||
|
import PySideKick.Call
|
||||||
|
|
||||||
|
|
0
samples/dbg/commands/__init__.py
Normal file
0
samples/dbg/commands/__init__.py
Normal file
54
samples/dbg/commands/dbgcmd.py
Normal file
54
samples/dbg/commands/dbgcmd.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
from widget import *
|
||||||
|
|
||||||
|
class DebuggerController(BaseController):
|
||||||
|
|
||||||
|
def __init__(self,dbgCore,mainWindow):
|
||||||
|
BaseController.__init__(self,dbgCore,mainWindow)
|
||||||
|
debugMenu = QMenu( "Debug" )
|
||||||
|
|
||||||
|
self.breakAction = QAction("Break", debugMenu )
|
||||||
|
self.breakAction.triggered.connect( self.onBreak )
|
||||||
|
self.breakAction.setDisabled(True)
|
||||||
|
debugMenu.addAction( self.breakAction )
|
||||||
|
|
||||||
|
self.goAction = QAction("Go", debugMenu )
|
||||||
|
self.goAction.triggered.connect( self.onGo )
|
||||||
|
self.goAction.setDisabled(True)
|
||||||
|
debugMenu.addAction( self.goAction )
|
||||||
|
|
||||||
|
self.stepAction = QAction("Step", debugMenu )
|
||||||
|
self.stepAction.triggered.connect( self.onStep )
|
||||||
|
self.stepAction.setDisabled(True)
|
||||||
|
debugMenu.addAction( self.stepAction )
|
||||||
|
|
||||||
|
mainWindow.menuBar().addMenu( debugMenu )
|
||||||
|
|
||||||
|
def onBreak( self ):
|
||||||
|
self.dbgCore.breakin()
|
||||||
|
|
||||||
|
def onGo( self ):
|
||||||
|
self.dbgCore.go()
|
||||||
|
|
||||||
|
def onStep( self ):
|
||||||
|
self.dbgCore.step()
|
||||||
|
|
||||||
|
def onDbgBreak(self):
|
||||||
|
self.breakAction.setDisabled(True)
|
||||||
|
self.goAction.setDisabled(False)
|
||||||
|
self.stepAction.setDisabled(False)
|
||||||
|
|
||||||
|
def onDbgRun(self):
|
||||||
|
self.breakAction.setDisabled(False)
|
||||||
|
self.goAction.setDisabled(True)
|
||||||
|
self.stepAction.setDisabled(True)
|
||||||
|
|
||||||
|
def onDbgAttach(self):
|
||||||
|
self.breakAction.setDisabled(False)
|
||||||
|
self.goAction.setDisabled(False)
|
||||||
|
self.stepAction.setDisabled(False)
|
||||||
|
|
||||||
|
def onDbgDetach(self):
|
||||||
|
self.breakAction.setDisabled(False)
|
||||||
|
self.goAction.setDisabled(False)
|
||||||
|
self.stepAction.setDisabled(False)
|
||||||
|
|
38
samples/dbg/commands/processcmd.py
Normal file
38
samples/dbg/commands/processcmd.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
from widget import *
|
||||||
|
|
||||||
|
class ProcessController(BaseController):
|
||||||
|
|
||||||
|
def __init__(self, dbgCore, mainWindow):
|
||||||
|
BaseController.__init__(self,dbgCore,mainWindow)
|
||||||
|
|
||||||
|
self.openProcessAction = QAction( "Open process...", mainWindow.fileMenu )
|
||||||
|
self.openProcessAction.triggered.connect(self.onOpenProcess)
|
||||||
|
mainWindow.fileMenu.addAction(self.openProcessAction)
|
||||||
|
|
||||||
|
self.detachProcessAction = QAction( "Detach process", mainWindow.fileMenu )
|
||||||
|
self.detachProcessAction.triggered.connect(self.onDetachProcess)
|
||||||
|
self.detachProcessAction.setDisabled(True)
|
||||||
|
mainWindow.fileMenu.addAction(self.detachProcessAction)
|
||||||
|
|
||||||
|
def onOpenProcess(self):
|
||||||
|
fileDlg = QFileDialog( self.mainWnd )
|
||||||
|
fileDlg.setNameFilter( "Executable (*.exe)" )
|
||||||
|
self.dbgCore.openProcess( fileDlg.getOpenFileName()[0] )
|
||||||
|
|
||||||
|
def onDetachProcess(self):
|
||||||
|
self.dbgCore.detachProcess()
|
||||||
|
|
||||||
|
def onDbgAttach(self):
|
||||||
|
self.openProcessAction.setDisabled(True)
|
||||||
|
self.detachProcessAction.setDisabled(True)
|
||||||
|
|
||||||
|
def onDbgDetach(self):
|
||||||
|
self.openProcessAction.setDisabled(False)
|
||||||
|
self.detachProcessAction.setDisabled(True)
|
||||||
|
|
||||||
|
def onDbgBreak(self):
|
||||||
|
self.detachProcessAction.setDisabled(False)
|
||||||
|
|
||||||
|
def onDbgRun(self):
|
||||||
|
self.detachProcessAction.setDisabled(True)
|
40
samples/dbg/dbg.py
Normal file
40
samples/dbg/dbg.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
from PySide.QtCore import *
|
||||||
|
from PySide.QtGui import *
|
||||||
|
|
||||||
|
|
||||||
|
from dbgcore import DbgCore
|
||||||
|
from settings import settings
|
||||||
|
|
||||||
|
class MainForm(QMainWindow):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
QMainWindow.__init__(self, None)
|
||||||
|
self.resize( 800, 600 )
|
||||||
|
self.setWindowTitle("Pykd Debugger Sample")
|
||||||
|
self.setDockNestingEnabled( True )
|
||||||
|
|
||||||
|
self.dbgCore = DbgCore()
|
||||||
|
|
||||||
|
self.fileMenu = QMenu( "&File" )
|
||||||
|
self.menuBar().addMenu( self.fileMenu )
|
||||||
|
|
||||||
|
self.viewMenu = QMenu( "View" )
|
||||||
|
self.menuBar().addMenu( self.viewMenu )
|
||||||
|
|
||||||
|
self.widgets = settings["default"](self.dbgCore, self )
|
||||||
|
|
||||||
|
self.fileMenu.addAction( "Exit", self.onExit )
|
||||||
|
|
||||||
|
def onExit(self):
|
||||||
|
self.dbgCore.close()
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
app = QApplication( [] )
|
||||||
|
mainForm = MainForm()
|
||||||
|
mainForm.show()
|
||||||
|
exitres = app.exec_()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
70
samples/dbg/dbgcore.py
Normal file
70
samples/dbg/dbgcore.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
|
||||||
|
import pykd
|
||||||
|
|
||||||
|
from PySide.QtCore import QThread
|
||||||
|
from PySide.QtCore import QObject
|
||||||
|
from PySide.QtCore import Signal
|
||||||
|
|
||||||
|
class DbgThread( QThread ):
|
||||||
|
|
||||||
|
def __init__(self, func):
|
||||||
|
QThread.__init__(self)
|
||||||
|
self.func = func
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.func()
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
class DbgCore( QObject ):
|
||||||
|
|
||||||
|
targetBreak = Signal()
|
||||||
|
targetRunning = Signal()
|
||||||
|
targetAttached = Signal()
|
||||||
|
targetDetached = Signal()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.processOpened:
|
||||||
|
if self.thread != None:
|
||||||
|
self.breakin()
|
||||||
|
|
||||||
|
def openProcess( self, name ):
|
||||||
|
pykd.startProcess( name )
|
||||||
|
self.processOpened = True
|
||||||
|
self.targetAttached.emit()
|
||||||
|
self.targetBreak.emit()
|
||||||
|
|
||||||
|
def detachProcess(self):
|
||||||
|
pykd.detachProcess()
|
||||||
|
self.processOpened = False
|
||||||
|
self.targetDetached.emit()
|
||||||
|
|
||||||
|
def killProcess(self):
|
||||||
|
pykd.killProcess()
|
||||||
|
self.processOpened = False
|
||||||
|
self.targetDetached.emit()
|
||||||
|
|
||||||
|
|
||||||
|
def breakin( self ):
|
||||||
|
pykd.breakin()
|
||||||
|
|
||||||
|
def go( self ):
|
||||||
|
self.thread = DbgThread( pykd.go )
|
||||||
|
self.thread.finished.connect( self.onDebugStop )
|
||||||
|
self.targetRunning.emit()
|
||||||
|
self.thread.start()
|
||||||
|
|
||||||
|
def step( self ):
|
||||||
|
self.thread = DbgThread( pykd.step )
|
||||||
|
self.thread.finished.connect( self.onDebugStop )
|
||||||
|
self.targetRunning.emit()
|
||||||
|
self.thread.start()
|
||||||
|
|
||||||
|
def onDebugStop(self):
|
||||||
|
self.thread.wait(100)
|
||||||
|
self.thread = None
|
||||||
|
self.targetBreak.emit()
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
QObject.__init__(self)
|
||||||
|
self.thread = None
|
||||||
|
self.processOpened = False
|
18
samples/dbg/settings.py
Normal file
18
samples/dbg/settings.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
from commands.processcmd import ProcessController
|
||||||
|
from commands.dbgcmd import DebuggerController
|
||||||
|
|
||||||
|
from widgets.registers import RegisterWidget
|
||||||
|
from widgets.cmd import CmdWidget
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultSettings:
|
||||||
|
|
||||||
|
def __init__( self, dbgcore, parentwnd ):
|
||||||
|
self.processCmd = ProcessController( dbgcore, parentwnd )
|
||||||
|
self.debugCmd = DebuggerController( dbgcore, parentwnd )
|
||||||
|
|
||||||
|
self.regWidget = RegisterWidget( dbgcore, parentwnd )
|
||||||
|
self.cmdWidget = CmdWidget( dbgcore, parentwnd )
|
||||||
|
|
||||||
|
settings = { "default" : DefaultSettings }
|
97
samples/dbg/widget.py
Normal file
97
samples/dbg/widget.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
|
||||||
|
from PySide.QtCore import *
|
||||||
|
from PySide.QtGui import *
|
||||||
|
|
||||||
|
class BaseWidget( QDockWidget ):
|
||||||
|
|
||||||
|
def __init__( self, dbgCore, mainWindow, title = "", visible = False ):
|
||||||
|
QDockWidget.__init__( self )
|
||||||
|
self.setWindowTitle( title )
|
||||||
|
self.setVisible( visible )
|
||||||
|
mainWindow.addDockWidget( Qt.LeftDockWidgetArea, self )
|
||||||
|
|
||||||
|
self.dbgCore=dbgCore
|
||||||
|
self.mainWnd = mainWindow
|
||||||
|
dbgCore.targetBreak.connect( self.onDbgBreak )
|
||||||
|
dbgCore.targetRunning.connect( self.onDbgRun )
|
||||||
|
dbgCore.targetAttached.connect( self.onDbgAttach )
|
||||||
|
dbgCore.targetDetached.connect( self.onDbgDetach )
|
||||||
|
|
||||||
|
def addMenuTriggerAction( self, actionName ):
|
||||||
|
self.action = QAction( actionName, self.mainWnd )
|
||||||
|
self.action.triggered.connect(self.onTriggerAction)
|
||||||
|
self.action.setDisabled( True )
|
||||||
|
self.mainWnd.viewMenu.addAction(self.action)
|
||||||
|
|
||||||
|
def onDbgBreak(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onDbgRun(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onDbgAttach(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onDbgDetach(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onTriggerAction(self):
|
||||||
|
self.setVisible( not self.isVisible() )
|
||||||
|
|
||||||
|
|
||||||
|
class DebugWidget( BaseWidget ):
|
||||||
|
|
||||||
|
def __init__( self, dbgCore, mainWindow, title = "", visible = False ):
|
||||||
|
BaseWidget.__init__( self, dbgCore, mainWindow, title, visible )
|
||||||
|
self.action = None
|
||||||
|
|
||||||
|
def onDbgAttach(self):
|
||||||
|
if self.action != None:
|
||||||
|
self.action.setDisabled( True )
|
||||||
|
self.setDisabled( True )
|
||||||
|
|
||||||
|
def onDbgDetach(self):
|
||||||
|
if self.action != None:
|
||||||
|
self.action.setDisabled( True )
|
||||||
|
self.setDisabled( True )
|
||||||
|
|
||||||
|
def onDbgBreak(self):
|
||||||
|
if self.action != None:
|
||||||
|
self.action.setDisabled( False )
|
||||||
|
self.setDisabled( False )
|
||||||
|
self.updateView()
|
||||||
|
|
||||||
|
def onDbgRun(self):
|
||||||
|
if self.action != None:
|
||||||
|
self.action.setDisabled( True )
|
||||||
|
self.setDisabled( True )
|
||||||
|
|
||||||
|
def updateView(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BaseController(QObject):
|
||||||
|
|
||||||
|
def __init__(self,dbgCore,mainWindow):
|
||||||
|
QObject.__init__(self,mainWindow)
|
||||||
|
self.dbgCore=dbgCore
|
||||||
|
self.mainWnd = mainWindow
|
||||||
|
dbgCore.targetBreak.connect( self.onDbgBreak )
|
||||||
|
dbgCore.targetRunning.connect( self.onDbgRun )
|
||||||
|
dbgCore.targetAttached.connect( self.onDbgAttach )
|
||||||
|
dbgCore.targetDetached.connect( self.onDbgDetach )
|
||||||
|
|
||||||
|
def onDbgBreak(self):
|
||||||
|
self.onStateChange()
|
||||||
|
|
||||||
|
def onDbgRun(self):
|
||||||
|
self.onStateChange()
|
||||||
|
|
||||||
|
def onDbgAttach(self):
|
||||||
|
self.onStateChange()
|
||||||
|
|
||||||
|
def onDbgDetach(self):
|
||||||
|
self.onStateChange()
|
||||||
|
|
||||||
|
def onStateChange(self):
|
||||||
|
pass
|
0
samples/dbg/widgets/__init__.py
Normal file
0
samples/dbg/widgets/__init__.py
Normal file
23
samples/dbg/widgets/cmd.py
Normal file
23
samples/dbg/widgets/cmd.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
from widget import *
|
||||||
|
from PySideKick.Console import QPythonConsole
|
||||||
|
|
||||||
|
class CmdWidget( DebugWidget ):
|
||||||
|
|
||||||
|
def __init__(self, dbgCore, mainWindow, visible = False ):
|
||||||
|
BaseWidget.__init__( self, dbgCore, mainWindow, "Commands", visible )
|
||||||
|
|
||||||
|
self.addMenuTriggerAction( "Commands" )
|
||||||
|
self.console = QPythonConsole()
|
||||||
|
self.setWidget( self.console )
|
||||||
|
self.console.interpreter.push("from pykd import *")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
40
samples/dbg/widgets/registers.py
Normal file
40
samples/dbg/widgets/registers.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
from widget import *
|
||||||
|
import pykd
|
||||||
|
|
||||||
|
class RegisterWidget( DebugWidget ):
|
||||||
|
|
||||||
|
def __init__(self, dbgCore, mainWindow, visible = False ):
|
||||||
|
BaseWidget.__init__( self, dbgCore, mainWindow, "Registers", visible )
|
||||||
|
|
||||||
|
self.addMenuTriggerAction( "Registers" )
|
||||||
|
|
||||||
|
self.textArea = QTextEdit()
|
||||||
|
self.setWidget( self.textArea )
|
||||||
|
|
||||||
|
def updateView(self):
|
||||||
|
|
||||||
|
s = ""
|
||||||
|
|
||||||
|
try:
|
||||||
|
i = 0
|
||||||
|
while True:
|
||||||
|
reg = pykd.reg(i)
|
||||||
|
s += "%s %x ( %d )\r\n" % ( reg.name(), reg, reg )
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
except pykd.BaseException:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.textArea.setPlainText( s )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user