Пример #1
0
/*
 * 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);
	}
}
Пример #2
0
/*
 * 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;
}
Пример #3
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);
}
Пример #4
0
/*
 * 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");
}
Пример #5
0
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);
}
Пример #6
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, &regs) < 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);
}
Пример #7
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);
	}
}
Пример #8
0
/*
 * 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 );
}
Пример #9
0
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);
}
Пример #10
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);
}