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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdio.h> 27 #include <fcntl.h> 28 #include <limits.h> 29 #include <time.h> 30 #include <libgen.h> 31 #include <unistd.h> 32 #include <strings.h> 33 #include "filebench.h" 34 #include "ipc.h" 35 #include "eventgen.h" 36 #include "utils.h" 37 #include "fsplug.h" 38 39 /* File System functions vector */ 40 fsplug_func_t *fs_functions_vec; 41 42 /* 43 * Routines to access high resolution system time, initialize and 44 * shutdown filebench, log filebench run progress and errors, and 45 * access system information strings. 46 */ 47 48 #if !defined(sun) && defined(USE_RDTSC) 49 /* 50 * Lets us use the rdtsc instruction to get highres time. 51 * Thanks to libmicro 52 */ 53 uint64_t cpu_hz = 0; 54 55 /* 56 * Uses the rdtsc instruction to get high resolution (cpu 57 * clock ticks) time. Only used for non Sun compiles. 58 */ 59 __inline__ long long 60 rdtsc(void) 61 { 62 unsigned long long x; 63 __asm__ volatile(".byte 0x0f, 0x31" : "=A" (x)); 64 return (x); 65 } 66 67 /* 68 * Get high resolution time in nanoseconds. This is the version 69 * used when not compiled for Sun systems. It uses rdtsc call to 70 * get clock ticks and converts to nanoseconds 71 */ 72 uint64_t 73 gethrtime(void) 74 { 75 uint64_t hrt; 76 77 /* convert to nanosecs and return */ 78 hrt = 1000000000UL * rdtsc() / cpu_hz; 79 return (hrt); 80 } 81 82 /* 83 * Gets CPU clock frequency in MHz from cpuinfo file. 84 * Converts to cpu_hz and stores in cpu_hz global uint64_t. 85 * Only used for non Sun compiles. 86 */ 87 static uint64_t 88 parse_cpu_hz(void) 89 { 90 /* 91 * Parse the following from /proc/cpuinfo. 92 * cpu MHz : 2191.563 93 */ 94 FILE *cpuinfo; 95 double hertz = -1; 96 uint64_t hz; 97 98 if ((cpuinfo = fopen("/proc/cpuinfo", "r")) == NULL) { 99 filebench_log(LOG_ERROR, "open /proc/cpuinfo failed: %s", 100 strerror(errno)); 101 filebench_shutdown(1); 102 } 103 while (!feof(cpuinfo)) { 104 char buffer[80]; 105 106 fgets(buffer, 80, cpuinfo); 107 if (strlen(buffer) == 0) continue; 108 if (strncasecmp(buffer, "cpu MHz", 7) == 0) { 109 char *token = strtok(buffer, ":"); 110 111 if (token != NULL) { 112 token = strtok((char *)NULL, ":"); 113 hertz = strtod(token, NULL); 114 } 115 break; 116 } 117 } 118 hz = hertz * 1000000; 119 120 return (hz); 121 } 122 123 #elif !defined(sun) 124 125 /* 126 * Get high resolution time in nanoseconds. This is the version 127 * used if compiled for Sun systems. It calls gettimeofday 128 * to get current time and converts it to nanoseconds. 129 */ 130 uint64_t 131 gethrtime(void) 132 { 133 struct timeval tv; 134 uint64_t hrt; 135 136 gettimeofday(&tv, NULL); 137 138 hrt = (uint64_t)tv.tv_sec * 1000000000UL + 139 (uint64_t)tv.tv_usec * 1000UL; 140 return (hrt); 141 } 142 #endif 143 144 /* 145 * Main filebench initialization. Opens the random number 146 * "device" file or shuts down the run if one is not found. 147 * Sets the cpu clock frequency variable or shuts down the 148 * run if one is not found. 149 */ 150 void 151 filebench_init(void) 152 { 153 fb_random_init(); 154 155 #if defined(USE_RDTSC) && (LINUX_PORT) 156 cpu_hz = parse_cpu_hz(); 157 if (cpu_hz <= 0) { 158 filebench_log(LOG_ERROR, "Error getting CPU Mhz: %s", 159 strerror(errno)); 160 filebench_shutdown(1); 161 } 162 #endif /* USE_RDTSC */ 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->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->shm_log_fd < 0)) { 193 char path[MAXPATHLEN]; 194 char *s; 195 196 (void) strcpy(path, filebench_shm->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->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->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->shm_dump_filename == 0)) 222 return; 223 224 if ((level == LOG_DUMP) && 225 (filebench_shm->shm_dump_fd < 0)) { 226 227 filebench_shm->shm_dump_fd = 228 open(filebench_shm->shm_dump_filename, 229 O_RDWR | O_CREAT | O_TRUNC, 0666); 230 } 231 232 if ((level == LOG_DUMP) && 233 (filebench_shm->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->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->shm_msg_lock); 280 281 if (level == LOG_LOG) { 282 if (filebench_shm->shm_log_fd > 0) { 283 (void) snprintf(buf, sizeof (buf), "%s\n", line); 284 (void) write(filebench_shm->shm_log_fd, buf, 285 strlen(buf)); 286 (void) fsync(filebench_shm->shm_log_fd); 287 (void) ipc_mutex_unlock(&filebench_shm->shm_msg_lock); 288 return; 289 } 290 291 } else if (level == LOG_DUMP) { 292 if (filebench_shm->shm_dump_fd != -1) { 293 (void) snprintf(buf, sizeof (buf), "%s\n", line); 294 (void) write(filebench_shm->shm_dump_fd, buf, 295 strlen(buf)); 296 (void) fsync(filebench_shm->shm_dump_fd); 297 (void) ipc_mutex_unlock(&filebench_shm->shm_msg_lock); 298 return; 299 } 300 301 } else if (filebench_shm->shm_debug_level > LOG_INFO) { 302 if (level < LOG_INFO) 303 (void) fprintf(stderr, "%5d: ", (int)my_pid); 304 else 305 (void) fprintf(stdout, "%5d: ", (int)my_pid); 306 } 307 308 if (level < LOG_INFO) { 309 (void) fprintf(stderr, "%4.3f: %s", 310 (now - filebench_shm->shm_epoch) / FSECS, 311 line); 312 313 if (my_procflow == NULL) 314 (void) fprintf(stderr, " on line %d", lex_lineno); 315 316 (void) fprintf(stderr, "\n"); 317 (void) fflush(stderr); 318 } else { 319 (void) fprintf(stdout, "%4.3f: %s", 320 (now - filebench_shm->shm_epoch) / FSECS, 321 line); 322 (void) fprintf(stdout, "\n"); 323 (void) fflush(stdout); 324 } 325 326 (void) ipc_mutex_unlock(&filebench_shm->shm_msg_lock); 327 } 328 329 /* 330 * Stops the run and exits filebench. If filebench is 331 * currently running a workload, calls procflow_shutdown() 332 * to stop the run. Also closes and deletes shared memory. 333 */ 334 void 335 filebench_shutdown(int error) { 336 337 if (error) { 338 filebench_log(LOG_DEBUG_IMPL, "Shutdown on error"); 339 filebench_shm->shm_f_abort = FILEBENCH_ABORT_ERROR; 340 } else { 341 filebench_log(LOG_DEBUG_IMPL, "Shutdown"); 342 } 343 344 procflow_shutdown(); 345 346 (void) unlink("/tmp/filebench_shm"); 347 ipc_ismdelete(); 348 exit(error); 349 } 350 351 /* 352 * Put the hostname in ${hostname}. The system supplied 353 * host name string is copied into an allocated string and 354 * the pointer to the string is placed in the supplied 355 * variable "var". If var->var_val.string already points to 356 * a string, the string is freed. The routine always 357 * returns zero (0). 358 */ 359 var_t * 360 host_var(var_t *var) 361 { 362 char hoststr[128]; 363 char *strptr; 364 365 (void) gethostname(hoststr, 128); 366 if (VAR_HAS_STRING(var) && var->var_val.string) 367 free(var->var_val.string); 368 369 if ((strptr = fb_stralloc(hoststr)) == NULL) { 370 filebench_log(LOG_ERROR, 371 "unable to allocate string for host name"); 372 return (NULL); 373 } 374 375 VAR_SET_STR(var, strptr); 376 return (0); 377 } 378 379 /* 380 * Put the date string in ${date}. The system supplied date is 381 * copied into an allocated string and the pointer to the string 382 * is placed in the supplied var_t's var_val.string. If 383 * var->var_val.string already points to a string, the string 384 * is freed. The routine returns a pointer to the supplied var_t, 385 * unless it is unable to allocate string for the date, in which 386 * case it returns NULL. 387 */ 388 var_t * 389 date_var(var_t *var) 390 { 391 char datestr[128]; 392 char *strptr; 393 #ifdef HAVE_CFTIME 394 time_t t = time(NULL); 395 #else 396 struct tm t; 397 #endif 398 399 #ifdef HAVE_CFTIME 400 cftime(datestr, "%y%m%d%H" "%M", &t); 401 #else 402 (void) strftime(datestr, sizeof (datestr), "%y%m%d%H %M", &t); 403 #endif 404 405 if (VAR_HAS_STRING(var) && var->var_val.string) 406 free(var->var_val.string); 407 408 if ((strptr = fb_stralloc(datestr)) == NULL) { 409 filebench_log(LOG_ERROR, 410 "unable to allocate string for date"); 411 return (NULL); 412 } 413 414 VAR_SET_STR(var, strptr); 415 416 return (var); 417 } 418 419 extern char *fscriptname; 420 421 /* 422 * Put the script name in ${script}. The path name of the script 423 * used with this filebench run trimmed of the trailing ".f" and 424 * all leading subdirectories. The remaining script name is 425 * copied into the var_val.string field of the supplied variable 426 * "var". The routine returns a pointer to the supplied var_t, 427 * unless it is unable to allocate string space, in which case it 428 * returns NULL. 429 */ 430 var_t * 431 script_var(var_t *var) 432 { 433 char *scriptstr; 434 char *f = fb_stralloc(fscriptname); 435 char *strptr; 436 437 /* Trim the .f suffix */ 438 for (scriptstr = f + strlen(f) - 1; scriptstr != f; scriptstr--) { 439 if (*scriptstr == '.') { 440 *scriptstr = 0; 441 break; 442 } 443 } 444 445 if ((strptr = fb_stralloc(basename(f))) == NULL) { 446 filebench_log(LOG_ERROR, 447 "unable to allocate string for script name"); 448 free(f); 449 return (NULL); 450 } 451 452 VAR_SET_STR(var, strptr); 453 free(f); 454 455 return (var); 456 } 457 458 void fb_lfs_funcvecinit(void); 459 460 /* 461 * Initialize any "plug-in" I/O function vectors. Called by each 462 * filebench process that is forked, as the vector is relative to 463 * its image. 464 */ 465 void 466 filebench_plugin_funcvecinit(void) 467 { 468 469 switch (filebench_shm->shm_filesys_type) { 470 case LOCAL_FS_PLUG: 471 fb_lfs_funcvecinit(); 472 break; 473 474 case NFS3_PLUG: 475 case NFS4_PLUG: 476 case CIFS_PLUG: 477 break; 478 default: 479 filebench_log(LOG_ERROR, 480 "filebench_plugin_funcvecinit: unknown file system"); 481 break; 482 } 483 } 484