3

私は、Python で gdb 用のきれいなプリンターを作成しており、ゆっくりと方法論のコツをつかんでいます。このシステムがどのように機能し、メソッドから予想される結果の例を示す実際のドキュメントを見つけようとするのは、歯を抜くようなものです。あちこちで小片を見つけましたが、すべてを網羅しているものはありません。私が把握した情報の一部は、試行錯誤の結果であり、ゆっくりと進んでいます。

これまでのところ、プリティ プリンターは文字列のみを返すことが許可されているようですto_string()が (確かに)、 はaまたはとのペアをchildren()返すことができます。印刷されている /c++ オブジェクト。私は実際には、きれいなプリンター オブジェクトを返して、それを呼び出せることを望んでいましたが、残念ながら、そうではありませんでした。文字列を返すこともできますが、VSCode などの IDE でペイロード要素を折りたたみ可能にしたいので、値オブジェクトを返す必要があります。これに相当するのは、Natvis の合成アイテムですstringstringvaluevalue

バッファーである C++ クラスがあります。生、バイトベクトルが含まれており、読み取り可能な方法で処理する必要があります。

収集した制約を与えると、疑似型を使用してポインターをプロキシ値オブジェクトにラップできれば、バイトを使用可能な単位に分解できる可能性があります。ここに私が話していることのハードコードされた例があります:

#include <cstdint>
struct alignas(std::uint16_t) buffer {
  enum id : char { id1, id2 };
  // structure is: payload_size, id, payload[]
  char buf[11] = { 2, id1, 1, 0, 2, 3
                 , 0, id1
                 , 1, id2, 1
                 };
  char* end = std::end(buf);
};

int main() {
  buffer b;
  return 0;
}

ビッグエンディアンのマシンにブレークポイントを設定するとreturn 0;、次のようなものが表示されます。

(gdb) p b
$1 = buffer @ 0xaddre55 = { id1[2] = {1, 2, 3}, id1[0] = {}, id2 = {1} }

以下は、プリティ プリンターの python コードについて、これまでに得たものです。

class bufferPacketPrinter:
  def __init__(self, p_begin, p_end) -> None:
    self.p_begin = p_begin  # begining of packet
    self.p_end = p_end      # end of packet
    self.cmd_id       = self.p_begin[1].cast('buffer::id')
    self.payload_size = self.p_begin[0].cast('unsigned char').cast('int')

  def to_string(self):
    return 'packet {}[{}]' \
      .format(self.cmd_id, self.payload_size)

  def children(self):
    payload = self.p_begin + 2
    if self.cmd_id == 'id1':
      if self.payload_size == 0:
        return '{}'
      elif self.payload_size == 3:
        yield payload.cast(gdb.lookup_type('std::uint16_t').pointer())
        payload += 2
        yield payload[0].cast(gdb.lookup_type('unsigned char')).cast(gdb.lookup_type('int'))
        payload += 1
        return payload[0].cast(gdb.lookup_type('unsigned char')).cast(gdb.lookup_type('int'))
    elif self.cmd_id == 'id2':
      if self.payload_size == 1:
        return payload[0]
    return 'Invalid payload size of ' + str(self.payload_size)

class bufferPrinter:
  def __init__(self, val) -> None:
    self.val = val
    self.begin = self.val['buf'].cast(gdb.lookup_type('char').pointer())
    self.end = self.val['end']

  def to_string(self):
    return 'buffer @ {}'.format(self.val.address)
    
  def children(self):
    payload_size = self.begin[0].cast('unsigned char').cast('int')
    while self.begin != self.end:
      yield ??? # <=== Here is where the magic that I need is to happen
      self.begin += 2 + payload_size

(この API だけでなく python も勉強中なので、エラーがあれば教えてください。)

最後から2番目の行yield ???は、私が立ち往生しているものです。何か案は?これでダメなら別の方法を教えてください。

4

2 に答える 2

0

疑似型の代わりに、自分で入れなければならなかった実際の型を使用していることを除いて、私は自分がやりたかったことをすることができました。したがって、次の C++ コードが与えられます。

#include <cstdint>
#include <vector>
struct alignas(std::uint16_t) buffer {
  enum id_e : char { id1 = 6, id2 };
  struct packet_header_t { unsigned char payload_size; id_e id; };
  // structure is: payload_size, id, payload[]
  char buf[13] = { 4, id1, 1, 0, 2, 3
                 , 0, id1
                 , 0, id1
                 , 1, id2, 1
                 };
  char* end = std::end(buf);
};

int main() {
  buffer b;
  // Have to use types buffer::packet_header_t and buffer::id_e or they aren't
  // saved in the symbol table.
  buffer::packet_header_t y = {};
  buffer::id_e x = buffer::id1;
  return 0;
}

そして、このきれいなプリンター:

class bufferPrinterSuper:
  # Shared code between pretty-printers
  meaning = {
    #              +-- packet info
    #              |     +-- payload info
    #              |     | +-- element info
    #              v     v v
    'buffer::id1': { 0 : [                            ]                      
                   , 4 : [ [2, 'uint16_t*'            ]       
                         , [1, 'unsigned char*', 'int']  
                         , [1, 'unsigned char*', 'int']
                         ] 
                   }                                  
  , 'buffer::id2': { 1 : [ [1, 'unsigned char*', 'int']
                         ]
                   }
  }

  def payload_size(self, packet_it):
    return int(packet_it[0] \
      .cast(gdb.lookup_type('unsigned char')) \
      .cast(gdb.lookup_type('int')))

  def cmd_id(self, packet_it):
    return packet_it[1].cast(gdb.lookup_type('buffer::id_e'))

  def payload(self, packet_it):
    return packet_it + 2

  def get_value(self, it, element_info):
    for i in range(1, len(element_info)):
      if element_info[i][-1] == '*':
        pointer = gdb.lookup_type(element_info[i][0:-1]).pointer()
        it = it.cast(pointer).dereference()
      else:
        assert it.type.strip_typedefs() != gdb.TYPE_CODE_PTR
        value = gdb.lookup_type(element_info[i])
        it = it.cast(value)
    return it

class bufferHeaderPrinter(bufferPrinterSuper):
  def __init__(self, val):
    self.val = val
    self.begin = self.val['payload_size'].address
    self.end = self.begin + self.payload_size(self.val['payload_size'].address)

  def to_string(self):
    return 'packet @ {}'.format(self.val.address)

  def children(self):
    packet_it = self.begin
    cmd_id = self.cmd_id(packet_it)
    if str(cmd_id) in self.meaning:
      payload_info = self.meaning[str(cmd_id)]
      payload_size = self.payload_size(packet_it)
      if payload_size in payload_info:
        payload_it = packet_it + 2
        payload_info = payload_info[payload_size]
        payload_counter = 0
        for element_info in payload_info:
          yield '[{}]' \
            .format(payload_counter), self.get_value(payload_it, element_info)
          payload_it += element_info[0]
          payload_counter += 1

        # Error handling
        if payload_it > packet_it + 2 + payload_size:
          yield 'ERROR: payload_info {} exceeds payload size {}' \
            .format(payload_info, payload_size), 0
        elif packet_it + 2 + payload_size > payload_it:
          bytes_unaccounted_for = (packet_it - payload_it + 2 + payload_size) 
          # A warning because they could be padding
          yield "WARNING: payload_info doesn't account for {} bytes: {}" \
            .format(bytes_unaccounted_for
                , '['
                + ', '.join('{:02x}'.format(int(payload_it[i]))
                            for i in range(0, bytes_unaccounted_for))
                + ']'), 0
      else:
        yield 'ERROR: Size {} for id {} not recognized.'.format(payload_size, cmd_id), 0
    else:
      yield 'ERROR: Command {} not recognized.'.format(cmd_id), 0


class bufferPrinter(bufferPrinterSuper):
  def __init__(self, val) -> None:
    self.val = val
    self.begin = self.val['buf'].cast(gdb.lookup_type('char').pointer())
    self.end = self.val['end']

  def to_string(self):
    return 'buffer @ {}'.format(self.val.address)

  def children(self):
    packet_it = self.begin
    packet_counter = 0
    while packet_it < self.end:
      cmd_id = self.cmd_id(packet_it)
      yield '[{}] {}({})' \
        .format(packet_counter, self.cmd_id(packet_it), self.payload_size(packet_it)) \
        , packet_it.cast(gdb.lookup_type('buffer::packet_header_t').pointer()).dereference()
      packet_counter += 1
      packet_it += 2 + self.payload_size(packet_it)

    if packet_it != self.end:
      yield 'ERROR', 'Jumped {} bytes past end.'.format(packet_it - self.end)
    return 

def my_pp_fn(val):
  if str(val.type) == 'buffer': return bufferPrinter(val)
  if str(val.type) == 'buffer::packet_header_t': return bufferHeaderPrinter(val)

gdb.pretty_printers.append(my_pp_fn)

次の出力が得られます。

(gdb) p b
$1 = buffer @ 0x8c47fffa20 = {[0] buffer::id1(4) = packet @ 0x8c47fffa20 = {[0] = 1, [1] = 2, [2] = 3}, [1] buffer::id1(0) = packet @ 0x8c47fffa26, [2] buffer::id1(0) = packet @ 0x8c47fffa28, [3] buffer::id2(1) = packet @ 0x8c47fffa2a = {[0] = 1}}

これに関するいくつかの問題は、シンボル テーブルに残るように、このヘッダー タイプを確実に使用する必要があることです。これは、オプティマイザーが不要と判断した場合に削除する可能性があるため、少し注意が必要です。実際には、プログラムのオペレーターには必要ありません。これはデバッグのためだけです。

疑似型を生成する方法、または折りたたみ可能な子を生成する他の方法を誰かが教えてくれない限り、これを答えとしてマークする必要があると思います。はぁ

于 2021-11-29T23:25:07.773 に答える