]> git.saurik.com Git - apple/xnu.git/blame - tools/tests/xnu_quick_test/memory_tests.c
xnu-2050.7.9.tar.gz
[apple/xnu.git] / tools / tests / xnu_quick_test / memory_tests.c
CommitLineData
2d21ac55
A
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"
b0d623f7 11#include <mach/mach.h>
6d2010ae 12#include <dirent.h> /* crashcount() */
2d21ac55
A
13
14extern char g_target_path[ PATH_MAX ];
15
6d2010ae
A
16/*
17 * static to localize to this compilation unit; volatile to avoid register
18 * optimization which would prevent modification by a signal handler.
19 */
20static 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 */
30void
31bus_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 */
44int
45crashcount(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;
316670eb 52 DIR *dirp1 = NULL, *dirp2 = NULL;
6d2010ae
A
53 struct dirent *dep1, *dep2;
54 int count = 0;
55
316670eb
A
56 /* If we can't open the directory, dirp1 will be NULL */
57 dirp1 = opendir(crashdir1);
6d2010ae 58
316670eb 59 while(dirp1 != NULL && ((dep1 = readdir(dirp1)) != NULL)) {
6d2010ae
A
60 if (strncmp(crash_file_pfx, dep1->d_name, crash_file_pfxlen))
61 continue;
62 /* record each one to get the last one */
63 if (namebuf1) {
64 strcpy(namebuf1, crashdir1);
65 strcat(namebuf1, "/");
66 strcat(namebuf1, dep1->d_name);
67 }
68 count++;
69 }
70
316670eb
A
71 if (dirp1 != NULL)
72 closedir(dirp1);
6d2010ae 73
316670eb
A
74#if !TARGET_OS_EMBEDDED
75 /* If we can't open the directory, dirp2 will be NULL */
76 dirp2 = opendir(crashdir2);
6d2010ae 77
316670eb 78 while(dirp2 != NULL && (dep2 = readdir(dirp2)) != NULL) {
6d2010ae
A
79 if (strncmp(crash_file_pfx, dep2->d_name, crash_file_pfxlen))
80 continue;
81 /* record each one to get the last one */
82 if (namebuf2) {
83 strcpy(namebuf2, crashdir2);
84 strcat(namebuf2, "/");
85 strcat(namebuf2, dep2->d_name);
86 }
87 count++;
88 }
316670eb
A
89 if (dirp2 != NULL)
90 closedir(dirp2);
91#endif
92 return( count );
6d2010ae
A
93}
94
95
2d21ac55
A
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 * **************************************************************************************************************
100 */
101int memory_tests( void * the_argp )
102{
2d21ac55
A
103 int my_page_size, my_status;
104 int my_fd = -1;
105 char * my_pathp = NULL;
106 char * my_bufp = NULL;
107 char * my_addr = NULL;
108 char * my_test_page_p = NULL;
109 ssize_t my_result;
110 pid_t my_pid, my_wait_pid;
b0d623f7 111 kern_return_t my_kr;
6d2010ae
A
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];
2d21ac55 116
b0d623f7
A
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;
121 }
122
2d21ac55
A
123 *my_pathp = 0x00;
124 strcat( my_pathp, &g_target_path[0] );
125 strcat( my_pathp, "/" );
126
127 /* create a test file */
128 my_err = create_random_name( my_pathp, 1 );
129 if ( my_err != 0 ) {
130 goto test_failed_exit;
131 }
132
133 my_page_size = getpagesize( );
b0d623f7
A
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;
138 }
139
2d21ac55
A
140 *my_test_page_p = 0x00;
141 strcat( my_test_page_p, "parent data" );
142
143 /* test minherit - share a page with child, add to the string in child then
144 * check for modification after child terminates.
145 */
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;
150 }
151
6d2010ae
A
152 /*
153 * Find out how many crashes there have already been; if it's not
154 * zero, then don't even attempt this test.
155 */
316670eb
A
156 my_namebuf1[0] = '\0';
157 my_namebuf2[0] = '\0';
6d2010ae
A
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
2d21ac55
A
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 */
b0d623f7
A
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 ) );
2d21ac55
A
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
b0d623f7
A
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
2d21ac55
A
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 ) {
b0d623f7
A
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) );
2d21ac55
A
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
b0d623f7
A
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
2d21ac55
A
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
b0d623f7
A
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
2d21ac55
A
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
6d2010ae
A
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
2d21ac55 368 *my_addr = 'z'; /* should cause SIGBUS signal (we look for this at child termination within the parent) */
2d21ac55 369
6d2010ae
A
370 /* NOTREACHED */
371
372 printf("Expected SIGBUS signal, got nothing!\n");
373 my_err = -1;
2d21ac55
A
374exit_child:
375 exit( my_err );
376 }
377
2d21ac55 378 /* parent process -
6d2010ae 379 * we should get no error if the child has completed all tests successfully
2d21ac55
A
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
6d2010ae
A
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 );
2d21ac55
A
397 goto test_failed_exit;
398 }
399
6d2010ae
A
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 */
316670eb
A
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)) {
6d2010ae 413 printf( "child did not crash as expected \n");
316670eb 414 printf( "saw %d crashes including %s \n", my_crashcount, my_namebuf1);
6d2010ae
A
415 goto test_failed_exit;
416 }
417
418 /* post-remove the expected crash report */
316670eb 419 if (unlink(my_namebuf1) && !(errno == ENOENT || errno == ENOTDIR)) {
6d2010ae
A
420 printf("unlink of expected crash report '%s' failed \n", my_namebuf1);
421 goto test_failed_exit;
422 }
316670eb
A
423#if !TARGET_OS_EMBEDDED
424 /* /Library/Logs/DiagnosticReports/ does not exist on embedded targets. */
425 if (unlink(my_namebuf2) && !(errno == ENOENT || errno == ENOTDIR)) {
6d2010ae
A
426 printf("unlink of expected crash report '%s' failed \n", my_namebuf2);
427 goto test_failed_exit;
428 }
316670eb 429#endif
2d21ac55
A
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;
434 }
435 my_err = 0;
436 goto test_passed_exit;
437
438test_failed_exit:
439 my_err = -1;
440
441test_passed_exit:
442 if ( my_pathp != NULL ) {
443 remove( my_pathp );
b0d623f7 444 vm_deallocate(mach_task_self(), (vm_address_t)my_pathp, PATH_MAX);
2d21ac55
A
445 }
446 if ( my_test_page_p != NULL ) {
b0d623f7 447 vm_deallocate(mach_task_self(), (vm_address_t)my_test_page_p, my_page_size);
2d21ac55
A
448 }
449 return( my_err );
450}
451