1/* 2 * Copyright (c) 2006 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29#include <unistd.h> 30#include <stdlib.h> 31#include <stdio.h> 32#include <stdbool.h> 33#include <errno.h> 34#include <string.h> 35 36// add additional headers needed here. 37 38#include "../libmicro.h" 39#include <membership.h> 40#include <pwd.h> 41#include <uuid/uuid.h> 42 43#if DEBUG 44# define debug(fmt, args...) (void) fprintf(stderr, fmt "\n" , ##args) 45#else 46# define debug(fmt, args...) 47#endif 48 49 50// Correct use case 51// 52// getpwnam -E -L -S -W -B 200 -C 10 -c 100 -r 300 -U test_user_ 53// 54// libMicro default benchmark run options are "-E -L -S -W -C 200" 55// 56// -B is batch size: loop iteration per each benchmark run. Needs to match # of 57// real lookups. This is total number of lookups to issue. 58// -C is min sample number: how many benchmark needs to run to get proper sample 59// 1 is mimumum, but you get at least 3 benchmark run 60// samples. Do not set to zero. Default is 200 for most 61// runs in libMicro. 62// -r is the number of total users 63// -c is the cache hit rate for lookup. set to 10%, you need -c 10. 64// ie. -B 100 -c 50 -r 1000 -C 200 (out of 1000 records, I want 50% 65// lookup, and batch size is 100. 66// To get 50% cache hit rate, you need 500 record lookups. 67// Batch size will be adjusted to 500 to get 500 record 68// lookup in each benchmark. If -r size is smaller than -B, 69// then -B will not be adjusted. 70// -u prefix: the user name prefix to use in front the user number as the 71// login name to lookup 72 73extern int gL1CacheEnabled; 74 75/* 76 * Your state variables should live in the tsd_t struct below 77 */ 78typedef struct { 79} tsd_t; 80 81// temporary buffer size 82#define BUFSIZE 200 83 84// the number of record lookup to issue is covered by standard option optB 85static int optRecords = 100; // the number of total records 86static int optCachehit = 100; // specify cache hit rate (% of record re-lookup) 87 88// This will use local users (local_test_*) 89static char *default_uprefix = "local_test_"; 90 91#define USERNAME_LEN 20 92static char *username_list; 93 94int 95benchmark_init() 96{ 97 debug("benchmark_init"); 98 (void) sprintf(lm_optstr, "l:c:r:u:"); 99 100 lm_tsdsize = sizeof (tsd_t); 101 lm_defB = 100; 102 103 (void) sprintf(lm_usage, 104 "\n ------- getpwnam specific options (default: *)\n" 105 " [-c hitrate%% (100%%*)]\n" 106 " [-r total number of records (100*)]\n" 107 " [-u username_prefix (local_test_)]\n" 108 "\n" ); 109 return (0); 110} 111 112/* 113 * This is where you parse your lower-case arguments. 114 */ 115int 116benchmark_optswitch(int opt, char *optarg) 117{ 118 debug("benchmark_optswitch"); 119 120 switch (opt) { 121 case 'c': // cache hit rate. 100% means lookup the same records over and over 122 optCachehit = atoi(optarg); 123 debug("optCachehit = %d\n", optCachehit); 124 if (optCachehit > 100 || optCachehit < 0) { 125 printf("cache hit rate should be in between 0%% and 100%%"); 126 return (-1); 127 } 128 break; 129 130 case 'l': 131 gL1CacheEnabled = atoi(optarg); 132 break; 133 134 case 'r': // total number of records. default is 100 135 optRecords = atoi(optarg); 136 debug("optRecords = %d\n", optRecords); 137 break; 138 139 case 'u': 140 default_uprefix = strdup(optarg); 141 debug("default_uprefix = %s\n", default_uprefix); 142 break; 143 144 default: 145 return -1; 146 } 147 148 return 0; 149} 150 151 152// Initialize all structures that will be used in benchmark() 153// moved template init from benchmark_initworker -> benchmark_initrun 154// since username_list is static across threads and processes 155// 156 157int 158benchmark_initrun() 159{ 160 int i; 161 162 debug("\nbenchmark_initrun"); 163 164 // Adjust # of record lookups to reflect cache hit rate 165 if (optCachehit < 100) { 166 optRecords = (int) ((float) optRecords * ((float) optCachehit / 100)); 167 debug("# of records adjusted to %d for cache hit rate %d%%\n", optRecords, optCachehit); 168 } 169 170 // if batch size (one benchmark run) is less than the number records, adjust 171 // it to match the number record lookups in one batch run 172 if (lm_optB < optRecords) { 173 lm_optB = optRecords; 174 debug("Adjusting batch size to %d to match the lookups required in benchmark run\n", lm_optB); 175 } 176 177 // create an array of usernames to use in benchmark before their use 178 // realtime generation in benchmark effects performance measurements 179 username_list = malloc( optRecords * USERNAME_LEN ); 180 if (!username_list) { 181 debug ("malloc error"); 182 exit (1); 183 } 184 185 for (i = 0; i < optRecords; i++) { 186 sprintf(&username_list[i*USERNAME_LEN], "%s%d", default_uprefix, i+1); 187 // debug("creating username %s", &username_list[i*USERNAME_LEN]); 188 } 189 190 return (0); 191} 192 193 194int 195benchmark(void *tsd, result_t *res) 196{ 197 int i, err; 198 struct passwd *passwd = NULL; 199 200 res->re_errors = 0; 201 202 debug("in to benchmark - optB = %i", lm_optB); 203 for (i = 0; i < lm_optB; i++) { 204 int index = (random() % optRecords) * USERNAME_LEN; 205 206 if (lm_optT > 1) { 207 struct passwd pd; 208 struct passwd *pwd_ptr = &pd; 209 struct passwd *tmp_ptr; 210 char pbuf[BUFSIZE]; 211 212 err = getpwnam_r( &username_list[index], pwd_ptr, pbuf, BUFSIZE, &tmp_ptr); 213 if (err) { 214 printf("error: %s -> %s", &username_list[index], strerror(err)); 215 res->re_errors++; 216 } 217 else if (!tmp_ptr) { 218 debug("not found: %s", &username_list[index]); 219 res->re_errors++; 220 } 221 } 222 else { 223 errno = 0; 224 passwd = getpwnam( &username_list[index] ); 225 226 if (!passwd) { 227 if (errno) { 228 debug("error: %s -> %s", &username_list[index], strerror(errno)); 229 res->re_errors++; 230 } 231 else { 232 debug("not found: %s", &username_list[index]); 233 res->re_errors++; 234 } 235 } 236 } 237 } 238 res->re_count = i; 239 240 return (0); 241} 242 243// We need to release all the structures we allocated in benchmark_initrun() 244int 245benchmark_finirun(void *tsd) 246{ 247 // tsd_t *ts = (tsd_t *)tsd; 248 debug("benchmark_finirun: deallocating structures"); 249 250 free (username_list); 251 252 return (0); 253} 254 255char * 256benchmark_result() 257{ 258 static char result = '\0'; 259 debug("benchmark_result"); 260 return (&result); 261} 262 263