15184Sek110237 /* 25184Sek110237 * CDDL HEADER START 35184Sek110237 * 45184Sek110237 * The contents of this file are subject to the terms of the 55184Sek110237 * Common Development and Distribution License (the "License"). 65184Sek110237 * You may not use this file except in compliance with the License. 75184Sek110237 * 85184Sek110237 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 95184Sek110237 * or http://www.opensolaris.org/os/licensing. 105184Sek110237 * See the License for the specific language governing permissions 115184Sek110237 * and limitations under the License. 125184Sek110237 * 135184Sek110237 * When distributing Covered Code, include this CDDL HEADER in each 145184Sek110237 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 155184Sek110237 * If applicable, add the following below this CDDL HEADER, with the 165184Sek110237 * fields enclosed by brackets "[]" replaced with your own identifying 175184Sek110237 * information: Portions Copyright [yyyy] [name of copyright owner] 185184Sek110237 * 195184Sek110237 * CDDL HEADER END 205184Sek110237 */ 215184Sek110237 /* 22*6084Saw148015 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 235184Sek110237 * Use is subject to license terms. 245184Sek110237 */ 255184Sek110237 265184Sek110237 #pragma ident "%Z%%M% %I% %E% SMI" 275184Sek110237 285184Sek110237 #include <stdio.h> 295184Sek110237 #include <fcntl.h> 305184Sek110237 #include <limits.h> 315184Sek110237 #include <time.h> 325184Sek110237 #include <libgen.h> 335184Sek110237 #include <unistd.h> 345184Sek110237 #include <strings.h> 355184Sek110237 #include "filebench.h" 365184Sek110237 #include "ipc.h" 375184Sek110237 #include "eventgen.h" 385184Sek110237 #include "utils.h" 395184Sek110237 405184Sek110237 /* 415184Sek110237 * Routines to access high resolution system time, initialize and 425184Sek110237 * shutdown filebench, obtain system generated random numbers from 435184Sek110237 * "urandom", log filebench run progress and errors, and access system 445184Sek110237 * information strings. 455184Sek110237 */ 465184Sek110237 475184Sek110237 485184Sek110237 #if !defined(sun) && defined(USE_RDTSC) 495184Sek110237 /* 505184Sek110237 * Lets us use the rdtsc instruction to get highres time. 515184Sek110237 * Thanks to libmicro 525184Sek110237 */ 535184Sek110237 uint64_t cpu_hz = 0; 545184Sek110237 555184Sek110237 /* 565184Sek110237 * Uses the rdtsc instruction to get high resolution (cpu 575184Sek110237 * clock ticks) time. Only used for non Sun compiles. 585184Sek110237 */ 595184Sek110237 __inline__ long long 605184Sek110237 rdtsc(void) 615184Sek110237 { 625184Sek110237 unsigned long long x; 635184Sek110237 __asm__ volatile(".byte 0x0f, 0x31" : "=A" (x)); 645184Sek110237 return (x); 655184Sek110237 } 665184Sek110237 675184Sek110237 /* 685184Sek110237 * Get high resolution time in nanoseconds. This is the version 695184Sek110237 * used when not compiled for Sun systems. It uses rdtsc call to 705184Sek110237 * get clock ticks and converts to nanoseconds 715184Sek110237 */ 725184Sek110237 uint64_t 735184Sek110237 gethrtime(void) 745184Sek110237 { 755184Sek110237 uint64_t hrt; 765184Sek110237 775184Sek110237 /* convert to nanosecs and return */ 785184Sek110237 hrt = 1000000000UL * rdtsc() / cpu_hz; 795184Sek110237 return (hrt); 805184Sek110237 } 815184Sek110237 825184Sek110237 /* 835184Sek110237 * Gets CPU clock frequency in MHz from cpuinfo file. 845184Sek110237 * Converts to cpu_hz and stores in cpu_hz global uint64_t. 855184Sek110237 * Only used for non Sun compiles. 865184Sek110237 */ 875184Sek110237 static uint64_t 885184Sek110237 parse_cpu_hz(void) 895184Sek110237 { 905184Sek110237 /* 915184Sek110237 * Parse the following from /proc/cpuinfo. 925184Sek110237 * cpu MHz : 2191.563 935184Sek110237 */ 945184Sek110237 FILE *cpuinfo; 955184Sek110237 double hertz = -1; 965184Sek110237 uint64_t hz; 975184Sek110237 985184Sek110237 if ((cpuinfo = fopen("/proc/cpuinfo", "r")) == NULL) { 995184Sek110237 filebench_log(LOG_ERROR, "open /proc/cpuinfo failed: %s", 1005184Sek110237 strerror(errno)); 1015184Sek110237 filebench_shutdown(1); 1025184Sek110237 } 1035184Sek110237 while (!feof(cpuinfo)) { 1045184Sek110237 char buffer[80]; 1055184Sek110237 1065184Sek110237 fgets(buffer, 80, cpuinfo); 1075184Sek110237 if (strlen(buffer) == 0) continue; 1085184Sek110237 if (strncasecmp(buffer, "cpu MHz", 7) == 0) { 1095184Sek110237 char *token = strtok(buffer, ":"); 1105184Sek110237 1115184Sek110237 if (token != NULL) { 1125184Sek110237 token = strtok((char *)NULL, ":"); 1135184Sek110237 hertz = strtod(token, NULL); 1145184Sek110237 } 1155184Sek110237 break; 1165184Sek110237 } 1175184Sek110237 } 1185184Sek110237 printf("CPU Mhz %9.6f, sysconf:%ld\n", hertz, sysconf(_SC_CLK_TCK)); 1195184Sek110237 hz = hertz * 1000000; 1205184Sek110237 1215184Sek110237 return (hz); 1225184Sek110237 } 1235184Sek110237 1245184Sek110237 #elif !defined(sun) 1255184Sek110237 1265184Sek110237 /* 1275184Sek110237 * Get high resolution time in nanoseconds. This is the version 1285184Sek110237 * used if compiled for Sun systems. It calls gettimeofday 1295184Sek110237 * to get current time and converts it to nanoseconds. 1305184Sek110237 */ 1315184Sek110237 uint64_t 1325184Sek110237 gethrtime(void) 1335184Sek110237 { 1345184Sek110237 struct timeval tv; 1355184Sek110237 uint64_t hrt; 1365184Sek110237 1375184Sek110237 gettimeofday(&tv, NULL); 1385184Sek110237 1395184Sek110237 hrt = (uint64_t)tv.tv_sec * 1000000000UL + 1405184Sek110237 (uint64_t)tv.tv_usec * 1000UL; 1415184Sek110237 return (hrt); 1425184Sek110237 } 1435184Sek110237 #endif 1445184Sek110237 1455184Sek110237 static int urandomfd; 1465184Sek110237 1475184Sek110237 /* 1485184Sek110237 * Main filebench initialization. Opens the random number 1495184Sek110237 * "device" file or shuts down the run if one is not found. 1505184Sek110237 * Sets the cpu clock frequency variable or shuts down the 1515184Sek110237 * run if one is not found. 1525184Sek110237 */ 1535184Sek110237 void 1545184Sek110237 filebench_init(void) 1555184Sek110237 { 1565184Sek110237 /* open the "urandom" random number device file */ 1575184Sek110237 if ((urandomfd = open("/dev/urandom", O_RDONLY)) < 0) { 1585184Sek110237 filebench_log(LOG_ERROR, "open /dev/urandom failed: %s", 1595184Sek110237 strerror(errno)); 1605184Sek110237 filebench_shutdown(1); 1615184Sek110237 } 1625184Sek110237 #if defined(USE_RDTSC) && (LINUX_PORT) 1635184Sek110237 cpu_hz = parse_cpu_hz(); 1645184Sek110237 if (cpu_hz <= 0) { 1655184Sek110237 filebench_log(LOG_ERROR, "Error getting CPU Mhz: %s", 1665184Sek110237 strerror(errno)); 1675184Sek110237 filebench_shutdown(1); 1685184Sek110237 } 1695184Sek110237 #endif /* USE_RDTSC */ 1705184Sek110237 1715184Sek110237 } 1725184Sek110237 1735184Sek110237 /* 1745184Sek110237 * Reads a 64 bit random number from the urandom "file". 1755184Sek110237 * Shuts down the run if the read fails. Otherwise returns 1765184Sek110237 * the random number after rounding it off by "round". 1775184Sek110237 * Returns 0 on success, -1 on failure. 1785184Sek110237 */ 1795184Sek110237 int 1805184Sek110237 filebench_randomno64(uint64_t *randp, uint64_t max, uint64_t round) 1815184Sek110237 { 1825184Sek110237 uint64_t random; 1835184Sek110237 1845184Sek110237 /* check for round value too large */ 1855184Sek110237 if (max <= round) { 1865184Sek110237 *randp = 0; 1875184Sek110237 1885184Sek110237 /* if it just fits, its ok, otherwise error */ 1895184Sek110237 if (max == round) 1905184Sek110237 return (0); 1915184Sek110237 else 1925184Sek110237 return (-1); 1935184Sek110237 } 1945184Sek110237 1955184Sek110237 if (read(urandomfd, &random, 1965184Sek110237 sizeof (uint64_t)) != sizeof (uint64_t)) { 1975184Sek110237 filebench_log(LOG_ERROR, "read /dev/urandom failed: %s", 1985184Sek110237 strerror(errno)); 1995184Sek110237 filebench_shutdown(1); 2005184Sek110237 } 2015184Sek110237 2025184Sek110237 /* clip with max and optionally round */ 2035184Sek110237 max -= round; 2045184Sek110237 random = random / (FILEBENCH_RANDMAX64 / max); 2055184Sek110237 if (round) { 2065184Sek110237 random = random / round; 2075184Sek110237 random *= round; 2085184Sek110237 } 2095184Sek110237 if (random > max) 2105184Sek110237 random = max; 2115184Sek110237 2125184Sek110237 *randp = random; 2135184Sek110237 return (0); 2145184Sek110237 } 2155184Sek110237 2165184Sek110237 2175184Sek110237 /* 2185184Sek110237 * Reads a 32 bit random number from the urandom "file". 2195184Sek110237 * Shuts down the run if the read fails. Otherwise returns 2205184Sek110237 * the random number after rounding it off by "round". 2215184Sek110237 * Returns 0 on success, -1 on failure. 2225184Sek110237 */ 2235184Sek110237 int 2245184Sek110237 filebench_randomno32(uint32_t *randp, uint32_t max, uint32_t round) 2255184Sek110237 { 2265184Sek110237 uint32_t random; 2275184Sek110237 2285184Sek110237 /* check for round value too large */ 2295184Sek110237 if (max <= round) { 2305184Sek110237 *randp = 0; 2315184Sek110237 2325184Sek110237 /* if it just fits, its ok, otherwise error */ 2335184Sek110237 if (max == round) 2345184Sek110237 return (0); 2355184Sek110237 else 2365184Sek110237 return (-1); 2375184Sek110237 } 2385184Sek110237 2395184Sek110237 if (read(urandomfd, &random, 2405184Sek110237 sizeof (uint32_t)) != sizeof (uint32_t)) { 2415184Sek110237 filebench_log(LOG_ERROR, "read /dev/urandom failed: %s", 2425184Sek110237 strerror(errno)); 2435184Sek110237 filebench_shutdown(1); 2445184Sek110237 } 2455184Sek110237 2465184Sek110237 /* clip with max and optionally round */ 2475184Sek110237 max -= round; 2485184Sek110237 random = random / (FILEBENCH_RANDMAX32 / max); 2495184Sek110237 if (round) { 2505184Sek110237 random = random / round; 2515184Sek110237 random *= round; 2525184Sek110237 } 2535184Sek110237 if (random > max) 2545184Sek110237 random = max; 2555184Sek110237 2565184Sek110237 *randp = random; 2575184Sek110237 return (0); 2585184Sek110237 } 2595184Sek110237 2605184Sek110237 extern int lex_lineno; 2615184Sek110237 2625184Sek110237 /* 2635184Sek110237 * Writes a message consisting of information formated by 2645184Sek110237 * "fmt" to the log file, dump file or stdout. The supplied 2655184Sek110237 * "level" argument determines which file to write to and 2665184Sek110237 * what other actions to take. The level LOG_LOG writes to 2675184Sek110237 * the "log" file, and will open the file on the first 2685184Sek110237 * invocation. The level LOG_DUMP writes to the "dump" file, 2695184Sek110237 * and will open it on the first invocation. Other levels 2705184Sek110237 * print to the stdout device, with the amount of information 2715184Sek110237 * dependent on the error level and the current error level 2725184Sek110237 * setting in filebench_shm->debug_level. 2735184Sek110237 */ 2745184Sek110237 void filebench_log 2755184Sek110237 __V((int level, const char *fmt, ...)) 2765184Sek110237 { 2775184Sek110237 va_list args; 2785184Sek110237 hrtime_t now; 2795184Sek110237 char line[131072]; 2805184Sek110237 char buf[131072]; 2815184Sek110237 2825184Sek110237 if (level == LOG_FATAL) 2835184Sek110237 goto fatal; 2845184Sek110237 2855184Sek110237 /* open logfile if not already open and writing to it */ 2865184Sek110237 if ((level == LOG_LOG) && 2875184Sek110237 (filebench_shm->log_fd < 0)) { 2885184Sek110237 char path[MAXPATHLEN]; 2895184Sek110237 char *s; 2905184Sek110237 2915184Sek110237 (void) strcpy(path, filebench_shm->fscriptname); 2925184Sek110237 if ((s = strstr(path, ".f"))) 2935184Sek110237 *s = 0; 2945184Sek110237 else 2955184Sek110237 (void) strcpy(path, "filebench"); 2965184Sek110237 2975184Sek110237 (void) strcat(path, ".csv"); 2985184Sek110237 2995184Sek110237 filebench_shm->log_fd = 3005184Sek110237 open(path, O_RDWR | O_CREAT | O_TRUNC, 0666); 3015184Sek110237 } 3025184Sek110237 3035184Sek110237 /* 3045184Sek110237 * if logfile still not open, switch to LOG_ERROR level so 3055184Sek110237 * it gets reported to stdout 3065184Sek110237 */ 3075184Sek110237 if ((level == LOG_LOG) && 3085184Sek110237 (filebench_shm->log_fd < 0)) { 3095184Sek110237 (void) snprintf(line, sizeof (line), "Open logfile failed: %s", 3105184Sek110237 strerror(errno)); 3115184Sek110237 level = LOG_ERROR; 3125184Sek110237 } 3135184Sek110237 3145184Sek110237 /* open dumpfile if not already open and writing to it */ 3155184Sek110237 if ((level == LOG_DUMP) && 3165184Sek110237 (*filebench_shm->dump_filename == 0)) 3175184Sek110237 return; 3185184Sek110237 3195184Sek110237 if ((level == LOG_DUMP) && 3205184Sek110237 (filebench_shm->dump_fd < 0)) { 3215184Sek110237 3225184Sek110237 filebench_shm->dump_fd = 3235184Sek110237 open(filebench_shm->dump_filename, 3245184Sek110237 O_RDWR | O_CREAT | O_TRUNC, 0666); 3255184Sek110237 } 3265184Sek110237 3275184Sek110237 if ((level == LOG_DUMP) && 3285184Sek110237 (filebench_shm->dump_fd < 0)) { 3295184Sek110237 (void) snprintf(line, sizeof (line), "Open logfile failed: %s", 3305184Sek110237 strerror(errno)); 3315184Sek110237 level = LOG_ERROR; 3325184Sek110237 } 3335184Sek110237 334*6084Saw148015 /* Quit if this is a LOG_ERROR messages and they are disabled */ 335*6084Saw148015 if ((filebench_shm->shm_1st_err) && (level == LOG_ERROR)) 336*6084Saw148015 return; 337*6084Saw148015 338*6084Saw148015 if (level == LOG_ERROR1) { 339*6084Saw148015 if (filebench_shm->shm_1st_err) 340*6084Saw148015 return; 341*6084Saw148015 342*6084Saw148015 /* A LOG_ERROR1 temporarily disables LOG_ERROR messages */ 343*6084Saw148015 filebench_shm->shm_1st_err = 1; 344*6084Saw148015 level = LOG_ERROR; 345*6084Saw148015 } 346*6084Saw148015 3475184Sek110237 /* Only log greater than debug setting */ 3485184Sek110237 if ((level != LOG_DUMP) && (level != LOG_LOG) && 3495184Sek110237 (level > filebench_shm->debug_level)) 3505184Sek110237 return; 3515184Sek110237 3525184Sek110237 now = gethrtime(); 3535184Sek110237 3545184Sek110237 fatal: 3555184Sek110237 3565184Sek110237 #ifdef __STDC__ 3575184Sek110237 va_start(args, fmt); 3585184Sek110237 #else 3595184Sek110237 char *fmt; 3605184Sek110237 va_start(args); 3615184Sek110237 fmt = va_arg(args, char *); 3625184Sek110237 #endif 3635184Sek110237 3645184Sek110237 (void) vsprintf(line, fmt, args); 3655184Sek110237 3665184Sek110237 va_end(args); 3675184Sek110237 3685184Sek110237 if (level == LOG_FATAL) { 3695184Sek110237 (void) fprintf(stdout, "%s\n", line); 3705184Sek110237 return; 3715184Sek110237 } 3725184Sek110237 3735184Sek110237 /* Serialize messages to log */ 3745184Sek110237 (void) ipc_mutex_lock(&filebench_shm->msg_lock); 3755184Sek110237 3765184Sek110237 if (level == LOG_LOG) { 3775184Sek110237 if (filebench_shm->log_fd > 0) { 3785184Sek110237 (void) snprintf(buf, sizeof (buf), "%s\n", line); 3795184Sek110237 (void) write(filebench_shm->log_fd, buf, strlen(buf)); 3805184Sek110237 (void) fsync(filebench_shm->log_fd); 3815184Sek110237 } 3825184Sek110237 3835184Sek110237 } else if (level == LOG_DUMP) { 3845184Sek110237 if (filebench_shm->dump_fd != -1) { 3855184Sek110237 (void) snprintf(buf, sizeof (buf), "%s\n", line); 3865184Sek110237 (void) write(filebench_shm->dump_fd, buf, strlen(buf)); 3875184Sek110237 (void) fsync(filebench_shm->dump_fd); 3885184Sek110237 } 3895184Sek110237 3905184Sek110237 } else if (filebench_shm->debug_level > LOG_INFO) { 3915216Sek110237 (void) fprintf(stdout, "%5d: %4.3f: %s", 392*6084Saw148015 (int)my_pid, (now - filebench_shm->epoch) / FSECS, 3935184Sek110237 line); 3945184Sek110237 } else { 3955184Sek110237 (void) fprintf(stdout, "%4.3f: %s", 3965184Sek110237 (now - filebench_shm->epoch) / FSECS, 3975184Sek110237 line); 3985184Sek110237 } 3995184Sek110237 4005184Sek110237 if (level == LOG_ERROR) { 401*6084Saw148015 if (my_procflow == NULL) 402*6084Saw148015 (void) fprintf(stdout, " on line %d", lex_lineno); 4035184Sek110237 } 4045184Sek110237 4055184Sek110237 if ((level != LOG_LOG) && (level != LOG_DUMP)) { 4065184Sek110237 (void) fprintf(stdout, "\n"); 4075184Sek110237 (void) fflush(stdout); 4085184Sek110237 } 4095184Sek110237 4105184Sek110237 (void) ipc_mutex_unlock(&filebench_shm->msg_lock); 4115184Sek110237 } 4125184Sek110237 4135184Sek110237 /* 4145184Sek110237 * Stops the run and exits filebench. If filebench is 4155184Sek110237 * currently running a workload, calls procflow_shutdown() 4165184Sek110237 * to stop the run. Also closes and deletes shared memory. 4175184Sek110237 */ 4185184Sek110237 void 4195184Sek110237 filebench_shutdown(int error) { 4205184Sek110237 filebench_log(LOG_DEBUG_IMPL, "Shutdown"); 4215184Sek110237 (void) unlink("/tmp/filebench_shm"); 422*6084Saw148015 if (filebench_shm->shm_running) 4235184Sek110237 procflow_shutdown(); 4245184Sek110237 filebench_shm->f_abort = 1; 4255184Sek110237 ipc_ismdelete(); 4265184Sek110237 exit(error); 4275184Sek110237 } 4285184Sek110237 4295184Sek110237 /* 4305184Sek110237 * Put the hostname in ${hostname}. The system supplied 4315184Sek110237 * host name string is copied into an allocated string and 4325184Sek110237 * the pointer to the string is placed in the supplied 4335184Sek110237 * variable "var". If var->var_string already points to 4345184Sek110237 * a string, the string is freed. The routine always 4355184Sek110237 * returns zero (0). 4365184Sek110237 */ 4375184Sek110237 var_t * 4385184Sek110237 host_var(var_t *var) 4395184Sek110237 { 4405184Sek110237 char hoststr[128]; 4415184Sek110237 4425184Sek110237 (void) gethostname(hoststr, 128); 4435184Sek110237 if (var->var_string) 4445184Sek110237 free(var->var_string); 4455184Sek110237 var->var_string = fb_stralloc(hoststr); 4465184Sek110237 return (0); 4475184Sek110237 } 4485184Sek110237 4495184Sek110237 /* 4505184Sek110237 * Put the date string in ${date}. The system supplied date is 4515184Sek110237 * copied into an allocated string and the pointer to the string 4525184Sek110237 * is placed in the supplied var_t's var_string. If 4535184Sek110237 * var->var_string already points to a string, the string 4545184Sek110237 * is freed. The routine always returns a pointer to the 4555184Sek110237 * supplied var_t. 4565184Sek110237 */ 4575184Sek110237 var_t * 4585184Sek110237 date_var(var_t *var) 4595184Sek110237 { 4605184Sek110237 char datestr[128]; 4615184Sek110237 #ifdef HAVE_CFTIME 4625184Sek110237 time_t t = time(NULL); 4635184Sek110237 #else 4645184Sek110237 struct tm t; 4655184Sek110237 #endif 4665184Sek110237 4675184Sek110237 #ifdef HAVE_CFTIME 4685184Sek110237 cftime(datestr, "%y%m%d%H" "%M", &t); 4695184Sek110237 #else 4705184Sek110237 (void) strftime(datestr, sizeof (datestr), "%y%m%d%H %M", &t); 4715184Sek110237 #endif 4725184Sek110237 4735184Sek110237 if (var->var_string) 4745184Sek110237 free(var->var_string); 4755184Sek110237 var->var_string = fb_stralloc(datestr); 4765184Sek110237 4775184Sek110237 return (var); 4785184Sek110237 } 4795184Sek110237 4805184Sek110237 extern char *fscriptname; 4815184Sek110237 4825184Sek110237 /* 4835184Sek110237 * Put the script name in ${script}. The path name of the script 4845184Sek110237 * used with this filebench run trimmed of the trailing ".f" and 4855184Sek110237 * all leading subdirectories. The remaining script name is 4865184Sek110237 * copied into the var_string field of the supplied variable 4875184Sek110237 * "var". The routine always returns a pointer to the supplied 4885184Sek110237 * var_t. 4895184Sek110237 */ 4905184Sek110237 var_t * 4915184Sek110237 script_var(var_t *var) 4925184Sek110237 { 4935184Sek110237 char *scriptstr; 4945184Sek110237 char *f = fb_stralloc(fscriptname); 4955184Sek110237 4965184Sek110237 /* Trim the .f suffix */ 4975184Sek110237 for (scriptstr = f + strlen(f) - 1; scriptstr != f; scriptstr--) { 4985184Sek110237 if (*scriptstr == '.') { 4995184Sek110237 *scriptstr = 0; 5005184Sek110237 break; 5015184Sek110237 } 5025184Sek110237 } 5035184Sek110237 5045184Sek110237 var->var_string = fb_stralloc(basename(f)); 5055184Sek110237 free(f); 5065184Sek110237 5075184Sek110237 return (var); 5085184Sek110237 } 509