]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/unit_tests/cpu_monitor_tests_11646922_src/cpumon_test_framework.c
e49dd7750615158fcfcc4db0d714ffd260708a60
[apple/xnu.git] / tools / tests / unit_tests / cpu_monitor_tests_11646922_src / cpumon_test_framework.c
1 /*
2 * Testing Framework for CPU Usage Monitor
3 *
4 * The framework tests for correctness of the CPU Usage Monitor.
5 * It creates a new exception port and an associated handling thread.
6 * For each test case, the framework sets its own exception port to the
7 * newly allocated port, execs a new child (which inherits the new
8 * exception port) and restores the parent's exception port to the
9 * original handler. The child process is invoked with a different
10 * parameters based on the scenario being tested.
11 *
12 * Usage: ./cpu_monitor_tests_11646922 [test case ID]
13 * If no test case ID is supplied, the framework runs all test cases.
14 *
15 */
16
17 #include <stdio.h>
18 #include <spawn.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <signal.h>
23 #include <pthread.h>
24 #include <mach/mach.h>
25 #include <spawn_private.h>
26 #include <libproc_internal.h>
27 #include <excserver.h>
28 #include <kern/exc_resource.h>
29
30 #define MAX_TEST_ID_LEN 16
31 #define MAX_ARGV 8
32
33 #define GENERATE_TEST_EXC_CODE(type, flavor) \
34 ((0) | ((type & 0x7ULL) << 61) | ((flavor & 0x7ULL) << 58))
35
36 /*
37 * To add a new test case to this framework:
38 * - Increment the NUMTESTS value
39 * - Add exec args for cpu_hog/cpu_hog unentitled to test the
40 * scenario. Also add a case to the main loop child_args assignment.
41 * - Add timeout for exception. If no timeout, specify 0.
42 * - Add expected duration for exception. 0 if no exception expected.
43 * - Add (Exception Type | flavor) to "test_exception_code" if the
44 * test case generates an exception; 0 otherwise
45 */
46
47 #define NUMTESTS 7
48
49 const char *test_description[] = {
50 "Basic test for EXC_RESOURCE.",
51 "Test Program stays under limit.",
52 "Test Program disables monitor.",
53 "Unentitled Test Program attempts to disable monitor.",
54 "Test Program resets monitor to default.",
55 "Set high watermark, munch past it, and confirm EXC_RESOURCE received for FLAVOR_HIGH_WATERMARK.",
56 "Set high watermark but don't munch past it. Confirm no EXC_RESOURCE received.",
57 };
58
59 /*
60 * Exec arguments for cpu hogging programs
61 * (NULL indicates test should not be run)
62 */
63 char *test_argv_0[] = { "./cpu_hog-unentitled", "-c", "30", "-C", "10", "-p", "100", "-i", "1", NULL };
64 char *test_argv_1[] = { "./cpu_hog-unentitled", "-c", "50", "-C", "15", "-p", "25", "-i", "1", NULL };
65 #ifdef TARGET_SDK_iphoneos_internal
66 char *test_argv_2[] = { "./cpu_hog", "-c", "20", "-C", "15", "-x", "0", "-p", "100", "-i", "1", NULL };
67 char *test_argv_3[] = { "./cpu_hog-unentitled", "-c", "20", "-C", "15", "-x", "1", "-p", "100", "-i", "1", NULL };
68 #else
69 char *test_argv_2[] = { "./cpu_hog-unentitled", "-c", "20", "-C", "15", "-x", "0", "-p", "100", "-i", "1", NULL };
70 char **test_argv_3 = NULL;
71 #endif
72 char *test_argv_4[] = { "./cpu_hog-unentitled", "-c", "20", "-C", "15", "-r", "1", "-p", "100", "-i", "1", NULL };
73 #ifdef TARGET_SDK_iphoneos_internal
74 char *test_argv_5[] = { "./mem_hog", "-e", "-w", "50", "-m", "150", "10", "200", NULL };
75 char *test_argv_6[] = { "./mem_hog", "-e", "-w", "190", "-m", "160", "10", "200", NULL };
76 #else
77 char **test_argv_5 = NULL;
78 char **test_argv_6 = NULL;
79 #endif
80
81 /*
82 * Timeout in seconds for test scenario to complete
83 * (0 indicates no timeout enabled)
84 */
85 int timeout_secs[] = {
86 15,
87 20,
88 20,
89 110,
90 110,
91 20,
92 20,
93 };
94
95 /*
96 * Exception should be generated within the specified duration
97 * (0 indicates no exception/time constraints for the exception
98 * to occur)
99 */
100 int exc_expected_at[] = {
101 0,
102 0,
103 0,
104 90,
105 90,
106 10,
107 0,
108 };
109
110 /*
111 * EXC_RESOURCE exception codes expected (0 indicates no
112 * exception expected)
113 */
114 uint64_t test_exception_code[] = {
115 GENERATE_TEST_EXC_CODE(RESOURCE_TYPE_CPU, FLAVOR_CPU_MONITOR),
116 0,
117 0,
118 GENERATE_TEST_EXC_CODE(RESOURCE_TYPE_CPU, FLAVOR_CPU_MONITOR),
119 GENERATE_TEST_EXC_CODE(RESOURCE_TYPE_CPU, FLAVOR_CPU_MONITOR),
120 GENERATE_TEST_EXC_CODE(RESOURCE_TYPE_MEMORY, FLAVOR_HIGH_WATERMARK),
121 0,
122 };
123
124 #define DEFAULT_PERCENTAGE "50"
125 #define DEFAULT_INTERVAL "180"
126
127 /* Global Variables used by parent/child */
128 mach_port_t exc_port; /* Exception port for child process */
129 uint64_t exception_code; /* Exception code for the exception generated */
130 int time_for_exc; /* Time (in secs.) for the exception to be generated */
131 extern char **environ; /* Environment variables for the child process */
132 int test_status; /* Test Suite Status */
133 int indiv_results[NUMTESTS]; /* Results of individual tests (-1=didn't run; 0=pass; 1=fail) */
134
135 /* Cond Var and Mutex to indicate timeout for child process */
136 pthread_cond_t cv;
137 pthread_mutex_t lock;
138
139 /* Timer Routines to calculate elapsed time and run timer thread */
140 time_t start_time; /* Test case start time (in secs.) */
141
142 int elapsed(void)
143 {
144 return (time(NULL) - start_time);
145 }
146
147 void *timeout_thread(void *arg)
148 {
149 int err;
150 int timeout = (int)arg;
151
152 sleep(timeout);
153 fprintf(stderr, "Test Program timed out... Terminating!\n");
154
155 if ((err = pthread_cond_broadcast(&cv)) != 0) {
156 fprintf(stderr, "pthread_cond_broadcast: %s\n", strerror(err));
157 exit(1);
158 }
159
160 return (NULL);
161 }
162
163 /* Routine to wait for child to complete */
164 void *wait4_child_thread(void *arg)
165 {
166 int err;
167 int child_stat;
168
169 wait4(-1, &child_stat, 0, NULL);
170
171 if ((err = pthread_cond_broadcast(&cv)) != 0) {
172 fprintf(stderr, "pthread_cond_broadcast: %s\n", strerror(err));
173 exit(1);
174 }
175
176 return (NULL);
177 }
178
179 /* Mach Server Routines */
180 boolean_t mach_exc_server(
181 mach_msg_header_t *InHeadP,
182 mach_msg_header_t *OutHeadP);
183
184 kern_return_t catch_mach_exception_raise
185 (
186 mach_port_t exception_port,
187 mach_port_t thread,
188 mach_port_t task,
189 exception_type_t exception,
190 mach_exception_data_t code,
191 mach_msg_type_number_t codeCnt
192 )
193 {
194 if (exception == EXC_RESOURCE) {
195 /* Set global variable to indicate exception received */
196 exception_code = *((uint64_t *)code);
197 time_for_exc = elapsed();
198 } else {
199 /* Terminate test on all other unexpected exceptions */
200 fprintf(stderr, "received unexpected exception type %#x\n", exception);
201 exit(1);
202 }
203
204 return (KERN_SUCCESS);
205 }
206
207 kern_return_t catch_mach_exception_raise_state
208 (
209 mach_port_t exception_port,
210 exception_type_t exception,
211 const mach_exception_data_t code,
212 mach_msg_type_number_t codeCnt,
213 int *flavor,
214 const thread_state_t old_state,
215 mach_msg_type_number_t old_stateCnt,
216 thread_state_t new_state,
217 mach_msg_type_number_t *new_stateCnt
218 )
219 {
220 fprintf(stderr, "Unexpected exception handler called\n");
221 exit(1);
222 return (KERN_FAILURE);
223 }
224
225
226 kern_return_t catch_mach_exception_raise_state_identity
227 (
228 mach_port_t exception_port,
229 mach_port_t thread,
230 mach_port_t task,
231 exception_type_t exception,
232 mach_exception_data_t code,
233 mach_msg_type_number_t codeCnt,
234 int *flavor,
235 thread_state_t old_state,
236 mach_msg_type_number_t old_stateCnt,
237 thread_state_t new_state,
238 mach_msg_type_number_t *new_stateCnt
239 )
240 {
241 fprintf(stderr, "Unexpected exception handler called\n");
242 exit(1);
243 return (KERN_FAILURE);
244 }
245
246 void *server_thread(void *arg)
247 {
248 kern_return_t kr;
249
250 while(1) {
251 /* Handle exceptions on exc_port */
252 if ((kr = mach_msg_server_once(mach_exc_server, 4096, exc_port, 0)) != KERN_SUCCESS) {
253 fprintf(stderr, "mach_msg_server_once: error %#x\n", kr);
254 exit(1);
255 }
256 }
257 return (NULL);
258 }
259
260 int main(int argc, char *argv[])
261 {
262 posix_spawnattr_t attrs;
263 uint64_t percent, interval;
264 int i, err, ret = 0;
265
266 kern_return_t kr;
267 mach_port_t task = mach_task_self();
268 mach_port_t child_task;
269 char **child_args;
270
271 pthread_t exception_thread;
272 pthread_t timer_thread;
273 pthread_t wait_thread;
274
275 mach_msg_type_number_t maskCount = 1;
276 exception_mask_t mask;
277 exception_handler_t handler;
278 exception_behavior_t behavior;
279 thread_state_flavor_t flavor;
280
281 pid_t child_pid;
282 int test_case_id = -1;
283
284 if (argc > 1)
285 test_case_id = atoi(argv[1]);
286
287 /* Initialize mutex and condition variable */
288 if ((err = pthread_mutex_init(&lock, NULL)) != 0) {
289 fprintf(stderr,"pthread_mutex_init: %s\n", strerror(err));
290 exit(1);
291 }
292
293 if ((err = pthread_cond_init(&cv, NULL)) != 0) {
294 fprintf(stderr, "pthread_cond_init: %s\n", strerror(err));
295 exit(1);
296 }
297
298 /* Allocate and initialize new exception port */
299 if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &exc_port)) != KERN_SUCCESS) {
300 fprintf(stderr, "mach_port_allocate: %s\n", mach_error_string(kr));
301 exit(1);
302 }
303
304 if ((kr = mach_port_insert_right(task, exc_port,
305 exc_port, MACH_MSG_TYPE_MAKE_SEND)) != KERN_SUCCESS) {
306 fprintf(stderr, "mach_port_allocate: %s\n", mach_error_string(kr));
307 exit(1);
308 }
309
310 /* Get Current exception ports */
311 if ((kr = task_get_exception_ports(task, EXC_MASK_RESOURCE, &mask,
312 &maskCount, &handler, &behavior, &flavor)) != KERN_SUCCESS) {
313 fprintf(stderr,"task_get_exception_ports: %s\n", mach_error_string(kr));
314 exit(1);
315 }
316
317 /* Create exception serving thread */
318 if ((err = pthread_create(&exception_thread, NULL, server_thread, 0)) != 0) {
319 fprintf(stderr, "pthread_create server_thread: %s\n", strerror(err));
320 exit(1);
321 }
322
323 fprintf(stderr, "---------------System Configuration------------------------------------------\n");
324 fprintf(stderr, "System Kernel Version: ");
325 system("uname -a");
326 fprintf(stderr, "System SDK Version: ");
327 system("sw_vers");
328
329 for (i = 0; i < NUMTESTS; i++) {
330 indiv_results[i] = -1;
331 }
332
333 /* Run Tests */
334 for(i=0; i<NUMTESTS; i++) {
335 int j;
336
337 if (test_case_id != -1 && test_case_id != i)
338 continue;
339
340 fprintf(stderr, "---------------Test [%d] Configuration------------------------------------------\n", i);
341 fprintf(stderr, "Test Case ID: %d\n", i);
342 fprintf(stderr, "Description: %s\n", test_description[i]);
343
344 switch(i) {
345 case 0:
346 child_args = test_argv_0;
347 break;
348 case 1:
349 child_args = test_argv_1;
350 break;
351 case 2:
352 child_args = test_argv_2;
353 break;
354 case 3:
355 child_args = test_argv_3;
356 break;
357 case 4:
358 child_args = test_argv_4;
359 break;
360 case 5:
361 child_args = test_argv_5;
362 break;
363 case 6:
364 child_args = test_argv_6;
365 break;
366 default:
367 fprintf(stderr, "no test argv found\n");
368 exit(1);
369 }
370
371 /* Test cases which do not need to run for certain platforms */
372 if (child_args == NULL) {
373 fprintf(stderr, "Test case unimplemented for current platform.\n");
374 fprintf(stderr, "[PASSED]\n");
375 fprintf(stderr, "-------------------------------------------------------------------------------\n");
376 continue;
377 }
378
379 fprintf(stderr, "Helper args: ");
380 for (j = 0; child_args[j] != NULL; j++) {
381 fprintf(stderr, "%s ", child_args[j]);
382 }
383 fprintf(stderr, "\n");
384
385 /* Print Test Case Configuration */
386 fprintf(stderr, "Test Case expects EXC_RESOURCE?: %s\n", test_exception_code[i] ? "Yes":"No");
387 if (test_exception_code[i])
388 fprintf(stderr, "Expected EXC_RESOURCE code: 0x%llx\n", test_exception_code[i]);
389 if (timeout_secs[i])
390 fprintf(stderr, "Timeout for Test Program: %d secs\n", timeout_secs[i]);
391 if (exc_expected_at[i])
392 fprintf(stderr, "Exception Expected After: %d secs\n", exc_expected_at[i]);
393
394 /* Initialize posix_spawn attributes */
395 posix_spawnattr_init(&attrs);
396
397 if ((err = posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETEXEC)) != 0) {
398 fprintf(stderr, "posix_spawnattr_setflags: %s\n", strerror(err));
399 exit(1);
400 }
401
402 /* Use high values so the system defaults take effect (spawn attrs are capped) */
403 percent = 100;
404 interval = 10000;
405
406 /* Enable CPU Monitor */
407 if ((err = posix_spawnattr_setcpumonitor(&attrs, percent, interval)) != 0) {
408 fprintf(stderr, "posix_spawnattr_setcpumonitor: %s\n", strerror(err));
409 exit(1);
410 }
411
412
413 exception_code = 0;
414 time_for_exc = -1;
415
416 /* Set Exception Ports for Current Task */
417 if ((kr = task_set_exception_ports(task, EXC_MASK_RESOURCE, exc_port,
418 EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavor)) != KERN_SUCCESS) {
419 fprintf(stderr, "task_set_exception_ports: %#x\n", kr);
420 exit(1);
421 }
422
423 /*
424 * Note the time at start of test.
425 */
426 start_time = time(NULL);
427
428 fprintf(stderr, "---------------Test [%d] Runtime------------------------------------------------\n", i);
429
430 /* Fork and exec child */
431 if ((child_pid = fork()) == 0) {
432 if ((err = posix_spawn(NULL, child_args[0], NULL, &attrs, &child_args[0], environ)) != 0) {
433 fprintf(stderr, "posix_spawn: %s\n", strerror(err));
434 exit(1);
435 }
436 }
437
438 /* Restore exception ports for parent */
439 if ((kr = task_set_exception_ports(task, EXC_MASK_RESOURCE, handler,
440 EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavor)) != KERN_SUCCESS) {
441 fprintf(stderr, "task_set_exception_ports: %#x\n", kr);
442 exit(1);
443 }
444
445 /* Create Timer Thread if timeout specified */
446 if (timeout_secs[i]) {
447 if ((err = pthread_create(&timer_thread, NULL, timeout_thread, (void *)timeout_secs[i])) != 0) {
448 fprintf(stderr, "pthread_create timeout_thread: %s\n", strerror(err));
449 test_status = 1;
450 goto cleanup;
451 }
452 }
453
454 /* Create waiting for child thread */
455 if ((err = pthread_create(&wait_thread, NULL, wait4_child_thread, NULL)) != 0) {
456 fprintf(stderr, "pthread_create wait4_child_thread: %s\n", strerror(err));
457 test_status = 1;
458 goto cleanup;
459 }
460
461 pthread_mutex_lock(&lock);
462 pthread_cond_wait(&cv, &lock);
463 pthread_mutex_unlock(&lock);
464
465 kill(child_pid, SIGKILL);
466 pthread_join(timer_thread, NULL);
467 pthread_join(wait_thread, NULL);
468
469 int test_case_status = 0;
470 indiv_results[i] = 0;
471
472 fprintf(stderr, "---------------Test [%d] Results------------------------------------------------\n", i);
473
474 if (exception_code)
475 fprintf(stderr, "EXC_RESOURCE Received with Code: 0x%llx\n", exception_code);
476 else
477 fprintf(stderr, "No EXC_RESOURCE Received!\n");
478
479 if (time_for_exc > 0)
480 fprintf(stderr, "EXC_RESOURCE Received after %d secs\n", time_for_exc);
481
482 if (!!exception_code != !!test_exception_code[i]) {
483 test_status = 1;
484 test_case_status = 1;
485 indiv_results[i] = 1;
486 }
487
488 if (exception_code) {
489 /* Validate test success by checking code and expected time */
490 if ((exception_code & test_exception_code[i]) != test_exception_code[i]) {
491 fprintf(stderr, "Test Failure Reason: EXC_RESOURCE code did not match expected exception code!\n");
492 fprintf(stderr, "Expected: 0x%llx Found: 0x%llx\n", test_exception_code[i], exception_code);
493 test_status = 1;
494 test_case_status = 1;
495 indiv_results[i] = 1;
496 }
497 if(exc_expected_at[i] &&
498 (time_for_exc < (exc_expected_at[i] - 10) ||
499 time_for_exc > (exc_expected_at[i] + 10))) {
500 fprintf(stderr, "Test Failure Reason: Test case did not receive EXC_RESOURCE within expected time!\n");
501 test_status = 1;
502 test_case_status = 1;
503 indiv_results[i] = 1;
504 }
505 }
506
507 if(test_case_status)
508 fprintf(stderr, "[FAILED]\n");
509 else
510 fprintf(stderr, "[PASSED]\n");
511 fprintf(stderr, "-------------------------------------------------------------------------------\n");
512
513 }
514
515 if (test_case_id == -1) {
516 fprintf(stderr, "--------------- Results Summary -----------------------------------------------\n");
517
518 for (i = 0; i < NUMTESTS; i++) {
519 fprintf(stderr, "%2d: %s\n", i, (indiv_results[i] < 0) ? "N/A" :
520 (indiv_results[i] == 0) ? "PASSED" : "FAILED");
521 }
522 }
523
524 cleanup:
525 kill(child_pid, SIGKILL);
526 exit(test_status);
527 }
528
529