debugging 如何自动打印GDB中的每一行执行,直到到达给定的断点?

i2loujxw  于 2022-11-14  发布在  其他
关注(0)|答案(6)|浏览(2963)

I would like to be able to set a breakpoint in GDB, and have it run to that point - and in the process, print out lines it has "stepped through".
Here is an example, based on this simple file with a main and a function, and two breakpoints for each:

$ cat > test.c <<EOF
#include "stdio.h"

int count=0;

void doFunction(void) {
  // two steps forward
  count += 2;
  // one step back
  count--;
}

int main(void) {
  // some pointless init commands;
  count = 1;
  count += 2;
  count = 0;
  //main loop
  while(1) {
    doFunction();
    printf("%d\n", count);
  }
}
EOF

$ gcc -g -Wall test.c -o test.exe
$ chmod +x test.exe
$ gdb -se test.exe
...
Reading symbols from /path/to/test.exe...done.
(gdb) b main
Breakpoint 1 at 0x80483ec: file test.c, line 14.
(gdb) b doFunction
Breakpoint 2 at 0x80483c7: file test.c, line 7.

To start the session, I need to run ( r ) the program, which will then stop at first breakpoint ( main ):

(gdb) r
Starting program: /path/to/test.exe 

Breakpoint 1, main () at test.c:14
14    count = 1;
(gdb)

At this point - I can, for instance, hit continue ( c ); and the process will run through, not outputing anything, and break at the requested line:

(gdb) c
Continuing.

Breakpoint 2, doFunction () at test.c:7
7     count += 2;
(gdb)

On the other hand, instead of continue - I can go line by line, either by using step ( s ) or next ( n ); for instance:

14    count = 1;
(gdb) n
15    count += 2;
(gdb) s
16    count = 0;
(gdb) s
19      doFunction();
(gdb) s

Breakpoint 2, doFunction () at test.c:7
7     count += 2;
(gdb) s
9     count--;
(gdb) s
10  }
(gdb) s
main () at test.c:20
20      printf("%d\n", count);
(gdb) s
...
(gdb) s
_IO_vfprintf_internal (s=Cannot access memory at address 0xe5853361
) at vfprintf.c:210
210 vfprintf.c: No such file or directory.
    in vfprintf.c
(gdb) s
245 in vfprintf.c
(gdb) s
210 in vfprintf.c
(gdb) n
245 in vfprintf.c
...
(gdb) n
2006    in vfprintf.c
(gdb) n
__printf (format=0x80484f0 "%d\n") at printf.c:39
39  printf.c: No such file or directory.
    in printf.c
(gdb) n
main () at test.c:21
21    }
(gdb) n
19      doFunction();
(gdb) n

Breakpoint 2, doFunction () at test.c:7
7     count += 2;
(gdb)

Anyways, I am aware that I can keep Enter pressed, and the last entered command (step or next) will repeat (left a bit longer session in the second case, to show that 'next' remains on same level, 'step' steps inside the functions being called). However, as it can be seen, depending on whether step or next runs, it may take a while until a result is reached - and so, I don't want to sit for 10 minutes with my hand stuck on the Enter button :)
So, my question is - can I somehow instruct gdb to run to 'breakpoint 2' without further user intervention - while printing out the lines it goes through, as if step (or next) was pressed?

cunj1qz1

cunj1qz11#

好吧,这并不容易--但我想我有点明白了:)我经历了一堆失败的尝试(发布了here);相关代码如下。
基本上,“next/step until breakpoint”中的问题是,如果调试器被停止(在一个步骤),如何确定你是否“在”一个断点上。还要注意,我使用的是GDB 7.2- 1ubuntu 11(Ubuntu 11.04的当前版本)。所以,它是这样的:

  • 我第一次发现Convenience Variables,我想--既然有程序计数器之类的东西,那么一定有一些GDB方便变量给出“断点”状态,并且可以直接在GDB脚本中使用。然而,在浏览了GDB reference Index一段时间后,我根本找不到任何这样的变量(我的尝试是在nub.gdb中)
  • 缺少这样一个“断点状态”内部变量--剩下要做的唯一事情就是捕获GDB的('stdout')命令行输出(响应命令)作为字符串,并解析它(寻找“断点”)
  • 然后,我发现了Python API到GDB,以及gdb.execute("CMDSTR", toString=True)命令-这似乎正是捕获输出所需要的:“* 默认情况下,命令生成的任何输出都将发送到gdb的标准输出。如果to_string参数为True,则输出将由gdb.execute收集并作为字符串[1]返回 *"!
  • 因此,首先我尝试创建一个脚本(pygdb-nub.pygdbwrap),该脚本将以推荐的方式使用gdb.execute;这里失败-因为这个:
  • Bug 627506 – python: gdb.execute([...], to_string=True) partly prints to stdout/stderr
  • Bug 10808 – Allow GDB/Python API to capture and store GDB output
  • 然后,我想我应该使用python脚本来subprocess.Popen GDB程序,同时替换它的stdin和stdout;然后从那里继续控制GDB(pygdb-sub.py)-这也失败了...(* 显然,因为我没有正确重定向stdin/out *)
  • 然后,我想我应该使用python脚本从GDB调用(通过source),当gdb.execute应该被调用时,它将在内部分叉到一个pty,以便捕获它的输出(pygdb-fork.gdbpygdb-fork.py)...这 * 几乎 * 起作用了-因为有字符串返回;但广发银行发现有些不对劲:“[tcsetpgrp在下级终端中失败:不允许操作]",并且后续返回字符串似乎没有更改。

最后,行之有效的方法是:临时将GDB输出从gdb.execute重定向到RAM中的日志文件(Linux:/dev/shm)的值;然后从Python中阅读回、解析并打印它-- Python还处理一个简单的while循环,一直执行到断点。
具有讽刺意味的是-大多数这些错误,导致这个解决方案通过重定向日志文件,实际上是最近修复的SVN;这意味着这些将在不久的将来传播到发行版,人们将能够直接使用gdb.execute("CMDSTR", toString=True):/然而,由于我现在不能冒险从源代码构建GDB(可能会遇到新的不兼容问题),这对我来说也足够好了:)
以下是相关文件(部分文件也在pygdb-fork.gdbpygdb-fork.py中):
pygdb-logg.gdb为:

# gdb script: pygdb-logg.gdb
# easier interface for pygdb-logg.py stuff
# from within gdb: (gdb) source -v pygdb-logg.gdb
# from cdmline: gdb -x pygdb-logg.gdb -se test.exe

# first, "include" the python file:
source -v pygdb-logg.py

# define shorthand for nextUntilBreakpoint():
define nub
  python nextUntilBreakpoint()
end

# set up breakpoints for test.exe:
b main
b doFunction

# go to main breakpoint
run

pygdb-logg.py为:

# gdb will 'recognize' this as python
#  upon 'source pygdb-logg.py'
# however, from gdb functions still have
#  to be called like:
#  (gdb) python print logExecCapture("bt")

import sys
import gdb
import os

def logExecCapture(instr):
  # /dev/shm - save file in RAM
  ltxname="/dev/shm/c.log"

  gdb.execute("set logging file "+ltxname) # lpfname
  gdb.execute("set logging redirect on")
  gdb.execute("set logging overwrite on")
  gdb.execute("set logging on")
  gdb.execute(instr)
  gdb.execute("set logging off")

  replyContents = open(ltxname, 'r').read() # read entire file
  return replyContents

# next until breakpoint
def nextUntilBreakpoint():
  isInBreakpoint = -1;
  # as long as we don't find "Breakpoint" in report:
  while isInBreakpoint == -1:
    REP=logExecCapture("n")
    isInBreakpoint = REP.find("Breakpoint")
    print "LOOP:: ", isInBreakpoint, "\n", REP

基本上,pygdb-logg.gdb加载pygdb-logg.py python脚本,为nextUntilBreakpoint设置别名nub,并初始化会话--其他一切都由python脚本处理。

$ gdb -x pygdb-logg.gdb -se test.exe
...
Reading symbols from /path/to/test.exe...done.
Breakpoint 1 at 0x80483ec: file test.c, line 14.
Breakpoint 2 at 0x80483c7: file test.c, line 7.

Breakpoint 1, main () at test.c:14
14    count = 1;
(gdb) nub
LOOP::  -1
15    count += 2;

LOOP::  -1
16    count = 0;

LOOP::  -1
19      doFunction();

LOOP::  1

Breakpoint 2, doFunction () at test.c:7
7     count += 2;

(gdb) nub
LOOP::  -1
9     count--;

LOOP::  -1
10  }

LOOP::  -1
main () at test.c:20
20      printf("%d\n", count);

1
LOOP::  -1
21    }

LOOP::  -1
19      doFunction();

LOOP::  1

Breakpoint 2, doFunction () at test.c:7
7     count += 2;

(gdb)

...就像我想要它:P只是不知道它有多可靠(* 以及是否有可能在avr-gdb中使用,这是我需要它的原因:)编辑:Ubuntu 11.04中avr-gdb的当前版本为6.4,该版本无法识别python命令:(*)
希望这能帮到你们
干杯!干杯!
这里有些参考资料:

zynd9foi

zynd9foi2#

在gdb中这样做怎么样,使用一个命令文件。根据需要更改文件参数和循环计数。

gdb -x run.gdb

run.gdb:

set pagination off
set logging file gdb.log
set logging on
set $i = 0
file main
break main
break WriteData
# sadly, commands not getting executed on reaching breakpoint 2
commands 2
  set $i=1000
  print "commands 2 : %d",$i
end
run
while ( $i < 1000 )
  step
  # next
  # continue
  set $i = $i + 1
end
jljoyd4f

jljoyd4f3#

基于@sdaau的答案(http://www.mail-archive.com/gdb@gnu.org/msg00031.html)中的链接,我创建了自己的脚本,简单地不断发送's'并阅读gdb的输出,同时将输出打印到文本文件和终端,当然,我的脚本可以修改以满足其他人的需要,但是,我希望我所做的修改应该适合大多数人的需要。
http://www.codeground.net/coding/gdb-step-into-all-lines-to-get-full-application-flow/

wget http://www.codeground.net/downloads/gdbwalkthrough.c
gcc gdbwalkthrough.c -o gdbwalkthrough
./gdbwalkthrough <application full path> [application arguments]
bn31dyow

bn31dyow4#

作为一个新的答案,因为前面的已经被占用了:)基本上,如果重点是观察源代码(和/或汇编)行的执行,因为程序正在运行-作为动机,我经常在寻找“* 自动打印输出 *”-那么,基本上,一个非常快速的方法是使用GDB TUI模式;我引述:
c - gdb行为:值优化输出-堆栈溢出#1354762
使用GDB TUI模式。当我键入减号和回车键时,我的GDB副本会启用它。然后键入C-x 2(即按住Control并按X,释放两者,然后按2)。这将把它放入拆分源代码和反汇编显示。然后使用stepi和nexti一次移动一条机器指令。使用C-x o在TUI窗口之间切换。
这里的技巧是,即使你点击了continue-这个时间源也会在TUI上显示和指示;并在程序运行时执行以下操作:

...这对我来说避免了许多情况,我必须在“自动单步执行上下文”中编写断点脚本(尽管仍然存在这样的情况)..有关TUI的文档:TUI - Debugging with GDB
干杯!干杯!

vc9ivgsu

vc9ivgsu5#

实际上,我有一个带有Python-GDB扩展的Github repo,它做的事情与您所描述的完全相同,但功能更多。
您可以直接克隆存储库:

git clone https://github.com/Viaceslavus/gdb-debug-until.git

并在GDB内部使用以下命令将python脚本提供给GDB:

source gdb-debug-until/debug_until.py

(如有必要,请更改python脚本路径)
现在,您可以使用以下命令运行代码的每一行,直到出现断点:

debug-until somefile.c:100 --args="" --end="somefile.c:200"

这里的“somefile.c:100”是一个起始断点,“somefile.c:200”是最后一个断点。
“--args”指定程序的一组参数(如果没有参数,可以省略它)。
使用此扩展,您还可以多次运行代码(使用'-r'选项),甚至可以指定调试时应处理的一些事件。有关详细信息,请参阅:
https://github.com/Viaceslavus/gdb-debug-until

mbjcgjjk

mbjcgjjk6#

当前接受的答案包括大量文件io,并且只在断点处停止,但是忽略了观察点、信号,甚至可能忽略了程序结束。
使用python api可以很好地处理这个问题:

  • 定义一个用户命令(带有附加参数以说明自动步进速度)
  • 可选项:定义默认值的参数(下面的两个变量)
  • 在python中执行while循环,处理CTRL-C的“预期”键盘中断
  • 注册一个stop事件处理程序,用于检查停止原因并在其中存储步骤类型
  • 调整while循环以停止“非简单”停止(断点/观察点/信号/...)

下面的代码可以放在www.example.com中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):
        # sanity check - are we even active, prevents a spurious "no registers" exception
        try:
            gdb.newest_frame()
        except gdb.error:
            raise gdb.GdbError("The program is not being run.")

        # calculate sleep time
        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)

        # activate GDB scrolling, otherwise we'd auto-step only one page
        pagination = gdb.parameter("pagination")
        if pagination:
            gdb.execute("set pagination off", False, False)

        # recognize the kind of stop via stop_handler_auto_step 
        global last_stop_was_simple
        last_stop_was_simple = True

        # actual auto-stepping
        try:
            while last_stop_was_simple:
                gdb.execute("step")
                time.sleep(sleep_time)
        # we just quit the loop as requested
        # pass keyboard and user errors unchanged
        except (KeyboardInterrupt, gdb.GdbError):
            raise
        # that exception is unexpected, but we never know...
        except Exception:
            traceback.print_exc()
        # never leave without cleanup...
        finally:
            if pagination:
                gdb.execute("set pagination on", False, False)

def stop_handler_auto_step(event):
    # check the type of stop, the following is the common one after step/next,
    # a more complex one would be a subclass (for example breakpoint or signal)
    global last_stop_was_simple
    last_stop_was_simple = type(event) is gdb.StopEvent

CmdAutoStep()

要使用参数指定默认值(也称为“gdb方式”),请添加另一个参数并使用该参数(还附带0 =无限制)

import gdb
import time
import traceback

class ParameterAutoStep (gdb.Parameter):
    """auto-step default speed (0-19, default 5)"""
    def __init__(self):
        self.set_doc = """Set speed for "auto-step", internally used to calculate sleep time between "step"s.
set "auto-step 0" causes there to be no sleeping."""
        self.show_doc = "Speed value for auto-step."
        super(ParameterAutoStep, self).__init__("auto-step", gdb.COMMAND_RUNNING, gdb.PARAM_UINTEGER)
        self.value = 5
        self.backup = self.value

    def get_set_string (self):
        try:
            self.value = int(ParameterAutoStep.validate(self.value))
        except gdb.GdbError:
            self.value = int (self.backup)
            raise
        self.backup = self.value
        return ""

    @staticmethod
    def validate (argument):
        """validation for auto-step 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 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-step")."""
    def __init__(self):
        print('Registering command and parameter auto-step')
        super(CmdAutoStep, self).__init__("auto-step", gdb.COMMAND_RUNNING)
        self.defaultSpeed = ParameterAutoStep()
        gdb.events.stop.connect(stop_handler_auto_step)

    def invoke(self, argument, from_tty):
        # sanity check - are we even active, prevents a spurious "no registers" exception
        try:
            gdb.newest_frame()
        except gdb.error:
            raise gdb.GdbError("The program is not being run.")

        # calculate sleep time
        if argument:
            number = ParameterAutoStep.validate(argument) # raises an error if not valid
        else:
            number = self.defaultSpeed.value
        if number:
            sleep_time = 3.0 / (1.4 ** number)
        else:
            sleep_time = 0

        # activate GDB scrolling, otherwise we'd auto-step only one page
        pagination = gdb.parameter("pagination")
        if pagination:
            gdb.execute("set pagination off", False, False)

        # recognize the kind of stop via stop_handler_auto_step 
        global last_stop_was_simple
        last_stop_was_simple = True

        # actual auto-stepping
        try:
            while last_stop_was_simple:
                gdb.execute("step")
                time.sleep(sleep_time)
        # we just quit the loop as requested
        # pass keyboard and user errors unchanged
        except (KeyboardInterrupt, gdb.GdbError):
            raise
        # that exception is unexpected, but we never know...
        except Exception:
            traceback.print_exc()
        # never leave without cleanup...
        finally:
            if pagination:
                gdb.execute("set pagination on", False, False)

def stop_handler_auto_step(event):
    # check the type of stop, the following is the common one after step/next,
    # a more complex one would be a subclass (for example breakpoint or signal)
    global last_stop_was_simple
    last_stop_was_simple = type(event) is gdb.StopEvent

CmdAutoStep()

相关问题