1 /*
2    american fuzzy lop - free CPU gizmo
3    -----------------------------------
4 
5    Written and maintained by Michal Zalewski <lcamtuf@google.com>
6 
7    Copyright 2015, 2016 Google Inc. All rights reserved.
8 
9    Licensed under the Apache License, Version 2.0 (the "License");
10    you may not use this file except in compliance with the License.
11    You may obtain a copy of the License at:
12 
13      http://www.apache.org/licenses/LICENSE-2.0
14 
15    This tool provides a fairly accurate measurement of CPU preemption rate.
16    It is meant to complement the quick-and-dirty load average widget shown
17    in the afl-fuzz UI. See docs/parallel_fuzzing.txt for more info.
18 
19    For some work loads, the tool may actually suggest running more instances
20    than you have CPU cores. This can happen if the tested program is spending
21    a portion of its run time waiting for I/O, rather than being 100%
22    CPU-bound.
23 
24    The idea for the getrusage()-based approach comes from Jakub Wilk.
25 
26  */
27 
28 #define AFL_MAIN
29 #define _GNU_SOURCE
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <sched.h>
36 
37 #include <sys/time.h>
38 #include <sys/times.h>
39 #include <sys/resource.h>
40 #include <sys/wait.h>
41 
42 #include "types.h"
43 #include "debug.h"
44 
45 #ifdef __linux__
46 #  define HAVE_AFFINITY 1
47 #endif /* __linux__ */
48 
49 
50 /* Get unix time in microseconds. */
51 
get_cur_time_us(void)52 static u64 get_cur_time_us(void) {
53 
54   struct timeval tv;
55   struct timezone tz;
56 
57   gettimeofday(&tv, &tz);
58 
59   return (tv.tv_sec * 1000000ULL) + tv.tv_usec;
60 
61 }
62 
63 
64 /* Get CPU usage in microseconds. */
65 
get_cpu_usage_us(void)66 static u64 get_cpu_usage_us(void) {
67 
68   struct rusage u;
69 
70   getrusage(RUSAGE_SELF, &u);
71 
72   return (u.ru_utime.tv_sec * 1000000ULL) + u.ru_utime.tv_usec +
73          (u.ru_stime.tv_sec * 1000000ULL) + u.ru_stime.tv_usec;
74 
75 }
76 
77 
78 /* Measure preemption rate. */
79 
measure_preemption(u32 target_ms)80 static u32 measure_preemption(u32 target_ms) {
81 
82   static volatile u32 v1, v2;
83 
84   u64 st_t, en_t, st_c, en_c, real_delta, slice_delta;
85   s32 loop_repeats = 0;
86 
87   st_t = get_cur_time_us();
88   st_c = get_cpu_usage_us();
89 
90 repeat_loop:
91 
92   v1 = CTEST_BUSY_CYCLES;
93 
94   while (v1--) v2++;
95   sched_yield();
96 
97   en_t = get_cur_time_us();
98 
99   if (en_t - st_t < target_ms * 1000) {
100     loop_repeats++;
101     goto repeat_loop;
102   }
103 
104   /* Let's see what percentage of this time we actually had a chance to
105      run, and how much time was spent in the penalty box. */
106 
107   en_c = get_cpu_usage_us();
108 
109   real_delta  = (en_t - st_t) / 1000;
110   slice_delta = (en_c - st_c) / 1000;
111 
112   return real_delta * 100 / slice_delta;
113 
114 }
115 
116 
117 /* Do the benchmark thing. */
118 
main(int argc,char ** argv)119 int main(int argc, char** argv) {
120 
121 #ifdef HAVE_AFFINITY
122 
123   u32 cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN),
124       idle_cpus = 0, maybe_cpus = 0, i;
125 
126   SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
127 
128   ACTF("Measuring per-core preemption rate (this will take %0.02f sec)...",
129        ((double)CTEST_CORE_TRG_MS) / 1000);
130 
131   for (i = 0; i < cpu_cnt; i++) {
132 
133     s32 fr = fork();
134 
135     if (fr < 0) PFATAL("fork failed");
136 
137     if (!fr) {
138 
139       cpu_set_t c;
140       u32 util_perc;
141 
142       CPU_ZERO(&c);
143       CPU_SET(i, &c);
144 
145       if (sched_setaffinity(0, sizeof(c), &c))
146         PFATAL("sched_setaffinity failed");
147 
148       util_perc = measure_preemption(CTEST_CORE_TRG_MS);
149 
150       if (util_perc < 110) {
151 
152         SAYF("    Core #%u: " cLGN "AVAILABLE\n" cRST, i);
153         exit(0);
154 
155       } else if (util_perc < 250) {
156 
157         SAYF("    Core #%u: " cYEL "CAUTION " cRST "(%u%%)\n", i, util_perc);
158         exit(1);
159 
160       }
161 
162       SAYF("    Core #%u: " cLRD "OVERBOOKED " cRST "(%u%%)\n" cRST, i,
163            util_perc);
164       exit(2);
165 
166     }
167 
168   }
169 
170   for (i = 0; i < cpu_cnt; i++) {
171 
172     int ret;
173     if (waitpid(-1, &ret, 0) < 0) PFATAL("waitpid failed");
174 
175     if (WEXITSTATUS(ret) == 0) idle_cpus++;
176     if (WEXITSTATUS(ret) <= 1) maybe_cpus++;
177 
178   }
179 
180   SAYF(cGRA "\n>>> ");
181 
182   if (idle_cpus) {
183 
184     if (maybe_cpus == idle_cpus) {
185 
186       SAYF(cLGN "PASS: " cRST "You can run more processes on %u core%s.",
187            idle_cpus, idle_cpus > 1 ? "s" : "");
188 
189     } else {
190 
191       SAYF(cLGN "PASS: " cRST "You can run more processes on %u to %u core%s.",
192            idle_cpus, maybe_cpus, maybe_cpus > 1 ? "s" : "");
193 
194     }
195 
196     SAYF(cGRA " <<<" cRST "\n\n");
197     return 0;
198 
199   }
200 
201   if (maybe_cpus) {
202 
203     SAYF(cYEL "CAUTION: " cRST "You may still have %u core%s available.",
204          maybe_cpus, maybe_cpus > 1 ? "s" : "");
205     SAYF(cGRA " <<<" cRST "\n\n");
206     return 1;
207 
208   }
209 
210   SAYF(cLRD "FAIL: " cRST "All cores are overbooked.");
211   SAYF(cGRA " <<<" cRST "\n\n");
212   return 2;
213 
214 #else
215 
216   u32 util_perc;
217 
218   SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
219 
220   /* Run a busy loop for CTEST_TARGET_MS. */
221 
222   ACTF("Measuring gross preemption rate (this will take %0.02f sec)...",
223        ((double)CTEST_TARGET_MS) / 1000);
224 
225   util_perc = measure_preemption(CTEST_TARGET_MS);
226 
227   /* Deliver the final verdict. */
228 
229   SAYF(cGRA "\n>>> ");
230 
231   if (util_perc < 105) {
232 
233     SAYF(cLGN "PASS: " cRST "You can probably run additional processes.");
234 
235   } else if (util_perc < 130) {
236 
237     SAYF(cYEL "CAUTION: " cRST "Your CPU may be somewhat overbooked (%u%%).",
238          util_perc);
239 
240   } else {
241 
242     SAYF(cLRD "FAIL: " cRST "Your CPU is overbooked (%u%%).", util_perc);
243 
244   }
245 
246   SAYF(cGRA " <<<" cRST "\n\n");
247 
248   return (util_perc > 105) + (util_perc > 130);
249 
250 #endif /* ^HAVE_AFFINITY */
251 
252 }
253