// // Copyright (C) 2014 Jens Korinth, TU Darmstadt // // This file is part of Tapasco (TPC). // // Tapasco is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Tapasco 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 Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with Tapasco. If not, see . // /** * @file stress-ioctl.c * @brief * @author J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de) **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static long thrdcnt = 0; static volatile int stop = 0; static volatile int finish = 0; static volatile long runs = 0; static volatile unsigned long long alloced_bytes = 0ULL; static volatile unsigned long long freed_bytes = 0ULL; static volatile unsigned long long copyto_bytes = 0ULL; static volatile unsigned long long copyfrom_bytes = 0ULL; static volatile long errors = 0; static volatile long terrors = 0; static inline void random_fill(void *p, size_t const len) { FILE *fd = fopen("/dev/urandom", "r"); assert(fd); fread(p, 1, len, fd); fclose(fd); } static inline void copy_check(size_t const *lp) { size_t const sz = lp ? *lp : rand() % (1 << 20) & ~0x3; platform_mem_addr_t addr; unsigned char *data1 = malloc(sz); unsigned char *data2 = malloc(sz); assert(data1); assert(data2); random_fill(data1, sz); if (platform_alloc(sz, &addr, PLATFORM_ALLOC_FLAGS_NONE) == PLATFORM_SUCCESS && platform_write_mem(addr, sz, data1, PLATFORM_MEM_FLAGS_NONE) == PLATFORM_SUCCESS) { __atomic_fetch_add(&alloced_bytes, sz, __ATOMIC_SEQ_CST); __atomic_fetch_add(©to_bytes, sz, __ATOMIC_SEQ_CST); if (platform_read_mem(addr, sz, data2, PLATFORM_MEM_FLAGS_NONE) == PLATFORM_SUCCESS) { __atomic_fetch_add(©from_bytes, sz, __ATOMIC_SEQ_CST); } else __atomic_fetch_add(&errors, 1, __ATOMIC_SEQ_CST); if (platform_dealloc(addr, PLATFORM_MEM_FLAGS_NONE) == PLATFORM_SUCCESS) { __atomic_fetch_add(&freed_bytes, sz, __ATOMIC_SEQ_CST); } else __atomic_fetch_add(&errors, 1, __ATOMIC_SEQ_CST); } else __atomic_fetch_add(&errors, 1, __ATOMIC_SEQ_CST); if (memcmp(data1, data2, sz) != 0) __atomic_fetch_add(&terrors, 1, __ATOMIC_SEQ_CST); free(data1); free(data2); } static inline void alloc_free(size_t const *lp) { size_t sz = lp ? *lp : rand() % (1 << 20) & ~0x3; platform_mem_addr_t addr; if (platform_alloc(sz, &addr, PLATFORM_ALLOC_FLAGS_NONE) == PLATFORM_SUCCESS) { __atomic_fetch_add(&alloced_bytes, sz, __ATOMIC_SEQ_CST); if (platform_dealloc(addr, PLATFORM_MEM_FLAGS_NONE) == PLATFORM_SUCCESS) __atomic_fetch_add(&freed_bytes, sz, __ATOMIC_SEQ_CST); else __atomic_fetch_add(&errors, 1, __ATOMIC_SEQ_CST); } else __atomic_fetch_add(&errors, 1, __ATOMIC_SEQ_CST); } static inline void platform_write_ctl_speed(void) { const uint32_t x = 0xe5ae1337; uint32_t d = 42; if (platform_write_ctl(platform_address_get_slot_base(0, 0) + 0x20, 4, &x, PLATFORM_CTL_FLAGS_NONE) != PLATFORM_SUCCESS) { __atomic_fetch_add(&errors, 1, __ATOMIC_SEQ_CST); } else { __atomic_fetch_add(©to_bytes, 4, __ATOMIC_SEQ_CST); } if (platform_read_ctl(platform_address_get_slot_base(0, 0) + 0x20, 4, &d, PLATFORM_CTL_FLAGS_NONE) != PLATFORM_SUCCESS || d != x) { __atomic_fetch_add(&errors, 1, __ATOMIC_SEQ_CST); } else { __atomic_fetch_add(©from_bytes, 4, __ATOMIC_SEQ_CST); } } static void *stress(void *p) { long const which = (long) p; while (! finish) { switch (which) { case 1: alloc_free(NULL); break; case 2: copy_check(NULL); break; case 3: platform_write_ctl_speed(); break; default: stop = 1; return NULL; } __atomic_fetch_add(&runs, 1, __ATOMIC_SEQ_CST); sched_yield(); } return NULL; } static void init_ncurses() { initscr(); noecho(); cbreak(); curs_set(0); timeout(0); } static void exit_ncurses() { endwin(); } static int runtest(long const which) { const char *const stre = "--- press any key to exit ---"; struct timespec tv_begin, tv_now; pthread_t threads[thrdcnt]; char str[255]; int rows, cols; double to_speed = 0.0; double from_speed = 0.0; double to_speed_delta = 100000.0; unsigned long refreshes = 0; getmaxyx(stdscr, rows, cols); platform_res_t res = platform_init(); if (res != PLATFORM_SUCCESS) { exit_ncurses(); fprintf(stderr, "Platform init failed: %s", platform_strerror(errno)); exit(EXIT_FAILURE); } clear(); mvprintw(rows / 2, (cols - strlen(stre)) / 2, stre); clock_gettime(CLOCK_MONOTONIC, &tv_begin); for (long t = 0; t < thrdcnt; ++t) pthread_create(&threads[t], NULL, stress, (void *)which); while (getch() == ERR && (refreshes < 10000 || to_speed_delta > 1.0)) { clock_gettime(CLOCK_MONOTONIC, &tv_now); if (tv_now.tv_nsec < tv_begin.tv_nsec) tv_now.tv_sec += 1; int r = rows / 3 - 3; double nfrom_speed = tv_now.tv_sec == tv_begin.tv_sec ? 0.0 : (copyfrom_bytes >> 10) / (double) (tv_now.tv_sec - tv_begin.tv_sec); from_speed = ((from_speed * refreshes) + nfrom_speed) / (double)(refreshes + 1); double nto_speed = tv_now.tv_sec == tv_begin.tv_sec ? 0.0 : (copyto_bytes >> 10) / (double) (tv_now.tv_sec - tv_begin.tv_sec); double delta = abs(to_speed - nto_speed); to_speed_delta = ((to_speed_delta * refreshes) + delta) / (double)(refreshes + 1); to_speed = ((to_speed * refreshes) + nto_speed) / (double)(refreshes + 1); snprintf(str, 255, " Passes: %16lu runs ", runs); mvprintw(r++, (cols - strlen(str)) / 2, str); snprintf(str, 255, " Errors: %16lu ", errors); mvprintw(r++, (cols - strlen(str)) / 2, str); snprintf(str, 255, " T-Errors: %16lu ", terrors); mvprintw(r++, (cols - strlen(str)) / 2, str); snprintf(str, 255, " Alloc'ed: %16llu bytes", alloced_bytes); mvprintw(r++, (cols - strlen(str)) / 2, str); snprintf(str, 255, " Free'd: %16llu bytes", freed_bytes); mvprintw(r++, (cols - strlen(str)) / 2, str); snprintf(str, 255, " CopyTo'ed: %16llu bytes", copyto_bytes); mvprintw(r++, (cols - strlen(str)) / 2, str); snprintf(str, 255, "CopyFrom'ed: %16llu bytes", copyfrom_bytes); mvprintw(r++, (cols - strlen(str)) / 2, str); snprintf(str, 255, " Delta: %16.2f KiB/s", to_speed_delta); mvprintw(r++, (cols - strlen(str)) / 2, str); snprintf(str, 255, " SpeedTo: %16.2f KiB/s", to_speed); mvprintw(r++, (cols - strlen(str)) / 2, str); snprintf(str, 255, " SpeedFrom: %16.2f KiB/s", from_speed); mvprintw(r++, (cols - strlen(str)) / 2, str); refresh(); ++refreshes; } finish = 1; for (long t = 0; t < thrdcnt; ++t) pthread_join(threads[t], NULL); platform_deinit(); return errors + terrors; } static int menu(long const thrdcnt) { int rows, cols, r, c; const char *const strwelcome = "Welcome to TPC Platform Test! Choose Test:"; const char *const strc1 = "1) alloc-free (multi-threaded)"; const char *const strc2 = "2) copyto-copyfree (multi-threaded)"; const char *const strc3 = "3) measure platform_write_ctl speed"; const char *const strcq = "--- any other key to exit ---"; char strparams[255]; const int off = strlen(strc2); getmaxyx(stdscr, rows, cols); r = rows / 3; mvprintw(r++, (cols - strlen(strwelcome)) / 2, strwelcome); r += 2; mvprintw(r++, (cols - off) / 2, strc1); mvprintw(r++, (cols - off) / 2, strc2); mvprintw(r++, (cols - off) / 2, strc3); r += 1; mvprintw(r++, (cols - strlen(strcq)) / 2, strcq); r += 2; snprintf(strparams, 255, "Threads: %lu", thrdcnt); mvprintw(r, (cols - strlen(strparams)) / 2, strparams); while ((c = getch()) == ERR); if (c == '1' || c == '2' || c == '3') runtest(c == '1' ? 1 : (c == '2' ? 2 : 3)); return c; } static void print_summary() { printf( "Passes : %16lu runs\n" "Errors : %16lu\n" "T-Errors : %16lu\n" "Allocated: %16llu bytes\n" "Freed : %16llu bytes\n" "CopyTo : %16llu bytes\n" "CopyFrom : %16llu bytes\n", runs, errors, terrors, alloced_bytes, freed_bytes, copyto_bytes, copyfrom_bytes); } int main(int argc, char *argv[]) { thrdcnt = argc > 1 ? strtol(argv[1], NULL, 0) : sysconf(_SC_NPROCESSORS_CONF); srand(time(NULL)); init_ncurses(); menu(thrdcnt); exit_ncurses(); print_summary(); if (! stop) printf("Test successful.\n"); else fprintf(stderr, "Test failed!\n"); return stop ? EXIT_FAILURE : EXIT_SUCCESS; } /* vim: set foldmarker=@{,}@ foldlevel=0 foldmethod=marker : */