Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/const.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions src/lagscope.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
6 changes: 4 additions & 2 deletions src/lagscope.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
59 changes: 44 additions & 15 deletions src/latencies_stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
Expand Down
66 changes: 44 additions & 22 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -181,26 +186,36 @@ 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++;
test_runtime->current_time = recv_time;
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)
Expand All @@ -223,37 +238,44 @@ 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 */
if(test->perc) {
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...");
Expand Down
21 changes: 16 additions & 5 deletions src/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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);
Expand Down Expand Up @@ -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");
}
}
Expand Down Expand Up @@ -215,14 +219,15 @@ 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}
};

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;
Expand Down Expand Up @@ -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':
Expand Down
5 changes: 3 additions & 2 deletions src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down