为大小字符串类型声明一个 lldb summary-string

Declare an lldb summary-string for a sized string type

我想要一个用于 nim 语言的内置字符串类型的格式化程序,但不知何故我未能提供它。 Nim 编译为 c,以及您在此处看到的字符串类型的 c 表示:

#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)
#  define SEQ_DECL_SIZE /* empty is correct! */
#else
#  define SEQ_DECL_SIZE 1000000
#endif
typedef char NIM_CHAR;
typedef long long int NI64;
typedef NI64 NI;
struct TGenericSeq {NI len; NI reserved; };
struct NimStringDesc {TGenericSeq Sup; NIM_CHAR data[SEQ_DECL_SIZE]; };

这是我在 lldb 会话中尝试的输出:

(lldb) frame variable *longstring
(NimStringDesc) *longstring = {
  Sup = (len = 9, reserved = 15)
  data = {}
}
(lldb) frame variable longstring->data
(NIM_CHAR []) longstring->data = {}
(lldb)  type summary add --summary-string "${&var[0]%s}" "NIM_CHAR []"
(lldb) frame variable longstring->data
(NIM_CHAR []) longstring->data = {}
(lldb)  type summary add --summary-string "${var%s}" "NIM_CHAR *"
(lldb) frame variable longstring->data
(NIM_CHAR []) longstring->data = {}
(lldb) frame variable &longstring->data[0]
(NIM_CHAR *) &[0] = 0x00007ffff7f3a060 "9 - 3 - 2"
(lldb) frame variable *longstring
(lldb)  type summary add --summary-string "${var.data%s}" "NimStringDesc"
(lldb) frame variable *longstring
(NimStringDesc) *longstring = NIM_CHAR [] @ 0x7ffff7f3a060
(lldb)  type summary add --summary-string "${&var.data[0]%s}" "NimStringDesc"
(lldb) frame variable *longstring
(NimStringDesc) *longstring = {
  Sup = (len = 9, reserved = 15)
  data = {}
}
(lldb) 

我根本无法管理,输出将只是解释为 '[=12=]' 终止的 c-string

的数据

您尝试过的摘要字符串语法(按设计)不如 C 语法丰富。

并且由于您使用的是零大小的数组,我认为我们没有任何神奇的规定可以将其视为指向字符串的指针。你可能想提交一个关于它的错误,但在这种情况下,它是否对你有帮助是有争议的。由于您的字符串是长度编码的,因此它实际上不需要以零结尾,这是 LLDB 开箱即用的唯一提示,可以知道何时停止读取指向字符的指针。

在你的情况下,你将不得不求助于 Python 格式化程序

您需要的东西是:

  • 字符串缓冲区的内存位置
  • 字符串缓冲区的长度
  • 一个从
  • 中读取内存的进程

这是一个非常小的 Python 片段 - 我也会为您提供增强建议,但让我们从基础开始:

def NimStringSummary(valobj,stuff):
    l = valobj.GetChildMemberWithName('Sup').GetChildMemberWithName('len').GetValueAsUnsigned(0)
    s = valobj.GetChildMemberWithName('data').AddressOf()
    return '"%s"'% valobj.process.ReadMemory(s.GetValueAsUnsigned(0),l,lldb.SBError())

如你所见,首先它读取了长度字段的值; 然后它读取数据缓冲区的地址;然后它使用值来自的过程来读取字符串内容,并且returns它在引号

现在,这是一个概念验证。如果您在生产中使用它,您很快就会 运行 遇到几个问题:

如果您的字符串缓冲区还没有初始化,它说缓冲区的大小是 20 GB 怎么办?您将不得不限制您愿意读取的数据的大小。对于类似字符串的类型,它具有 (char*, std::string, Swift.String, ...) 的内置知识 LLDB 打印出 t运行cated 缓冲区,后跟 ...,例如

  (const char*) buffer = "myBufferIsVeryLong"...

如果指向数据的指针无效怎么办?您应该检查 s.GetValueAsUnsigned(0) 实际上不是零 - 如果是,您可能想要打印一条错误消息,例如 "null buffer".

此外,这里我只是传递了一个 SBError,然后忽略它 - 传递一个然后检查它会更好 总而言之,你最终会得到这样的结果:

import lldb
import os

def NimStringSummary(valobj,stuff):
    l = valobj.GetChildMemberWithName('Sup').GetChildMemberWithName('len').GetValueAsUnsigned(0)
    if l == 0: return '""'
    if l > 1024: l = 1024
    s = valobj.GetChildMemberWithName('data').AddressOf()
    addr = s.GetValueAsUnsigned(0)
    if addr == 0: return '<null buffer>'
    err = lldb.SBError()
    buf = valobj.process.ReadMemory(s.GetValueAsUnsigned(0),l,err)
    if err.Fail(): return '<error: %s>' % str(err)
    return '"%s"' % buf

def __lldb_init_module(debugger, internal_dict):
    debugger.HandleCommand("type summary add NimStringDesc -F %s.NimStringSummary" % os.path.splitext(os.path.basename(__file__))[0])

一个额外的技巧是 __lldb_init_module 函数——只要你 'command script import' 一个 python 文件,LLDB 就会自动调用这个函数。这将允许您将 'command script import ' 添加到您的 ~/.lldbinit 文件并自动获取要在所有调试会话中选取的格式化程序

希望对您有所帮助!