You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
894 lines
22 KiB
894 lines
22 KiB
/* A program to put stress on a POSIX system (stress).
|
|
*
|
|
* Copyright (C) 2001, 2002 Amos Waterland <awaterl@yahoo.com>
|
|
*
|
|
* This program 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 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <libgen.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <sys/wait.h>
|
|
|
|
/* By default, print all messages of severity info and above. */
|
|
static int global_debug = 2;
|
|
|
|
/* By default, just print warning for non-critical errors. */
|
|
static int global_ignore = 1;
|
|
|
|
/* By default, retry on non-critical errors every 50ms. */
|
|
static int global_retry = 50000;
|
|
|
|
/* By default, use this as backoff coefficient for good fork throughput. */
|
|
static int global_backoff = 3000;
|
|
|
|
/* By default, do not timeout. */
|
|
static int global_timeout = 0;
|
|
|
|
/* Name of this program */
|
|
static char *global_progname = PACKAGE;
|
|
|
|
/* By default, do not hang after allocating memory. */
|
|
static int global_vmhang = 0;
|
|
|
|
/* Implemention of runtime-selectable severity message printing. */
|
|
#define dbg if (global_debug >= 3) \
|
|
fprintf (stdout, "%s: debug: (%d) ", global_progname, __LINE__), \
|
|
fprintf
|
|
#define out if (global_debug >= 2) \
|
|
fprintf (stdout, "%s: info: ", global_progname), \
|
|
fprintf
|
|
#define wrn if (global_debug >= 1) \
|
|
fprintf (stderr, "%s: warn: (%d) ", global_progname, __LINE__), \
|
|
fprintf
|
|
#define err if (global_debug >= 0) \
|
|
fprintf (stderr, "%s: error: (%d) ", global_progname, __LINE__), \
|
|
fprintf
|
|
|
|
/* Implementation of check for option argument correctness. */
|
|
#define assert_arg(A) \
|
|
if (++i == argc || ((arg = argv[i])[0] == '-' && \
|
|
!isdigit ((int)arg[1]) )) \
|
|
{ \
|
|
err (stderr, "missing argument to option '%s'\n", A); \
|
|
exit (1); \
|
|
}
|
|
|
|
/* Prototypes for utility functions. */
|
|
int usage(int status);
|
|
int version(int status);
|
|
long long atoll_s(const char *nptr);
|
|
long long atoll_b(const char *nptr);
|
|
|
|
/* Prototypes for the worker functions. */
|
|
int hogcpu(long long forks);
|
|
int hogio(long long forks);
|
|
int hogvm(long long forks, long long chunks, long long bytes);
|
|
int hoghdd(long long forks, int clean, long long files, long long bytes);
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int i, pid, children = 0, retval = 0;
|
|
long starttime, stoptime, runtime;
|
|
|
|
/* Variables that indicate which options have been selected. */
|
|
int do_dryrun = 0;
|
|
int do_timeout = 0;
|
|
int do_cpu = 0; /* Default to 1 fork. */
|
|
long long do_cpu_forks = 1;
|
|
int do_io = 0; /* Default to 1 fork. */
|
|
long long do_io_forks = 1;
|
|
int do_vm = 0; /* Default to 1 fork, 1 chunk of 256MB. */
|
|
long long do_vm_forks = 1;
|
|
long long do_vm_chunks = 1;
|
|
long long do_vm_bytes = 256 * 1024 * 1024;
|
|
int do_hdd = 0; /* Default to 1 fork, clean, 1 file of 1GB. */
|
|
long long do_hdd_forks = 1;
|
|
int do_hdd_clean = 0;
|
|
long long do_hdd_files = 1;
|
|
long long do_hdd_bytes = 1024 * 1024 * 1024;
|
|
|
|
/* Record our start time. */
|
|
if ((starttime = time(NULL)) == -1) {
|
|
err(stderr, "failed to acquire current time\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* SuSv3 does not define any error conditions for this function. */
|
|
global_progname = basename(argv[0]);
|
|
|
|
/* For portability, parse command line options without getopt_long. */
|
|
for (i = 1; i < argc; i++) {
|
|
char *arg = argv[i];
|
|
|
|
if (strcmp(arg, "--help") == 0 || strcmp(arg, "-?") == 0) {
|
|
usage(0);
|
|
} else if (strcmp(arg, "--version") == 0) {
|
|
version(0);
|
|
} else if (strcmp(arg, "--verbose") == 0
|
|
|| strcmp(arg, "-v") == 0) {
|
|
global_debug = 3;
|
|
} else if (strcmp(arg, "--quiet") == 0
|
|
|| strcmp(arg, "-q") == 0) {
|
|
global_debug = 0;
|
|
} else if (strcmp(arg, "--dry-run") == 0
|
|
|| strcmp(arg, "-n") == 0) {
|
|
do_dryrun = 1;
|
|
} else if (strcmp(arg, "--no-retry") == 0) {
|
|
global_ignore = 0;
|
|
dbg(stdout,
|
|
"turning off ignore of non-critical errors");
|
|
} else if (strcmp(arg, "--retry-delay") == 0) {
|
|
assert_arg("--retry-delay");
|
|
global_retry = atoll(arg);
|
|
dbg(stdout, "setting retry delay to %dus\n",
|
|
global_retry);
|
|
} else if (strcmp(arg, "--backoff") == 0) {
|
|
assert_arg("--backoff");
|
|
global_backoff = atoll(arg);
|
|
if (global_backoff < 0) {
|
|
err(stderr, "invalid backoff factor: %i\n",
|
|
global_backoff);
|
|
exit(1);
|
|
}
|
|
dbg(stdout, "setting backoff coeffient to %dus\n",
|
|
global_backoff);
|
|
} else if (strcmp(arg, "--timeout") == 0
|
|
|| strcmp(arg, "-t") == 0) {
|
|
do_timeout = 1;
|
|
assert_arg("--timeout");
|
|
global_timeout = atoll_s(arg);
|
|
dbg(stdout, "setting timeout to %ds\n", global_timeout);
|
|
} else if (strcmp(arg, "--cpu") == 0 || strcmp(arg, "-c") == 0) {
|
|
do_cpu = 1;
|
|
assert_arg("--cpu");
|
|
do_cpu_forks = atoll_b(arg);
|
|
} else if (strcmp(arg, "--io") == 0 || strcmp(arg, "-i") == 0) {
|
|
do_io = 1;
|
|
assert_arg("--io");
|
|
do_io_forks = atoll_b(arg);
|
|
} else if (strcmp(arg, "--vm") == 0 || strcmp(arg, "-m") == 0) {
|
|
do_vm = 1;
|
|
assert_arg("--vm");
|
|
do_vm_forks = atoll_b(arg);
|
|
} else if (strcmp(arg, "--vm-chunks") == 0) {
|
|
assert_arg("--vm-chunks");
|
|
do_vm_chunks = atoll_b(arg);
|
|
} else if (strcmp(arg, "--vm-bytes") == 0) {
|
|
assert_arg("--vm-bytes");
|
|
do_vm_bytes = atoll_b(arg);
|
|
} else if (strcmp(arg, "--vm-hang") == 0) {
|
|
global_vmhang = 1;
|
|
} else if (strcmp(arg, "--hdd") == 0 || strcmp(arg, "-d") == 0) {
|
|
do_hdd = 1;
|
|
assert_arg("--hdd");
|
|
do_hdd_forks = atoll_b(arg);
|
|
} else if (strcmp(arg, "--hdd-noclean") == 0) {
|
|
do_hdd_clean = 2;
|
|
} else if (strcmp(arg, "--hdd-files") == 0) {
|
|
assert_arg("--hdd-files");
|
|
do_hdd_files = atoll_b(arg);
|
|
} else if (strcmp(arg, "--hdd-bytes") == 0) {
|
|
assert_arg("--hdd-bytes");
|
|
do_hdd_bytes = atoll_b(arg);
|
|
} else {
|
|
err(stderr, "unrecognized option: %s\n", arg);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/* Hog CPU option. */
|
|
if (do_cpu) {
|
|
out(stdout, "dispatching %lli hogcpu forks\n", do_cpu_forks);
|
|
|
|
switch (pid = fork()) {
|
|
case 0: /* child */
|
|
if (do_dryrun)
|
|
exit(0);
|
|
exit(hogcpu(do_cpu_forks));
|
|
case -1: /* error */
|
|
err(stderr, "hogcpu dispatcher fork failed\n");
|
|
exit(1);
|
|
default: /* parent */
|
|
children++;
|
|
dbg(stdout, "--> hogcpu dispatcher forked (%i)\n", pid);
|
|
}
|
|
}
|
|
|
|
/* Hog I/O option. */
|
|
if (do_io) {
|
|
out(stdout, "dispatching %lli hogio forks\n", do_io_forks);
|
|
|
|
switch (pid = fork()) {
|
|
case 0: /* child */
|
|
if (do_dryrun)
|
|
exit(0);
|
|
exit(hogio(do_io_forks));
|
|
case -1: /* error */
|
|
err(stderr, "hogio dispatcher fork failed\n");
|
|
exit(1);
|
|
default: /* parent */
|
|
children++;
|
|
dbg(stdout, "--> hogio dispatcher forked (%i)\n", pid);
|
|
}
|
|
}
|
|
|
|
/* Hog VM option. */
|
|
if (do_vm) {
|
|
out(stdout,
|
|
"dispatching %lli hogvm forks, each %lli chunks of %lli bytes\n",
|
|
do_vm_forks, do_vm_chunks, do_vm_bytes);
|
|
|
|
switch (pid = fork()) {
|
|
case 0: /* child */
|
|
if (do_dryrun)
|
|
exit(0);
|
|
exit(hogvm(do_vm_forks, do_vm_chunks, do_vm_bytes));
|
|
case -1: /* error */
|
|
err(stderr, "hogvm dispatcher fork failed\n");
|
|
exit(1);
|
|
default: /* parent */
|
|
children++;
|
|
dbg(stdout, "--> hogvm dispatcher forked (%i)\n", pid);
|
|
}
|
|
}
|
|
|
|
/* Hog HDD option. */
|
|
if (do_hdd) {
|
|
out(stdout, "dispatching %lli hoghdd forks, each %lli files of "
|
|
"%lli bytes\n", do_hdd_forks, do_hdd_files, do_hdd_bytes);
|
|
|
|
switch (pid = fork()) {
|
|
case 0: /* child */
|
|
if (do_dryrun)
|
|
exit(0);
|
|
exit(hoghdd
|
|
(do_hdd_forks, do_hdd_clean, do_hdd_files,
|
|
do_hdd_bytes));
|
|
case -1: /* error */
|
|
err(stderr, "hoghdd dispatcher fork failed\n");
|
|
exit(1);
|
|
default: /* parent */
|
|
children++;
|
|
dbg(stdout, "--> hoghdd dispatcher forked (%i)\n", pid);
|
|
}
|
|
}
|
|
|
|
/* We have no work to do, so bail out. */
|
|
if (children == 0)
|
|
usage(0);
|
|
|
|
/* Wait for our children to exit. */
|
|
while (children) {
|
|
int status, ret;
|
|
|
|
if ((pid = wait(&status)) > 0) {
|
|
if ((WIFEXITED(status)) != 0) {
|
|
if ((ret = WEXITSTATUS(status)) != 0) {
|
|
err(stderr,
|
|
"dispatcher %i returned error %i\n",
|
|
pid, ret);
|
|
retval += ret;
|
|
} else {
|
|
dbg(stdout,
|
|
"<-- dispatcher return (%i)\n",
|
|
pid);
|
|
}
|
|
} else {
|
|
err(stderr,
|
|
"dispatcher did not exit normally\n");
|
|
++retval;
|
|
}
|
|
|
|
--children;
|
|
} else {
|
|
dbg(stdout, "wait() returned error: %s\n",
|
|
strerror(errno));
|
|
err(stderr, "detected missing dispatcher children\n");
|
|
++retval;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Record our stop time. */
|
|
if ((stoptime = time(NULL)) == -1) {
|
|
err(stderr, "failed to acquire current time\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* Calculate our runtime. */
|
|
runtime = stoptime - starttime;
|
|
|
|
/* Print final status message. */
|
|
if (retval) {
|
|
err(stderr, "failed run completed in %lis\n", runtime);
|
|
} else {
|
|
out(stdout, "successful run completed in %lis\n", runtime);
|
|
}
|
|
|
|
exit(retval);
|
|
}
|
|
|
|
int usage(int status)
|
|
{
|
|
char *mesg =
|
|
"`%s' imposes certain types of compute stress on your system\n\n"
|
|
"Usage: %s [OPTION [ARG]] ...\n\n"
|
|
" -?, --help show this help statement\n"
|
|
" --version show version statement\n"
|
|
" -v, --verbose be verbose\n"
|
|
" -q, --quiet be quiet\n"
|
|
" -n, --dry-run show what would have been done\n"
|
|
" --no-retry exit rather than retry non-critical errors\n"
|
|
" --retry-delay n wait n us before continuing past error\n"
|
|
" -t, --timeout n timeout after n seconds\n"
|
|
" --backoff n wait for factor of n us before starting work\n"
|
|
" -c, --cpu n spawn n procs spinning on sqrt()\n"
|
|
" -i, --io n spawn n procs spinning on sync()\n"
|
|
" -m, --vm n spawn n procs spinning on malloc()\n"
|
|
" --vm-chunks c malloc c chunks (default is 1)\n"
|
|
" --vm-bytes b malloc chunks of b bytes (default is 256MB)\n"
|
|
" --vm-hang hang in a sleep loop after memory allocated\n"
|
|
" -d, --hdd n spawn n procs spinning on write()\n"
|
|
" --hdd-noclean do not unlink file to which random data written\n"
|
|
" --hdd-files f write to f files (default is 1)\n"
|
|
" --hdd-bytes b write b bytes (default is 1GB)\n\n"
|
|
"Infinity is denoted with 0. For -m, -d: n=0 means infinite redo,\n"
|
|
"n<0 means redo abs(n) times. Valid suffixes are m,h,d,y for time;\n"
|
|
"k,m,g for size.\n\n";
|
|
|
|
fprintf(stdout, mesg, global_progname, global_progname);
|
|
|
|
if (status <= 0)
|
|
exit(-1 * status);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int version(int status)
|
|
{
|
|
char *mesg = "%s %s\n";
|
|
|
|
fprintf(stdout, mesg, global_progname, VERSION);
|
|
|
|
if (status <= 0)
|
|
exit(-1 * status);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Convert a string representation of a number with an optional size suffix
|
|
* to a long long.
|
|
*/
|
|
long long atoll_b(const char *nptr)
|
|
{
|
|
int pos;
|
|
char suffix;
|
|
long long factor = 1;
|
|
|
|
if ((pos = strlen(nptr) - 1) < 0) {
|
|
err(stderr, "invalid string\n");
|
|
exit(1);
|
|
}
|
|
|
|
switch (suffix = nptr[pos]) {
|
|
case 'k':
|
|
case 'K':
|
|
factor = 1024;
|
|
break;
|
|
case 'm':
|
|
case 'M':
|
|
factor = 1024 * 1024;
|
|
break;
|
|
case 'g':
|
|
case 'G':
|
|
factor = 1024 * 1024 * 1024;
|
|
break;
|
|
default:
|
|
if (suffix < '0' || suffix > '9') {
|
|
err(stderr, "unrecognized suffix: %c\n", suffix);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
factor = atoll(nptr) * factor;
|
|
|
|
return factor;
|
|
}
|
|
|
|
/* Convert a string representation of a number with an optional time suffix
|
|
* to a long long.
|
|
*/
|
|
long long atoll_s(const char *nptr)
|
|
{
|
|
int pos;
|
|
char suffix;
|
|
long long factor = 1;
|
|
|
|
if ((pos = strlen(nptr) - 1) < 0) {
|
|
err(stderr, "invalid string\n");
|
|
exit(1);
|
|
}
|
|
|
|
switch (suffix = nptr[pos]) {
|
|
case 's':
|
|
case 'S':
|
|
factor = 1;
|
|
break;
|
|
case 'm':
|
|
case 'M':
|
|
factor = 60;
|
|
break;
|
|
case 'h':
|
|
case 'H':
|
|
factor = 60 * 60;
|
|
break;
|
|
case 'd':
|
|
case 'D':
|
|
factor = 60 * 60 * 24;
|
|
break;
|
|
case 'y':
|
|
case 'Y':
|
|
factor = 60 * 60 * 24 * 360;
|
|
break;
|
|
default:
|
|
if (suffix < '0' || suffix > '9') {
|
|
err(stderr, "unrecognized suffix: %c\n", suffix);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
factor = atoll(nptr) * factor;
|
|
|
|
return factor;
|
|
}
|
|
|
|
int hogcpu(long long forks)
|
|
{
|
|
long long i;
|
|
double d;
|
|
int pid, retval = 0;
|
|
|
|
/* Make local copies of global variables. */
|
|
int ignore = global_ignore;
|
|
int retry = global_retry;
|
|
int timeout = global_timeout;
|
|
long backoff = global_backoff * forks;
|
|
|
|
dbg(stdout, "using backoff sleep of %lius for hogcpu\n", backoff);
|
|
|
|
for (i = 0; forks == 0 || i < forks; i++) {
|
|
switch (pid = fork()) {
|
|
case 0: /* child */
|
|
alarm(timeout);
|
|
|
|
/* Use a backoff sleep to ensure we get good fork throughput. */
|
|
usleep(backoff);
|
|
|
|
while (1)
|
|
d = sqrt(rand());
|
|
|
|
/* This case never falls through; alarm signal can cause exit. */
|
|
case -1: /* error */
|
|
if (ignore) {
|
|
++retval;
|
|
wrn(stderr,
|
|
"hogcpu worker fork failed, continuing\n");
|
|
usleep(retry);
|
|
continue;
|
|
}
|
|
|
|
err(stderr, "hogcpu worker fork failed\n");
|
|
return 1;
|
|
default: /* parent */
|
|
dbg(stdout, "--> hogcpu worker forked (%i)\n", pid);
|
|
}
|
|
}
|
|
|
|
/* Wait for our children to exit. */
|
|
while (i) {
|
|
int status, ret;
|
|
|
|
if ((pid = wait(&status)) > 0) {
|
|
if ((WIFEXITED(status)) != 0) {
|
|
if ((ret = WEXITSTATUS(status)) != 0) {
|
|
err(stderr,
|
|
"hogcpu worker %i exited %i\n", pid,
|
|
ret);
|
|
retval += ret;
|
|
} else {
|
|
dbg(stdout,
|
|
"<-- hogcpu worker exited (%i)\n",
|
|
pid);
|
|
}
|
|
} else {
|
|
dbg(stdout,
|
|
"<-- hogcpu worker signalled (%i)\n", pid);
|
|
}
|
|
|
|
--i;
|
|
} else {
|
|
dbg(stdout, "wait() returned error: %s\n",
|
|
strerror(errno));
|
|
err(stderr,
|
|
"detected missing hogcpu worker children\n");
|
|
++retval;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int hogio(long long forks)
|
|
{
|
|
long long i;
|
|
int pid, retval = 0;
|
|
|
|
/* Make local copies of global variables. */
|
|
int ignore = global_ignore;
|
|
int retry = global_retry;
|
|
int timeout = global_timeout;
|
|
long backoff = global_backoff * forks;
|
|
|
|
dbg(stdout, "using backoff sleep of %lius for hogio\n", backoff);
|
|
|
|
for (i = 0; forks == 0 || i < forks; i++) {
|
|
switch (pid = fork()) {
|
|
case 0: /* child */
|
|
alarm(timeout);
|
|
|
|
/* Use a backoff sleep to ensure we get good fork throughput. */
|
|
usleep(backoff);
|
|
|
|
while (1)
|
|
sync();
|
|
|
|
/* This case never falls through; alarm signal can cause exit. */
|
|
case -1: /* error */
|
|
if (ignore) {
|
|
++retval;
|
|
wrn(stderr,
|
|
"hogio worker fork failed, continuing\n");
|
|
usleep(retry);
|
|
continue;
|
|
}
|
|
|
|
err(stderr, "hogio worker fork failed\n");
|
|
return 1;
|
|
default: /* parent */
|
|
dbg(stdout, "--> hogio worker forked (%i)\n", pid);
|
|
}
|
|
}
|
|
|
|
/* Wait for our children to exit. */
|
|
while (i) {
|
|
int status, ret;
|
|
|
|
if ((pid = wait(&status)) > 0) {
|
|
if ((WIFEXITED(status)) != 0) {
|
|
if ((ret = WEXITSTATUS(status)) != 0) {
|
|
err(stderr,
|
|
"hogio worker %i exited %i\n", pid,
|
|
ret);
|
|
retval += ret;
|
|
} else {
|
|
dbg(stdout,
|
|
"<-- hogio worker exited (%i)\n",
|
|
pid);
|
|
}
|
|
} else {
|
|
dbg(stdout, "<-- hogio worker signalled (%i)\n",
|
|
pid);
|
|
}
|
|
|
|
--i;
|
|
} else {
|
|
dbg(stdout, "wait() returned error: %s\n",
|
|
strerror(errno));
|
|
err(stderr, "detected missing hogio worker children\n");
|
|
++retval;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int hogvm(long long forks, long long chunks, long long bytes)
|
|
{
|
|
long long i, j, k;
|
|
int pid, retval = 0;
|
|
char **ptr;
|
|
|
|
/* Make local copies of global variables. */
|
|
int ignore = global_ignore;
|
|
int retry = global_retry;
|
|
int timeout = global_timeout;
|
|
long backoff = global_backoff * forks;
|
|
|
|
dbg(stdout, "using backoff sleep of %lius for hogvm\n", backoff);
|
|
|
|
if (bytes == 0) {
|
|
/* 512MB is guess at the largest value can than be malloced at once. */
|
|
bytes = 512 * 1024 * 1024;
|
|
}
|
|
|
|
for (i = 0; forks == 0 || i < forks; i++) {
|
|
switch (pid = fork()) {
|
|
case 0: /* child */
|
|
alarm(timeout);
|
|
|
|
/* Use a backoff sleep to ensure we get good fork throughput. */
|
|
usleep(backoff);
|
|
|
|
while (1) {
|
|
ptr = (char **)malloc(chunks * 2);
|
|
for (j = 0; chunks == 0 || j < chunks; j++) {
|
|
if ((ptr[j] =
|
|
(char *)malloc(bytes *
|
|
sizeof(char)))) {
|
|
for (k = 0; k < bytes; k++)
|
|
ptr[j][k] = 'Z'; /* Ensure that COW happens. */
|
|
dbg(stdout,
|
|
"hogvm worker malloced %lli bytes\n",
|
|
k);
|
|
} else if (ignore) {
|
|
++retval;
|
|
wrn(stderr,
|
|
"hogvm malloc failed, continuing\n");
|
|
usleep(retry);
|
|
continue;
|
|
} else {
|
|
++retval;
|
|
err(stderr,
|
|
"hogvm malloc failed\n");
|
|
break;
|
|
}
|
|
}
|
|
if (global_vmhang && retval == 0) {
|
|
dbg(stdout,
|
|
"sleeping forever with allocated memory\n");
|
|
while (1)
|
|
sleep(1024);
|
|
}
|
|
if (retval == 0) {
|
|
dbg(stdout,
|
|
"hogvm worker freeing memory and starting over\n");
|
|
for (j = 0; chunks == 0 || j < chunks;
|
|
j++) {
|
|
free(ptr[j]);
|
|
}
|
|
free(ptr);
|
|
continue;
|
|
}
|
|
|
|
exit(retval);
|
|
}
|
|
|
|
/* This case never falls through; alarm signal can cause exit. */
|
|
case -1: /* error */
|
|
if (ignore) {
|
|
++retval;
|
|
wrn(stderr,
|
|
"hogvm worker fork failed, continuing\n");
|
|
usleep(retry);
|
|
continue;
|
|
}
|
|
|
|
err(stderr, "hogvm worker fork failed\n");
|
|
return 1;
|
|
default: /* parent */
|
|
dbg(stdout, "--> hogvm worker forked (%i)\n", pid);
|
|
}
|
|
}
|
|
|
|
/* Wait for our children to exit. */
|
|
while (i) {
|
|
int status, ret;
|
|
|
|
if ((pid = wait(&status)) > 0) {
|
|
if ((WIFEXITED(status)) != 0) {
|
|
if ((ret = WEXITSTATUS(status)) != 0) {
|
|
err(stderr,
|
|
"hogvm worker %i exited %i\n", pid,
|
|
ret);
|
|
retval += ret;
|
|
} else {
|
|
dbg(stdout,
|
|
"<-- hogvm worker exited (%i)\n",
|
|
pid);
|
|
}
|
|
} else {
|
|
dbg(stdout, "<-- hogvm worker signalled (%i)\n",
|
|
pid);
|
|
}
|
|
|
|
--i;
|
|
} else {
|
|
dbg(stdout, "wait() returned error: %s\n",
|
|
strerror(errno));
|
|
err(stderr, "detected missing hogvm worker children\n");
|
|
++retval;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int hoghdd(long long forks, int clean, long long files, long long bytes)
|
|
{
|
|
long long i, j;
|
|
int fd, pid, retval = 0;
|
|
int chunk = (1024 * 1024) - 1; /* Minimize slow writing. */
|
|
char buff[chunk];
|
|
|
|
/* Make local copies of global variables. */
|
|
int ignore = global_ignore;
|
|
int retry = global_retry;
|
|
int timeout = global_timeout;
|
|
long backoff = global_backoff * forks;
|
|
|
|
/* Initialize buffer with some random ASCII data. */
|
|
dbg(stdout, "seeding buffer with random data\n");
|
|
for (i = 0; i < chunk - 1; i++) {
|
|
j = rand();
|
|
j = (j < 0) ? -j : j;
|
|
j %= 95;
|
|
j += 32;
|
|
buff[i] = j;
|
|
}
|
|
buff[i] = '\n';
|
|
|
|
dbg(stdout, "using backoff sleep of %lius for hoghdd\n", backoff);
|
|
|
|
for (i = 0; forks == 0 || i < forks; i++) {
|
|
switch (pid = fork()) {
|
|
case 0: /* child */
|
|
alarm(timeout);
|
|
|
|
/* Use a backoff sleep to ensure we get good fork throughput. */
|
|
usleep(backoff);
|
|
|
|
while (1) {
|
|
for (i = 0; i < files; i++) {
|
|
char name[] = "./stress.XXXXXX";
|
|
|
|
if ((fd = mkstemp(name)) < 0) {
|
|
perror("mkstemp");
|
|
err(stderr, "mkstemp failed\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (clean == 0) {
|
|
dbg(stdout, "unlinking %s\n",
|
|
name);
|
|
if (unlink(name)) {
|
|
err(stderr,
|
|
"unlink failed\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
dbg(stdout, "fast writing to %s\n",
|
|
name);
|
|
for (j = 0;
|
|
bytes == 0 || j + chunk < bytes;
|
|
j += chunk) {
|
|
if (write(fd, buff, chunk) !=
|
|
chunk) {
|
|
err(stderr,
|
|
"write failed\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
dbg(stdout, "slow writing to %s\n",
|
|
name);
|
|
for (; bytes == 0 || j < bytes - 1; j++) {
|
|
if (write(fd, "Z", 1) != 1) {
|
|
err(stderr,
|
|
"write failed\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
if (write(fd, "\n", 1) != 1) {
|
|
err(stderr, "write failed\n");
|
|
exit(1);
|
|
}
|
|
++j;
|
|
|
|
dbg(stdout,
|
|
"closing %s after writing %lli bytes\n",
|
|
name, j);
|
|
close(fd);
|
|
|
|
if (clean == 1) {
|
|
if (unlink(name)) {
|
|
err(stderr,
|
|
"unlink failed\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
if (retval == 0) {
|
|
dbg(stdout,
|
|
"hoghdd worker starting over\n");
|
|
continue;
|
|
}
|
|
|
|
exit(retval);
|
|
}
|
|
|
|
/* This case never falls through; alarm signal can cause exit. */
|
|
case -1: /* error */
|
|
if (ignore) {
|
|
++retval;
|
|
wrn(stderr,
|
|
"hoghdd worker fork failed, continuing\n");
|
|
usleep(retry);
|
|
continue;
|
|
}
|
|
|
|
err(stderr, "hoghdd worker fork failed\n");
|
|
return 1;
|
|
default: /* parent */
|
|
dbg(stdout, "--> hoghdd worker forked (%i)\n", pid);
|
|
}
|
|
}
|
|
|
|
/* Wait for our children to exit. */
|
|
while (i) {
|
|
int status, ret;
|
|
|
|
if ((pid = wait(&status)) > 0) {
|
|
if ((WIFEXITED(status)) != 0) {
|
|
if ((ret = WEXITSTATUS(status)) != 0) {
|
|
err(stderr,
|
|
"hoghdd worker %i exited %i\n", pid,
|
|
ret);
|
|
retval += ret;
|
|
} else {
|
|
dbg(stdout,
|
|
"<-- hoghdd worker exited (%i)\n",
|
|
pid);
|
|
}
|
|
} else {
|
|
dbg(stdout,
|
|
"<-- hoghdd worker signalled (%i)\n", pid);
|
|
}
|
|
|
|
--i;
|
|
} else {
|
|
dbg(stdout, "wait() returned error: %s\n",
|
|
strerror(errno));
|
|
err(stderr,
|
|
"detected missing hoghdd worker children\n");
|
|
++retval;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|