diff --git a/picoquic_t/picoquic_t.c b/picoquic_t/picoquic_t.c index b5ccb56e74defea92131387b065a68a2f6dbd346..f02860bfee9982dc4b23cbd0c849dd29849bd11c 100644 --- a/picoquic_t/picoquic_t.c +++ b/picoquic_t/picoquic_t.c @@ -143,7 +143,7 @@ static int do_one_test(size_t i, FILE* F) int usage(char const * argv0) { - fprintf(stderr, "PicoQUIC test exexution\n"); + fprintf(stderr, "PicoQUIC test execution\n"); fprintf(stderr, "Usage: picoquic_ct [-x <excluded>] [<list of tests]\n"); fprintf(stderr, "\nUsage: %s [test1 [test2 ..[testN]]]\n\n", argv0); fprintf(stderr, " Or: %s [-x test]*", argv0); @@ -158,6 +158,7 @@ int usage(char const * argv0) } fprintf(stderr, "Options: \n"); fprintf(stderr, " -x test Do not run the specified test.\n"); + fprintf(stderr, " -s nnn Run stress for nnn minutes.\n"); fprintf(stderr, " -h Print this help message\n"); return -1; @@ -181,6 +182,8 @@ int main(int argc, char** argv) int ret = 0; int nb_test_tried = 0; int nb_test_failed = 0; + int stress_minutes = 0; + int found_exclusion = 0; int * is_excluded = malloc(sizeof(int)*nb_tests); int opt; @@ -193,7 +196,7 @@ int main(int argc, char** argv) { memset(is_excluded, 0, sizeof(int)*nb_tests); - while (ret == 0 && (opt = getopt(argc, argv, "x:h")) != -1) { + while (ret == 0 && (opt = getopt(argc, argv, "s:x:h")) != -1) { switch (opt) { case 'x': { int test_number = get_test_number(optarg); @@ -204,9 +207,17 @@ int main(int argc, char** argv) } else { is_excluded[test_number] = 1; + found_exclusion = 1; } break; } + case 's': + stress_minutes = atoi(optarg); + if (stress_minutes <= 0) { + fprintf(stderr, "Incorrect stress minutes: %s\n", optarg); + ret = usage(argv[0]); + } + break; case 'h': usage(argv[0]); exit(0); @@ -217,6 +228,18 @@ int main(int argc, char** argv) } } + if (ret == 0 && stress_minutes > 0) { + if (optind >= argc && found_exclusion == 0) { + for (size_t i = 0; i < nb_tests; i++) { + if (strcmp(test_table[i].test_name, "stress") != 0) { + is_excluded[i] = 1; + } + } + picoquic_stress_test_duration = stress_minutes; + picoquic_stress_test_duration *= 60000000; + } + } + if (ret == 0) { if (optind >= argc) { @@ -228,7 +251,7 @@ int main(int argc, char** argv) ret = -1; } } - else { + else if (stress_minutes == 0) { fprintf(stderr, "test number %d (%s) is bypassed.\n", (int)i, test_table[i].test_name); } } diff --git a/picoquictest/picoquictest.h b/picoquictest/picoquictest.h index 9efc404b6fec5707268ca9e23b9b9c2babc900e0..e75d459dba2fa2ca5a35d1353aba604d08cdc6c5 100644 --- a/picoquictest/picoquictest.h +++ b/picoquictest/picoquictest.h @@ -26,6 +26,11 @@ extern "C" { #endif +/* Control variables for the duration of the stress test */ + +extern uint64_t picoquic_stress_test_duration; /* In microseconds; defaults to 2 minutes */ + +/* List of test functions */ int picohash_test(); int cnxcreation_test(); int parseheadertest(); diff --git a/picoquictest/stresstest.c b/picoquictest/stresstest.c index 63959f018de7ff1efd32d0605409aa2c3dfefd4c..70e30da109c8367c7711b84ef093445cffb586de 100644 --- a/picoquictest/stresstest.c +++ b/picoquictest/stresstest.c @@ -48,6 +48,8 @@ uint64_t picoquic_stress_test_duration = 120000000; /* Default to 2 minutes */ size_t picoquic_stress_nb_clients = 4; /* Default to 4 clients */ uint64_t picoquic_stress_max_bidir = 8 * 4; /* Default to 8 streams max per connection */ size_t picoquic_stress_max_open_streams = 4; /* Default to 4 simultaneous streams max per connection */ +uint64_t stress_random_ctx = 0xBabaC001BaddBab1ull; +uint32_t picoquic_stress_max_message_before_drop = 25; typedef struct st_picoquic_stress_server_callback_ctx_t { // picoquic_first_server_stream_ctx_t* first_stream; @@ -63,8 +65,9 @@ typedef struct st_picoquic_stress_client_callback_ctx_t { size_t max_open_streams; size_t nb_open_streams; uint64_t stream_id[PICOQUIC_STRESS_MAX_CLIENT_STREAMS]; - uint32_t nb_client_streams; uint64_t last_interaction_time; + uint32_t nb_client_streams; + uint32_t message_disconnect_trigger; int progress_observed; } picoquic_stress_client_callback_ctx_t; @@ -387,6 +390,13 @@ int stress_client_set_callback(picoquic_cnx_t* cnx) } picoquic_set_callback(cnx, stress_client_callback, ctx); + if ((ctx->message_disconnect_trigger = (uint32_t) picoquic_test_uniform_random(&stress_random_ctx, 2* picoquic_stress_max_message_before_drop)) >= picoquic_stress_max_message_before_drop){ + ctx->message_disconnect_trigger = 0; + } + else { + ctx->message_disconnect_trigger++; + } + stress_client_start_streams(cnx, ctx); } } @@ -512,10 +522,27 @@ static int stress_handle_packet_prepare(picoquic_stress_ctx_t * ctx, picoquic_qu picoquic_packet* p = picoquic_create_packet(); picoquic_cnx_t* cnx = q->cnx_wake_first; picoquictest_sim_link_t* target_link = NULL; + int simulate_disconnect = 0; + int was_null = 0; if (packet != NULL && p != NULL && cnx != NULL) { - ret = picoquic_prepare_packet(cnx, p, ctx->simulated_time, - packet->bytes, PICOQUIC_MAX_PACKET_SIZE, &packet->length); + /* Check that the client connection was properly terminated */ + picoquic_stress_client_callback_ctx_t* c_ctx = (c_index >= 0) ? + (picoquic_stress_client_callback_ctx_t*)picoquic_get_callback_context(cnx) : NULL; + + was_null = (c_ctx != 0); + + if (c_ctx == NULL || cnx->cnx_state == picoquic_state_disconnected || c_ctx->message_disconnect_trigger == 0 || + cnx->send_sequence <= c_ctx->message_disconnect_trigger) { + ret = picoquic_prepare_packet(cnx, p, ctx->simulated_time, + packet->bytes, PICOQUIC_MAX_PACKET_SIZE, &packet->length); + } + else if (c_ctx != NULL) { + /* simulate an abrupt */ + ret = PICOQUIC_ERROR_DISCONNECTED; + simulate_disconnect = 1; + } + if (ret == 0 && p->length > 0) { memcpy(&packet->addr_from, &cnx->path[0]->dest_addr, sizeof(struct sockaddr_in)); memcpy(&packet->addr_to, &cnx->path[0]->peer_addr, sizeof(struct sockaddr_in)); @@ -547,14 +574,16 @@ static int stress_handle_packet_prepare(picoquic_stress_ctx_t * ctx, picoquic_qu packet = NULL; if (ret == PICOQUIC_ERROR_DISCONNECTED) { + /* Check the context again, it may have been freed in a callback */ + c_ctx = (c_index >= 0) ? + (picoquic_stress_client_callback_ctx_t*)picoquic_get_callback_context(cnx) : NULL; + if (c_index >= 0) { - /* Check that the client connection was properly terminated */ - picoquic_stress_client_callback_ctx_t* c_ctx = - (picoquic_stress_client_callback_ctx_t*)picoquic_get_callback_context(cnx); ret = 0; if (c_ctx != NULL) { - if (c_ctx->next_bidir <= c_ctx->max_bidir || - c_ctx->nb_open_streams != 0) { + if (simulate_disconnect == 0 && ( + c_ctx->next_bidir <= c_ctx->max_bidir || + c_ctx->nb_open_streams != 0)) { stress_debug_break(); ret = -1; } @@ -819,6 +848,10 @@ int stress_test() { int ret = 0; picoquic_stress_ctx_t stress_ctx; + int run_time_seconds = 0; + int wall_time_seconds = 0; + uint64_t wall_time_start = picoquic_current_time(); + /* Initialization */ memset(&stress_ctx, 0, sizeof(picoquic_stress_ctx_t)); @@ -885,5 +918,11 @@ int stress_test() stress_ctx.qserver = NULL; } + /* Report */ + run_time_seconds = (int)(stress_ctx.simulated_time / 1000000ull); + wall_time_seconds = (int)((picoquic_current_time() - wall_time_start) / 1000000ull); + DBG_PRINTF("Stress complete after simulating %d s. in %d s., returns %d\n", + run_time_seconds, wall_time_seconds, ret); + return ret; }