/* * 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(); }