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