xref: /freebsd/crypto/krb5/src/tests/threads/profread.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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