/**************************************************************************** * apps/testing/memstress/memorystress_main.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define MEMSTRESS_PREFIX "MemoryStress:" #define DEBUG_MAGIC 0xaa #define OPTARG_TO_VALUE(value, type) \ do \ { \ FAR char *ptr; \ value = (type)strtoul(optarg, &ptr, 10); \ if (*ptr != '\0') \ { \ printf(MEMSTRESS_PREFIX "Parameter error -%c %s\n", ch, optarg); \ show_usage(argv[0]); \ } \ } while (0) /**************************************************************************** * Private Types ****************************************************************************/ enum memorystress_rwerror_e { MEMORY_STRESS_READ_ERROR, MEMORY_STRESS_WRITE_ERROR }; struct memorystress_func_s { void *(*malloc)(size_t size); void *(*aligned_alloc)(size_t align, size_t nbytes); void *(*realloc)(FAR void *ptr, size_t new_size); void (*freefunc)(FAR void *ptr); }; struct memorystress_config_s { FAR struct memorystress_func_s *func; size_t max_allocsize; size_t nodelen; }; struct memorystress_error_s { FAR uint8_t *buf; uintptr_t node; size_t size; size_t offset; size_t cnt; size_t index; uint8_t readvalue; uint8_t writevalue; enum memorystress_rwerror_e rwerror; }; struct memorystress_node_s { FAR uint8_t *buf; size_t size; }; struct memorystress_context_s { FAR struct memorystress_node_s *node_array; FAR struct memorystress_config_s *config; FAR pthread_t *threads; struct memorystress_error_s error; uint32_t sleep_us; size_t nthreads; bool debug; }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: show_usage ****************************************************************************/ static void show_usage(FAR const char *progname) { printf("\nUsage: %s -m -n -t " " -x [nthreads Default:1] -d [debuger mode]\n", progname); printf("\nWhere:\n"); printf(" -m max alloc size.\n"); printf(" -n Number of allocated memory blocks .\n"); printf(" -t Length of time between each test.\n"); printf(" -x [nthreads] Enable multi-thread stress testing. \n"); printf(" -d [debug mode] Helps to localize the problem situation," "there is a lot of information output in this mode.\n"); exit(EXIT_FAILURE); } /**************************************************************************** * Name: randnum ****************************************************************************/ static uint32_t randnum(uint32_t max, FAR uint32_t *seed) { uint32_t x = *seed; x ^= x << 13; x ^= x >> 17; x ^= x << 5; *seed = x; return x % max; } /**************************************************************************** * Name: genvalue ****************************************************************************/ static uint32_t genvalue(FAR uint32_t *seed, bool debug) { if (debug) { return DEBUG_MAGIC; } return randnum(UINT32_MAX, seed); } /**************************************************************************** * Name: error_result ****************************************************************************/ static void error_result(struct memorystress_error_s error) { syslog(LOG_ERR, MEMSTRESS_PREFIX "%s ERROR!, " "buf = %p, " "node = %p, " "size = %zu, " "offset = %zu(addr = %p), " "cnt = %zu, " "index = %zu, " "readValue = 0x%x, " "writeValue = 0x%x\n", error.rwerror == MEMORY_STRESS_READ_ERROR ? "READ" : "WRITE", error.buf, (uintptr_t *)error.node, error.size, error.offset, error.buf + error.offset, error.cnt, error.index, error.readvalue, error.writevalue); } /**************************************************************************** * Name: checknode ****************************************************************************/ static void checknode(FAR struct memorystress_context_s *context, FAR struct memorystress_node_s *node, enum memorystress_rwerror_e rwerror) { size_t size = node->size; uint32_t seed = size; size_t i; /* check data */ for (i = 0; i < size; i++) { uint8_t write_value = genvalue(&seed, context->debug); uint8_t read_value = node->buf[i]; if (read_value != write_value) { context->error.buf = node->buf; context->error.size = node->size; context->error.offset = i; context->error.readvalue = read_value; context->error.writevalue = write_value; context->error.rwerror = rwerror; error_result(context->error); lib_dumpbuffer("debuger", node->buf, size); /* Trigger the ASSET once it occurs, retaining the error site */ DEBUGASSERT(false); } } } /**************************************************************************** * Name: memorystress_iter ****************************************************************************/ static bool memorystress_iter(FAR struct memorystress_context_s *context) { FAR struct memorystress_node_s *node; FAR struct memorystress_func_s *func; uint32_t seed = rand() % UINT32_MAX; bool debug = context->debug; size_t index; index = randnum(context->config->nodelen, &seed); node = &(context->node_array[index]); func = (FAR struct memorystress_func_s *)context->config->func; /* Record the current index and node address */ context->error.index = index; context->error.node = (uintptr_t)node; /* check state */ if (!node->buf) { /* Selection of test type and test size by random number */ FAR uint8_t *ptr = NULL; size_t size = randnum(context->config->max_allocsize, &seed); int switch_func = randnum(3, &seed); int align = 1 << (randnum(4, &seed) + 2); /* There are currently three types of tests: * 0.standard malloc * 1.align_alloc * 2.realloc */ switch (switch_func) { case 0: ptr = func->malloc(size); break; case 1: ptr = func->aligned_alloc(align, size); break; case 2: /* We have to allocate memory randomly once first, * otherwise realloc's behavior is equivalent to malloc */ ptr = func->malloc(1024); if (ptr == NULL) { return true; } ptr = func->realloc(ptr, size); break; default: syslog(LOG_ERR, "Invalid switch_func number.\n"); break; } /* Check the pointer to the test, if it is null there * may not be enough memory allocated. */ if (ptr == NULL) { return true; } node->buf = ptr; node->size = size; /* fill random data */ seed = size; while (size--) { *ptr++ = genvalue(&seed, debug); } /* Check write success */ checknode(context, node, MEMORY_STRESS_WRITE_ERROR); } else { /* check read */ checknode(context, node, MEMORY_STRESS_READ_ERROR); /* free node */ func->freefunc(node->buf); node->buf = NULL; } context->error.cnt++; return true; } /**************************************************************************** * Name: debug_malloc ****************************************************************************/ static FAR void *debug_malloc(size_t size) { void *ptr = malloc(size); syslog(LOG_INFO, MEMSTRESS_PREFIX "malloc: %zu bytes, ptr = %p\n", size, ptr); return ptr; } /**************************************************************************** * Name: debug_free ****************************************************************************/ static void debug_free(FAR void *ptr) { syslog(LOG_INFO, MEMSTRESS_PREFIX "free: %p\n", ptr); free(ptr); } /**************************************************************************** * Name: debug_aligned_alloc ****************************************************************************/ static FAR void *debug_aligned_alloc(size_t align, size_t nbytes) { void *ptr = memalign(align, nbytes); syslog(LOG_INFO, MEMSTRESS_PREFIX "aligned_alloc: %zu bytes, align: %zu," " ptr: %p\n", nbytes, align, ptr); return ptr; } /**************************************************************************** * Name: debug_realloc ****************************************************************************/ static FAR void *debug_realloc(FAR void *ptr, size_t new_size) { ptr = realloc(ptr, new_size); syslog(LOG_INFO, MEMSTRESS_PREFIX "realloc: %zu bytes, ptr: %p\n", new_size, ptr); return ptr; } /**************************************************************************** * Name: init ****************************************************************************/ static void init(FAR struct memorystress_context_s *context, int argc, FAR char *argv[]) { FAR struct memorystress_config_s *config; FAR struct memorystress_func_s *func; int ch; memset(context, 0, sizeof(struct memorystress_context_s)); context->nthreads = 1; config = zalloc(sizeof(struct memorystress_config_s)); func = zalloc(sizeof(struct memorystress_func_s)); if (func == NULL || config == NULL) { free(config); free(func); syslog(LOG_ERR, MEMSTRESS_PREFIX "Malloc struct Failed\n"); exit(EXIT_FAILURE); } while ((ch = getopt(argc, argv, "dm:n:t:x::")) != ERROR) { switch (ch) { case 'd': context->debug = true; break; case 'm': OPTARG_TO_VALUE(config->max_allocsize, size_t); break; case 'n': OPTARG_TO_VALUE(config->nodelen, int); break; case 't': OPTARG_TO_VALUE(context->sleep_us, uint32_t); break; case 'x': OPTARG_TO_VALUE(context->nthreads, int); break; default: show_usage(argv[0]); break; } } if (config->max_allocsize == 0 || config->nodelen == 0 || context->sleep_us == 0) { free(config); free(func); show_usage(argv[0]); } /* initialization function */ if (context->debug) { func->malloc = debug_malloc; func->aligned_alloc = debug_aligned_alloc; func->realloc = debug_realloc; func->freefunc = debug_free; } else { func->malloc = malloc; func->aligned_alloc = aligned_alloc; func->realloc = realloc; func->freefunc = free; } config->func = func; context->config = config; /* init node array */ context->node_array = zalloc(config->nodelen * sizeof(struct memorystress_node_s)); if (context->node_array == NULL) { free(func); free(config); syslog(LOG_ERR, MEMSTRESS_PREFIX "Malloc Node Array Failed\n"); exit(EXIT_FAILURE); } context->threads = zalloc(context->nthreads * sizeof(pthread_t)); if (context->threads == NULL) { free(func); free(config); free(context->node_array); syslog(LOG_ERR, MEMSTRESS_PREFIX "Malloc threads Failed\n"); exit(EXIT_FAILURE); } srand(time(NULL)); } /**************************************************************************** * Name: memorystress_thread ****************************************************************************/ FAR void *memorystress_thread(FAR void *arg) { FAR struct memorystress_context_s *context; context = (FAR struct memorystress_context_s *)arg; while (memorystress_iter(context)) { usleep(context->sleep_us); } return NULL; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: main ****************************************************************************/ int main(int argc, FAR char *argv[]) { struct memorystress_context_s context; int i; init(&context, argc, argv); syslog(LOG_INFO, MEMSTRESS_PREFIX "testing...\n"); for (i = 0; i < context.nthreads; i++) { if (pthread_create(&context.threads[i], NULL, memorystress_thread, (FAR void *)&context) != 0) { syslog(LOG_ERR, "Failed to create thread\n"); return 1; } } for (i = 0; i < context.nthreads; i++) { pthread_join(context.threads[i], NULL); } return 0; }