我可以在 OS X 10.5 上使用 dtrace 来确定我的哪个 perl 子程序导致的内存分配最多吗?

Posted

技术标签:

【中文标题】我可以在 OS X 10.5 上使用 dtrace 来确定我的哪个 perl 子程序导致的内存分配最多吗?【英文标题】:Can I use dtrace on OS X 10.5 to determine which of my perl subs is causing the most memory allocation? 【发布时间】:2012-01-03 16:49:12 【问题描述】:

我们有一个相当大的 perl 代码库。

一些运行多个小时的进程(ETL 作业)突然开始消耗比平时更多的 RAM。分析相关版本的变化是一个缓慢而令人沮丧的过程。我希望通过更自动化的分析来找出罪魁祸首。

我们的实时环境是 Debian Squeeze 上的 perl 5.14。

不过,我可以使用很多 OS X 10.5 机器。 Dtrace 和 perl 似乎在这个平台上很好地结合在一起。似乎在 linux 上使用 dtrace 需要启动更多工作。我希望我们的实时系统和开发 OS X 系统之间的内存分配模式会相似——或者至少相似到足以帮助我找到这种新内存使用的来源。

这个幻灯片:

https://dgl.cx/2011/01/dtrace-and-perl

展示了如何使用 dtrace 来显示 perl sub 对 malloc 的调用次数。我有兴趣跟踪 perl 在进程生命周期内执行每个子程序时分配的内存总量。

关于如何做到这一点的任何想法?

【问题讨论】:

这>可能github.com/astletron/perl-dtrace-malloc/blob/master/… 人力资源部。那是在做>某事 我想我可能有这个工作。我运行 dtrace 的方式有问题,而不是我的 D 程序。我认为我现在拥有的输出是 malloc 请求的字节数,按子名称和子文件所在的文件细分,该子文件是在 malloc 之前输入的最后一个子文件。这并不完全简单,但看起来很有方向性。仍然可以使用来自碰巧路过的任何 dtrace 忍者的输入来验证我的工作就在这里。 【参考方案1】:

没有单一的方法可以做到这一点,并且逐个进行检查并不总是检查内存使用情况的最佳方法。我将推荐一组你可以使用的工具,一些工具可以作为一个整体工作,其他工具可以让你检查代码的单个部分或单个变量。

您可能需要考虑使用Valgrind。甚至还有一个名为 Test::Valgrind 的 Perl 模块,它可以帮助为您的 Perl 构建设置一个抑制文件,然后检查脚本中的内存泄漏或错误。

还有Devel::Size 可以完全满足您的要求,但是是在逐个变量的基础上而不是在逐个子的基础上。

您可以使用Devel::Cycle 在复杂数据结构中搜索无意的循环内存引用。虽然循环引用并不意味着您在使用对象时会浪费内存,但循环引用会阻止链中的任何内容在循环中断之前被释放。

Devel::Leak 比其他的更神秘一点,但它基本上可以让您获得有关在程序执行的两点之间创建和未销毁的任何SVs 的完整信息。如果您在子调用中检查这一点,您将知道该子例程分配的任何新内存。

您可能还想阅读 Perl 手册的 perldebguts 部分。

我真的无能为力,因为每个代码库最终都会有所不同。 Test::Valgrind 对于某些代码库非常有用,而在其他代码库中则非常糟糕。如果你打算尝试一下,我建议你使用最新版本的 Valgrind 并且 Perl >= 5.10,因为 Perl 5.8 和 Valgrind 历来相处得不太好。

【讨论】:

嗨@Dan。感谢您的快速回复。我不认为我有内存泄漏或循环引用。我认为很可能有人已经在包范围内声明了一个 hashef,并且随着进程的进行,它会慢慢被填满(可能是一个执行不善的“缓存”,没有最大大小)。寻找一种方法,让我确定我的哪些代码导致 perl 要求更多 RAM。我的理想输出将是一个完全限定的子名称列表,其中分配的 RAM 量是它们执行的副作用。我想我可能会用 dtrace 接近这个输出。【参考方案2】:

您可能想查看Memory::Usage 和Devel::Size

检查整个过程或子:

use Memory::Usage;
my $mu = Memory::Usage->new();

# Record amount of memory used by current process
$mu->record('starting work');

# Do the thing you want to measure
$object->something_memory_intensive();

# Record amount in use afterwards
$mu->record('after something_memory_intensive()');

# Spit out a report
$mu->dump();

或检查特定变量:

use Devel::Size qw(size total_size);

my $size = size("A string");

my @foo = (1, 2, 3, 4, 5);
my $other_size = size(\@foo);

my $foo = 
     a => [1, 2, 3],
     b => a => [1, 3, 4]
;
my $total_size = total_size($foo);

【讨论】:

嘿@Ranguard。我希望避免需要我在代码中添加更多检测的解决方案。这样很容易陷入代码/运行/分析/代码循环。也许我正在与风车作斗争,但我希望使用 dtrace 作为 perl 内存消耗的 NYTProf - 我可以运行它来分析我的代码而无需更改代码。【参考方案3】:

问题的答案是“是”。 Dtrace 可用于分析 perl 进程中的内存使用情况。

这个sn-p的代码:

https://github.com/astletron/perl-dtrace-malloc/blob/master/perl-malloc-total-bytes-by-sub.d

跟踪程序中每个子程序的调用和返回之间的内存使用量如何增加。作为额外的奖励,dtrace 似乎为您排序输出(至少在 OS X 上)。很酷。

感谢大家的参与。我自己回答了这个问题,因为这个问题确实是 dtrace/perl 特有的。

【讨论】:

嗨,您能解释一下您的 sn-p 中发生了什么吗?你的主要 perl 代码在哪里? 那个 sn-p 是用 D,dtrace 脚本语言编写的。它将 perl 子调用堆栈记录在一个数组中。每次调用 malloc 时,请求的内存量都会添加到与当前 perl sub 关联的总量中。当 dtrace 的程序结束时,在每个 sub 执行期间通过 malloc 请求的内存总量被打印出来。我认为这适用于任何 perl 代码(或任何使用适当 dtrace 陷阱的代码)。 这实际上是一个很好的答案。不要忘记接受它(即使你自己回答了)!【参考方案4】:

您可以基于Devel::CallTrace 编写一个简单的调试模块,打印输入的子程序以及当前进程的当前内存大小。 (使用 /proc 或其他。)

【讨论】:

嗨@Mithaldu。对于这种方法,我想我想记录从输入 sub 到离开 sub 的更改过程大小。绝对看看我如何以这种方式获得“类似分析器”的输出(内存增加代替执行时间)。感谢您的建议。如果基于 dtrace 的方法没有成功,我会试一试。

以上是关于我可以在 OS X 10.5 上使用 dtrace 来确定我的哪个 perl 子程序导致的内存分配最多吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Mac OS X 上构建 DTrace?

Mac OS X 上是不是有 dtrace ustack() 助手?

在本地主机(OS X 10.5)上设置通配符域?

是否可以从 DTrace 隐藏 OS X 应用程序?

无法在 Mac OS X 上通过 jinfo 启用 DTrace 探测

dtrace 影响,监控进程 (OS X)?