Fix before_prompt event on old GDB versions (#464)

* Fix before_prompt event on old GDB versions

This adds an `EventWrapper` class which behaves similar to gdb events but lets us:
* check whether event is a real gdb event or not
* call event callbacks if it is not a real gdb event

* Better comment
pull/465/head
Disconnect3d 8 years ago committed by GitHub
parent 9fd5d35712
commit 7d77bf7004
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -39,12 +39,15 @@ class StartEvent(object):
def __init__(self):
self.registered = list()
self.running = False
def connect(self, function):
if function not in self.registered:
self.registered.append(function)
def disconnect(self, function):
if function in self.registered:
self.registered.remove(function)
def on_new_objfile(self):
if self.running or not gdb.selected_thread():
return
@ -62,8 +65,46 @@ class StartEvent(object):
def on_stop(self):
self.on_new_objfile()
gdb.events.start = StartEvent()
class EventWrapper(object):
"""
Wraper for GDB events which may not exist on older GDB versions but we still can
fire them manually (to invoke them you have to call `invoke_callbacks`).
"""
def __init__(self, name):
self.name = name
self._event = getattr(gdb.events, self.name, None)
self._is_real_event = self._event is not None
def connect(self, func):
if self._event is not None:
self._event.connect(func)
def disconnect(self, func):
if self._event is not None:
self._event.disconnect(func)
@property
def is_real_event(self):
return self._is_real_event
def invoke_callbacks(self):
"""
As an optimization please don't call this if your GDB has this event (check `.is_real_event`).
"""
for f in registered[self]:
f()
# Old GDBs doesn't have gdb.events.before_prompt, so we will emulate it using gdb.prompt_hook
before_prompt_event = EventWrapper('before_prompt')
gdb.events.before_prompt = before_prompt_event
# In order to support reloading, we must be able to re-fire
# all 'objfile' and 'stop' events.
registered = {
@ -72,7 +113,7 @@ registered = {
gdb.events.new_objfile: [],
gdb.events.stop: [],
gdb.events.start: [],
gdb.events.before_prompt: []
gdb.events.before_prompt: [] # The real event might not exist, but we wrap it
}
# GDB 7.9 and above only
@ -82,20 +123,24 @@ try:
except (NameError, AttributeError):
pass
class Pause(object):
def __enter__(self, *a, **kw):
global pause
pause += 1
def __exit__(self, *a, **kw):
global pause
pause -= 1
# When performing remote debugging, gdbserver is very noisy about which
# objects are loaded. This greatly slows down the debugging session.
# In order to combat this, we keep track of which objfiles have been loaded
# this session, and only emit objfile events for each *new* file.
objfile_cache = set()
def connect(func, event_handler, name=''):
if debug:
print("Connecting", func.__name__, event_handler)
@ -130,25 +175,31 @@ def connect(func, event_handler, name=''):
event_handler.connect(caller)
return func
def exit(func): return connect(func, gdb.events.exited, 'exit')
def cont(func): return connect(func, gdb.events.cont, 'cont')
def new_objfile(func): return connect(func, gdb.events.new_objfile, 'obj')
def stop(func): return connect(func, gdb.events.stop, 'stop')
def start(func): return connect(func, gdb.events.start, 'start')
before_prompt = partial(connect, event_handler=gdb.events.before_prompt, name='before_prompt')
def reg_changed(func):
try:
return connect(func, gdb.events.register_changed, 'reg_changed')
except Exception:
except AttributeError:
return func
def mem_changed(func):
try:
return connect(func, gdb.events.memory_changed, 'mem_changed')
except Exception:
except AttributeError:
return func
def log_objfiles(ofile=None):
if not (debug and ofile):
return
@ -158,8 +209,10 @@ def log_objfiles(ofile=None):
print("objfile: %r" % name)
gdb.execute('info sharedlibrary')
gdb.events.new_objfile.connect(log_objfiles)
def after_reload(start=True):
if gdb.selected_inferior().pid:
for f in registered[gdb.events.stop]:
@ -168,6 +221,9 @@ def after_reload(start=True):
if start: f()
for f in registered[gdb.events.new_objfile]:
f()
for f in registered[gdb.events.before_prompt]:
f()
def on_reload():
for event, functions in registered.items():
@ -175,18 +231,22 @@ def on_reload():
event.disconnect(function)
registered[event] = []
@new_objfile
def _start_newobjfile():
gdb.events.start.on_new_objfile()
@exit
def _start_exit():
gdb.events.start.on_exited()
@stop
def _start_stop():
gdb.events.start.on_stop()
@exit
def _reset_objfiles():
global objfile_cache

@ -58,4 +58,13 @@ def set_prompt():
gdb.execute('set prompt %s' % prompt)
gdb.prompt_hook = prompt_hook
if pwndbg.events.before_prompt_event.is_real_event:
gdb.prompt_hook = prompt_hook
else:
# Old GDBs doesn't have gdb.events.before_prompt, so we will emulate it using gdb.prompt_hook
def extended_prompt_hook(*a):
pwndbg.events.before_prompt_event.invoke_callbacks()
return prompt_hook(*a)
gdb.prompt_hook = extended_prompt_hook

Loading…
Cancel
Save