/* * icount_start -- begin instruction counting * * Inputs: * life_remaining -- simulate a crash after counting this many * instructions. pass in 0ull to disable this * feature. * * There is some variability on how far the parent can get before the child * attaches to it for tracing, especially when the system is loaded down * (e.g. due to zillions of parallel icount traces happening at once). * To coordinate this, we use a multi-step procedure where the tracee * runs until it gets to the function pretrigger() and the tracer() detects * that and raises a signal (SIGRTMIN+15) in the tracee, who then catches * it and longjmps out of signal handler, back here, and then calls trigger(). * * The multi-step coordination sounds complex, but it handles the * surprisingly common cases where the tracer attached to the tracee * before it even finished executing the libc fork() wrapper, and the * other end of the spectrum where the tracee ran way too far ahead * before the tracer attached and the tracer missed the call to trigger(). */ void icount_start(unsigned long life_remaining) { if (Tracer_pid) { /* nested call not allowed */ icount_stop(); FATAL("icount_start called while counting already active"); } Total = 0ull; if (pipe(Tracerpipe) < 0) FATALSYS("pipe"); if (signal(SIGRTMIN+15, handler) == SIG_ERR) FATALSYS("signal: SIGRTMIN+15"); if ((Tracer_pid = fork()) < 0) FATALSYS("fork"); else if (Tracer_pid) { /* parent */ close(Tracerpipe[1]); if (!setjmp(Trigjmp)) pretrigger(); close(-1); /* harmless syscall for tracer */ trigger(); return; } else { /* child */ close(Tracerpipe[0]); tracer(life_remaining); } }
/* * icount_stop -- stop counting instructions */ void icount_stop(void) { int status; if (read(Tracerpipe[0], &Total, sizeof(Total)) < 0) FATALSYS("read from pipe"); close(Tracerpipe[0]); /* wait for child */ if (waitpid(Tracer_pid, &status, 0) < 0) FATALSYS("waitpid(pid=%d)", Tracer_pid); Tracer_pid = 0; }
/* * pmem_flush_cache -- flush processor cache for the given range * * This is the fit version (fault injection test) that uses copy-on-write. */ void pmem_flush_cache_fit(void *addr, size_t len, int flags) { uintptr_t uptr; if (!PM_base) FATAL("pmem_map hasn't been called"); /* * even though pwrite() can take any random byte addresses and * lengths, we simulate cache flushing by writing the full 64B * chunks that cover the given range. */ for (uptr = (uintptr_t)addr & ~(ALIGN - 1); uptr < (uintptr_t)addr + len; uptr += ALIGN) if (pwrite(PM_fd, (void *)uptr, ALIGN, uptr - PM_base) < 0) FATALSYS("pwrite len %d offset %lu", len, addr - PM_base); }
/* * pmem_flush_cache -- flush processor cache for the given range * * This is the msync-based version. */ void pmem_flush_cache_msync(void *addr, size_t len, int flags) { uintptr_t uptr; /* * msync requires len to be a multiple of pagesize, so * adjust addr and len to represent the full 4k chunks * covering the given range. */ /* increase len by the amount we gain when we round addr down */ len += (uintptr_t)addr & (ALIGN - 1); /* round addr down to page boundary */ uptr = (uintptr_t)addr & ~(ALIGN - 1); /* round len up to multiple of page size */ len = (len + (ALIGN - 1)) & ~(ALIGN - 1); if (msync((void *)uptr, len, MS_SYNC) < 0) FATALSYS("msync"); }
int main(int argc, char *argv[]) { int opt; int iflag = 0; unsigned long icount; const char *path; struct stat stbuf; size_t size; int fd; char *pmaddr; Myname = argv[0]; while ((opt = getopt(argc, argv, "FMdi:")) != -1) { switch (opt) { case 'F': pmem_fit_mode(); break; case 'M': pmem_msync_mode(); break; case 'd': Debug++; break; case 'i': iflag++; icount = strtoul(optarg, NULL, 10); break; default: USAGE(NULL); } } if (optind >= argc) USAGE("No path given"); path = argv[optind++]; if (stat(path, &stbuf) < 0) { /* * file didn't exist, create it with DEFAULT_SIZE */ if ((fd = open(path, O_CREAT|O_RDWR, 0666)) < 0) FATALSYS("can't create %s", path); if ((errno = posix_fallocate(fd, 0, DEFAULT_SIZE)) != 0) FATALSYS("posix_fallocate"); size = DEFAULT_SIZE; } else { /* * file exists, just open it */ if ((fd = open(path, O_RDWR)) < 0) FATALSYS("open %s", path); size = stbuf.st_size; } /* * map the file into our address space. */ if ((pmaddr = pmem_map(fd, size)) == NULL) FATALSYS("pmem_map"); if (optind < argc) { /* strings supplied as arguments? */ int i; char *ptr = pmaddr; if (iflag) icount_start(icount); /* start instruction count */ for (i = optind; i < argc; i++) { size_t len = strlen(argv[i]) + 1; /* includes '\0' */ if (len > size) FATAL("no more room for %d-byte string", len); /* store to Persistent Memory */ strcpy(ptr, argv[i]); /* make that change durable */ pmem_persist(ptr, len, 0); ptr += len; size -= len; } if (iflag) { icount_stop(); /* end instruction count */ printf("Total instruction count: %lu\n", icount_total()); } } else { char *ptr = pmaddr; char *sep = ""; /* * dump out all the strings we find in Persistent Memory */ while (ptr < &pmaddr[size]) { /* load from Persistent Memory */ if (isprint(*ptr)) { putc(*ptr, stdout); sep = "\n"; } else if (*ptr == '\0') { fputs(sep, stdout); sep = ""; } ptr++; } } exit(0); }
/* * tracer -- internal function used in child process to trace parent */ static void tracer(unsigned long ttl) { pid_t ppid = getppid(); int status; int triggered = 0; int signaled = 0; if (ptrace(PTRACE_ATTACH, ppid, 0, 0) < 0) FATALSYS("PTRACE_ATTACH"); while (1) { if (waitpid(ppid, &status, 0) < 0) FATALSYS("waitpid(pid=%d)", ppid); if (WIFSTOPPED(status)) { struct user_regs_struct regs; if (triggered) Total++; if (ttl && Total >= ttl) { if (kill(ppid, SIGKILL) < 0) FATAL("SIGKILL %d", ppid); printf("Program terminated after %lu " "instructions\n", Total); fflush(stdout); _exit(0); } if (ptrace(PTRACE_GETREGS, ppid, 0, ®s) < 0) FATALSYS("PTRACE_GETREGS"); if ((unsigned long)regs.rip >= (unsigned long)pretrigger && (unsigned long)regs.rip < (unsigned long)trigger) { if (!signaled) { if (ptrace(PTRACE_SYSCALL, ppid, 0, SIGRTMIN+15) < 0) FATALSYS("PTRACE_SYSCALL"); signaled = 1; continue; } } else if ((unsigned long) regs.rip == (unsigned long) trigger) { triggered = 1; } else if ((unsigned long) regs.rip == (unsigned long) icount_stop) { if (ptrace(PTRACE_DETACH, ppid, 0, 0) < 0) FATALSYS("PTRACE_DETACH"); break; } if (ptrace(PTRACE_SINGLESTEP, ppid, 0, 0) < 0) FATALSYS("PTRACE_SINGLESTEP"); } else if (WIFEXITED(status)) FATAL("tracee: exit %d", WEXITSTATUS(status)); else if (WIFSIGNALED(status)) FATAL("tracee: %s", strsignal(WTERMSIG(status))); else FATAL("unexpected wait status: 0x%x", status); } /* * our counting is done, send the count back to the tracee * via the pipe and exit. */ if (write(Tracerpipe[1], &Total, sizeof(Total)) < 0) FATALSYS("write to pipe"); close(Tracerpipe[1]); _exit(0); }
/* * pmemalloc_check -- check the consistency of a pmem pool * * Inputs: * path -- path to the file which contains the memory pool * * The current state of the pmem pool is printed. This routine does * not make any changes to the pmem pool (maps it read-only, in fact). * It is not necessary to call pmemalloc_init() before calling this. */ void pmemalloc_check(const char *path) { void *pmp; int fd; struct stat stbuf; struct clump *clp; struct clump *lastclp; struct pool_header *hdrp; size_t clumptotal; /* * stats we keep for each type of memory: * stats[PMEM_STATE_FREE] for free clumps * stats[PMEM_STATE_RESERVED] for reserved clumps * stats[PMEM_STATE_ACTIVATING] for activating clumps * stats[PMEM_STATE_ACTIVE] for active clumps * stats[PMEM_STATE_FREEING] for freeing clumps * stats[PMEM_STATE_UNUSED] for overall totals */ struct { size_t largest; size_t smallest; size_t bytes; unsigned count; } stats[PMEM_STATE_UNUSED + 1] = { 0 }; const char *names[] = { "Free", "Reserved", "Activating", "Active", "Freeing", "TOTAL", }; int i; DEBUG("path=%s", path); if ((fd = open(path, O_RDONLY)) < 0) FATALSYS("%s", path); if (fstat(fd, &stbuf) < 0) FATALSYS("fstat"); DEBUG("file size 0x%lx", stbuf.st_size); if (stbuf.st_size < PMEM_MIN_POOL_SIZE) FATAL("size %lu too small (must be at least %lu)", stbuf.st_size, PMEM_MIN_POOL_SIZE); if ((pmp = mmap(NULL, stbuf.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) FATALSYS("mmap"); DEBUG("pmp %lx", pmp); close(fd); hdrp = PMEM(pmp, (struct pool_header *)PMEM_HDR_OFFSET); DEBUG(" hdrp 0x%lx (off 0x%lx)", hdrp, OFF(pmp, hdrp)); if (strcmp(hdrp->signature, PMEM_SIGNATURE)) FATAL("failed signature check"); DEBUG("signature check passed"); clp = PMEM(pmp, (struct clump *)PMEM_CLUMP_OFFSET); /* * location of last clump is calculated by rounding the file * size down to a multiple of 64, and then subtracting off * another 64 to hold the struct clump. the last clump is * indicated by a size of zero. */ lastclp = PMEM(pmp, (struct clump *) (stbuf.st_size & ~(PMEM_CHUNK_SIZE - 1)) - PMEM_CHUNK_SIZE); DEBUG(" clp 0x%lx (off 0x%lx)", clp, OFF(pmp, clp)); DEBUG("lastclp 0x%lx (off 0x%lx)", lastclp, OFF(pmp, lastclp)); clumptotal = (uintptr_t)lastclp - (uintptr_t)clp; DEBUG("expected clumptotal: %lu", clumptotal); /* * check that: * * the overhead size (stuff up to CLUMP_OFFSET) * + clumptotal * + last clump marker (CHUNK_SIZE) * + any bytes we rounded off the end * = file size */ if (PMEM_CLUMP_OFFSET + clumptotal + (stbuf.st_size & (PMEM_CHUNK_SIZE - 1)) + PMEM_CHUNK_SIZE == stbuf.st_size) { DEBUG("section sizes correctly add up to file size"); } else { FATAL("CLUMP_OFFSET %d + clumptotal %lu + rounded %d + " "CHUNK_SIZE %d = %lu, (not st_size %lu)", PMEM_CLUMP_OFFSET, clumptotal, (stbuf.st_size & (PMEM_CHUNK_SIZE - 1)), PMEM_CHUNK_SIZE, PMEM_CLUMP_OFFSET + clumptotal + (stbuf.st_size & (PMEM_CHUNK_SIZE - 1)) + PMEM_CHUNK_SIZE, stbuf.st_size); } if (clp->size == 0) FATAL("no clumps found"); while (clp->size) { size_t sz = clp->size & ~PMEM_STATE_MASK; int state = clp->size & PMEM_STATE_MASK; DEBUG("[%u]clump size 0x%lx state %d", OFF(pmp, clp), sz, state); DEBUG("on: 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", clp->on[0].off, clp->on[0].ptr_, clp->on[1].off, clp->on[1].ptr_, clp->on[2].off, clp->on[2].ptr_); if (sz > stats[PMEM_STATE_UNUSED].largest) stats[PMEM_STATE_UNUSED].largest = sz; if (stats[PMEM_STATE_UNUSED].smallest == 0 || sz < stats[PMEM_STATE_UNUSED].smallest) stats[PMEM_STATE_UNUSED].smallest = sz; stats[PMEM_STATE_UNUSED].bytes += sz; stats[PMEM_STATE_UNUSED].count++; switch (state) { case PMEM_STATE_FREE: DEBUG("clump state: free"); ASSERTeq(clp->on[0].off, 0); ASSERTeq(clp->on[1].off, 0); ASSERTeq(clp->on[2].off, 0); break; case PMEM_STATE_RESERVED: DEBUG("clump state: reserved"); break; case PMEM_STATE_ACTIVATING: DEBUG("clump state: activating"); break; case PMEM_STATE_ACTIVE: DEBUG("clump state: active"); ASSERTeq(clp->on[0].off, 0); ASSERTeq(clp->on[1].off, 0); ASSERTeq(clp->on[2].off, 0); break; case PMEM_STATE_FREEING: DEBUG("clump state: freeing"); break; default: FATAL("unknown clump state: %d", state); } if (sz > stats[state].largest) stats[state].largest = sz; if (stats[state].smallest == 0 || sz < stats[state].smallest) stats[state].smallest = sz; stats[state].bytes += sz; stats[state].count++; clp = (struct clump *)((uintptr_t)clp + sz); DEBUG("next clp 0x%lx, offset 0x%lx", clp, OFF(pmp, clp)); } if (clp == lastclp) DEBUG("all clump space accounted for"); else FATAL("clump list stopped at %lx instead of %lx", clp, lastclp); if (munmap(pmp, stbuf.st_size) < 0) FATALSYS("munmap"); /* * print the report */ printf("Summary of pmem pool:\n"); printf("File size: %lu, %d allocatable bytes in pool\n\n", stbuf.st_size, clumptotal); printf(" State Bytes Clumps Largest Smallest\n"); for (i = 0; i < PMEM_STATE_UNUSED + 1; i++) { printf("%10s %10d %10d %10d %10d\n", names[i], stats[i].bytes, stats[i].count, stats[i].largest, stats[i].smallest); } }
/* * main() for the allocating thread. */ void * alloc_main( void *tparam ) { unsigned long thread_num = (unsigned long) tparam; int retval; void **mbx_list = (*mbx_array_ptr)[thread_num]; pthread_t free_thread; void *rel_mem_ptr_ = NULL; unsigned int malloc_size = 0; int mbx; DEBUG( "Enter alloc thread %d\n", thread_num ); retval = pthread_create( &free_thread, NULL, free_main, (void *) thread_num ); if (retval) { errno = retval; FATALSYS( "free thread create in thread %l\n", thread_num ); } /* * Wait for the starting gun to send all the threads * off and running.... */ pthread_mutex_lock( &start_lock ); while (!b_start_flag) { pthread_cond_wait( &start_cv, &start_lock ); } pthread_mutex_unlock( &start_lock ); /* * Until the parent thread says to stop just keep looping * through the mailboxes looking for empty ones. When one is * found, do a random sized alloc and put a pointer in the mailbox * for the freeing thread to free. */ while (!b_all_stop) { for (mbx=0; mbx<MAILBOXES; ++mbx) { if (mbx_list[mbx] == NULL) { malloc_size = (int)random() % max_malloc; if( (rel_mem_ptr_=pmemalloc_reserve( pmp, malloc_size )) != NULL ) { pmemalloc_onactive( pmp, rel_mem_ptr_, &mbx_list[mbx], rel_mem_ptr_ ); pmemalloc_activate( pmp, rel_mem_ptr_ ); DEBUG( "malloc %d bytes", malloc_size ); } else { /* * Malloc failed. * Sleep to let the Freeing thread catch up. */ DEBUG( "malloc failed for size %d\n", malloc_size ); sleep(0); } } /* end if mbx empty */ } /* end for each mbx */ } /* end while not all_stop */ /* * Time to stop. Wait for the freeing thread to complete. */ retval = pthread_join( free_thread, NULL ); if (retval) { errno = retval; FATALSYS( "Join with freeing thread %d\n", thread_num ); } pthread_exit( NULL ); }
int main(int argc, char *argv[]) { const char *path; int opt; int retval; unsigned long thrd; int mbx; void **sa_ptr; mailbox_array_t *mbx_offset_; Myname = argv[0]; while ((opt = getopt(argc, argv, "t:r:s:d")) != -1) { switch (opt) { case 't': if (sscanf(optarg, "%u", &num_threads) == EOF) { USAGE( "-t option error"); } if (num_threads > MAX_THREADS) { fprintf( stderr, "using max threads %d\n", MAX_THREADS ); num_threads = MAX_THREADS; } break; case 'r': if (sscanf(optarg, "%u", &runtime)==EOF) { USAGE("-r option error"); } break; case 's': if (sscanf(optarg, "%u", &max_malloc)==EOF) USAGE("-s option error"); break; case 'd': Debug=TRUE; break; default: USAGE(NULL); } } /* end while opt */ if (optind >= argc) USAGE("No path given"); path = argv[optind++]; if (optind < argc) USAGE(NULL); /* * Use the alloc_init lib function to open the pool * via pmfs, and map it into our address space. * This returns a regular (absolute) pointer. */ if ((pmp = pmemalloc_init(path, POOL_SIZE)) == NULL) FATALSYS("pmemalloc_init on %s", path); /* * Fetch our static info. * The first word is used to store a relative pointer to * the mailbox array. The library function converts this * to an absolute pointer. */ sa_ptr = (void**)pmemalloc_static_area(pmp); /* The static area for a new pmem pool is zero'd */ if (*sa_ptr == NULL) { /* * Create and initialize the mailbox array in PM */ if ((mbx_offset_=pmemalloc_reserve(pmp, sizeof(mailbox_array_t))) == NULL ) FATALSYS("pmemalloc mailbox array"); /* * Place a pointer to this array in the first word of the * static area on activation */ pmemalloc_onactive( pmp, mbx_offset_, (void**)sa_ptr, mbx_offset_ ); pmemalloc_activate( pmp, mbx_offset_ ); /* Set the static, regular pointer to be used in the program */ mbx_array_ptr = PMEM( pmp, mbx_offset_ ); for (thrd=0; thrd<MAX_THREADS; ++thrd) { for (mbx=0; mbx<MAILBOXES; ++mbx) { (*mbx_array_ptr)[thrd][mbx] = NULL; } } } else { /* * This region already exists from a previous run. * Free any pmem spaces still in the mailbox. */ mbx_array_ptr = PMEM( pmp, (mailbox_array_t*)*sa_ptr ); for (thrd=0; thrd<MAX_THREADS; ++thrd) { for (mbx=0; mbx<MAILBOXES; ++mbx) { if ((*mbx_array_ptr)[thrd][mbx] != NULL) { pmemalloc_onfree( pmp, (*mbx_array_ptr)[thrd][mbx], &(*mbx_array_ptr)[thrd][mbx], NULL ); pmemalloc_free( pmp, (*mbx_array_ptr)[thrd][mbx] ); } } } } /* Commit the initialized mailbox to persistent media */ pmem_persist( mbx_array_ptr, sizeof(mailbox_array_t), 0 ); DEBUG( "Number of threads = %d", num_threads); DEBUG( "Runtime: %d seconds", runtime ); DEBUG( "Max alloc size %d bytes", max_malloc ); /* * Create each allocating thread. Each allocating thread * will create its corresponding freeing thread. * Once each each thread is created, signal the start condition * so they all start running around the same time. */ for (thrd=0; thrd<num_threads; ++thrd) { retval = pthread_create( &alloc_threads[thrd], NULL, alloc_main, (void *) thrd ); if (retval) { errno = retval; FATALSYS( "alloc thread create %d\n", thrd ); } } /* Give the new threads a chance to start */ sleep(0); pthread_mutex_lock( &start_lock ); b_start_flag = TRUE; pthread_cond_broadcast( &start_cv ); pthread_mutex_unlock( &start_lock ); /* Let run for the desired seconds then tell all threads to stop */ sleep( runtime ); b_all_stop = TRUE; /* Wait for each alloating thread to complete. */ for (thrd=0; thrd<num_threads; ++thrd) { retval = pthread_join( alloc_threads[thrd], NULL ); if (retval) { errno = retval; FATALSYS( "Allocating thread JOIN %d", thrd ); } } /* Commit the final mailbox array to persistent media */ pmem_persist( mbx_array_ptr, sizeof(mailbox_array_t), 0 ); DEBUG("Done."); exit(0); }
int main(int argc, char *argv[]) { const char *path; int opt; int fflag = 0; int iflag = 0; unsigned long icount; void *pmp; struct static_info *sp; struct node *parent_; struct node *np_; Myname = argv[0]; while ((opt = getopt(argc, argv, "FMdfi:")) != -1) { switch (opt) { case 'F': pmem_fit_mode(); break; case 'M': pmem_msync_mode(); break; case 'd': Debug++; break; case 'f': fflag++; break; case 'i': iflag++; icount = strtoul(optarg, NULL, 10); break; default: USAGE(NULL); } } if (optind >= argc) USAGE("No path given"); path = argv[optind++]; if ((pmp = pmemalloc_init(path, MY_POOL_SIZE)) == NULL) FATALSYS("pmemalloc_init on %s", path); /* fetch our static info */ sp = (struct static_info *) pmemalloc_static_area(pmp); if (optind < argc) { /* numbers supplied as arguments? */ int i; if (fflag) USAGE("unexpected extra arguments given with -f flag"); if (iflag) icount_start(icount); /* start instruction count */ for (i = optind; i < argc; i++) { int value = atoi(argv[i]); if ((np_ = pmemalloc_reserve(pmp, sizeof(*np_))) == NULL) FATALSYS("pmemalloc_reserve"); /* link it in at the beginning of the list */ PMEM(pmp, np_)->next_ = sp->rootnp_; PMEM(pmp, np_)->value = value; pmemalloc_onactive(pmp, np_, (void **) &sp->rootnp_, np_); pmemalloc_activate(pmp, np_); } if (iflag) { icount_stop(); /* end instruction count */ printf("Total instruction count: %lu\n", icount_total()); } } else if (fflag) { /* * remove first item from list */ if (sp->rootnp_ == NULL) FATAL("the list is empty"); if (iflag) icount_start(icount); /* start instruction count */ np_ = sp->rootnp_; pmemalloc_onfree(pmp, np_, (void **) &sp->rootnp_, PMEM(pmp, np_)->next_); pmemalloc_free(pmp, np_); if (iflag) { icount_stop(); /* end instruction count */ printf("Total instruction count: %lu\n", icount_total()); } } else { char *sep = ""; /* * traverse the list, printing the numbers found */ np_ = sp->rootnp_; while (np_) { printf("%s%d", sep, PMEM(pmp, np_)->value); sep = " "; np_ = PMEM(pmp, np_)->next_; } printf("\n"); } DEBUG("Done."); exit(0); }