iobw.c revision 60aa78315174e5bf9d98f66db582ad381e013817
1/*
2 * iobw.c - simple I/O bandwidth benchmark
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 021110-1307, USA.
18 *
19 * Copyright (C) 2008 Andrea Righi <righi.andrea@gmail.com>
20 */
21
22#define _GNU_SOURCE
23#define __USE_GNU
24
25#include <errno.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <malloc.h>
29#include <fcntl.h>
30#include <signal.h>
31#include <string.h>
32#include <unistd.h>
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <sys/time.h>
36#include <sys/wait.h>
37#include <asm/page.h>
38
39#ifndef PAGE_SIZE
40#define PAGE_SIZE getpagesize()
41#endif
42
43#define ALIGN(x,a)		__ALIGN_MASK(x,(typeof(x))(a)-1)
44#define __ALIGN_MASK(x,mask)	(((x)+(mask))&~(mask))
45#define KB(x)			((x) >> 10)
46
47const char usage[] = "Usage: iobw [-direct] threads chunk_size data_size\n";
48const char child_fmt[] =
49		"(%s) task %3d: time %4lu.%03lu bw %7lu KiB/s (%s)\n";
50const char parent_fmt[] =
51		"(%s) parent %d: time %4lu.%03lu bw %7lu KiB/s (%s)\n";
52
53static int directio = 0;
54static size_t data_size = 0;
55static size_t chunk_size = 0;
56
57typedef enum {
58	OP_WRITE,
59	OP_READ,
60	NUM_IOPS,
61} iops_t;
62
63static const char *iops[] = {
64	"WRITE",
65	"READ ",
66	"TOTAL",
67};
68
69static int threads;
70pid_t *children;
71
72char *mygroup;
73
74static void print_results(int id, iops_t op, size_t bytes, struct timeval *diff)
75{
76	fprintf(stdout, id ? child_fmt : parent_fmt,
77		mygroup, id, diff->tv_sec, diff->tv_usec / 1000,
78		(bytes / (diff->tv_sec * 1000000L + diff->tv_usec))
79		* 1000000L / 1024, iops[op]);
80}
81
82static void thread(int id)
83{
84	struct timeval start, stop, diff;
85	int fd, i, ret;
86	size_t n;
87	void *buf;
88	int flags = O_CREAT | O_RDWR | O_LARGEFILE;
89	char filename[32];
90
91	ret = posix_memalign(&buf, PAGE_SIZE, chunk_size);
92	if (ret < 0) {
93		fprintf(stderr,
94			"ERROR: task %d couldn't allocate %lu bytes (%s)\n",
95			id, chunk_size, strerror(errno));
96		exit(1);
97	}
98	memset(buf, 0xaa, chunk_size);
99
100	snprintf(filename, sizeof(filename), "%s-%d-iobw.tmp", mygroup, id);
101	if (directio)
102		flags |= O_DIRECT;
103	fd = open(filename, flags, 0600);
104	if (fd < 0) {
105		fprintf(stderr, "ERROR: task %d couldn't open %s (%s)\n",
106			id, filename, strerror(errno));
107		free(buf);
108		exit(1);
109	}
110
111	/* Write */
112	lseek(fd, 0, SEEK_SET);
113	n = 0;
114	gettimeofday(&start, NULL);
115	while (n < data_size) {
116		i = write(fd, buf, chunk_size);
117		if (i < 0) {
118			fprintf(stderr, "ERROR: task %d writing to %s (%s)\n",
119				id, filename, strerror(errno));
120			ret = 1;
121			goto out;
122		}
123		n += i;
124	}
125        gettimeofday(&stop, NULL);
126        timersub(&stop, &start, &diff);
127	print_results(id + 1, OP_WRITE, data_size, &diff);
128
129	/* Read */
130	lseek(fd, 0, SEEK_SET);
131	n = 0;
132	gettimeofday(&start, NULL);
133	while (n < data_size) {
134		i = read(fd, buf, chunk_size);
135		if (i < 0) {
136			fprintf(stderr, "ERROR: task %d reading to %s (%s)\n",
137				id, filename, strerror(errno));
138			ret = 1;
139			goto out;
140		}
141		n += i;
142	}
143        gettimeofday(&stop, NULL);
144        timersub(&stop, &start, &diff);
145	print_results(id + 1, OP_READ, data_size, &diff);
146out:
147	close(fd);
148	unlink(filename);
149	free(buf);
150	exit(ret);
151}
152
153static void spawn(int id)
154{
155	pid_t pid;
156
157	pid = fork();
158	switch (pid) {
159	case -1:
160		fprintf(stderr, "ERROR: couldn't fork thread %d\n", id);
161		exit(1);
162	case 0:
163		thread(id);
164	default:
165		children[id] = pid;
166	}
167}
168
169void signal_handler(int sig)
170{
171	char filename[32];
172	int i;
173
174	for (i = 0; i < threads; i++)
175		if (children[i])
176			kill(children[i], SIGKILL);
177
178	for (i = 0; i < threads; i++) {
179		struct stat mystat;
180
181		snprintf(filename, sizeof(filename), "%s-%d-iobw.tmp",
182				mygroup,i);
183		if (stat(filename, &mystat) < 0)
184			continue;
185		unlink(filename);
186	}
187
188	fprintf(stdout, "received signal %d, exiting\n", sig);
189	exit(0);
190}
191
192unsigned long long memparse(char *ptr, char **retptr)
193{
194	unsigned long long ret = strtoull(ptr, retptr, 0);
195
196	switch (**retptr) {
197	case 'G':
198	case 'g':
199		ret <<= 10;
200	case 'M':
201	case 'm':
202		ret <<= 10;
203	case 'K':
204	case 'k':
205		ret <<= 10;
206		(*retptr)++;
207	default:
208		break;
209	}
210	return ret;
211}
212
213int main(int argc, char *argv[])
214{
215	struct timeval start, stop, diff;
216	char *end;
217	int i;
218
219	if (argv[1] && strcmp(argv[1], "-direct") == 0) {
220		directio = 1;
221		argc--;
222		argv++;
223	}
224	if (argc != 4) {
225		fprintf(stderr, usage);
226		exit(1);
227	}
228	if ((threads = atoi(argv[1])) == 0) {
229		fprintf(stderr, usage);
230		exit(1);
231	}
232	chunk_size = ALIGN(memparse(argv[2], &end), PAGE_SIZE);
233	if (*end) {
234		fprintf(stderr, usage);
235		exit(1);
236	}
237	data_size = ALIGN(memparse(argv[3], &end), PAGE_SIZE);
238	if (*end) {
239		fprintf(stderr, usage);
240		exit(1);
241	}
242
243	/* retrieve group name */
244	mygroup = getenv("MYGROUP");
245	if (!mygroup) {
246		fprintf(stderr,
247			"ERROR: undefined environment variable MYGROUP\n");
248		exit(1);
249	}
250
251	children = malloc(sizeof(pid_t) * threads);
252	if (!children) {
253		fprintf(stderr, "ERROR: not enough memory\n");
254		exit(1);
255	}
256
257	/* handle user interrupt */
258	signal(SIGINT, signal_handler);
259	/* handle kill from shell */
260	signal(SIGTERM, signal_handler);
261
262	fprintf(stdout, "chunk_size %luKiB, data_size %luKiB\n",
263		KB(chunk_size), KB(data_size));
264	fflush(stdout);
265
266        gettimeofday(&start, NULL);
267        for (i = 0; i < threads ; i++)
268                spawn(i);
269        for (i = 0; i < threads; i++) {
270                int status;
271                wait(&status);
272                if (!WIFEXITED(status))
273                        exit(1);
274        }
275        gettimeofday(&stop, NULL);
276
277        timersub(&stop, &start, &diff);
278	print_results(0, NUM_IOPS, data_size * threads * NUM_IOPS, &diff);
279	fflush(stdout);
280	free(children);
281
282        exit(0);
283}
284