Newsgroups: comp.sources.unix
From: lm@slovax.Eng.Sun.COM (Larry McVoy)
Subject: v26i061: lmdd - Trivial disk I/O performance tester - like dd
Sender: unix-sources-moderator@pa.dec.com
Approved: vixie@pa.dec.com

Submitted-By: lm@slovax.Eng.Sun.COM (Larry McVoy)
Posting-Number: Volume 26, Issue 61
Archive-Name: lmdd

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  lmdd.1 Makefile lmdd.c ddperf
# Wrapped by vixie@cognition.pa.dec.com on Sat Jun 13 17:03:23 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'lmdd.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lmdd.1'\"
else
echo shar: Extracting \"'lmdd.1'\" \(2809 characters\)
sed "s/^X//" >'lmdd.1' <<'END_OF_FILE'
X.\" @(#)lmdd.1	1.1 2/27/92
X.TH LMDD 1 
X.SH NAME
lmdd \- move io for performance and debugging tests
X.SH SYNOPSIS
X.B lmdd
X[
X.IB option = value
X] .\|.\|.
X.SH DESCRIPTION
X.B lmdd
copies a specified input file to a specified output with possible
conversions.  This program is primarily useful for timing I/O since it
prints out the timing statistics after completing.
X.SH OPTIONS
X.TP 15
X.BI if= name
Input file is taken from
X.IR name ;
standard input is default.
X.I internal
is a special file that acts like Sun's 
X.IR /dev/zero ,
i.e., it provides a buffer of zeros without doing a system call to get them.
X.TP 
X.BI of= name
Output file is taken from
X.IR name ;
standard output is default.  
X.I internal
is a special file that acts like 
X.IR /dev/null ,
without doing a system call to get rid of the data.
X.TP 
X.BI bs= n
Input and output block size
X.I n
bytes (default 8192).  Note that this is different from dd(1), it has
a 512 byte default.   Also note that the block size can be followed
by 'k' or 'm' to indicate kilo bytes (*1024) or megabytes (*1024*1024),
respectively.
X.TP 
X.BI ipat= n
If 
X.B n
is non zero, expect a known pattern in the file (see opat).  Mismatches
will be displayed as "ERROR: off=%d want=%x got=%x".  The pattern is
a sequence of 4 byte integers with the first 0, second 1, and so on.
The default is not to check for the pattern.
X.TP
X.BI opat= n
If 
X.B n
is non zero, generate a known pattern on the output stream.  Used for
debugging file system correctness.
The default is not to generate the pattern.
X.TP 
X.BI mismatch= n
If 
X.B n
is non zero, stop at the first mismatched value.  Used with ipat.
X.TP 
X.BI skip= n
Skip
X.IR n ""
input blocks before starting copy.
X.TP 
X.BI fsync= n
If
X.I n
is non-zero, call fsync(2) on the output file before exiting or printing
timing statistics.
X.TP 
X.BI sync= n
If
X.I n
is non-zero, call sync(2) before exiting or printing
timing statistics.
X.TP 
X.BI flush= n
If
X.I n
is non-zero and mmap(2) is available, call msync(2) to invalidate the
output file.  This flushes the file to disk so that you don't have
unmount/mount.  It is not as good as mount/unmount because it just
flushes file pages - it misses the indirect blocks which are still
cached.
X.TP 
X.BI rusage= n
If
X.I n
is non-zero, print rusage statistics as well as timing statistics.
X.TP 
X.BI count= n
Copy only
X.IR n ""
input records.
X.SH EXAMPLES
X.LP
This is the most common usage, the intent is to measure disk performance.
The disk is a spare partition mounted on /spare.
X.sp
X.nf
X.in +4
X# mount /spare
X# lmdd if=internal of=/spare/XXX count=1000 fsync=1
X7.81 MB in 3.78 seconds (2.0676 MB/sec)
X
X: Flush cache
X# umount /spare
X# mount /spare
X
X# lmdd if=/spare/XXX of=internal 
X7.81 MB in 2.83 seconds (2.7611 MB/sec)
X.in
X.sp
X.fi
X.SH AUTHOR
Larry McVoy, lm@sun.com
X.br
Not copyrighted.
END_OF_FILE
if test 2809 -ne `wc -c <'lmdd.1'`; then
    echo shar: \"'lmdd.1'\" unpacked with wrong size!
fi
# end of 'lmdd.1'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(315 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
DESTROOT=
DESTPATH=$(DESTROOT)/usr/local
X
all: lmdd ddperf
X
lmdd: lmdd.o
X	cc -o lmdd lmdd.o
X
ddperf: ddperf.sh
X	cp ddperf.sh ddperf
X	chmod +x ddperf
X
install:
X	install -c -s lmdd   $(DESTPATH)/bin/lmdd
X	install -c    lmdd.1 $(DESTPATH)/man/man1/lmdd.1
X
X# we don't install ddperf because it has no man page (vixie)
X
END_OF_FILE
if test 315 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'lmdd.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lmdd.c'\"
else
echo shar: Extracting \"'lmdd.c'\" \(6270 characters\)
sed "s/^X//" >'lmdd.c' <<'END_OF_FILE'
static char sccsid[] = "@(#)lmdd.c	1.7";
X
X/*
X * lmdd, lm@sun.com (Larry McVoy)
X *
X * Uncopyrighted, do whatever you want with it.
X *
X * defaults:
X *	bs=8k
X *	count=forever
X *	if=stdin
X *	of=stdout
X *	ipat=0
X *	opat=0
X *	mismatch=0
X *	rusage=0
X *	flush=0
X * shorthands:
X *	recognizes 'k' or 'm' at the end of a number for 1024 & 1024^2
X *	recognizes "internal" as an internal /dev/zero /dev/null file.
X */
X#include <signal.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#ifndef	SVR4
X#include <sys/resource.h>
X#endif
X#define	FLUSH
X#ifdef	FLUSH
X#include <unistd.h>
X#include <sys/mman.h>
X#include <sys/stat.h>
X#endif
X#define	STARTUP		(.11)	/* XXX - about how long before it starts
X				 * working.  So time matches w/ rusage. This is
X				 * close on a 20Mhz SS. */
X#define	uchar	unsigned char
X
int     out, Fsync, Sync, Flush, ru, c;/* done needs it */
char   *cmds[] = {
X	"if",			/* input file */
X	"of",			/* output file */
X	"ipat",			/* check input for pattern */
X	"opat",			/* generate pattern on output */
X	"mismatch",		/* stop at first mismatch */
X	"bs",			/* block size */
X	"count",		/* number of blocks */
X	"skip",			/* skip this number of blocks on input */
X	"fsync",		/* fsync output before exit */
X	"sync",			/* sync output before exit */
X	"rusage",		/* dump rusage stats */
X#ifdef	FLUSH
X	"flush",		/* map in out and invalidate (flush) */
X#endif
X	0,
X};
X
main(ac, av)
X	char  **av;
X{
X	uint  *buf;
X	int     misses, mismatch, outpat, inpat, bs, in, gotcnt, count;
X	int     skip;
X	extern char *valloc();
X	void    done();
X	int     i;
X
X	for (i = 1; i < ac; ++i) {
X		chkarg(av[i]);
X	}
X	misses = mismatch = getarg("mismatch=", ac, av);
X	inpat = getarg("ipat=", ac, av);
X	outpat = getarg("opat=", ac, av);
X	in = getfile("if=", ac, av);
X	if (in == -1)
X		in = 0;
X	out = getfile("of=", ac, av);
X	if (out == -1)
X		out = 1;
X	bs = getarg("bs=", ac, av);
X	if (bs < 0)
X		bs = 8192;
X	Fsync = getarg("fsync=", ac, av);
X	Sync = getarg("sync=", ac, av);
X	ru = getarg("rusage=", ac, av);
X	count = getarg("count=", ac, av);
X	Flush = getarg("flush=", ac, av);
X	if (count < 0)
X		gotcnt = 0;
X	else
X		gotcnt = 1;
X	c = 0;
X	skip = getarg("skip=", ac, av);
X
X	if (inpat != -1 || outpat != -1 && (bs & 3)) {
X		printf("Block size must be word aligned\n");
X		exit(1);
X	}
X	if (!(buf = (uint *) valloc((unsigned) bs))) {
X		perror("valloc");
X		exit(1);
X	}
X	bzero((char *) buf, bs);
X
X	signal(SIGINT, done);
X	if (skip > 0)
X		lseek(in, skip * bs, 0);
X	start();
X	for (;;) {
X		register x;
X
X		if (gotcnt && count-- <= 0)
X			done();
X		if (in >= 0)
X			x = read(in, buf, bs);
X		else
X			x = bs;
X		if (x <= 0)
X			done();
X		if (inpat != -1) {
X			register foo, cnt;
X
X			for (foo = 0, cnt = x >> 2; cnt--; foo++) {
X				if (buf[foo] != (uint) (c + foo)) {
X					printf("ERROR: off=%d want=%x got=%x\n",
X					    c + foo, c + foo, buf[foo]);
X					if (mismatch != -1 && --misses == 0)
X						done();
X				}
X			}
X		}
X		if (outpat != -1) {
X			register foo, cnt;
X
X			for (foo = 0, cnt = x >> 2; cnt--; foo++)
X				buf[foo] = c + foo;
X		}
X		if (out >= 0)
X			if (write(out, buf, x) != x)
X				done();
X		c += x >> 2;
X	}
X}
X
chkarg(arg)
X	char	*arg;
X{
X	int	i;
X	char	*a, *b;
X
X	for (i = 0; cmds[i]; ++i) {
X		for (a = arg, b = cmds[i]; *a && *b && *a == *b; a++, b++)
X			;
X		if (*a == '=')
X			return (0);
X	}
X	printf("Bad arg: %s\n", arg);
X	exit(1);
X}
X
void
done()
X{
X	close(1);
X	open("/dev/tty", 1);
X	if (Sync > 0)
X		sync();
X	if (Fsync > 0)
X		fsync(out);
X	if (Flush > 0)
X		flush(out);
X	stop();
X#ifndef	SVR4
X	if (ru != -1)
X		rusage();
X#endif
X	ptime(c << 2);
X	exit(0);
X}
X
X#define	secs(tv)	(tv.tv_sec + tv.tv_usec / 1000000.0)
X
X#ifndef	SVR4
rusage()
X{
X	struct rusage r;
X	double  timespent();
X	double  idle;
X
X	getrusage(RUSAGE_SELF, &r);
X	idle = timespent() - (secs(r.ru_stime) + secs(r.ru_utime));
X	idle = idle / timespent() * 100;
X	printf("sys=%g user=%g stalled=%.0f%% rd=%d wr=%d min=%d maj=%d\n",
X	    secs(r.ru_stime), secs(r.ru_utime),
X	    idle, r.ru_inblock, r.ru_oublock,
X	    r.ru_minflt, r.ru_majflt);
X}
X
X#endif
X
getarg(s, ac, av)
X	char   *s;
X	char  **av;
X{
X	register len, i;
X
X	len = strlen(s);
X
X	for (i = 1; i < ac; ++i)
X		if (!strncmp(av[i], s, len)) {
X			register bs = atoi(&av[i][len]);
X
X			if (rindex(&av[i][len], 'k'))
X				bs *= 1024;
X			else if (rindex(&av[i][len], 'm'))
X				bs *= (1024 * 1024);
X			return (bs);
X		}
X	return (-1);
X}
X
char	*output;
X
getfile(s, ac, av)
X	char   *s;
X	char  **av;
X{
X	register ret, len, i;
X
X	len = strlen(s);
X
X	for (i = 1; i < ac; ++i) {
X		if (!strncmp(av[i], s, len)) {
X			if (av[i][0] == 'o') {
X				if (!strcmp("of=internal", av[i]))
X					return (-2);
X				ret = creat(&av[i][len], 0644);
X				if (ret == -1)
X					error(&av[i][len]);
X				output = &av[i][len];
X				return (ret);
X			} else {
X				if (!strcmp("if=internal", av[i]))
X					return (-2);
X				ret = open(&av[i][len], 0);
X				if (ret == -1)
X					error(&av[i][len]);
X				return (ret);
X			}
X		}
X	}
X	return (-1);
X}
X
X#ifdef	FLUSH
flush()
X{
X	int	fd;
X	struct	stat sb;
X	caddr_t	where;
X
X	if (output == NULL || (fd = open(output, 2)) == -1) 
X		return (warning("No output file"));
X	if (fstat(fd, &sb) == -1 || sb.st_size == 0)
X		return (warning(output));
X	where = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
X	msync(where, sb.st_size, MS_INVALIDATE);
X	/* XXX - didn't unmap */
X}
X#endif
X
X/*
X * utilities for timing
X */
X#include "stdio.h"
X#include "sys/types.h"
X#include "sys/time.h"
X
static struct timeval t1, t2;
X
warning(s)
X	char   *s;
X{
X	perror(s);
X	return (-1);
X}
X
error(s)
X	char   *s;
X{
X	perror(s);
X	exit(1);
X}
X
start() {
X	gettimeofday(&t1, (struct timezone *) 0);
X}
X
stop() {
X	gettimeofday(&t2, (struct timezone *) 0);
X}
X
ptime(bytes) {
X	ptransfer(bytes, &t1, &t2);
X}
double 
timespent()
X{
X	struct timeval td;
X
X	tvsub(&td, &t2, &t1);
X	return (td.tv_sec + (td.tv_usec / 1000000.) + STARTUP);
X}
X
ptransfer(bytes, t0, t1)
X	struct timeval *t0, *t1;
X{
X	struct timeval td;
X	float   s, bs;
X
X	tvsub(&td, t1, t0);
X	s = td.tv_sec + (td.tv_usec / 1000000.);
X#define	nz(x)	((x) == 0 ? 1 : (x))
X#define	MB	(1024*1024.0)
X	bs = bytes / nz(s);
X	printf("%.2f MB in %.2f seconds (%.4f MB/sec)\n", bytes / MB, s, bs / MB);
X}
X
tvsub(tdiff, t1, t0)
X	struct timeval *tdiff, *t1, *t0;
X{
X
X	tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
X	tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
X	if (tdiff->tv_usec < 0)
X		tdiff->tv_sec--, tdiff->tv_usec += 1000000;
X}
END_OF_FILE
if test 6270 -ne `wc -c <'lmdd.c'`; then
    echo shar: \"'lmdd.c'\" unpacked with wrong size!
fi
# end of 'lmdd.c'
fi
if test -f 'ddperf' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ddperf'\"
else
echo shar: Extracting \"'ddperf'\" \(371 characters\)
sed "s/^X//" >'ddperf' <<'END_OF_FILE'
X#!/bin/sh
X
X#
X# usage: ddperf mount_point
X#
X# test FSR, FSW
X#
doit()
X{
X	umount $D
X	mount $D
X	$*
X}
X
if [ X$1 != X ]
then	D=$1
else	D=/home
fi
X
if [ X$2 != X ]
then	C=$2
else	C=1k
fi
X
echo WRITES
for i in 1 2 3 4
do 	
X	lmdd if=internal of=$D/XXX count=$C rusage=1 fsync=1
done
echo READS
for i in 1 2 3 4
do 	
X	doit lmdd of=internal if=$D/XXX count=$C rusage=1 fsync=1
done
END_OF_FILE
if test 371 -ne `wc -c <'ddperf'`; then
    echo shar: \"'ddperf'\" unpacked with wrong size!
fi
# end of 'ddperf'
fi
echo shar: End of shell archive.
exit 0
