Verifying mlockall() effects on stack memory proof

From RTwiki
Revision as of 08:59, 15 January 2008 by Remy (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Verifying mlockall() effects on stack memory proof

This is a test-application that shows the effects of mlockall() on stack memory.

   // g++ -o checkStackPageFaults -Wall -O3 checkStackPageFaults.cpp -lpthread
   
   // This application checks whether mlockall() forces all pages of a
   // stack into RAM.
   
   #include <stdio.h>
   #include <string.h>
   #include <pthread.h>
   #include <semaphore.h>
   #include <sched.h>
   #include <unistd.h>
   #include <sys/mman.h>
   #include <sys/resource.h>
   
   // Lock the application in memory to avoid page faults
   static void lockApplication(void)
   {
     if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0 )
     {
       perror( "mlockall() failed" );
     }
   }
   
   // Dump minor and major page faults that occurred since the previous call
   static bool dumpPageFaults(void)
   {
     bool l_PageFaultsDetected = false;
     static bool ls_Init = false;
     static struct rusage ls_RusagePrevious;
     struct rusage l_Rusage;
   
     getrusage(RUSAGE_SELF, &l_Rusage);
     int a_NewMinorPageFaults = l_Rusage.ru_minflt - ls_RusagePrevious.ru_minflt;
     int a_NewMajorPageFaults = l_Rusage.ru_majflt - ls_RusagePrevious.ru_majflt;
     ls_RusagePrevious.ru_minflt = l_Rusage.ru_minflt;
     ls_RusagePrevious.ru_majflt = l_Rusage.ru_majflt;
   
     if (ls_Init)
     {
       if ((a_NewMinorPageFaults > 0) || (a_NewMajorPageFaults > 0))
       {
         printf ("New minor/major page faults: %d/%d\n",
           a_NewMinorPageFaults,
           a_NewMajorPageFaults);
         l_PageFaultsDetected = true;
       }
     }
     ls_Init = true;
     return l_PageFaultsDetected;
   }
   
   // Start a thread
   static pthread_t startThread(int prio, int stack_size, void *(*thread_run)(void*), void *args)
   {
     const char     * failure;
     pthread_attr_t   attr;
     pthread_t      tid;
     struct sched_param      sched;
     int              policy;
     int              priority_min;
     int              priority_max;
     int              min_stack_size = 16384;
   
     for(;;)
     {
       if( (policy = sched_getscheduler( 0 )) == -1 )
       {
         failure = "sched_getscheduler";
         break;
       }
   
       if( (priority_min = sched_get_priority_min( policy )) == -1 )
       {
         failure = "sched_get_priority_min";
         break;
       }
   
       if( prio < priority_min )
         prio = priority_min;
   
       if( (priority_max = sched_get_priority_max( policy )) == -1 )
       {
         failure = "sched_get_priority_max";
         break;
       }
   
       if( prio > priority_max )
         prio = priority_max;
   
       if( sched_getparam( 0, &sched ) != 0 )
       {
         failure = "sched_getparam";
         break;
       }
   
       if( pthread_attr_init( &attr ) != 0 )
       {
         failure = "pthread_attr_init";
         break;
       }
   
       if( pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ) != 0 )
       {
         failure = "pthread_attr_setdetachstate";
         break;
       }
   
       if( pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ) != 0 )
       {
         failure = "pthread_attr_setscope";
         break;
       }
   
       if( stack_size < min_stack_size )
         stack_size = min_stack_size;
   
       if( pthread_attr_setstacksize( &attr, stack_size ) != 0 )
       {
         failure = "pthread_attr_setstacksize";
         break;
       }
   
       if( pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ) != 0 )
       {
         failure = "pthread_attr_setinheritsched";
         break;
       }
   
       if( pthread_attr_setschedpolicy( &attr, policy ) != 0 )
       {
         failure = "pthread_attr_setschedpolicy";
         break;
       }
   
       sched.sched_priority = prio;
   
       if( pthread_attr_setschedparam( &attr, &sched ) != 0 )
       {
         failure = "pthread_attr_setschedparam";
         break;
       }
   
       if( pthread_create( &tid, &attr, thread_run, args ) != 0 )
       {
         failure = "pthread_create";
         break;
       }
   
       if( pthread_attr_destroy( &attr ) != 0 )
       {
         failure = "pthread_attr_destroy";
         break;
       }
   
       return tid;
     }
   
     return tid;
   }
   
   // threadData_t communicates data to our threads
   typedef struct
   {
     char *name;
     sem_t semId;
     size_t stackFillerSize;
   } threadData_t;
   
   // Define a stack size for our threads
   static const int gs_StackSize = 1024 * 1024 * 8;
   
   // Our thread routine
   static void *checkStackThread(void *pArg)
   {
     threadData_t *lp_ThreadData = static_cast<threadData_t*>(pArg);
     int i;
   
     // Tell the world we are alive
     printf ("%s started\n", lp_ThreadData->name);
     // Wait until we are told to fill the stack
     sem_wait(&(lp_ThreadData->semId));
   
     char stackFiller[lp_ThreadData->stackFillerSize];
   
     printf ("%s performing checks\n", lp_ThreadData->name);
     for (i=0; i<(int)lp_ThreadData->stackFillerSize; i+=sysconf(_SC_PAGESIZE))
     {
       stackFiller[i] = 42;
     }
     printf ("%s done\n", lp_ThreadData->name);
   
     // Wait until we are told to terminate
     sem_wait(&(lp_ThreadData->semId));
     printf ("%s terminating\n", lp_ThreadData->name);
   
     return 0;
   }
   
   int main(int argc, char *argv[])
   {
     // Thread data
     pthread_t l_ThreadIds[2];
     threadData_t l_ThreadData[2];
   
     // Start a thread prior to locking memory.
     l_ThreadData[0].name = "Thread 1: ";
     (void)sem_init(&(l_ThreadData[0].semId), 0, 0);
     l_ThreadData[0].stackFillerSize = gs_StackSize - (1024*16);
     l_ThreadIds[0] = startThread(0, gs_StackSize, checkStackThread, static_cast<void*>(&(l_ThreadData[0])));
     // Give it 1 second to start
     sleep(1);
   
     bool l_LockMemory = true;
     int i;
     for (i=1; i<argc; i++)
     {
       if (strncmp (argv[i], "-nolockmem", 10) == 0)
       {
         l_LockMemory = false;
       }
     }
   
     if (l_LockMemory)
     {
       lockApplication();
       printf ("Current and future memory locked in RAM\n");
     }
   
     // printf something so we avoid introducing a page fault simply by performing
     // the potentially first printf call.
     const int l_PageSize = sysconf(_SC_PAGESIZE);
     printf ("Page size = %d\n", l_PageSize);
   
     (void)dumpPageFaults();        // Set the baseline
   
     // From this point onwards we no longer expect to have any
     // page faults for currently allocated memory.
   
     // Make the first thread fill the stack.
     sem_post(&(l_ThreadData[0].semId));
     // Give it 1 second to complete.
     sleep(1);
   
     bool l_PageFaultsDetectedInThread1 = dumpPageFaults();
   
     if (l_LockMemory)
     {
       if (l_PageFaultsDetectedInThread1 == false)
       {
         printf ("After locking memory, no new page faults have been generated during stack access of thread 1. This is expected.\n");
       }
       else
       {
         printf ("After locking memory, new page faults have been generated during during stack access of thread 1. This is unexpected!\n");
       }
     }
     // Thread 1 has not yet terminated, meaning that thread 2 will not
     // reuse the same (locked) stack region.
   
     // Start the second thread and make it fill the stack.
     l_ThreadData[1].name = "Thread 2: ";
     (void)sem_init(&(l_ThreadData[1].semId), 0, 0);
     l_ThreadData[1].stackFillerSize = gs_StackSize - (1024*16);
     l_ThreadIds[1] = startThread(0, gs_StackSize, checkStackThread,  static_cast<void*>(&(l_ThreadData[1])));
     // Give it 1 second to start
     sleep(1);
   
     if (l_LockMemory)
     {
       if (dumpPageFaults())        // Reset the baseline
       {
         printf ("New page faults have occurred as a result of starting thread 2. This is expected.\n");
       }
       else
       {
         printf ("Did not observe new page faults as a result of starting thread 2. This is unexpected!\n");
       }
     }
     else
     {
       (void)dumpPageFaults();      // Reset the baseline
     }
   
     sem_post(&(l_ThreadData[1].semId));
     // Give it 1 second to complete.
     sleep(1);
   
     bool l_PageFaultsDetectedInThread2 = dumpPageFaults();
   
     // Check whether page faults have been detected.
     int l_ExitCode = 0;
     if ((l_PageFaultsDetectedInThread1 == true) ||
       (l_PageFaultsDetectedInThread2 == true))
     {
       l_ExitCode = 1;
     }
   
     // Make the threads terminate
     sem_post(&(l_ThreadData[0].semId));
     sem_post(&(l_ThreadData[1].semId));
     (void)pthread_join(l_ThreadIds[0], 0);
     (void)pthread_join(l_ThreadIds[1], 0);
   
     printf ("Done, result: %s\n",
       (l_ExitCode != 0) ? "failed":"success");
   
     return l_ExitCode;
   }

Authors

Alex van der Wal

Personal tools