目前被接受的答案包括大量文件IO,并且仅在断点上停止,但忽略了监视点、信号甚至程序结束。
使用Python API可以很好地处理这个问题:
- 定义一个用户命令(带有附加参数以指定自动步进的速度)
- 可选:为默认值定义一个参数(下面两种变体都适用)
- 在Python中执行while循环,处理CTRL-C的“预期”键盘中断
- 注册一个
stop
事件处理程序,检查停止原因并在那里存储步骤类型
- 调整while循环以停止于“非简单”停止(断点/监视点/信号/...)
以下代码可以放置在gdb-auto-step.py中,每当需要时可以使用source gdb-auto-step.py
激活它(或将其包含在.gdbinit文件中以使其始终可用):
import gdb
import time
import traceback
class CmdAutoStep (gdb.Command):
"""Auto-Step through the code until something happens or manually interrupted.
An argument says how fast auto stepping is done (1-19, default 5)."""
def __init__(self):
print('Registering command auto-step')
super(CmdAutoStep, self).__init__("auto-step", gdb.COMMAND_RUNNING)
gdb.events.stop.connect(stop_handler_auto_step)
def invoke(self, argument, from_tty):
try:
gdb.newest_frame()
except gdb.error:
raise gdb.GdbError("The program is not being run.")
if argument:
if not argument.isdigit():
raise gdb.GdbError("argument must be a digit, not " + argument)
number = int(argument)
if number == 0 or number > 19:
raise gdb.GdbError("argument must be a digit between 1 and 19")
sleep_time = 3.0 / (1.4 ** number)
pagination = gdb.parameter("pagination")
if pagination:
gdb.execute("set pagination off", False, False)
global last_stop_was_simple
last_stop_was_simple = True
try:
while last_stop_was_simple:
gdb.execute("step")
time.sleep(sleep_time)
except (KeyboardInterrupt, gdb.GdbError):
raise
except Exception:
traceback.print_exc()
finally:
if pagination:
gdb.execute("set pagination on", False, False)
def stop_handler_auto_step(event):
global last_stop_was_simple
last_stop_was_simple = type(event) is gdb.StopEvent
CmdAutoStep()
为了指定默认参数(也称为“gdb方式”),通过API添加一个新的参数,并按照以下方式使用它(还带有0 =无限制,处理进程退出,额外的自动下一步命令和更多类包装):
import gdb
import time
import traceback
class ParameterAutoSpeed (gdb.Parameter):
"""default speed for auto-step and auto-next commands (0-19, default 5)"""
def __init__(self):
self.set_doc = """Set speed for "auto-step" and "auto-next",
internally used to calculate sleep time between iterations of "step" / "next";
set "auto-speed 0" causes there to be no sleeping."""
self.show_doc = "Speed value for auto-step/auto-next."
super(ParameterAutoSpeed, self).__init__("auto-speed", gdb.COMMAND_RUNNING, gdb.PARAM_UINTEGER)
self.value = 5
self.backup = self.value
def get_set_string (self):
try:
self.value = int(self.validate(self.value))
except gdb.GdbError:
self.value = int (self.backup)
raise
self.backup = self.value
return ""
def validate (self, argument):
"""validation for auto-step/auto-next speed"""
try:
speed = int(argument)
if speed < 0 or speed > 19:
raise ValueError()
except (TypeError, ValueError):
raise gdb.GdbError("speed argument must be an integer between 1 and 19, or 0")
return speed
class CmdAutoNext (gdb.Command):
"""Auto-Next through the code until something happens or manually interrupted.
An argument says how fast auto stepping is done (see parameter "auto-speed")."""
def __init__(self, worker):
self.worker = worker
super(CmdAutoNext, self).__init__("auto-next", gdb.COMMAND_RUNNING)
def invoke(self, argument, from_tty):
self.worker.invoke (argument)
class CmdAutoStep (gdb.Command):
"""Auto-Step through the code until something happens or manually interrupted.
An argument says how fast auto stepping is done (see parameter "auto-speed").
Note: To be usable you likely need several "skip" setup for not stepping into functions of
the C library and other system libraries which may have no debug symbols available
or are of no interest.
You may press [CTRL]+[C] and execute "skip file", then "finish" to leave those."""
def __init__(self, worker):
self.worker = worker
super(CmdAutoStep, self).__init__("auto-step", gdb.COMMAND_RUNNING)
def invoke(self, argument, from_tty):
self.worker.invoke (argument, do_step=True)
class AutoWorker ():
def __init__(self):
print('Registering parameter auto-speed and commands auto-step, auto-next')
self.speed = ParameterAutoSpeed()
CmdAutoStep(self)
CmdAutoNext(self)
gdb.events.stop.connect(self.stop_handler_auto)
gdb.events.exited.connect(self.exit_handler_auto)
def invoke(self, argument, do_step=False):
if argument:
number = self.speed.validate(argument)
else:
number = self.speed.value
if number:
sleep_time = 3.0 / (1.4 ** number)
else:
sleep_time = 0
pagination = gdb.parameter("pagination")
if pagination:
gdb.execute("set pagination off", False, False)
self.last_stop_was_simple = True
try:
while self.last_stop_was_simple:
if do_step:
gdb.execute ("step")
else:
gdb.execute ("next")
time.sleep(sleep_time)
except (KeyboardInterrupt, gdb.GdbError):
raise
except gdb.error as err:
raise gdb.GdbError(err)
except Exception:
traceback.print_exc()
finally:
if pagination:
gdb.execute("set pagination on", False, False)
def stop_handler_auto(self, event):
self.last_stop_was_simple = type(event) is gdb.StopEvent
def exit_handler_auto(self, event):
self.last_stop_was_simple = False
AutoWorker()