/* * Copyright (C) 2013-2020 Canonical, Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * This code is a complete clean re-write of the stress tool by * Colin Ian King and attempts to be * backwardly compatible with the stress tool by Amos Waterland * but has more stress tests and more * functionality. * */ #include "stress-ng.h" /* * the STR stress test has different classes of string stressors */ typedef void (*stress_str_func)( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed); typedef struct { const char *name; /* human readable form of stressor */ const stress_str_func func; /* the stressor function */ const void *libc_func; } stress_str_method_info_t; static const stress_help_t help[] = { { NULL, "str N", "start N workers exercising lib C string functions" }, { NULL, "str-method func", "specify the string function to stress" }, { NULL, "str-ops N", "stop after N bogo string operations" }, { NULL, NULL, NULL } }; static const stress_str_method_info_t str_methods[]; static inline void strchk( const char *name, const int ok, const char *msg, bool *failed) { if ((g_opt_flags & OPT_FLAGS_VERIFY) && (!ok)) { pr_fail("%s: %s did not return expected result\n", name, msg); *failed = true; } } #define STR(x) # x #define STRCHK(name, test, failed) \ strchk(name, test, STR(test), failed) #if defined(HAVE_STRINGS_H) /* * stress_strcasecmp() * stress on strcasecmp */ static void stress_strcasecmp( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { register size_t i; int (*test_strcasecmp)(const char *s1, const char *s2) = libc_func; (void)len2; for (i = 1; keep_stressing_flag() && (i < len1); i++) { STRCHK(name, 0 == test_strcasecmp(str1, str1), failed); STRCHK(name, 0 == test_strcasecmp(str2, str2), failed); STRCHK(name, 0 != test_strcasecmp(str2, str1), failed); STRCHK(name, 0 != test_strcasecmp(str1, str2), failed); STRCHK(name, 0 != test_strcasecmp(str1 + i, str1), failed); STRCHK(name, 0 != test_strcasecmp(str1, str1 + i), failed); STRCHK(name, 0 == test_strcasecmp(str1 + i, str1 + i), failed); STRCHK(name, 0 != test_strcasecmp(str1 + i, str2), failed); STRCHK(name, 0 != test_strcasecmp(str2, str1 + i), failed); } } #endif #if defined(HAVE_STRINGS_H) /* * stress_strncasecmp() * stress on strncasecmp */ static void stress_strncasecmp( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { register size_t i; int (*test_strncasecmp)(const char *s1, const char *s2, size_t n) = libc_func; (void)len2; for (i = 1; keep_stressing_flag() && (i < len1); i++) { STRCHK(name, 0 == test_strncasecmp(str1, str1, len1), failed); STRCHK(name, 0 == test_strncasecmp(str2, str2, len2), failed); STRCHK(name, 0 != test_strncasecmp(str2, str1, len2), failed); STRCHK(name, 0 != test_strncasecmp(str1, str2, len1), failed); STRCHK(name, 0 != test_strncasecmp(str1 + i, str1, len1), failed); STRCHK(name, 0 != test_strncasecmp(str1, str1 + i, len1), failed); STRCHK(name, 0 == test_strncasecmp(str1 + i, str1 + i, len1), failed); STRCHK(name, 0 != test_strncasecmp(str1 + i, str2, len1), failed); STRCHK(name, 0 != test_strncasecmp(str2, str1 + i, len2), failed); } } #endif #if defined(HAVE_STRINGS_H) /* * stress_index() * stress on index */ static void stress_index( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { register size_t i; char * (*test_index)(const char *s, int c) = libc_func; (void)len2; for (i = 0; keep_stressing_flag() && (i < len1 - 1); i++) { STRCHK(name, NULL == test_index(str1, '_'), failed); STRCHK(name, NULL != test_index(str1, str1[0]), failed); STRCHK(name, NULL == test_index(str2, '_'), failed); STRCHK(name, NULL != test_index(str2, str2[0]), failed); } } #endif #if defined(HAVE_STRINGS_H) /* * stress_rindex() * stress on rindex */ static void stress_rindex( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { register size_t i; char * (*test_rindex)(const char *s, int c) = libc_func; (void)len2; for (i = 0; keep_stressing_flag() && (i < len1 - 1); i++) { STRCHK(name, NULL == test_rindex(str1, '_'), failed); STRCHK(name, NULL != test_rindex(str1, str1[0]), failed); STRCHK(name, NULL == test_rindex(str2, '_'), failed); STRCHK(name, NULL != test_rindex(str2, str2[0]), failed); } } #endif #if defined(HAVE_STRLCPY) /* * stress_strlcpy() * stress on strlcpy */ static void stress_strlcpy( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { register size_t i; size_t (*test_strlcpy)(char *dest, const char *src, size_t len) = libc_func; char buf[len1 + len2 + 1]; const size_t buf_len = sizeof(buf); const size_t str_len1 = strlen(str1); const size_t str_len2 = strlen(str2); for (i = 0; keep_stressing_flag() && (i < len1 - 1); i++) { STRCHK(name, str_len1 == test_strlcpy(buf, str1, buf_len), failed); STRCHK(name, str_len2 == test_strlcpy(buf, str2, buf_len), failed); } } #else /* * stress_strcpy() * stress on strcpy */ static void stress_strcpy( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { register size_t i; char * (*test_strcpy)(char *dest, const char *src) = libc_func; char buf[len1 + len2 + 1]; for (i = 0; keep_stressing_flag() && (i < len1 - 1); i++) { STRCHK(name, buf == test_strcpy(buf, str1), failed); STRCHK(name, buf == test_strcpy(buf, str2), failed); } } #endif #if defined(HAVE_STRLCAT) /* * stress_strlcat() * stress on strlcat */ static void stress_strlcat( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { register size_t i; size_t (*test_strlcat)(char *dest, const char *src, size_t len) = libc_func; char buf[len1 + len2 + 1]; const size_t buf_len = sizeof(buf); const size_t str_len1 = strlen(str1); const size_t str_len2 = strlen(str2); const size_t str_len = str_len1 + str_len2; for (i = 0; keep_stressing_flag() && (i < len1 - 1); i++) { *buf = '\0'; STRCHK(name, str_len1 == test_strlcat(buf, str1, buf_len), failed); *buf = '\0'; STRCHK(name, str_len2 == test_strlcat(buf, str2, buf_len), failed); *buf = '\0'; STRCHK(name, str_len1 == test_strlcat(buf, str1, buf_len), failed); STRCHK(name, str_len == test_strlcat(buf, str2, buf_len), failed); *buf = '\0'; STRCHK(name, str_len2 == test_strlcat(buf, str2, buf_len), failed); STRCHK(name, str_len == test_strlcat(buf, str1, buf_len), failed); } } #else /* * stress_strcat() * stress on strcat */ static void stress_strcat( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { register size_t i; char * (*test_strcat)(char *dest, const char *src) = libc_func; char buf[len1 + len2 + 1]; for (i = 0; keep_stressing_flag() && (i < len1 - 1); i++) { *buf = '\0'; STRCHK(name, buf == test_strcat(buf, str1), failed); *buf = '\0'; STRCHK(name, buf == test_strcat(buf, str2), failed); *buf = '\0'; STRCHK(name, buf == test_strcat(buf, str1), failed); STRCHK(name, buf == test_strcat(buf, str2), failed); *buf = '\0'; STRCHK(name, buf == test_strcat(buf, str2), failed); STRCHK(name, buf == test_strcat(buf, str1), failed); } } #endif /* * stress_strncat() * stress on strncat */ static void stress_strncat( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { register size_t i; char * (*test_strncat)(char *dest, const char *src, size_t n) = libc_func; char buf[len1 + len2 + 1]; for (i = 0; keep_stressing_flag() && (i < len1 - 1); i++) { *buf = '\0'; STRCHK(name, buf == test_strncat(buf, str1, len1), failed); *buf = '\0'; STRCHK(name, buf == test_strncat(buf, str2, len2), failed); *buf = '\0'; STRCHK(name, buf == test_strncat(buf, str1, len1), failed); STRCHK(name, buf == test_strncat(buf, str2, len1 + len2), failed); *buf = '\0'; STRCHK(name, buf == test_strncat(buf, str2, i), failed); STRCHK(name, buf == test_strncat(buf, str1, i), failed); } } /* * stress_strchr() * stress on strchr */ static void stress_strchr( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { register size_t i; char * (*test_strchr)(const char *s, int c) = libc_func; (void)len2; for (i = 0; keep_stressing_flag() && (i < len1 - 1); i++) { STRCHK(name, NULL == test_strchr(str1, '_'), failed); STRCHK(name, NULL != test_strchr(str1, str1[0]), failed); STRCHK(name, NULL == test_strchr(str2, '_'), failed); STRCHK(name, NULL != test_strchr(str2, str2[0]), failed); } } /* * stress_strrchr() * stress on strrchr */ static void stress_strrchr( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { register size_t i; char * (*test_strrchr)(const char *s, int c) = libc_func; (void)len2; for (i = 0; keep_stressing_flag() && (i < len1 - 1); i++) { STRCHK(name, NULL == test_strrchr(str1, '_'), failed); STRCHK(name, NULL != test_strrchr(str1, str1[0]), failed); STRCHK(name, NULL == test_strrchr(str2, '_'), failed); STRCHK(name, NULL != test_strrchr(str2, str2[0]), failed); } } /* * stress_strcmp() * stress on strcmp */ static void stress_strcmp( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { register size_t i; int (*test_strcmp)(const char *s1, const char *s2) = libc_func; (void)len2; for (i = 1; keep_stressing_flag() && (i < len1); i++) { STRCHK(name, 0 == test_strcmp(str1, str1), failed); STRCHK(name, 0 == test_strcmp(str2, str2), failed); STRCHK(name, 0 != test_strcmp(str2, str1), failed); STRCHK(name, 0 != test_strcmp(str1, str2), failed); STRCHK(name, 0 != test_strcmp(str1 + i, str1), failed); STRCHK(name, 0 != test_strcmp(str1, str1 + i), failed); STRCHK(name, 0 == test_strcmp(str1 + i, str1 + i), failed); STRCHK(name, 0 != test_strcmp(str1 + i, str2), failed); STRCHK(name, 0 != test_strcmp(str2, str1 + i), failed); } } /* * stress_strncmp() * stress on strncmp */ static void stress_strncmp( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { register size_t i; int (*test_strncmp)(const char *s1, const char *s2, size_t n) = libc_func; for (i = 1; keep_stressing_flag() && (i < len1); i++) { STRCHK(name, 0 == test_strncmp(str1, str1, len1), failed); STRCHK(name, 0 == test_strncmp(str2, str2, len2), failed); STRCHK(name, 0 != test_strncmp(str2, str1, len2), failed); STRCHK(name, 0 != test_strncmp(str1, str2, len1), failed); STRCHK(name, 0 != test_strncmp(str1 + i, str1, len1), failed); STRCHK(name, 0 != test_strncmp(str1, str1 + i, len1), failed); STRCHK(name, 0 == test_strncmp(str1 + i, str1 + i, len1), failed); STRCHK(name, 0 != test_strncmp(str1 + i, str2, len2), failed); STRCHK(name, 0 != test_strncmp(str2, str1 + i, len2), failed); } } /* * stress_strcoll() * stress on strcoll */ static void stress_strcoll( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { register size_t i; int (*test_strcoll)(const char *s1, const char *s2) = libc_func; (void)len2; for (i = 1; keep_stressing_flag() && (i < len1); i++) { STRCHK(name, 0 == test_strcoll(str1, str1), failed); STRCHK(name, 0 == test_strcoll(str2, str2), failed); STRCHK(name, 0 != test_strcoll(str2, str1), failed); STRCHK(name, 0 != test_strcoll(str1, str2), failed); STRCHK(name, 0 != test_strcoll(str1 + i, str1), failed); STRCHK(name, 0 != test_strcoll(str1, str1 + i), failed); STRCHK(name, 0 == test_strcoll(str1 + i, str1 + i), failed); STRCHK(name, 0 != test_strcoll(str1 + i, str2), failed); STRCHK(name, 0 != test_strcoll(str2, str1 + i), failed); } } /* * stress_strlen() * stress on strlen */ static void stress_strlen( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { register size_t i; size_t (*test_strlen)(const char *s) = libc_func; for (i = 0; keep_stressing_flag() && (i < len1 - 1); i++) { STRCHK(name, len1 - 1 == test_strlen(str1), failed); STRCHK(name, len1 - 1 - i == test_strlen(str1 + i), failed); } for (i = 0; keep_stressing_flag() && (i < len2 - 1); i++) { STRCHK(name, len2 - 1 == test_strlen(str2), failed); STRCHK(name, len2 - 1 - i == test_strlen(str2 + i), failed); } } /* * stress_strxfrm() * stress on strxfrm */ static void stress_strxfrm( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { register size_t i; char buf[len1 + len2]; size_t (*test_strxfrm)(char *dest, const char *src, size_t n) = libc_func; for (i = 0; keep_stressing_flag() && (i < len1 - 1); i++) { *buf = '\0'; STRCHK(name, 0 != test_strxfrm(buf, str1, sizeof(buf)), failed); *buf = '\0'; STRCHK(name, 0 != test_strxfrm(buf, str2, sizeof(buf)), failed); *buf = '\0'; STRCHK(name, 0 != test_strxfrm(buf, str1, sizeof(buf)), failed); STRCHK(name, 0 != test_strxfrm(buf, str2, sizeof(buf)), failed); *buf = '\0'; STRCHK(name, 0 != test_strxfrm(buf, str2, sizeof(buf)), failed); STRCHK(name, 0 != test_strxfrm(buf, str1, sizeof(buf)), failed); } } /* * stress_str_all() * iterate over all string stressors */ static void stress_str_all( const void *libc_func, const char *name, char *str1, const size_t len1, char *str2, const size_t len2, bool *failed) { static int i = 1; /* Skip over stress_str_all */ (void)libc_func; str_methods[i].func(str_methods[i].libc_func, name, str1, len1, str2, len2, failed); i++; if (!str_methods[i].func) i = 1; } /* * Table of string stress methods */ static const stress_str_method_info_t str_methods[] = { { "all", stress_str_all, NULL }, /* Special "all test */ #if defined(HAVE_STRINGS_H) { "index", stress_index, index }, { "rindex", stress_rindex, rindex }, { "strcasecmp", stress_strcasecmp, strcasecmp }, #endif #if defined(HAVE_STRLCAT) { "strlcat", stress_strlcat, strlcat }, #else { "strcat", stress_strcat, strcat }, #endif { "strchr", stress_strchr, strchr }, { "strcoll", stress_strcoll, strcoll }, { "strcmp", stress_strcmp, strcmp }, #if defined(HAVE_STRLCPY) { "strlcpy", stress_strlcpy, strlcpy }, #else { "strcpy", stress_strcpy, strcpy }, #endif { "strlen", stress_strlen, strlen }, #if defined(HAVE_STRINGS_H) { "strncasecmp", stress_strncasecmp, strncasecmp }, #endif { "strncat", stress_strncat, strncat }, { "strncmp", stress_strncmp, strncmp }, { "strrchr", stress_strrchr, strrchr }, { "strxfrm", stress_strxfrm, strxfrm }, { NULL, NULL, NULL } }; /* * stress_set_str_method() * set the default string stress method */ static int stress_set_str_method(const char *name) { stress_str_method_info_t const *info; for (info = str_methods; keep_stressing_flag() && info->func; info++) { if (!strcmp(info->name, name)) { stress_set_setting("str-method", TYPE_ID_UINTPTR_T, &info); return 0; } } (void)fprintf(stderr, "str-method must be one of:"); for (info = str_methods; info->func; info++) { (void)fprintf(stderr, " %s", info->name); } (void)fprintf(stderr, "\n"); return -1; } /* * stress_str() * stress CPU by doing various string operations */ static int stress_str(const stress_args_t *args) { const stress_str_method_info_t *str_method = &str_methods[0]; stress_str_func func; const void *libc_func; bool failed = false; char ALIGN64 str1[256], ALIGN64 str2[128]; register char *ptr1, *ptr2; register size_t len1, len2; const char *name = args->name; (void)stress_get_setting("str-method", &str_method); func = str_method->func; libc_func = str_method->libc_func; ptr1 = str1; len1 = sizeof(str1); ptr2 = str2; len2 = sizeof(str2); stress_strnrnd(ptr1, len1); do { register char *tmpptr; register size_t tmplen; stress_strnrnd(ptr2, len2); (void)func(libc_func, name, ptr1, len1, ptr2, len2, &failed); tmpptr = ptr1; ptr1 = ptr2; ptr2 = tmpptr; tmplen = len1; len1 = len2; len2 = tmplen; inc_counter(args); } while (keep_stressing()); return failed ? EXIT_FAILURE : EXIT_SUCCESS; } static void stress_str_set_default(void) { stress_set_str_method("all"); } static const stress_opt_set_func_t opt_set_funcs[] = { { OPT_str_method, stress_set_str_method }, { 0, NULL } }; stressor_info_t stress_str_info = { .stressor = stress_str, .set_default = stress_str_set_default, .class = CLASS_CPU | CLASS_CPU_CACHE | CLASS_MEMORY, .opt_set_funcs = opt_set_funcs, .help = help };