summaryrefslogtreecommitdiff
path: root/src/main.c
blob: 3fb9a4a050ce71933fffa6f6045ca925ea09bb90 (plain)
    1 // 
    2 // Copyright (C) 2015 Aaron Ball <nullspoon@iohq.net>
    3 // 
    4 // Cnetbench is free software: you can redistribute it and/or modify
    5 // it under the terms of the GNU General Public License as published by
    6 // the Free Software Foundation, either version 3 of the License, or
    7 // (at your option) any later version.
    8 // 
    9 // Cnetbench is distributed in the hope that it will be useful,
   10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
   11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12 // GNU General Public License for more details.
   13 // 
   14 // You should have received a copy of the GNU General Public License
   15 // along with cnetbench.  If not, see <http://www.gnu.org/licenses/>.
   16 // 
   17 #include <stdlib.h>
   18 #include <stdio.h>
   19 #include <sys/socket.h>
   20 #include <arpa/inet.h>
   21 #include <netinet/in.h>
   22 #include <strings.h>
   23 #include <unistd.h>
   24 #include <sys/time.h>
   25 #include <wait.h>
   26 
   27 
   28 //
   29 // Watches an array of pids for their exit, and updates the output array
   30 // exit_times with each pid's exit timeval struct timestamp (epoch with
   31 // microseconds).
   32 //
   33 // Note that this function does not start the pids, but only watches them. To
   34 // calculate runtime, external code needs to track when the pids were created.
   35 //
   36 // @param pids       Array of pid_t pids to be watched
   37 // @param count      Number of pids in the pids array
   38 // @param exit_times Array of timeval structs that will contain exit times for
   39 //                   all of the pids in the array. Must be at least the size of
   40 //                   the pids array or unpredictable behavior may result.
   41 //
   42 // @return int Overall status of all exited pids (if any fail exit, returns 1)
   43 //
   44 int time_pids(pid_t* pids, int count, struct timeval *exit_times) {
   45   int i;
   46   int waiting = 1;
   47 
   48   while(waiting != 0) {
   49     waiting = 0;
   50     for(i = 0; i < count; i++) {
   51       // Pid with this index has exited, skip it.
   52       if(pids[i] == 0)
   53         continue;
   54 
   55       // 0 = state change (exit)
   56       if(waitpid(pids[i], NULL, WNOHANG) != 0) {
   57         // Child has exited
   58         pids[i] = 0;
   59         // Write the exit time
   60         gettimeofday(&exit_times[i], NULL);
   61       } else {
   62         waiting = 1;
   63       }
   64     }
   65     sleep(1);
   66   }
   67   return 0;
   68 }
   69 
   70 
   71 //
   72 // Performs a benchmark test on the specified destination/port, sending the
   73 // specified amount of data.
   74 //
   75 // @param amount Megabytes to send
   76 // @param dest   Destination host to send data to
   77 // @param port   Destination port to send the data on
   78 //
   79 // @return int Succcess (0) or failure (1) of data send
   80 //
   81 int test(int amount, char *dest, int port) {
   82   int socket_desc;
   83   // This is 1 megabyte
   84   long size = 1048576;
   85   char *message = calloc(size, 1);
   86 
   87   // AF_INET:     ipv4 (AF_INET6 would be ipv6)
   88   // SOCK_STREAM: TCP
   89   // 0: Protocol 0 (IP)
   90   socket_desc = socket(AF_INET, SOCK_STREAM, 0);
   91 
   92   if(socket_desc == -1) {
   93     printf("Error creating socket\n");
   94     return 1;
   95   }
   96 
   97   // Set socket values
   98   struct sockaddr_in server;
   99   server.sin_addr.s_addr = inet_addr(dest);
  100   server.sin_family = AF_INET;
  101   server.sin_port = htons(port);
  102 
  103   if(connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) {
  104     printf("Connection error\n");
  105     return -1;
  106   }
  107   printf("Connected\n");
  108 
  109   // Send some data
  110   long i = 0;
  111   while(i < amount) {
  112     if(send(socket_desc, message, size, 0) < 0) {
  113         return -1;
  114     }
  115     i++;
  116   }
  117 
  118   free(message);
  119   return 0;
  120 }
  121 
  122 
  123 //
  124 // Forks sub-processes off stream_count times, to test bandwidth to the
  125 // specified destination host/port, sending the specified amount of data.
  126 //
  127 // Note that start_times and exit_times are two arrays that will receive start
  128 // and exit timestamps for each pid for timing purposes.
  129 //
  130 // @param amount       Amount of data in megabytes to send
  131 // @param stream_count Number of data streams to test concurrently
  132 // @param dest         Destination host to send data to
  133 // @param port         Destination port to send data over
  134 // @param start_times  Output array of timeval start time timestamps 
  135 // @param exit_times   Output array of timeval exit time timestamps
  136 //
  137 // @return int Success (0) or failure(1)
  138 //
  139 int spawn_tests(int amount,
  140                 int stream_count,
  141                 char* dest,
  142                 int port,
  143                 struct timeval *start_times,
  144                 struct timeval *exit_times) {
  145   pid_t pids[stream_count];
  146 
  147   pid_t pid;
  148   int i = 0;
  149   while(i < stream_count) {
  150     // Fork
  151     pid = fork();
  152 
  153     if(pid == 0) {
  154       // Execute the test
  155       printf("Fork %d starting\n", i);
  156       test(amount, dest, port);
  157       exit(0);
  158     } else if(pid > 0) {
  159       // parent
  160       pids[i] = pid;
  161       // Set start time
  162       gettimeofday(&start_times[i], NULL);
  163       i++;
  164     } else {
  165       // failure
  166       printf("%d: Failure\n", i);
  167       return 1;
  168     }
  169   }
  170 
  171   // Wait for all children to exit, and clock their exit time
  172   time_pids(pids, stream_count, exit_times);
  173 
  174   printf("Fin!\n");
  175   return 0;
  176 }
  177 
  178 
  179 //
  180 // Ye olde main
  181 //
  182 int main(int argc, char *argv[]) {
  183   int amount;
  184   int stream_count;
  185   char *server;
  186   int port;
  187 
  188   if(argc != 5) {
  189     printf("\nUsage:\n");
  190     printf("  %s <megabytes> <stream_count> <server-ip> <port>\n\n", argv[0]);
  191     return 1;
  192   }
  193 
  194   // Parse program arguments
  195   amount       = atoi(argv[1]);
  196   stream_count = atoi(argv[2]);
  197   server       = argv[3];
  198   port         = atoi(argv[4]);
  199 
  200   struct timeval start_times[stream_count];
  201   struct timeval exit_times[stream_count];
  202 
  203   spawn_tests(amount, stream_count, server, port, start_times, exit_times);
  204 
  205   // Clear a little space
  206   printf("\n\n");
  207 
  208   long total_amount = amount * stream_count;
  209   double total_time = 0;
  210 
  211   for(int i = 0; i < stream_count; i++) {
  212     // calculate average transfer rate for this thread
  213 
  214     // Second difference, multiplied by 1000 to make room for microseconds
  215     double time_s = 1000 * (exit_times[i].tv_sec - start_times[i].tv_sec);
  216     // Microsecond difference
  217     double time_ms = exit_times[i].tv_usec - start_times[i].tv_usec;
  218     // Sum of microsecond and second differences
  219     double time = (time_s + time_ms) / 1000;
  220 
  221     // Append duration to the total duration of transfers.
  222     total_time += time;
  223 
  224     double avg = amount / time;
  225 
  226     printf("Fork %d/%d transferred %d MB in %.4f seconds, averaging %.2f MB/s\n",
  227       i + 1,
  228       stream_count,
  229       amount,
  230       time,
  231       avg
  232     );
  233   }
  234 
  235   printf("\n%d forks transferred a total of %ld MBs in %.2f parallel seconds\n",
  236     stream_count,
  237     total_amount,
  238     total_time
  239   );
  240     
  241   return 0;
  242 }

Generated by cgit