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 <pwd.h>
40
41#if DEBUG
42# define debug(fmt, args...)    (void) fprintf(stderr, fmt "\n" , ##args)
43#else
44# define debug(fmt, args...)
45#endif
46
47
48// Correct use case
49//
50//    getpwuid -E  -L -S -W -B 200 -C 10 -c 100 -u 5000-5200
51//
52//      libMicro default benchmark run options are "-E -L -S -W -C 200"
53//
54// -B is batch size: loop iteration per each benchmark run. Needs to match # of
55//                   real lookups. This is total number of lookups to issue.
56// -C is min sample number: how many benchmark needs to run to get proper sample
57//                          1 is mimumum, but you get at least 3 benchmark run
58//                          samples. Do not set to zero. Default is 200 for most
59//                          runs in libMicro.
60// -c is the cache hit rate for lookup. set to 10%, you need -c 10.
61//                ie. -B 100 -c 50 -u 5000-5199
62//                     out of 200 UIDs, I want 50% cache hit, and batch size is 100.
63// -u uid range in the form of "min-max". For example, -u 5000-5200
64//
65
66extern int gL1CacheEnabled;
67
68/*
69 *    Your state variables should live in the tsd_t struct below
70 */
71typedef struct {
72} tsd_t;
73
74// temporary buffer size
75#define BUFSIZE 200
76#define INVALID_ID  -1
77
78static uid_t  uid_min = INVALID_ID;
79static int    uid_range = 0;  // uid_max = uid_min + uid_range
80
81// the number of record lookup to issue is covered by standard option optB
82static int    optCachehit =   100;  // specify cache hit rate (% of record re-lookup)
83
84int
85benchmark_init()
86{
87    debug("benchmark_init");
88    (void) sprintf(lm_optstr, "l:c:u:");
89
90    lm_tsdsize = sizeof (tsd_t);
91    lm_defB = 100;
92
93    (void) sprintf(lm_usage,
94                "\n     ------- getpwuid specific options (default: *)\n"
95                "       [-c hitrate%% (100%%*)]\n"
96                "       [-u UID range (min-max)]\n"
97                "       [-l]\n"
98                "\n" );
99    return (0);
100}
101
102int
103parse_range(uid_t *min, int *offset, char *buf)
104{
105    char *value, *tmp_ptr = strdup(buf);
106    int range=0;
107    debug("parse_range");
108
109    value = strsep(&tmp_ptr, "-");
110    *min = atoi(value);
111    debug("min = %d", *min);
112    if (tmp_ptr) {
113        value = strsep(&tmp_ptr, "-");
114        range = atoi(value);
115        if (range < *min) {
116            printf("max id should be larger than min id\n");
117            return -1;
118        }
119        *offset = range - *min + 1;
120        debug("range = %d", *offset);
121    }
122    else {
123        printf("argument should be in the form of min-max\n");
124        return -1;
125    }
126
127    return 0;
128}
129
130/*
131 * This is where you parse your lower-case arguments.
132 */
133int
134benchmark_optswitch(int opt, char *optarg)
135{
136    debug("benchmark_optswitch");
137
138    switch (opt) {
139    case 'c':    // cache hit rate. 100% means lookup the same records over and over
140        optCachehit = atoi(optarg);
141        debug("optCachehit = %d\n", optCachehit);
142        if (optCachehit > 100 || optCachehit < 0) {
143            printf("cache hit rate should be in between 0%% and 100%%");
144            return (-1);
145        }
146        break;
147
148    case 'l':
149        gL1CacheEnabled = atoi(optarg);
150        break;
151
152    case 'u':    // UID range
153        return parse_range( &uid_min, &uid_range, optarg);
154        break;
155
156    default:
157        return -1;
158    }
159
160    return 0;
161}
162
163
164// Initialize all structures that will be used in benchmark()
165// moved template init from benchmark_initworker -> benchmark_initrun
166//  since username_list is static across threads and processes
167//
168int
169benchmark_initrun()
170{
171    uid_t i, range;
172    struct passwd *passwd = NULL;
173
174    debug("\nbenchmark_initrun");
175
176    // To satisfy cache hit rate, lookup cachehit percentage of the UIDs here
177    if (optCachehit < 100) {
178
179        range = (int) ((float) uid_range * ((float) optCachehit / 100));
180        for (i = uid_min; i < uid_min+range; i++)
181            passwd = getpwuid( i );
182    }
183
184    return (0);
185}
186
187
188int
189benchmark(void *tsd, result_t *res)
190{
191    int         i, err;
192    struct passwd *passwd = NULL;
193
194    res->re_errors = 0;
195
196    debug("in to benchmark - optB = %i", lm_optB);
197    for (i = 0; i < lm_optB; i++) {
198        uid_t uid = uid_min + random() % uid_range ;
199
200        // XXX No need to use getpwuid_r() since getpwuid() is already thread-safe
201        // so it depends on what you want to exercise
202        if (lm_optT > 1) {
203            struct passwd pd;
204            struct passwd *pwd_ptr = &pd;
205            struct passwd *tmp_ptr;
206            char pbuf[BUFSIZE];
207
208            err = getpwuid_r( uid, pwd_ptr, pbuf, BUFSIZE, &tmp_ptr );
209            if (err) {
210                debug("error: %s", strerror(err));
211                res->re_errors++;
212            }
213            else if (!tmp_ptr) {
214                debug("not found: UID %d", uid);
215                res->re_errors++;
216            }
217        }
218        else {
219            errno = 0;
220            passwd = getpwuid( uid );
221
222            if (!passwd) {
223                if (errno) {
224                    debug("error: %s", strerror(errno));
225                    res->re_errors++;
226                }
227                else {
228                    debug("not found: UID %d", uid);
229                    res->re_errors++;
230                }
231            }
232        }
233    }
234    res->re_count = i;
235
236    return (0);
237}
238
239// We need to release all the structures we allocated in benchmark_initrun()
240int
241benchmark_finirun(void *tsd)
242{
243    // tsd_t    *ts = (tsd_t *)tsd;
244    debug("benchmark_finirun ");
245
246    return (0);
247}
248
249char *
250benchmark_result()
251{
252    static char    result = '\0';
253    debug("benchmark_result");
254    return (&result);
255}
256
257