• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /macosx-10.9.5/xnu-2422.115.4/tools/tests/unit_tests/cpu_monitor_tests_11646922_src/
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
49const 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 */
63char *test_argv_0[] = { "./cpu_hog-unentitled", "-c", "30", "-C", "10", "-p", "100", "-i", "1", NULL };
64char *test_argv_1[] = { "./cpu_hog-unentitled", "-c", "50", "-C", "15", "-p", "25", "-i", "1", NULL };
65#ifdef TARGET_SDK_iphoneos_internal
66char *test_argv_2[] = { "./cpu_hog",            "-c", "20", "-C", "15", "-x", "0", "-p", "100", "-i", "1", NULL };
67char *test_argv_3[] = { "./cpu_hog-unentitled", "-c", "20", "-C", "15", "-x", "1", "-p", "100", "-i", "1", NULL };
68#else
69char *test_argv_2[] = { "./cpu_hog-unentitled", "-c", "20", "-C", "15", "-x", "0", "-p", "100", "-i", "1", NULL };
70char **test_argv_3 = NULL;
71#endif
72char *test_argv_4[] = { "./cpu_hog-unentitled", "-c", "20", "-C", "15", "-r", "1", "-p", "100", "-i", "1", NULL };
73#ifdef TARGET_SDK_iphoneos_internal
74char *test_argv_5[] = { "./mem_hog", "-e", "-w", "50", "-m", "150", "10", "200", NULL };
75char *test_argv_6[] = { "./mem_hog", "-e", "-w", "190", "-m", "160", "10", "200", NULL };
76#else
77char **test_argv_5 = NULL;
78char **test_argv_6 = NULL;
79#endif
80
81/*
82 * Timeout in seconds for test scenario to complete
83 * (0 indicates no timeout enabled)
84 */
85int 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 */
100int 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 */
114uint64_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 */
128mach_port_t	exc_port;	/* Exception port for child process */
129uint64_t	exception_code; /* Exception code for the exception generated */
130int		time_for_exc;	/* Time (in secs.) for the exception to be generated */
131extern char	**environ;	/* Environment variables for the child process */
132int		test_status;	/* Test Suite Status */
133int		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 */
136pthread_cond_t	cv;
137pthread_mutex_t lock;
138
139/* Timer Routines to calculate elapsed time and run timer thread */
140time_t		start_time;	/* Test case start time (in secs.) */
141
142int elapsed(void)
143{
144	return (time(NULL) - start_time);
145}
146
147void *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 */
164void *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 */
180boolean_t mach_exc_server(
181		mach_msg_header_t *InHeadP,
182		mach_msg_header_t *OutHeadP);
183
184kern_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
207kern_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
226kern_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
246void *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
260int 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
524cleanup:
525	kill(child_pid, SIGKILL);
526	exit(test_status);
527}
528
529
530