为大小的字符串类型声明lldb摘要字符串

时间:2016-04-10 18:31:42

标签: debugging lldb

我想有一个用于nim语言的buildin字符串类型的格式化程序,但不知何故我提供它失败了。 Nim compilis to 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) 

我根本无法管理,输出只会被解释为'\0'终止的c-string

1 个答案:

答案 0 :(得分:2)

您尝试过的摘要字符串语法(按设计)并不像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())

如您所见,首先它读取长度字段的值; 然后它读取数据缓冲区的地址;然后它使用值来自的过程来读取字符串内容,并将其返回引号

现在,这是一个概念证明。如果你在生产中使用它,你会很快遇到一些问题:

如果您的字符串缓冲区尚未初始化,并且它表示缓冲区的大小为20千兆字节,该怎么办?您将不得不限制您愿意阅读的数据的大小。对于类似字符串的类型,它具有内置的知识(char *,std :: string,Swift.String,...)LLDB打印出截断的缓冲区,后跟......,例如

  (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函数 - 只要你命令脚本导入'python文件',LLDB就会自动调用这个函数。这将允许您将“命令脚本导入”添加到〜/ .lldbinit文件中,并自动获取在所有调试会话中拾取的格式化程序

希望这有帮助!