]>
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
);
52 DIR *dirp1
= NULL
, *dirp2
= NULL
;
53 struct dirent
*dep1
, *dep2
;
56 /* If we can't open the directory, dirp1 will be NULL */
57 dirp1
= opendir(crashdir1
);
59 while(dirp1
!= NULL
&& ((dep1
= readdir(dirp1
)) != NULL
)) {
60 if (strncmp(crash_file_pfx
, dep1
->d_name
, crash_file_pfxlen
))
62 /* record each one to get the last one */
64 strcpy(namebuf1
, crashdir1
);
65 strcat(namebuf1
, "/");
66 strcat(namebuf1
, dep1
->d_name
);
74 #if !TARGET_OS_EMBEDDED
75 /* If we can't open the directory, dirp2 will be NULL */
76 dirp2
= opendir(crashdir2
);
78 while(dirp2
!= NULL
&& (dep2
= readdir(dirp2
)) != NULL
) {
79 if (strncmp(crash_file_pfx
, dep2
->d_name
, crash_file_pfxlen
))
81 /* record each one to get the last one */
83 strcpy(namebuf2
, crashdir2
);
84 strcat(namebuf2
, "/");
85 strcat(namebuf2
, dep2
->d_name
);
96 /* **************************************************************************************************************
97 * Test madvise, mincore, minherit, mlock, mlock, mmap, mprotect, msync, munmap system calls.
98 * todo - see if Francois has better versions of these tests...
99 * **************************************************************************************************************
101 int memory_tests( void * the_argp
)
103 int my_page_size
, my_status
;
105 char * my_pathp
= NULL
;
106 char * my_bufp
= NULL
;
107 char * my_addr
= NULL
;
108 char * my_test_page_p
= NULL
;
110 pid_t my_pid
, my_wait_pid
;
112 struct sigaction my_sa
;
113 static int my_crashcount
;
114 static char my_namebuf1
[256]; /* XXX big enough */
115 static char my_namebuf2
[256];
117 my_kr
= vm_allocate((vm_map_t
) mach_task_self(), (vm_address_t
*)&my_pathp
, PATH_MAX
, VM_FLAGS_ANYWHERE
);
118 if(my_kr
!= KERN_SUCCESS
){
119 printf( "vm_allocate failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
120 goto test_failed_exit
;
124 strcat( my_pathp
, &g_target_path
[0] );
125 strcat( my_pathp
, "/" );
127 /* create a test file */
128 my_err
= create_random_name( my_pathp
, 1 );
130 goto test_failed_exit
;
133 my_page_size
= getpagesize( );
134 my_kr
= vm_allocate((vm_map_t
) mach_task_self(), (vm_address_t
*)&my_test_page_p
, my_page_size
, VM_FLAGS_ANYWHERE
);
135 if(my_kr
!= KERN_SUCCESS
){
136 printf( "vm_allocate failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
137 goto test_failed_exit
;
140 *my_test_page_p
= 0x00;
141 strcat( my_test_page_p
, "parent data" );
143 /* test minherit - share a page with child, add to the string in child then
144 * check for modification after child terminates.
146 my_err
= minherit( my_test_page_p
, my_page_size
, VM_INHERIT_SHARE
);
147 if ( my_err
== -1 ) {
148 printf( "minherit failed with error %d - \"%s\" \n", errno
, strerror( errno
) );
149 goto test_failed_exit
;
153 * Find out how many crashes there have already been; if it's not
154 * zero, then don't even attempt this test.
156 my_namebuf1
[0] = '\0';
157 my_namebuf2
[0] = '\0';
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 my_namebuf1
[0] = '\0';
410 my_namebuf2
[0] = '\0';
411 my_crashcount
= crashcount(my_namebuf1
, my_namebuf2
);
412 if (!(my_crashcount
== 1 || my_crashcount
== 2)) {
413 printf( "child did not crash as expected \n");
414 printf( "saw %d crashes including %s \n", my_crashcount
, my_namebuf1
);
415 goto test_failed_exit
;
418 /* post-remove the expected crash report */
419 if (unlink(my_namebuf1
) && !(errno
== ENOENT
|| errno
== ENOTDIR
)) {
420 printf("unlink of expected crash report '%s' failed \n", my_namebuf1
);
421 goto test_failed_exit
;
423 #if !TARGET_OS_EMBEDDED
424 /* /Library/Logs/DiagnosticReports/ does not exist on embedded targets. */
425 if (unlink(my_namebuf2
) && !(errno
== ENOENT
|| errno
== ENOTDIR
)) {
426 printf("unlink of expected crash report '%s' failed \n", my_namebuf2
);
427 goto test_failed_exit
;
430 /* make sure shared page got modified in child */
431 if ( strcmp( my_test_page_p
, "parent data child data" ) != 0 ) {
432 printf( "minherit did not work correctly - shared page looks wrong \n" );
433 goto test_failed_exit
;
436 goto test_passed_exit
;
442 if ( my_pathp
!= NULL
) {
444 vm_deallocate(mach_task_self(), (vm_address_t
)my_pathp
, PATH_MAX
);
446 if ( my_test_page_p
!= NULL
) {
447 vm_deallocate(mach_task_self(), (vm_address_t
)my_test_page_p
, my_page_size
);