LLDB 数据格式化程序可以调用方法吗?
Posted
技术标签:
【中文标题】LLDB 数据格式化程序可以调用方法吗?【英文标题】:Can LLDB data formatters call methods? 【发布时间】:2014-08-18 18:52:48 【问题描述】:我正在使用 LLDB 调试 Qt 应用程序。在断点我可以写
(lldb) p myQString.toUtf8().data()
并查看 myQString 中包含的字符串,因为 data() 返回 char*。我希望能够写作
(lldb) p myQString
并获得相同的输出。这对我不起作用:
(lldb) type summary add --summary-string "$var.toUtf8().data()" QString
是否可以像这样编写一个简单的格式化程序,还是我需要知道 QString 的内部结构并编写一个 python 脚本?
或者,我应该以另一种方式使用 LLDB 以这种方式查看 QStrings 吗?
【问题讨论】:
在 XCode 中这个工作(char16_t*)((char*)$VAR->d + $VAR->d->offset):s
【参考方案1】:
以下确实有效。
首先,注册您的摘要命令:
debugger.HandleCommand('type summary add -F set_sblldbbp.qstring_summary "QString"')
这是一个实现
def make_string_from_pointer_with_offset(F,OFFS,L):
strval = 'u"'
try:
data_array = F.GetPointeeData(0, L).uint16
for X in range(OFFS, L):
V = data_array[X]
if V == 0:
break
strval += unichr(V)
except:
pass
strval = strval + '"'
return strval.encode('utf-8')
#qt5
def qstring_summary(value, unused):
try:
d = value.GetChildMemberWithName('d')
#have to divide by 2 (size of unsigned short = 2)
offset = d.GetChildMemberWithName('offset').GetValueAsUnsigned() / 2
size = get_max_size(value)
return make_string_from_pointer_with_offset(d, offset, size)
except:
print '?????????????????????????'
return value
def get_max_size(value):
_max_size_ = None
try:
debugger = value.GetTarget().GetDebugger()
_max_size_ = int(lldb.SBDebugger.GetInternalVariableValue('target.max-string-summary-length', debugger.GetInstanceName()).GetStringAtIndex(0))
except:
_max_size_ = 512
return _max_size_
【讨论】:
以下是我如何使其正常工作: 1. 使用这 3 个 python 函数和另一个函数def __lldb_init_module(debugger, internal_dict): debugger.HandleCommand('type summary add -x -F LLDBQstringOutput.qstring_summary "QString"')
制作一个名为 ~/LLDBQstringOutput.py 的脚本。 (-x 使命令包含 QString 引用和指针) 2. 创建一个文件 ~/.lldbinit 包含行 command script import ~/LLDBQstringOutput.py
试过了,它似乎不起作用:所有 QStrings 呈现为:(QString) $7 = b'u""'
【参考方案2】:
预计您尝试做的事情不会奏效。摘要字符串功能不允许调用表达式。
在调试器中调用表达式总是很有趣,在数据格式化程序中更是如此(如果您在 IDE 中 - 比如说 Xcode - 格式化程序会自动运行)。每次你在某个地方停下来,即使你只是跨过一条线,所有这些小表达式都会自动一遍又一遍地运行,性能成本很高——这甚至没有考虑到你的数据可能在一个有趣的状态已经存在并且正在运行的表达式有可能对其进行更多的改变,从而使您的调试会话变得比需要的更棘手。
如果上面的文字墙仍然没有让您灰心(:-)),您想编写一个 Python 格式化程序,并使用 SB API 来运行您的表达式。您的值是一个 SBValue 对象,它可以访问一个 SBFrame 和一个 SBTarget。这两者的组合允许您运行 EvaluateExpression("blah") 并取回另一个 SBValue,可能是一个 char*,然后您可以要求 GetSummary() 取回您的 c 字符串。
另一方面,如果您现在认为在格式化程序中运行表达式不是最理想的,那么好消息是 QString 肯定必须将其数据指针存储在某个地方.. 如果您发现它在哪里,您可以编写一个格式化程序为 $var.member1.member2.member3.theDataPointer 并获得相同的结果!
【讨论】:
QString 确实将数据存储在某处。不幸的是,您必须获取data
成员地址,添加offset
值,然后重新解释为(我认为)UTF16 字符串。我只部分成功地让它在 LLDB 格式化程序中工作,因为您需要的信息不是直接在 SBValue 中。
LLDB 确实支持 UTF16 字符串格式。有关其工作原理的示例,请查看llvm.org/svn/llvm-project/lldb/trunk/examples/summaries/…
我还认为 QString 主题不久前出现在 LLDB 邮件列表中,因此如果您查找列表档案,您可能会找到更具体的示例。但希望即使是一般示例也足以让您入门!【参考方案3】:
这是我在网上找到的一个 UTF16 字符串解释 lldb 脚本的试错改编(抱歉,我不记得出处 - 并且我不能相信作者)
请注意,这是适用于 Qt 4.3.2 和接近它的版本 - 因为“数据”指针的处理在那时和 Qt 5.x 之间发生了变化
def QString_SummaryProvider(valobj, internal_dict):
data = valobj.GetChildMemberWithName('d')#.GetPointeeData()
strSize = data.GetChildMemberWithName('size').GetValueAsUnsigned()
newchar = -1
i = 0
s = u'"'
while newchar != 0:
# read next wchar character out of memory
data_val = data.GetChildMemberWithName('data').GetPointeeData(i, 1)
size = data_val.GetByteSize()
e = lldb.SBError()
if size == 1:
newchar = data_val.GetUnsignedInt8(e, 0) # utf-8
elif size == 2:
newchar = data_val.GetUnsignedInt16(e, 0) # utf-16
elif size == 4:
newchar = data_val.GetUnsignedInt32(e, 0) # utf-32
else:
s = s + '<unexpected char size - error parsing QString>'
break
if e.fail:
s = s + '<parse error:' + e.why() + '>'
break
i = i + 1
if i > strSize:
break
# add the character to our string 's'
# print "char2 = %s" % newchar
if newchar != 0:
s = s + unichr(newchar)
s = s + u'"'
return s.encode('utf-8')
【讨论】:
:) 是的,我应该澄清这是 Qt 4.3.2 和 Yosemite。我将 Qt Creator 的原生格式化程序用于 Qt 5.x。以上是关于LLDB 数据格式化程序可以调用方法吗?的主要内容,如果未能解决你的问题,请参考以下文章
Python MySQLdb模块:不调用字符串格式可以插入或更新吗?