blob: 244bc64c6e4d8b5724d40e14259e1f8326d92bcc [file] [log] [blame]
/*
* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/*
* Continually writes a large file until user breaks out of test.
* This is to "hammer" the storage device.
* Prints rate of writes.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <style.h>
#include <eprintf.h>
#include <puny.h>
enum { FILE_SIZE_MEG = 1 << 12,
BUF_SIZE = 1 << 12,
WINDOW_SIZE = 20 };
u8 Buf[BUF_SIZE];
unint Bufs_written;
bool Done = FALSE;
bool Keep = FALSE;
void pr_human(double x)
{
char *suffix;
u64 y;
if (x >= EXBI) {
suffix = "EiB";
y = EXBI;
} else if (x >= PEBI) {
suffix = "PiB";
y = PEBI;
} else if (x >= TEBI) {
suffix = "TiB";
y = TEBI;
} else if (x >= GIBI) {
suffix = "GiB";
y = GIBI;
} else if (x >= MEBI) {
suffix = "MiB";
y = MEBI;
} else if (x >= KIBI) {
suffix = "KiB";
y = KIBI;
} else {
suffix = "B";
y = 1;
}
printf("%7.1f %s/sec", x / y, suffix);
}
void *writer(void *arg)
{
int fd;
int rc;
u64 size = 0;
u64 max_size = Option.file_size * MEBI;
fd = open(Option.file, O_WRONLY | O_CREAT | O_TRUNC, 0700);
if (fd == -1) fatal("open %s:", Option.file);
if (!Keep) unlink(Option.file);
for (;;) {
rc = write(fd, Buf, sizeof(Buf));
if (rc == -1) {
fatal("unexpected write error %s:", Option.file);
}
if (rc != sizeof(Buf)) {
fatal("didn't write all the data %d:", rc);
}
++Bufs_written;
size += rc;
if (size >= max_size) {
fsync(fd);
rc = lseek(fd, 0, 0);
if (rc == -1) {
fatal("lseek:");
}
size = 0;
}
}
Done = TRUE;
return NULL;
}
void pr_bytes_per_sec (double bytes_per_sec)
{
static double window[WINDOW_SIZE] = { 0 };
static double *next = window;
static double num_windows = 0;
static double window_total = 0;
static double num = 0;
static double total = 0;
double window_avg;
double avg;
++num;
total += bytes_per_sec;
avg = total / num;
if (next == &window[WINDOW_SIZE]) {
next = window;
}
if (num_windows != WINDOW_SIZE) {
++num_windows;
}
/*
* Subtract the oldest window entry from the total
* before overwriting it with the new bytes_per_sec.
*/
window_total -= *next;
window_total += bytes_per_sec;
*next++ = bytes_per_sec;
window_avg = window_total / num_windows;
pr_human(bytes_per_sec);
pr_human(window_avg);
pr_human(avg);
}
void *timer(void *arg)
{
struct timespec sleep = { Option.sleep_secs, 0 * ONE_MILLION };
unint old_bufs_written;
u64 delta;
u64 i;
printf("secs bytes/sec window avg(%2d) avg\n", WINDOW_SIZE);
for (i = 0; !Done; i++) {
old_bufs_written = Bufs_written;
nanosleep(&sleep, NULL);
delta = Bufs_written - old_bufs_written;
printf("%4llu. ", i);
pr_bytes_per_sec((double)(delta * sizeof(Buf)) /
Option.sleep_secs);
printf("\n");
}
return NULL;
}
void usage(void)
{
pr_usage("-k -f<file> -s<secs to sleep> -z<size in MiBs>\n"
" hammer writes continuously to a large file\n"
" It does an fsync when it reaches the specified\n"
" size then starts over. It displays the write rate\n"
" in seconds\n"
"\tk - keep file used to hammer the disk\n"
"\tf - file path to hammer the disk\n"
"\ts - seconds to sleep between reports\n"
"\tz - size in MiBs of file to hammer system");
}
bool myopt(int c)
{
switch (c) {
case 'k':
Keep = TRUE;
break;
default:
return FALSE;
}
return TRUE;
}
int main(int argc, char *argv[])
{
pthread_t timer_thread;
pthread_t writer_thread;
int rc;
Option.file_size = FILE_SIZE_MEG;
Option.sleep_secs = 1;
punyopt(argc, argv, myopt, "k");
rc = pthread_create(&timer_thread, NULL, timer, NULL);
if (rc) fatal("timer thread:");
rc = pthread_create(&writer_thread, NULL, writer, NULL);
if (rc) fatal("writer thread:");
pthread_join(writer_thread, NULL);
pthread_join(timer_thread, NULL);
return 0;
}