需求
想要了解模組之間函式呼叫的關係時, 與其一層層比對多個類別之間的呼叫關係, 不如直接在最後一個呼叫函式放中斷點, 直接顯示 backtrace。但是當函式裡有太多參數或 template 時, backtrace 的 frame 訊息會變得很長, 不易閱讀。我的目的只是找出呼叫的函式名稱、檔名和行數, 函式帶的參數反而是困擾。
作法一: 用 gdb.execute()
一個簡單的作法是截取 gdb 的輸出, 然後解析文字去掉不要的部份:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import gdb | |
import re | |
class ShorternBacktraceCommand(gdb.Command): | |
'''Show a backtrace without argument info in each frame.''' | |
def __init__(self): | |
super(ShorternBacktraceCommand, self).__init__ ("bt", | |
gdb.COMMAND_SUPPORT, | |
gdb.COMPLETE_NONE) | |
def invoke(self, arg, from_tty): | |
if not arg: | |
arg = '' | |
raw = gdb.execute("backtrace %s" % arg, True, True) | |
lines = raw.split('\n') | |
for i, line in enumerate(lines): | |
if not line: | |
continue | |
tokens = line.split() | |
# first line format: e.g., #0 A::hello (...) at a.cpp:8 | |
# the rest : e.g., #2 0x0..0 in A::foo (...) at a.cpp:18 | |
func_index = 1 if i == 0 else 3 | |
print ('\033[1;33m%2s\033[m %s at %s' | |
'' % (tokens[0], tokens[func_index], tokens[-1])) | |
ShorternBacktraceCommand() |
Btw, 上面的作法還順便幫行首的標號上色。
但是, 使用 cgdb 時會無法運作, 理由是 cgdb 使用 GDB MI, gdb.execute('backtrace') 的結果不是原本看到的格式, 難以解析。
作法二: 用 gdb.Frame() API
只好改用中規中矩的方式逐一讀取 frame, 取出需要的資訊:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import gdb | |
class ShorternBacktraceCommand(gdb.Command): | |
'''Show a backtrace without argument info in each frame.''' | |
def __init__(self): | |
super(ShorternBacktraceCommand, self).__init__ ("bt", | |
gdb.COMMAND_SUPPORT, | |
gdb.COMPLETE_NONE) | |
def invoke(self, arg, from_tty): | |
num = 0; | |
try: | |
num = int(arg) | |
except Exception, e: | |
pass | |
lines = [] | |
f = gdb.newest_frame() | |
fn = 0 | |
while f is not None: | |
symtab_and_line = gdb.Frame.find_sal(f) | |
frame_name = gdb.Frame.name(f) | |
if frame_name: | |
args = [ | |
fn, | |
frame_name, | |
symtab_and_line.symtab.filename, | |
symtab_and_line.line, | |
] | |
else: | |
args = [fn, '??', 'unknown', 0] | |
lines.append('#%2d %s at %s:%s' % tuple(args)) | |
f = gdb.Frame.older(f) | |
fn += 1 | |
if num > 0: | |
lines = lines[:num] | |
elif num < 0: | |
lines = lines[len(lines) + num:] | |
for line in lines: | |
print line | |
ShorternBacktraceCommand() |
將上面的 script 存到 /path/to/gdb/scripts/backtrace.py, 接著在 $HOME/.gdbinit 裡加入以下設定:
python sys.path.insert(0, '/path/to/gdb/scripts') import backtrace end
之後就能用 bt 顯示精簡後的 backtrace 了, 也方便手動複製貼上到筆記裡。以下是一個輸出例子:
(gdb) bt # 0 A::hello at a.cpp:8 # 1 A::bar at a.cpp:13 # 2 A::foo at a.cpp:18 # 3 main at a.cpp:25
Btw, 若是需求比較簡單, 可以試看看 Print Settings, 有些選項可以改變 backtrace 顯示的訊息。
沒有留言:
張貼留言