【问题标题】:View array in LLDB: equivalent of GDB's '@' operator in Xcode 4.1在 LLDB 中查看数组:相当于 Xcode 4.1 中 GDB 的“@”运算符
【发布时间】:2011-10-27 02:11:30
【问题描述】:

我想查看一个指针指向的元素数组。在 GDB 中,这可以通过使用运算符 '@' as 将指向的内存视为给定长度的人工数组来完成

*pointer @ length

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

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

有什么方法可以在 LLDB 中完成上述操作吗?

【问题讨论】:

  • 将近一年后,still 似乎在 lldb 中没有这种功能(我正在使用带有 Xcode 4.3.3 的 LLDB-112.2) - 添加赏金,希望有人能想出一个可用的解决方法(除了回到 gdb)。

标签: xcode debugging xcode4 llvm lldb


【解决方案1】:

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

最常见的是,您使用parray lldb 命令,该命令采用COUNTEXPRESSIONEXPRESSION 被评估并且应该产生一个指向内存的指针。然后 lldb 将在该地址打印该类型的 COUNT 项目。例如

parray 10 ptr

其中ptr 的类型为int *

或者,可以通过将指针转换为指向数组的指针来完成。

例如,如果您有一个int* ptr,并且希望将其视为一个由十个整数组成的数组,您可以这样做

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

由于它仅依赖于标准 C 功能,因此此方法无需任何插件或特殊设置即可工作。它同样适用于 GDB 或 CDB 等其他调试器,尽管它们也具有用于打印数组的专用语法。

【讨论】:

  • 这是一个很好的答案 - 它值得更多的支持。不需要自定义脚本或任何东西,它甚至可以与结构一起使用。
  • 对于那些使用 Xcode GUI 并且指针只显示第一个数据元素的用户,请执行以下操作:right click on data pointer > View value as... > Custom Type... 在表达式字段中输入*(double(*)[10])value_type。这将打印出指向的 10 个值。您可以将 double 和 10 修改为您想要的类型/数量。
  • 感谢@AndrewHundt 提供与 GUI 相关的帮助。这正是我想要的。
  • @weezma2004 如果您能对评论进行投票,我将不胜感激 :-) @Siyuan Ren 也许这些信息可以包含在您的答案中?
  • @AndrewHundt 完成。直到现在才知道你可以投票给 cmets。 :)
【解决方案2】:

从 Xcode 8.0 中的 lldb 开始,有一个新的内置 parray 命令。所以你可以说:

(lldb) parray <COUNT> <EXPRESSION>

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

如果计数存储在当前帧中可用的变量中,请记住您可以这样做:

(lldb) parray `count_variable` pointer_to_malloced_array

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

【讨论】:

  • 有没有办法永久设置这个变量,这样我每次运行我的应用程序时就不必在 lldb 命令提示符中重新输入这个变量?
  • 不太清楚你的意思。如果你有一个 lldb 命令,你想多次逐字使用,你可以使用command alias 来创建一个快捷方式。
【解决方案3】:

我找到的唯一方法是通过 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

现在你可以使用“数组可变长度”:

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

【讨论】:

  • 提示:如果您需要在修改后重新加载脚本,请输入“script reload(parray)”(参见libertypages.com/clarktech/?p=4303
  • @Raffi:谢谢你的提示。并且每个指向 lldb/Python 信息的链接(如果有价值),因为官方文档仍然有限。
  • @MartinR 因为在我的实验中,值 'a' 必须是存在于堆栈帧中的直接指针,如果它是任何类型的表达式,则它不起作用。 (例如指针转换、应用偏移等)
  • 当我尝试在结构中打印一个数组时,我得到AttributeError: 'NoneType' object has no attribute 'FindVariable'
【解决方案4】:

使用 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]

此示例假设“指针”是一个 64 个浮点数的数组:float pointer[64];

【讨论】:

  • 我对那里的任何东西都不太了解,但它可以工作并且非常有帮助!你从哪里学到这么棒的 lldb 技巧?
  • 不会让 every float* 从现在开始打印为 64 个元素的数组吗?
  • 是的,确实如此。当您不再需要它时,您可以删除类型摘要。还是比只看到第一个值要好。
【解决方案5】:

好像还不支持。

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

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

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

【讨论】:

  • 您可以使用反引号来计算指针表达式,例如:(lldb) memory read -ff -c10 `test`
  • 这应该是公认的答案!这很简单,开箱即用
  • 并节省一些输入x/10f test
【解决方案6】:

从 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”然后检索其子项来解决此问题。

  3. 添加了一个可选的“第一个”参数(用法:parray [FIRST] COUNT),这在您拥有大量元素时很有用。

  4. 让它在初始化时执行“命令脚本添加 -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)要求首先分配和计数在不同的行上。除此之外,这很好用!谢谢!
  • 为了让 Martin R 适应我的具体情况,我苦苦挣扎了一个小时,感谢 GetValueForVariablePath 提示!!
  • 伟大的尝试,非常有用。对于我感兴趣的大多数指针表达式,GetValueForVariablePath 正在返回No Value。我在 Xcode 5.0 中使用 lldb-300.2.47。对于 int array[8]parry array 8 返回 No Value 八次,而 print array[0] 按预期工作。
  • 我认为问题在于 lldb.frame 是在模块导入时设置的,因此您需要使用命令来获取当前帧: target = debugger.GetSelectedTarget() process = target.GetProcess() thread = process.GetSelectedThread() frame = thread.GetSelectedFrame() 然后使用 frame.GetValueForVariablePath 而不是 lldb.frame.GetValueForVariablePath
  • @DaveReed 的上述评论解决了部分问题。简单的指针用法开始起作用。 (当前帧中的指针变量,没有类型转换或算术)。我想做更复杂的表达式,所以我将GetValueForVariablePath 更改为EvaluateExpression,因为我仍然看到No value。现在像这样的指针表达式可以工作:parray ((double*)sourcePointer+1) 5。根据 API 文档,这两个函数的返回类型相同,因此 EvaluateExpression 似乎是更好的选择。
【解决方案7】:

我尝试添加评论,但这对于发布完整答案并不好,因此我做出了自己的答案。这解决了获得“无价值”的问题。您需要获取当前帧,因为我相信 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,因为我仍然看到 No value。现在像这样的指针表达式有效:parray ((double*)sourcePointer+1) 5. 根据 API 文档,两个函数的返回类型相同,因此 EvaluateExpression 似乎是更好的方法。你同意吗?
  • 嗯,一个区别是EvaluateExpression 的输出被分配给 lldb 变量,并且不打印数组索引。因此,输出如下:(double) $68 = 0
  • @dave-reed,如何将此脚本安装或附加到 lldb?我应该把它保存在某个地方,然后添加到 .lldbinit 中吗?
【解决方案8】:

要检查变量,您可以使用 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 不支持该标志

【讨论】:

    【解决方案9】:

    此时,您不妨编写自己的自定义 C 函数并调用它:

    call (int)myprint(args)
    

    【讨论】:

    • 并非如此 - 您可以将 Python 版本实时添加到 LLDB 会话中,而无需重新启动程序并重现错误。
    猜你喜欢
    • 2012-11-01
    • 2012-08-25
    • 2011-10-11
    • 2017-01-08
    • 1970-01-01
    • 1970-01-01
    • 2013-10-20
    • 2012-04-02
    • 2011-12-28
    相关资源
    最近更新 更多