]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/libMicro/apple/od_query_create_with_node.c
7cf18ce6dde973f7ef2b2bdece1a6477e98563cd
[apple/xnu.git] / tools / tests / libMicro / apple / od_query_create_with_node.c
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 */
86 typedef struct {
87 ODNodeRef node;
88 } tsd_t;
89
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) };
95
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";
102
103 static CFStringRef *key; // username array
104
105 // parse -t option and return enum type: user, group, and host
106 // called by benchmark_optswitch()
107 int
108 ds_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
121 int
122 benchmark_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 */
144 int
145 benchmark_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
186 int
187 benchmark_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
247 int
248 benchmark_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
277 int
278 benchmark(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()
344 int
345 benchmark_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
359 int
360 benchmark_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
373 char *
374 benchmark_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