| /* |
| * Scheduler speed test for CUPS. |
| * |
| * Copyright 2007-2014 by Apple Inc. |
| * Copyright 1997-2005 by Easy Software Products. |
| * |
| * Licensed under Apache License v2.0. See the file "LICENSE" for more information. |
| */ |
| |
| /* |
| * Include necessary headers... |
| */ |
| |
| #include <cups/string-private.h> |
| #include <cups/cups.h> |
| #include <cups/language.h> |
| #include <cups/debug-private.h> |
| #include <sys/types.h> |
| #include <sys/time.h> |
| #include <sys/wait.h> |
| |
| |
| /* |
| * Local functions... |
| */ |
| |
| static int do_test(const char *server, int port, |
| http_encryption_t encryption, int requests, |
| const char *opstring, int verbose); |
| static void usage(void) _CUPS_NORETURN; |
| |
| |
| /* |
| * 'main()' - Send multiple IPP requests and report on the average response |
| * time. |
| */ |
| |
| int |
| main(int argc, /* I - Number of command-line arguments */ |
| char *argv[]) /* I - Command-line arguments */ |
| { |
| int i; /* Looping var */ |
| char *server, /* Server to use */ |
| *ptr; /* Pointer to port in server */ |
| int port; /* Port to use */ |
| http_encryption_t encryption; /* Encryption to use */ |
| int requests; /* Number of requests to send */ |
| int children; /* Number of children to fork */ |
| int good_children; /* Number of children that exited normally */ |
| int pid; /* Child PID */ |
| int status; /* Child status */ |
| time_t start, /* Start time */ |
| end; /* End time */ |
| double elapsed; /* Elapsed time */ |
| int verbose; /* Verbosity */ |
| const char *opstring; /* Operation name */ |
| |
| |
| /* |
| * Parse command-line options... |
| */ |
| |
| requests = 100; |
| children = 5; |
| server = (char *)cupsServer(); |
| port = ippPort(); |
| encryption = HTTP_ENCRYPT_IF_REQUESTED; |
| verbose = 0; |
| opstring = NULL; |
| |
| for (i = 1; i < argc; i ++) |
| if (argv[i][0] == '-') |
| { |
| for (ptr = argv[i] + 1; *ptr; ptr ++) |
| switch (*ptr) |
| { |
| case 'E' : /* Enable encryption */ |
| encryption = HTTP_ENCRYPT_REQUIRED; |
| break; |
| |
| case 'c' : /* Number of children */ |
| i ++; |
| if (i >= argc) |
| usage(); |
| |
| children = atoi(argv[i]); |
| break; |
| |
| case 'o' : /* Operation */ |
| i ++; |
| if (i >= argc) |
| usage(); |
| |
| opstring = argv[i]; |
| break; |
| |
| case 'r' : /* Number of requests */ |
| i ++; |
| if (i >= argc) |
| usage(); |
| |
| requests = atoi(argv[i]); |
| break; |
| |
| case 'v' : /* Verbose logging */ |
| verbose ++; |
| break; |
| |
| default : |
| usage(); |
| break; |
| } |
| } |
| else |
| { |
| server = argv[i]; |
| |
| if (server[0] != '/' && (ptr = strrchr(server, ':')) != NULL) |
| { |
| *ptr++ = '\0'; |
| port = atoi(ptr); |
| } |
| } |
| |
| /* |
| * Then create child processes to act as clients... |
| */ |
| |
| if (children > 0) |
| { |
| printf("testspeed: Simulating %d clients with %d requests to %s with " |
| "%sencryption...\n", children, requests, server, |
| encryption == HTTP_ENCRYPT_IF_REQUESTED ? "no " : ""); |
| } |
| |
| start = time(NULL); |
| |
| if (children < 1) |
| return (do_test(server, port, encryption, requests, opstring, verbose)); |
| else if (children == 1) |
| good_children = do_test(server, port, encryption, requests, opstring, |
| verbose) ? 0 : 1; |
| else |
| { |
| char options[255], /* Command-line options for child */ |
| reqstr[255], /* Requests string for child */ |
| serverstr[255]; /* Server:port string for child */ |
| |
| |
| snprintf(reqstr, sizeof(reqstr), "%d", requests); |
| |
| if (port == 631 || server[0] == '/') |
| strlcpy(serverstr, server, sizeof(serverstr)); |
| else |
| snprintf(serverstr, sizeof(serverstr), "%s:%d", server, port); |
| |
| strlcpy(options, "-cr", sizeof(options)); |
| |
| if (encryption == HTTP_ENCRYPT_REQUIRED) |
| strlcat(options, "E", sizeof(options)); |
| |
| if (verbose) |
| strlcat(options, "v", sizeof(options)); |
| |
| for (i = 0; i < children; i ++) |
| { |
| fflush(stdout); |
| |
| if ((pid = fork()) == 0) |
| { |
| /* |
| * Child goes here... |
| */ |
| |
| if (opstring) |
| execlp(argv[0], argv[0], options, "0", reqstr, "-o", opstring, |
| serverstr, (char *)NULL); |
| else |
| execlp(argv[0], argv[0], options, "0", reqstr, serverstr, (char *)NULL); |
| |
| exit(errno); |
| } |
| else if (pid < 0) |
| { |
| printf("testspeed: Fork failed: %s\n", strerror(errno)); |
| break; |
| } |
| else |
| printf("testspeed: Started child %d...\n", pid); |
| } |
| |
| /* |
| * Wait for children to finish... |
| */ |
| |
| puts("testspeed: Waiting for children to finish..."); |
| |
| for (good_children = 0;;) |
| { |
| pid = wait(&status); |
| |
| if (pid < 0 && errno != EINTR) |
| break; |
| |
| printf("testspeed: Ended child %d (%d)...\n", pid, status / 256); |
| |
| if (!status) |
| good_children ++; |
| } |
| } |
| |
| /* |
| * Compute the total run time... |
| */ |
| |
| if (good_children > 0) |
| { |
| end = time(NULL); |
| elapsed = end - start; |
| i = good_children * requests; |
| |
| printf("testspeed: %dx%d=%d requests in %.1fs (%.3fs/r, %.1fr/s)\n", |
| good_children, requests, i, elapsed, elapsed / i, i / elapsed); |
| } |
| |
| /* |
| * Exit with no errors... |
| */ |
| |
| return (0); |
| } |
| |
| |
| /* |
| * 'do_test()' - Run a test on a specific host... |
| */ |
| |
| static int /* O - Exit status */ |
| do_test(const char *server, /* I - Server to use */ |
| int port, /* I - Port number to use */ |
| http_encryption_t encryption, /* I - Encryption to use */ |
| int requests, /* I - Number of requests to send */ |
| const char *opstring, /* I - Operation string */ |
| int verbose) /* I - Verbose output? */ |
| { |
| int i; /* Looping var */ |
| http_t *http; /* Connection to server */ |
| ipp_t *request; /* IPP Request */ |
| struct timeval start, /* Start time */ |
| end; /* End time */ |
| double reqtime, /* Time for this request */ |
| elapsed; /* Elapsed time */ |
| int op; /* Current operation */ |
| static ipp_op_t ops[5] = /* Operations to test... */ |
| { |
| IPP_PRINT_JOB, |
| CUPS_GET_DEFAULT, |
| CUPS_GET_PRINTERS, |
| CUPS_GET_CLASSES, |
| IPP_GET_JOBS |
| }; |
| |
| |
| /* |
| * Connect to the server... |
| */ |
| |
| if ((http = httpConnectEncrypt(server, port, encryption)) == NULL) |
| { |
| printf("testspeed(%d): unable to connect to server - %s\n", (int)getpid(), |
| strerror(errno)); |
| return (1); |
| } |
| |
| /* |
| * Do multiple requests... |
| */ |
| |
| for (elapsed = 0.0, i = 0; i < requests; i ++) |
| { |
| /* |
| * Build a request which requires the following attributes: |
| * |
| * attributes-charset |
| * attributes-natural-language |
| * |
| * In addition, IPP_GET_JOBS needs a printer-uri attribute. |
| */ |
| |
| if (opstring) |
| op = ippOpValue(opstring); |
| else |
| op = ops[i % (int)(sizeof(ops) / sizeof(ops[0]))]; |
| |
| request = ippNewRequest(op); |
| |
| gettimeofday(&start, NULL); |
| |
| if (verbose) |
| printf("testspeed(%d): %.6f %s ", (int)getpid(), elapsed, |
| ippOpString(op)); |
| |
| switch (op) |
| { |
| case IPP_GET_JOBS : |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", |
| NULL, "ipp://localhost/printers/"); |
| |
| default : |
| ippDelete(cupsDoRequest(http, request, "/")); |
| break; |
| |
| case IPP_PRINT_JOB : |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", |
| NULL, "ipp://localhost/printers/test"); |
| ippDelete(cupsDoFileRequest(http, request, "/printers/test", |
| "../data/testprint.ps")); |
| break; |
| } |
| |
| gettimeofday(&end, NULL); |
| |
| reqtime = (end.tv_sec - start.tv_sec) + |
| 0.000001 * (end.tv_usec - start.tv_usec); |
| elapsed += reqtime; |
| |
| switch (cupsLastError()) |
| { |
| case IPP_OK : |
| case IPP_NOT_FOUND : |
| if (verbose) |
| { |
| printf("succeeded: %s (%.6f)\n", cupsLastErrorString(), reqtime); |
| fflush(stdout); |
| } |
| break; |
| |
| default : |
| if (!verbose) |
| printf("testspeed(%d): %s ", (int)getpid(), |
| ippOpString(ops[i & 3])); |
| |
| printf("failed: %s\n", cupsLastErrorString()); |
| httpClose(http); |
| return (1); |
| } |
| } |
| |
| httpClose(http); |
| |
| printf("testspeed(%d): %d requests in %.1fs (%.3fs/r, %.1fr/s)\n", |
| (int)getpid(), i, elapsed, elapsed / i, i / elapsed); |
| |
| return (0); |
| } |
| |
| |
| /* |
| * 'usage()' - Show program usage... |
| */ |
| |
| static void |
| usage(void) |
| { |
| puts("Usage: testspeed [-c children] [-h] [-o operation] [-r requests] [-v] " |
| "[-E] hostname[:port]"); |
| exit(0); |
| } |