为什么C++中的LLDB能够打印我的整个数据结构,但无法打印子组件?

问题描述 投票:0回答:1

我使用的是 Mac OS Sonoma 14.5,通过 x 代码工具安装了 LLDB 1500.0.404.7。我注意到我的 LLDB 的行为方式相当令人困惑。它能够完整地打印出一些 std:container,但无法打印出这些相同容器的子组件。特别是我能够执行以下

p primary_map
命令

(lldb) p primary_map
(std::unordered_map<int, std::unordered_set<int> >) size=2 {
  [0] = {
    first = 1
    second = size=2 {
      [0] = 5
      [1] = 4
    }
  }
  [1] = {
    first = 0
    second = size=2 {
      [0] = 2
      [1] = 1
    }
  }
}

显然 LLDB 可以向我展示整个地图。但出于某种原因,如果我执行

p primary_map[0]

(lldb) p primary_map[0]
error: Couldn't lookup symbols:
  __ZNSt3__113unordered_mapIiNS_13unordered_setIiNS_4hashIiEENS_8equal_toIiEENS_9allocatorIiEEEES3_S5_NS6_INS_4pairIKiS8_EEEEEixEOi

LLDB 无法呈现数据结构的子组件。

问题:

我想知道,为什么会发生这种情况,将来如何预防它,如果无法预防,什么是最好的解决方法。

背景:

我有一段示例代码。它基本上通过读取文件的内容来创建整数到整数集的映射。特别是文件中的行数和每行的整数数在编译时是未知的,只能在运行时通过读取文件本身来发现(不知怎的,我认为这很重要)。

工作驱动程序.cpp

#include <fstream>
#include <string>
#include <unordered_set>
#include <unordered_map>

using namespace std;

unordered_map<int, unordered_set<int> > primary_map;

int main() {
    ifstream input("sample_data_working.txt");
    int row_count; int col_count; int holder;
    input >> row_count; input >> col_count; 

    for (int i =0; i < row_count; i++) {
        primary_map[i] = unordered_set<int>{}; 
        for (int j = 0; j < col_count; j++) {
            input >> holder;
            primary_map[i].insert(holder);
        }
    }
    return 0;
}

该文件如下,其标题是一对数字(行数,列数),然后是包含数字的行。

样本数据工作.txt

2 2
1 2
4 5

重现问题:

我使用以下命令编译我的代码:

g++ -std=c++14 -g -O0 -fstandalone-debug working_driver.cpp -o working_driver
,它实际上调用了 clang(因为 Mac OS),版本为:Apple clang version 15.0.0 (clang-1500.3.9.4)。

然后执行

lldb working_driver
来启动 LLDB 实例。从这里,我通过
b 23
在 return 语句上设置一个断点,然后执行
r
命令,该命令继续前进并让我到达断点。在终端中,这应该类似于:

➜  differentiation git:(main) ✗ lldb working_driver
(lldb) target create "working_driver"
Current executable set to '/Users/sidharthghoshal/cp_training/USACO/python3/chapter4/2/stall4/differentiation/working_driver' (x86_64).
(lldb) b 23
Breakpoint 1: where = working_driver`main + 465 at working_driver.cpp:23:5, address = 0x0000000100000b91
(lldb) r
Process 3140 launched: '/Users/sidharthghoshal/cp_training/USACO/python3/chapter4/2/stall4/differentiation/working_driver' (x86_64)
Process 3140 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100000b91 working_driver`main at working_driver.cpp:23:5
   20               primary_map[i].insert(holder);
   21           }
   22       }
-> 23       return 0;
   24   }
Target 0: (working_driver) stopped.

正是在这一点上,混乱随之而来。

p primary_map
完全按照这个问题顶部的预期工作,但
p primary_map[0]
却没有。

我对问题本质的看法:

  1. 我过去见过

    p primary_map[0]
    命令在相同的结构
    unordered_map<int, unordered_set<int> >
    上工作,但形式略有不同(即循环中的硬编码上限
    row_count
    col_count
    )。因此,stackoverflow 上说
    primary_map[0]
    定义不明确的帖子并不完全令人满意,因为该命令有时确实有效,只是在这种情况下不起作用。不知何故,“运行时发现的循环边界”似乎是一个令人困惑的问题。

  2. 通常讨论的修复方法是编写自定义分配器或自定义解析器,我不明白的是为什么我必须这样做?显然,LLDB 为

    unordered_map<int, unordered_set<int> >
    提供的内置解析器在某些情况下就足够了。只是不是这个例子。我想知道一个为什么。然后我会深深感激如何

一些进一步的研究:

评分最高的答案这里澄清了很多。特别是

frame var primary_map[0]
的行为确实符合预期。似乎 LLDB 足够聪明,知道为我的某些代码打印 frame var,但其他时候它不够聪明,无法
frame var
,然后只是发出奇怪的符号错误。我不清楚运行时定义的循环边界与从
frame var
切换到
expr
有什么关系,但现在联系变得更加清晰了。
    

c++ clang llvm lldb
1个回答
0
投票
我想知道,为什么会发生这种情况,将来如何预防它,如果无法预防,最好的解决方法是什么。

我认为原因是,当您在控制台中输入
primary_map[0]

时,您正在编写一些调用

library
代码的内容。我的意思是,您使用的 operator[] 不是您在 C 样式数组上使用的内置
operator[]
。您需要
compile
primary_map[0] 调试器才能运行它。
另一方面,如果您查看 

primary_map

类的实现,您可能会了解如何深入数据结构以提取您想要的内容,但我不确定它总是可行的,并且这肯定不容易。

这是一个更简单的例子。如果您使用调试符号编译此代码,

#include <vector> int main() { std::vector<int> v{17,2,3}; return 0; }

然后在 
return

上放置一个断点,您会发现在控制台中键入

v[0]
会给出完全相同的错误。
但是,如果您研究 

std::vector

的实现,您会发现实际值存储在位于

v._M_impl._M_start
的 C 数组中,因此如果您输入
v._M_impl._M_start[0]
/
v._M_impl._M_start[1]
/
 v._M_impl._M_start[2]
在控制台中,确实会按预期打印
17
/
2
/
3
我在哪里看到

v

的值存储在

v._M_impl._M_start
中?好吧,最简单的方法是在
v
上放置一个观察器,调试器将显示如下:
Expression: v
 *- Result: size=3
   *- [0]: 1
   *- [1]: 2
   *- [2]: 3
   *+ [raw]: std::vector<int, std::allocator<int> >

raw

就是您想要研究的内容,所以让我们扩展它:

Expression: v
 *- Result: size=3
   *- [0]: 1
   *- [1]: 2
   *- [2]: 3
   *- [raw]: std::vector<int, std::allocator<int> >
      - std::_Vector_base<int, std::allocator<int> >: {...}
        - _M_impl: {...}
          - std::_Vector_base<int, std::allocator<int> >::_Vector_impl_data: {_M_start:0x000055555556d2b0, _M_finish:0x000055555556d2bc, ...}
            - _M_start: 0x000055555556d2b0
             *- *_M_start: 17
            + _M_finish: 0x000055555556d2bc
            + _M_end_of_storage: 0x000055555556d2bc

在这里,搜索 
17

,您会发现它等于

*_M_start
,因此
_M_start
是包含数据的 C 数组。那是在
_M_impl
下,在
v
下。
    

© www.soinside.com 2019 - 2024. All rights reserved.