实现一个中断驱动的采样分析器
Implementing an Interrupt driven Sampling Profiler
我正在尝试创建一个在linux上工作的采样分析器,我不确定如何发送中断或如何获得程序计数器(pc),因此我可以找出程序在中断时的位置。
我已经尝试使用信号(SIGUSR1, Foo*)和调用backtrace,但是当我raise(SIGUSR1)而不是程序正在运行的线程时,我得到了线程的堆栈。我真的不确定这是否是正确的方法……
任何建议吗?
如果您必须编写一个分析器,让我建议您使用一个好的(Zoom)作为您的模型,而不是一个坏的(gprof)。这些是它的特点。
有两个阶段。首先是数据收集阶段:
-
当它获取一个样本时,它读取整个调用堆栈,而不仅仅是程序计数器。
-
即使进程由于I/O,睡眠或其他任何原因而阻塞,也可以进行采样。
-
您可以打开/关闭采样,以便仅在您关心的时间进行采样。例如,在等待用户输入某些内容时,采样是没有意义的。
第二个是数据表示阶段。你所拥有的是一个堆栈样本的集合,其中堆栈样本是一个内存地址的向量,这些地址几乎都是返回地址。每个返回地址表示函数中的一行代码,除非它是在某些没有符号信息的系统例程中。
关键的有用信息是驻留分数(通常表示为百分比)。如果总共有m堆栈样本,并且代码行L出现在样本的n上的任何位置,则其驻留分数为n/m。这是成立的即使L在一个样本中出现了不止一次,那仍然只是它出现的一个样本。驻留分数的重要性在于它直接表明了语句L占用了多少时间。如果你取了m=1000个样本,L出现在n=300个样本上,那么L的居住分数是300/1000或30%。这意味着如果L可以被移除,总时间将减少30%。它通常被称为包括的百分比。
您不仅可以为代码行确定驻留分数,还可以为您可以描述的任何其他内容确定驻留分数。例如,代码行L位于某个函数F内。因此您可以确定函数的驻留分数,而不是代码行数。这样就得到了包含函数的百分比。你可以看看函数对,比如在样本的多少百分比上你看到函数F调用函数G。这将为您提供构成呼叫图的信息。
你可以从堆栈样本中得到各种各样的信息。一种常见的是"蝴蝶视图",在这种视图中,您将"焦点"放在一行L或函数F上,并在一边显示堆栈样本中紧邻它的所有行或函数,而在另一边显示紧邻它的所有函数行。在每一个上面,你都可以显示居住分数。您可以在这里单击,以尝试找到具有高驻留分数的代码行,您可以找到消除或减少这些代码行的方法。这就是提高代码速度的方法。
无论您为输出做什么,我认为允许用户实际检查其中的一小部分是非常重要的,这些是随机选择的。它们传达的信息比任何浓缩信息的方法都要深刻得多。知道分析器应该做什么很重要,知道不应该做什么也很重要,即使很多其他分析器都在做:
自我时间。一个无用的号码。看看一些合理大小的程序,你就知道为什么了。
调用计数。对于寻找具有高驻留分数的代码没有任何帮助,而且无论如何,您无法仅通过示例获得它。
高频采样。令人惊讶的是,很多人,当然是分析器构建者,认为获得大量的样本是很重要的。假设直线L是1000个样本的30%那么它真正的包容性百分比是30±1.4%。另一方面,如果它在10样品的30%上,则其包含百分比为30 +/- 14%。它仍然相当大——大到足以修复。在大多数分析器中发生的事情是人们认为他们需要"数值精度",所以他们采取了大量的样本并积累了他们所谓的"统计",然后扔掉了样本。这就像挖出钻石,称重,然后扔掉。真正的价值在于样本本身,因为它们能告诉你问题出在哪里。
可以使用目标线程的pthread_kill
和tid (gettid()
)向特定线程发送信号。
创建简单分析器的正确方法是使用setitimer
,它可以发送周期性信号(SIGALRM
或SIGPROF
),例如,每10毫秒;或者posix计时器(timer_create, timer_settime,或timerfd),不需要单独的线程来发送分析信号。检查google-perftools (gperftools)的源代码,它们使用setitimer或posix定时器,并通过回溯收集配置文件。
gprof也使用setitimer
来实现cpu时间分析。安排内核定期向进程发送信号(通常通过setitimer())")。
示例:gperftools源代码中setitimer
的代码研究结果:https://code.google.com/p/gperftools/codesearch#search/&q=setitimer&sq=package:gperftools&type=cs
void ProfileHandler::StartTimer() {
if (!allowed_) {
return;
}
struct itimerval timer;
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_usec = 1000000 / frequency_;
timer.it_value = timer.it_interval;
setitimer(timer_type_, &timer, 0);
}
你应该知道settimer有fork
和clone
的问题;它不适用于多线程应用程序。有尝试创建helper包装器:http://sam.zoy.org/writings/programming/gprof.html(错误的一个),但我不记得,它是否正常工作(setitimer
通常发送进程范围的信号,而不是线程范围的)。UPD:似乎自linux内核2.6.12以来,setitimer的信号被定向到整个进程(任何线程都可能得到它)。
要将信号从timer_create引导到特定的线程,需要gettid()
(#include <sys/syscall.h>
, syscall(__NR_gettid)
)和SIGEV_THREAD_ID flag
。不要检查如何使用thread_create创建周期性posix计时器(可能使用timer_settime和非零it_interval)。
PS:维基教科书中有一些概要分析:http://en.wikibooks.org/wiki/Introduction_to_Software_Engineering/Tools/Profiling
- Mongodb c++驱动程序:如何查询元素的数组
- C++ Windows 驱动程序MSB3030无法复制该文件,因为它找不到
- 使用mongocxx驱动程序时包含头文件问题
- 如何用RISC-V GD32VF103CBT6开发板卸载精确的ADC过采样
- void*到驱动程序中的UnicodeString
- 类成员和中断
- 捕获标准输出以压缩并使用 CTRL-C 中断会给出损坏的 zip 文件
- C++驱动程序看不到头文件
- 如何创建事件驱动的 SDL2 应用程序
- 用于创建/注册虚拟存储设备的 IOKit 驱动程序
- 我的代码运行良好,但在游戏循环中中断
- 使用带有MCP23017的 pigpio 进行中断读取
- 为什么我在 AVR 中的中断无法正常工作?
- 员工测试驱动程序数据结构
- 使用 UDP 中断 while()-循环
- C++:在不中断共享的情况下通过引用传递共享 PTR?
- 如何使用采样器立方体作为数组
- Qt5:如何创建基于qt应用程序中断驱动的gpio按钮
- 实现一个中断驱动的采样分析器
- 查找硬件中正在使用的中断驱动程序的命令是什么?