/* wmem_test.c * Wireshark Memory Manager Tests * Copyright 2012, Evan Huus * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include #include #include "wmem.h" #include "wmem_tree-int.h" #include "wmem_allocator.h" #include "wmem_allocator_block.h" #include "wmem_allocator_block_fast.h" #include "wmem_allocator_simple.h" #include "wmem_allocator_strict.h" #include #define STRING_80 "12345678901234567890123456789012345678901234567890123456789012345678901234567890" #define MAX_ALLOC_SIZE (1024*64) #define MAX_SIMULTANEOUS_ALLOCS 1024 #define CONTAINER_ITERS 10000 typedef void (*wmem_verify_func)(wmem_allocator_t *allocator); /* A local copy of wmem_allocator_new that ignores the * WIRESHARK_DEBUG_WMEM_OVERRIDE variable so that test functions are * guaranteed to actually get the allocator type they asked for */ static wmem_allocator_t * wmem_allocator_force_new(const wmem_allocator_type_t type) { wmem_allocator_t *allocator; allocator = wmem_new(NULL, wmem_allocator_t); allocator->type = type; allocator->callbacks = NULL; allocator->in_scope = true; switch (type) { case WMEM_ALLOCATOR_SIMPLE: wmem_simple_allocator_init(allocator); break; case WMEM_ALLOCATOR_BLOCK: wmem_block_allocator_init(allocator); break; case WMEM_ALLOCATOR_BLOCK_FAST: wmem_block_fast_allocator_init(allocator); break; case WMEM_ALLOCATOR_STRICT: wmem_strict_allocator_init(allocator); break; default: g_assert_not_reached(); /* This is necessary to squelch MSVC errors; is there any way to tell it that g_assert_not_reached() never returns? */ return NULL; }; return allocator; } /* A helper for generating pseudo-random strings. Just uses glib's random number * functions to generate 'numbers' in the printable character range. */ static char * wmem_test_rand_string(wmem_allocator_t *allocator, int minlen, int maxlen) { char *str; int len, i; len = g_random_int_range(minlen, maxlen); /* +1 for null-terminator */ str = (char*)wmem_alloc(allocator, len + 1); str[len] = '\0'; for (i=0; i=0; i--) { /* no wmem_realloc0 so just use memset manually */ ptrs[i] = (char *)wmem_realloc(allocator, ptrs[i], 4*len); memset(ptrs[i], 0, 4*len); } for (i=0; i, orig_str); wmem_strict_check_canaries(allocator); orig_str = "TEST123456789"; new_str = wmem_strndup(allocator, orig_str, 6); g_assert_cmpstr(new_str, ==, "TEST12"); g_assert_cmpstr(new_str, <, orig_str); new_str[0] = 'X'; g_assert_cmpstr(new_str, >, orig_str); wmem_strict_check_canaries(allocator); new_str = wmem_strdup_printf(allocator, "abc %s %% %d", "boo", 23); g_assert_cmpstr(new_str, ==, "abc boo % 23"); new_str = wmem_strdup_printf(allocator, "%s", STRING_80); g_assert_cmpstr(new_str, ==, STRING_80); wmem_strict_check_canaries(allocator); orig_str = "Short String"; new_str = wmem_strdup_printf(allocator, "TEST %s", orig_str); g_assert_cmpstr(new_str, ==, "TEST Short String"); orig_str = "Very Long..............................." "........................................" "........................................" "........................................" "........................................" "........................................" "..................................String"; new_str = wmem_strdup_printf(allocator, "TEST %s", orig_str); g_assert_cmpstr(new_str, ==, "TEST Very Long..............................." "........................................" "........................................" "........................................" "........................................" "........................................" "..................................String"); wmem_destroy_allocator(allocator); } #define RESOURCE_USAGE_START get_resource_usage(&start_utime, &start_stime) #define RESOURCE_USAGE_END \ get_resource_usage(&end_utime, &end_stime); \ utime_ms = (end_utime - start_utime) * 1000.0; \ stime_ms = (end_stime - start_stime) * 1000.0 /* NOTE: You have to run "wmem_test -m perf" to run the performance tests. */ static void wmem_test_stringperf(void) { #define LOOP_COUNT (1 * 1000 * 1000) wmem_allocator_t *allocator; #ifdef _WIN32 char buffer[1]; #endif char **str_ptr = g_new(char *, LOOP_COUNT); char *s_val = "test string"; double d_val = 1000.2; unsigned u_val = 54321; int i_val = -12345; int i; double start_utime, start_stime, end_utime, end_stime, utime_ms, stime_ms; allocator = wmem_allocator_new(WMEM_ALLOCATOR_BLOCK); /* C99 snprintf */ RESOURCE_USAGE_START; for (i = 0; i < LOOP_COUNT; i++) { snprintf(NULL, 0, "%s", s_val); } RESOURCE_USAGE_END; g_test_minimized_result(utime_ms + stime_ms, "snprintf 1 string: u %.3f ms s %.3f ms", utime_ms, stime_ms); RESOURCE_USAGE_START; for (i = 0; i < LOOP_COUNT; i++) { snprintf(NULL, 0, "%s%s%s%s%s", s_val, s_val, s_val, s_val, s_val); } RESOURCE_USAGE_END; g_test_minimized_result(utime_ms + stime_ms, "snprintf 5 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms); RESOURCE_USAGE_START; for (i = 0; i < LOOP_COUNT; i++) { snprintf(NULL, 0, "%s%u%3.5f%02d", s_val, u_val, d_val, i_val); } RESOURCE_USAGE_END; g_test_minimized_result(utime_ms + stime_ms, "snprintf mixed args: u %.3f ms s %.3f ms", utime_ms, stime_ms); /* GLib g_snprintf (can use C99 or Gnulib) */ RESOURCE_USAGE_START; for (i = 0; i < LOOP_COUNT; i++) { g_snprintf(NULL, 0, "%s", s_val); } RESOURCE_USAGE_END; g_test_minimized_result(utime_ms + stime_ms, "g_printf_string_upper_bound (via g_snprintf) 1 string: u %.3f ms s %.3f ms", utime_ms, stime_ms); RESOURCE_USAGE_START; for (i = 0; i < LOOP_COUNT; i++) { g_snprintf(NULL, 0, "%s%s%s%s%s", s_val, s_val, s_val, s_val, s_val); } RESOURCE_USAGE_END; g_test_minimized_result(utime_ms + stime_ms, "g_printf_string_upper_bound (via g_snprintf) 5 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms); RESOURCE_USAGE_START; for (i = 0; i < LOOP_COUNT; i++) { g_snprintf(NULL, 0, "%s%u%3.5f%02d", s_val, u_val, d_val, i_val); } RESOURCE_USAGE_END; g_test_minimized_result(utime_ms + stime_ms, "g_printf_string_upper_bound (via g_snprintf) mixed args: u %.3f ms s %.3f ms", utime_ms, stime_ms); /* Windows _snprintf_s */ #ifdef _WIN32 RESOURCE_USAGE_START; for (i = 0; i < LOOP_COUNT; i++) { _snprintf_s(buffer, 1, _TRUNCATE, "%s", s_val); } RESOURCE_USAGE_END; g_test_minimized_result(utime_ms + stime_ms, "_snprintf_s upper bound 1 string: u %.3f ms s %.3f ms", utime_ms, stime_ms); RESOURCE_USAGE_START; for (i = 0; i < LOOP_COUNT; i++) { _snprintf_s(buffer, 1, _TRUNCATE, "%s%s%s%s%s", s_val, s_val, s_val, s_val, s_val); } RESOURCE_USAGE_END; g_test_minimized_result(utime_ms + stime_ms, "_snprintf_s upper bound 5 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms); RESOURCE_USAGE_START; for (i = 0; i < LOOP_COUNT; i++) { _snprintf_s(buffer, 1, _TRUNCATE, "%s%u%3.5f%02d", s_val, u_val, d_val, i_val); } RESOURCE_USAGE_END; g_test_minimized_result(utime_ms + stime_ms, "_snprintf_s upper bound mixed args: u %.3f ms s %.3f ms", utime_ms, stime_ms); #endif /* GLib strdup */ RESOURCE_USAGE_START; for (i = 0; i < LOOP_COUNT; i++) { str_ptr[i] = g_strdup_printf("%s%s", s_val, s_val); } RESOURCE_USAGE_END; g_test_minimized_result(utime_ms + stime_ms, "g_strdup_printf 2 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms); for (i = 0; i < LOOP_COUNT; i++) { g_free(str_ptr[i]); } RESOURCE_USAGE_START; for (i = 0; i < LOOP_COUNT; i++) { str_ptr[i] = g_strdup_printf("%s%s%s%s%s", s_val, s_val, s_val, s_val, s_val); } RESOURCE_USAGE_END; g_test_minimized_result(utime_ms + stime_ms, "g_strdup_printf 5 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms); for (i = 0; i < LOOP_COUNT; i++) { g_free(str_ptr[i]); } /* wmem strdup null allocator */ RESOURCE_USAGE_START; for (i = 0; i < LOOP_COUNT; i++) { str_ptr[i] = wmem_strdup_printf(NULL, "%s%s", s_val, s_val); } RESOURCE_USAGE_END; g_test_minimized_result(utime_ms + stime_ms, "wmem_strdup_printf() 2 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms); for (i = 0; i < LOOP_COUNT; i++) { g_free(str_ptr[i]); } RESOURCE_USAGE_START; for (i = 0; i < LOOP_COUNT; i++) { str_ptr[i] = wmem_strdup_printf(NULL, "%s%s%s%s%s", s_val, s_val, s_val, s_val, s_val); } RESOURCE_USAGE_END; g_test_minimized_result(utime_ms + stime_ms, "wmem_strdup_printf(NULL) 5 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms); for (i = 0; i < LOOP_COUNT; i++) { g_free(str_ptr[i]); } /* wmem strdup strict allocator */ RESOURCE_USAGE_START; for (i = 0; i < LOOP_COUNT; i++) { wmem_strdup_printf(allocator, "%s%s", s_val, s_val); } RESOURCE_USAGE_END; g_test_minimized_result(utime_ms + stime_ms, "wmem_strdup_printf(allocator) 2 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms); RESOURCE_USAGE_START; for (i = 0; i < LOOP_COUNT; i++) { wmem_strdup_printf(allocator, "%s%s%s%s%s", s_val, s_val, s_val, s_val, s_val); } RESOURCE_USAGE_END; g_test_minimized_result(utime_ms + stime_ms, "wmem_strdup_printf(allocator) 5 strings: u %.3f ms s %.3f ms", utime_ms, stime_ms); wmem_destroy_allocator(allocator); g_free(str_ptr); } /* DATA STRUCTURE TESTING FUNCTIONS (/wmem/datastruct/) */ static void wmem_test_array(void) { wmem_allocator_t *allocator; wmem_array_t *array; unsigned int i, j, k; uint32_t val, *buf; uint32_t vals[8]; uint32_t *raw; uint32_t lastint; allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT); array = wmem_array_new(allocator, sizeof(uint32_t)); g_assert_true(array); g_assert_true(wmem_array_get_count(array) == 0); for (i=0; i 1) { wmem_list_remove(list, GINT_TO_POINTER(i)); i--; } wmem_list_remove(list, GINT_TO_POINTER(CONTAINER_ITERS - 1)); g_assert_true(wmem_list_count(list) == 0); g_assert_true(wmem_list_head(list) == NULL); g_assert_true(wmem_list_tail(list) == NULL); for (i=0; i0; i--) { g_assert_true(wmem_stack_peek(stack) == GINT_TO_POINTER(i-1)); g_assert_true(wmem_stack_pop(stack) == GINT_TO_POINTER(i-1)); g_assert_true(wmem_stack_count(stack) == i-1); } g_assert_true(wmem_stack_count(stack) == 0); wmem_destroy_stack(stack); wmem_destroy_allocator(allocator); } static void wmem_test_strbuf(void) { wmem_allocator_t *allocator; wmem_strbuf_t *strbuf; int i; allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT); strbuf = wmem_strbuf_new(allocator, "TEST"); g_assert_true(strbuf); g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TEST"); g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 4); wmem_strbuf_append(strbuf, "FUZZ"); g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TESTFUZZ"); g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 8); wmem_strbuf_append_printf(strbuf, "%d%s", 3, "a"); g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TESTFUZZ3a"); g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 10); wmem_strbuf_append_c(strbuf, 'q'); g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TESTFUZZ3aq"); g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 11); wmem_strbuf_append_unichar(strbuf, g_utf8_get_char("\xC2\xA9")); g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TESTFUZZ3aq\xC2\xA9"); g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 13); wmem_strbuf_append_c_count(strbuf, '+', 8); g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TESTFUZZ3aq\xC2\xA9++++++++"); g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 21); wmem_strbuf_truncate(strbuf, 32); wmem_strbuf_truncate(strbuf, 24); wmem_strbuf_truncate(strbuf, 16); wmem_strbuf_truncate(strbuf, 13); g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TESTFUZZ3aq\xC2\xA9"); g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 13); wmem_strbuf_truncate(strbuf, 3); g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TES"); g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 3); wmem_strbuf_append_len(strbuf, "TFUZZ1234", 5); g_assert_cmpstr(wmem_strbuf_get_str(strbuf), ==, "TESTFUZZ"); g_assert_cmpuint(wmem_strbuf_get_len(strbuf), ==, 8); wmem_free_all(allocator); strbuf = wmem_strbuf_new(allocator, "TEST"); for (i=0; i<1024; i++) { if (g_test_rand_bit()) { wmem_strbuf_append(strbuf, "ABC"); } else { wmem_strbuf_append_printf(strbuf, "%d%d", 3, 777); } wmem_strict_check_canaries(allocator); } g_assert_true(strlen(wmem_strbuf_get_str(strbuf)) == wmem_strbuf_get_len(strbuf)); wmem_destroy_allocator(allocator); } static void wmem_test_strbuf_validate(void) { wmem_strbuf_t *strbuf; const char *endptr; strbuf = wmem_strbuf_new(NULL, "TEST\xEF ABC"); g_assert_false(wmem_strbuf_utf8_validate(strbuf, &endptr)); g_assert_true(endptr == &strbuf->str[4]); wmem_strbuf_destroy(strbuf); strbuf = wmem_strbuf_new(NULL, NULL); wmem_strbuf_append_len(strbuf, "TEST\x00\x00 ABC", 10); g_assert_true(wmem_strbuf_utf8_validate(strbuf, &endptr)); wmem_strbuf_destroy(strbuf); strbuf = wmem_strbuf_new(NULL, NULL); wmem_strbuf_append_len(strbuf, "TEST\x00\xEF ABC", 10); g_assert_false(wmem_strbuf_utf8_validate(strbuf, &endptr)); g_assert_true(endptr == &strbuf->str[5]); wmem_strbuf_destroy(strbuf); strbuf = wmem_strbuf_new(NULL, NULL); wmem_strbuf_append_len(strbuf, "TEST\x00 ABC \x00 DEF \x00", 17); g_assert_true(wmem_strbuf_utf8_validate(strbuf, &endptr)); wmem_strbuf_destroy(strbuf); } static void wmem_test_tree(void) { wmem_allocator_t *allocator, *extra_allocator; wmem_tree_t *tree; uint32_t i; int seen_values = 0; int j; char *str_key; #define WMEM_TREE_MAX_KEY_COUNT 8 #define WMEM_TREE_MAX_KEY_LEN 4 int key_count; wmem_tree_key_t keys[WMEM_TREE_MAX_KEY_COUNT]; allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT); extra_allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT); tree = wmem_tree_new(allocator); g_assert_true(tree); g_assert_true(wmem_tree_is_empty(tree)); /* test basic 32-bit key operations */ for (i=0; i 0) { g_assert_true(wmem_tree_lookup32_le(tree, i) == GINT_TO_POINTER(i-1)); } wmem_tree_insert32(tree, i, GINT_TO_POINTER(i)); g_assert_true(wmem_tree_lookup32(tree, i) == GINT_TO_POINTER(i)); g_assert_true(!wmem_tree_is_empty(tree)); } g_assert_true(wmem_tree_count(tree) == CONTAINER_ITERS); wmem_free_all(allocator); tree = wmem_tree_new(allocator); for (i=0; irange)) { d->counter++; } return false; } static bool wmem_test_overlap(uint64_t low, uint64_t high, uint64_t lowbis, uint64_t highbis) { wmem_range_t r1 = {low, high, 0}; wmem_range_t r2 = {lowbis, highbis, 0}; return wmem_itree_range_overlap(&r1, &r2); } static void wmem_test_itree(void) { wmem_allocator_t *allocator, *extra_allocator; wmem_itree_t *tree; int i = 0; int32_t max_rand = 0; wmem_test_itree_user_data_t userData; wmem_range_t range, r2; allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT); extra_allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT); tree = wmem_itree_new(allocator); g_assert_true(tree); g_assert_true(wmem_itree_is_empty(tree)); wmem_free_all(allocator); /* make sure that wmem_test_overlap is correct (well it's no proof but...)*/ g_assert_true(wmem_test_overlap(0, 10, 0, 4)); g_assert_true(wmem_test_overlap(0, 10, 9, 14)); g_assert_true(wmem_test_overlap(5, 10, 3, 8)); g_assert_true(wmem_test_overlap(5, 10, 1, 12)); g_assert_true(!wmem_test_overlap(0, 10, 11, 12)); /* Generate a reference range, then fill an itree with random ranges, then we count greedily the number of overlapping ranges and compare the result with the optimized result */ userData.counter = 0; tree = wmem_itree_new(allocator); /* even though keys are uint64_t, we use INT32_MAX as a max because of the type returned by g_test_rand_int_range. */ max_rand = INT32_MAX; r2.max_edge = range.max_edge = 0; range.low = g_test_rand_int_range(0, max_rand); range.high = g_test_rand_int_range( (int32_t)range.low, (int32_t)max_rand); userData.range = range; for (i=0; i