2014-05-10 3 views
10

मैं पाइथन से बहुत परिचित नहीं हूं, और मैं सिर्फ जीडीबी पायथन स्क्रिप्टिंग क्षमताओं की खोज कर रहा हूं; मेरे प्रश्न का प्रेरणा MELT monitor के अंदर मूल्यों के जीडीबी मुद्रण को बढ़ाने के लिए है जो बाद में GCC MELT से जुड़ा होगा। लेकिन यहां एक सरल संस्करण है।जीडीबी पाइथन के साथ बहुत प्रिंटिंग एक रिकर्सिव संरचना

मेरा सिस्टम लिनक्स/डेबियन/सिड/x86-64 है। जीसीसी संकलक 4.8.2 है; जीडीबी डीबगर 7.6.2 है; अपने अजगर 3.3

है मैं एक "भेदभाव संघ" प्रकार के साथ एक सी कार्यक्रम डिबग करने के लिए करना चाहते हैं:

// file tiny.c in the public domain by Basile Starynkevitch 
// compile with gcc -g3 -Wall -std=c99 tiny.c -o tiny 
// debug with gdb tiny 
// under gdb: python tiny-gdb.py 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 

typedef union my_un myval_t; 
enum tag_en { 
    tag_none, 
    tag_int, 
    tag_string, 
    tag_sequence 
}; 
struct boxint_st; 
struct boxstring_st; 
struct boxsequence_st; 
union my_un { 
    void* ptr; 
    enum tag_en *ptag; 
    struct boxint_st *pint; 
    struct boxstring_st *pstr; 
    struct boxsequence_st *pseq; 
}; 

struct boxint_st { 
    enum tag_en tag;  // for tag_int 
    int ival; 
}; 
struct boxstring_st { 
    enum tag_en tag;  // for tag_string 
    char strval[];  // a zero-terminated C string 
}; 
struct boxsequence_st { 
    enum tag_en tag;  // for tag_sequence 
    unsigned slen; 
    myval_t valtab[];  // of length slen 
}; 


int main (int argc, char **argv) { 
    printf ("start %s, argc=%d", argv[0], argc); 
    struct boxint_st *iv42 = malloc (sizeof (struct boxint_st)); 
    iv42->tag = tag_int; 
    iv42->ival = 42; 
    struct boxstring_st *istrhello = 
    malloc (sizeof (struct boxstring_st) + sizeof ("hello") + 1); 
    istrhello->tag = tag_string; 
    strcpy (istrhello->strval, "hello"); 
    struct boxsequence_st *iseq3 = 
    malloc (sizeof (struct boxsequence_st) + 3 * sizeof (myval_t)); 
    iseq3->tag = tag_sequence; 
    iseq3->slen = 3; 
    iseq3->valtab[0] = (myval_t)iv42; 
    iseq3->valtab[1] = (myval_t)istrhello; 
    iseq3->valtab[2] = (myval_t)NULL; 
    printf ("before %s:%d gdb print iseq3\n", __FILE__, __LINE__); 
} 

यहाँ मेरी अजगर फ़ाइल gdb

# file tiny-gdb.py in the public domain by Basile Starynkevitch 
## see also tiny.c file 
class my_val_Printer: 
    """pretty prints a my_val""" 
    def __init__ (self, val): 
     self.val = val 
    def to_string (self): 
     outs = "[email protected]" + self.val['ptr'] 
     mytag = self.val['ptag'].dereference(); 
     if (mytag): 
      outs = outs + mytag.to_string() 
    def display_hint (self): 
     return 'my_val' 

def my_val_lookup(val): 
    lookup = val.type.tag 
    if (lookup == None): 
     return None 
    if lookup == "my_val": 
     return my_val_Printer(val) 
    return None 

मैं नीचे पढ़ने के लिए है निम्नलिखित बुनियादी सवालों के साथ अटक गया।

  1. जीडीबी के तहत पाइथन में अपना सुंदर प्रिंटर कैसे स्थापित करें? (मैं प्रलेखन में कई तरीकों को देख रहा हूं, और मैं उचित विकल्प नहीं चुन सकता)।
  2. यह सुनिश्चित करने के लिए कि जीडीबी union my_un और इसके टाइपपीफ-एड समानार्थी myval_t दोनों को सुंदर प्रिंट करता है।
  3. सुंदर प्रिंटर को नल पॉइंटर्स का पता कैसे लगाया जाना चाहिए?
  4. मेरा सुंदर प्रिंटर struct boxsequence_st के लिए कैसे कर सकता है? इसका मतलब यह है कि पॉइंटर गैर-शून्य है, फिर ptag को संदर्भित करते हुए, उस टैग की तुलना tag_sequence से करें, valtab लचीली सरणी सदस्य को बहुत प्रिंट करना।
  5. सुंदर प्रिंटिंग को बहुत गहराई से पुनः प्राप्त करने से कैसे बचें?

उत्तर

9

मेरे पास यह जवाब देने के लिए जीडीबी पायथन एपीआई के साथ पर्याप्त अनुभव नहीं है; मैं इसे एक साथी डेवलपर से कुछ शोध नोटों पर विचार करता हूं। नीचे मेरा कोड काफी कच्चे और बदसूरत है। हालांकि, यह 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 फ़ाइल के ऊपर के लिए इस्तेमाल किया:

  1. कैसे सुंदर-प्रिंटर 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 जोड़ने के लिए काफी उबालता है, मानते हुए कि आप रखते हैं यह एक फ़ाइल के रूप में। अधिक जटिल सुंदर-प्रिंटर के लिए, निर्देशिका लेआउट का उपयोग करना एक अच्छा विचार हो सकता है।

  2. यदि आपके पास val मान है, तो val.typegdb.Type ऑब्जेक्ट का प्रकार बताता है; स्ट्रिंग में इसे परिवर्तित करने से मानव-पठनीय प्रकार का नाम पैदा होता है।

    val.type.strip_typedefs() छिपे हुए सभी टाइपिफ के साथ वास्तविक प्रकार उत्पन्न करता है। मैंने .unqualified() भी जोड़ा, ताकि सभी कॉन्स/अस्थिर/आदि। प्रकार क्वालीफायर हटा दिए जाते हैं।

  3. नल पॉइंटर का पता लगाना थोड़ा मुश्किल है।

    मुझे मिला सबसे अच्छा तरीका लक्ष्य gdb.Value ऑब्जेक्ट के स्ट्रिंग .address सदस्य की जांच करना था, और देखें कि यह "0x0" है या नहीं।

    जीवन को आसान बनाने के लिए, मैं एक साधारण deref() फ़ंक्शन लिखने में सक्षम था, जो एक सूचक को हटाना चाहता है। यदि लक्ष्य (शून्य *) 0 को इंगित करता है, तो यह स्ट्रिंग "NULL" देता है, अन्यथा यह लक्ष्य gdb.Value ऑब्जेक्ट देता है।

    तरह से मैं deref() का उपयोग तथ्य यह है कि "array" प्रकार बहुत-प्रिंटर 2-tuples है, जहां पहले आइटम नाम स्ट्रिंग है की एक सूची उत्पन्न करते हैं, और दूसरे मद या तो एक gdb.Value वस्तु, या है पर आधारित है एक स्ट्रिंग। यह सूची सुंदर प्रिंटर ऑब्जेक्ट की children() विधि द्वारा वापस कर दी गई है।

  4. "भेदभाव संघ" प्रकारों को संभालना बहुत आसान होगा, यदि आपके पास सामान्य इकाई के लिए एक अलग प्रकार था।यही कारण है कि, अगर आप

    struct box_st { 
        enum tag_en tag; 
    }; 
    

    था और यह हर जगह इस्तेमाल किया गया था जब tag मूल्य अभी भी अनिश्चित है है; और विशिष्ट संरचना प्रकार केवल तभी उपयोग किए जाते हैं जहां उनके tag मान तय किए जाते हैं। यह एक बहुत ही सरल प्रकार की अनुमान की अनुमति देगा।

    जैसा कि tiny.cstruct box*_st प्रकारों में एक दूसरे के लिए उपयोग किया जा सकता है। (या, और अधिक विशेष रूप से, हम अकेले प्रकार के आधार पर एक विशिष्ट टैग मान पर निर्भर नहीं कर सकते।)

    अनुक्रम मामले वास्तव में काफी आसान है, क्योंकि valtab[] शून्य संकेत की एक सरणी के रूप में के रूप में बस इलाज किया जा सकता। अनुक्रम टैग का उपयोग सही संघ के सदस्य को चुनने के लिए किया जाता है। वास्तव में, अगर valtab [] बस एक शून्य पॉइंटर सरणी था, तो gdb.Value.cast (gdb.lookup_type()) या gdb.Value.reinterpret_cast (gdb.lookup_type()) प्रत्येक पॉइंटर प्रकार को आवश्यकतानुसार बदलने के लिए उपयोग किया जा सकता है , जैसे कि मैं बॉक्स किए गए संरचना प्रकारों के लिए करता हूं।

  5. रिकर्सन सीमाएं?

    आप @ ऑपरेटर का उपयोग 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 

उपयोग कर सकते हैं। चूंकि यह पहले से देखे गए नोड्स (पायथन सेट के रूप में) की एक सूची बनाए रखता है, यह रिकर्सिव संरचनाओं के बारे में परेशान नहीं होता है।

शायद मुझे पोस्ट करने से पहले मेरे पायथन स्निपेट को साफ़ करना चाहिए था, लेकिन कोई फर्क नहीं पड़ता। कृपया इन केवल प्रारंभिक परीक्षण संस्करणों पर विचार करें; अपने जोखिम पार इस्तेमाल करें। :)

+0

आपके उत्तर के लिए एक बड़ा धन्यवाद। मैंने अभी तक इसका परीक्षण नहीं किया है (क्योंकि वर्तमान में मुझे डीबग करने के लिए 'gdb' की आवश्यकता नहीं है), लेकिन यह बहुत जानकारीपूर्ण है! –

संबंधित मुद्दे