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