xref: /onnv-gate/usr/src/cmd/filebench/common/misc.c (revision 6305:ce48ff893c37)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdio.h>
29 #include <fcntl.h>
30 #include <limits.h>
31 #include <time.h>
32 #include <libgen.h>
33 #include <unistd.h>
34 #include <strings.h>
35 #include "filebench.h"
36 #include "ipc.h"
37 #include "eventgen.h"
38 #include "utils.h"
39 
40 /*
41  * Routines to access high resolution system time, initialize and
42  * shutdown filebench, log filebench run progress and errors, and
43  * access system information strings.
44  */
45 
46 
47 #if !defined(sun) && defined(USE_RDTSC)
48 /*
49  * Lets us use the rdtsc instruction to get highres time.
50  * Thanks to libmicro
51  */
52 uint64_t	cpu_hz = 0;
53 
54 /*
55  * Uses the rdtsc instruction to get high resolution (cpu
56  * clock ticks) time. Only used for non Sun compiles.
57  */
58 __inline__ long long
59 rdtsc(void)
60 {
61 	unsigned long long x;
62 	__asm__ volatile(".byte 0x0f, 0x31" : "=A" (x));
63 	return (x);
64 }
65 
66 /*
67  * Get high resolution time in nanoseconds. This is the version
68  * used when not compiled for Sun systems. It uses rdtsc call to
69  * get clock ticks and converts to nanoseconds
70  */
71 uint64_t
72 gethrtime(void)
73 {
74 	uint64_t hrt;
75 
76 	/* convert to nanosecs and return */
77 	hrt = 1000000000UL * rdtsc() / cpu_hz;
78 	return (hrt);
79 }
80 
81 /*
82  * Gets CPU clock frequency in MHz from cpuinfo file.
83  * Converts to cpu_hz and stores in cpu_hz global uint64_t.
84  * Only used for non Sun compiles.
85  */
86 static uint64_t
87 parse_cpu_hz(void)
88 {
89 	/*
90 	 * Parse the following from /proc/cpuinfo.
91 	 * cpu MHz		: 2191.563
92 	 */
93 	FILE *cpuinfo;
94 	double hertz = -1;
95 	uint64_t hz;
96 
97 	if ((cpuinfo = fopen("/proc/cpuinfo", "r")) == NULL) {
98 		filebench_log(LOG_ERROR, "open /proc/cpuinfo failed: %s",
99 		    strerror(errno));
100 		filebench_shutdown(1);
101 	}
102 	while (!feof(cpuinfo)) {
103 		char buffer[80];
104 
105 		fgets(buffer, 80, cpuinfo);
106 		if (strlen(buffer) == 0) continue;
107 		if (strncasecmp(buffer, "cpu MHz", 7) == 0) {
108 			char *token = strtok(buffer, ":");
109 
110 			if (token != NULL) {
111 				token = strtok((char *)NULL, ":");
112 				hertz = strtod(token, NULL);
113 			}
114 			break;
115 		}
116 	}
117 	hz = hertz * 1000000;
118 
119 	return (hz);
120 }
121 
122 #elif !defined(sun)
123 
124 /*
125  * Get high resolution time in nanoseconds. This is the version
126  * used if compiled for Sun systems. It calls gettimeofday
127  * to get current time and converts it to nanoseconds.
128  */
129 uint64_t
130 gethrtime(void)
131 {
132 	struct timeval tv;
133 	uint64_t hrt;
134 
135 	gettimeofday(&tv, NULL);
136 
137 	hrt = (uint64_t)tv.tv_sec * 1000000000UL +
138 	    (uint64_t)tv.tv_usec * 1000UL;
139 	return (hrt);
140 }
141 #endif
142 
143 /*
144  * Main filebench initialization. Opens the random number
145  * "device" file or shuts down the run if one is not found.
146  * Sets the cpu clock frequency variable or shuts down the
147  * run if one is not found.
148  */
149 void
150 filebench_init(void)
151 {
152 	fb_random_init();
153 
154 #if defined(USE_RDTSC) && (LINUX_PORT)
155 	cpu_hz = parse_cpu_hz();
156 	if (cpu_hz <= 0) {
157 		filebench_log(LOG_ERROR, "Error getting CPU Mhz: %s",
158 		    strerror(errno));
159 		filebench_shutdown(1);
160 	}
161 #endif /* USE_RDTSC */
162 
163 }
164 
165 extern int lex_lineno;
166 
167 /*
168  * Writes a message consisting of information formated by
169  * "fmt" to the log file, dump file or stdout.  The supplied
170  * "level" argument determines which file to write to and
171  * what other actions to take. The level LOG_LOG writes to
172  * the "log" file, and will open the file on the first
173  * invocation. The level LOG_DUMP writes to the "dump" file,
174  * and will open it on the first invocation. Other levels
175  * print to the stdout device, with the amount of information
176  * dependent on the error level and the current error level
177  * setting in filebench_shm->debug_level.
178  */
179 void filebench_log
180 __V((int level, const char *fmt, ...))
181 {
182 	va_list args;
183 	hrtime_t now;
184 	char line[131072];
185 	char buf[131072];
186 
187 	if (level == LOG_FATAL)
188 		goto fatal;
189 
190 	/* open logfile if not already open and writing to it */
191 	if ((level == LOG_LOG) &&
192 	    (filebench_shm->log_fd < 0)) {
193 		char path[MAXPATHLEN];
194 		char *s;
195 
196 		(void) strcpy(path, filebench_shm->fscriptname);
197 		if ((s = strstr(path, ".f")))
198 			*s = 0;
199 		else
200 			(void) strcpy(path, "filebench");
201 
202 		(void) strcat(path, ".csv");
203 
204 		filebench_shm->log_fd =
205 		    open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
206 	}
207 
208 	/*
209 	 * if logfile still not open, switch to LOG_ERROR level so
210 	 * it gets reported to stdout
211 	 */
212 	if ((level == LOG_LOG) &&
213 	    (filebench_shm->log_fd < 0)) {
214 		(void) snprintf(line, sizeof (line),  "Open logfile failed: %s",
215 		    strerror(errno));
216 		level = LOG_ERROR;
217 	}
218 
219 	/* open dumpfile if not already open and writing to it */
220 	if ((level == LOG_DUMP) &&
221 	    (*filebench_shm->dump_filename == 0))
222 		return;
223 
224 	if ((level == LOG_DUMP) &&
225 	    (filebench_shm->dump_fd < 0)) {
226 
227 		filebench_shm->dump_fd =
228 		    open(filebench_shm->dump_filename,
229 		    O_RDWR | O_CREAT | O_TRUNC, 0666);
230 	}
231 
232 	if ((level == LOG_DUMP) &&
233 	    (filebench_shm->dump_fd < 0)) {
234 		(void) snprintf(line, sizeof (line), "Open logfile failed: %s",
235 		    strerror(errno));
236 		level = LOG_ERROR;
237 	}
238 
239 	/* Quit if this is a LOG_ERROR messages and they are disabled */
240 	if ((filebench_shm->shm_1st_err) && (level == LOG_ERROR))
241 		return;
242 
243 	if (level == LOG_ERROR1) {
244 		if (filebench_shm->shm_1st_err)
245 			return;
246 
247 		/* A LOG_ERROR1 temporarily disables LOG_ERROR messages */
248 		filebench_shm->shm_1st_err = 1;
249 		level = LOG_ERROR;
250 	}
251 
252 	/* Only log greater than debug setting */
253 	if ((level != LOG_DUMP) && (level != LOG_LOG) &&
254 	    (level > filebench_shm->debug_level))
255 		return;
256 
257 	now = gethrtime();
258 
259 fatal:
260 
261 #ifdef __STDC__
262 	va_start(args, fmt);
263 #else
264 	char *fmt;
265 	va_start(args);
266 	fmt = va_arg(args, char *);
267 #endif
268 
269 	(void) vsprintf(line, fmt, args);
270 
271 	va_end(args);
272 
273 	if (level == LOG_FATAL) {
274 		(void) fprintf(stderr, "%s\n", line);
275 		return;
276 	}
277 
278 	/* Serialize messages to log */
279 	(void) ipc_mutex_lock(&filebench_shm->msg_lock);
280 
281 	if (level == LOG_LOG) {
282 		if (filebench_shm->log_fd > 0) {
283 			(void) snprintf(buf, sizeof (buf), "%s\n", line);
284 			(void) write(filebench_shm->log_fd, buf, strlen(buf));
285 			(void) fsync(filebench_shm->log_fd);
286 			(void) ipc_mutex_unlock(&filebench_shm->msg_lock);
287 			return;
288 		}
289 
290 	} else if (level == LOG_DUMP) {
291 		if (filebench_shm->dump_fd != -1) {
292 			(void) snprintf(buf, sizeof (buf), "%s\n", line);
293 			(void) write(filebench_shm->dump_fd, buf, strlen(buf));
294 			(void) fsync(filebench_shm->dump_fd);
295 			(void) ipc_mutex_unlock(&filebench_shm->msg_lock);
296 			return;
297 		}
298 
299 	} else if (filebench_shm->debug_level > LOG_INFO) {
300 		if (level < LOG_INFO)
301 			(void) fprintf(stderr, "%5d: ", (int)my_pid);
302 		else
303 			(void) fprintf(stdout, "%5d: ", (int)my_pid);
304 	}
305 
306 	if (level < LOG_INFO) {
307 		(void) fprintf(stderr, "%4.3f: %s",
308 		    (now - filebench_shm->epoch) / FSECS,
309 		    line);
310 
311 		if (my_procflow == NULL)
312 			(void) fprintf(stderr, " on line %d", lex_lineno);
313 
314 		(void) fprintf(stderr, "\n");
315 		(void) fflush(stderr);
316 	} else {
317 		(void) fprintf(stdout, "%4.3f: %s",
318 		    (now - filebench_shm->epoch) / FSECS,
319 		    line);
320 		(void) fprintf(stdout, "\n");
321 		(void) fflush(stdout);
322 	}
323 
324 	(void) ipc_mutex_unlock(&filebench_shm->msg_lock);
325 }
326 
327 /*
328  * Stops the run and exits filebench. If filebench is
329  * currently running a workload, calls procflow_shutdown()
330  * to stop the run. Also closes and deletes shared memory.
331  */
332 void
333 filebench_shutdown(int error) {
334 	filebench_log(LOG_DEBUG_IMPL, "Shutdown");
335 	(void) unlink("/tmp/filebench_shm");
336 	if (filebench_shm->shm_running)
337 		procflow_shutdown();
338 	filebench_shm->f_abort = 1;
339 	ipc_ismdelete();
340 	exit(error);
341 }
342 
343 /*
344  * Put the hostname in ${hostname}. The system supplied
345  * host name string is copied into an allocated string and
346  * the pointer to the string is placed in the supplied
347  * variable "var". If var->var_val.string already points to
348  * a string, the string is freed. The routine always
349  * returns zero (0).
350  */
351 var_t *
352 host_var(var_t *var)
353 {
354 	char hoststr[128];
355 	char *strptr;
356 
357 	(void) gethostname(hoststr, 128);
358 	if (VAR_HAS_STRING(var) && var->var_val.string)
359 		free(var->var_val.string);
360 
361 	if ((strptr = fb_stralloc(hoststr)) == NULL) {
362 		filebench_log(LOG_ERROR,
363 		    "unable to allocate string for host name");
364 		return (NULL);
365 	}
366 
367 	VAR_SET_STR(var, strptr);
368 	return (0);
369 }
370 
371 /*
372  * Put the date string in ${date}. The system supplied date is
373  * copied into an allocated string and the pointer to the string
374  * is placed in the supplied var_t's var_val.string. If
375  * var->var_val.string already points to a string, the string
376  * is freed. The routine returns a pointer to the supplied var_t,
377  * unless it is unable to allocate string for the date, in which
378  * case it returns NULL.
379  */
380 var_t *
381 date_var(var_t *var)
382 {
383 	char datestr[128];
384 	char *strptr;
385 #ifdef HAVE_CFTIME
386 	time_t t = time(NULL);
387 #else
388 	struct tm t;
389 #endif
390 
391 #ifdef HAVE_CFTIME
392 	cftime(datestr, "%y%m%d%H" "%M", &t);
393 #else
394 	(void) strftime(datestr, sizeof (datestr), "%y%m%d%H %M", &t);
395 #endif
396 
397 	if (VAR_HAS_STRING(var) && var->var_val.string)
398 		free(var->var_val.string);
399 
400 	if ((strptr = fb_stralloc(datestr)) == NULL) {
401 		filebench_log(LOG_ERROR,
402 		    "unable to allocate string for date");
403 		return (NULL);
404 	}
405 
406 	VAR_SET_STR(var, strptr);
407 
408 	return (var);
409 }
410 
411 extern char *fscriptname;
412 
413 /*
414  * Put the script name in ${script}. The path name of the script
415  * used with this filebench run trimmed of the trailing ".f" and
416  * all leading subdirectories. The remaining script name is
417  * copied into the var_val.string field of the supplied variable
418  * "var". The routine returns a pointer to the supplied var_t,
419  * unless it is unable to allocate string space, in which case it
420  * returns NULL.
421  */
422 var_t *
423 script_var(var_t *var)
424 {
425 	char *scriptstr;
426 	char *f = fb_stralloc(fscriptname);
427 	char *strptr;
428 
429 	/* Trim the .f suffix */
430 	for (scriptstr = f + strlen(f) - 1; scriptstr != f; scriptstr--) {
431 		if (*scriptstr == '.') {
432 			*scriptstr = 0;
433 			break;
434 		}
435 	}
436 
437 	if ((strptr = fb_stralloc(basename(f))) == NULL) {
438 		filebench_log(LOG_ERROR,
439 		    "unable to allocate string for script name");
440 		free(f);
441 		return (NULL);
442 	}
443 
444 	VAR_SET_STR(var, strptr);
445 	free(f);
446 
447 	return (var);
448 }
449