在LLDB中查看数组:Xcode 4.1中等同于GDB的'@'运算符

92

我想查看由指针指向的元素数组。在GDB中,可以使用运算符“@”将指向的内存视为给定长度的人工数组来实现此目的。

*pointer @ length

length是我想要查看的元素数量。

上述语法在Xcode 4.1提供的LLDB中不起作用。

是否有任何方法可以在LLDB中实现上述功能?


1
快一年过去了,但lldb(我使用的是带有Xcode 4.3.3的LLDB-112.2)似乎仍然没有这种功能 - 我添加了一个赏金,希望有人能想出一个可用的解决方法(而不是回到gdb)。 - Paul R
9个回答

169

在lldb中,有两种方法可以做到这一点。

最常见的是使用parray lldb命令,它接受COUNTEXPRESSIONEXPRESSION将被求值并应该得出一个指向内存的指针。然后lldb将在该地址打印COUNT个相同类型的元素。例如:

parray 10 ptr

其中,ptr 的类型为 int *

另外一种方法是通过将指针强制转换为指向数组的指针来完成。

例如,如果你有一个 int* ptr,想将其视为包含十个整数的数组,可以这样做:

p *(int(*)[10])ptr

由于它仅依赖于标准的C功能,因此这种方法可以在没有任何插件或特殊设置的情况下工作。即使像GDB或CDB这样的其他调试器也有专门用于打印数组的语法,但它同样适用。


5
这是一个很好的答案-它值得获得更多点赞。不需要自定义脚本或其他任何东西,而且它甚至适用于结构体。 - Bill
22
对于那些使用Xcode GUI并且只显示第一个数据元素的指针的人,请按照以下步骤操作:右键单击数据指针 > 查看值为... > 自定义类型... 在表达式字段中输入 *(double(*)[10])value_type。这将打印出所指向的10个值。您可以修改double和10以获得所需的类型/数量。 - Andrew Hundt
感谢 @AndrewHundt 提供的 GUI 相关帮助。这正是我想要的。 - weezma2004
@weezma2004 如果你能点赞这条评论的话,我会非常感激 :-) @Siyuan Ren 或许可以将这些信息融入到你的回答中? - Andrew Hundt
@AndrewHundt 已完成。之前甚至不知道你可以点赞评论。 :) - weezma2004
@AndrewHundt:我认为直接将您的评论并入答案不是一个好主意,因为OP没有就GUI提出问题。评论部分足以提供人们可能需要的信息,但并不能回答问题本身。 - Siyuan Ren

36

从Xcode 8.0开始,lldb有了一个新的内置parray命令。因此,您可以输入以下内容:

(lldb) parray <COUNT> <EXPRESSION>

将指向EXPRESSION结果的内存打印为该表达式指向类型的COUNT个元素数组。

如果计数存储在当前帧中可用的变量中,则请记住您可以执行以下操作:

(lldb) parray `count_variable` pointer_to_malloced_array

这是lldb的通用功能,任何用反引号括起来的lldb命令行参数都会被视为一个表达式,该表达式返回一个整数,然后该整数在命令执行之前替换为该参数。


有没有一种方法可以永久设置这个变量,这样我就不必每次运行应用程序时都重新输入它到lldb命令提示符中了? - MarcusJ
不太确定你的意思。如果你有一个想要逐字使用多次的lldb命令,可以使用command alias来创建快捷方式。 - Jim Ingham

29
我唯一发现的方法是通过一个Python脚本模块:
""" File: parray.py """
import lldb
import shlex

def parray(debugger, command, result, dict):
    args = shlex.split(command)
    va = lldb.frame.FindVariable(args[0])
    for i in range(0, int(args[1])):
        print va.GetChildAtIndex(i, 0, 1)

在 lldb 中定义一个名为 "parray" 的命令:

(lldb) command script import /path/to/parray.py
(lldb) command script add --function parray.parray parray

现在你可以使用 "parray 变量 长度":

(lldb) parray a 5
(double) *a = 0
(double) [1] = 0
(double) [2] = 1.14468
(double) [3] = 2.28936
(double) [4] = 3.43404

2
提示:如果您需要在进行一些修改后重新加载脚本,请输入“script reload(parray)”(请参见http://www.libertypages.com/clarktech/?p=4303) - Raffi
@Raffi:谢谢你的提示。每个关于lldb/Python信息的链接都很有价值,因为官方文档仍然有限。 - Martin R
1
@MartinR 因为在我的实验中,值“a”必须是一个直接存在于堆栈帧中的指针,如果它是任何类型的表达式(例如指针转换、偏移量应用等),则无法工作。 - NoahR
1
当我尝试在结构体内打印数组时,我会得到AttributeError: 'NoneType' object has no attribute 'FindVariable'的错误。 - fpg1503

15

使用 Xcode 4.5.1 (也许现在对您有帮助,也可能没有) ,您可以在 lldb 控制台中执行以下操作:

(lldb) type summary add -s "${var[0-63]}" "float *"
(lldb) frame variable pointer
  (float *) pointer = 0x000000010ba92950 [0.0,1.0,2.0,3.0, ... ,63.0]

这个例子假设 'pointer' 是一个由64个浮点数组成的数组:float pointer[64];


2
我并不真正理解那里面的任何东西,但它确实起作用并且非常有帮助!你是从哪里学到这些厉害的lldb技巧的? - Nathan
这样做会不会使得从现在开始打印的每个float都显示为一个包含64个元素的数组? - uliwitness
是的,可以删除类型摘要当你不再需要它。这仍然比只看到第一个值要好。 - davidA

14

目前似乎还不支持该功能。

您可以使用内存读取函数(memory read / x),例如

(lldb) memory read -ff -c10 `test`

从该指针打印浮点数十次。 这应该与gdb的@具有相同的功能。


2
你可以使用反引号来评估指针表达式,例如:(lldb) memory read -ff -c10 \test` ` - pmdj
1
这应该是被接受的答案!它很容易使用,开箱即用。 - wxs
1
为了节省一些打字,可以使用 x/10f test - wardw

13

从Martin R的回答开始,我对其进行了改进,如下所示:

  1. 如果指针不是一个简单变量,例如:

    struct {
      int* at;
      size_t size;
    } a;
    

    那么 "parray a.at 5" 将会失败。

    我通过将 "FindVariable" 替换为 "GetValueForVariablePath" 来解决了这个问题。

  2. 现在,如果你的数组中的元素是聚合的呢,例如:

    struct {
      struct { float x; float y; }* at;
      size_t size;
    } a;
    

    然后,“parray a.at 5”会打印出:a.at->x, a.at->y, a.at[2], a.at[3], a.at[4],因为 GetChildAtIndex() 返回聚合体的成员。

    我通过在循环中解析“a.at”+“[”+str(i)+“]”,而不是解析“a.at”并检索其子元素来解决了这个问题。

    添加了一个可选的“first”参数(用法:parray [FIRST] COUNT),当您拥有大量元素时非常有用。

    在初始化时执行了“command script add -f parray.parray parray”。

    这是我的修改版本:

    import lldb
    import shlex
    
    def parray(debugger, command, result, dict):
      args = shlex.split(command)
      if len(args) == 2:
        count = int(args[1])
        indices = range(count)
      elif len(args) == 3:
        first = int(args[1]), count = int(args[2])
        indices = range(first, first + count)
      else:
        print 'Usage: parray ARRAY [FIRST] COUNT'
        return
      for i in indices:
        print lldb.frame.GetValueForVariablePath(args[0] + "[" + str(i) + "]")
    
    def __lldb_init_module(debugger, internal_dict):
      debugger.HandleCommand('command script add -f parray.parray parray')
    

较新版本的lldb(或者是Python)要求将first和count的赋值放在不同的行上。除此之外,这个代码很好用!感谢您的分享! - Kaelin Colclasure
我曾经为了将Martin R适应到我的特定情况而苦苦挣扎了一个小时,感谢GetValueForVariablePath提示!! - Raffi
非常出色的尝试,非常有用。对于大多数指针表达式,我对GetValueForVariablePath返回的“无值”感兴趣。我正在Xcode 5.0中使用lldb-300.2.47。对于int array[8]parry array 8返回“无值”,而print array [0]按预期工作。 - NoahR
1
我相信问题在于lldb.frame被设置在模块导入时,因此你需要获取当前帧的命令:target = debugger.GetSelectedTarget(),process = target.GetProcess(),thread = process.GetSelectedThread(),frame = thread.GetSelectedFrame()。然后使用frame.GetValueForVariablePath代替lldb.frame.GetValueForVariablePath。 - Dave Reed
@DaveReed 上面的评论解决了一部分问题。简单指针使用开始工作了(当前帧中的指针变量,无类型转换或算术运算)。我想要做更复杂的表达式,所以我将 GetValueForVariablePath 更换为 EvaluateExpression,因为我仍然看到 No value。现在这样的指针表达式可以正常工作:parray ((double*)sourcePointer+1) 5。根据API文档,两个函数的返回类型相同,因此 EvaluateExpression 似乎更好些。 - NoahR

5

我试图添加评论,但这对于发布完整答案并不好,所以我自己回答了。这解决了获取“无值”的问题。您需要获取当前帧,因为我认为lldb.frame在模块导入时设置,因此当您在断点处停止时,如果从.lldbinit加载模块,则没有当前帧。如果您在断点处停止时导入或重新加载脚本,则另一个版本将起作用。下面的版本应始终起作用。

import lldb
import shlex

@lldb.command('parray', 'command script add -f parray.parray parray')
def parray(debugger, command, result, dict):

    target = debugger.GetSelectedTarget()
    process = target.GetProcess()
    thread = process.GetSelectedThread()
    frame = thread.GetSelectedFrame()

    args = shlex.split(command)
    if len(args) == 2:
        count = int(args[1])
        indices = range(count)
    elif len(args) == 3:
        first = int(args[1])
        count = int(args[2])
        indices = range(first, first + count)
    else:
        print 'Usage: parray ARRAY [FIRST] COUNT'
        return

    for i in indices:
        print frame.GetValueForVariablePath(args[0] + "[" + str(i) + "]")

糟糕。在看到您的答案之前,我已经评论了您的评论。因此,简单的指针用法起作用了(当前帧中的指针变量,无类型转换或算术)。我想要执行更复杂的表达式,因此我将GetValueForVariablePath更改为EvaluateExpression,因为我仍然看不到任何值。现在类似这样的指针表达式可行:parray((double *)sourcePointer+1) 5。根据API文档,两个函数的返回类型是相同的,因此使用EvaluateExpression似乎是更好的选择。您同意吗? - NoahR
一个区别是EvaluateExpression的输出被分配给lldb变量,而数组索引没有被打印。因此,输出的行看起来像这样: (double) $68 = 0 - NoahR
1
@dave-reed,如何安装或附加此脚本到lldb?我应该将它保存在某个地方然后添加到.lldbinit文件中吗? - Shmidt

5

要检查变量,您可以使用frame variable命令(fr v是最短的唯一前缀),该命令具有一个-Z标志,可以完全满足您的需求:

(lldb) fr v buffer -Z5
(int64_t *) buffer = 0x000000010950c000 {
  (int64_t) [0] = 0
  (int64_t) [1] = 0
  (int64_t) [2] = 0
  (int64_t) [3] = 0
  (int64_t) [4] = 0
}

不幸的是,expression 不支持那个标志。


2

那么在这种情况下,你可能不妨编写自己的自定义 C 函数并使用以下方式调用:

call (int)myprint(args)

不用重新启动程序并重现错误,您可以将Python版本实时添加到LLDB会话中。 - Bill

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接