/* vmallocate -- a utility to allocate memory.
Copyright (C) 2015,2016 Free Software Foundation, Inc.
This file is part of the GNU Hurd.
The GNU Hurd is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2, or (at
your option) any later version.
The GNU Hurd 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
General Public License for more details.
You should have received a copy of the GNU General Public License
along with the GNU Hurd. If not, see . */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
const char *argp_program_version = STANDARD_HURD_VERSION (vmallocate);
int verbose;
int do_pause;
int do_read;
int do_write;
uint64_t size;
uint64_t chunk_size = 1U << 30; /* 1 gigabyte */
uint64_t allocated;
static const struct argp_option options[] =
{
{NULL, 0, NULL, OPTION_DOC, "Mapping options", 1},
{"read", 'r', NULL, 0, "read from mapping", 1},
{"write", 'w', NULL, 0, "write to mapping", 1},
{NULL, 0, NULL, OPTION_DOC, "Options", 2},
{"verbose", 'v', NULL, 0, "be more verbose", 2},
{"pause", 'p', NULL, 0, "read newline from stdin before exiting", 2},
{0}
};
#define UNITS "gGmMkKpP"
static const char args_doc[] = "SIZE["UNITS"]";
static const char doc[] = "Allocates memory.\v"
"This is a stress-test for the vm system.";
/* Parse our options... */
error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
char *end;
switch (key)
{
case 'r':
do_read = 1;
break;
case 'w':
do_write = 1;
break;
case 'v':
verbose += 1;
break;
case 'p':
do_pause = 1;
break;
case ARGP_KEY_ARG:
if (size > 0)
argp_error (state, "Extra argument after size: %s", arg);
size = strtoull (arg, &end, 10);
if (arg == end || (end[0] != 0 && end[1] != 0))
argp_error (state, "Could not parse size `%s'.", arg);
switch (end[0]) {
case 0:
break;
case 'g':
case 'G':
size <<= 30;
break;
case 'm':
case 'M':
size <<= 20;
break;
case 'k':
case 'K':
size <<= 10;
break;
case 'p':
case 'P':
size <<= PAGE_SHIFT;
break;
default:
argp_error (state,
"Unknown unit `%c', expected one of "UNITS".",
end[0]);
}
break;
case ARGP_KEY_NO_ARGS:
argp_usage (state);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
const struct argp argp =
{
options: options,
parser: parse_opt,
args_doc: args_doc,
doc: doc,
};
#define min(a, b) ((a) < (b) ? (a) : (b))
struct child
{
task_t task;
struct child *next;
};
int
main (int argc, char **argv)
{
error_t err;
int nchildren = 0;
struct child *c, *children = NULL;
process_t proc = getproc ();
/* We must make sure that chunk_size fits into vm_size_t. */
assert (chunk_size <= 1U << (sizeof (vm_size_t) * 8 - 1));
/* Parse our arguments. */
argp_parse (&argp, argc, argv, 0, 0, 0);
if (verbose)
fprintf (stderr, "About to allocate %"PRIu64" bytes in chunks of "
"%"PRIu64" bytes.\n", size, chunk_size);
while (allocated < size)
{
task_t task;
uint64_t s = min (chunk_size, size - allocated);
vm_address_t address = 0;
volatile char *p;
char **argv = NULL;
err = vm_allocate (mach_task_self (), &address, (vm_size_t) s,
1 /* anywhere */);
if (err)
error (1, err, "vm_allocate");
/* Write an argument and environment vector. */
if (s > 128)
{
int written;
char *arg0;
arg0 = (char *) address;
written = sprintf (arg0, "[vmallocate %d]", nchildren);
argv = (char **) (address + (unsigned) written + 1);
argv[0] = arg0;
argv[1] = NULL;
}
for (p = (char *) address + PAGE_SIZE;
p < (char *) (address + (size_t) s);
p += PAGE_SIZE)
{
if (do_read)
(void) *p;
if (do_write)
*p = 1;
if (verbose > 1
&& ((unsigned int) (p - address) & ((1U<<20) - 1)) == 0)
fprintf (stderr, "\r%"PRIu64,
allocated
+ ((uint64_t) (uintptr_t) p - (uint64_t) address));
}
err = vm_inherit (mach_task_self (), address, (vm_size_t) s,
VM_INHERIT_COPY);
if (err)
error (1, err, "vm_inherit");
err = task_create (mach_task_self (), 1, &task);
if (err)
error (1, err, "task_create");
err = proc_child (proc, task);
if (err)
error (1, err, "proc_child");
if (argv != NULL)
{
process_t childp;
err = proc_task2proc (proc, task, &childp);
if (err)
error (1, err, "proc_task2proc");
err = proc_set_arg_locations (childp,
(vm_offset_t) &argv[0],
(vm_offset_t) &argv[1]);
if (err)
error (1, err, "proc_set_arg_locations");
}
c = malloc (sizeof *c);
if (c == NULL)
error (1, errno, "malloc");
c->task = task;
c->next = children;
children = c;
err = vm_deallocate (mach_task_self (), address, (vm_size_t) s);
if (err)
error (1, err, "vm_deallocate");
allocated += s;
nchildren += 1;
if (verbose)
fprintf (stderr, "\rAllocated %"PRIu64" bytes.\n", allocated);
}
if (do_pause)
{
fprintf (stderr, "Press enter to exit and release the memory.");
getchar ();
}
for (c = children; c; c = c->next)
task_terminate (c->task);
return EXIT_SUCCESS;
}