【问题标题】:How can I make gdb print unprintable characters of a string in hex instead of octal while preserving the ascii characters in ascii form?如何让 gdb 以十六进制而不是八进制打印字符串的不可打印字符,同时以 ascii 形式保留 ascii 字符?
【发布时间】:2013-04-08 12:10:18
【问题描述】:

假设我有一个缓冲区buf,其 c 字符串表示为

 char* buf = "Hello World \x1c"

当我使用命令p buf 在 gdb 中打印此 buf 时,我得到以下信息

 $1 = "Hello World \034"

是否有打印命令或 gdb 设置来代替打印以下内容?

$1 = "Hello World \x1c"

我尝试了/c/x等各种格式参数,但都没有达到我想要的效果。我也玩过 printf 但无法达到预期的效果。

更新:我正在使用“GNU gdb (GDB) 7.0.1-debian”。

更新: 我也玩过x。

如果我执行x/c,它会为不可打印字符打印八进制和十进制,然后使用 ascii 和十进制打印可打印字符。

如果我执行x/s,它的输出与 p 命令完全相同。

如果我使用x/x,它只会输出十六进制,但我们会丢失可打印部分的 ascii 字符。

更新:这个reference,除非不完整,否则暗示我想要的东西不可用,但有人可以确认吗?

【问题讨论】:

  • 看起来八进制在当时风靡一时。我问了a similar question for Emacs(感谢一位看到问题的人,Emacs 中添加了一个功能),现在在阅读 TeX 的源代码和查看 gdb 输出时再次遇到八进制。 :-) 感谢这个问题和答案。

标签: c string debugging assembly gdb


【解决方案1】:

您可以使用x 命令转储您的char 指针指向的内存:

(gdb) x/32xb buf

显示前 32 个字节。

(gdb) help x

了解详情。

【讨论】:

  • 我正在做一些 asm 调试,并试图通过使它们在字符串中并排时易于独立读取 hex 和 ascii 来最大化可读性。
【解决方案2】:

在没有现有解决方案的情况下,我创建了this gdb command,它为混合了可打印 ascii 和不可打印字符的字符串打印 ascii 和 hex。来源转载如下。

from __future__ import print_function

import gdb
import string
class PrettyPrintString (gdb.Command):
    "Command to print strings with a mix of ascii and hex."

    def __init__(self):
        super (PrettyPrintString, self).__init__("ascii-print",
                gdb.COMMAND_DATA,
                gdb.COMPLETE_EXPRESSION, True)
        gdb.execute("alias -a pp = ascii-print", True)

    def invoke(self, arg, from_tty):
        arg = arg.strip()
        if arg == "":
            print("Argument required (starting display address).")
            return
        startingAddress = gdb.parse_and_eval(arg)
        p = 0
        print('"', end='')
        while startingAddress[p] != ord("\0"):
            charCode = int(startingAddress[p].cast(gdb.lookup_type("char")))
            if chr(charCode) in string.printable:
                print("%c" % chr(charCode), end='')
            else:
                print("\\x%x" % charCode, end='')
            p += 1
        print('"')

PrettyPrintString()

要使用它,可以简单地把source AsciiPrintCommand.py 然后在gdb 中运行以下。为方便起见,可以把上面的源码命令放到他们的$HOME/.gdbinit中。

ascii-print buf
"Hello World \x1c"

【讨论】:

  • 我尝试了您的解决方案,放入文件 ~/.gdb-ascii-print.py。没有工作源〜/ .gdb-ascii-print0.py Traceback(最近一次调用最后):文件“〜/ .gdb-ascii-print0.py”,第31行,在文件“〜/ .gdb -ascii-print0.py",第 11 行,在 init 中 AttributeError: 'module' object has no attribute 'COMPLETE_EXPRESSION'
【解决方案3】:

OP 答案的一个小变化,用于 8 位数组的用例,不一定在第一次出现 \0 时终止,并且它也试图尊重 print elementsprint repeats GDB的参数:

from __future__ import print_function

def print_extended_ascii_char(charCode):
  if charCode == ord('"'): return r'\"'
  if charCode == ord('\\'): return r'\\'
  if 32 <= charCode <= 126: return "%c" % charCode
  return r"\x%02x" % charCode

def get_gdb_value(command, output_re):
  try:
    import re
    s = gdb.execute(command, to_string=True)
    m = re.match(output_re, s)
    value = m.group(1)
    if value != 'unlimited':
      value = int(value)
    return value
  except Exception as e:
    print("Sorry, ran into an error running '%s' and getting the value." % command)
    raise e

class PrettyPrintString(gdb.Command):
    """Command to print an array of 8-bit bytes, using ASCII when possible and hex otherwise.
    https://stackoverflow.com/a/54469844/4958"""

    def __init__(self):
        super (PrettyPrintString, self).__init__(name="ascii-print",
                command_class=gdb.COMMAND_DATA,
                completer_class=gdb.COMPLETE_EXPRESSION, prefix=True)

    def invoke(self, arg, from_tty):
        if not arg.strip():
            print("What do you want me to print?")
            return
        limit = get_gdb_value('show print elements', 'Limit on string chars or array elements to print is (.*).\n')
        repeats = get_gdb_value('show print repeats', 'Threshold for repeated print elements is (.*).\n')
        start = gdb.parse_and_eval(arg)
        p = 0
        print('"', end='')
        i = 0
        unprinted = (None, 0)
        while i < start.type.sizeof:
            i += 1
            charCode = int(start[p])
            toPrint = print_extended_ascii_char(charCode)
            if toPrint == unprinted[0]:
              unprinted = (toPrint, unprinted[1] + 1)
            else:
              if unprinted[0] is not None:
                print(unprinted[0] * min(unprinted[1], limit - (i - unprinted[1])), end='')
                if i > limit:
                  print('...', end='')
                  break
              unprinted = (toPrint, 1)
            p += 1
        if i - unprinted[1] > limit or unprinted[0] is None:
          print('"')
        elif repeats == 'unlimited' or unprinted[1] < repeats:
          print(unprinted[0] * unprinted[1], end='')
          print('"')
        else:
          print('",')
          print("'%s' <repeats %d times>" % (unprinted[0], unprinted[1] - 1))

PrettyPrintString()

和以前一样,将上述内容放入某个文件(例如~/.gdb-AsciiPrint.py),然后运行source ~/.gdb-AsciiPrint.py 或将该语句放入.gdbinit 文件中。 结果/比较:

(gdb) p tokmem[3]
$1 = "\000\030\000-1\320\a\200\031:\200\032[\200\024]\200\033\200\023;\200\034:\200\032[\200\023]\200\033\200\024;\320\r\200$:\200\030;\320\020\200 k\030\060\200!255\200\"\200\r\200\060(k:3,': \"');l\030k;\200+(\250\061)\200,\200\r\200\060(\200\034[\200\062],\200\034[\200\062]);\200+k<\f100\200,l\030k+\f100\200.\200+k<\f200\200,l\030k-\f100\200.\200\r\200*(k\200\063\061\066);\200\060(\200\034[l]);\200*(k\200\064\061\066);\200\020(\200$);\200\017;\200$\030\200$+2;\200\017;\200+l=\200\065\200,\200\060(\200\034[l],\200\034[l])\200.\200\060(\200\034[l]);\200\020(\200$);\200&('\"');\200\017", '\000' <repeats 65285 times>
(gdb) ascii-print tokmem[3]
"\x00\x18\x00-1\xd0\x07\x80\x19:\x80\x1a[\x80\x14]\x80\x1b\x80\x13;\x80\x1c:\x80\x1a[\x80\x13]\x80\x1b\x80\x14;\xd0\x0d\x80$:\x80\x18;\xd0\x10\x80 k\x180\x80!255\x80\"\x80\x0d\x800(k:3,': \"');l\x18k;\x80+(\xa81)\x80,\x80\x0d\x800(\x80\x1c[\x802],\x80\x1c[\x802]);\x80+k<\x0c100\x80,l\x18k+\x0c100\x80.\x80+k<\x0c200\x80,l\x18k-\x0c100\x80.\x80\x0d\x80*(k\x80316);\x800(\x80\x1c[l]);\x80*(k\x80416);\x80\x10(\x80$);\x80\x0f;\x80$\x18\x80$+2;\x80\x0f;\x80+l=\x805\x80,\x800(\x80\x1c[l],\x80\x1c[l])\x80.\x800(\x80\x1c[l]);\x80\x10(\x80$);\x80&('\"');\x80\x0f",
'\x00' <repeats 65285 times>

这有点老套,所以希望这个功能会被添加到 GDB 本身。

(旁白:前段时间我问过a similar question for Emacs,其中一位看到这个问题的人提交了a patch to Emacs。现在八进制似乎不像以前那么受欢迎了;例如,JavaScript 有@987654323 @ 其字符串文字的转义序列。)

【讨论】:

    【解决方案4】:

    对于其他对 GDB 中的八进制转义序列感到恼火的人,很容易修复(如果您准备自己构建 GDB):在 gdb/valprint.c 中,找到评论:

    /* If the value fits in 3 octal digits, print it that
                         way.  Otherwise, print it as a hex escape.  */
    

    并注释掉以下 4 行 - 所有转义序列都将打印为十六进制。

    【讨论】:

      【解决方案5】:

      根据您在字符串中显示非 ascii 字符的原因,稍微不同的方法可能是使用不同的编码打印字符串。

      在我的例子中,我想查看十六进制值,以确保在 UTF-8 环境中使用 ISO8859-1 对字符串进行编码。虽然GDB has encoding,但它不适合或不用于此目的。

      在探索由 OP 和 ShreevatsaR 创建的 gdb 命令时,我发现 Value.string() function 将 GDB 值打印为字符串(如果它是字符串)。你可以给它一个编码选项,以便下面的 Python 代码向 GDB 添加一个isostring 命令:

      from __future__ import print_function
      
      class PrettyPrintString(gdb.Command):
          """Command to print an array of 8-bit bytes as ISO8859-1"""
      
          def __init__(self):
              super (PrettyPrintString, self).__init__(name="isostring",
                      command_class=gdb.COMMAND_DATA,
                      completer_class=gdb.COMPLETE_EXPRESSION, prefix=True)
      
          def invoke(self, arg, from_tty):
              if not arg.strip():
                  print("What do you want me to print?")
                  return
              value = gdb.parse_and_eval(arg)
              print('"' + value.string("iso8859-1")+ '"')
      
      PrettyPrintString()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-07-19
        • 1970-01-01
        • 2012-09-25
        • 1970-01-01
        • 1970-01-01
        • 2014-06-24
        • 2017-08-30
        • 2018-04-04
        相关资源
        最近更新 更多