2 * Copyright (c) 2006 Apple Inc. All Rights Reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
35 // add additional headers needed here.
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>
45 # define debug(fmt, args...) (void) fprintf(stderr, fmt , ##args)
46 // # define debug(fmt, args...) (void) fprintf(stderr, fmt "\n" , ##args)
48 # define debug(fmt, args...)
54 // od_query_create_with_node -E -L -S -W -B 200 -C 10 -c 100 -r 300
56 // libMicro default benchmark run options are "-E -C 200 -L -S -W"
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
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.
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_")
84 * Your state variables should live in the tsd_t struct below
90 // dsRecTypeStandard type dictionary
91 enum {rectype_users
=0, rectype_groups
, rectype_hosts
};
92 CFStringRef rectype_dict
[] = { CFSTR(kDSStdRecordTypeUsers
),
93 CFSTR(kDSStdRecordTypeGroups
),
94 CFSTR(kDSStdRecordTypeHosts
) };
96 // the number of record lookup to issue is covered by standard option optB
97 static int optRecords
= 100; // the number of total records
98 static int optCachehit
= 100; // specify cache hit rate (% of record re-lookup)
99 static bool optNodeLocal
= 1; // which node to search. Local node is default
100 static int optType
= rectype_users
; // dsRecType to search for. "Users"" is the default
101 static const char *nodename
= "/LDAPv3/127.0.0.1";
103 static CFStringRef
*key
; // username array
105 // parse -t option and return enum type: user, group, and host
106 // called by benchmark_optswitch()
108 ds_rec_type(char *name
)
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
);
124 debug("benchmark_init");
125 (void) sprintf(lm_optstr
, "c:n:r:t:");
127 lm_tsdsize
= sizeof (tsd_t
);
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"
142 * This is where you parse your lower-case arguments.
145 benchmark_optswitch(int opt
, char *optarg
)
147 debug("benchmark_optswitch");
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%%");
159 case 'r': // total number of records. default is 100
160 optRecords
= atoi(optarg
);
161 debug("optRecords = %d\n", optRecords
);
168 case 't': // dsRecType: user, group, hots
169 optType
= ds_rec_type(optarg
);
170 debug("optType = %d\n", optType
);
173 printf("wrong -t record type option\n");
190 CFStringRef prefix
; // local user is default
192 debug("benchmark_initrun\n");
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
);
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
);
209 prefix
= (optNodeLocal
) ? LOCAL_U_PREFIX
: OD_U_PREFIX
;
212 prefix
= (optNodeLocal
) ? LOCAL_G_PREFIX
: OD_G_PREFIX
;
215 prefix
= (optNodeLocal
) ? LOCAL_H_PREFIX
: OD_H_PREFIX
;
218 // create an array of usernames to use in benchmark before their use
219 // realtime generation in benchmark effects performance measurements
221 key
= malloc(sizeof(CFStringRef
) * optRecords
);
223 // user, group, hosts key to lookup
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
,
235 // CFShow(key[i]); // print user name to check
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
248 benchmark_initworker(void *tsd
)
251 tsd_t
*ts
= (tsd_t
*)tsd
;
253 debug("benchmark_initworker: %s", (optNodeLocal
) ? "local" : "network");
256 // create OD node for local or OD query
258 ts
->node
= ODNodeCreateWithNodeType(NULL
, kODSessionDefault
, kODNodeTypeLocalNodes
, &error
);
261 CFStringRef nodenameStr
= CFStringCreateWithCString(kCFAllocatorDefault
, nodename
, kCFStringEncodingUTF8
);
262 ts
->node
= ODNodeCreateWithName(NULL
, kODSessionDefault
, nodenameStr
, &error
);
263 CFRelease(nodenameStr
);
267 debug("error calling ODNodeCreateWithNodeType\n");
273 debug("benchmark_initworker: ODNodeRef = 0x%lx\n", ts
->node
);
278 benchmark(void *tsd
, result_t
*res
)
281 tsd_t
*ts
= (tsd_t
*)tsd
;
291 debug("in to benchmark - optB = %i, node = 0x%lx \n", lm_optB
, node
);
292 for (i
= 0; i
< lm_optB
; i
++) {
294 debug("loop %d: querying\n", i
);
295 query
= ODQueryCreateWithNode(NULL
,
297 rectype_dict
[optType
], // inRecordTypeOrList
298 CFSTR(kDSNAttrRecordName
), // inAttribute
299 kODMatchInsensitiveEqualTo
, // inMatchType
300 key
[i
% optRecords
], // inQueryValueOrList
301 NULL
, // inReturnAttributeOrList
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
);
313 c
= CFArrayGetCount(results
);
315 debug("Successful run: %d results, ", c
);
318 debug("no result for ");
320 CFShow (key
[i
% optRecords
]);
326 debug("loop %d: ODQueryCopyResults returned empty result for ", i
);
328 CFShow (key
[i
% optRecords
]);
343 // We need to release all the structures we allocated in benchmark_initworker()
345 benchmark_finiworker(void *tsd
)
347 tsd_t
*ts
= (tsd_t
*)tsd
;
349 debug("benchmark_result: deallocating structures\n");
353 CFRelease (ts
->node
);
364 for (i
= 0; i
< optRecords
; i
++){
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");