【问题标题】:How to find C++ language constructs allocating dynamic memory?如何找到分配动态内存的 C++ 语言构造?
【发布时间】:2018-04-13 08:54:48
【问题描述】:

我从事基于嵌入式系统固件代码的工作,目前使用动态内存分配。现在,出于可靠性原因,应删除所有动态内存分配。

作为第一步,我删除了 _sbrk 系统调用实现,这样所有动态分配都无法链接。由于我使用 -ffunction-sections -fdata-sections 进行编译并使用 -Wl,--gc-sections 进行链接,因此只要不发生动态内存分配,我希望链接步骤能够完成。 (这在过去的 C 项目中对我有用,在这里不确定 C++。)

尽管预期会出现以下链接器错误消息,但它无助于查找触发动态分配的 C++ 构造。

[...]arm-none-eabi/lib/thumb/v7e-m/fpv4-sp/hard/libc.a(lib_a-sbrkr.o): In function `_sbrk_r':
sbrkr.c:(.text._sbrk_r+0xc): undefined reference to `_sbrk'
collect2: error: ld returned 1 exit status

有没有一种有效的方法可以找到使用动态堆分配的 C++ 语言结构(除了明显使用 new)?

编辑:我正在寻找静态工具,而不是运行时检测/分析技术。

EDIT2:(因为有人删除了相应的标签:)该项目以 STM32F4 MCU 为目标,并使用基于 GCC 的 arm-none-eabi-* 工具链进行编译和链接。

【问题讨论】:

  • @Ron 正确,这些是棘手的部分。我正在寻找一种方法来获取错误消息或警告,这些错误消息或警告指向我的代码基础中此类构造的具体出现。例如在编译期间或通过静态分析等。
  • 为什么在嵌入式系统上有一个 sbrk 系统调用?这表明你有一些类似 Linux、类似 PC 的东西。
  • @Lundin 目标是STM32F4,_sbrk只是newlib分配内存的方式。在这种情况下,您还能期待什么?
  • 我原以为图书馆会自行处理分配。但相反,它显然做了一些臭名昭著的“系统”调用。至于触发_sbrk调用的函数,它似乎是_sbrk_r,所以可能你没有以干净的方式杀死这个库。调用它的 _sbrk 可能在同一个翻译单元中存在函数,即使应用程序不使用这些函数。
  • @Lundin :在 newlib 中,_sbrk 是用户提供的系统调用,以便能够使其适应各种托管和非托管环境。 sbrk 不做实际的分配或堆管理,它只是向堆管理器提供数据。 sourceware.org/newlib/libc.html#Syscalls 定义了独立系统的最小实现

标签: c++ embedded


【解决方案1】:

阻塞与 sbrk 的链接的问题在于 sbrk 是堆管理进程的最底层,不一定对所有分配都调用。它只是一种添加到现有堆池的方法。

new 操作符可能是重载的,或者是placement-new,所以甚至不需要通过系统堆执行动态分配或分配。但是 new 的实例至少会调用 malloc()

GNU 链接器--cref 的输出可能会有所帮助;这将告诉您每个符号,哪些文件引用它们。不幸的是,GNU 链接器没有 ARM 链接器所具有的 --callgraph 选项,该选项将显示通向任何特定函数的完整调用路径。

避免动态内存分配的显式链接可能意味着删除标准库的大部分 C++ 特定部分 - 当然是 STL 容器类,std::string

【讨论】:

  • 这是对问题的(长)评论,您没有提供任何答案。
  • @YSC - --cref 建议只是部分答案。这是不能令人满意的,因为它只会缩小到目标模块级别。 ARM 链接器会将其缩小到函数调用级别。
【解决方案2】:

有没有找到使用动态堆分配的 C++ 语言结构的有效方法?

是的,有:地块。

Massif 是一个内存分析器,可让您查明代码库中的内存分配位置。有了这样的清单,您可以更改所有有罪的代码并再次检查是否存在动态分配。

来自Massif (valgrind) documentation

Massif 是一个堆分析器。它测量您的程序使用了多少堆内存。 [...] 重要的是,Massif 不仅告诉您程序使用了多少堆内存,它还提供了非常详细的信息,表明程序的哪些部分负责分配堆内存。

默认情况下,Massif 不测量 [直接用于分配内存的低级系统调用] 但是,如果您希望测量 所有 程序使用的内存,可以使用 @987654322 @。

由于您在嵌入式系统上工作,它可能会使 massif 使用您的代码库有点困难;但这是可能的,要么通过 linux/windows 端口,要么通过在您的特定目标上运行 massif。

【讨论】:

  • 运行时分析一切都很好,但问题是确保没有使用动态内存分配的代码被链接,以及它在哪里找出正在执行动态分配的代码。
  • @Clifford 正如所引用的,massif 使您能够查明程序分配内存的位置。来自 OP:“现在,出于可靠性原因,应删除所有动态内存分配。” 这意味着 massif 将是从其代码库中删除所有动态分配的合适工具。您所说的链接只是 OP 设想的第一个解决方案。
  • @YSC 感谢您的想法。一个更简单的动态解决方案是简单地在 sbrk 函数处放置一个断点。然而,这种方法是不可行的,因为需要对嵌入式系统进行密集刺激才能运行代码的所有状态。即使对于小型系统,这也是一项不可能完成的工作。因此,我编辑了问题以询问静态分析技术。
  • @YSC :这是一个运行时检查。它只会检测执行期间发生的分配 - 除非您以 100% 的代码覆盖率运行测试,否则它不会检测所有分配。它可能会检测到其中的大多数。也就是说,让一个用于托管环境的工具在非托管环境中工作将是一个问题。非托管环境没有进程模型或加载器——代码通常是一个单一的整体链接。此外,如果该工具本身包含动态内存分配,我不会感到惊讶。
猜你喜欢
  • 2017-10-02
  • 2020-02-16
  • 2011-09-13
  • 2021-03-19
  • 2016-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-20
相关资源
最近更新 更多