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 <string.h> 34 35// add additional headers needed here. 36 37#include "../libmicro.h" 38#include <CoreFoundation/CFArray.h> 39#include <CoreFoundation/CFString.h> 40#include <CoreFoundation/CFDictionary.h> 41#include <OpenDirectory/OpenDirectory.h> 42#include <DirectoryService/DirectoryService.h> 43 44#if DEBUG 45# define debug(fmt, args...) (void) fprintf(stderr, fmt , ##args) 46// # define debug(fmt, args...) (void) fprintf(stderr, fmt "\n" , ##args) 47#else 48# define debug(fmt, args...) 49#endif 50 51 52// Correct use case 53// 54// od_query_create_with_node -E -L -S -W -B 200 -C 10 -c 100 -r 300 55// 56// libMicro default benchmark run options are "-E -C 200 -L -S -W" 57// 58// -B is batch size: loop iteration per each benchmark run. Needs to match # of 59// real lookups. This is total number of lookups to issue. 60// -C is min sample number: how many benchmark needs to run to get proper sample 61// 1 is mimumum, but you get at least 3 benchmark run 62// samples. Do not set to zero. Default is 200 for most 63// runs in libMicro. 64// -r is the number of total records. 65// -c is the cache hit rate for lookup. set to 10%, you need -c 10. 66// ie. -B 100 -c 50 -r 1000 -C 200 (out of 1000 records, I want 50% 67// lookup, and batch size is 100. 68// To get 50% cache hit rate, you need 500 record lookups. 69// Batch size will be adjusted to 500 to get 500 record 70// lookup in each benchmark. If -r size is smaller than -B, 71// then -B will not be adjusted. 72 73// Defining prefix for user and group name 74// make sure that these match the ones in LDAP records 75// ie. local_test_1 , od_test_4525, od_test_group_43, od_test_host_63 76#define LOCAL_U_PREFIX CFSTR("local_test_") 77#define OD_U_PREFIX CFSTR("od_test_") 78#define LOCAL_G_PREFIX CFSTR("local_test_group_") 79#define OD_G_PREFIX CFSTR("od_test_group_") 80#define LOCAL_H_PREFIX CFSTR("local_test_host_") 81#define OD_H_PREFIX CFSTR("od_test_host_") 82 83/* 84 * Your state variables should live in the tsd_t struct below 85 */ 86typedef struct { 87 ODNodeRef node; 88} tsd_t; 89 90// dsRecTypeStandard type dictionary 91enum {rectype_users=0, rectype_groups, rectype_hosts}; 92CFStringRef rectype_dict[] = { CFSTR(kDSStdRecordTypeUsers), 93 CFSTR(kDSStdRecordTypeGroups), 94 CFSTR(kDSStdRecordTypeHosts) }; 95 96// the number of record lookup to issue is covered by standard option optB 97static int optRecords = 100; // the number of total records 98static int optCachehit = 100; // specify cache hit rate (% of record re-lookup) 99static bool optNodeLocal = 1; // which node to search. Local node is default 100static int optType = rectype_users; // dsRecType to search for. "Users"" is the default 101static const char *nodename = "/LDAPv3/127.0.0.1"; 102 103static CFStringRef *key; // username array 104 105// parse -t option and return enum type: user, group, and host 106// called by benchmark_optswitch() 107int 108ds_rec_type(char *name) 109{ 110 if (strcasecmp("u", name) == 0) { 111 return (rectype_users); 112 } else if (strcasecmp("g", name) == 0) { 113 return (rectype_groups); 114 } else if (strcasecmp("h", name) == 0) { 115 return (rectype_hosts); 116 } 117 118 return (-1); 119} 120 121int 122benchmark_init() 123{ 124 debug("benchmark_init"); 125 (void) sprintf(lm_optstr, "c:n:r:t:"); 126 127 lm_tsdsize = sizeof (tsd_t); 128 lm_defB = 1000; 129 130 (void) sprintf(lm_usage, 131 "\n ------- od_query_create_with_node specific options (default: *)\n" 132 " [-c hitrate%% (100%%*)]\n" 133 " [-r total number of records (100*)]\n" 134 " [-n nodename] node name to use for test\n" 135 " [-t record type: 'u'sers, 'g'roups, 'h'osts]\n" 136 " use -B option to specify total number of record lookups to issue" 137 "\n" ); 138 return (0); 139} 140 141/* 142 * This is where you parse your lower-case arguments. 143 */ 144int 145benchmark_optswitch(int opt, char *optarg) 146{ 147 debug("benchmark_optswitch"); 148 149 switch (opt) { 150 case 'c': // cache hit rate. 100% means lookup the same records over and over 151 optCachehit = atoi(optarg); 152 debug("optCachehit = %d\n", optCachehit); 153 if (optCachehit > 100 || optCachehit < 0) { 154 printf("cache hit rate should be in between 0%% and 100%%"); 155 return (-1); 156 } 157 break; 158 159 case 'r': // total number of records. default is 100 160 optRecords = atoi(optarg); 161 debug("optRecords = %d\n", optRecords); 162 break; 163 164 case 'n': // node 165 nodename = optarg; 166 break; 167 168 case 't': // dsRecType: user, group, hots 169 optType = ds_rec_type(optarg); 170 debug("optType = %d\n", optType); 171 172 if (optType == -1) { 173 printf("wrong -t record type option\n"); 174 return (-1); 175 } 176 break; 177 178 default: 179 return (-1); 180 } 181 182 return (0); 183} 184 185 186int 187benchmark_initrun() 188{ 189 int i; 190 CFStringRef prefix; // local user is default 191 192 debug("benchmark_initrun\n"); 193 194 // Adjust # of record lookups to reflect cache hit rate 195 if (optCachehit < 100) { 196 optRecords = (int) ((float) optRecords * ((float) optCachehit / 100)); 197 debug("# of records adjusted to %d for cache hit rate %d%%\n", optRecords, optCachehit); 198 } 199 200 // if batch size (one benchmark run) is less than the number records, adjust 201 // it to match the number record lookups in one batch run 202 if (lm_optB < optRecords) { 203 lm_optB = optRecords; 204 debug("Adjusting batch size to %d to match the lookups required in benchmark run\n", lm_optB); 205 } 206 207 switch (optType) { 208 case rectype_users: 209 prefix = (optNodeLocal) ? LOCAL_U_PREFIX : OD_U_PREFIX; 210 break; 211 case rectype_groups: 212 prefix = (optNodeLocal) ? LOCAL_G_PREFIX : OD_G_PREFIX; 213 break; 214 case rectype_hosts: 215 prefix = (optNodeLocal) ? LOCAL_H_PREFIX : OD_H_PREFIX; 216 break; 217 } 218 // create an array of usernames to use in benchmark before their use 219 // realtime generation in benchmark effects performance measurements 220 221 key = malloc(sizeof(CFStringRef) * optRecords); 222 223 // user, group, hosts key to lookup 224 switch (optType) { 225 226 case rectype_users: // users query 227 case rectype_groups: // groups query 228 case rectype_hosts: // hosts query 229 for (i = 0; i < optRecords; i++) { 230 key[i] = CFStringCreateWithFormat( kCFAllocatorDefault, 231 NULL, 232 CFSTR("%@%d"), 233 prefix, 234 i+1); 235 // CFShow(key[i]); // print user name to check 236 } 237 break; 238 } 239 240 return (0); 241} 242 243 244// Initialize all structures that will be used in benchmark() 245// 1. make local or network node for OD query 246// 2. create user key 247int 248benchmark_initworker(void *tsd) 249{ 250 CFErrorRef error; 251 tsd_t *ts = (tsd_t *)tsd; 252 253 debug("benchmark_initworker: %s", (optNodeLocal) ? "local" : "network"); 254 255 256 // create OD node for local or OD query 257 if (optNodeLocal) { 258 ts->node = ODNodeCreateWithNodeType(NULL, kODSessionDefault, kODNodeTypeLocalNodes, &error); 259 } 260 else { 261 CFStringRef nodenameStr = CFStringCreateWithCString(kCFAllocatorDefault, nodename, kCFStringEncodingUTF8); 262 ts->node = ODNodeCreateWithName(NULL, kODSessionDefault, nodenameStr, &error); 263 CFRelease(nodenameStr); 264 } 265 266 if (!ts->node) { 267 debug("error calling ODNodeCreateWithNodeType\n"); 268 exit(1); 269 } 270 271 CFRetain (ts->node); 272 273 debug("benchmark_initworker: ODNodeRef = 0x%lx\n", ts->node); 274 return (0); 275} 276 277int 278benchmark(void *tsd, result_t *res) 279{ 280 281 tsd_t *ts = (tsd_t *)tsd; 282 int i; 283 ODNodeRef node; 284 CFErrorRef error; 285 CFArrayRef results; 286 ODQueryRef query; 287 288 res->re_errors = 0; 289 node = ts->node; 290 291 debug("in to benchmark - optB = %i, node = 0x%lx \n", lm_optB, node); 292 for (i = 0; i < lm_optB; i++) { 293 294 debug("loop %d: querying\n", i); 295 query = ODQueryCreateWithNode(NULL, 296 node, // inNode 297 rectype_dict[optType], // inRecordTypeOrList 298 CFSTR(kDSNAttrRecordName), // inAttribute 299 kODMatchInsensitiveEqualTo, // inMatchType 300 key[i % optRecords], // inQueryValueOrList 301 NULL, // inReturnAttributeOrList 302 1, // inMaxResults 303 &error); 304 305 if (query) { 306 // we do not want to factually fetch the result in benchmark run 307 // debug("loop %d: calling ODQueryCopyResults\n", i); 308 results = ODQueryCopyResults(query, FALSE, &error); 309 CFRelease(query); 310 if (results) { 311#if DEBUG 312 int c; 313 c = CFArrayGetCount(results); 314 if (c > 0) { 315 debug("Successful run: %d results, ", c); 316 } 317 else { 318 debug("no result for "); 319 } 320 CFShow (key[i % optRecords]); 321 debug("\n"); 322#endif 323 CFRelease(results); 324 } 325 else { 326 debug("loop %d: ODQueryCopyResults returned empty result for ", i); 327 res->re_errors++; 328 CFShow (key[i % optRecords]); 329 debug("\n"); 330 } // if (results) 331 332 } // if (query) 333 else { 334 res->re_errors++; 335 } 336 } 337 res->re_count = i; 338 339 return (0); 340} 341 342 343// We need to release all the structures we allocated in benchmark_initworker() 344int 345benchmark_finiworker(void *tsd) 346{ 347 tsd_t *ts = (tsd_t *)tsd; 348 349 debug("benchmark_result: deallocating structures\n"); 350 351 // free the node 352 if (ts->node) 353 CFRelease (ts->node); 354 ts->node = NULL; 355 356 return (0); 357} 358 359int 360benchmark_finirun() 361{ 362 int i; 363 364 for (i = 0; i < optRecords; i++){ 365 CFRelease(key[i]); 366 } 367 368 free(key); 369 370 return (0); 371} 372 373char * 374benchmark_result() 375{ 376 static char result = '\0'; 377 debug("\n\n# of records adjusted to %d for cache hit rate %d%%\n", optRecords, optCachehit); 378 debug("benchmark_result\n"); 379 return (&result); 380} 381 382