]>
git.saurik.com Git - apple/xnu.git/blob - tools/tests/xnu_quick_test/memory_tests.c
5 * Created by Jerry Cottingham on 4/12/05.
6 * Copyright 2005 Apple Computer Inc. All rights reserved.
11 #include <mach/mach.h>
12 #include <dirent.h> /* crashcount() */
14 extern char g_target_path
[ PATH_MAX
];
17 * static to localize to this compilation unit; volatile to avoid register
18 * optimization which would prevent modification by a signal handler.
20 static volatile int my_err
;
23 * Handler; used by memory_tests() child to reset my_err so that it will
24 * exit normally following a SIGBUS, rather than triggering a crash report;
25 * this depends on setting the error non-zero before triggering the condition
26 * that would trigger a SIGBUS. To avoid confusion, this is most easily done
27 * right before the test in question, and if there are subsequent tests, then
28 * undone immediately after to avoid false test negatives.
31 bus_handler(int sig
, siginfo_t
*si
, void *mcontext
)
33 /* Reset global error value when we see a SIGBUS */
39 * Count the number of crashes for us in /Library/Logs/CrashReporter/
41 * XXX Assumes that CrashReporter uses our name as a prefix
42 * XXX Assumes no one lese has the same prefix as our name
45 crashcount(char *namebuf1
, char *namebuf2
)
47 char *crashdir1
= "/Library/Logs/CrashReporter";
48 char *crashdir2
= "/Library/Logs/DiagnosticReports";
49 char *crash_file_pfx
= "xnu_quick_test";
50 int crash_file_pfxlen
= strlen(crash_file_pfx
);
53 struct dirent
*dep1
, *dep2
;
56 /* If we can't open the directory, it hasn't been created */
57 if ((dirp1
= opendir(crashdir1
)) == NULL
) {
61 while((dep1
= readdir(dirp1
)) != NULL
) {
62 if (strncmp(crash_file_pfx
, dep1
->d_name
, crash_file_pfxlen
))
64 /* record each one to get the last one */
66 strcpy(namebuf1
, crashdir1
);
67 strcat(namebuf1
, "/");
68 strcat(namebuf1
, dep1
->d_name
);
75 /* If we can't open the directory, it hasn't been created */
76 if ((dirp2
= opendir(crashdir2
)) == NULL
) {
80 while((dep2
= readdir(dirp2
)) != NULL
) {
81 if (strncmp(crash_file_pfx
, dep2
->d_name
, crash_file_pfxlen
))
83 /* record each one to get the last one */
85 strcpy(namebuf2
, crashdir2
);
86 strcat(namebuf2
, "/");
87 strcat(namebuf2
, dep2
->d_name
);
98 /* **************************************************************************************************************
99 * Test madvise, mincore, minherit, mlock, mlock, mmap, mprotect, msync, munmap system calls.
100 * todo - see if Francois has better versions of these tests...
101 * **************************************************************************************************************
103 int memory_tests( void * the_argp
)
105 int my_page_size
, my_status
;
107 char * my_pathp
= NULL
;
108 char * my_bufp
= NULL
;
109 char * my_addr
= NULL
;
110 char * my_test_page_p
= NULL
;
112 pid_t my_pid
, my_wait_pid
;
114 struct sigaction my_sa
;
115 static int my_crashcount
;
116 static char my_namebuf1
[256]; /* XXX big enough */
117 static char my_namebuf2
[256];
119 my_kr
= vm_allocate((vm_map_t
) mach_task_self(), (vm_address_t
*)&my_pathp
, PATH_MAX
, VM_FLAGS_ANYWHERE
);
120 if(my_kr
!= KERN_SUCCESS
){
121 printf( "vm_allocate failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
122 goto test_failed_exit
;
126 strcat( my_pathp
, &g_target_path
[0] );
127 strcat( my_pathp
, "/" );
129 /* create a test file */
130 my_err
= create_random_name( my_pathp
, 1 );
132 goto test_failed_exit
;
135 my_page_size
= getpagesize( );
136 my_kr
= vm_allocate((vm_map_t
) mach_task_self(), (vm_address_t
*)&my_test_page_p
, my_page_size
, VM_FLAGS_ANYWHERE
);
137 if(my_kr
!= KERN_SUCCESS
){
138 printf( "vm_allocate failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
139 goto test_failed_exit
;
142 *my_test_page_p
= 0x00;
143 strcat( my_test_page_p
, "parent data" );
145 /* test minherit - share a page with child, add to the string in child then
146 * check for modification after child terminates.
148 my_err
= minherit( my_test_page_p
, my_page_size
, VM_INHERIT_SHARE
);
149 if ( my_err
== -1 ) {
150 printf( "minherit failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
151 goto test_failed_exit
;
155 * Find out how many crashes there have already been; if it's not
156 * zero, then don't even attempt this test.
158 if ((my_crashcount
= crashcount(my_namebuf1
, my_namebuf2
)) != 0) {
159 printf( "memtest aborted: can not distinguish our expected crash from \n");
160 printf( "%d existing crashes including %s \n", my_crashcount
, my_namebuf2
);
161 goto test_failed_exit
;
165 * spin off a child process that we will use for testing.
168 if ( my_pid
== -1 ) {
169 printf( "fork failed with errno %d - %s \n", errno
, strerror( errno
) );
170 goto test_failed_exit
;
176 strcat( my_test_page_p
, " child data" );
178 /* create a test file in page size chunks */
179 my_kr
= vm_allocate((vm_map_t
) mach_task_self(), (vm_address_t
*)&my_bufp
, (my_page_size
* 10), VM_FLAGS_ANYWHERE
);
180 if(my_kr
!= KERN_SUCCESS
){
181 printf( "vm_allocate failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
186 /* test madvise on anonymous memory */
187 my_err
= madvise(my_bufp
, (my_page_size
* 10), MADV_WILLNEED
);
188 if ( my_err
== -1 ) {
189 printf("madvise WILLNEED on anon memory failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
194 memset( my_bufp
, 'j', (my_page_size
* 10) );
195 my_fd
= open( my_pathp
, O_RDWR
, 0 );
197 printf( "open call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
202 /* test madvise on anonymous memory */
203 my_err
= madvise(my_bufp
, (my_page_size
* 10), MADV_DONTNEED
);
204 if ( my_err
== -1 ) {
205 printf("madvise DONTNEED on anon memory failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
210 my_result
= write( my_fd
, my_bufp
, (my_page_size
* 10) );
211 if ( my_result
== -1 ) {
212 printf( "write call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
217 /* map the file into memory */
218 my_addr
= (char *) mmap( NULL
, (my_page_size
* 2), (PROT_READ
| PROT_WRITE
), (MAP_FILE
| MAP_SHARED
), my_fd
, 0 );
219 if ( my_addr
== (char *) -1 ) {
220 printf( "mmap call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
225 /* make sure we got the right data mapped */
226 if ( *my_addr
!= 'j' || *(my_addr
+ my_page_size
) != 'j' ) {
227 printf( "did not map in correct data \n" );
233 my_err
= madvise( my_addr
, (my_page_size
* 2), MADV_WILLNEED
);
234 if ( my_err
== -1 ) {
235 printf( "madvise WILLNEED call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
240 my_err
= madvise( my_addr
, (my_page_size
* 2), MADV_DONTNEED
);
241 if ( my_err
== -1 ) {
242 printf( "madvise DONTNEED call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
247 /* test mincore, mlock, mlock */
248 my_err
= mlock( my_addr
, my_page_size
);
249 if ( my_err
== -1 ) {
250 printf( "mlock call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
255 /* mybufp is about to be reused, so test madvise on anonymous memory */
256 my_err
= madvise(my_bufp
, (my_page_size
* 10), MADV_FREE
);
257 if ( my_err
== -1 ) {
258 printf("madvise FREE on anon memory failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
263 my_err
= mincore( my_addr
, 1, my_bufp
);
264 if ( my_err
== -1 ) {
265 printf( "mincore call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
269 /* page my_addr is in should be resident after mlock */
270 if ( (*my_bufp
& MINCORE_INCORE
) == 0 ) {
271 printf( "mincore call failed to find resident page \n" );
276 my_err
= munlock( my_addr
, my_page_size
);
277 if ( my_err
== -1 ) {
278 printf( "munlock call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
283 /* modify first page then use msync to push data out */
284 memset( my_addr
, 'x', my_page_size
);
285 my_err
= msync( my_addr
, my_page_size
, (MS_SYNC
| MS_INVALIDATE
) );
286 if ( my_err
== -1 ) {
287 printf( "msync call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
293 my_err
= madvise( my_addr
, (my_page_size
* 2), MADV_DONTNEED
);
294 if ( my_err
== -1 ) {
295 printf( "madvise DONTNEED call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
301 my_err
= madvise( my_addr
, (my_page_size
* 2), MADV_FREE
);
302 if ( my_err
== -1 ) {
303 printf( "madvise FREE call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
308 /* verify that the file was updated */
309 lseek( my_fd
, 0, SEEK_SET
);
310 bzero( (void *)my_bufp
, my_page_size
);
311 my_result
= read( my_fd
, my_bufp
, my_page_size
);
312 if ( my_result
== -1 ) {
313 printf( "read call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
317 if ( *my_bufp
!= 'x' ) {
318 printf( "msync did not flush correct data \n" );
323 /* unmap our test page */
324 my_err
= munmap( my_addr
, (my_page_size
* 2) );
325 if ( my_err
== -1 ) {
326 printf( "munmap call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
332 /* map the file into memory again for mprotect test */
333 my_addr
= (char *) mmap( NULL
, (my_page_size
* 2), (PROT_READ
| PROT_WRITE
), (MAP_FILE
| MAP_SHARED
), my_fd
, 0 );
334 if ( my_addr
== (char *) -1 ) {
335 printf( "mmap call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
343 /* test mprotect - change protection to only PROT_READ */
344 my_err
= mprotect( my_addr
, my_page_size
, PROT_READ
);
345 if ( my_err
== -1 ) {
346 printf( "mprotect call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
352 * Establish SIGBUS handler; will reset (disable itself) if it fires;
353 * we would need how to recover from the exceptional condition that
354 * raised the SIGBUS by modifying the contents of the (opaque to us)
355 * mcontext in order to prevent this from being terminal, so we let
356 * it be terminal. This is enough to avoid triggering crash reporter.
358 my_sa
.sa_sigaction
= bus_handler
;
359 my_sa
.sa_flags
= SA_SIGINFO
| SA_RESETHAND
;
360 if ((my_err
= sigaction(SIGBUS
, &my_sa
, NULL
)) != 0) {
361 printf("sigaction call failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
366 my_err
= -1; /* default to error out if we do NOT trigger a SIGBUS */
368 *my_addr
= 'z'; /* should cause SIGBUS signal (we look for this at child termination within the parent) */
372 printf("Expected SIGBUS signal, got nothing!\n");
379 * we should get no error if the child has completed all tests successfully
381 my_wait_pid
= wait4( my_pid
, &my_status
, 0, NULL
);
382 if ( my_wait_pid
== -1 ) {
383 printf( "wait4 failed with errno %d - %s \n", errno
, strerror( errno
) );
384 goto test_failed_exit
;
387 /* wait4 should return our child's pid when it exits */
388 if ( my_wait_pid
!= my_pid
) {
389 printf( "wait4 did not return child pid - returned %d should be %d \n", my_wait_pid
, my_pid
);
390 goto test_failed_exit
;
393 /* If we were not signalled, or we died from an unexpected signal, report it.
395 if ( !WIFSIGNALED( my_status
) || WTERMSIG( my_status
) != SIGBUS
) {
396 printf( "wait4 returned child died of status - 0x%02X \n", my_status
);
397 goto test_failed_exit
;
401 * Wait long enough that CrashReporter has finished.
406 * Find out how many crashes there have already been; if it's not
407 * one, then don't even attempt this test.
409 if ((my_crashcount
= crashcount(my_namebuf1
, my_namebuf2
)) != 1) {
410 printf( "child did not crash as expected \n");
411 printf( "saw %d crashes including %s \n", my_crashcount
, my_namebuf2
);
412 goto test_failed_exit
;
415 /* post-remove the expected crash report */
416 if (unlink(my_namebuf1
)) {
417 printf("unlink of expected crash report '%s' failed \n", my_namebuf1
);
418 goto test_failed_exit
;
421 if (unlink(my_namebuf2
)) {
422 printf("unlink of expected crash report '%s' failed \n", my_namebuf2
);
423 goto test_failed_exit
;
426 /* make sure shared page got modified in child */
427 if ( strcmp( my_test_page_p
, "parent data child data" ) != 0 ) {
428 printf( "minherit did not work correctly - shared page looks wrong \n" );
429 goto test_failed_exit
;
432 goto test_passed_exit
;
438 if ( my_pathp
!= NULL
) {
440 vm_deallocate(mach_task_self(), (vm_address_t
)my_pathp
, PATH_MAX
);
442 if ( my_test_page_p
!= NULL
) {
443 vm_deallocate(mach_task_self(), (vm_address_t
)my_test_page_p
, my_page_size
);