1/*
2 * "$Id: testspeed.c 11560 2014-02-06 20:10:19Z msweet $"
3 *
4 * Scheduler speed test for CUPS.
5 *
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 1997-2005 by Easy Software Products.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file.  If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 */
15
16/*
17 * Include necessary headers...
18 */
19
20#include <cups/string-private.h>
21#include <cups/cups.h>
22#include <cups/language.h>
23#include <cups/debug-private.h>
24#include <sys/types.h>
25#include <sys/time.h>
26#include <sys/wait.h>
27
28
29/*
30 * Local functions...
31 */
32
33static int	do_test(const char *server, int port,
34		        http_encryption_t encryption, int requests,
35			const char *opstring, int verbose);
36static void	usage(void) __attribute__((noreturn));
37
38
39/*
40 * 'main()' - Send multiple IPP requests and report on the average response
41 *            time.
42 */
43
44int
45main(int  argc,				/* I - Number of command-line arguments */
46     char *argv[])			/* I - Command-line arguments */
47{
48  int		i;			/* Looping var */
49  char		*server,		/* Server to use */
50		*ptr;			/* Pointer to port in server */
51  int		port;			/* Port to use */
52  http_encryption_t encryption;		/* Encryption to use */
53  int		requests;		/* Number of requests to send */
54  int		children;		/* Number of children to fork */
55  int		good_children;		/* Number of children that exited normally */
56  int		pid;			/* Child PID */
57  int		status;			/* Child status */
58  time_t	start,			/* Start time */
59		end;			/* End time */
60  double	elapsed;		/* Elapsed time */
61  int		verbose;		/* Verbosity */
62  const char	*opstring;		/* Operation name */
63
64
65 /*
66  * Parse command-line options...
67  */
68
69  requests   = 100;
70  children   = 5;
71  server     = (char *)cupsServer();
72  port       = ippPort();
73  encryption = HTTP_ENCRYPT_IF_REQUESTED;
74  verbose    = 0;
75  opstring   = NULL;
76
77  for (i = 1; i < argc; i ++)
78    if (argv[i][0] == '-')
79    {
80      for (ptr = argv[i] + 1; *ptr; ptr ++)
81        switch (*ptr)
82	{
83	  case 'E' : /* Enable encryption */
84	      encryption = HTTP_ENCRYPT_REQUIRED;
85	      break;
86
87	  case 'c' : /* Number of children */
88	      i ++;
89	      if (i >= argc)
90		usage();
91
92	      children = atoi(argv[i]);
93	      break;
94
95          case 'o' : /* Operation */
96	      i ++;
97	      if (i >= argc)
98		usage();
99
100	      opstring = argv[i];
101	      break;
102
103          case 'r' : /* Number of requests */
104	      i ++;
105	      if (i >= argc)
106		usage();
107
108	      requests = atoi(argv[i]);
109	      break;
110
111          case 'v' : /* Verbose logging */
112              verbose ++;
113	      break;
114
115          default :
116              usage();
117	      break;
118        }
119    }
120    else
121    {
122      server = argv[i];
123
124      if (server[0] != '/' && (ptr = strrchr(server, ':')) != NULL)
125      {
126        *ptr++ = '\0';
127	port   = atoi(ptr);
128      }
129    }
130
131 /*
132  * Then create child processes to act as clients...
133  */
134
135  if (children > 0)
136  {
137    printf("testspeed: Simulating %d clients with %d requests to %s with "
138           "%sencryption...\n", children, requests, server,
139	   encryption == HTTP_ENCRYPT_IF_REQUESTED ? "no " : "");
140  }
141
142  start = time(NULL);
143
144  if (children < 1)
145    return (do_test(server, port, encryption, requests, opstring, verbose));
146  else if (children == 1)
147    good_children = do_test(server, port, encryption, requests, opstring,
148                            verbose) ? 0 : 1;
149  else
150  {
151    char	options[255],		/* Command-line options for child */
152		reqstr[255],		/* Requests string for child */
153		serverstr[255];		/* Server:port string for child */
154
155
156    snprintf(reqstr, sizeof(reqstr), "%d", requests);
157
158    if (port == 631 || server[0] == '/')
159      strlcpy(serverstr, server, sizeof(serverstr));
160    else
161      snprintf(serverstr, sizeof(serverstr), "%s:%d", server, port);
162
163    strlcpy(options, "-cr", sizeof(options));
164
165    if (encryption == HTTP_ENCRYPT_REQUIRED)
166      strlcat(options, "E", sizeof(options));
167
168    if (verbose)
169      strlcat(options, "v", sizeof(options));
170
171    for (i = 0; i < children; i ++)
172    {
173      fflush(stdout);
174
175      if ((pid = fork()) == 0)
176      {
177       /*
178	* Child goes here...
179	*/
180
181        if (opstring)
182	  execlp(argv[0], argv[0], options, "0", reqstr, "-o", opstring,
183	         serverstr, (char *)NULL);
184        else
185	  execlp(argv[0], argv[0], options, "0", reqstr, serverstr, (char *)NULL);
186
187	exit(errno);
188      }
189      else if (pid < 0)
190      {
191	printf("testspeed: Fork failed: %s\n", strerror(errno));
192	break;
193      }
194      else
195	printf("testspeed: Started child %d...\n", pid);
196    }
197
198   /*
199    * Wait for children to finish...
200    */
201
202    puts("testspeed: Waiting for children to finish...");
203
204    for (good_children = 0;;)
205    {
206      pid = wait(&status);
207
208      if (pid < 0 && errno != EINTR)
209	break;
210
211      printf("testspeed: Ended child %d (%d)...\n", pid, status / 256);
212
213      if (!status)
214        good_children ++;
215    }
216  }
217
218 /*
219  * Compute the total run time...
220  */
221
222  if (good_children > 0)
223  {
224    end     = time(NULL);
225    elapsed = end - start;
226    i       = good_children * requests;
227
228    printf("testspeed: %dx%d=%d requests in %.1fs (%.3fs/r, %.1fr/s)\n",
229	   good_children, requests, i, elapsed, elapsed / i, i / elapsed);
230  }
231
232 /*
233  * Exit with no errors...
234  */
235
236  return (0);
237}
238
239
240/*
241 * 'do_test()' - Run a test on a specific host...
242 */
243
244static int				/* O - Exit status */
245do_test(const char        *server,	/* I - Server to use */
246        int               port,		/* I - Port number to use */
247        http_encryption_t encryption,	/* I - Encryption to use */
248	int               requests,	/* I - Number of requests to send */
249	const char        *opstring,	/* I - Operation string */
250	int               verbose)	/* I - Verbose output? */
251{
252  int		i;			/* Looping var */
253  http_t	*http;			/* Connection to server */
254  ipp_t		*request;		/* IPP Request */
255  struct timeval start,			/* Start time */
256		end;			/* End time */
257  double	reqtime,		/* Time for this request */
258		elapsed;		/* Elapsed time */
259  int		op;			/* Current operation */
260  static ipp_op_t ops[5] =		/* Operations to test... */
261		{
262		  IPP_PRINT_JOB,
263		  CUPS_GET_DEFAULT,
264		  CUPS_GET_PRINTERS,
265		  CUPS_GET_CLASSES,
266		  IPP_GET_JOBS
267		};
268
269
270 /*
271  * Connect to the server...
272  */
273
274  if ((http = httpConnectEncrypt(server, port, encryption)) == NULL)
275  {
276    printf("testspeed(%d): unable to connect to server - %s\n", (int)getpid(),
277           strerror(errno));
278    return (1);
279  }
280
281 /*
282  * Do multiple requests...
283  */
284
285  for (elapsed = 0.0, i = 0; i < requests; i ++)
286  {
287   /*
288    * Build a request which requires the following attributes:
289    *
290    *    attributes-charset
291    *    attributes-natural-language
292    *
293    * In addition, IPP_GET_JOBS needs a printer-uri attribute.
294    */
295
296    if (opstring)
297      op = ippOpValue(opstring);
298    else
299      op = ops[i % (int)(sizeof(ops) / sizeof(ops[0]))];
300
301    request = ippNewRequest(op);
302
303    gettimeofday(&start, NULL);
304
305    if (verbose)
306      printf("testspeed(%d): %.6f %s ", (int)getpid(), elapsed,
307	     ippOpString(op));
308
309    switch (op)
310    {
311      case IPP_GET_JOBS :
312	  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
313                       NULL, "ipp://localhost/printers/");
314
315      default :
316	  ippDelete(cupsDoRequest(http, request, "/"));
317          break;
318
319      case IPP_PRINT_JOB :
320	  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
321                       NULL, "ipp://localhost/printers/test");
322	  ippDelete(cupsDoFileRequest(http, request, "/printers/test",
323	                              "../data/testprint.ps"));
324          break;
325    }
326
327    gettimeofday(&end, NULL);
328
329    reqtime = (end.tv_sec - start.tv_sec) +
330              0.000001 * (end.tv_usec - start.tv_usec);
331    elapsed += reqtime;
332
333    switch (cupsLastError())
334    {
335      case IPP_OK :
336      case IPP_NOT_FOUND :
337          if (verbose)
338	  {
339	    printf("succeeded: %s (%.6f)\n", cupsLastErrorString(), reqtime);
340	    fflush(stdout);
341	  }
342          break;
343
344      default :
345          if (!verbose)
346	    printf("testspeed(%d): %s ", (int)getpid(),
347	           ippOpString(ops[i & 3]));
348
349	  printf("failed: %s\n", cupsLastErrorString());
350          httpClose(http);
351	  return (1);
352    }
353  }
354
355  httpClose(http);
356
357  printf("testspeed(%d): %d requests in %.1fs (%.3fs/r, %.1fr/s)\n",
358         (int)getpid(), i, elapsed, elapsed / i, i / elapsed);
359
360  return (0);
361}
362
363
364/*
365 * 'usage()' - Show program usage...
366 */
367
368static void
369usage(void)
370{
371  puts("Usage: testspeed [-c children] [-h] [-o operation] [-r requests] [-v] "
372       "[-E] hostname[:port]");
373  exit(0);
374}
375
376
377
378/*
379 * End of "$Id: testspeed.c 11560 2014-02-06 20:10:19Z msweet $".
380 */
381