]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/xnu_quick_test/memory_tests.c
xnu-1699.26.8.tar.gz
[apple/xnu.git] / tools / tests / xnu_quick_test / memory_tests.c
1 /*
2 * memory_tests.c.c
3 * xnu_quick_test
4 *
5 * Created by Jerry Cottingham on 4/12/05.
6 * Copyright 2005 Apple Computer Inc. All rights reserved.
7 *
8 */
9
10 #include "tests.h"
11 #include <mach/mach.h>
12 #include <dirent.h> /* crashcount() */
13
14 extern char g_target_path[ PATH_MAX ];
15
16 /*
17 * static to localize to this compilation unit; volatile to avoid register
18 * optimization which would prevent modification by a signal handler.
19 */
20 static volatile int my_err;
21
22 /*
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.
29 */
30 void
31 bus_handler(int sig, siginfo_t *si, void *mcontext)
32 {
33 /* Reset global error value when we see a SIGBUS */
34 if (sig == SIGBUS)
35 my_err = 0;
36 }
37
38 /*
39 * Count the number of crashes for us in /Library/Logs/CrashReporter/
40 *
41 * XXX Assumes that CrashReporter uses our name as a prefix
42 * XXX Assumes no one lese has the same prefix as our name
43 */
44 int
45 crashcount(char *namebuf1, char *namebuf2)
46 {
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);
51 struct stat sb;
52 DIR *dirp1, *dirp2;
53 struct dirent *dep1, *dep2;
54 int count = 0;
55
56 /* If we can't open the directory, it hasn't been created */
57 if ((dirp1 = opendir(crashdir1)) == NULL) {
58 return( 0 );
59 }
60
61 while((dep1 = readdir(dirp1)) != NULL) {
62 if (strncmp(crash_file_pfx, dep1->d_name, crash_file_pfxlen))
63 continue;
64 /* record each one to get the last one */
65 if (namebuf1) {
66 strcpy(namebuf1, crashdir1);
67 strcat(namebuf1, "/");
68 strcat(namebuf1, dep1->d_name);
69 }
70 count++;
71 }
72
73 closedir(dirp1);
74
75 /* If we can't open the directory, it hasn't been created */
76 if ((dirp2 = opendir(crashdir2)) == NULL) {
77 return( 0 );
78 }
79
80 while((dep2 = readdir(dirp2)) != NULL) {
81 if (strncmp(crash_file_pfx, dep2->d_name, crash_file_pfxlen))
82 continue;
83 /* record each one to get the last one */
84 if (namebuf2) {
85 strcpy(namebuf2, crashdir2);
86 strcat(namebuf2, "/");
87 strcat(namebuf2, dep2->d_name);
88 }
89 count++;
90 }
91
92 closedir(dirp2);
93
94 return( count/2 );
95 }
96
97
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 * **************************************************************************************************************
102 */
103 int memory_tests( void * the_argp )
104 {
105 int my_page_size, my_status;
106 int my_fd = -1;
107 char * my_pathp = NULL;
108 char * my_bufp = NULL;
109 char * my_addr = NULL;
110 char * my_test_page_p = NULL;
111 ssize_t my_result;
112 pid_t my_pid, my_wait_pid;
113 kern_return_t my_kr;
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];
118
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;
123 }
124
125 *my_pathp = 0x00;
126 strcat( my_pathp, &g_target_path[0] );
127 strcat( my_pathp, "/" );
128
129 /* create a test file */
130 my_err = create_random_name( my_pathp, 1 );
131 if ( my_err != 0 ) {
132 goto test_failed_exit;
133 }
134
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;
140 }
141
142 *my_test_page_p = 0x00;
143 strcat( my_test_page_p, "parent data" );
144
145 /* test minherit - share a page with child, add to the string in child then
146 * check for modification after child terminates.
147 */
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;
152 }
153
154 /*
155 * Find out how many crashes there have already been; if it's not
156 * zero, then don't even attempt this test.
157 */
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;
162 }
163
164 /*
165 * spin off a child process that we will use for testing.
166 */
167 my_pid = fork( );
168 if ( my_pid == -1 ) {
169 printf( "fork failed with errno %d - %s \n", errno, strerror( errno ) );
170 goto test_failed_exit;
171 }
172 if ( my_pid == 0 ) {
173 /*
174 * child process...
175 */
176 strcat( my_test_page_p, " child data" );
177
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) );
182 my_err = -1;
183 goto exit_child;
184 }
185
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 ) );
190 my_err = -1;
191 goto exit_child;
192 }
193
194 memset( my_bufp, 'j', (my_page_size * 10) );
195 my_fd = open( my_pathp, O_RDWR, 0 );
196 if ( my_fd == -1 ) {
197 printf( "open call failed with error %d - \"%s\" \n", errno, strerror( errno) );
198 my_err = -1;
199 goto exit_child;
200 }
201
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 ) );
206 my_err = -1;
207 goto exit_child;
208 }
209
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) );
213 my_err = -1;
214 goto exit_child;
215 }
216
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) );
221 my_err = -1;
222 goto exit_child;
223 }
224
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" );
228 my_err = -1;
229 goto exit_child;
230 }
231
232 /* test madvise */
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) );
236 my_err = -1;
237 goto exit_child;
238 }
239
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) );
243 my_err = -1;
244 goto exit_child;
245 }
246
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) );
251 my_err = -1;
252 goto exit_child;
253 }
254
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 ) );
259 my_err = -1;
260 goto exit_child;
261 }
262
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) );
266 my_err = -1;
267 goto exit_child;
268 }
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" );
272 my_err = -1;
273 goto exit_child;
274 }
275
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) );
279 my_err = -1;
280 goto exit_child;
281 }
282
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) );
288 my_err = -1;
289 goto exit_child;
290 }
291
292 /* test madvise */
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) );
296 my_err = -1;
297 goto exit_child;
298 }
299
300 /* test madvise */
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) );
304 my_err = -1;
305 goto exit_child;
306 }
307
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) );
314 my_err = -1;
315 goto exit_child;
316 }
317 if ( *my_bufp != 'x' ) {
318 printf( "msync did not flush correct data \n" );
319 my_err = -1;
320 goto exit_child;
321 }
322
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) );
327 my_err = -1;
328 goto exit_child;
329 }
330 my_addr = NULL;
331
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) );
336 my_err = -1;
337 goto exit_child;
338 }
339 *my_addr = 'a';
340
341
342
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) );
347 my_err = -1;
348 goto exit_child;
349 }
350
351 /*
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.
357 */
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) );
362 my_err = -1;
363 goto exit_child;
364 }
365
366 my_err = -1; /* default to error out if we do NOT trigger a SIGBUS */
367
368 *my_addr = 'z'; /* should cause SIGBUS signal (we look for this at child termination within the parent) */
369
370 /* NOTREACHED */
371
372 printf("Expected SIGBUS signal, got nothing!\n");
373 my_err = -1;
374 exit_child:
375 exit( my_err );
376 }
377
378 /* parent process -
379 * we should get no error if the child has completed all tests successfully
380 */
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;
385 }
386
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;
391 }
392
393 /* If we were not signalled, or we died from an unexpected signal, report it.
394 */
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;
398 }
399
400 /*
401 * Wait long enough that CrashReporter has finished.
402 */
403 sleep(5);
404
405 /*
406 * Find out how many crashes there have already been; if it's not
407 * one, then don't even attempt this test.
408 */
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;
413 }
414
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;
419 }
420
421 if (unlink(my_namebuf2)) {
422 printf("unlink of expected crash report '%s' failed \n", my_namebuf2);
423 goto test_failed_exit;
424 }
425
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;
430 }
431 my_err = 0;
432 goto test_passed_exit;
433
434 test_failed_exit:
435 my_err = -1;
436
437 test_passed_exit:
438 if ( my_pathp != NULL ) {
439 remove( my_pathp );
440 vm_deallocate(mach_task_self(), (vm_address_t)my_pathp, PATH_MAX);
441 }
442 if ( my_test_page_p != NULL ) {
443 vm_deallocate(mach_task_self(), (vm_address_t)my_test_page_p, my_page_size);
444 }
445 return( my_err );
446 }
447