简体   繁体   English

如何让 gdb 以十六进制而不是八进制打印字符串的不可打印字符,同时以 ascii 形式保留 ascii 字符?

[英]How can I make gdb print unprintable characters of a string in hex instead of octal while preserving the ascii characters in ascii form?

Suppose I have a buffer buf whose c string representation is假设我有一个缓冲区buf其 c 字符串表示是

 char* buf = "Hello World \x1c"

When I print this buf in gdb using the command p buf , I get the following当我使用命令p buf buf 在 gdb 中打印这个 buf 时,我得到以下信息

 $1 = "Hello World \034"

Is there a print command or a gdb setting that will print the following instead?是否有打印命令或 gdb 设置来代替打印以下内容?

$1 = "Hello World \x1c"

I have tried various format parameters such as /c and /x , but none of them get the effect that I am looking for.我尝试了各种格式参数,例如/c/x ,但没有一个得到我正在寻找的效果。 I have also played with printf but was unable to achieve the desired effect.我也玩过 printf 但没能达到预期的效果。

Update: I am using "GNU gdb (GDB) 7.0.1-debian".更新:我正在使用“GNU gdb (GDB) 7.0.1-debian”。

Update: I have played with x as well.更新:我也玩过 x 。

If I do x/c it prints octal and decimal for nonprintable characters, and then prints printable characters with the ascii and decimal.如果我执行x/c它会为不可打印的字符打印八进制和十进制,然后用 ascii 和十进制打印可打印字符。

If I do x/s it outputs exactly the same as the p command.如果我执行x/s它的输出与 p 命令完全相同。

If I do x/x it just outputs hex but then we lose the ascii characters for the printable part.如果我执行x/x它只输出十六进制但是我们会丢失可打印部分的 ascii 字符。

Update: This reference , unless incomplete, suggests that what I desire is not available, but can anyone confirm?更新:除非不完整,否则此参考资料表明我想要的东西不可用,但有人可以确认吗?

You might use the x command to dump the memory your char -pointer points to:您可以使用x命令转储char指针指向的内存:

(gdb) x/32xb buf

shows the first 32 bytes.显示前 32 个字节。

See

(gdb) help x

for details.详情。

In the absence of an existing solution, I created this gdb command which prints ascii and hex for strings that have mixed printable ascii and non-printable characters.在没有现有解决方案的情况下,我创建了这个 gdb 命令,它为混合了可打印 ascii 和不可打印字符的字符串打印 ascii 和 hex。 The source is reproduced below.来源转载如下。

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()

To use this, one can simply put the source AsciiPrintCommand.py and then run the following in gdb.要使用它,可以简单地放置source AsciiPrintCommand.py ,然后在 gdb 中运行以下命令。 For convenience, one can put put the above source command into their $HOME/.gdbinit .为方便起见,可以将上述源命令放入他们的$HOME/.gdbinit

ascii-print buf
"Hello World \x1c"

For anyone else who shares the irritation with octal escape-sequences in GDB, it's easy to fix (if you're prepared to build GDB yourself): in gdb/valprint.c, find the comment:对于在 GDB 中对八进制转义序列感到恼火的其他人来说,修复很容易(如果您准备自己构建 GDB):在 gdb/valprint.c 中,找到评论:

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

and comment out the following 4 lines - all escape-sequences will then be printed as hex.并注释掉以下 4 行 - 然后所有转义序列都将打印为十六进制。

A small variation of the OP's answer, for the use-case of 8-bit arrays that don't necessarily terminate at the first occurrence of \\0 , and which tries to also respect the print elements and print repeats parameters of GDB: OP 答案的一个小变化,对于 8 位数组的用例,这些用例不一定在第一次出现\\0时终止,并且它试图也尊重 GDB 的print elementsprint repeats参数:

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()

As before, put the above in some file (say ~/.gdb-AsciiPrint.py ), and either run source ~/.gdb-AsciiPrint.py or put that statement in the .gdbinit file.和以前一样,将上面的内容放在某个文件中(比如~/.gdb-AsciiPrint.py ),然后运行source ~/.gdb-AsciiPrint.py或将该语句放在.gdbinit文件中。 Result/comparison:结果/比较:

(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>

This is slightly hacky, so hopefully the feature will get added to GDB itself.这有点hacky,所以希望这个功能会被添加到GDB本身。

(Aside: A while ago I asked a similar question for Emacs , and one of the people who saw the question submitted a patch to Emacs . It does seem that octal is less popular these days than it used to be; eg JavaScript has deprecated octal escape sequences for its string literals.) (旁白:不久前我为 Emacs提出了一个类似的问题,其中一个看到这个问题的人向 Emacs提交了一个补丁。现在八进制似乎不像以前那么流行了;例如 JavaScript 已经弃用了八进制其字符串文字的转义序列。)

Depending on your reason to show non-ascii chars in a string a slightly different approach could be to print the string using a different encoding.根据您在字符串中显示非 ascii 字符的原因,稍微不同的方法可能是使用不同的编码打印字符串。

In my case I wanted to see the hex values to ensure the string was encoded using ISO8859-1 in a UTF-8 environment.就我而言,我想查看十六进制值以确保字符串是在 UTF-8 环境中使用 ISO8859-1 编码的。 Although GDB has encoding , it is not intended, or useable, for this purpose.尽管GDB 具有 encoding ,但它并非旨在或可用于此目的。

While exploring the gdb commands created by the OP and ShreevatsaR I discovered the Value.string() function that prints a GDB value as a string if it's a string.在探索由 OP 和ShreevatsaR创建的 gdb 命令时,我发现了Value.string()函数,该函数将 GDB 值打印为字符串(如果它是字符串)。 You can give it an encoding option so the following Python code adds an isostring command to 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()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM