cpumon_test_framework.c [plain text]
#include <stdio.h>
#include <spawn.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <mach/mach.h>
#include <spawn_private.h>
#include <libproc_internal.h>
#include <excserver.h>
#include <kern/exc_resource.h>
#define MAX_TEST_ID_LEN 16
#define MAX_ARGV 8
#define GENERATE_TEST_EXC_CODE(type, flavor) \
((0) | ((type & 0x7ULL) << 61) | ((flavor & 0x7ULL) << 58))
#define NUMTESTS 7
const char *test_description[] = {
"Basic test for EXC_RESOURCE.",
"Test Program stays under limit.",
"Test Program disables monitor.",
"Unentitled Test Program attempts to disable monitor.",
"Test Program resets monitor to default.",
"Set high watermark, munch past it, and confirm EXC_RESOURCE received for FLAVOR_HIGH_WATERMARK.",
"Set high watermark but don't munch past it. Confirm no EXC_RESOURCE received.",
};
char *test_argv_0[] = { "./cpu_hog-unentitled", "-c", "30", "-C", "10", "-p", "100", "-i", "1", NULL };
char *test_argv_1[] = { "./cpu_hog-unentitled", "-c", "50", "-C", "15", "-p", "25", "-i", "1", NULL };
#ifdef TARGET_SDK_iphoneos_internal
char *test_argv_2[] = { "./cpu_hog", "-c", "20", "-C", "15", "-x", "0", "-p", "100", "-i", "1", NULL };
char *test_argv_3[] = { "./cpu_hog-unentitled", "-c", "20", "-C", "15", "-x", "1", "-p", "100", "-i", "1", NULL };
#else
char *test_argv_2[] = { "./cpu_hog-unentitled", "-c", "20", "-C", "15", "-x", "0", "-p", "100", "-i", "1", NULL };
char **test_argv_3 = NULL;
#endif
char *test_argv_4[] = { "./cpu_hog-unentitled", "-c", "20", "-C", "15", "-r", "1", "-p", "100", "-i", "1", NULL };
#ifdef TARGET_SDK_iphoneos_internal
char *test_argv_5[] = { "./mem_hog", "-e", "-w", "50", "-m", "150", "10", "200", NULL };
char *test_argv_6[] = { "./mem_hog", "-e", "-w", "190", "-m", "160", "10", "200", NULL };
#else
char **test_argv_5 = NULL;
char **test_argv_6 = NULL;
#endif
int timeout_secs[] = {
15,
20,
20,
110,
110,
20,
20,
};
int exc_expected_at[] = {
0,
0,
0,
90,
90,
10,
0,
};
uint64_t test_exception_code[] = {
GENERATE_TEST_EXC_CODE(RESOURCE_TYPE_CPU, FLAVOR_CPU_MONITOR),
0,
0,
GENERATE_TEST_EXC_CODE(RESOURCE_TYPE_CPU, FLAVOR_CPU_MONITOR),
GENERATE_TEST_EXC_CODE(RESOURCE_TYPE_CPU, FLAVOR_CPU_MONITOR),
GENERATE_TEST_EXC_CODE(RESOURCE_TYPE_MEMORY, FLAVOR_HIGH_WATERMARK),
0,
};
#define DEFAULT_PERCENTAGE "50"
#define DEFAULT_INTERVAL "180"
mach_port_t exc_port;
uint64_t exception_code;
int time_for_exc;
extern char **environ;
int test_status;
int indiv_results[NUMTESTS];
pthread_cond_t cv;
pthread_mutex_t lock;
time_t start_time;
int elapsed(void)
{
return (time(NULL) - start_time);
}
void *timeout_thread(void *arg)
{
int err;
int timeout = (int)arg;
sleep(timeout);
fprintf(stderr, "Test Program timed out... Terminating!\n");
if ((err = pthread_cond_broadcast(&cv)) != 0) {
fprintf(stderr, "pthread_cond_broadcast: %s\n", strerror(err));
exit(1);
}
return (NULL);
}
void *wait4_child_thread(void *arg)
{
int err;
int child_stat;
wait4(-1, &child_stat, 0, NULL);
if ((err = pthread_cond_broadcast(&cv)) != 0) {
fprintf(stderr, "pthread_cond_broadcast: %s\n", strerror(err));
exit(1);
}
return (NULL);
}
boolean_t mach_exc_server(
mach_msg_header_t *InHeadP,
mach_msg_header_t *OutHeadP);
kern_return_t catch_mach_exception_raise
(
mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
mach_exception_data_t code,
mach_msg_type_number_t codeCnt
)
{
if (exception == EXC_RESOURCE) {
exception_code = *((uint64_t *)code);
time_for_exc = elapsed();
} else {
fprintf(stderr, "received unexpected exception type %#x\n", exception);
exit(1);
}
return (KERN_SUCCESS);
}
kern_return_t catch_mach_exception_raise_state
(
mach_port_t exception_port,
exception_type_t exception,
const mach_exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
const thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
)
{
fprintf(stderr, "Unexpected exception handler called\n");
exit(1);
return (KERN_FAILURE);
}
kern_return_t catch_mach_exception_raise_state_identity
(
mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
mach_exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
)
{
fprintf(stderr, "Unexpected exception handler called\n");
exit(1);
return (KERN_FAILURE);
}
void *server_thread(void *arg)
{
kern_return_t kr;
while(1) {
if ((kr = mach_msg_server_once(mach_exc_server, 4096, exc_port, 0)) != KERN_SUCCESS) {
fprintf(stderr, "mach_msg_server_once: error %#x\n", kr);
exit(1);
}
}
return (NULL);
}
int main(int argc, char *argv[])
{
posix_spawnattr_t attrs;
uint64_t percent, interval;
int i, err, ret = 0;
kern_return_t kr;
mach_port_t task = mach_task_self();
mach_port_t child_task;
char **child_args;
pthread_t exception_thread;
pthread_t timer_thread;
pthread_t wait_thread;
mach_msg_type_number_t maskCount = 1;
exception_mask_t mask;
exception_handler_t handler;
exception_behavior_t behavior;
thread_state_flavor_t flavor;
pid_t child_pid;
int test_case_id = -1;
if (argc > 1)
test_case_id = atoi(argv[1]);
if ((err = pthread_mutex_init(&lock, NULL)) != 0) {
fprintf(stderr,"pthread_mutex_init: %s\n", strerror(err));
exit(1);
}
if ((err = pthread_cond_init(&cv, NULL)) != 0) {
fprintf(stderr, "pthread_cond_init: %s\n", strerror(err));
exit(1);
}
if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &exc_port)) != KERN_SUCCESS) {
fprintf(stderr, "mach_port_allocate: %s\n", mach_error_string(kr));
exit(1);
}
if ((kr = mach_port_insert_right(task, exc_port,
exc_port, MACH_MSG_TYPE_MAKE_SEND)) != KERN_SUCCESS) {
fprintf(stderr, "mach_port_allocate: %s\n", mach_error_string(kr));
exit(1);
}
if ((kr = task_get_exception_ports(task, EXC_MASK_RESOURCE, &mask,
&maskCount, &handler, &behavior, &flavor)) != KERN_SUCCESS) {
fprintf(stderr,"task_get_exception_ports: %s\n", mach_error_string(kr));
exit(1);
}
if ((err = pthread_create(&exception_thread, NULL, server_thread, 0)) != 0) {
fprintf(stderr, "pthread_create server_thread: %s\n", strerror(err));
exit(1);
}
fprintf(stderr, "---------------System Configuration------------------------------------------\n");
fprintf(stderr, "System Kernel Version: ");
system("uname -a");
fprintf(stderr, "System SDK Version: ");
system("sw_vers");
for (i = 0; i < NUMTESTS; i++) {
indiv_results[i] = -1;
}
for(i=0; i<NUMTESTS; i++) {
int j;
if (test_case_id != -1 && test_case_id != i)
continue;
fprintf(stderr, "---------------Test [%d] Configuration------------------------------------------\n", i);
fprintf(stderr, "Test Case ID: %d\n", i);
fprintf(stderr, "Description: %s\n", test_description[i]);
switch(i) {
case 0:
child_args = test_argv_0;
break;
case 1:
child_args = test_argv_1;
break;
case 2:
child_args = test_argv_2;
break;
case 3:
child_args = test_argv_3;
break;
case 4:
child_args = test_argv_4;
break;
case 5:
child_args = test_argv_5;
break;
case 6:
child_args = test_argv_6;
break;
default:
fprintf(stderr, "no test argv found\n");
exit(1);
}
if (child_args == NULL) {
fprintf(stderr, "Test case unimplemented for current platform.\n");
fprintf(stderr, "[PASSED]\n");
fprintf(stderr, "-------------------------------------------------------------------------------\n");
continue;
}
fprintf(stderr, "Helper args: ");
for (j = 0; child_args[j] != NULL; j++) {
fprintf(stderr, "%s ", child_args[j]);
}
fprintf(stderr, "\n");
fprintf(stderr, "Test Case expects EXC_RESOURCE?: %s\n", test_exception_code[i] ? "Yes":"No");
if (test_exception_code[i])
fprintf(stderr, "Expected EXC_RESOURCE code: 0x%llx\n", test_exception_code[i]);
if (timeout_secs[i])
fprintf(stderr, "Timeout for Test Program: %d secs\n", timeout_secs[i]);
if (exc_expected_at[i])
fprintf(stderr, "Exception Expected After: %d secs\n", exc_expected_at[i]);
posix_spawnattr_init(&attrs);
if ((err = posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETEXEC)) != 0) {
fprintf(stderr, "posix_spawnattr_setflags: %s\n", strerror(err));
exit(1);
}
percent = 100;
interval = 10000;
if ((err = posix_spawnattr_setcpumonitor(&attrs, percent, interval)) != 0) {
fprintf(stderr, "posix_spawnattr_setcpumonitor: %s\n", strerror(err));
exit(1);
}
exception_code = 0;
time_for_exc = -1;
if ((kr = task_set_exception_ports(task, EXC_MASK_RESOURCE, exc_port,
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavor)) != KERN_SUCCESS) {
fprintf(stderr, "task_set_exception_ports: %#x\n", kr);
exit(1);
}
start_time = time(NULL);
fprintf(stderr, "---------------Test [%d] Runtime------------------------------------------------\n", i);
if ((child_pid = fork()) == 0) {
if ((err = posix_spawn(NULL, child_args[0], NULL, &attrs, &child_args[0], environ)) != 0) {
fprintf(stderr, "posix_spawn: %s\n", strerror(err));
exit(1);
}
}
if ((kr = task_set_exception_ports(task, EXC_MASK_RESOURCE, handler,
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavor)) != KERN_SUCCESS) {
fprintf(stderr, "task_set_exception_ports: %#x\n", kr);
exit(1);
}
if (timeout_secs[i]) {
if ((err = pthread_create(&timer_thread, NULL, timeout_thread, (void *)timeout_secs[i])) != 0) {
fprintf(stderr, "pthread_create timeout_thread: %s\n", strerror(err));
test_status = 1;
goto cleanup;
}
}
if ((err = pthread_create(&wait_thread, NULL, wait4_child_thread, NULL)) != 0) {
fprintf(stderr, "pthread_create wait4_child_thread: %s\n", strerror(err));
test_status = 1;
goto cleanup;
}
pthread_mutex_lock(&lock);
pthread_cond_wait(&cv, &lock);
pthread_mutex_unlock(&lock);
kill(child_pid, SIGKILL);
pthread_join(timer_thread, NULL);
pthread_join(wait_thread, NULL);
int test_case_status = 0;
indiv_results[i] = 0;
fprintf(stderr, "---------------Test [%d] Results------------------------------------------------\n", i);
if (exception_code)
fprintf(stderr, "EXC_RESOURCE Received with Code: 0x%llx\n", exception_code);
else
fprintf(stderr, "No EXC_RESOURCE Received!\n");
if (time_for_exc > 0)
fprintf(stderr, "EXC_RESOURCE Received after %d secs\n", time_for_exc);
if (!!exception_code != !!test_exception_code[i]) {
test_status = 1;
test_case_status = 1;
indiv_results[i] = 1;
}
if (exception_code) {
if ((exception_code & test_exception_code[i]) != test_exception_code[i]) {
fprintf(stderr, "Test Failure Reason: EXC_RESOURCE code did not match expected exception code!\n");
fprintf(stderr, "Expected: 0x%llx Found: 0x%llx\n", test_exception_code[i], exception_code);
test_status = 1;
test_case_status = 1;
indiv_results[i] = 1;
}
if(exc_expected_at[i] &&
(time_for_exc < (exc_expected_at[i] - 10) ||
time_for_exc > (exc_expected_at[i] + 10))) {
fprintf(stderr, "Test Failure Reason: Test case did not receive EXC_RESOURCE within expected time!\n");
test_status = 1;
test_case_status = 1;
indiv_results[i] = 1;
}
}
if(test_case_status)
fprintf(stderr, "[FAILED]\n");
else
fprintf(stderr, "[PASSED]\n");
fprintf(stderr, "-------------------------------------------------------------------------------\n");
}
if (test_case_id == -1) {
fprintf(stderr, "--------------- Results Summary -----------------------------------------------\n");
for (i = 0; i < NUMTESTS; i++) {
fprintf(stderr, "%2d: %s\n", i, (indiv_results[i] < 0) ? "N/A" :
(indiv_results[i] == 0) ? "PASSED" : "FAILED");
}
}
cleanup:
kill(child_pid, SIGKILL);
exit(test_status);
}