/* vmabuse.c - abuse virtual memory

Runs on Linux.

On a system with 4GB of RAM, I first allocate 2GB of System V shared
memory.  Then I run this program, giving the paths of two 1GB files.

gcc -g -W -Wall vmabuse.c -lpthread -o vmabuse
gcc -g -W -Wall -Xlinker -T -Xlinker loadlow.lds vmabuse.c -lpthread -o vmabuse

*/

#define _XOPEN_SOURCE 500

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/times.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

#define FDSIZ 32
#define NTHR 32
#define HUGE (64 * 1024 * 1024)

void       *addrs[FDSIZ];
int         fds[FDSIZ];
off_t       lens[FDSIZ];
int         nfds  = 0;
pthread_t   tids[NTHR];


static void *
readWorker(void *arg)
{
  int              n  = (int) arg;
  int              fd;
  char            *buf;
  size_t           chunk;
  size_t           num;
  ssize_t          res;
  off_t            len;
  off_t            off;
  unsigned short   xi[3];

  setvbuf(stdout, NULL, _IONBF, 0);

  fd = fds[n];
  len = lens[n];
  chunk = HUGE;
  if (chunk > len)
    chunk = len;
  
  xi[0] = getpid();
  xi[1] = time(NULL);
  xi[2] = times(NULL);

  for (;;) {
    num = nrand48(xi) % chunk;
    off = nrand48(xi) % (len - num);
    buf = malloc(num);
    if (!buf)
      printf("malloc failure %u\n", num);
    else {
      res = pread(fd, buf, num, off);
      if (res < 0)
        printf("pread failure: %s\n", strerror(errno));
      free(buf);
    }
  }

  return NULL;
}


static void *
mapWorker(void *arg)
{
  int              n  = (int) arg;
  void            *addr;
  char            *buf;
  size_t           chunk;
  size_t           num;
  off_t            len;
  off_t            off;
  unsigned short   xi[3];

  setvbuf(stdout, NULL, _IONBF, 0);

  addr = addrs[n];
  len = lens[n];
  chunk = HUGE;
  if (chunk > len)
    chunk = len;
  
  xi[0] = getpid();
  xi[1] = time(NULL);
  xi[2] = times(NULL);

  for (;;) {
    num = nrand48(xi) % chunk;
    off = nrand48(xi) % (len - num);
    buf = malloc(num);
    if (!buf)
      printf("malloc failure %u\n", num);
    else {
      memcpy(buf, (char *) addr + off, num);
      free(buf);
    }
  }

  return NULL;
}


int
main(int argc, char **argv)
{
  int              i;
  int              n;
  int              fd;
  unsigned short   xi[3];
  void            *addr;
  off_t            len;
  pthread_attr_t   attr;

  if (argc < 2) {
    printf("usage: %s <file1> <file2> ...\n", argv[0]);
    goto abort;
  }

  for (i = 1; i < argc; ++i) {
    if (nfds < FDSIZ) {
      fd = open(argv[i], O_RDONLY);
      if (fd < 0) {
        printf("can't open %s: %s\n", argv[i], strerror(errno));
        goto abort;
      }
      len = lseek(fd, 0, SEEK_END);
      lseek(fd, 0, SEEK_SET);
      addr = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
      if (addr == MAP_FAILED) {
        printf("can't mmap %s, %lu: %s\n",
               argv[i], len, strerror(errno));
        goto abort;
      }
      addrs[nfds] = addr;
      lens[nfds] = len;
      fds[nfds++] = fd;
    }
  }

  xi[0] = getpid();
  xi[1] = time(NULL);
  xi[2] = times(NULL);

  pthread_attr_init(&attr);
  pthread_attr_setstacksize(&attr, 1048576);

  for (i = 0; i < NTHR; ++i) {
    n = nrand48(xi) % nfds;
    if (i & 1)
      pthread_create(tids + i, &attr, mapWorker, (void *) n);
    else
      pthread_create(tids + i, &attr, readWorker, (void *) n);
  }

  for (i = 0; i < NTHR; ++i)
    pthread_join(tids[i], NULL);

  return 0;

  abort:
    return 1;
}
