`dtrace` 如何探测内存分配 (Mac OS)
How does `dtrace` probe memory allocations (Mac OS)
有人知道 dtrace
用于跟踪 malloc
的功能/机制是什么吗?我正在尝试分析一段代码,我可以借助调试器和一些命令行脚本来完成,即:
sudo dtrace -n "pid`pgrep Mail | head -n 1`::malloc:entry { @sizes=quantize(arg0); }"
给我这样的东西:
dtrace: description 'pid31411::malloc:entry ' matched 4 probes
^C
value ------------- Distribution ------------- count
-1 | 0
0 | 214
1 | 7
2 | 191
4 | 1054
8 |@@@@ 15992
16 |@@@@@@@@@@@@ 44569
32 |@@@@@@@@@@ 37003
64 |@@@@ 15426
128 |@@@@ 15695
256 |@ 2616
512 |@ 1967
1024 |@ 1891
2048 |@@ 6010
4096 | 523
8192 | 43
16384 | 110
32768 | 19
65536 | 0
131072 | 69
262144 | 0
但这对我来说真的很乏味。我想知道如何从代码中以编程方式执行此操作。
我认为您对问题的看法是错误的。您的示例显示了对过程和函数的任意组合中的任意参数的相当复杂的解释——能够在 单行中执行此操作并且无需修改您自己的程序 非常强大。尝试让您自己的代码执行相同的分析是没有意义的:例如,如果您想要线性刻度而不是对数刻度,您会怎么做?也重新实现 lquantize()?
专注于编写您想要的代码,让 DTrace 进行分析。
编辑 回应第一条评论。
你举的例子执行路径非常迂回。非常广泛地,dtrace(1)
请求内核修改 malloc
的序言,以便在进入时调用线程陷入 DTrace 内核模块。在那里,在控制返回到检测线程之前,数据在每个 cpu 缓冲区内聚合。 dtrace
进程通过 libdtrace
定期请求内核的每个 CPU 缓冲区的快照,通过 ioctl(2)
。合并这些缓冲区然后渲染您看到的图形也是 libdtrace
执行的功能。在 macos 上,libdtrace
API,其中包括与内核交换的记录格式,是私有的。因此,即使是您的简单示例,重用任何此基础结构也是 "using a sledgehammer to crack a nut".
进一步的考虑是您将添加本身需要调试和维护的代码。如果您的代码足够复杂以至于需要自己的检测,那么似乎有可能有一天您会考虑 calloc()
、realloc()
和 mmap()
。也许您还想明确包含或排除对这些函数的调用,不仅是您自己的代码,还有它所链接的其他库。
最后,几乎总是最好将实现实际任务的代码与用于调试它的代码分开。一个示例方法是为 malloc()
编写自己的检测包装器,并将其放入一个共享对象中,您可以将其插入可执行文件和 libc
.
之间
pid 提供程序使用类似于调试器断点的机制。 Dtrace 附加到进程,就像调试器所做的那样。它在您的案例中找到 malloc 函数的第一条指令的地址,并在入口点插入陷阱指令。每当 malloc 被调用时,trap 指令触发控制转移到 dtrace 进程,dtrace 进程将 malloc 的第一个参数的值保存在其数据结构中以供以后聚合,最有可能在特定的特定 ABI 中找到参数的值受控进程状态的寄存器。 Dtrace 恢复在 malloc 入口处被陷阱指令替换的指令的原始操作码,使受控进程(您的应用程序)单步执行该指令,再次用陷阱替换它并让受控进程继续 运行.
至于你的后续问题"how to do this programmatically"。这与 dtrace 无关,但您可以查看 BDW garbage collector for C and C++ 并将其用作泄漏检测器,或者仅用作收集有关应用程序执行的内存分配信息的方法 http://www.hboehm.info/gc/leak.html 。最终,您可以在代码中实施类似且简化的方法,但事实证明,这可能比使用现有库更加乏味和复杂。
有人知道 dtrace
用于跟踪 malloc
的功能/机制是什么吗?我正在尝试分析一段代码,我可以借助调试器和一些命令行脚本来完成,即:
sudo dtrace -n "pid`pgrep Mail | head -n 1`::malloc:entry { @sizes=quantize(arg0); }"
给我这样的东西:
dtrace: description 'pid31411::malloc:entry ' matched 4 probes
^C
value ------------- Distribution ------------- count
-1 | 0
0 | 214
1 | 7
2 | 191
4 | 1054
8 |@@@@ 15992
16 |@@@@@@@@@@@@ 44569
32 |@@@@@@@@@@ 37003
64 |@@@@ 15426
128 |@@@@ 15695
256 |@ 2616
512 |@ 1967
1024 |@ 1891
2048 |@@ 6010
4096 | 523
8192 | 43
16384 | 110
32768 | 19
65536 | 0
131072 | 69
262144 | 0
但这对我来说真的很乏味。我想知道如何从代码中以编程方式执行此操作。
我认为您对问题的看法是错误的。您的示例显示了对过程和函数的任意组合中的任意参数的相当复杂的解释——能够在 单行中执行此操作并且无需修改您自己的程序 非常强大。尝试让您自己的代码执行相同的分析是没有意义的:例如,如果您想要线性刻度而不是对数刻度,您会怎么做?也重新实现 lquantize()?
专注于编写您想要的代码,让 DTrace 进行分析。
编辑 回应第一条评论。
你举的例子执行路径非常迂回。非常广泛地,dtrace(1)
请求内核修改 malloc
的序言,以便在进入时调用线程陷入 DTrace 内核模块。在那里,在控制返回到检测线程之前,数据在每个 cpu 缓冲区内聚合。 dtrace
进程通过 libdtrace
定期请求内核的每个 CPU 缓冲区的快照,通过 ioctl(2)
。合并这些缓冲区然后渲染您看到的图形也是 libdtrace
执行的功能。在 macos 上,libdtrace
API,其中包括与内核交换的记录格式,是私有的。因此,即使是您的简单示例,重用任何此基础结构也是 "using a sledgehammer to crack a nut".
进一步的考虑是您将添加本身需要调试和维护的代码。如果您的代码足够复杂以至于需要自己的检测,那么似乎有可能有一天您会考虑 calloc()
、realloc()
和 mmap()
。也许您还想明确包含或排除对这些函数的调用,不仅是您自己的代码,还有它所链接的其他库。
最后,几乎总是最好将实现实际任务的代码与用于调试它的代码分开。一个示例方法是为 malloc()
编写自己的检测包装器,并将其放入一个共享对象中,您可以将其插入可执行文件和 libc
.
pid 提供程序使用类似于调试器断点的机制。 Dtrace 附加到进程,就像调试器所做的那样。它在您的案例中找到 malloc 函数的第一条指令的地址,并在入口点插入陷阱指令。每当 malloc 被调用时,trap 指令触发控制转移到 dtrace 进程,dtrace 进程将 malloc 的第一个参数的值保存在其数据结构中以供以后聚合,最有可能在特定的特定 ABI 中找到参数的值受控进程状态的寄存器。 Dtrace 恢复在 malloc 入口处被陷阱指令替换的指令的原始操作码,使受控进程(您的应用程序)单步执行该指令,再次用陷阱替换它并让受控进程继续 运行.
至于你的后续问题"how to do this programmatically"。这与 dtrace 无关,但您可以查看 BDW garbage collector for C and C++ 并将其用作泄漏检测器,或者仅用作收集有关应用程序执行的内存分配信息的方法 http://www.hboehm.info/gc/leak.html 。最终,您可以在代码中实施类似且简化的方法,但事实证明,这可能比使用现有库更加乏味和复杂。