diff --git a/README.md b/README.md index 48b629a..89ac604 100644 --- a/README.md +++ b/README.md @@ -149,19 +149,26 @@ Percentile Latency(us) 99.999% 3921 ``` -### Example run to dump all latency values into a csv file +### Example run to dump all latency and/or TCP round trip time values into a csv file -On NODE2 (the sender), run: +For sent and received timed latencies on NODE2 (the sender), run: ``` ./lagscope -s192.168.4.1 -Rlatencies_log.csv ``` +OR + +For TCP socket's round trip times on NODE2 (the sender), run: +``` +./lagscope -s192.168.4.1 -Ttcprtt_log.csv +``` + (Translation: Run lagscope as a sender and dumps latencies into a csv file) Example sender-side output from a given run: ``` -paulkim@NODE2:~/lagscope/src# ./lagscope -s192.168.4.1 -Rlatencies_log.csv +paulkim@NODE2:~/lagscope/src# ./lagscope -s192.168.4.1 -Rlatencies_log.csv -Ttcprtt_log.csv lagscope 0.1.2 --------------------------------------------------------- 19:38:31 INFO: New connection: local:13948 [socket:3] --> 192.168.4.1:6001 @@ -170,6 +177,7 @@ lagscope 0.1.2 19:38:32 INFO: Number of successful Pings: 1000000 19:38:32 INFO: Minimum = 96.083us, Maximum = 2828.121us, Average = 147.913us 19:38:32 INFO: Dumping all latencies into csv file: latencies_log.csv +19:38:32 INFO: Dumping TCP rtt into csv file: tcprtt_log.csv ``` ## Save latency frequency table graph as an image file diff --git a/src/const.h b/src/const.h index 83d8cfb..0afbf7b 100644 --- a/src/const.h +++ b/src/const.h @@ -12,6 +12,7 @@ #define UDP SOCK_DGRAM #define DEFAULT_CSV_FILENAME "latencies_log.csv" +#define DEFAULT_RTT_CSV_FILENAME "tcprtt_latencies_log.csv" #define TIME_DURATION 1 #define PING_ITERATION 2 diff --git a/src/lagscope.c b/src/lagscope.c index 2f7f4c9..65d5604 100644 --- a/src/lagscope.c +++ b/src/lagscope.c @@ -45,8 +45,10 @@ void default_lagscope_test(struct lagscope_test *test) test->perc = false; test->freq_table_dump = false; - test->raw_dump = false; - test->csv_file_name = DEFAULT_CSV_FILENAME; + test->traffic_raw_dump = false; + test->traffic_raw_csv_filename = DEFAULT_CSV_FILENAME; + test->tcpi_rtt_raw_dump = false; + test->tcpi_rtt_raw_csv_filename = DEFAULT_RTT_CSV_FILENAME; test->verbose = false; } diff --git a/src/lagscope.h b/src/lagscope.h index 1ba2b37..e5802fe 100644 --- a/src/lagscope.h +++ b/src/lagscope.h @@ -47,8 +47,10 @@ struct lagscope_test bool freq_table_dump; /* if there's an argument after -P */ char *json_file_name; /* name of the file frequency table will be dumped into, follows '-P' */ - bool raw_dump; /* '-R' for dumping latencies into a file */ - char *csv_file_name; /* name of the file being dumped into, follows '-R' */ + bool traffic_raw_dump; /* '-R' for dumping latencies into a file */ + char *traffic_raw_csv_filename; /* name of the file being dumped into, follows '-R' */ + bool tcpi_rtt_raw_dump; /* '-T' for dumping rtt latencies from tcp socket into a file */ + char *tcpi_rtt_raw_csv_filename; /* name of the file tcp rtt latencies will be dumped into, follow '-T' */ /* end of client-only parameters */ bool verbose; /* '-V' for verbose logging */ diff --git a/src/latencies_stats.c b/src/latencies_stats.c index 0a3ef40..dbcf058 100644 --- a/src/latencies_stats.c +++ b/src/latencies_stats.c @@ -9,8 +9,12 @@ typedef struct node struct node *next; }node_t; -static node_t *head = NULL; // start of the latency list -static node_t *tail = NULL; // end of the latency list +static node_t *traffic_head = NULL; // start of the latency list +static node_t *traffic_tail = NULL; // end of the latency list + + +static node_t *tcpi_rtt_head = NULL; // start of the tcp_rtt latency list +static node_t *tcpi_rtt_tail = NULL; // end of the tcp_rtt latency list /* A Hashtable where the keys are the latencies and the values are the frequencies of that latency */ static unsigned long *freq_table = NULL; @@ -56,12 +60,12 @@ int process_latencies(unsigned long max_latency) memset(freq_table, 0, (max_latency + 1) * sizeof(unsigned long)); - if(head == NULL) + if(traffic_head == NULL) return ERROR_GENERAL; /* Using the latencies stored in the linked list as keys, increments at latency in frequency table */ - temp = head; + temp = traffic_head; while(temp != NULL) { freq_table[temp->lat]++; temp = temp->next; @@ -141,9 +145,16 @@ int show_histogram(int start, int len, int count, unsigned long max_latency) return NO_ERROR; } -void create_latencies_csv(const char *csv_filename) +void create_latencies_csv(struct lagscope_test *test, const char *csv_filename) { - node_t * temp = head; + node_t * temp = NULL; + if(test->traffic_raw_dump) { + temp = traffic_head; + } + else if(test->tcpi_rtt_raw_dump) { + temp = tcpi_rtt_head; + } + unsigned int latency_idx = 0; FILE *fp = NULL; @@ -197,14 +208,26 @@ void create_freq_table_json(unsigned long max_latency, const char *file_name) fclose(fp); } -void push(unsigned long lat) +void push_traffic_latency(unsigned long lat) { node_t *tmp = new_node(lat); - if(head == NULL) { - head = tail = tmp; + if(traffic_head == NULL) { + traffic_head = traffic_tail = tmp; } else { - tail->next = tmp; - tail = tail->next; + traffic_tail->next = tmp; + traffic_tail = traffic_tail->next; + } + return; +} + +void push_tcpi_rtt_latency(unsigned long lat) +{ + node_t *tmp = new_node(lat); + if(tcpi_rtt_head == NULL) { + tcpi_rtt_head = tcpi_rtt_tail = tmp; + } else { + tcpi_rtt_tail->next = tmp; + tcpi_rtt_tail = tcpi_rtt_tail->next; } return; } @@ -213,12 +236,18 @@ void push(unsigned long lat) void latencies_stats_cleanup(void) { node_t *temp = NULL; - while(head != NULL) { - temp = head; - head = head->next; + while(traffic_head != NULL) { + temp = traffic_head; + traffic_head = traffic_head->next; + free(temp); + } + while(tcpi_rtt_head != NULL) { + temp = tcpi_rtt_head; + tcpi_rtt_head = tcpi_rtt_head->next; free(temp); } - head = NULL; + traffic_head = NULL; + tcpi_rtt_head = NULL; if(freq_table) free(freq_table); return; diff --git a/src/main.c b/src/main.c index de906e1..d1f19fa 100644 --- a/src/main.c +++ b/src/main.c @@ -31,20 +31,25 @@ long run_lagscope_sender(struct lagscope_test_client *client) struct timeval now; struct timeval send_time; struct timeval recv_time; - double latency = 0; + double traffic_latency = 0; + unsigned long tcpi_rtt_latency = 0; int i = 0; /* for ping statistics */ unsigned long n_pings = 0; //number of pings - double max_latency = 0; - double min_latency = 60000; //60 seconds - double sum_latency = 0; + double traffic_max_latency = 0; + double traffic_min_latency = 60000; //60 seconds + double traffic_sum_latency = 0; int latencies_stats_err_check = 0; verbose_log = test->verbose; test_runtime = new_test_runtime(test); + struct tcp_info tcpinfo; + socklen_t tcp_info_length = sizeof(tcpinfo); + + ip_address_max_size = (test->domain == AF_INET? INET_ADDRSTRLEN : INET6_ADDRSTRLEN); if ((ip_address_str = (char *)malloc(ip_address_max_size)) == (char *)NULL) { PRINT_ERR("cannot allocate memory for ip address string"); @@ -181,14 +186,24 @@ long run_lagscope_sender(struct lagscope_test_client *client) gettimeofday(&now, NULL); recv_time = now; - latency = get_time_diff(&recv_time, &send_time) * 1000 * 1000; + traffic_latency = get_time_diff(&recv_time, &send_time) * 1000 * 1000; + + push_traffic_latency(traffic_latency); // Push latency onto linked list - push(latency); // Push latency onto linked list + if(test->tcpi_rtt_raw_dump) { + if(getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &tcpinfo, &tcp_info_length) != 0) { + PRINT_INFO("getsockopt (TCP_INFO) failed"); + } + else { + tcpi_rtt_latency = tcpinfo.tcpi_rtt; + push_tcpi_rtt_latency(tcpi_rtt_latency); + } + } ASPRINTF(&log, "Reply from %s: bytes=%d time=%.3fus", ip_address_str, n, - latency); + traffic_latency); PRINT_DBG_FREE(log); n_pings++; @@ -196,11 +211,11 @@ long run_lagscope_sender(struct lagscope_test_client *client) test_runtime->ping_elapsed = n_pings; /* calculate max. avg. min. */ - sum_latency += latency; - if (max_latency < latency) - max_latency = latency; - if (min_latency > latency) - min_latency = latency; + traffic_sum_latency += traffic_latency; + if (traffic_max_latency < traffic_latency) + traffic_max_latency = traffic_latency; + if (traffic_min_latency > traffic_latency) + traffic_min_latency = traffic_latency; if (test->test_mode == PING_ITERATION) if (n_pings >= test->iteration) @@ -223,21 +238,28 @@ long run_lagscope_sender(struct lagscope_test_client *client) PRINT_INFO_FREE(log); if (n_pings > 0) { ASPRINTF(&log, "\t Minimum = %.3fus, Maximum = %.3fus, Average = %.3fus", - min_latency, - max_latency, - sum_latency/n_pings); + traffic_min_latency, + traffic_max_latency, + traffic_sum_latency/n_pings); PRINT_INFO_FREE(log); } /* function call to dump latencies into a csv file */ - if(test->raw_dump) { - ASPRINTF(&log, "Dumping all latencies into csv file: %s", test->csv_file_name); + if(test->traffic_raw_dump) { + ASPRINTF(&log, "Dumping all latencies into csv file: %s", test->traffic_raw_csv_filename); + PRINT_INFO_FREE(log); + create_latencies_csv(test, test->traffic_raw_csv_filename); + test->traffic_raw_dump = false; + } + + if(test->tcpi_rtt_raw_dump) { + ASPRINTF(&log, "Dumping all TCP rtt into csv file: %s", test->tcpi_rtt_raw_csv_filename); PRINT_INFO_FREE(log); - create_latencies_csv(test->csv_file_name); + create_latencies_csv(test, test->tcpi_rtt_raw_csv_filename); } if (test->perc || test->hist) { - latencies_stats_err_check = process_latencies(max_latency); + latencies_stats_err_check = process_latencies(traffic_max_latency); if (latencies_stats_err_check == NO_ERROR) { /* function call to show percentiles */ @@ -245,15 +267,15 @@ long run_lagscope_sender(struct lagscope_test_client *client) if(test->freq_table_dump) { ASPRINTF(&log, "Dumping latency frequency table into json file: %s", test->json_file_name); PRINT_INFO_FREE(log); - create_freq_table_json((unsigned long) max_latency, test->json_file_name); + create_freq_table_json((unsigned long) traffic_max_latency, test->json_file_name); } - show_percentile(max_latency, n_pings); + show_percentile(traffic_max_latency, n_pings); } /* function call to show histogram */ if(test->hist) { - show_histogram(test->hist_start, test->hist_len, test->hist_count, (unsigned long) max_latency); + show_histogram(test->hist_start, test->hist_len, test->hist_count, (unsigned long) traffic_max_latency); } } else if (latencies_stats_err_check == ERROR_MEMORY_ALLOC) { PRINT_ERR("Memory allocation failed, aborting..."); diff --git a/src/util.c b/src/util.c index 6302f6b..0dfc587 100644 --- a/src/util.c +++ b/src/util.c @@ -61,7 +61,7 @@ void print_flags(struct lagscope_test *test) void print_usage() { printf("Author: %s\n", AUTHOR_NAME); - printf("lagscope: [-r|-s|-D|-f|-6|-u|-p|-b|-B|-z|-t|-n|-i|-R|-P|-H|-a|-l|-c|-V|-h]\n\n"); + printf("lagscope: [-r|-s|-D|-f|-6|-u|-p|-b|-B|-z|-t|-n|-i|-R|-T|-P|-H|-a|-l|-c|-V|-h]\n\n"); printf("\t-r Run as a receiver\n"); printf("\t-s Run as a sender\n"); printf("\t-D Run as daemon\n"); @@ -80,6 +80,7 @@ void print_usage() printf("\t '-n' will be ignored if '-t' provided\n"); printf("\t-R [SENDER ONLY] dumps raw latencies into csv file\n"); + printf("\t-T [SENDER ONLY] dumps raw latencies from TCP socket, the round trip time, into csv file\n"); printf("\t-H [SENDER ONLY] print histogram of per-iteration latency values\n"); printf("\t-a [SENDER ONLY] histogram 1st interval start value [default: %d]\n", HIST_DEFAULT_START_AT); @@ -151,7 +152,10 @@ int verify_args(struct lagscope_test *test) } if (test->server_role) { - if (test->raw_dump) { + if (test->traffic_raw_dump) { + PRINT_ERR("dumping latencies into a file not supported on receiver side; ignored"); + } + if (test->tcpi_rtt_raw_dump) { PRINT_ERR("dumping latencies into a file not supported on receiver side; ignored"); } } @@ -215,6 +219,7 @@ int parse_arguments(struct lagscope_test *test, int argc, char **argv) {"hist-count", required_argument, NULL, 'c'}, {"perc", optional_argument, NULL, 'P'}, {"raw_dump", optional_argument, NULL, 'R'}, + {"raw_dump_rtt", optional_argument, NULL, 'T'}, {"verbose", no_argument, NULL, 'V'}, {"help", no_argument, NULL, 'h'}, {0, 0, 0, 0} @@ -222,7 +227,7 @@ int parse_arguments(struct lagscope_test *test, int argc, char **argv) int flag; - while ((flag = getopt_long(argc, argv, "r::s::Df:6up:b:B:z:t:n:i:R::P::Ha:l:c:Vh", longopts, NULL)) != -1) { + while ((flag = getopt_long(argc, argv, "r::s::Df:6up:b:B:z:t:n:i:R::T::P::Ha:l:c:Vh", longopts, NULL)) != -1) { switch (flag) { case 'r': test->server_role = true; @@ -311,9 +316,15 @@ int parse_arguments(struct lagscope_test *test, int argc, char **argv) break; case 'R': - test->raw_dump = true; + test->traffic_raw_dump = true; + if(optarg) + test->traffic_raw_csv_filename = optarg; + break; + + case 'T': + test->tcpi_rtt_raw_dump = true; if(optarg) - test->csv_file_name = optarg; + test->tcpi_rtt_raw_csv_filename = optarg; break; case 'h': diff --git a/src/util.h b/src/util.h index 6bb03cd..1bae00c 100644 --- a/src/util.h +++ b/src/util.h @@ -36,9 +36,10 @@ void print_test_stats(); int process_latencies(unsigned long max_latency); int show_percentile(unsigned long, unsigned long); int show_histogram(int, int, int, unsigned long); -void create_latencies_csv(const char *); +void create_latencies_csv(struct lagscope_test *test, const char *); void create_freq_table_json(unsigned long, const char *); -void push(unsigned long); +void push_traffic_latency(unsigned long); +void push_tcpi_rtt_latency(unsigned long); void latencies_stats_cleanup(void); double unit_atod(const char *s);