gdb PrettyPrinting APIで人生を飾る

そのgdbは、どうにかしてPythonで改善できます。ドキュメントを見たことがある人なら誰でも知っています。 そして、少なくとも一度斜めに見た人は、「プリティプリンタ」などのことを知っています。これにより、gdbはさまざまな複雑な構造を美しく印刷できます。 マニュアルには目を通しませんでしたが、実際には入りませんでした。 しかし、もう一度、次のようなものを入力します(MariaDBのソースからのすべての例、私は毎日何度もデビューしていますが、週末を除くこともあります)。



(gdb) p/t table->read_set->bitmap[0] @ (table->read_set->n_bits+7)/8
      
      





「でたらめはどうだ」と思った。 そしてすべてをまとめて......



PythonのPretty Printerはクラスです。 コンストラクターで、出力される値がコンストラクターに渡されます(これは単なるスカラーではなく、 gdb.Value



)。 また、このクラスにはto_string()



メソッドがあり、実際には、見たいものを返します。 たとえば、次のように:



 class BitmapPrinter: def __init__(self, val): self.val = val def to_string(self): s='' for i in range((self.val['n_bits']+7)//8): s = format(int(self.val['bitmap'][i]), '032b') + s return "b'" + s[-int(self.val['n_bits']):] + "'" class StringPrinter: def __init__(self, val): self.val = val def to_string(self): return '_' + self.val['str_charset']['name'].string() + \ ' "' + self.val['Ptr'].string('ascii', 'strict', self.val['str_length']) + '"'
      
      





それだけではありません。 このgdb.Value



の型をチェックし、クラスを使用するかどうかを決定する関数もあります。 簡単にするために、 gdb.printing



モジュールには、次のような正規表現を使用する既製の実装があります。



 import gdb.printing def build_pretty_printer(): pp = gdb.printing.RegexpCollectionPrettyPrinter( "my_library") pp.add_printer('String', '^String$', StringPrinter) pp.add_printer('bitmap', '^st_bitmap$', BitmapPrinter) return pp gdb.printing.register_pretty_printer( gdb.current_objfile(), my_library.build_pretty_printer()
      
      





引数には、 pp.add_printer()



名前、タイプの正規名、およびこのタイプを出力するクラスがあります。



私の好みのために-少し複雑。 クラスには多くの余計なものがあります。本当に必要なのはto_string()



メソッドだけです。他のすべてはクラスからクラスにコピーされます。 プリンターの登録コードも少し多く、タイプ名が3回続けて繰り返されます。



はい、ポインターは処理されません。 私はそれがgdbのようになりたいです:



 (gdb) p opt_tc_log_file $2 = 0x555556204273 "tc.log" (gdb) p table->field[0] $3 = (Field_varstring *) 0x555557437a88
      
      





つまり、ポインターがある場合、タイプ、アドレスが表示され、値は文字列と同じになります。 これを行うには、 StringPtrPrinter



BitmapPtrPrinter



2つのクラスをリベットしますto_string()



ほぼ同じですが、アドレスも表示します。 また、チェックを前後に追加することで、すでに書き込まれているものを展開して、 val



がポインターであるときにキャッチし、それに応じてアドレスを出力できるようにすることができます。 何らかの方法で、コピーしてコピーします。 クラスからクラスへの同じコード。 すぐに飽きて、すべてを単純化し始めました。 理想的には、これらのプリンターを次のように書きたいと思いました。



 @PrettyPrinter def String(val): return '_' + val['str_charset']['name'].string() + \ ' "' + val['Ptr'].string('ascii', 'strict', val['str_length']) + '"' @PrettyPrinter def st_bitmap(val): s='' for i in range((val['n_bits']+7)//8): s = format(int(val['bitmap'][i]), '032b') + s return "b'" + s[-int(val['n_bits']):] + "'"
      
      





ビジネスでのみ。 残りは単独で動作します。 しかし、それが私の手なのか、デコレータとgdb.printing



共謀したのかはgdb.printing



ませんが、ラッパーがラッパーの上に座ってラッパーを追いかけていることがgdb.printing



ました。



ここにコードがあります
 import gdb.printing def PrettyPrinter(arg): name = getattr(arg, '__name__', arg) def PrettyPrinterWrapperWrapperWrapper(func): class PrettyPrinterWrapperWrapper: class PrettyPrinterWrapper: def __init__(self, prefix, val, cb): self.prefix = prefix self.val = val self.cb = cb def to_string(self): return self.prefix + self.cb(self.val) def __init__(self, name, cb): self.name = name self.enabled = True self.cb = cb def __call__(self, val): prefix = '' if val.type.code == gdb.TYPE_CODE_PTR: prefix = '({}) {:#08x} '.format(str(val.type), long(val)) try: val = val.dereference() except: return None valtype = val.type.unqualified() if valtype.name == self.name: return self.PrettyPrinterWrapper(prefix, val, self.cb) if valtype.strip_typedefs().name == self.name: return self.PrettyPrinterWrapper(prefix, val, self.cb) return None pp = PrettyPrinterWrapperWrapper(name, func) gdb.printing.register_pretty_printer(None, pp, True) return func if callable(arg): return PrettyPrinterWrapperWrapperWrapper(arg) return PrettyPrinterWrapperWrapperWrapper
      
      







要するに、それはどのように機能しますか? デコレータは単に関数名を取得し、このタイプのきれいなプリンタを登録します。 関数自体とgdb.Value



値は、どちらもgdb.Value



クラスのコンストラクターに渡されます。 そしてto_string()



により、この値でこの関数を呼び出します。



さらに、プリティプリンタをgdbに登録するには、呼び出し可能な別のクラスが必要であり、使用するプリティプリンタを選択します。 最初の例では、これはネイティブのgdb-shny gdb.printing.RegexpCollectionPrettyPrinter



ですが、これは必要ないので、ポーカーと遊女の独自のクラスがあります。 値がポインタであるときを自動的に検出し、 (Typename *) address



を出力し(Typename *) address



。 そして、対応するPrettyPrinterWrapperを作成して返します。 typedefをサポートします。 もちろん、PrettyPrinterWrapperWrapperと呼ばれます。 それから私もそれが面白いと思った。



その後、 Alter_inplace_info::HA_ALTER_FLAGS



ような名前で判明したため、Pythonで関数を作成できなかったため、デコレーターに引数として型名を受け入れさせる必要がありました。



 @PrettyPrinter('Alter_inplace_info::HA_ALTER_FLAGS') def HA_ALTER_FLAGS(val): s='' ...
      
      





これを行う方法は? Pythonでは、そのようなデコレータは1つの引数で呼び出され、関数自体をデコレートする新しいデコレータを返します。 ここで私はすでに病気でしたが、止めるには遅すぎました。 デコレータが作成し、それ自体がPrettyPrinterWrapperWrapperオブジェクトを作成する関数は、当然ながらPrettyPrinterWrapperWrapperWrapperとして知られるようになりました。 幸い、この段階ですべてが機能し、ラッパーbacchanaliaは停止しました。



しかし、プリンター自体は今では簡単でエレガントに書かれています。



 @PrettyPrinter def sql_mode_t(val): s='' modes=['STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES', 'NO_ZERO_IN_DATE', 'TRADITIONAL', 'NO_AUTO_CREATE_USER', 'HIGH_NOT_PRECEDENCE', 'NO_ENGINE_SUBSTITUTION', 'PAD_CHAR_TO_FULL_LENGTH'] for i in range(0,len(modes)): if val & (1 << i): s += ',' + modes[i] return s[1:]
      
      





使用中、これらすべてが完全に自動的にデバッグに必要な美しさを誘発します:



 (gdb) p table->alias $1 = _binary "t3" (gdb) p &table->alias $2 = (String *) 0x7fffd409c250 _binary "t3" (gdb) p table->read_set[0] $3 = b'10011' (gdb) p thd->variables.sql_mode $4 = STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION (gdb) p ha_alter_info.handler_flags $5 = ADD_INDEX,DROP_INDEX,ADD_PK_INDEX,ALTER_STORED_COLUMN_ORDER
      
      





はい、自宅でそのようなデコレータを使用したい人のために-これはすべてPython 2です。3番目の場合、あなたはファイルでそれを処理する必要があります。 パッチは大歓迎です。



All Articles