]> git.saurik.com Git - apple/xnu.git/blobdiff - tools/tests/xnu_quick_test/memory_tests.c
xnu-1699.22.73.tar.gz
[apple/xnu.git] / tools / tests / xnu_quick_test / memory_tests.c
index eb8817b9ad7849732c6079ac5a982b9fc868a97f..dc8675087475ebf93428539dcb1513393aadfdfe 100644 (file)
@@ -9,9 +9,92 @@
 
 #include "tests.h"
 #include <mach/mach.h>
+#include <dirent.h>            /* 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" );