1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* tests/threads/profread.c */
3 /*
4 * Copyright (C) 2009 by the Massachusetts Institute of Technology.
5 * All rights reserved.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26
27 /*
28 * krb5 profile data retrieval performance testing
29 * initially contributed by Ken Raeburn
30 */
31
32 #include "k5-platform.h"
33 #include <unistd.h>
34 #include <pthread.h>
35 #include <krb5.h>
36 #include <profile.h>
37
38 #include <sys/time.h>
39 #include <sys/resource.h>
40
41 #define N_THREADS 4
42 #define ITER_COUNT 40000
43 static int init_krb5_first = 0;
44
45 struct resource_info {
46 struct timeval start_time, end_time;
47 };
48 struct thread_info {
49 pthread_t tid;
50 struct resource_info r;
51 krb5_context ctx;
52 };
53
54 static char *prog;
55 static unsigned int n_threads = N_THREADS;
56 static int iter_count = ITER_COUNT;
57 static int do_pause;
58
59 static void usage (void) __attribute__((noreturn));
60
61 static void
usage()62 usage ()
63 {
64 fprintf (stderr, "usage: %s [ options ]\n", prog);
65 fprintf (stderr, "options:\n");
66 fprintf (stderr, "\t-t N\tspecify number of threads (default %d)\n",
67 N_THREADS);
68 fprintf (stderr, "\t-i N\tset iteration count (default %d)\n",
69 ITER_COUNT);
70 fprintf (stderr, "\t-K\tinitialize a krb5_context for the duration\n");
71 fprintf (stderr, "\t-P\tpause briefly after starting, to allow attaching dtrace/strace/etc\n");
72 exit (1);
73 }
74
75 static int
numarg(char * arg)76 numarg (char *arg)
77 {
78 char *end;
79 long val;
80
81 val = strtol (arg, &end, 10);
82 if (*arg == 0 || *end != 0) {
83 fprintf (stderr, "invalid numeric argument '%s'\n", arg);
84 usage ();
85 }
86 if (val >= 1 && val <= INT_MAX)
87 return val;
88 fprintf (stderr, "out of range numeric value %ld (1..%d)\n",
89 val, INT_MAX);
90 usage ();
91 }
92
93 static char optstring[] = "t:i:KP";
94
95 static void
process_options(int argc,char * argv[])96 process_options (int argc, char *argv[])
97 {
98 int c;
99
100 prog = strrchr (argv[0], '/');
101 if (prog)
102 prog++;
103 else
104 prog = argv[0];
105 while ((c = getopt (argc, argv, optstring)) != -1) {
106 switch (c) {
107 case '?':
108 case ':':
109 usage ();
110 break;
111
112 case 't':
113 n_threads = numarg (optarg);
114 if (n_threads >= SIZE_MAX / sizeof (struct thread_info)) {
115 n_threads = SIZE_MAX / sizeof (struct thread_info);
116 fprintf (stderr, "limiting n_threads to %u\n", n_threads);
117 }
118 break;
119
120 case 'i':
121 iter_count = numarg (optarg);
122 break;
123
124 case 'K':
125 init_krb5_first = 1;
126 break;
127
128 case 'P':
129 do_pause = 1;
130 break;
131 }
132 }
133 if (argc != optind)
134 usage ();
135 }
136
137 static long double
tvsub(struct timeval t1,struct timeval t2)138 tvsub (struct timeval t1, struct timeval t2)
139 {
140 /* POSIX says .tv_usec is signed. */
141 return (t1.tv_sec - t2.tv_sec
142 + (long double) 1.0e-6 * (t1.tv_usec - t2.tv_usec));
143 }
144
145 static struct timeval
now(void)146 now (void)
147 {
148 struct timeval tv;
149 if (gettimeofday (&tv, NULL) < 0) {
150 perror ("gettimeofday");
151 exit (1);
152 }
153 return tv;
154 }
155
run_iterations(struct resource_info * r)156 static void run_iterations (struct resource_info *r)
157 {
158 int i;
159 krb5_error_code err;
160 krb5_context ctx;
161 profile_t prof = NULL;
162
163 err = krb5_init_context(&ctx);
164 if (err) {
165 com_err(prog, err, "initializing krb5 context");
166 exit(1);
167 }
168 err = krb5_get_profile(ctx, &prof);
169 if (err) {
170 com_err(prog, err, "fetching profile from context");
171 exit(1);
172 }
173 r->start_time = now ();
174 for (i = 0; i < iter_count; i++) {
175 int ival;
176 err = profile_get_integer(prof, "one", "two", "three", 42, &ival);
177 if (err) {
178 com_err(prog, err, "fetching value from profile");
179 exit(1);
180 }
181 }
182 r->end_time = now ();
183 profile_release (prof);
184 krb5_free_context(ctx);
185 }
186
187 static void *
thread_proc(void * p)188 thread_proc (void *p)
189 {
190 run_iterations (p);
191 return 0;
192 }
193
194 static struct thread_info *tinfo;
195
196 static krb5_context kctx;
197 static struct rusage start, finish;
198 static struct timeval start_time, finish_time;
199
200 int
main(int argc,char * argv[])201 main (int argc, char *argv[])
202 {
203 long double user, sys, wallclock, total;
204 unsigned int i;
205
206 process_options (argc, argv);
207
208 /*
209 * Some places in the krb5 library cache data globally.
210 * This option allows you to test the effect of that.
211 */
212 if (init_krb5_first && krb5_init_context (&kctx) != 0) {
213 fprintf (stderr, "krb5_init_context error\n");
214 exit (1);
215 }
216 tinfo = calloc (n_threads, sizeof (*tinfo));
217 if (tinfo == NULL) {
218 perror ("calloc");
219 exit (1);
220 }
221 printf ("Threads: %d iterations: %d\n", n_threads, iter_count);
222 if (do_pause) {
223 printf ("pid %lu napping...\n", (unsigned long) getpid ());
224 sleep (10);
225 }
226 printf ("starting...\n");
227 /* And *now* we start measuring the performance. */
228 if (getrusage (RUSAGE_SELF, &start) < 0) {
229 perror ("getrusage");
230 exit (1);
231 }
232 start_time = now ();
233 #define foreach_thread(IDXVAR) for (IDXVAR = 0; IDXVAR < n_threads; IDXVAR++)
234 foreach_thread (i) {
235 int err;
236
237 err = pthread_create (&tinfo[i].tid, NULL, thread_proc, &tinfo[i].r);
238 if (err) {
239 fprintf (stderr, "pthread_create: %s\n", strerror (err));
240 exit (1);
241 }
242 }
243 foreach_thread (i) {
244 int err;
245 void *val;
246
247 err = pthread_join (tinfo[i].tid, &val);
248 if (err) {
249 fprintf (stderr, "pthread_join: %s\n", strerror (err));
250 exit (1);
251 }
252 }
253 finish_time = now ();
254 if (getrusage (RUSAGE_SELF, &finish) < 0) {
255 perror ("getrusage");
256 exit (1);
257 }
258 if (init_krb5_first)
259 krb5_free_context (kctx);
260 foreach_thread (i) {
261 printf ("Thread %2d: elapsed time %Lfs\n", i,
262 tvsub (tinfo[i].r.end_time, tinfo[i].r.start_time));
263 }
264 wallclock = tvsub (finish_time, start_time);
265 /*
266 * Report on elapsed time and CPU usage. Depending what
267 * performance issue you're chasing down, different values may be
268 * of particular interest, so report all the info we've got.
269 */
270 printf ("Overall run time with %d threads = %Lfs, %.2Lfus per iteration.\n",
271 n_threads, wallclock, 1000000 * wallclock / iter_count);
272 user = tvsub (finish.ru_utime, start.ru_utime);
273 sys = tvsub (finish.ru_stime, start.ru_stime);
274 total = user + sys;
275 printf ("CPU usage: user=%Lfs sys=%Lfs total=%Lfs.\n", user, sys, total);
276 printf ("Utilization: user=%5.1Lf%% sys=%5.1Lf%% total=%5.1Lf%%\n",
277 100 * user / wallclock,
278 100 * sys / wallclock,
279 100 * total / wallclock);
280 printf ("Util/thread: user=%5.1Lf%% sys=%5.1Lf%% total=%5.1Lf%%\n",
281 100 * user / wallclock / n_threads,
282 100 * sys / wallclock / n_threads,
283 100 * total / wallclock / n_threads);
284 printf ("Total CPU use per iteration per thread: %.2Lfus\n",
285 1000000 * total / n_threads / iter_count);
286 return 0;
287 }
288