Skip to content

Commit b258cf4

Browse files
authored
Merge pull request stevenj#1 from kleinweby/output-json-stream
Implement streaming json output
2 parents 169b0d9 + a09a076 commit b258cf4

6 files changed

Lines changed: 90 additions & 4 deletions

File tree

src/iperf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ struct iperf_test
309309
int bidirectional; /* --bidirectional */
310310
int verbose; /* -V option - verbose mode */
311311
int json_output; /* -J option - JSON output */
312+
int json_stream; /* --json-stream */
312313
int zerocopy; /* -Z option - use sendfile */
313314
int debug; /* -d option - enable debug */
314315
int get_server_output; /* --get-server-output */

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 competely known
99+
Normally the contents of the JSON structure are only competely 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.
@@ -153,6 +155,9 @@ give more detailed output
153155
.BR -J ", " --json " "
154156
output in JSON format
155157
.TP
158+
.BR --json-stream " "
159+
output in line-delimited JSON format
160+
.TP
156161
.BR --logfile " \fIfile\fR"
157162
send output to a log file.
158163
.TP

src/iperf_api.c

Lines changed: 78 additions & 3 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 ****************************/
@@ -314,6 +315,12 @@ iperf_get_test_json_output_string(struct iperf_test *ipt)
314315
return ipt->json_output_string;
315316
}
316317

318+
int
319+
iperf_get_test_json_stream(struct iperf_test *ipt)
320+
{
321+
return ipt->json_stream;
322+
}
323+
317324
int
318325
iperf_get_test_zerocopy(struct iperf_test *ipt)
319326
{
@@ -622,6 +629,12 @@ iperf_set_test_json_output(struct iperf_test *ipt, int json_output)
622629
ipt->json_output = json_output;
623630
}
624631

632+
void
633+
iperf_set_test_json_stream(struct iperf_test *ipt, int json_stream)
634+
{
635+
ipt->json_stream = json_stream;
636+
}
637+
625638
int
626639
iperf_has_zerocopy( void )
627640
{
@@ -827,8 +840,12 @@ iperf_on_test_start(struct iperf_test *test)
827840
iperf_printf(test, test_start_time, test->protocol->name, test->num_streams, test->settings->blksize, test->omit, test->duration, test->settings->tos);
828841
}
829842
}
843+
if (test->json_stream) {
844+
JSONStream_Output(test, "start", test->json_start);
845+
}
830846
}
831847

848+
832849
/* This converts an IPv6 string address from IPv4-mapped format into regular
833850
** old IPv4 format, which is easier on the eyes of network veterans.
834851
**
@@ -938,6 +955,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
938955
{"one-off", no_argument, NULL, '1'},
939956
{"verbose", no_argument, NULL, 'V'},
940957
{"json", no_argument, NULL, 'J'},
958+
{"json-stream", no_argument, NULL, OPT_JSON_STREAM},
941959
{"version", no_argument, NULL, 'v'},
942960
{"server", no_argument, NULL, 's'},
943961
{"client", required_argument, NULL, 'c'},
@@ -1084,6 +1102,10 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
10841102
case 'J':
10851103
test->json_output = 1;
10861104
break;
1105+
case OPT_JSON_STREAM:
1106+
test->json_output = 1;
1107+
test->json_stream = 1;
1108+
break;
10871109
case 'v':
10881110
printf("%s (cJSON %s)\n%s\n%s\n", version, cJSON_Version(), get_system_info(),
10891111
get_optional_features());
@@ -2495,6 +2517,29 @@ JSON_read(int fd)
24952517
return json;
24962518
}
24972519

2520+
/*************************************************************/
2521+
/**
2522+
* JSONStream_Output - outputs an obj as event without distrubing it
2523+
*/
2524+
2525+
static int
2526+
JSONStream_Output(struct iperf_test * test, const char * event_name, cJSON * obj)
2527+
{
2528+
cJSON *event = cJSON_CreateObject();
2529+
if (!event)
2530+
return -1;
2531+
cJSON_AddStringToObject(event, "event", event_name);
2532+
cJSON_AddItemReferenceToObject(event, "data", obj);
2533+
char *str = cJSON_PrintUnformatted(event);
2534+
if (str == NULL)
2535+
return -1;
2536+
fprintf(test->outfile, "%s\n", str);
2537+
iflush(test);
2538+
cJSON_free(str);
2539+
cJSON_Delete(event);
2540+
return 0;
2541+
}
2542+
24982543
/*************************************************************/
24992544
/**
25002545
* add_to_interval_list -- adds new interval to the interval_list
@@ -3131,6 +3176,7 @@ iperf_print_intermediate(struct iperf_test *test)
31313176

31323177
int lower_mode, upper_mode;
31333178
int current_mode;
3179+
int discard_json;
31343180

31353181
/*
31363182
* Due to timing oddities, there can be cases, especially on the
@@ -3176,11 +3222,20 @@ iperf_print_intermediate(struct iperf_test *test)
31763222
return;
31773223
}
31783224

3225+
/*
3226+
* When we use streamed json, we don't actually need to keep the interval
3227+
* results around unless we're the server and the client requested the server output.
3228+
*
3229+
* This avoids unneeded memory build up for long sessions.
3230+
*/
3231+
discard_json = test->json_stream == 1 && !(test->role == 's' && test->get_server_output);
3232+
31793233
if (test->json_output) {
31803234
json_interval = cJSON_CreateObject();
31813235
if (json_interval == NULL)
31823236
return;
3183-
cJSON_AddItemToArray(test->json_intervals, json_interval);
3237+
if (!discard_json)
3238+
cJSON_AddItemToArray(test->json_intervals, json_interval);
31843239
json_interval_streams = cJSON_CreateArray();
31853240
if (json_interval_streams == NULL)
31863241
return;
@@ -3313,6 +3368,11 @@ iperf_print_intermediate(struct iperf_test *test)
33133368
}
33143369
}
33153370
}
3371+
3372+
if (test->json_stream)
3373+
JSONStream_Output(test, "interval", json_interval);
3374+
if (discard_json)
3375+
cJSON_Delete(json_interval);
33163376
}
33173377

33183378
/**
@@ -4443,8 +4503,23 @@ iperf_json_finish(struct iperf_test *test)
44434503
cJSON_free(str);
44444504
if (test->json_output_string == NULL)
44454505
return -1;
4446-
fprintf(test->outfile, "%s\n", test->json_output_string);
4447-
iflush(test);
4506+
if (test->json_stream) {
4507+
cJSON *error = cJSON_GetObjectItem(test->json_top, "error");
4508+
if (error) {
4509+
JSONStream_Output(test, "error", error);
4510+
}
4511+
if (test->json_server_output) {
4512+
JSONStream_Output(test, "server_output_json", test->json_server_output);
4513+
}
4514+
if (test->server_output_text) {
4515+
JSONStream_Output(test, "server_output_text", cJSON_CreateString(test->server_output_text));
4516+
}
4517+
JSONStream_Output(test, "end", test->json_end);
4518+
}
4519+
else {
4520+
fprintf(test->outfile, "%s\n", test->json_output_string);
4521+
iflush(test);
4522+
}
44484523
cJSON_Delete(test->json_top);
44494524
test->json_top = test->json_start = test->json_connected = test->json_intervals = test->json_server_output = test->json_end = NULL;
44504525
return 0;

src/iperf_api.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ typedef uint64_t iperf_size_t;
8787
#define OPT_IDLE_TIMEOUT 25
8888
#define OPT_DONT_FRAGMENT 26
8989
#define OPT_RCV_TIMEOUT 27
90+
#define OPT_JSON_STREAM 28
9091

9192
/* states */
9293
#define TEST_START 1
@@ -135,6 +136,7 @@ char* iperf_get_test_template( struct iperf_test* ipt );
135136
int iperf_get_test_protocol_id( struct iperf_test* ipt );
136137
int iperf_get_test_json_output( struct iperf_test* ipt );
137138
char* iperf_get_test_json_output_string ( struct iperf_test* ipt );
139+
int iperf_get_test_json_stream( struct iperf_test* ipt );
138140
int iperf_get_test_zerocopy( struct iperf_test* ipt );
139141
int iperf_get_test_get_server_output( struct iperf_test* ipt );
140142
char* iperf_get_test_bind_address ( struct iperf_test* ipt );
@@ -174,6 +176,7 @@ void iperf_set_test_server_hostname( struct iperf_test* ipt, const char* server_
174176
void iperf_set_test_template( struct iperf_test *ipt, const char *tmp_template );
175177
void iperf_set_test_reverse( struct iperf_test* ipt, int reverse );
176178
void iperf_set_test_json_output( struct iperf_test* ipt, int json_output );
179+
void iperf_set_test_json_stream( struct iperf_test* ipt, int json_stream );
177180
int iperf_has_zerocopy( void );
178181
void iperf_set_test_zerocopy( struct iperf_test* ipt, int zerocopy );
179182
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
@@ -111,6 +111,7 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
111111
#endif /* HAVE_SO_BINDTODEVICE */
112112
" -V, --verbose more detailed output\n"
113113
" -J, --json output in JSON format\n"
114+
" --json-stream output in line-delimited JSON format\n"
114115
" --logfile f send output to a log file\n"
115116
" --forceflush force flushing output at every interval\n"
116117
" --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)