मेरे पास यह जवाब देने के लिए जीडीबी पायथन एपीआई के साथ पर्याप्त अनुभव नहीं है; मैं इसे एक साथी डेवलपर से कुछ शोध नोटों पर विचार करता हूं। नीचे मेरा कोड काफी कच्चे और बदसूरत है। हालांकि, यह gdb-7.4 और पायथन-2.7.3 के साथ काम करता है। एक उदाहरण डिबगिंग रन:
$ 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
उपरोक्त सभी दलदल की मानक सुंदर मुद्रित आउटपुट कर रहे हैं - मेरा तर्क यह है कि मैं अक्सर क्या संकेत हैं देखना चाहते हैं, तो मैं ओवरराइड करने के लिए नहीं करना चाहता था उन।
(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
अंतिम पंक्ति से पता चलता है कि जब एक ही निर्देशिका में tiny
, tiny-gdb.py
डिबगिंग स्वचालित रूप से लोड हो जाता है (हालांकि आप इस निष्क्रिय कर सकते हैं, मेरा मानना है कि यह डिफ़ॉल्ट है: हालांकि, संकेत dreferencing prettyprinter आगे नीचे दिखाया गया है का उपयोग करता है व्यवहार)।
tiny-gdb.py
फ़ाइल के ऊपर के लिए इस्तेमाल किया:
कैसे सुंदर-प्रिंटर GDB को स्थापित करने के लिए:
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)
तर्क मेरी पसंद के पीछे इस प्रकार हैं?
इस प्रश्न के दो भाग हैं: पाइथन फ़ाइलों को कहां स्थापित करना है, और सुंदर प्रिंटर को gdb पर कैसे हुक करना है।
क्योंकि सुंदर-प्रिंटर चयन अकेले अनुमानित प्रकार पर भरोसा नहीं कर सकता है, लेकिन वास्तविक डेटा फ़ील्ड में देखना है, आप नियमित अभिव्यक्ति मिलान कार्यों का उपयोग नहीं कर सकते हैं। इसके बजाय, मैंने in the documentation वर्णित वैश्विक सुंदर-प्रिंटर सूची में, अपना खुद का सुंदर-प्रिंटर चयनकर्ता फ़ंक्शन, typefilter()
जोड़ना चुना। मैंने सक्षम/अक्षम कार्यक्षमता को लागू नहीं किया है, क्योंकि मेरा मानना है कि इसके बजाय प्रासंगिक पायथन स्क्रिप्ट को लोड/लोड करना आसान नहीं है।
(typefilter()
हर चर संदर्भ प्रति एक बार कहा जाता हो जाता है, जब तक कि कुछ अन्य बहुत-प्रिंटर पहले से यह स्वीकार कर लिया है।)
फ़ाइल स्थान मुद्दा एक और अधिक जटिल एक है। एप्लिकेशन-विशिष्ट सुंदर-प्रिंटर के लिए, उन्हें एक पाइथन स्क्रिप्ट फ़ाइल में डालकर समझदार लगता है, लेकिन लाइब्रेरी के लिए, कुछ विभाजन क्रम में प्रतीत होता है। प्रलेखन recommends फ़िथन मॉड्यूल में फ़ंक्शन पैकेजिंग, ताकि एक साधारण python import module
सुंदर-प्रिंटर सक्षम कर सके। सौभाग्य से, पायथन पैकेजिंग काफी सरल है। यदि आप शीर्ष पर import gdb
पर थे और इसे /usr/lib/pythonX.Y/tiny.py
पर सहेजते हैं, जहां X.Y
पाइथन संस्करण का उपयोग किया जाता है, तो आपको सुंदर प्रिंटर को सक्षम करने के लिए केवल gdb में python import tiny
चलाने की आवश्यकता है।
बेशक, ठीक से packaging सुंदर प्रिंटर एक बहुत अच्छा विचार है, खासकर यदि आप इसे वितरित करना चाहते हैं, लेकिन यह स्क्रिप्ट की शुरुआत में कुछ चर और cetera जोड़ने के लिए काफी उबालता है, मानते हुए कि आप रखते हैं यह एक फ़ाइल के रूप में। अधिक जटिल सुंदर-प्रिंटर के लिए, निर्देशिका लेआउट का उपयोग करना एक अच्छा विचार हो सकता है।
यदि आपके पास val
मान है, तो val.type
gdb.Type ऑब्जेक्ट का प्रकार बताता है; स्ट्रिंग में इसे परिवर्तित करने से मानव-पठनीय प्रकार का नाम पैदा होता है।
val.type.strip_typedefs()
छिपे हुए सभी टाइपिफ के साथ वास्तविक प्रकार उत्पन्न करता है। मैंने .unqualified()
भी जोड़ा, ताकि सभी कॉन्स/अस्थिर/आदि। प्रकार क्वालीफायर हटा दिए जाते हैं।
नल पॉइंटर का पता लगाना थोड़ा मुश्किल है।
मुझे मिला सबसे अच्छा तरीका लक्ष्य gdb.Value ऑब्जेक्ट के स्ट्रिंग .address
सदस्य की जांच करना था, और देखें कि यह "0x0"
है या नहीं।
जीवन को आसान बनाने के लिए, मैं एक साधारण deref()
फ़ंक्शन लिखने में सक्षम था, जो एक सूचक को हटाना चाहता है। यदि लक्ष्य (शून्य *) 0 को इंगित करता है, तो यह स्ट्रिंग "NULL"
देता है, अन्यथा यह लक्ष्य gdb.Value ऑब्जेक्ट देता है।
तरह से मैं deref()
का उपयोग तथ्य यह है कि "array"
प्रकार बहुत-प्रिंटर 2-tuples है, जहां पहले आइटम नाम स्ट्रिंग है की एक सूची उत्पन्न करते हैं, और दूसरे मद या तो एक gdb.Value वस्तु, या है पर आधारित है एक स्ट्रिंग। यह सूची सुंदर प्रिंटर ऑब्जेक्ट की children()
विधि द्वारा वापस कर दी गई है।
"भेदभाव संघ" प्रकारों को संभालना बहुत आसान होगा, यदि आपके पास सामान्य इकाई के लिए एक अलग प्रकार था।यही कारण है कि, अगर आप
struct box_st {
enum tag_en tag;
};
था और यह हर जगह इस्तेमाल किया गया था जब tag
मूल्य अभी भी अनिश्चित है है; और विशिष्ट संरचना प्रकार केवल तभी उपयोग किए जाते हैं जहां उनके tag
मान तय किए जाते हैं। यह एक बहुत ही सरल प्रकार की अनुमान की अनुमति देगा।
जैसा कि tiny.c
struct box*_st
प्रकारों में एक दूसरे के लिए उपयोग किया जा सकता है। (या, और अधिक विशेष रूप से, हम अकेले प्रकार के आधार पर एक विशिष्ट टैग मान पर निर्भर नहीं कर सकते।)
अनुक्रम मामले वास्तव में काफी आसान है, क्योंकि valtab[]
शून्य संकेत की एक सरणी के रूप में के रूप में बस इलाज किया जा सकता। अनुक्रम टैग का उपयोग सही संघ के सदस्य को चुनने के लिए किया जाता है। वास्तव में, अगर valtab [] बस एक शून्य पॉइंटर सरणी था, तो gdb.Value.cast (gdb.lookup_type()) या gdb.Value.reinterpret_cast (gdb.lookup_type()) प्रत्येक पॉइंटर प्रकार को आवश्यकतानुसार बदलने के लिए उपयोग किया जा सकता है , जैसे कि मैं बॉक्स किए गए संरचना प्रकारों के लिए करता हूं।
रिकर्सन सीमाएं?
आप @
ऑपरेटर का उपयोग print
कमांड में निर्दिष्ट कर सकते हैं ताकि यह निर्दिष्ट किया जा सके कि कितने तत्व मुद्रित हैं, लेकिन यह घोंसले से मदद नहीं करता है।
यदि आप iseq3->valtab[2] = (myval_t)iseq3;
tiny.c
जोड़ते हैं, तो आपको असीमित रिकर्सिव अनुक्रम मिलता है। जीडीबी इसे अच्छी तरह प्रिंट करता है, खासकर set print array
के साथ, लेकिन यह रिकर्सन के बारे में ध्यान या देखभाल नहीं करता है।
मेरी राय में, आप गहराई से नेस्टेड या पुनरावर्ती डाटा संरचनाओं के लिए एक सुंदर-प्रिंटर के अलावा एक gdb कमान लिखना चाहते हैं तो हो सकता है। मेरे परीक्षण के दौरान, मैंने एक कमांड लिखा जो ग्राफ़विज़ का उपयोग सीधे जीडीबी के भीतर से बाइनरी पेड़ संरचनाओं को आकर्षित करने के लिए करता है; मुझे पूरी तरह से आश्वस्त है कि यह सादा पाठ आउटपुट धड़कता है।
जोड़ा गया: यदि आप सहेजते हैं निम्नलिखित के रूप में /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()
आप इसे 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
आप जैसे हैं, तो
struct node {
struct node *le;
struct node *gt;
long key;
char val[];
}
struct node *sometree;
और आप X11 (स्थानीय या दूरस्थ) कनेक्शन और Graphviz स्थापित है, तो आप वृक्ष संरचना देखने पर
(gdb) tree left=le right=gt label=key,val sometree
उपयोग कर सकते हैं। चूंकि यह पहले से देखे गए नोड्स (पायथन सेट के रूप में) की एक सूची बनाए रखता है, यह रिकर्सिव संरचनाओं के बारे में परेशान नहीं होता है।
शायद मुझे पोस्ट करने से पहले मेरे पायथन स्निपेट को साफ़ करना चाहिए था, लेकिन कोई फर्क नहीं पड़ता। कृपया इन केवल प्रारंभिक परीक्षण संस्करणों पर विचार करें; अपने जोखिम पार इस्तेमाल करें। :)
आपके उत्तर के लिए एक बड़ा धन्यवाद। मैंने अभी तक इसका परीक्षण नहीं किया है (क्योंकि वर्तमान में मुझे डीबग करने के लिए 'gdb' की आवश्यकता नहीं है), लेकिन यह बहुत जानकारीपूर्ण है! –