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