aboutsummaryrefslogblamecommitdiff
path: root/src/afl-gotcpu.c
blob: 43b3196bebe00f6469dc587e6d9bbf2baa594119 (plain) (tree)
1
2
3
4
5
6
7
8
9
  
                                        

                                      
                                        
 
                                               

                                                                      
 
                                                        
                                                                








                                                                           
                                                                  










                                                                             
                   
                     
      
 
                  
                             
      












                         
                   
 

                                                                          















                                                    
                                                                              
 












                                                                



                                              
                          










                                                      

                
         


   




                                       
 

                     
 






                                                                       
                                    





                                        

                             
                                 
 
                 
 
                                                         



                                                               
 

   

                    
                                                                                
 
                                                               







                                                                           
                                          


              
                    
                                                                          
                  


                     
                           
                  

                          
                                                    

                       
                          
                                            

                                                                           

                                                                    
        
 
                                                    

                                                                
        
 
                         



                                                                    
        
 
                        

                                                
                                                         


       
        




                                                        
                                                                              



                                   
                                                                             














                                                                       
                                                               
 

                                                








































                                                                               
                                                               






























                                                                             
                                                                              

 
 
/*
   american fuzzy lop++ - free CPU gizmo
   -----------------------------------

   Originally written by Michal Zalewski

   Now maintained by Marc Heuse <mh@mh-sec.de>,
                        Heiko Eißfeldt <heiko.eissfeldt@hexco.de> and
                        Andrea Fioraldi <andreafioraldi@gmail.com>

   Copyright 2016, 2017 Google Inc. All rights reserved.
   Copyright 2019-2020 AFLplusplus Project. All rights reserved.

   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:

     http://www.apache.org/licenses/LICENSE-2.0

   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.md for more info.

   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.

   The idea for the getrusage()-based approach comes from Jakub Wilk.

 */

#define AFL_MAIN
#ifndef _GNU_SOURCE
  #define _GNU_SOURCE
#endif

#ifdef __ANDROID__
  #include "android-ashmem.h"
#endif
#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"
#include "common.h"

#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
    defined(__APPLE__) || defined(__DragonFly__)
  #define HAVE_AFFINITY 1
  #if defined(__FreeBSD__) || defined(__DragonFly__)
    #include <pthread.h>
    #include <pthread_np.h>
    #if defined(__FreeBSD__)
      #include <sys/cpuset.h>
    #endif
    #define cpu_set_t cpuset_t
  #elif defined(__NetBSD__)
    #include <pthread.h>
    #include <sched.h>
  #elif defined(__APPLE__)
    #include <pthread.h>
    #include <mach/thread_act.h>
    #include <mach/thread_policy.h>
  #endif
#endif               /* __linux__ || __FreeBSD__ || __NetBSD__ || __APPLE__ */

/* Get CPU usage in microseconds. */

static u64 get_cpu_usage_us(void) {

  struct rusage 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;

}

/* Measure preemption rate. */

static u32 measure_preemption(u32 target_ms) {

  volatile u32 v1, v2 = 0;

  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();

repeat_loop:

  v1 = CTEST_BUSY_CYCLES;

  while (v1--) {

    v2++;

  }

  sched_yield();

  en_t = get_cur_time_us();

  if (en_t - st_t < target_ms * 1000) {

    loop_repeats++;
    goto repeat_loop;

  }

  /* Let's see what percentage of this time we actually had a chance to
     run, and how much time was spent in the penalty box. */

  en_c = get_cpu_usage_us();

  real_delta = (en_t - st_t) / 1000;
  slice_delta = (en_c - st_c) / 1000;

  return real_delta * 100 / slice_delta;

}

/* Do the benchmark thing. */

int main(int argc, char **argv) {

  if (argc > 1) {

    printf("afl-gotcpu" VERSION " by Michal Zalewski\n");
    printf("\n%s \n\n", argv[0]);
    printf("afl-gotcpu does not have command line options\n");
    printf("afl-gotcpu prints out which CPUs are available\n");
    return -1;

  }

#ifdef HAVE_AFFINITY

  u32 cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN), idle_cpus = 0, maybe_cpus = 0, i;

  SAYF(cCYA "afl-gotcpu" VERSION cRST " by Michal Zalewski\n");

  ACTF("Measuring per-core preemption rate (this will take %0.02f sec)...",
       ((double)CTEST_CORE_TRG_MS) / 1000);

  for (i = 0; i < cpu_cnt; i++) {

    s32 fr = fork();

    if (fr < 0) { PFATAL("fork failed"); }

    if (!fr) {

      u32 util_perc;
  #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)
      cpu_set_t c;

      CPU_ZERO(&c);
      CPU_SET(i, &c);
  #elif defined(__NetBSD__)
      cpuset_t *c;

      c = cpuset_create();
      if (c == NULL) PFATAL("cpuset_create failed");

      cpuset_set(i, c);
  #elif defined(__APPLE__)
      thread_affinity_policy_data_t c = {i};
      thread_port_t native_thread = pthread_mach_thread_np(pthread_self());
      if (thread_policy_set(native_thread, THREAD_AFFINITY_POLICY,
                            (thread_policy_t)&c, 1) != KERN_SUCCESS)
        PFATAL("thread_policy_set failed");
  #endif

  #if defined(__FreeBSD__) || defined(__DragonFly__)
      if (pthread_setaffinity_np(pthread_self(), sizeof(c), &c))
        PFATAL("pthread_setaffinity_np failed");
  #endif

  #if defined(__NetBSD__)
      if (pthread_setaffinity_np(pthread_self(), cpuset_size(c), c))
        PFATAL("pthread_setaffinity_np failed");

      cpuset_destroy(c);
  #endif

  #if defined(__linux__)
      if (sched_setaffinity(0, sizeof(c), &c)) {

        PFATAL("sched_setaffinity failed for cpu %d", i);

      }

  #endif

      util_perc = measure_preemption(CTEST_CORE_TRG_MS);

      if (util_perc < 110) {

        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);

      }

      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

  u32 util_perc;

  SAYF(cCYA "afl-gotcpu" VERSION cRST " by Michal Zalewski\n");

  /* Run a busy loop for 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 */

}