diff options
Diffstat (limited to 'src/main.c')
-rw-r--r-- | src/main.c | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..3fb9a4a --- /dev/null +++ b/src/main.c @@ -0,0 +1,242 @@ +// +// Copyright (C) 2015 Aaron Ball <nullspoon@iohq.net> +// +// Cnetbench is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Cnetbench is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with cnetbench. If not, see <http://www.gnu.org/licenses/>. +// +#include <stdlib.h> +#include <stdio.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <strings.h> +#include <unistd.h> +#include <sys/time.h> +#include <wait.h> + + +// +// Watches an array of pids for their exit, and updates the output array +// exit_times with each pid's exit timeval struct timestamp (epoch with +// microseconds). +// +// Note that this function does not start the pids, but only watches them. To +// calculate runtime, external code needs to track when the pids were created. +// +// @param pids Array of pid_t pids to be watched +// @param count Number of pids in the pids array +// @param exit_times Array of timeval structs that will contain exit times for +// all of the pids in the array. Must be at least the size of +// the pids array or unpredictable behavior may result. +// +// @return int Overall status of all exited pids (if any fail exit, returns 1) +// +int time_pids(pid_t* pids, int count, struct timeval *exit_times) { + int i; + int waiting = 1; + + while(waiting != 0) { + waiting = 0; + for(i = 0; i < count; i++) { + // Pid with this index has exited, skip it. + if(pids[i] == 0) + continue; + + // 0 = state change (exit) + if(waitpid(pids[i], NULL, WNOHANG) != 0) { + // Child has exited + pids[i] = 0; + // Write the exit time + gettimeofday(&exit_times[i], NULL); + } else { + waiting = 1; + } + } + sleep(1); + } + return 0; +} + + +// +// Performs a benchmark test on the specified destination/port, sending the +// specified amount of data. +// +// @param amount Megabytes to send +// @param dest Destination host to send data to +// @param port Destination port to send the data on +// +// @return int Succcess (0) or failure (1) of data send +// +int test(int amount, char *dest, int port) { + int socket_desc; + // This is 1 megabyte + long size = 1048576; + char *message = calloc(size, 1); + + // AF_INET: ipv4 (AF_INET6 would be ipv6) + // SOCK_STREAM: TCP + // 0: Protocol 0 (IP) + socket_desc = socket(AF_INET, SOCK_STREAM, 0); + + if(socket_desc == -1) { + printf("Error creating socket\n"); + return 1; + } + + // Set socket values + struct sockaddr_in server; + server.sin_addr.s_addr = inet_addr(dest); + server.sin_family = AF_INET; + server.sin_port = htons(port); + + if(connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) { + printf("Connection error\n"); + return -1; + } + printf("Connected\n"); + + // Send some data + long i = 0; + while(i < amount) { + if(send(socket_desc, message, size, 0) < 0) { + return -1; + } + i++; + } + + free(message); + return 0; +} + + +// +// Forks sub-processes off stream_count times, to test bandwidth to the +// specified destination host/port, sending the specified amount of data. +// +// Note that start_times and exit_times are two arrays that will receive start +// and exit timestamps for each pid for timing purposes. +// +// @param amount Amount of data in megabytes to send +// @param stream_count Number of data streams to test concurrently +// @param dest Destination host to send data to +// @param port Destination port to send data over +// @param start_times Output array of timeval start time timestamps +// @param exit_times Output array of timeval exit time timestamps +// +// @return int Success (0) or failure(1) +// +int spawn_tests(int amount, + int stream_count, + char* dest, + int port, + struct timeval *start_times, + struct timeval *exit_times) { + pid_t pids[stream_count]; + + pid_t pid; + int i = 0; + while(i < stream_count) { + // Fork + pid = fork(); + + if(pid == 0) { + // Execute the test + printf("Fork %d starting\n", i); + test(amount, dest, port); + exit(0); + } else if(pid > 0) { + // parent + pids[i] = pid; + // Set start time + gettimeofday(&start_times[i], NULL); + i++; + } else { + // failure + printf("%d: Failure\n", i); + return 1; + } + } + + // Wait for all children to exit, and clock their exit time + time_pids(pids, stream_count, exit_times); + + printf("Fin!\n"); + return 0; +} + + +// +// Ye olde main +// +int main(int argc, char *argv[]) { + int amount; + int stream_count; + char *server; + int port; + + if(argc != 5) { + printf("\nUsage:\n"); + printf(" %s <megabytes> <stream_count> <server-ip> <port>\n\n", argv[0]); + return 1; + } + + // Parse program arguments + amount = atoi(argv[1]); + stream_count = atoi(argv[2]); + server = argv[3]; + port = atoi(argv[4]); + + struct timeval start_times[stream_count]; + struct timeval exit_times[stream_count]; + + spawn_tests(amount, stream_count, server, port, start_times, exit_times); + + // Clear a little space + printf("\n\n"); + + long total_amount = amount * stream_count; + double total_time = 0; + + for(int i = 0; i < stream_count; i++) { + // calculate average transfer rate for this thread + + // Second difference, multiplied by 1000 to make room for microseconds + double time_s = 1000 * (exit_times[i].tv_sec - start_times[i].tv_sec); + // Microsecond difference + double time_ms = exit_times[i].tv_usec - start_times[i].tv_usec; + // Sum of microsecond and second differences + double time = (time_s + time_ms) / 1000; + + // Append duration to the total duration of transfers. + total_time += time; + + double avg = amount / time; + + printf("Fork %d/%d transferred %d MB in %.4f seconds, averaging %.2f MB/s\n", + i + 1, + stream_count, + amount, + time, + avg + ); + } + + printf("\n%d forks transferred a total of %ld MBs in %.2f parallel seconds\n", + stream_count, + total_amount, + total_time + ); + + return 0; +} |