/* * ut_sighandler -- fatal signal handler */ void ut_sighandler(int sig) { UT_ERR("\n"); UT_ERR("Signal %d, backtrace:", sig); ut_dump_backtrace(); UT_ERR("\n"); exit(128 + sig); }
/* * mutex_write_worker -- (internal) write data with mutex */ static void * mutex_write_worker(void *arg) { for (unsigned run = 0; run < WORKER_RUNS; run++) { if (pmemobj_mutex_lock(&Mock_pop, &Test_obj->mutex)) { UT_ERR("pmemobj_mutex_lock"); return NULL; } memset(Test_obj->data, (int)(uintptr_t)arg, DATA_SIZE); if (pmemobj_mutex_unlock(&Mock_pop, &Test_obj->mutex)) UT_ERR("pmemobj_mutex_unlock"); } return NULL; }
/* * timed_check_worker -- (internal) check consistency with mutex */ static void * timed_check_worker(void *arg) { for (unsigned run = 0; run < WORKER_RUNS; run++) { int mutex_id = (int)(uintptr_t)arg % 2; PMEMmutex *mtx = mutex_id == LOCKED_MUTEX ? &Test_obj->mutex_locked : &Test_obj->mutex; struct timespec t1, t2, t_diff, abs_time; os_clock_gettime(CLOCK_REALTIME, &t1); abs_time = t1; abs_time.tv_nsec += TIMEOUT; if (abs_time.tv_nsec >= NANO_PER_ONE) { abs_time.tv_sec += abs_time.tv_nsec / NANO_PER_ONE; abs_time.tv_nsec %= NANO_PER_ONE; } int ret = pmemobj_mutex_timedlock(&Mock_pop, mtx, &abs_time); os_clock_gettime(CLOCK_REALTIME, &t2); if (mutex_id == LOCKED_MUTEX) { UT_ASSERTeq(ret, ETIMEDOUT); t_diff.tv_sec = t2.tv_sec - t1.tv_sec; t_diff.tv_nsec = t2.tv_nsec - t1.tv_nsec; if (t_diff.tv_nsec < 0) { --t_diff.tv_sec; t_diff.tv_nsec += NANO_PER_ONE; } UT_ASSERT(t_diff.tv_sec * NANO_PER_ONE + t_diff.tv_nsec >= TIMEOUT); return NULL; } if (ret == 0) { UT_ASSERTne(mutex_id, LOCKED_MUTEX); pmemobj_mutex_unlock(&Mock_pop, mtx); } else if (ret == ETIMEDOUT) { t_diff.tv_sec = t2.tv_sec - t1.tv_sec; t_diff.tv_nsec = t2.tv_nsec - t1.tv_nsec; if (t_diff.tv_nsec < 0) { --t_diff.tv_sec; t_diff.tv_nsec += NANO_PER_ONE; } UT_ASSERT(t_diff.tv_sec * NANO_PER_ONE + t_diff.tv_nsec >= TIMEOUT); } else { errno = ret; UT_ERR("!pmemobj_mutex_timedlock"); } } return NULL; }
/* * rwlock_check_worker -- (internal) check consistency with rwlock */ static void * rwlock_check_worker(void *arg) { for (unsigned run = 0; run < WORKER_RUNS; run++) { if (pmemobj_rwlock_rdlock(&Mock_pop, &Test_obj->rwlock)) { UT_ERR("pmemobj_rwlock_rdlock"); return NULL; } uint8_t val = Test_obj->data[0]; for (int i = 1; i < DATA_SIZE; i++) UT_ASSERTeq(Test_obj->data[i], val); if (pmemobj_rwlock_unlock(&Mock_pop, &Test_obj->rwlock)) UT_ERR("pmemobj_rwlock_unlock"); } return NULL; }
/* * ut_dump_backtrace -- dump stacktrace to error log using libc's backtrace */ void ut_dump_backtrace(void) { int j, nptrs; void *buffer[SIZE]; char **strings; nptrs = backtrace(buffer, SIZE); strings = backtrace_symbols(buffer, nptrs); if (strings == NULL) { UT_ERR("!backtrace_symbols"); return; } for (j = 0; j < nptrs; j++) UT_ERR("%u: %s", j, strings[j]); free(strings); }
/* * ut_dump_backtrace -- dump stacktrace to error log using libunwind */ void ut_dump_backtrace(void) { unw_context_t context; unw_proc_info_t pip; pip.unwind_info = NULL; int ret = unw_getcontext(&context); if (ret) { UT_ERR("unw_getcontext: %s [%d]", unw_strerror(ret), ret); return; } unw_cursor_t cursor; ret = unw_init_local(&cursor, &context); if (ret) { UT_ERR("unw_init_local: %s [%d]", unw_strerror(ret), ret); return; } ret = unw_step(&cursor); char procname[PROCNAMELEN]; unsigned i = 0; while (ret > 0) { ret = unw_get_proc_info(&cursor, &pip); if (ret) { UT_ERR("unw_get_proc_info: %s [%d]", unw_strerror(ret), ret); break; } unw_word_t off; ret = unw_get_proc_name(&cursor, procname, PROCNAMELEN, &off); if (ret && ret != -UNW_ENOMEM) { if (ret != -UNW_EUNSPEC) { UT_ERR("unw_get_proc_name: %s [%d]", unw_strerror(ret), ret); } strcpy(procname, "?"); } void *ptr = (void *)(pip.start_ip + off); Dl_info dlinfo; const char *fname = "?"; if (dladdr(ptr, &dlinfo) && dlinfo.dli_fname && *dlinfo.dli_fname) fname = dlinfo.dli_fname; UT_ERR("%u: %s (%s%s+0x%lx) [%p]", i++, fname, procname, ret == -UNW_ENOMEM ? "..." : "", off, ptr); ret = unw_step(&cursor); if (ret < 0) UT_ERR("unw_step: %s [%d]", unw_strerror(ret), ret); } }
/* * open_file_walk -- walk lut for any left-overs * * prints error if any found, increments Fd_errcount */ static void open_file_walk(struct fd_lut *root) { if (root) { open_file_walk(root->left); if (root->fdfile) { UT_ERR("open file missing: fd %d => \"%s\"", root->fdnum, root->fdfile); Fd_errcount++; } open_file_walk(root->right); } }
/* * open_file_remove -- find exact match & remove it from lut * * prints error if exact match not found, increments Fd_errcount */ static void open_file_remove(struct fd_lut *root, int fdnum, const char *fdfile) { if (root == NULL) { UT_ERR("unexpected open file: fd %d => \"%s\"", fdnum, fdfile); Fd_errcount++; } else if (root->fdnum == fdnum) { if (root->fdfile == NULL) { UT_ERR("open file dup: fd %d => \"%s\"", fdnum, fdfile); Fd_errcount++; } else if (strcmp(root->fdfile, fdfile) == 0) { /* found exact match */ FREE(root->fdfile); root->fdfile = NULL; } else { UT_ERR("open file changed: fd %d was \"%s\" now \"%s\"", fdnum, root->fdfile, fdfile); Fd_errcount++; } } else if (root->fdnum < fdnum) open_file_remove(root->left, fdnum, fdfile); else open_file_remove(root->right, fdnum, fdfile); }
/* * cond_write_worker -- (internal) write data with cond variable */ static void * cond_write_worker(void *arg) { for (unsigned run = 0; run < WORKER_RUNS; run++) { if (pmemobj_mutex_lock(&Mock_pop, &Test_obj->mutex)) return NULL; memset(Test_obj->data, (int)(uintptr_t)arg, DATA_SIZE); Test_obj->check_data = 1; if (pmemobj_cond_signal(&Mock_pop, &Test_obj->cond)) UT_ERR("pmemobj_cond_signal"); pmemobj_mutex_unlock(&Mock_pop, &Test_obj->mutex); } return NULL; }
int main(int argc, char *argv[]) { START(argc, argv, "remote_basic"); if (argc != 2) UT_FATAL("usage: %s <file-to-be-checked>", argv[0]); const char *file = argv[1]; if (access(file, F_OK) != 0) UT_FATAL("File '%s' does not exist", file); else UT_OUT("File '%s' exists", file); UT_OUT("An example of OUT message"); UT_ERR("An example of ERR message"); DONE(NULL); }
/* * cond_check_worker -- (internal) check consistency with cond variable */ static void * cond_check_worker(void *arg) { for (unsigned run = 0; run < WORKER_RUNS; run++) { if (pmemobj_mutex_lock(&Mock_pop, &Test_obj->mutex)) return NULL; while (Test_obj->check_data != 1) { if (pmemobj_cond_wait(&Mock_pop, &Test_obj->cond, &Test_obj->mutex)) UT_ERR("pmemobj_cond_wait"); } uint8_t val = Test_obj->data[0]; for (int i = 1; i < DATA_SIZE; i++) UT_ASSERTeq(Test_obj->data[i], val); memset(Test_obj->data, 0, DATA_SIZE); pmemobj_mutex_unlock(&Mock_pop, &Test_obj->mutex); } return NULL; }
int main(int argc, char *argv[]) { int fd; size_t mapped_len; char *dest; char *dest1; char *ret; START(argc, argv, "pmem_memset"); if (argc != 4) UT_FATAL("usage: %s file offset length", argv[0]); fd = OPEN(argv[1], O_RDWR); /* open a pmem file and memory map it */ if ((dest = pmem_map_file(argv[1], 0, 0, 0, &mapped_len, NULL)) == NULL) UT_FATAL("!Could not mmap %s\n", argv[1]); int dest_off = atoi(argv[2]); size_t bytes = strtoul(argv[3], NULL, 0); char *buf = MALLOC(bytes); memset(dest, 0, bytes); util_persist_auto(util_fd_is_device_dax(fd), dest, bytes); dest1 = MALLOC(bytes); memset(dest1, 0, bytes); /* * This is used to verify that the value of what a non persistent * memset matches the outcome of the persistent memset. The * persistent memset will match the file but may not be the * correct or expected value. */ memset(dest1 + dest_off, 0x5A, bytes / 4); memset(dest1 + dest_off + (bytes / 4), 0x46, bytes / 4); /* Test the corner cases */ ret = pmem_memset_persist(dest + dest_off, 0x5A, 0); UT_ASSERTeq(ret, dest + dest_off); UT_ASSERTeq(*(char *)(dest + dest_off), 0); /* * Do the actual memset with persistence. */ ret = pmem_memset_persist(dest + dest_off, 0x5A, bytes / 4); UT_ASSERTeq(ret, dest + dest_off); ret = pmem_memset_persist(dest + dest_off + (bytes / 4), 0x46, bytes / 4); UT_ASSERTeq(ret, dest + dest_off + (bytes / 4)); if (memcmp(dest, dest1, bytes / 2)) UT_ERR("%s: first %zu bytes do not match", argv[1], bytes / 2); LSEEK(fd, (off_t)0, SEEK_SET); if (READ(fd, buf, bytes / 2) == bytes / 2) { if (memcmp(buf, dest, bytes / 2)) UT_ERR("%s: first %zu bytes do not match", argv[1], bytes / 2); } UT_ASSERTeq(pmem_unmap(dest, mapped_len), 0); FREE(dest1); FREE(buf); CLOSE(fd); DONE(NULL); }
/* * do_memmove: Worker function for memmove. * * Always work within the boundary of bytes. Fill in 1/2 of the src * memory with the pattern we want to write. This allows us to check * that we did not overwrite anything we were not supposed to in the * dest. Use the non pmem version of the memset/memcpy commands * so as not to introduce any possible side affects. */ static void do_memmove(int fd, char *dest, char *src, char *file_name, off_t dest_off, off_t src_off, off_t off, off_t bytes) { void *ret; char *src1 = MALLOC(bytes); char *buf = MALLOC(bytes); char old; memset(buf, 0, bytes); memset(src1, 0, bytes); memset(src, 0x5A, bytes / 4); util_persist_auto(util_fd_is_device_dax(fd), src, bytes / 4); memset(src + bytes / 4, 0x54, bytes / 4); util_persist_auto(util_fd_is_device_dax(fd), src + bytes / 4, bytes / 4); /* dest == src */ old = *(char *)(dest + dest_off); ret = pmem_memmove_persist(dest + dest_off, dest + dest_off, bytes / 2); UT_ASSERTeq(ret, dest + dest_off); UT_ASSERTeq(*(char *)(dest + dest_off), old); /* len == 0 */ old = *(char *)(dest + dest_off); ret = pmem_memmove_persist(dest + dest_off, src + src_off, 0); UT_ASSERTeq(ret, dest + dest_off); UT_ASSERTeq(*(char *)(dest + dest_off), old); /* * A side affect of the memmove call is that * src contents will be changed in the case of overlapping * addresses. */ memcpy(src1, src, bytes / 2); ret = pmem_memmove_persist(dest + dest_off, src + src_off, bytes / 2); UT_ASSERTeq(ret, dest + dest_off); /* memcmp will validate that what I expect in memory. */ if (memcmp(src1 + src_off, dest + dest_off, bytes / 2)) UT_ERR("%s: %zu bytes do not match with memcmp", file_name, bytes / 2); /* * This is a special case. An overlapping dest means that * src is a pointer to the file, and destination is src + dest_off + * overlap. This is the basis for the comparison. The use of ERR * here is deliberate. This will force a failure of the test but allow * it to continue until its done. The idea is that allowing some * to succeed and others to fail gives more information about what * went wrong. */ if (dest > src && off != 0) { LSEEK(fd, (off_t)dest_off + off, SEEK_SET); if (READ(fd, buf, bytes / 2) == bytes / 2) { if (memcmp(src1 + src_off, buf, bytes / 2)) UT_ERR("%s: first %zu bytes do not match", file_name, bytes / 2); } } else { LSEEK(fd, (off_t)dest_off, SEEK_SET); if (READ(fd, buf, bytes / 2) == bytes / 2) { if (memcmp(src1 + src_off, buf, bytes / 2)) UT_ERR("%s: first %zu bytes do not match", file_name, bytes / 2); } } FREE(src1); FREE(buf); }
int main(int argc, char *argv[]) { int fd; char *dest; char *src; off_t dest_off = 0; off_t src_off = 0; uint64_t bytes = 0; int who = 0; off_t overlap = 0; size_t mapped_len; START(argc, argv, "pmem_memmove"); fd = OPEN(argv[1], O_RDWR); if (argc < 3) USAGE(); for (int arg = 2; arg < argc; arg++) { if (strchr("dsboS", argv[arg][0]) == NULL || argv[arg][1] != ':') UT_FATAL("op must be d: or s: or b: or o: or S:"); off_t val = strtoul(&argv[arg][2], NULL, 0); switch (argv[arg][0]) { case 'd': if (val <= 0) UT_FATAL("bad offset (%lu) with d: option", val); dest_off = val; break; case 's': if (val <= 0) UT_FATAL("bad offset (%lu) with s: option", val); src_off = val; break; case 'b': if (val <= 0) UT_FATAL("bad length (%lu) with b: option", val); bytes = val; break; case 'o': if (val != 1 && val != 2) UT_FATAL("bad val (%lu) with o: option", val); who = (int)val; break; case 'S': overlap = val; break; } } if (who == 0 && overlap != 0) USAGE(); /* for overlap the src and dest must be created differently */ if (who == 0) { /* src > dest */ dest = pmem_map_file(argv[1], 0, 0, 0, &mapped_len, NULL); if (dest == NULL) UT_FATAL("!could not mmap dest file %s", argv[1]); src = MMAP(dest + mapped_len, mapped_len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); /* * Its very unlikely that src would not be > dest. pmem_map_file * chooses the first unused address >= 1TB, large * enough to hold the give range, and 1GB aligned. Log * the error if the mapped addresses cannot be swapped * but allow the test to continue. */ if (src <= dest) { swap_mappings(&dest, &src, mapped_len, fd); if (src <= dest) UT_ERR("cannot map files in memory order"); } do_memmove(fd, dest, src, argv[1], dest_off, src_off, 0, bytes); /* dest > src */ swap_mappings(&dest, &src, mapped_len, fd); if (dest <= src) UT_ERR("cannot map files in memory order"); do_memmove(fd, dest, src, argv[1], dest_off, src_off, 0, bytes); MUNMAP(dest, mapped_len); MUNMAP(src, mapped_len); } else if (who == 1) { /* src overlap with dest */ dest = pmem_map_file(argv[1], 0, 0, 0, &mapped_len, NULL); if (dest == NULL) UT_FATAL("!Could not mmap %s: \n", argv[1]); src = dest + overlap; memset(dest, 0, bytes); util_persist_auto(util_fd_is_device_dax(fd), dest, bytes); do_memmove(fd, dest, src, argv[1], dest_off, src_off, overlap, bytes); MUNMAP(dest, mapped_len); } else { /* dest overlap with src */ dest = pmem_map_file(argv[1], 0, 0, 0, &mapped_len, NULL); if (dest == NULL) { UT_FATAL("!Could not mmap %s: \n", argv[1]); } src = dest; dest = src + overlap; memset(src, 0, bytes); util_persist_auto(util_fd_is_device_dax(fd), src, bytes); do_memmove(fd, dest, src, argv[1], dest_off, src_off, overlap, bytes); MUNMAP(src, mapped_len); } CLOSE(fd); DONE(NULL); }
int main(int argc, char *argv[]) { START(argc, argv, "vmem_delete"); VMEM *vmp; void *ptr; if (argc < 2) UT_FATAL("usage: %s op:h|f|m|c|r|a|s|d", argv[0]); /* allocate memory for function vmem_create_in_region() */ void *mem_pool = MMAP_ANON_ALIGNED(VMEM_MIN_POOL, 4 << 20); vmp = vmem_create_in_region(mem_pool, VMEM_MIN_POOL); if (vmp == NULL) UT_FATAL("!vmem_create_in_region"); ptr = vmem_malloc(vmp, sizeof(long long)); if (ptr == NULL) UT_ERR("!vmem_malloc"); vmem_delete(vmp); /* arrange to catch SEGV */ struct sigaction v; sigemptyset(&v.sa_mask); v.sa_flags = 0; v.sa_handler = signal_handler; SIGACTION(SIGSEGV, &v, NULL); SIGACTION(SIGABRT, &v, NULL); SIGACTION(SIGILL, &v, NULL); /* go through all arguments one by one */ for (int arg = 1; arg < argc; arg++) { /* Scan the character of each argument. */ if (strchr("hfmcrasd", argv[arg][0]) == NULL || argv[arg][1] != '\0') UT_FATAL("op must be one of: h, f, m, c, r, a, s, d"); switch (argv[arg][0]) { case 'h': UT_OUT("Testing vmem_check..."); if (!sigsetjmp(Jmp, 1)) { UT_OUT("\tvmem_check returned %i", vmem_check(vmp)); } break; case 'f': UT_OUT("Testing vmem_free..."); if (!sigsetjmp(Jmp, 1)) { vmem_free(vmp, ptr); UT_OUT("\tvmem_free succeeded"); } break; case 'm': UT_OUT("Testing vmem_malloc..."); if (!sigsetjmp(Jmp, 1)) { ptr = vmem_malloc(vmp, sizeof(long long)); if (ptr != NULL) UT_OUT("\tvmem_malloc succeeded"); else UT_OUT("\tvmem_malloc returned NULL"); } break; case 'c': UT_OUT("Testing vmem_calloc..."); if (!sigsetjmp(Jmp, 1)) { ptr = vmem_calloc(vmp, 10, sizeof(int)); if (ptr != NULL) UT_OUT("\tvmem_calloc succeeded"); else UT_OUT("\tvmem_calloc returned NULL"); } break; case 'r': UT_OUT("Testing vmem_realloc..."); if (!sigsetjmp(Jmp, 1)) { ptr = vmem_realloc(vmp, ptr, 128); if (ptr != NULL) UT_OUT("\tvmem_realloc succeeded"); else UT_OUT("\tvmem_realloc returned NULL"); } break; case 'a': UT_OUT("Testing vmem_aligned_alloc..."); if (!sigsetjmp(Jmp, 1)) { ptr = vmem_aligned_alloc(vmp, 128, 128); if (ptr != NULL) UT_OUT("\tvmem_aligned_alloc " "succeeded"); else UT_OUT("\tvmem_aligned_alloc" " returned NULL"); } break; case 's': UT_OUT("Testing vmem_strdup..."); if (!sigsetjmp(Jmp, 1)) { ptr = vmem_strdup(vmp, "Test string"); if (ptr != NULL) UT_OUT("\tvmem_strdup succeeded"); else UT_OUT("\tvmem_strdup returned NULL"); } break; case 'd': UT_OUT("Testing vmem_delete..."); if (!sigsetjmp(Jmp, 1)) { vmem_delete(vmp); if (errno != 0) UT_OUT("\tvmem_delete failed: %s", vmem_errormsg()); else UT_OUT("\tvmem_delete succeeded"); } break; } } DONE(NULL); }