xref: /freebsd/crypto/krb5/src/tests/threads/init_ctx.c (revision b670c9bafc0e31c7609969bf374b2e80bdc00211)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* tests/threads/init_ctx.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 context creation 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 
37 #include <sys/time.h>
38 #include <sys/resource.h>
39 
40 #define N_THREADS 4
41 #define ITER_COUNT 40000
42 static int init_krb5_first = 0;
43 
44 struct resource_info {
45     struct timeval start_time, end_time;
46 };
47 struct thread_info {
48     pthread_t tid;
49     struct resource_info r;
50 };
51 
52 static char *prog;
53 static unsigned int n_threads = N_THREADS;
54 static int iter_count = ITER_COUNT;
55 static int do_pause;
56 
57 static void usage (void) __attribute__((noreturn));
58 
59 static void
60 usage ()
61 {
62     fprintf (stderr, "usage: %s [ options ]\n", prog);
63     fprintf (stderr, "options:\n");
64     fprintf (stderr, "\t-t N\tspecify number of threads (default %d)\n",
65              N_THREADS);
66     fprintf (stderr, "\t-i N\tset iteration count (default %d)\n",
67              ITER_COUNT);
68     fprintf (stderr, "\t-K\tinitialize a krb5_context for the duration\n");
69     fprintf (stderr, "\t-P\tpause briefly after starting, to allow attaching dtrace/strace/etc\n");
70     exit (1);
71 }
72 
73 static int
74 numarg (char *arg)
75 {
76     char *end;
77     long val;
78 
79     val = strtol (arg, &end, 10);
80     if (*arg == 0 || *end != 0) {
81         fprintf (stderr, "invalid numeric argument '%s'\n", arg);
82         usage ();
83     }
84     if (val >= 1 && val <= INT_MAX)
85         return val;
86     fprintf (stderr, "out of range numeric value %ld (1..%d)\n",
87              val, INT_MAX);
88     usage ();
89 }
90 
91 static char optstring[] = "t:i:KP";
92 
93 static void
94 process_options (int argc, char *argv[])
95 {
96     int c;
97 
98     prog = strrchr (argv[0], '/');
99     if (prog)
100         prog++;
101     else
102         prog = argv[0];
103     while ((c = getopt (argc, argv, optstring)) != -1) {
104         switch (c) {
105         case '?':
106         case ':':
107             usage ();
108             break;
109 
110         case 't':
111             n_threads = numarg (optarg);
112             if (n_threads >= SIZE_MAX / sizeof (struct thread_info)) {
113                 n_threads = SIZE_MAX / sizeof (struct thread_info);
114                 fprintf (stderr, "limiting n_threads to %u\n", n_threads);
115             }
116             break;
117 
118         case 'i':
119             iter_count = numarg (optarg);
120             break;
121 
122         case 'K':
123             init_krb5_first = 1;
124             break;
125 
126         case 'P':
127             do_pause = 1;
128             break;
129         }
130     }
131     if (argc != optind)
132         usage ();
133 }
134 
135 static long double
136 tvsub (struct timeval t1, struct timeval t2)
137 {
138     /* POSIX says .tv_usec is signed.  */
139     return (t1.tv_sec - t2.tv_sec
140             + (long double) 1.0e-6 * (t1.tv_usec - t2.tv_usec));
141 }
142 
143 static struct timeval
144 now (void)
145 {
146     struct timeval tv;
147     if (gettimeofday (&tv, NULL) < 0) {
148         perror ("gettimeofday");
149         exit (1);
150     }
151     return tv;
152 }
153 
154 static void run_iterations (struct resource_info *r)
155 {
156     int i;
157     krb5_error_code err;
158     krb5_context ctx;
159 
160     r->start_time = now ();
161     for (i = 0; i < iter_count; i++) {
162         err = krb5_init_context(&ctx);
163         if (err) {
164             com_err(prog, err, "initializing krb5 context");
165             exit(1);
166         }
167         krb5_free_context(ctx);
168     }
169     r->end_time = now ();
170 }
171 
172 static void *
173 thread_proc (void *p)
174 {
175     run_iterations (p);
176     return 0;
177 }
178 
179 static struct thread_info *tinfo;
180 
181 static krb5_context kctx;
182 static struct rusage start, finish;
183 static struct timeval start_time, finish_time;
184 
185 int
186 main (int argc, char *argv[])
187 {
188     long double user, sys, wallclock, total;
189     unsigned int i;
190 
191     process_options (argc, argv);
192 
193     /*
194      * Some places in the krb5 library cache data globally.
195      * This option allows you to test the effect of that.
196      */
197     if (init_krb5_first && krb5_init_context (&kctx) != 0) {
198         fprintf (stderr, "krb5_init_context error\n");
199         exit (1);
200     }
201     tinfo = calloc (n_threads, sizeof (*tinfo));
202     if (tinfo == NULL) {
203         perror ("calloc");
204         exit (1);
205     }
206     printf ("Threads: %d  iterations: %d\n", n_threads, iter_count);
207     if (do_pause) {
208         printf ("pid %lu napping...\n", (unsigned long) getpid ());
209         sleep (10);
210     }
211     printf ("starting...\n");
212     /* And *now* we start measuring the performance.  */
213     if (getrusage (RUSAGE_SELF, &start) < 0) {
214         perror ("getrusage");
215         exit (1);
216     }
217     start_time = now ();
218 #define foreach_thread(IDXVAR) for (IDXVAR = 0; IDXVAR < n_threads; IDXVAR++)
219     foreach_thread (i) {
220         int err;
221 
222         err = pthread_create (&tinfo[i].tid, NULL, thread_proc, &tinfo[i].r);
223         if (err) {
224             fprintf (stderr, "pthread_create: %s\n", strerror (err));
225             exit (1);
226         }
227     }
228     foreach_thread (i) {
229         int err;
230         void *val;
231 
232         err = pthread_join (tinfo[i].tid, &val);
233         if (err) {
234             fprintf (stderr, "pthread_join: %s\n", strerror (err));
235             exit (1);
236         }
237     }
238     finish_time = now ();
239     if (getrusage (RUSAGE_SELF, &finish) < 0) {
240         perror ("getrusage");
241         exit (1);
242     }
243     if (init_krb5_first)
244         krb5_free_context (kctx);
245     foreach_thread (i) {
246         printf ("Thread %2d: elapsed time %Lfs\n", i,
247                 tvsub (tinfo[i].r.end_time, tinfo[i].r.start_time));
248     }
249     wallclock = tvsub (finish_time, start_time);
250     /*
251      * Report on elapsed time and CPU usage.  Depending what
252      * performance issue you're chasing down, different values may be
253      * of particular interest, so report all the info we've got.
254      */
255     printf ("Overall run time with %d threads = %Lfs, %Lfms per iteration.\n",
256             n_threads, wallclock, 1000 * wallclock / iter_count);
257     user = tvsub (finish.ru_utime, start.ru_utime);
258     sys = tvsub (finish.ru_stime, start.ru_stime);
259     total = user + sys;
260     printf ("CPU usage:   user=%Lfs sys=%Lfs total=%Lfs.\n", user, sys, total);
261     printf ("Utilization: user=%5.1Lf%% sys=%5.1Lf%% total=%5.1Lf%%\n",
262             100 * user / wallclock,
263             100 * sys / wallclock,
264             100 * total / wallclock);
265     printf ("Util/thread: user=%5.1Lf%% sys=%5.1Lf%% total=%5.1Lf%%\n",
266             100 * user / wallclock / n_threads,
267             100 * sys / wallclock / n_threads,
268             100 * total / wallclock / n_threads);
269     printf ("Total CPU use per iteration per thread: %Lfms\n",
270             1000 * total / n_threads / iter_count);
271     free(tinfo);
272     return 0;
273 }
274