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