Verifying mlockall() effects on stack memory proof

From RTwiki
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.

   /* 
      gcc -o check_stack_page_faults check_stack_page_faults.c -Wall -O3 -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>
   #include <limits.h>
   
   /* struct thread_data communicates data to our threads */
   struct thread_data {
   	char   *name;
   	sem_t  semId;
   	size_t stack_filler_size;
   };
   
   /* Lock the application in memory to avoid page faults */
   static void lock_application(void)
   {
   	if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) {
   		perror("mlockall() failed");
   	}
   }
   
   /* Dump minor and major page faults that occurred since the previous call */
   static int dump_page_faults(void)
   {
   	int			page_faults_detected = 0;
   	static int		init = 0;
   	static struct rusage	rusage_prev;
   	struct rusage		rusage;
   	int 			new_minor_page_faults, new_major_page_faults;
   
   	getrusage(RUSAGE_SELF, &rusage);
   	new_minor_page_faults = rusage.ru_minflt - rusage_prev.ru_minflt;
   	new_major_page_faults = rusage.ru_majflt - rusage_prev.ru_majflt;
   	rusage_prev.ru_minflt = rusage.ru_minflt;
   	rusage_prev.ru_majflt = rusage.ru_majflt;
   
   	if (init) {
   		if ((new_minor_page_faults > 0) || 
   		    (new_major_page_faults > 0)) {
   			printf("New minor/major page faults: %d/%d\n",
   			       new_minor_page_faults, new_major_page_faults);
   			page_faults_detected = 1;
   		}
   	}
   	init = 1;
   	return page_faults_detected;
   }
   
   /* 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;
   }
   
   /* Define a stack size for our threads */
   static const int stack_size = 1024 * 1024 * 8;
   
   /* Our thread routine */
   static void *check_stack_thread(void *pArg)
   {
   	struct thread_data *pthread_data = (struct thread_data *)pArg;
   	int i;
   
   	/* Tell the world we are alive */
   	printf("%s started\n", pthread_data->name);
   	/* Wait until we are told to fill the stack */
   	sem_wait(&(pthread_data->semId));
   
   	{ /* Limit the scope */
   		char stack_filler[pthread_data->stack_filler_size];
   	
   		printf("%s performing checks\n", pthread_data->name);
   		for (i = 0; 
   		     i < (int)pthread_data->stack_filler_size;
   		     i += sysconf(_SC_PAGESIZE)) {
   			stack_filler[i] = 42;
   		}
   		printf("%s done\n", pthread_data->name);
   	}
   
   	/* Wait until we are told to terminate */
   	sem_wait(&(pthread_data->semId));
   	printf("%s terminating\n", pthread_data->name);
   
   	return 0;
   }
   
   int main(int argc, char *argv[])
   {
   	int 			lock_memory = 1;
   	int 			i;
   	const int 		page_size = sysconf(_SC_PAGESIZE);
   	int			page_faults_detected_in_thread1;
   	int			page_faults_detected_in_thread2;
   	/* Thread data */
   	pthread_t		thread_ids[2];
   	struct thread_data 	thread_data[2];
   
   	/* Start a thread prior to locking memory. */
   	thread_data[0].name = "Thread 1: ";
   	(void)sem_init(&(thread_data[0].semId), 0, 0);
   	thread_data[0].stack_filler_size = stack_size - PTHREAD_STACK_MIN;
   	thread_ids[0] = startThread(0, stack_size, check_stack_thread,
   						(void *)&thread_data[0]);
   	/* Give it 1 second to start */
   	sleep(1);
   
   	for (i = 1; i < argc; i++) {
   		if (strncmp(argv[i], "-nolockmem", 10) == 0) {
   			lock_memory = 0;
   		}
   	}
   
   	if (lock_memory) {
   		lock_application();
   		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. */
   	printf("Page size = %d\n", page_size);
   
   	(void)dump_page_faults(); /* 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(&(thread_data[0].semId));
   	/* Give it 1 second to complete. */
   	sleep(1);
   
   	page_faults_detected_in_thread1 = dump_page_faults();
   
   	if (lock_memory) {
   		if (!page_faults_detected_in_thread1)
   			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. */
   	thread_data[1].name = "Thread 2: ";
   	(void)sem_init(&(thread_data[1].semId), 0, 0);
   	thread_data[1].stack_filler_size = stack_size - PTHREAD_STACK_MIN;
   	thread_ids[1] = startThread(0, stack_size, check_stack_thread,
   						(void *)&thread_data[1]);
   	/* Give it 1 second to start */
   	sleep(1);
   
   	if (lock_memory) {
   		if (dump_page_faults())	/* 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)dump_page_faults(); /* Reset the baseline */
   	}
   
   	sem_post(&(thread_data[1].semId));
   	/* Give it 1 second to complete. */
   	sleep(1);
   
   	page_faults_detected_in_thread2 = dump_page_faults();
   
   	/* Check whether page faults have been detected. */
   	int exit_code = 0;
   	if ((page_faults_detected_in_thread1) ||
   	    (page_faults_detected_in_thread2)) {
   		exit_code = 1;
   	}
   	/* Make the threads terminate */
   	sem_post(&(thread_data[0].semId));
   	sem_post(&(thread_data[1].semId));
   	(void)pthread_join(thread_ids[0], 0);
   	(void)pthread_join(thread_ids[1], 0);
   
   	printf("Done, result: %s\n", (exit_code != 0) ? "failed" : "success");
   
   	return exit_code;
   }
   

Authors

Alex van der Wal

Personal tools