|
|
|
@ -1,35 +1,29 @@
|
|
|
|
|
/*
|
|
|
|
|
Copyright 2015 Google LLC All rights reserved.
|
|
|
|
|
/*
|
|
|
|
|
版权所有 2015 Google LLC 保留所有权利。
|
|
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
|
You may obtain a copy of the License at:
|
|
|
|
|
根据Apache License, 版本2.0(“许可证”)授权;
|
|
|
|
|
除非遵守许可证,否则不得使用此文件。
|
|
|
|
|
您可以在以下网址获得许可证的副本:
|
|
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
|
limitations under the License.
|
|
|
|
|
除非适用法律要求或书面同意,否则按照许可证分发的软件是基于“原样”分发的基础上进行的,
|
|
|
|
|
不提供任何明示或暗示的保证或条件。
|
|
|
|
|
请参阅许可证,了解许可证下的具体语言,以了解权限和限制。
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
american fuzzy lop - free CPU gizmo
|
|
|
|
|
/*
|
|
|
|
|
美国模糊跳 - 免费CPU小工具
|
|
|
|
|
-----------------------------------
|
|
|
|
|
|
|
|
|
|
Written and maintained by Michal Zalewski <lcamtuf@google.com>
|
|
|
|
|
由Michal Zalewski <lcamtuf@google.com>编写和维护
|
|
|
|
|
|
|
|
|
|
This tool provides a fairly accurate measurement of CPU preemption rate.
|
|
|
|
|
It is meant to complement the quick-and-dirty load average widget shown
|
|
|
|
|
in the afl-fuzz UI. See docs/parallel_fuzzing.txt for more info.
|
|
|
|
|
这个工具提供了一个相当准确的CPU抢占率测量。
|
|
|
|
|
它旨在补充afl-fuzz UI中显示的快速且粗略的负载平均小部件。更多信息请参见docs/parallel_fuzzing.txt。
|
|
|
|
|
|
|
|
|
|
For some work loads, the tool may actually suggest running more instances
|
|
|
|
|
than you have CPU cores. This can happen if the tested program is spending
|
|
|
|
|
a portion of its run time waiting for I/O, rather than being 100%
|
|
|
|
|
CPU-bound.
|
|
|
|
|
对于一些工作负载,该工具实际上可能建议您运行的实例数量超过您拥有的CPU核心数。如果被测试的程序在其运行时间中花费了一部分时间等待I/O,而不是100% CPU绑定,这种情况可能会发生。
|
|
|
|
|
|
|
|
|
|
The idea for the getrusage()-based approach comes from Jakub Wilk.
|
|
|
|
|
基于getrusage()的方法的想法来自Jakub Wilk。
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define AFL_MAIN
|
|
|
|
@ -51,161 +45,191 @@
|
|
|
|
|
#include "debug.h"
|
|
|
|
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
|
# define HAVE_AFFINITY 1 // 如果是Linux系统,支持CPU亲和性
|
|
|
|
|
# define HAVE_AFFINITY 1
|
|
|
|
|
#endif /* __linux__ */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 获取当前时间,单位:微秒 */
|
|
|
|
|
static u64 get_cur_time_us(void) {
|
|
|
|
|
/*
|
|
|
|
|
获取以微秒为单位的Unix时间。
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static u64 get_cur_time_us(void) {
|
|
|
|
|
// 定义一个timeval结构体变量tv,用于存储当前时间
|
|
|
|
|
struct timeval tv;
|
|
|
|
|
// 定义一个timezone结构体变量tz,用于存储时区信息
|
|
|
|
|
struct timezone tz;
|
|
|
|
|
|
|
|
|
|
gettimeofday(&tv, &tz); // 获取当前的系统时间
|
|
|
|
|
// 使用gettimeofday函数获取当前时间,并存储到tv和tz中
|
|
|
|
|
gettimeofday(&tv, &tz);
|
|
|
|
|
|
|
|
|
|
return (tv.tv_sec * 1000000ULL) + tv.tv_usec; // 将秒转换为微秒并返回
|
|
|
|
|
// 将秒转换为微秒,并加上微秒部分,返回当前时间的微秒值
|
|
|
|
|
return (tv.tv_sec * 1000000ULL) + tv.tv_usec;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
获取CPU使用时间,以微秒为单位。
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* 获取当前进程的CPU使用时间,单位:微秒 */
|
|
|
|
|
static u64 get_cpu_usage_us(void) {
|
|
|
|
|
|
|
|
|
|
// 定义一个rusage结构体变量u,用于存储资源使用情况
|
|
|
|
|
struct rusage u;
|
|
|
|
|
|
|
|
|
|
getrusage(RUSAGE_SELF, &u); // 获取当前进程的资源使用情况
|
|
|
|
|
// 使用getrusage函数获取当前进程的资源使用情况,并存储到u中
|
|
|
|
|
getrusage(RUSAGE_SELF, &u);
|
|
|
|
|
|
|
|
|
|
return (u.ru_utime.tv_sec * 1000000ULL) + u.ru_utime.tv_usec + // 用户态CPU时间
|
|
|
|
|
(u.ru_stime.tv_sec * 1000000ULL) + u.ru_stime.tv_usec; // 内核态CPU时间
|
|
|
|
|
// 计算用户态和核心态的总使用时间(微秒),并返回
|
|
|
|
|
return (u.ru_utime.tv_sec * 1000000ULL) + u.ru_utime.tv_usec +
|
|
|
|
|
(u.ru_stime.tv_sec * 1000000ULL) + u.ru_stime.tv_usec;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 测量预占率,target_ms为目标时间,单位:毫秒 */
|
|
|
|
|
static u32 measure_preemption(u32 target_ms) {
|
|
|
|
|
/*
|
|
|
|
|
测量抢占率。
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static u32 measure_preemption(u32 target_ms) {
|
|
|
|
|
// 定义两个易失性变量,用于循环测试
|
|
|
|
|
static volatile u32 v1, v2;
|
|
|
|
|
|
|
|
|
|
u64 st_t, en_t, st_c, en_c, real, slice;
|
|
|
|
|
// 定义变量用于存储开始和结束的时间(微秒),以及CPU使用时间
|
|
|
|
|
u64 st_t, en_t, st_c, en_c, real_delta, slice_delta;
|
|
|
|
|
// 定义循环重复次数
|
|
|
|
|
s32 loop_repeats = 0;
|
|
|
|
|
|
|
|
|
|
st_t = get_cur_time_us(); // 获取开始时间
|
|
|
|
|
st_c = get_cpu_usage_us(); // 获取开始时的CPU使用时间
|
|
|
|
|
// 获取当前时间(微秒)
|
|
|
|
|
st_t = get_cur_time_us();
|
|
|
|
|
// 获取当前CPU使用时间(微秒)
|
|
|
|
|
st_c = get_cpu_usage_us();
|
|
|
|
|
|
|
|
|
|
repeat_loop:
|
|
|
|
|
// 设置v1为一个忙循环的计数
|
|
|
|
|
v1 = CTEST_BUSY_CYCLES;
|
|
|
|
|
|
|
|
|
|
v1 = CTEST_BUSY_CYCLES; // 模拟的忙循环次数
|
|
|
|
|
// 执行忙循环,同时v2自增
|
|
|
|
|
while (v1--) v2++;
|
|
|
|
|
// 调用sched_yield(),让出CPU
|
|
|
|
|
sched_yield();
|
|
|
|
|
|
|
|
|
|
while (v1--) v2++; // 执行忙循环
|
|
|
|
|
sched_yield(); // 强制当前进程让出CPU
|
|
|
|
|
// 再次获取当前时间(微秒)
|
|
|
|
|
en_t = get_cur_time_us();
|
|
|
|
|
|
|
|
|
|
en_t = get_cur_time_us(); // 获取结束时间
|
|
|
|
|
|
|
|
|
|
// 如果目标时间未达到,则继续重复循环
|
|
|
|
|
// 如果当前时间与开始时间的差小于目标时间(毫秒转换为微秒),则增加循环次数并继续循环
|
|
|
|
|
if (en_t - st_t < target_ms * 1000) {
|
|
|
|
|
loop_repeats++;
|
|
|
|
|
goto repeat_loop;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
en_c = get_cpu_usage_us(); // 获取结束时的CPU使用时间
|
|
|
|
|
/* 让我们看看这段时间里我们实际上有多少百分比的机会运行,
|
|
|
|
|
以及在惩罚箱中花费了多少时间。 */
|
|
|
|
|
|
|
|
|
|
// 获取当前CPU使用时间(微秒)
|
|
|
|
|
en_c = get_cpu_usage_us();
|
|
|
|
|
|
|
|
|
|
// 计算实际经过时间和CPU时间的差值,单位:毫秒
|
|
|
|
|
real = (en_t - st_t) / 1000;
|
|
|
|
|
slice = (en_c - st_c) / 1000;
|
|
|
|
|
// 计算实际时间差(毫秒)和CPU使用时间差(毫秒)
|
|
|
|
|
real_delta = (en_t - st_t) / 1000;
|
|
|
|
|
slice_delta = (en_c - st_c) / 1000;
|
|
|
|
|
|
|
|
|
|
// 返回CPU预占率,单位:百分比
|
|
|
|
|
return real * 100 / slice;
|
|
|
|
|
// 返回实际时间差占CPU使用时间差的百分比,即抢占率
|
|
|
|
|
return real_delta * 100 / slice_delta;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
执行基准测试。
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* 主程序 */
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_AFFINITY // 如果支持CPU亲和性
|
|
|
|
|
#ifdef HAVE_AFFINITY
|
|
|
|
|
|
|
|
|
|
u32 cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN), // 获取系统中CPU核心数
|
|
|
|
|
// 获取在线的CPU核心数
|
|
|
|
|
u32 cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN),
|
|
|
|
|
// 初始化空闲CPU和可能可用CPU的计数器
|
|
|
|
|
idle_cpus = 0, maybe_cpus = 0, i;
|
|
|
|
|
|
|
|
|
|
// 打印欢迎信息和版本号
|
|
|
|
|
SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
|
|
|
|
|
|
|
|
|
|
// 打印测量每个核心抢占率所需的时间
|
|
|
|
|
ACTF("Measuring per-core preemption rate (this will take %0.02f sec)...",
|
|
|
|
|
((double)CTEST_CORE_TRG_MS) / 1000); // 输出提示信息,显示测量时间
|
|
|
|
|
((double)CTEST_CORE_TRG_MS) / 1000);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < cpu_cnt; i++) { // 遍历每个CPU核心
|
|
|
|
|
// 遍历每个CPU核心
|
|
|
|
|
for (i = 0; i < cpu_cnt; i++) {
|
|
|
|
|
|
|
|
|
|
s32 fr = fork(); // 创建子进程
|
|
|
|
|
// 创建子进程
|
|
|
|
|
s32 fr = fork();
|
|
|
|
|
|
|
|
|
|
// 如果fork失败,则打印错误信息并退出
|
|
|
|
|
if (fr < 0) PFATAL("fork failed");
|
|
|
|
|
|
|
|
|
|
if (!fr) { // 子进程执行以下代码
|
|
|
|
|
// 如果是子进程,则执行以下操作
|
|
|
|
|
if (!fr) {
|
|
|
|
|
|
|
|
|
|
// 定义CPU集合
|
|
|
|
|
cpu_set_t c;
|
|
|
|
|
// 定义CPU使用率百分比
|
|
|
|
|
u32 util_perc;
|
|
|
|
|
|
|
|
|
|
CPU_ZERO(&c); // 清空CPU集合
|
|
|
|
|
CPU_SET(i, &c); // 将当前核心i加入CPU集合
|
|
|
|
|
// 初始化CPU集合
|
|
|
|
|
CPU_ZERO(&c);
|
|
|
|
|
// 将当前核心加入CPU集合
|
|
|
|
|
CPU_SET(i, &c);
|
|
|
|
|
|
|
|
|
|
if (sched_setaffinity(0, sizeof(c), &c)) // 设置当前进程的CPU亲和性
|
|
|
|
|
// 如果设置CPU亲和性失败,则打印错误信息并退出
|
|
|
|
|
if (sched_setaffinity(0, sizeof(c), &c))
|
|
|
|
|
PFATAL("sched_setaffinity failed for cpu %d", i);
|
|
|
|
|
|
|
|
|
|
util_perc = measure_preemption(CTEST_CORE_TRG_MS); // 测量当前核心的预占率
|
|
|
|
|
// 测量抢占率
|
|
|
|
|
util_perc = measure_preemption(CTEST_CORE_TRG_MS);
|
|
|
|
|
|
|
|
|
|
// 根据预占率判断当前核心的负载状态
|
|
|
|
|
// 如果使用率低于110%,则标记为核心可用,并退出
|
|
|
|
|
if (util_perc < 110) {
|
|
|
|
|
SAYF(" Core #%u: " cLGN "AVAILABLE " cRST "(%u%%)\n", i, util_perc);
|
|
|
|
|
exit(0); // 退出子进程
|
|
|
|
|
SAYF(" Core #%u: " cLGN "AVAILABLE" cRST "(%u%%)\n", i, util_perc);
|
|
|
|
|
exit(0);
|
|
|
|
|
} else if (util_perc < 250) {
|
|
|
|
|
SAYF(" Core #%u: " cYEL "CAUTION " cRST "(%u%%)\n", i, util_perc);
|
|
|
|
|
exit(1); // 退出子进程
|
|
|
|
|
// 如果使用率低于250%,则标记为核心需要谨慎,并退出
|
|
|
|
|
SAYF(" Core #%u: " cYEL "CAUTION" cRST "(%u%%)\n", i, util_perc);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SAYF(" Core #%u: " cLRD "OVERBOOKED " cRST "(%u%%)\n" cRST, i, util_perc);
|
|
|
|
|
exit(2); // 退出子进程
|
|
|
|
|
// 如果使用率高于250%,则标记为核心过载,并退出
|
|
|
|
|
SAYF(" Core #%u: " cLRD "OVERBOOKED" cRST "(%u%%)\n" cRST, i,
|
|
|
|
|
util_perc);
|
|
|
|
|
exit(2);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 等待所有子进程完成
|
|
|
|
|
// 等待子进程结束,并统计空闲和可能可用的核心数
|
|
|
|
|
for (i = 0; i < cpu_cnt; i++) {
|
|
|
|
|
|
|
|
|
|
int ret;
|
|
|
|
|
if (waitpid(-1, &ret, 0) < 0) PFATAL("waitpid failed");
|
|
|
|
|
|
|
|
|
|
if (WEXITSTATUS(ret) == 0) idle_cpus++; // 统计空闲的CPU核心
|
|
|
|
|
if (WEXITSTATUS(ret) <= 1) maybe_cpus++; // 统计可能可用的CPU核心
|
|
|
|
|
|
|
|
|
|
if (WEXITSTATUS(ret) == 0) idle_cpus++;
|
|
|
|
|
if (WEXITSTATUS(ret) <= 1) maybe_cpus++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 根据空闲和可能可用的核心数打印结果
|
|
|
|
|
SAYF(cGRA "\n>>> ");
|
|
|
|
|
|
|
|
|
|
if (idle_cpus) { // 如果有空闲的CPU核心
|
|
|
|
|
|
|
|
|
|
if (maybe_c
|
|
|
|
|
|
|
|
|
|
if (idle_cpus) {
|
|
|
|
|
|
|
|
|
|
if (maybe_cpus == idle_cpus) {
|
|
|
|
|
|
|
|
|
|
SAYF(cLGN "PASS: " cRST "You can run more processes on %u core%s.",
|
|
|
|
|
idle_cpus, idle_cpus > 1 ? "s" : "");
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
SAYF(cLGN "PASS: " cRST "You can run more processes on %u to %u core%s.",
|
|
|
|
|
idle_cpus, maybe_cpus, maybe_cpus > 1 ? "s" : "");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SAYF(cGRA " <<<" cRST "\n\n");
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (maybe_cpus) {
|
|
|
|
|
|
|
|
|
|
SAYF(cYEL "CAUTION: " cRST "You may still have %u core%s available.",
|
|
|
|
|
maybe_cpus, maybe_cpus > 1 ? "s" : "");
|
|
|
|
|
SAYF(cGRA " <<<" cRST "\n\n");
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SAYF(cLRD "FAIL: " cRST "All cores are overbooked.");
|
|
|
|
@ -214,38 +238,35 @@ int main(int argc, char** argv) {
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
// 如果没有CPU亲和性支持,则执行总体抢占率的测量
|
|
|
|
|
u32 util_perc;
|
|
|
|
|
|
|
|
|
|
SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
|
|
|
|
|
|
|
|
|
|
/* Run a busy loop for CTEST_TARGET_MS. */
|
|
|
|
|
/* 运行一个忙循环,持续CTEST_TARGET_MS毫秒。 */
|
|
|
|
|
|
|
|
|
|
ACTF("Measuring gross preemption rate (this will take %0.02f sec)...",
|
|
|
|
|
((double)CTEST_TARGET_MS) / 1000);
|
|
|
|
|
|
|
|
|
|
// 测量抢占率
|
|
|
|
|
util_perc = measure_preemption(CTEST_TARGET_MS);
|
|
|
|
|
|
|
|
|
|
/* Deliver the final verdict. */
|
|
|
|
|
/* 输出最终结果。 */
|
|
|
|
|
|
|
|
|
|
SAYF(cGRA "\n>>> ");
|
|
|
|
|
|
|
|
|
|
if (util_perc < 105) {
|
|
|
|
|
|
|
|
|
|
SAYF(cLGN "PASS: " cRST "You can probably run additional processes.");
|
|
|
|
|
|
|
|
|
|
} else if (util_perc < 130) {
|
|
|
|
|
|
|
|
|
|
SAYF(cYEL "CAUTION: " cRST "Your CPU may be somewhat overbooked (%u%%).",
|
|
|
|
|
util_perc);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
SAYF(cLRD "FAIL: " cRST "Your CPU is overbooked (%u%%).", util_perc);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SAYF(cGRA " <<<" cRST "\n\n");
|
|
|
|
|
|
|
|
|
|
// 返回结果代码
|
|
|
|
|
return (util_perc > 105) + (util_perc > 130);
|
|
|
|
|
|
|
|
|
|
#endif /* ^HAVE_AFFINITY */
|
|
|
|
|