簡體   English   中英

"如何在到達給定斷點之前自動打印 GDB 中的每個執行行?"

[英]How to print every executed line in GDB automatically until a given breakpoint is reached?

我希望能夠在 GDB 中設置一個斷點,並讓它運行到那個點 - 在這個過程中,打印出它已經“逐步通過”的行。

這是一個示例,基於這個簡單的文件,其中包含一個main和一個函數,每個文件都有兩個斷點:

$ 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.

要開始會話,我需要運行 ( r ) 程序,然后它將在第一個斷點 ( main ) 處停止:

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

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

在這一點上 - 例如,我可以點擊 continue ( c ); 並且該過程將運行,不輸出任何內容,並在請求的行處中斷:

(gdb) c
Continuing.

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

另一方面,我可以使用 step ( s ) 或 next ( n ) 逐行執行,而不是 continue; 例如:

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) 

無論如何,我知道我可以按住Enter鍵,並且最后輸入的命令(步驟或下一步)將重復(在第二種情況下留下更長的會話,以顯示“下一步”保持在同一級別,“步驟”步驟在被調用的函數內部)。 但是,可以看出,根據是 step 還是 next 運行,可能需要一段時間才能達到結果 - 所以,我不想坐 10 分鍾,我的手卡在 Enter 按鈕上 :)

所以,我的問題是——我能否以某種方式指示gdb在沒有進一步用戶干預的情況下運行到“斷點 2”——同時打印出它經過的行,就好像按下了步驟(或下一步)一樣?

嗯,這並不容易 - 但我想我有點得到了:)我經歷了一系列失敗的嘗試(在這里發布); 相關代碼如下。

基本上,“下一步/直到斷點”的問題是如果調試器停止(步驟),如何確定是否“斷開”斷點。 另請注意,我使用GDB 7.2-1ubuntu11(Ubuntu 11.04的當前版本)。 所以,它是這樣的:

  • 我首先發現了Convenience Variables ,並且考慮到 - 如果有程序計數器等可用,必須有一些GDB便利變量給出“斷點”狀態,並且可以直接在GDB腳本中使用。 然而,在瀏覽了GDB參考索引一段時間后,我根本找不到任何這樣的變量(我的嘗試是在nub.gdb中
  • 缺少這樣的“斷點狀態”內部變量 - 唯一要做的就是將GDB的('stdout')命令行輸出(作為對命令的響應)捕獲為字符串,並解析它(尋找“斷點“)
  • 然后,我發現了GDB的Python API ,以及gdb.execute("CMDSTR", toString=True)命令 - 這看起來正是捕獲輸出所需要的:“ 默認情況下,命令生成的任何輸出都會被發送到gdb的標准輸出。如果to_string參數為True,則輸出將由gdb.execute收集並作為字符串[ 1 ]返回 “!

最后,有效的方法是:暫時將gdb.execute的GDB輸出重定向到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腳本處理。 這是一個示例會話 - 關於OP中的測試源:

$ 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 ,這就是我需要的:)編輯:avr-gdb的版本Ubuntu 11.04目前是6.4,它無法識別python命令:(

嗯,希望這有助於某人,
干杯!

這里有一些參考:

怎么樣在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

根據@ 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]

作為一個新的答案,因為前一個已經已經陷入困境了:)基本上,如果重點是觀察執行源(和/或匯編)代碼行作為程序運行 - 因為當我調查“ 自動時,通常是我的動機打印輸出 “ - 然后,基本上,一種非常快速的方法是使用GDB TUI模式; 我引用:

c - gdb行為:值優化了 - Stack Overflow#1354762

使用GDB TUI模式。 當我鍵入減號和Enter時,我的GDB副本啟用它。 然后鍵入Cx 2(按住Control並按X,釋放兩個然后按2)。 這將把它放入拆分源和反匯編顯示中。 然后使用stepi和nexti一次移動一台機器指令。 使用Cx o在TUI窗口之間切換。

這里的技巧是,即使你點擊continue - 這個時間源將顯示並顯示在TUI上; 並在程序運行時跟隨:

GDB TUI截圖

...這對我來說避免了很多情況,我必須在“自動步進上下文”中編寫斷點腳本(雖然仍然存在這種情況)。關於TUI的文檔: TUI - 使用GDB進行調試

干杯!

當前接受的答案包括很多文件 io 並且只在斷點處停止,但是觀察點、信號甚至程序結束都被忽略了。

使用 python api 可以很好地處理:

  • 定義一個用戶命令(帶有額外的參數來說明自動步進的速度)
  • 可選:為默認定義一個參數(下面的兩個變體)
  • 在python中做while循環,處理CTRL-C的“預期”鍵盤中斷
  • 注冊一個stop事件處理程序,檢查停止原因並在那里存儲步驟類型
  • 調整 while 循環以停止“不簡單”的停止(斷點/觀察點/信號/...)

下面的代碼可以放在 gdb-auto-step.py 中,只要你想,它就可以用source gdb-auto-step.py激活(或包含在 .gdbinit 文件中以使其始終可用):

import gdb
import time

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 = 無限制)

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()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM