Bloatview

BloatView 是一款显示累积内存使用情况和泄漏信息的工具。如果发现泄漏,可以使用 引用计数跟踪和平衡 来查找根本原因。

如何使用 BloatView 构建

使用 --enable-debug--enable-logrefcnt 构建。

如何使用 BloatView 运行

可以使用两个环境变量。

XPCOM_MEM_BLOAT_LOG

如果设置,则会在程序退出时以及每次调用 nsTraceRefcnt::DumpStatistics 时打印 bloat 日志。此日志包含有关泄漏和膨胀(也称为使用情况)的数据。

XPCOM_MEM_LEAK_LOG

这类似于 XPCOM_MEM_BLOAT_LOG,但将日志限制为仅显示有关泄漏的数据。

您可以将这些环境变量设置为以下任何值。

  • 1 - 将日志输出到标准输出。

  • 2 - 将日志输出到标准错误。

  • 文件名 - 将日志写入文件。

读取单个 bloat 日志

完整的 BloatView 输出包含每个类的分配和引用计数统计信息,并提供有关按类细分的内存泄漏量的总数量。以下是 BloatView 输出示例。

== BloatView: ALL (cumulative) LEAK AND BLOAT STATISTICS, tab process 1862
    |<----------------Class--------------->|<-----Bytes------>|<----Objects---->|
    |                                      | Per-Inst   Leaked|   Total      Rem|
  0 |TOTAL                                 |       17     2484|253953338       38|
 17 |AsyncTransactionTrackersHolder        |       40       40|   10594        1|
 78 |CompositorChild                       |      472      472|       1        1|
 79 |CondVar                               |       24       48|    3086        2|
279 |MessagePump                           |        8        8|      30        1|
285 |Mutex                                 |       20       60|   89987        3|
302 |PCompositorChild                      |      412      412|       1        1|
308 |PImageBridgeChild                     |      416      416|       1        1|

第一行告诉您泄漏进程的进程 ID 以及进程类型。

以下是列的解释方式。

  • 第一个数字列 是泄漏类的索引

  • - 有问题的类的名称(截断为 20 个字符)。

  • 每个实例的字节数 - 如果您要编写 sizeof(Class),则返回的字节数。请注意,此数字不反映类持有的任何内存,例如内部缓冲区等。(例如,对于 nsString,您将看到头结构的大小,而不是字符串内容的大小!)

  • 泄漏字节数 - 每个实例的字节数乘以泄漏的对象数:(每个实例的字节数)x(剩余对象数)。使用此数字查找最严重的违规者。(应为零!)

  • 对象总数 - 给定类分配的对象总数。

  • 剩余对象数 - 给定类分配的对象中未删除的对象数。(应为零!)

值得关注的事项

  • 您的类是否在列表中? - 看!如果它们不在,那么您没有为 xpcom 对象使用 NS_IMPL_ADDREFNS_IMPL_RELEASE(或调用它们的 NS_IMPL_ISUPPORTS),或者为非 xpcom 对象使用 MOZ_COUNT_CTORMOZ_COUNT_DTOR。您的类不在列表中 不是 可以接受的。这意味着没有人查看它们,我们无法判断是否有人引入了泄漏。(请参阅 下文,了解如何解决此问题。)

  • 您的类的泄漏字节数应为零! - 我还需要多说吗?如果不是,您应该使用其他工具来修复它。

  • 剩余对象数可能不等于对象总数。 这可能表示手写 Release 方法(未使用 nsTraceRefcnt.h 中的 NS_LOG_RELEASE 宏),或者您可能只是没有释放分配的任何实例。这些类型的泄漏很容易修复。

  • 对象总数可能为 1。 这可能表示全局变量或服务。通常,这将具有大量的引用计数。

如果发现泄漏,可以使用 引用计数跟踪和平衡 来查找根本原因。

合并和排序 bloat 日志

您可以通过运行以下程序在浏览器中查看一个或多个 bloat 日志。

perl tools/bloatview/bloattable.pl *log1* *log2* \... *logn* >
*htmlfile*

这将生成一个 HTML 文件,其中包含类似于以下内容的表格(但添加了 JavaScript,以便您可以按列对数据进行排序)。

Byte Bloats

   ---------- ---------------- --------------------------
   Name       File             Date
   blank      `blank.txt`      Tue Aug 29 14:17:40 2000
   mozilla    `mozilla.txt`    Tue Aug 29 14:18:42 2000
   yahoo      `yahoo.txt`      Tue Aug 29 14:19:32 2000
   netscape   `netscape.txt`   Tue Aug 29 14:20:14 2000
   ---------- ---------------- --------------------------

这些数字不包括 malloc 分配的数据,例如字符串内容。

单击列标题可按该列排序。单击类名可查看该类的详细信息。

   -------------------- --------------- ----------------- --------- --------- ---------- ---------- ------------------------------- --------- -------- ---------- ---------
   Class Name           Instance Size   Bytes allocated                                             Bytes allocated but not freed
                                        blank             mozilla   yahoo     netscape   Total      blank                           mozilla   yahoo    netscape   Total
   TOTAL                                                                                            1754408                         432556    179828   404184     2770976
   nsStr                20              6261600           3781900   1120920   1791340    12955760   222760                          48760     13280    76160      360960
   nsHashKey            8               610568            1842400   2457872   1134592    6045432    32000                           536       568      1216       34320
   nsTextTransformer    548             8220              469088    1414936   1532756    3425000    0                               0         0        0          0
   nsStyleContextData   736             259808            325312    489440    338560     1413120    141312                          220800    -11040   94944      446016
   nsLineLayout         1100            2200              225500    402600    562100     1192400    0                               0         0        0          0
   nsLocalFile          424             558832            19928     1696      1272       581728     72080                           1272      424      -424       73352
   -------------------- --------------- ----------------- --------- --------- ---------- ---------- ------------------------------- --------- -------- ---------- ---------

第一组列,分配的字节数,显示第一个日志文件 (blank.txt) 中分配的内存量,第一个日志文件和第二个日志文件 (mozilla.txt) 之间的差异,第二个日志文件和第三个日志文件 (yahoo.txt) 之间的差异,第三个日志文件和第四个日志文件 (netscape.txt) 之间的差异,以及第四个日志文件中分配的内存总量。这些列提供了内存分配器工作强度的概念,但它们并不表示工作集的大小。

第二组列,分配但未释放的字节数,显示通过从分配的内存量中减去释放的内存量获得的净内存增益或损失。

显示对象显示引用按钮显示相同的统计信息,但计数对象或 AddRef 的引用而不是字节。

比较 Bloat 日志

您还可以比较任何两个 bloat 日志(程序关闭时产生的日志或写入 bloatlogs 目录的日志),方法是运行以下程序。

`perl tools/bloatview/bloatdiff.pl` <previous-log> <current-log>

这将为您提供以下格式的输出

 Bloat/Leak Delta Report
 Current file:  dist/win32_D.OBJ/bin/bloatlogs/all-1999-10-22-133450.txt
 Previous file: dist/win32_D.OBJ/bin/bloatlogs/all-1999-10-16-010302.txt
 --------------------------------------------------------------------------
 CLASS                     LEAKS       delta      BLOAT       delta
 --------------------------------------------------------------------------
 TOTAL                   6113530       2.79%   67064808       9.18%
 StyleContextImpl         265440      81.19%     283584     -26.99%
 CToken                   236500      17.32%     306676      20.64%
 nsStr                    217760      14.94%    5817060       7.63%
 nsXULAttribute           113048     -70.92%     113568     -71.16%
 LiteralImpl               53280      26.62%      75840      19.40%
 nsXULElement              51648       0.00%      51648       0.00%
 nsProfile                 51224       0.00%      51224       0.00%
 nsFrame                   47568     -26.15%      48096     -50.49%
 CSSDeclarationImpl        42984       0.67%      43488       0.67%

此“增量报告”显示泄漏违规者,从泄漏最多到泄漏最少进行排序。增量数字显示运行之间泄漏量和膨胀量的百分比变化(负数更好!)。膨胀数是通过将给定类的分配对象总数乘以类大小来确定的指标。请注意,虽然这并不一定是在任何给定时间消耗的内存量,但它确实指示了我们正在消耗多少内存。通常内存越多,性能和占用空间就越差。百分比 99999.99% 将显示为表示“无限”量的泄漏。当以前没有泄漏的东西现在开始泄漏时,就会发生这种情况。

BloatView 和持续集成

BloatView 在 Mozilla 在持续集成下运行的许多测试套件的调试版本上运行。如果发生新的泄漏,它将触发测试作业失败。

BloatView 的输出文件还可以显示泄漏对象是在哪里分配的。为此,应将 XPCOM_MEM_LOG_CLASSES 环境变量设置为 BloatView 表格中的类名

XPCOM_MEM_LOG_CLASSES=MyClass mach mochitest [options]

可以通过将 XPCOM_MEM_LOG_CLASSES 设置为以逗号分隔的名称列表来指定多个类名

XPCOM_MEM_LOG_CLASSES=MyClass,MyOtherClass,DeliberatelyLeakedClass mach mochitest [options]

测试套件脚本通常接受一个 --setenv 选项来指定环境变量,在某些情况下这可能更方便

mach mochitest --setenv=XPCOM_MEM_LOG_CLASSES=MyClass [options]

要在自动化中获取分配堆栈,您可以将相应的 --setenv 选项添加到您感兴趣的平台的测试配置中。这些配置位于 testing/mozharness/configs/ 中。您可能需要修改的最可能的配置列在下面

  • Linux:unittests/linux_unittest.py

  • Mac:unittests/mac_unittest.py

  • Windows:unittests/win_unittest.py

  • Android:android/androidarm.py

如何为 BloatView 为您的对象添加检测

首先,如果您的对象是 xpcom 对象,并且您使用 NS_IMPL_ADDREFNS_IMPL_RELEASE(或其变体)宏来实现您的 AddRefRelease 方法,那么您无需执行任何操作。默认情况下,这些宏直接支持 refcnt 日志记录。

如果您的对象不是 xpcom 对象,则需要进行一些手动编辑。以下示例代码显示了必须执行的操作

MyType::MyType()
{
  MOZ_COUNT_CTOR(MyType);
  ...
}

MyType::~MyType()
{
  MOZ_COUNT_DTOR(MyType);
  ...
}