X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/060df5ea7c632b1ac8cc8aac1fb59758165c2084..6d2010ae8f7a6078e10b361c6962983bab233e0f:/tools/tests/xnu_quick_test/memory_tests.c diff --git a/tools/tests/xnu_quick_test/memory_tests.c b/tools/tests/xnu_quick_test/memory_tests.c index eb8817b9a..dc8675087 100644 --- a/tools/tests/xnu_quick_test/memory_tests.c +++ b/tools/tests/xnu_quick_test/memory_tests.c @@ -9,9 +9,92 @@ #include "tests.h" #include +#include /* crashcount() */ extern char g_target_path[ PATH_MAX ]; +/* + * static to localize to this compilation unit; volatile to avoid register + * optimization which would prevent modification by a signal handler. + */ +static volatile int my_err; + +/* + * Handler; used by memory_tests() child to reset my_err so that it will + * exit normally following a SIGBUS, rather than triggering a crash report; + * this depends on setting the error non-zero before triggering the condition + * that would trigger a SIGBUS. To avoid confusion, this is most easily done + * right before the test in question, and if there are subsequent tests, then + * undone immediately after to avoid false test negatives. + */ +void +bus_handler(int sig, siginfo_t *si, void *mcontext) +{ + /* Reset global error value when we see a SIGBUS */ + if (sig == SIGBUS) + my_err = 0; +} + +/* + * Count the number of crashes for us in /Library/Logs/CrashReporter/ + * + * XXX Assumes that CrashReporter uses our name as a prefix + * XXX Assumes no one lese has the same prefix as our name + */ +int +crashcount(char *namebuf1, char *namebuf2) +{ + char *crashdir1 = "/Library/Logs/CrashReporter"; + char *crashdir2 = "/Library/Logs/DiagnosticReports"; + char *crash_file_pfx = "xnu_quick_test"; + int crash_file_pfxlen = strlen(crash_file_pfx); + struct stat sb; + DIR *dirp1, *dirp2; + struct dirent *dep1, *dep2; + int count = 0; + + /* If we can't open the directory, it hasn't been created */ + if ((dirp1 = opendir(crashdir1)) == NULL) { + return( 0 ); + } + + while((dep1 = readdir(dirp1)) != NULL) { + if (strncmp(crash_file_pfx, dep1->d_name, crash_file_pfxlen)) + continue; + /* record each one to get the last one */ + if (namebuf1) { + strcpy(namebuf1, crashdir1); + strcat(namebuf1, "/"); + strcat(namebuf1, dep1->d_name); + } + count++; + } + + closedir(dirp1); + + /* If we can't open the directory, it hasn't been created */ + if ((dirp2 = opendir(crashdir2)) == NULL) { + return( 0 ); + } + + while((dep2 = readdir(dirp2)) != NULL) { + if (strncmp(crash_file_pfx, dep2->d_name, crash_file_pfxlen)) + continue; + /* record each one to get the last one */ + if (namebuf2) { + strcpy(namebuf2, crashdir2); + strcat(namebuf2, "/"); + strcat(namebuf2, dep2->d_name); + } + count++; + } + + closedir(dirp2); + + return( count/2 ); +} + + /* ************************************************************************************************************** * Test madvise, mincore, minherit, mlock, mlock, mmap, mprotect, msync, munmap system calls. * todo - see if Francois has better versions of these tests... @@ -19,7 +102,6 @@ extern char g_target_path[ PATH_MAX ]; */ int memory_tests( void * the_argp ) { - int my_err; int my_page_size, my_status; int my_fd = -1; char * my_pathp = NULL; @@ -29,6 +111,10 @@ int memory_tests( void * the_argp ) ssize_t my_result; pid_t my_pid, my_wait_pid; kern_return_t my_kr; + struct sigaction my_sa; + static int my_crashcount; + static char my_namebuf1[256]; /* XXX big enough */ + static char my_namebuf2[256]; my_kr = vm_allocate((vm_map_t) mach_task_self(), (vm_address_t*)&my_pathp, PATH_MAX, VM_FLAGS_ANYWHERE); if(my_kr != KERN_SUCCESS){ @@ -65,6 +151,16 @@ int memory_tests( void * the_argp ) goto test_failed_exit; } + /* + * Find out how many crashes there have already been; if it's not + * zero, then don't even attempt this test. + */ + if ((my_crashcount = crashcount(my_namebuf1, my_namebuf2)) != 0) { + printf( "memtest aborted: can not distinguish our expected crash from \n"); + printf( "%d existing crashes including %s \n", my_crashcount, my_namebuf2); + goto test_failed_exit; + } + /* * spin off a child process that we will use for testing. */ @@ -252,18 +348,35 @@ int memory_tests( void * the_argp ) goto exit_child; } + /* + * Establish SIGBUS handler; will reset (disable itself) if it fires; + * we would need how to recover from the exceptional condition that + * raised the SIGBUS by modifying the contents of the (opaque to us) + * mcontext in order to prevent this from being terminal, so we let + * it be terminal. This is enough to avoid triggering crash reporter. + */ + my_sa.sa_sigaction = bus_handler; + my_sa.sa_flags = SA_SIGINFO | SA_RESETHAND; + if ((my_err = sigaction(SIGBUS, &my_sa, NULL)) != 0) { + printf("sigaction call failed with error %d - \"%s\" \n", errno, strerror( errno) ); + my_err = -1; + goto exit_child; + } + + my_err = -1; /* default to error out if we do NOT trigger a SIGBUS */ + *my_addr = 'z'; /* should cause SIGBUS signal (we look for this at child termination within the parent) */ - - - my_err = 0; + /* NOTREACHED */ + + printf("Expected SIGBUS signal, got nothing!\n"); + my_err = -1; exit_child: exit( my_err ); } - /* parent process - - * we should get SIGBUS exit when child tries to write to read only memory + * we should get no error if the child has completed all tests successfully */ my_wait_pid = wait4( my_pid, &my_status, 0, NULL ); if ( my_wait_pid == -1 ) { @@ -277,11 +390,39 @@ exit_child: goto test_failed_exit; } - if ( WIFSIGNALED( my_status ) && WTERMSIG( my_status ) != SIGBUS ) { - printf( "wait4 returned wrong signal status - 0x%02X \n", my_status ); + /* If we were not signalled, or we died from an unexpected signal, report it. + */ + if ( !WIFSIGNALED( my_status ) || WTERMSIG( my_status ) != SIGBUS) { + printf( "wait4 returned child died of status - 0x%02X \n", my_status ); goto test_failed_exit; } + /* + * Wait long enough that CrashReporter has finished. + */ + sleep(5); + + /* + * Find out how many crashes there have already been; if it's not + * one, then don't even attempt this test. + */ + if ((my_crashcount = crashcount(my_namebuf1, my_namebuf2)) != 1) { + printf( "child did not crash as expected \n"); + printf( "saw %d crashes including %s \n", my_crashcount, my_namebuf2); + goto test_failed_exit; + } + + /* post-remove the expected crash report */ + if (unlink(my_namebuf1)) { + printf("unlink of expected crash report '%s' failed \n", my_namebuf1); + goto test_failed_exit; + } + + if (unlink(my_namebuf2)) { + printf("unlink of expected crash report '%s' failed \n", my_namebuf2); + goto test_failed_exit; + } + /* make sure shared page got modified in child */ if ( strcmp( my_test_page_p, "parent data child data" ) != 0 ) { printf( "minherit did not work correctly - shared page looks wrong \n" );