summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAaron Ball <nullspoon@iohq.net>2015-10-05 08:37:27 -0600
committerAaron Ball <nullspoon@iohq.net>2015-10-05 08:37:38 -0600
commit522efa1f37ada373c6ea6624ab6d00e0fcb53e2e (patch)
tree6efb3837410e1583bec0a7e12084c5c5721f1073 /src
parent7c9eea953af3f42553b75a4dabb81f4b40ae6540 (diff)
parent2f480e7c8dab41e4a637e7f86a972d41ee7e8042 (diff)
downloadcnetbench-master.tar.gz
cnetbench-master.tar.xz
Merge branch 't_test'HEADmaster
Diffstat (limited to 'src')
-rw-r--r--src/common.c59
-rw-r--r--src/common.h26
-rw-r--r--src/main.c287
-rw-r--r--src/test.c141
-rw-r--r--src/test.h43
5 files changed, 370 insertions, 186 deletions
diff --git a/src/common.c b/src/common.c
new file mode 100644
index 0000000..558dedf
--- /dev/null
+++ b/src/common.c
@@ -0,0 +1,59 @@
+//
+// 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 "common.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;
+}
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 0000000..7fdb513
--- /dev/null
+++ b/src/common.h
@@ -0,0 +1,26 @@
+//
+// 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>
+// For sleep()
+#include <unistd.h>
+// For pid_t
+#include <netinet/in.h>
+// For timing functionality
+#include <sys/time.h>
+#include <wait.h>
+
+int time_pids(pid_t*, int, struct timeval*);
diff --git a/src/main.c b/src/main.c
index 3fb9a4a..2e0f391 100644
--- a/src/main.c
+++ b/src/main.c
@@ -16,162 +16,119 @@
//
#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>
+#include <string.h>
+
+#include "common.h"
+#include "test.h"
+
+
+void get_help() {
+ printf(
+ "\n"
+ "Cnetbench is a simple protocol-agnostic network benchmarking program.\n"
+ "Simply put, it is a fire-hydrant of data and sends as much data as is\n"
+ "allowed, and reports what those numbers are. This can be single stream\n"
+ "or multiple concurrent data streams.\n"
+ "\n"
+ "Usage:\n\n"
+ " cnetbench -d <destination> -p <port> [-m <megabytes>] [-c <stream_count>]"
+ "\n\n");
+}
//
-// 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.
+// Prints test results in a friendly, human-readable format.
//
-// @return int Overall status of all exited pids (if any fail exit, returns 1)
+// @param t_test* Test type containing test parameters
+// @param start_times Array of child start times, ordered by child process index
+// @param exit_times Array of child exit times, ordered by child process index
//
-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;
-}
+void print_results(t_test *t,
+ struct timeval *start_times,
+ struct timeval *exit_times) {
+ // Clear a little space
+ printf("\n\n");
+ long total_amount = t->amount * t->streams;
+ double total_time = 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;
- }
+ for(int i = 0; i < t->streams; i++) {
+ // calculate average transfer rate for this thread
- // 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);
+ // 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;
- if(connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) {
- printf("Connection error\n");
- return -1;
- }
- printf("Connected\n");
+ // Append duration to the total duration of transfers.
+ total_time += time;
- // Send some data
- long i = 0;
- while(i < amount) {
- if(send(socket_desc, message, size, 0) < 0) {
- return -1;
- }
- i++;
+ double avg = t->amount / time;
+
+ printf("Fork %d/%d transferred %d MB in %.4f seconds, averaging %.2f MB/s\n",
+ i + 1,
+ t->streams,
+ t->amount,
+ time,
+ avg
+ );
}
- free(message);
- return 0;
+ printf("\n%d forks transferred a total of %ld MBs in %.2f parallel seconds\n",
+ t->streams,
+ total_amount,
+ total_time
+ );
}
//
-// 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++;
+// Parses the program arguments and populates a t_test type with the values
+// specified by the user.
+//
+// @param argc Number of arguments passed to the program (the main argc)
+// @param argv Arguments passed to the program (the main argv)
+// @param test T_test object to be populated
+//
+int parse_args(int argc, char *argv[], t_test *test) {
+ int i = 1;
+
+ char server = 0;
+ char port = 0;
+
+ while(i < argc) {
+ if(strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--megabytes") == 0) {
+ ++i;
+ test->amount = atoi(argv[i]);
+ } else if(strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--count") == 0) {
+ ++i;
+ test->streams = atoi(argv[i]);
+ } else if(strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--dest") == 0) {
+ ++i;
+ strcpy(test->server, argv[i]);
+ server = 1;
+ } else if(strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) {
+ ++i;
+ test->port = atoi(argv[i]);
+ port = 1;
} else {
- // failure
- printf("%d: Failure\n", i);
+ get_help();
return 1;
}
+ ++i;
}
- // Wait for all children to exit, and clock their exit time
- time_pids(pids, stream_count, exit_times);
+ if(server == 0) {
+ printf("Please specify a destination server (-d,--dest) to test with.\n");
+ return 1;
+ }
+ if(port == 0) {
+ printf("Please specify a port (-p,--port) to run tests over.\n");
+ return 1;
+ }
- printf("Fin!\n");
return 0;
}
@@ -180,63 +137,21 @@ int spawn_tests(int amount,
// 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;
- }
+ t_test test;
+ // Initialize the test object with defaults
+ new_test(&test);
// Parse program arguments
- amount = atoi(argv[1]);
- stream_count = atoi(argv[2]);
- server = argv[3];
- port = atoi(argv[4]);
+ int arg_status = parse_args(argc, argv, &test);
- 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;
+ if(arg_status > 0)
+ return 1;
- // Append duration to the total duration of transfers.
- total_time += time;
+ struct timeval start_times[test.streams];
+ struct timeval exit_times[test.streams];
- double avg = amount / time;
+ test_start_all(&test, start_times, exit_times);
+ print_results(&test, start_times, exit_times);
- 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;
}
diff --git a/src/test.c b/src/test.c
new file mode 100644
index 0000000..83015c1
--- /dev/null
+++ b/src/test.c
@@ -0,0 +1,141 @@
+//
+// 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 "test.h"
+
+#ifndef cnetbench_test
+#define cnetbench_test
+
+#define test_default_amount 64
+#define test_default_streams 1
+
+void new_test(t_test *test) {
+ test->amount = test_default_amount;
+ test->streams = test_default_streams;
+}
+
+
+//
+// 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_execute(t_test* test) {
+ 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(test->server);
+ server.sin_family = AF_INET;
+ server.sin_port = htons(test->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 < test->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) {
+int test_start_all(t_test *test_def,
+ struct timeval *start_times,
+ struct timeval *exit_times) {
+ pid_t pids[test_def->streams];
+
+ pid_t pid;
+ int i = 0;
+ while(i < test_def->streams) {
+ // Fork
+ pid = fork();
+
+ if(pid == 0) {
+ // Execute the test
+ printf("Fork %d starting\n", i);
+ test_execute(test_def);
+ 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, test_def->streams, exit_times);
+
+ printf("Fin!\n");
+ return 0;
+}
+
+#endif
diff --git a/src/test.h b/src/test.h
new file mode 100644
index 0000000..753109c
--- /dev/null
+++ b/src/test.h
@@ -0,0 +1,43 @@
+//
+// 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 <string.h>
+// Networking-related tasks
+#include <sys/socket.h>
+#include <arpa/inet.h>
+// For pid timing
+#include <sys/time.h>
+#include <wait.h>
+// For fork()
+#include <unistd.h>
+
+// for time_pids function
+#include "common.h"
+
+typedef struct {
+ int amount;
+ int streams;
+ char server[256];
+ int port;
+} t_test;
+
+void new_test(t_test*);
+
+int test_execute(t_test*);
+
+int test_start_all(t_test*, struct timeval*, struct timeval*);

Generated by cgit