Skip to content

Commit 6d88bdc

Browse files
committed
Merging changes from PR-Implement streaming json output esnet#1098
1 parent 97c2e40 commit 6d88bdc

File tree

6 files changed

+94
-10
lines changed

6 files changed

+94
-10
lines changed

src/iperf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ struct iperf_test
339339
int bidirectional; /* --bidirectional */
340340
int verbose; /* -V option - verbose mode */
341341
int json_output; /* -J option - JSON output */
342+
int json_stream; /* --json-stream */
342343
int zerocopy; /* -Z option - use sendfile */
343344
int debug; /* -d option - enable debug */
344345
enum debug_level debug_level; /* -d option option - level of debug messages to show */

src/iperf3.1

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,11 @@ test by specifying the --get-server-output flag.
9696
Either the client or the server can produce its output in a JSON
9797
structure, useful for integration with other programs, by passing it
9898
the -J flag.
99-
Because the contents of the JSON structure are only completely known
99+
Normally the contents of the JSON structure are only completely known
100100
after the test has finished, no JSON output will be emitted until the
101101
end of the test.
102+
By enabling line-delimited JSON multiple objects will be emitted to
103+
provide a real-time parsable JSON output.
102104
.PP
103105
iperf3 has a (overly) large set of command-line options that can be
104106
used to set the parameters of a test.
@@ -157,6 +159,9 @@ give more detailed output
157159
.BR -J ", " --json " "
158160
output in JSON format
159161
.TP
162+
.BR --json-stream " "
163+
output in line-delimited JSON format
164+
.TP
160165
.BR --logfile " \fIfile\fR"
161166
send output to a log file.
162167
.TP

src/iperf_api.c

Lines changed: 82 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ static int diskfile_recv(struct iperf_stream *sp);
102102
static int JSON_write(int fd, cJSON *json);
103103
static void print_interval_results(struct iperf_test *test, struct iperf_stream *sp, cJSON *json_interval_streams);
104104
static cJSON *JSON_read(int fd);
105+
static int JSONStream_Output(struct iperf_test *test, const char* event_name, cJSON* obj);
105106

106107

107108
/*************************** Print usage functions ****************************/
@@ -320,6 +321,12 @@ iperf_get_test_json_output(struct iperf_test *ipt)
320321
return ipt->json_output;
321322
}
322323

324+
int
325+
iperf_get_test_json_stream(struct iperf_test *ipt)
326+
{
327+
return ipt->json_stream;
328+
}
329+
323330
char *
324331
iperf_get_test_json_output_string(struct iperf_test *ipt)
325332
{
@@ -682,6 +689,12 @@ iperf_set_test_json_output(struct iperf_test *ipt, int json_output)
682689
ipt->json_output = json_output;
683690
}
684691

692+
void
693+
iperf_set_test_json_stream(struct iperf_test *ipt, int json_stream)
694+
{
695+
ipt->json_stream = json_stream;
696+
}
697+
685698
int
686699
iperf_has_zerocopy( void )
687700
{
@@ -892,6 +905,9 @@ iperf_on_test_start(struct iperf_test *test)
892905
iperf_printf(test, test_start_time, test->protocol->name, test->num_streams, test->settings->blksize, test->omit, test->duration, test->settings->tos);
893906
}
894907
}
908+
if (test->json_stream) {
909+
JSONStream_Output(test, "start", test->json_start);
910+
}
895911
}
896912

897913
/* This converts an IPv6 string address from IPv4-mapped format into regular
@@ -1058,6 +1074,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
10581074
{"one-off", no_argument, NULL, '1'},
10591075
{"verbose", no_argument, NULL, 'V'},
10601076
{"json", no_argument, NULL, 'J'},
1077+
{"json-stream", no_argument, NULL, OPT_JSON_STREAM},
10611078
{"version", no_argument, NULL, 'v'},
10621079
{"server", no_argument, NULL, 's'},
10631080
{"client", required_argument, NULL, 'c'},
@@ -1207,6 +1224,10 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
12071224
case 'J':
12081225
test->json_output = 1;
12091226
break;
1227+
case OPT_JSON_STREAM:
1228+
test->json_output = 1;
1229+
test->json_stream = 1;
1230+
break;
12101231
case 'v':
12111232
printf("%s (cJSON %s)\n%s\n%s\n", version, cJSON_Version(), get_system_info(),
12121233
get_optional_features());
@@ -2736,6 +2757,29 @@ JSON_read(int fd)
27362757
return json;
27372758
}
27382759

2760+
/*************************************************************/
2761+
/**
2762+
* JSONStream_Output - outputs an obj as event without distrubing it
2763+
*/
2764+
2765+
static int
2766+
JSONStream_Output(struct iperf_test * test, const char * event_name, cJSON * obj)
2767+
{
2768+
cJSON *event = cJSON_CreateObject();
2769+
if (!event)
2770+
return -1;
2771+
cJSON_AddStringToObject(event, "event", event_name);
2772+
cJSON_AddItemReferenceToObject(event, "data", obj);
2773+
char *str = cJSON_PrintUnformatted(event);
2774+
if (str == NULL)
2775+
return -1;
2776+
fprintf(test->outfile, "%s\n", str);
2777+
iflush(test);
2778+
cJSON_free(str);
2779+
cJSON_Delete(event);
2780+
return 0;
2781+
}
2782+
27392783
/*************************************************************/
27402784
/**
27412785
* add_to_interval_list -- adds new interval to the interval_list
@@ -3400,6 +3444,7 @@ iperf_print_intermediate(struct iperf_test *test)
34003444

34013445
int lower_mode, upper_mode;
34023446
int current_mode;
3447+
int discard_json;
34033448

34043449
/*
34053450
* Due to timing oddities, there can be cases, especially on the
@@ -3445,12 +3490,21 @@ iperf_print_intermediate(struct iperf_test *test)
34453490
return;
34463491
}
34473492

3493+
/*
3494+
* When we use streamed json, we don't actually need to keep the interval
3495+
* results around unless we're the server and the client requested the server output.
3496+
*
3497+
* This avoids unneeded memory build up for long sessions.
3498+
*/
3499+
discard_json = test->json_stream == 1 && !(test->role == 's' && test->get_server_output);
3500+
34483501
if (test->json_output) {
34493502
json_interval = cJSON_CreateObject();
34503503
if (json_interval == NULL)
34513504
return;
3452-
cJSON_AddItemToArray(test->json_intervals, json_interval);
3453-
json_interval_streams = cJSON_CreateArray();
3505+
if(!discard_json)
3506+
cJSON_AddItemToArray(test->json_intervals, json_interval);
3507+
json_interval_streams = cJSON_CreateArray();
34543508
if (json_interval_streams == NULL)
34553509
return;
34563510
cJSON_AddItemToObject(json_interval, "streams", json_interval_streams);
@@ -3600,6 +3654,10 @@ iperf_print_intermediate(struct iperf_test *test)
36003654
}
36013655
}
36023656
}
3657+
if (test->json_stream)
3658+
JSONStream_Output(test, "interval", json_interval);
3659+
if (discard_json)
3660+
cJSON_Delete(json_interval);
36033661
}
36043662

36053663
/**
@@ -4811,14 +4869,29 @@ iperf_json_finish(struct iperf_test *test)
48114869
return -1;
48124870
}
48134871

4814-
if (pthread_mutex_lock(&(test->print_mutex)) != 0) {
4815-
perror("iperf_json_finish: pthread_mutex_lock");
4816-
}
4817-
fprintf(test->outfile, "%s\n", test->json_output_string);
4818-
if (pthread_mutex_unlock(&(test->print_mutex)) != 0) {
4819-
perror("iperf_json_finish: pthread_mutex_unlock");
4872+
if (test->json_stream) {
4873+
cJSON *error = cJSON_GetObjectItem(test->json_top, "error");
4874+
if (error) {
4875+
JSONStream_Output(test, "error", error);
4876+
}
4877+
if (test->json_server_output) {
4878+
JSONStream_Output(test, "server_output_json", test->json_server_output);
4879+
}
4880+
if (test->server_output_text) {
4881+
JSONStream_Output(test, "server_output_text", cJSON_CreateString(test->server_output_text));
4882+
}
4883+
JSONStream_Output(test, "end", test->json_end);
48204884
}
4821-
iflush(test);
4885+
else {
4886+
if (pthread_mutex_lock(&(test->print_mutex)) != 0) {
4887+
perror("iperf_json_finish: pthread_mutex_lock");
4888+
}
4889+
fprintf(test->outfile, "%s\n", test->json_output_string);
4890+
if (pthread_mutex_unlock(&(test->print_mutex)) != 0) {
4891+
perror("iperf_json_finish: pthread_mutex_unlock");
4892+
}
4893+
iflush(test);
4894+
}
48224895
cJSON_Delete(test->json_top);
48234896
test->json_top = NULL;
48244897
}

src/iperf_api.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ typedef atomic_uint_fast64_t atomic_iperf_size_t;
101101
#define OPT_DONT_FRAGMENT 26
102102
#define OPT_RCV_TIMEOUT 27
103103
#define OPT_SND_TIMEOUT 28
104+
#define OPT_JSON_STREAM 29
104105

105106
/* states */
106107
#define TEST_START 1
@@ -151,6 +152,7 @@ char* iperf_get_test_template( struct iperf_test* ipt );
151152
int iperf_get_test_protocol_id( struct iperf_test* ipt );
152153
int iperf_get_test_json_output( struct iperf_test* ipt );
153154
char* iperf_get_test_json_output_string ( struct iperf_test* ipt );
155+
int iperf_get_test_json_stream( struct iperf_test* ipt );
154156
int iperf_get_test_zerocopy( struct iperf_test* ipt );
155157
int iperf_get_test_get_server_output( struct iperf_test* ipt );
156158
char iperf_get_test_unit_format(struct iperf_test *ipt);
@@ -195,6 +197,7 @@ void iperf_set_test_server_hostname( struct iperf_test* ipt, const char* server_
195197
void iperf_set_test_template( struct iperf_test *ipt, const char *tmp_template );
196198
void iperf_set_test_reverse( struct iperf_test* ipt, int reverse );
197199
void iperf_set_test_json_output( struct iperf_test* ipt, int json_output );
200+
void iperf_set_test_json_stream( struct iperf_test* ipt, int json_stream );
198201
int iperf_has_zerocopy( void );
199202
void iperf_set_test_zerocopy( struct iperf_test* ipt, int zerocopy );
200203
void iperf_set_test_get_server_output( struct iperf_test* ipt, int get_server_output );

src/iperf_locale.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
126126
#endif /* HAVE_SO_BINDTODEVICE */
127127
" -V, --verbose more detailed output\n"
128128
" -J, --json output in JSON format\n"
129+
" --json-stream output in line-delimited JSON format\n"
129130
" --logfile f send output to a log file\n"
130131
" --forceflush force flushing output at every interval\n"
131132
" --timestamps<=format> emit a timestamp at the start of each output line\n"

src/libiperf.3

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Setting test parameters:
3232
void iperf_set_test_blksize( struct iperf_test *t, int blksize );
3333
void iperf_set_test_num_streams( struct iperf_test *t, int num_streams );
3434
void iperf_set_test_json_output( struct iperf_test *t, int json_output );
35+
void iperf_set_test_json_stream( struct iperf_test *t, int json_stream );
3536
int iperf_has_zerocopy( void );
3637
void iperf_set_test_zerocopy( struct iperf_test* t, int zerocopy );
3738
void iperf_set_test_tos( struct iperf_test* t, int tos );

0 commit comments

Comments
 (0)