summaryrefslogtreecommitdiff
path: root/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c242
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;
+}

Generated by cgit