You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AFL/afl-gotcpu.c

275 lines
7.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
版权所有 2015 Google LLC 保留所有权利。
根据Apache License, 版本2.0(“许可证”)授权;
除非遵守许可证,否则不得使用此文件。
您可以在以下网址获得许可证的副本:
http://www.apache.org/licenses/LICENSE-2.0
除非适用法律要求或书面同意,否则按照许可证分发的软件是基于“原样”分发的基础上进行的,
不提供任何明示或暗示的保证或条件。
请参阅许可证,了解许可证下的具体语言,以了解权限和限制。
*/
/*
美国模糊跳 - 免费CPU小工具
-----------------------------------
由Michal Zalewski <lcamtuf@google.com>编写和维护
这个工具提供了一个相当准确的CPU抢占率测量。
它旨在补充afl-fuzz UI中显示的快速且粗略的负载平均小部件。更多信息请参见docs/parallel_fuzzing.txt。
对于一些工作负载该工具实际上可能建议您运行的实例数量超过您拥有的CPU核心数。如果被测试的程序在其运行时间中花费了一部分时间等待I/O而不是100% CPU绑定这种情况可能会发生。
基于getrusage()的方法的想法来自Jakub Wilk。
*/
#define AFL_MAIN
#include "android-ashmem.h"
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sched.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include "types.h"
#include "debug.h"
#ifdef __linux__
# define HAVE_AFFINITY 1
#endif /* __linux__ */
/*
获取以微秒为单位的Unix时间。
*/
static u64 get_cur_time_us(void) {
// 定义一个timeval结构体变量tv用于存储当前时间
struct timeval tv;
// 定义一个timezone结构体变量tz用于存储时区信息
struct timezone tz;
// 使用gettimeofday函数获取当前时间并存储到tv和tz中
gettimeofday(&tv, &tz);
// 将秒转换为微秒,并加上微秒部分,返回当前时间的微秒值
return (tv.tv_sec * 1000000ULL) + tv.tv_usec;
}
/*
获取CPU使用时间以微秒为单位。
*/
static u64 get_cpu_usage_us(void) {
// 定义一个rusage结构体变量u用于存储资源使用情况
struct rusage u;
// 使用getrusage函数获取当前进程的资源使用情况并存储到u中
getrusage(RUSAGE_SELF, &u);
// 计算用户态和核心态的总使用时间(微秒),并返回
return (u.ru_utime.tv_sec * 1000000ULL) + u.ru_utime.tv_usec +
(u.ru_stime.tv_sec * 1000000ULL) + u.ru_stime.tv_usec;
}
/*
测量抢占率。
*/
static u32 measure_preemption(u32 target_ms) {
// 定义两个易失性变量,用于循环测试
static volatile u32 v1, v2;
// 定义变量用于存储开始和结束的时间微秒以及CPU使用时间
u64 st_t, en_t, st_c, en_c, real_delta, slice_delta;
// 定义循环重复次数
s32 loop_repeats = 0;
// 获取当前时间(微秒)
st_t = get_cur_time_us();
// 获取当前CPU使用时间微秒
st_c = get_cpu_usage_us();
repeat_loop:
// 设置v1为一个忙循环的计数
v1 = CTEST_BUSY_CYCLES;
// 执行忙循环同时v2自增
while (v1--) v2++;
// 调用sched_yield()让出CPU
sched_yield();
// 再次获取当前时间(微秒)
en_t = get_cur_time_us();
// 如果当前时间与开始时间的差小于目标时间(毫秒转换为微秒),则增加循环次数并继续循环
if (en_t - st_t < target_ms * 1000) {
loop_repeats++;
goto repeat_loop;
}
/* 让我们看看这段时间里我们实际上有多少百分比的机会运行,
以及在惩罚箱中花费了多少时间。 */
// 获取当前CPU使用时间微秒
en_c = get_cpu_usage_us();
// 计算实际时间差毫秒和CPU使用时间差毫秒
real_delta = (en_t - st_t) / 1000;
slice_delta = (en_c - st_c) / 1000;
// 返回实际时间差占CPU使用时间差的百分比即抢占率
return real_delta * 100 / slice_delta;
}
/*
执行基准测试。
*/
int main(int argc, char** argv) {
#ifdef HAVE_AFFINITY
// 获取在线的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);
// 遍历每个CPU核心
for (i = 0; i < cpu_cnt; i++) {
// 创建子进程
s32 fr = fork();
// 如果fork失败则打印错误信息并退出
if (fr < 0) PFATAL("fork failed");
// 如果是子进程,则执行以下操作
if (!fr) {
// 定义CPU集合
cpu_set_t c;
// 定义CPU使用率百分比
u32 util_perc;
// 初始化CPU集合
CPU_ZERO(&c);
// 将当前核心加入CPU集合
CPU_SET(i, &c);
// 如果设置CPU亲和性失败则打印错误信息并退出
if (sched_setaffinity(0, sizeof(c), &c))
PFATAL("sched_setaffinity failed for cpu %d", i);
// 测量抢占率
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);
} else if (util_perc < 250) {
// 如果使用率低于250%,则标记为核心需要谨慎,并退出
SAYF(" Core #%u: " cYEL "CAUTION" cRST "(%u%%)\n", i, util_perc);
exit(1);
}
// 如果使用率高于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++;
if (WEXITSTATUS(ret) <= 1) maybe_cpus++;
}
// 根据空闲和可能可用的核心数打印结果
SAYF(cGRA "\n>>> ");
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.");
SAYF(cGRA " <<<" cRST "\n\n");
return 2;
#else
// 如果没有CPU亲和性支持则执行总体抢占率的测量
u32 util_perc;
SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
/* 运行一个忙循环持续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);
/* 输出最终结果。 */
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 */
}