[英]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的當前版本)。 所以,它是這樣的:
gdb.execute("CMDSTR", toString=True)
命令 - 這看起來正是捕獲輸出所需要的:“ 默認情況下,命令生成的任何輸出都會被發送到gdb的標准輸出。如果to_string參數為True,則輸出將由gdb.execute收集並作為字符串[ 1 ]返回 “!
gdb.execute
的腳本( pygdb-nub.py , gdbwrap ); 失敗了 - 因為這個:
subprocess.Popen
GDB程序,同時替換它的stdin和stdout; 然后從那里繼續控制GDB( pygdb-sub.py ) - 那也失敗了......( 顯然,因為我沒有重定向stdin / out right ) source
),每當gdb.execute
時,它會在內部分叉為pty,以便捕獲其輸出( pygdb-fork.gdb , pygdb-fork) .py )...這幾乎奏效 - 因為有字符串返回; 然而,GDB注意到某些事情是不對的:“ [tcsetpgrp在terminal_inferior中失敗:操作不被允許] ”,后續的返回字符串似乎沒有改變。 最后,有效的方法是:暫時將gdb.execute
的GDB輸出重定向到RAM中的日志文件(Linux: /dev/shm
); 然后讀回來,解析它並從python-python打印它也處理一個簡單的while循環,直到達到斷點。
具有諷刺意味的是 - 大多數這些錯誤,通過重定向日志文件引起了這個解決方案,實際上最近修復了SVN; 這意味着那些將在不久的將來傳播到發行版,並且可以直接使用gdb.execute("CMDSTR", toString=True)
:/但是,因為我現在不能冒險從源代碼構建GDB(並且可能會碰到進入可能的新的不兼容性),這對我來說也足夠了:)
以下是相關文件(部分也在pygdb-fork.gdb , pygdb-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命令:( )
嗯,希望這有助於某人,
干杯!
這里有一些參考:
parse_and_eval
了? - 堆棧溢出 怎么樣在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上; 並在程序運行時跟隨:
...這對我來說避免了很多情況,我必須在“自動步進上下文”中編寫斷點腳本(雖然仍然存在這種情況)。關於TUI的文檔: TUI - 使用GDB進行調試
干杯!
當前接受的答案包括很多文件 io 並且只在斷點處停止,但是觀察點、信號甚至程序結束都被忽略了。
使用 python api 可以很好地處理:
stop
事件處理程序,檢查停止原因並在那里存儲步驟類型 下面的代碼可以放在 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()
實際上,我有一個帶有 Python-GDB 擴展的Github 存儲庫<\/a>,它的功能與您所描述的完全相同,但功能更多。
您可以克隆回購:
git clone https://github.com/Viaceslavus/gdb-debug-until.git
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.