2017-07-25 211 views
3

我试图监视一个进程的执行情况,并在linux上使用perf_event来取样其已退役指令的数量。 我想随着时间的推移以固定的,恒定的采样周期对采样过程进行采样。如何以恒定速率采样perf_event_open?

一切似乎运作良好,我已经成功地编写的代码,它调用perf_event_open()并设置了两个不同的计数器:

  • 的领导者之一,属于PERF_TYPE_HARDWARE类型,确定为PERF_COUNT_HW_CPU_CYCLES,其中工程作为我的'采样'事件。我指定其取样周期是10000
  • 从属之一,代表的引退指令的数量的情况下(I指定其身份通过传递的十六进制数,其为0x00c4到perf_event)

Perf_event正确样品目标进程,允许我通过共享的内存部分mmap()ed来访问它的示例。

然后,我将获得的值写入文件并将它们保存为矩阵,而每个第n列对应第n个样本,并且对每一行进行全新的测量运行。

我在这里报告一些随机行从矩阵:

910 1963 3159 4384 5631 6858 8114 9362 10303 15511 21270 26984 32818 38601 44157 49837 55500 
768 1924 3161 4404 5651 6890 8133 9370 10274 14987 20740 26539 32350 38133 43692 49360 55085 
759 1801 3025 4274 5518 6755 8001 9246 10264 14171 19871 25563 31362 37102 42663 48310 53980 
617 1459 2322 3135 3972 4741 5518 6260 7043 7850 8657 9446 10218 11339 14040 16389 18924 21325 23831 26581 29174 31774 34566 37294 39873 42588 45290 47869 50523 53334 55784 57535 
855 2006 3248 4483 5729 6951 8198 9440 10334 15637 21401 27168 32979 38799 44341 50065 55756 
688 1463 2242 2978 3738 4376 5094 5811 6467 7151 7814 8488 9042 9741 10278 12218 14425 16995 19598 22327 24941 27632 30176 32780 35289 37703 40501 43329 45939 49551 55116 
748 1929 3175 4423 5666 6911 8150 9392 10272 14469 20152 25922 31691 37510 43099 48732 54464 
775 1950 3190 4429 5674 6914 8157 9397 10275 15093 20848 26675 32437 38241 43764 49512 55129 
787 1840 2947 4192 5427 6656 7766 8885 9994 11547 17023 22712 28515 34282 40051 45726 51421 57066 
883 2073 3310 4558 5790 7033 8283 9532 10357 15754 21475 27231 33061 38831 44472 50195 55849 
798 1996 3239 4461 5706 6950 8199 9441 10275 15229 21009 26783 32626 38414 43977 49707 55429 
736 1486 2262 2863 3665 4388 4987 5707 6313 6892 7634 8355 9058 9730 10271 11794 14023 16123 17814 20313 22850 25413 27924 30670 33231 35726 38003 40310 42840 44565 46475 47660 51698 57085 
627 1814 3048 4296 5540 6776 8026 9273 10264 13855 19581 25328 31167 36929 42462 48098 53746 57523 
920 2062 3310 4547 5794 7029 8267 9517 10496 15891 21649 27425 33213 38931 44435 50107 55746 

的问题是,我得到不同长度的行:(

我获得不同数量的不同的测量样本运行的

最终值总是相同的(〜55000),但在某些行中,我几乎得到了双倍的样本数 我唯一的解释是内核有时会随意决定修改sampli ng期间。

这可能吗?

我怎样才能确保sampling_period恒定的内核端处理到perf_event

编辑:

如所建议的,我已经研究了dmesg内核日志:我期待找到有关内核动态地修改采样时段的消息,如在核函数perf_duration_warn()在linux/4.11.3实施/kernel/events/core.c,但没有显示。

为了尝试启动我的测量如下解决之前,我已经决定修改perf_event_max_sample_rateperf_cpu_time_max_percent值问题:

sudo sysctl -w kernel/perf_cpu_time_max_percent=0 
sudo sysctl -w kernel/perf_event_max_sample_rate=1000000 

我还发现,时间事件我使用采样可能会影响CPU频率调整。 为了避免这个问题,开始我的测量我限制所有CPU对节电工作,恒定频率之前:

for ((i = 0; i < 8; i++)); do 
    sudo cpufreq-set -g powersave -c $((i)) 
done 

即使这些预防措施后,我仍然在每次测量得到不同数量的样本,我我会在这里报告一些行:

880 2064 3316 4582 5837 7088 8339 9595 10585 15868 21519 27263 33080 38859 44472 50216 55836 
927 2084 3324 4574 5815 7050 8303 9544 10683 16008 21743 27449 33213 38982 44511 50262 55965 
749 1825 3063 4306 5575 6818 8070 9317 10262 13926 19637 25396 31207 36995 42539 48096 53800 
629 1466 2323 3130 3926 4702 5439 6198 6918 7516 8114 8708 9351 10034 10734 12957 15283 17657 20200 22771 25464 28114 30451 32899 35225 37775 40371 43050 45609 48342 53820 
886 2063 3299 4208 5006 5814 6683 7565 8435 9336 10130 11222 13600 17364 23080 28853 34643 40365 45965 51625 57237 
821 2001 3251 4495 5733 6986 8247 9480 10265 12197 14699 17284 19897 22463 25141 28072 30695 33320 35964 38750 44183 49872 55515 
917 2071 3320 4562 5801 7029 8275 9508 10509 15865 21575 27317 33118 38916 44566 50256 55923 
472 1573 2806 4044 5270 6514 7743 8977 10217 13221 18923 24610 30389 36127 41717 47398 53076 57518 
924 1980 3225 4477 5712 6946 8183 9435 10353 15575 21318 27078 30246 32751 35310 37808 40322 42769 45306 47800 50340 52852 55395 57499 
932 2038 3270 4527 5655 6907 8030 9139 10237 13390 18965 24708 30389 36115 41719 47397 53030 57520 
927 2088 3327 4581 5826 7078 8316 9549 10692 16007 21726 27449 33235 39001 44564 50225 55890 
582 1405 2263 3061 3859 4651 5370 6170 6861 7461 8017 8601 9153 9808 10281 12318 14509 16810 19173 21820 24183 26912 29796 32447 34809 37347 39652 42216 44943 47728 50588 53182 57081 
903 2065 3320 4546 5795 7025 8272 9515 10294 15324 21081 26863 32644 38439 43975 49714 55388 
921 2094 3325 4571 5810 7045 8296 9548 10759 16134 21869 27560 33412 39154 44837 50581 56307 
860 2049 3284 4525 5771 7025 8283 9532 10415 15646 21325 27056 32827 38560 44065 49691 55352 
953 2124 3368 4610 5844 7071 8317 9575 10790 16141 21889 27642 33406 39151 44725 50435 56092 
921 2098 3338 4529 5311 6160 7030 7910 8792 9644 10293 12630 14972 17493 20388 26173 31915 37689 43267 48951 54696 
575 1384 2040 2841 3511 4068 4680 5386 6086 6677 7244 7828 8476 9104 9761 10279 11976 14187 16747 19192 21859 24662 26853 29386 31702 34421 36952 39444 41934 44699 47371 49965 52794 57509 
925 2101 3333 4575 5814 7066 8318 9562 10849 16188 21910 27648 33348 39069 44626 50245 55877 
591 1462 2321 3140 3975 4731 5494 6279 7042 7684 8252 8831 9415 10066 10793 13202 15480 17800 20151 22779 25268 27824 30673 33340 35680 38182 40851 43263 45995 48661 51409 54078 56696 
885 1927 3041 4157 5024 5779 6615 7473 8289 9124 9883 10440 12771 17987 23720 29495 35303 40764 46468 52157 57485 

有人有任何想法,我可以进一步调查这个问题吗?

编辑3

我增加了采样周期从10000至100000,看看这是什么发生:我得到较小的“长”行,但问题依然存在,我得到不同长度的行不幸

+1

是的,内核可能会修改采样周期,但它并不完全是任意的。您的抽样周期为10000,这可能是合理的,具体取决于工作量。有一个内核参数'perf_event_max_sample_rate' - 它的值开始为** 100,000 **,如果在缓冲区满了时引发的性能监控中断开始丢失大量样本,则可以减少该值。你可以在运行你的进程之后检查'dmesg'日志,获取这个内核参数吗? –

+1

谢谢你的建议!我已经调查过perf内核参数,并试图在编辑中修改它们的值,但我仍然得到相同的问题 – AndreaB

回答

1

我已经解决了这个问题。

为了一定要在使用perf_event系统调用时间的恒定周期被有效地采样,则需要:

  • 指定与perf_event_attr struct内部的sample_period字段事件采样周期。如perf wiki tutorial中所述,您应该注意超过可表示为32位的值,否则系统会自动截断它。
  • 请勿在perf_event_attr struct内使用sample_freq字段指定采样周期。如果这样做,内核将动态地改变采样周期以达到期望的频率,如perf wiki tutorial
  • 中记录的那样,使用dmesg核对内核日志:您不应该得到任何类型的消息。否则,如Arnabjyoti Kalita所建议的那样,采样周期值可能太接近在内核变量perf_event_max_sample_rate中设置的最大值。在这种情况下,最好的方法是使用sudo sysctl -w kernel/perf_cpu_time_max_percent=0修改内核变量,以便内核不会默认修改采样周期,即使这可能会导致计算机卡住。 内核实现此功能以避免在PMI(性能监视中断)过多计算时间过长时卡住。这些中断被perf_event功能挂钩,并且在采样事件溢出指定的采样周期值时触发。 有关perf_event内核变量的更多信息,请参阅linux-VERSION/Documentation/sysctl/kernel.txt中的内核源代码文档。
  • 将CPU频率设置为恒定,使您的CPU的测量值将不会受到频率调整影响 。
  • 计算样本间采样事件值的差异,并验证它是否等于指定的采样周期(小误差范围内)。

我问了这个问题,因为我相信获取不同数量的样本与修改采样周期的内核有关。

它不是。 (它可能但不是我的情况)

我得到不同数量的样本的原因是因为任何类型的受监控线程在其执行过程中可能需要不同的时间执行。 对于那些“更长”的线条,我确实获得了更大的时间价值。 这是由于这个过程偶尔会有一些缓存未命中或不同的预测错误分支。

一些将来的工作在这个方向上做可能是:

  • 使用其他时间事件采样过程并监督差异
  • 使用其他柜台,发现什么是较长的执行时间的原因对于某些运行

如果有人发现有关此新信息,请评论/修改答案或与我联系。

0

perf和cpu之间有一层,结果不准确。

如果使用的CPU是英特尔,您可以使用框架testp这是Agner雾提供的工具。

根据我的经验,它很容易使用和准确。

我已经用它来做一些优化analysis

+0

谢谢你的回答!如问题中所述,我没有使用perf工具,但是我已经构建了一些用于执行perf_event_open()系统调用并显示结果的最小C代码。在我的代码和内核系统调用之间不应该有任何其他“层”。你能更具体地说明你的意思吗?我想用linux系统调用来解决我的问题,并且如果有必要,我也准备修改一些内核代码。顺便说一下,我已经下载了testp,但似乎没有提供采样功能。 – AndreaB

+0

一层意味着linux内核中的perf模块,它会做一些我们不清楚的事情,因为它的目标是适合所有的CPU,例如,英特尔,AMD。在perf模块读取CPU PMU寄存器后,它可能会做一些事情,然后向我们显示它可能使它不准确。在testp中有一个驱动程序,它提供了一个用户空间的接口,通过接口我们可以获得来自PMU的数据直接注册,所以它是准确的。 – Forward

+0

testp中没有采样函数,您可以在“测试代码开始”和“测试代码结束”之间将想要测试的函数放入文件PMCTestB.cpp中。并在阵列“CounterTypesDesired”中写入您要监控的寄存器编号。运行应用程序后,结果将显示。 – Forward