/*
* Copyright (c) 2004, 2005 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
#include "sm/generic.h"
SM_IDSTR(id, "@(#)$Id: t-readwrite.c,v 1.6 2006/07/16 02:07:39 ca Exp $")
#include "sm/assert.h"
#include "sm/fcntl.h"
#include "sm/io.h"
#include "sm/string.h"
#include "sm/wait.h"
#include "sm/resource.h"
#include "sm/test.h"
/*
** Write and read a file.
** This is just a very simple test to see whether an OS caches the file
** data after it has been written.
** This can be used for two test cases:
** write a file and read it back in the same process: this shouldn't cause
** any disk reads;
** write a file and read it back in a different process: this shouldn't cause
** any disk reads either.
** See also rdwr.sh
**
** Optional this program can write several files from several (sub)processes.
** This is useful to figure out whether it is more efficient to write files
** from just a single process or from multiple processes. In the latter case
** the OS (FS) might be able to "group" operations such that they are more
** efficient when performing disk I/O.
*/
#define MAXFN 128
#define MAX_BUFSZ (8 * 1024)
uchar buf[MAX_BUFSZ];
/*
** READFILE -- read data from file
**
** Parameters:
** fn -- file name context
**
** Returns:
** usual sm_error code
*/
static int
readfile(char *fn)
{
sm_ret_T res;
sm_file_T *fp;
size_t s;
ssize_t rd;
SM_REQUIRE(fn != NULL);
res = sm_io_open(SmStStdio, fn, SM_IO_RDONLY, &fp, SM_IO_WHAT_END);
SM_TEST(res == SM_SUCCESS);
SM_TEST(fp != NULL);
if (fp != NULL)
{
s = sizeof(buf);
do
{
res = sm_io_read(fp, buf, s, &rd);
if (res == SM_SUCCESS)
{
SM_TEST(res == SM_SUCCESS);
SM_TEST(rd >= 0);
}
else
{
SM_TEST(res == SM_IO_EOF);
SM_TEST(rd == 0);
}
} while (rd > 0);
res = sm_io_close(fp, SM_IO_CF_NONE);
SM_TEST(res == SM_SUCCESS);
}
return res;
}
/*
** WRITEFILE -- write data to file
**
** Parameters:
** fn -- file name context
** sync -- call fsync()?
**
** Returns:
** usual sm_error code
*/
static int
writefile(char *fn, bool sync)
{
sm_ret_T res;
ssize_t n;
size_t s, i;
int r;
sm_file_T *fp;
SM_REQUIRE(fn != NULL);
res = sm_io_open(SmStStdio, fn, SM_IO_WRONLY, &fp, SM_IO_WHAT_END);
SM_TEST(res == SM_SUCCESS);
SM_TEST(fp != NULL);
if (fp != NULL)
{
s = sizeof(buf);
for (i = 0; i < s; i++)
buf[i] = 'a' + (i & 0x1f);
res = sm_io_write(fp, buf, s, &n);
SM_TEST(res == SM_SUCCESS);
SM_TEST(n == (ssize_t) s);
if (sync)
{
r = fsync(f_fd(*fp));
SM_TEST(r == 0);
}
res = sm_io_close(fp, SM_IO_CF_NONE);
SM_TEST(res == SM_SUCCESS);
}
return res;
}
/*
** WRITEMANY -- write many files
**
** Parameters:
** base -- start of filename
** n -- number of files to write
** sync -- call fsync()?
**
** Returns:
** usual sm_error code
*/
static int
writemany(char *base, int n, int p, bool sync)
{
int i, ret;
char fn[MAXFN];
ret = SM_SUCCESS;
for (i = 0; i < n; i++)
{
sm_snprintf(fn, sizeof(fn), "%s-%04d-%04d", base, i, p);
ret = writefile(fn, sync);
if (ret != SM_SUCCESS)
break;
}
return ret;
}
/*
** STARTPROC -- start several processes
**
** Parameters:
** base -- start of filename
** nproc -- number of processes to write
** nfiles -- number of files to write
** sync -- call fsync()?
**
** Returns:
** usual sm_error code
*/
static int
startproc(char *base, int nproc, int nfiles, bool sync)
{
int i, ret;
pid_t pid, *pids;
struct rusage rusage;
ret = SM_SUCCESS;
pids = malloc(sizeof(int) * nproc);
SM_TEST(pids != NULL);
if (pids == NULL)
return ENOMEM;
for (i = 0; i < nproc; i++)
{
pid = fork();
if (pid < 0)
{
ret = errno;
break;
}
else if (pid == 0)
{
return writemany(base, nfiles, i, sync);
}
else if (pid > 0)
{
pids[i] = pid;
}
}
for (i = 0; i < nproc; i++)
{
pid = wait(&ret);
if (pid < 0)
{
ret = errno;
break;
}
}
if (getrusage(RUSAGE_CHILDREN, &rusage) == 0)
{
sm_io_fprintf(smioerr,
"ru_utime= %7ld.%07ld\n"
"ru_stime= %7ld.%07ld\n"
"ru_maxrss= %7ld\n"
"ru_ixrss= %7ld\n"
"ru_idrss= %7ld\n"
"ru_isrss= %7ld\n"
"ru_minflt= %7ld\n"
"ru_majflt= %7ld\n"
"ru_nswap= %7ld\n"
"ru_inblock= %7ld\n"
"ru_oublock= %7ld\n"
"ru_msgsnd= %7ld\n"
"ru_msgrcv= %7ld\n"
"ru_nsignals=%7ld\n"
"ru_nvcsw= %7ld\n"
"ru_nivcsw= %7ld\n"
, rusage.ru_utime.tv_sec
, rusage.ru_utime.tv_usec
, rusage.ru_stime.tv_sec
, rusage.ru_stime.tv_usec
, rusage.ru_maxrss
, rusage.ru_ixrss
, rusage.ru_idrss
, rusage.ru_isrss
, rusage.ru_minflt
, rusage.ru_majflt
, rusage.ru_nswap
, rusage.ru_inblock
, rusage.ru_oublock
, rusage.ru_msgsnd
, rusage.ru_msgrcv
, rusage.ru_nsignals
, rusage.ru_nvcsw
, rusage.ru_nivcsw
);
}
return ret;
}
static void
usage(void)
{
sm_io_fprintf(smioerr,
"usage: t-readwrite [options] [filename]\n"
"-b write and read file\n"
"-f n number of files to write\n"
"-p n number of processes to use\n"
"-s call fsync(2) after write\n"
"-w write only\n"
);
}
int
main(int argc, char *argv[])
{
int c;
char *fn;
bool wr, rd, sync;
int nproc, nfiles;
wr = false;
rd = true;
sync = false;
fn = "file";
nfiles = nproc = 0;
while ((c = getopt(argc, argv, "bf:p:sw")) != -1)
{
switch (c)
{
case 'b':
wr = true;
break;
case 'f':
nfiles = atoi(optarg);
break;
case 'p':
nproc = atoi(optarg);
break;
case 's':
sync = true;
break;
case 'w':
wr = true;
rd = false;
break;
default:
usage();
exit(1);
}
}
sm_test_begin(argc, argv, "test readwrite");
argc -= optind;
argv += optind;
if (argc > 0)
fn = argv[0];
if (nfiles > 0 && nproc > 0)
{
startproc(fn, nproc, nfiles, sync);
}
else
{
if (wr)
writefile(fn, sync);
if (rd)
readfile(fn);
}
return sm_test_end();
}
syntax highlighted by Code2HTML, v. 0.9.1