如何使用perf_event_open以恒定速率进行采样?

时间:2017-07-25 09:22:39

标签: c linux linux-kernel operating-system perf

我正在尝试监视进程的执行情况,并使用linux上的perf_event对其退出指令的数量进行采样。 我希望随着时间的推移,在固定的,恒定的采样周期内对过程进行采样。

一切似乎都在努力,我已经成功编写了代码,调用了perf_event_open()并设置了两个不同的计数器:

  • 属于PERF_TYPE_HARDWARE类型的领导者1,标识为PERF_COUNT_HW_CPU_CYCLES,作为我的“抽样”事件。我将其采样周期指定为10000。
  • slave one,表示退役指令数量的事件(我通过传递其十六进制数字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),但在某些行中,我几乎得到双倍数量的样本。 我提出的唯一解释是内核有时会随意决定修改采样周期。

这可能吗?

在处理到sampling_period时,如何确保perf_event成为常量内核?

编辑:

正如所建议的那样,我调查了dmesg内核日志:我希望找到一条关于内核动态修改采样周期的消息,如linux / 4.11中的内核函数perf_duration_warn()所实现的那样。 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频率缩放的影响。 为了避免这个问题,在开始我的测量之前,我限制所有cpus在powersave上工作,恒定频率:

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以查看它发生了什么:我得到的'更长'行更长,但问题仍然存在,不幸的是我得到了不同长度的行

2 个答案:

答案 0 :(得分:1)

我已经解决了这个问题。

为了确保使用perf_event系统调用在一段固定的时间内有效采样,需要:

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

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

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

我获得不同数量的样本的原因是因为任何类型的受监视线程在执行期间可能需要执行不同的时间。 对那些更长的&#39;确实我获得了更大的时间价值。 这是因为该过程可能偶尔会出现一些缓存未命中或错误预测的错误分支。

在这个方向上要做的一些进一步的工作可能是:

  • 使用其他时间事件对流程进行采样并监控差异
  • 使用其他计数器,检测某些运行的执行时间较长的原因

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

答案 1 :(得分:0)

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

如果使用的cpu是Intel,你能否使用框架testp,这是Agner Fog提供的工具。

根据我的经验,它易于使用且准确。

我用它做了一些优化analysis