Tôi không có đủ kinh nghiệm với api gdb Python để gọi đây là câu trả lời; Tôi xem đây chỉ là một số ghi chú nghiên cứu từ một nhà phát triển đồng nghiệp. Mã của tôi được đính kèm bên dưới khá thô và xấu xí. Tuy nhiên, điều này không làm việc với gdb-7,4 và python-2.7.3. Một ví dụ chạy gỡ lỗi:
$ gcc -Wall -g3 tiny.c -o tiny
$ gdb tiny
(gdb) b 58
(gdb) run
(gdb) print iseq3
$1 = (struct boxsequence_st *) 0x602050
(gdb) print iv42
$2 = (struct boxint_st *) 0x602010
(gdb) print istrhello
$3 = (struct boxstring_st *) 0x602030
Tất cả những điều trên là bog tiêu chuẩn khá-in kết quả đầu ra - lý luận của tôi là tôi thường muốn xem những gì con trỏ là, vì vậy tôi không muốn ghi đè những, cái đó. Tuy nhiên, dreferencing các con trỏ sử dụng prettyprinter hiện thêm dưới đây:
(gdb) print *iseq3
$4 = (struct boxsequence_st)(3) = {(struct boxint_st)42, (struct boxstring_st)"hello"(5), NULL}
(gdb) print *iv42
$5 = (struct boxint_st)42
(gdb) print *istrhello
$6 = (struct boxstring_st)"hello"(5)
(gdb) set print array
(gdb) print *iseq3
$7 = (struct boxsequence_st)(3) = {
(struct boxint_st)42,
(struct boxstring_st)"hello"(5),
NULL
}
(gdb) info auto-load
Loaded Script
Yes /home/.../tiny-gdb.py
Dòng cuối cùng cho thấy khi gỡ lỗi tiny
, tiny-gdb.py
trong cùng thư mục được nạp tự động (mặc dù bạn có thể vô hiệu hóa này, tôi tin rằng đây là mặc định hành vi).
Các tiny-gdb.py
tập tin được sử dụng để ở trên:
def deref(reference):
target = reference.dereference()
if str(target.address) == '0x0':
return 'NULL'
else:
return target
class cstringprinter:
def __init__(self, value, maxlen=4096):
try:
ends = gdb.selected_inferior().search_memory(value.address, maxlen, b'\0')
if ends is not None:
maxlen = ends - int(str(value.address), 16)
self.size = str(maxlen)
else:
self.size = '%s+' % str(maxlen)
self.data = bytearray(gdb.selected_inferior().read_memory(value.address, maxlen))
except:
self.data = None
def to_string(self):
if self.data is None:
return 'NULL'
else:
return '\"%s\"(%s)' % (str(self.data).encode('string_escape').replace('"', '\\"').replace("'", "\\\\'"), self.size)
class boxintprinter:
def __init__(self, value):
self.value = value.cast(gdb.lookup_type('struct boxint_st'))
def to_string(self):
return '(struct boxint_st)%s' % str(self.value['ival'])
class boxstringprinter:
def __init__(self, value):
self.value = value.cast(gdb.lookup_type('struct boxstring_st'))
def to_string(self):
return '(struct boxstring_st)%s' % (self.value['strval'])
class boxsequenceprinter:
def __init__(self, value):
self.value = value.cast(gdb.lookup_type('struct boxsequence_st'))
def display_hint(self):
return 'array'
def to_string(self):
return '(struct boxsequence_st)(%s)' % str(self.value['slen'])
def children(self):
value = self.value
tag = str(value['tag'])
count = int(str(value['slen']))
result = []
if tag == 'tag_none':
for i in xrange(0, count):
result.append(('#%d' % i, deref(value['valtab'][i]['ptag'])))
elif tag == 'tag_int':
for i in xrange(0, count):
result.append(('#%d' % i, deref(value['valtab'][i]['pint'])))
elif tag == 'tag_string':
for i in xrange(0, count):
result.append(('#%d' % i, deref(value['valtab'][i]['pstr'])))
elif tag == 'tag_sequence':
for i in xrange(0, count):
result.append(('#%d' % i, deref(value['valtab'][i]['pseq'])))
return result
def typefilter(value):
"Pick a pretty-printer for 'value'."
typename = str(value.type.strip_typedefs().unqualified())
if typename == 'char []':
return cstringprinter(value)
if (typename == 'struct boxint_st' or
typename == 'struct boxstring_st' or
typename == 'struct boxsequence_st'):
tag = str(value['tag'])
if tag == 'tag_int':
return boxintprinter(value)
if tag == 'tag_string':
return boxstringprinter(value)
if tag == 'tag_sequence':
return boxsequenceprinter(value)
return None
gdb.pretty_printers.append(typefilter)
Lý do đằng sau sự lựa chọn của tôi là như sau:
Làm thế nào để cài đặt khá-máy in để gdb?
Có hai phần của câu hỏi này: nơi cài đặt các tệp Python và cách móc các máy in đẹp vào gdb.
Vì lựa chọn máy in đẹp không thể dựa vào loại suy luận một mình, nhưng phải nhìn vào các trường dữ liệu thực tế, bạn không thể sử dụng các hàm so khớp cụm từ thông dụng. Thay vào đó, tôi đã chọn thêm chức năng chọn máy in đẹp của riêng mình, typefilter()
, vào danh sách máy in khá toàn cầu, như được mô tả in the documentation. Tôi đã không thực hiện chức năng bật/tắt, bởi vì tôi tin rằng việc tải/không tải tập lệnh Python có liên quan sẽ dễ dàng hơn.
(typefilter()
được gọi một lần mỗi mỗi tài liệu tham khảo biến, trừ một số khá-in khác đã chấp nhận nó.) Vị trí
File vấn đề là một phức tạp hơn. Đối với các máy in đẹp dành riêng cho ứng dụng, việc đưa chúng vào một tệp tập lệnh Python đơn giản có vẻ hợp lý, nhưng đối với một thư viện, một số phần tách có vẻ như theo thứ tự. Tài liệu hướng dẫn recommends đóng gói các chức năng vào một mô-đun Python, sao cho một đơn giản python import module
cho phép máy in đẹp. May mắn thay, bao bì Python khá đơn giản. Nếu bạn đã import gdb
lên trên cùng và lưu nó vào /usr/lib/pythonX.Y/tiny.py
, trong đó X.Y
là phiên bản python được sử dụng, bạn chỉ cần chạy python import tiny
trong gdb để bật máy in đẹp. Tất nhiên, đúng là packaging máy in khá là một ý tưởng rất hay, đặc biệt là nếu bạn có ý định phân phối nó, nhưng nó hơi sôi xuống để thêm một số biến và cetera vào đầu kịch bản, giả sử bạn giữ nó như một tập tin duy nhất. Đối với các máy in khá phức tạp, việc sử dụng bố cục thư mục có thể là một ý tưởng hay.
Nếu bạn có giá trị val
, thì val.type
là đối tượng gdb.Type mô tả loại của nó; chuyển đổi nó thành chuỗi mang lại một tên kiểu người có thể đọc được.
val.type.strip_typedefs()
mang lại loại thực tế với tất cả các typedef bị tước. Tôi thậm chí còn thêm .unqualified()
, để tất cả const/volatile/etc. loại vòng loại được loại bỏ.
Phát hiện con trỏ NULL khó hiểu một chút.
Cách tốt nhất mà tôi tìm thấy là kiểm tra thành viên .address
đã được hợp nhất của đối tượng gdb.Value đích và xem liệu đó có phải là "0x0"
hay không.
Để làm cho cuộc sống dễ dàng hơn, tôi đã có thể viết một hàm deref()
đơn giản, cố gắng bỏ qua con trỏ. Nếu mục tiêu trỏ tới (void *) 0, nó trả về chuỗi "NULL"
, nếu không nó sẽ trả về đối tượng gdb.Value đích.
Cách tôi sử dụng deref()
dựa trên thực tế là "array"
loại máy in đẹp mang lại danh sách 2 bộ, trong đó mục đầu tiên là chuỗi tên và mục thứ hai là đối tượng gdb.Value hoặc một chuỗi. Danh sách này được trả về bởi phương thức children()
của đối tượng máy in đẹp.
Xử lý các loại "công đoàn phân biệt đối xử" sẽ dễ dàng hơn nhiều, nếu bạn có loại riêng cho thực thể chung.Tức là, nếu bạn có
struct box_st {
enum tag_en tag;
};
và nó đã được sử dụng ở mọi nơi khi giá trị tag
vẫn không chắc chắn; và các loại cấu trúc cụ thể chỉ được sử dụng khi giá trị tag
của chúng được cố định. Điều này sẽ cho phép suy luận kiểu đơn giản hơn nhiều.
Vì vậy, trong tiny.c
các loại struct box*_st
có thể được sử dụng thay thế cho nhau. (Hoặc, cụ thể hơn, chúng tôi không thể dựa vào giá trị thẻ cụ thể chỉ dựa trên loại giá.)
Trường hợp trình đơn thực sự khá đơn giản, vì valtab[]
có thể được coi là đơn giản. Thẻ trình tự được sử dụng để chọn thành viên công đoàn chính xác. Trong thực tế, nếu valtab [] chỉ đơn giản là một mảng con trỏ void, thì gdb.Value.cast (gdb.lookup_type()) hoặc gdb.Value.reinterpret_cast (gdb.lookup_type()) có thể được sử dụng để thay đổi từng loại con trỏ khi cần thiết , giống như tôi làm cho các loại cấu trúc đóng hộp.
Giới hạn đệ quy?
Bạn có thể sử dụng toán tử @
trong lệnh print
để chỉ định số lượng phần tử được in, nhưng điều đó không giúp làm tổ.
Nếu bạn thêm iseq3->valtab[2] = (myval_t)iseq3;
vào tiny.c
, bạn sẽ nhận được một chuỗi đệ quy vô hạn. gdb in nó độc đáo, đặc biệt là với set print array
, nhưng nó không chú ý hoặc quan tâm đến đệ quy.
Theo tôi, bạn có thể muốn viết một lệnh gdb ngoài một khá-in cho các cấu trúc dữ liệu lồng nhau sâu sắc hoặc đệ quy. Trong quá trình thử nghiệm, tôi đã viết một lệnh sử dụng Graphviz để vẽ các cấu trúc cây nhị phân trực tiếp từ bên trong gdb; Tôi hoàn toàn tin rằng nó đánh bại đầu ra văn bản đơn giản.
Added: Nếu bạn lưu như sau /usr/lib/pythonX.Y/tree.py
:
import subprocess
import gdb
def pretty(value, field, otherwise=''):
try:
if str(value[field].type) == 'char []':
data = str(gdb.selected_inferior().read_memory(value[field].address, 64))
try:
size = data.index("\0")
return '\\"%s\\"' % data[0:size].encode('string_escape').replace('"', '\\"').replace("'", "\\'")
except:
return '\\"%s\\"..' % data.encode('string_escape').replace('"', '\\"').replace("'", "\\'")
else:
return str(value[field])
except:
return otherwise
class tee:
def __init__(self, cmd, filename):
self.file = open(filename, 'wb')
gdb.write("Saving DOT to '%s'.\n" % filename)
self.cmd = cmd
def __del__(self):
if self.file is not None:
self.file.flush()
self.file.close()
self.file = None
def __call__(self, arg):
self.cmd(arg)
if self.file is not None:
self.file.write(arg)
def do_dot(value, output, visited, source, leg, label, left, right):
if value.type.code != gdb.TYPE_CODE_PTR:
return
target = value.dereference()
target_addr = int(str(target.address), 16)
if target_addr == 0:
return
if target_addr in visited:
if source is not None:
path='%s.%s' % (source, target_addr)
if path not in visited:
visited.add(path)
output('\t"%s" -> "%s" [ taillabel="%s" ];\n' % (source, target_addr, leg))
return
visited.add(target_addr)
if source is not None:
path='%s.%s' % (source, target_addr)
if path not in visited:
visited.add(path)
output('\t"%s" -> "%s" [ taillabel="%s" ];\n' % (source, target_addr, leg))
if label is None:
output('\t"%s" [ label="%s" ];\n' % (target_addr, target_addr))
elif "," in label:
lab = ''
for one in label.split(","):
cur = pretty(target, one, '')
if len(cur) > 0:
if len(lab) > 0:
lab = '|'.join((lab,cur))
else:
lab = cur
output('\t"%s" [ shape=record, label="{%s}" ];\n' % (target_addr, lab))
else:
output('\t"%s" [ label="%s" ];\n' % (target_addr, pretty(target, label, target_addr)))
if left is not None:
try:
target_left = target[left]
do_dot(target_left, output, visited, target_addr, left, label, left, right)
except:
pass
if right is not None:
try:
target_right = target[right]
do_dot(target_right, output, visited, target_addr, right, label, left, right)
except:
pass
class Tree(gdb.Command):
def __init__(self):
super(Tree, self).__init__('tree', gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL, False)
def do_invoke(self, name, filename, left, right, label, cmd, arg):
try:
node = gdb.selected_frame().read_var(name)
except:
gdb.write('No symbol "%s" in current context.\n' % str(name))
return
if len(arg) < 1:
cmdlist = [ cmd ]
else:
cmdlist = [ cmd, arg ]
sub = subprocess.Popen(cmdlist, bufsize=16384, stdin=subprocess.PIPE, stdout=None, stderr=None)
if filename is None:
output = sub.stdin.write
else:
output = tee(sub.stdin.write, filename)
output('digraph {\n')
output('\ttitle = "%s";\n' % name)
if len(label) < 1: label = None
if len(left) < 1: left = None
if len(right) < 1: right = None
visited = set((0,))
do_dot(node, output, visited, None, None, label, left, right)
output('}\n')
sub.communicate()
sub.wait()
def help(self):
gdb.write('Usage: tree [OPTIONS] variable\n')
gdb.write('Options:\n')
gdb.write(' left=name Name member pointing to left child\n')
gdb.write(' right=name Name right child pointer\n')
gdb.write(' label=name[,name] Define node fields\n')
gdb.write(' cmd=dot arg=-Tx11 Specify the command (and one option)\n')
gdb.write(' dot=filename.dot Save .dot to a file\n')
gdb.write('Suggestions:\n')
gdb.write(' tree cmd=neato variable\n')
def invoke(self, argument, from_tty):
args = argument.split()
if len(args) < 1:
self.help()
return
num = 0
cfg = { 'left':'left', 'right':'right', 'label':'value', 'cmd':'dot', 'arg':'-Tx11', 'dot':None }
for arg in args[0:]:
if '=' in arg:
key, val = arg.split('=', 1)
cfg[key] = val
else:
num += 1
self.do_invoke(arg, cfg['dot'], cfg['left'], cfg['right'], cfg['label'], cfg['cmd'], cfg['arg'])
if num < 1:
self.help()
Tree()
bạn có thể sử dụng nó trong gdb:
(gdb) python import tree
(gdb) tree
Usage: tree [OPTIONS] variable
Options:
left=name Name member pointing to left child
right=name Name right child pointer
label=name[,name] Define node fields
cmd=dot arg=-Tx11 Specify the command (and one option)
dot=filename.dot Save .dot to a file
Suggestions:
tree cmd=neato variable
Nếu bạn có ví dụ
struct node {
struct node *le;
struct node *gt;
long key;
char val[];
}
struct node *sometree;
và bạn có X11 (địa phương hoặc từ xa) kết nối và Graphviz cài đặt, bạn có thể sử dụng
(gdb) tree left=le right=gt label=key,val sometree
để xem các cấu trúc cây. Bởi vì nó giữ lại một danh sách các nút đã được truy cập (như một bộ Python), nó không bị lúng túng về các cấu trúc đệ quy.
Tôi có lẽ phải làm sạch các đoạn mã Python trước khi đăng, nhưng không sao cả. Vui lòng xem xét những phiên bản thử nghiệm ban đầu này; Sử dụng có nguy cơ của riêng bạn. :)
Cảm ơn bạn đã trả lời. Tôi chưa thử nghiệm (vì hiện tại tôi không cần 'gdb' để gỡ lỗi), nhưng nó rất thông tin! –