//============================================================================= /** * @file Malloc_Test.cpp * * This is a test of the position-independent memory * manager using the and . * * @author Douglas C. Schmidt */ //============================================================================= #include "test_config.h" #include "Malloc_Test.h" #include "ace/Malloc_T.h" #include "ace/MMAP_Memory_Pool.h" #include "ace/Process.h" #include "ace/Auto_Ptr.h" #include "ace/Process_Mutex.h" #include "ace/PI_Malloc.h" #include "ace/RW_Thread_Mutex.h" #include "ace/Time_Value.h" #include "ace/OS_NS_unistd.h" #if defined (ACE_HAS_PROCESS_SPAWN) #if (ACE_HAS_POSITION_INDEPENDENT_POINTERS == 1) using MALLOC = ACE_Malloc_T; #else typedef ACE_Malloc MALLOC; #endif /* ACE_HAS_POSITION_INDEPENDENT_POINTERS == 1 */ #define MMAP_FILENAME ACE_TEXT ("test_file") #define MUTEX_NAME ACE_TEXT ("test_lock") #if !defined (ACE_LINUX) \ && !defined (ACE_ANDROID) \ && !(defined (ACE_WIN32) \ && (defined (ghs) || defined (__MINGW32__) )) \ && !(defined (__OpenBSD__) && defined (ACE_HAS_PTHREADS)) #define ACE_TEST_REMAP_ON_FAULT // Linux seems to have problem when calling mmap from the signal handler. // The Green Hills Native x86 compiler does not support structural exceptions. // Mingw's gcc does not support structural exceptions. // Win9x doesn't support remaps. // OpenBSD causes this test to hang in the child when pthreads are enabled. // On these platforms, we make sure the remapping will never occur. #endif /* ACE_LINUX && Win32 GHS*/ #if defined (ACE_WIN32) // When looking for the file to execute a process on Win32, the directory from // containing the parent process file is searched first. Since certain Win32 // configurations (e.g. Borland C++Builder) put the output files in a different // directory we will use this feature rather than specifying '.\'. # define EXE_LOCATION ACE_TEXT ("") #else # define EXE_LOCATION ACE_TEXT (".") ACE_DIRECTORY_SEPARATOR_STR #endif /*ACE_WIN32*/ // Parents base address in shared memory. static const void *PARENT_BASE_ADDR = ACE_DEFAULT_BASE_ADDR; // If the platform supports position-independent malloc, choose // another base address that's 1M higher so that will be // mapped into a different address in the child's virtual memory. // Note that on HP-UX on PA-RISC hardware, a single range of a file // cannot be mapped into multiple virtual address ranges, even across // processes. So, though the whole PI pointer thing is tested here, // it isn't actually using multiple address ranges. #if (ACE_HAS_POSITION_INDEPENDENT_POINTERS == 1) # define CHILD_ADDR_DELTA (1024*1024) #else # define CHILD_ADDR_DELTA 0 #endif /* CHILD_ADDR_DELTA */ static const void *CHILD_BASE_ADDR = (const void *)(CHILD_ADDR_DELTA + ACE_DEFAULT_BASE_ADDR); // Shared memory allocator. Hide the allocator inside this function // so that it doesn't get constructed until after the // gets constructed, even with // . static MALLOC * myallocator (const void *base_addr = 0) { static std::unique_ptr static_allocator; if (static_allocator.get () == 0) { ACE_MMAP_Memory_Pool_Options options (base_addr); #if !defined (ACE_TEST_REMAP_ON_FAULT) options.minimum_bytes_ = 512 * 1024; #endif /* ACE_TEST_REMAP_ON_FAULT */ MALLOC *ptr = new MALLOC (MMAP_FILENAME, MUTEX_NAME, &options); static_allocator.reset (ptr); } return static_allocator.get (); } static void init_test (const void *base_addr = 0) { // Cleanup the MMAP file so we won't trip over the leftover mmap // file from the previous crash. ACE_MMAP_Memory_Pool_Options options (base_addr); //FUZZ: disable check_for_lack_ACE_OS ACE_MMAP_Memory_Pool mmap (MMAP_FILENAME, &options); //FUZZ: enable check_for_lack_ACE_OS size_t rbyte = 0; int ft = 0; mmap.init_acquire (1024, rbyte, ft); mmap.release (); } static Test_Data * initialize (MALLOC *allocator) { double *temp = 0; ACE_ALLOCATOR_RETURN (temp, (double *) allocator->malloc (sizeof (double)), 0); // Make sure that doubles work! *temp = 5.0; allocator->free (temp); void *ptr = 0; ACE_ALLOCATOR_RETURN (ptr, allocator->malloc (sizeof (Test_Data)), 0); Test_Data *data1 = new (ptr) Test_Data; data1->i1_ = 111; data1->i2_ = 222; data1->i3_ = 333; data1->d1_ = 87.5; void *gap = 0; ACE_ALLOCATOR_RETURN (gap, allocator->malloc (sizeof (256)), 0); allocator->free (gap); ACE_ALLOCATOR_RETURN (ptr, allocator->malloc (sizeof (Test_Data)), 0); Test_Data *data2 = new (ptr) Test_Data; data1->next_ = 0; data2->next_ = data1; data2->i1_ = -111; data2->i2_ = -222; data2->i3_ = -333; data2->d1_ = 77.34; // Test in shared memory using long (array/pointer) ACE_ALLOCATOR_RETURN (ptr, allocator->malloc (sizeof (Long_Test)), 0); Long_Test *lt = new (ptr) Long_Test; lt->array_[0] = 1000; lt->array_[1] = 1001; lt->array_[2] = 1002; lt->array_[3] = 1003; lt->array_[4] = 1004; lt->bpl_ = lt->array_; data1->long_test_= lt; long long_cont_1 = *lt->bpl_; long long_cont_2 = lt->bpl_[3]; ACE_TEST_ASSERT (long_cont_1 == 1000); ACE_TEST_ASSERT (long_cont_2 == 1003); ACE_ALLOCATOR_RETURN (ptr, allocator->malloc (sizeof (Long_Test)), 0); lt = new (ptr) Long_Test; lt->array_[0] = 2000; lt->array_[1] = 2001; lt->array_[2] = 2002; lt->array_[3] = 2003; lt->array_[4] = 2004; lt->bpl_ = lt->array_; data2->long_test_= lt; long long_cont_3 = *lt->bpl_; long long_cont_4 = lt->bpl_[4]; ACE_TEST_ASSERT (long_cont_3 == 2000); ACE_TEST_ASSERT (long_cont_4 == 2004); return data2; } static void print (const char *process_name, Test_Data *data) { for (Test_Data *t = data; t != 0; t = t->next_) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("<<<< (%P) %C\ni1_ = %d, i2_ = %d, i3_ = %d, d1_ = %f\n"), process_name, t->i1_, t->i2_, t->i3_, t->d1_)); ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("*t->bpl_ = %d, t->long_test_->array_[0] = ") ACE_TEXT ("%d\n>>>>\n"), *t->long_test_->bpl_, t->long_test_->array_[0])); } } static int parent (Test_Data *data) { MALLOC *myalloc = myallocator (); { ACE_GUARD_RETURN (ACE_Process_Mutex, guard, myalloc->mutex (), -1); print ("parent", data); } // Sleep for a 200 msecs so that the child will have a chance to spin! ACE_OS::sleep (ACE_Time_Value (0, 200 * 1000)); #if defined (ACE_TEST_REMAP_ON_FAULT) char *small_buf[1024]; int cntr; for (cntr = 0 ; cntr < 1024; ++cntr) small_buf[cntr] = (char *) myalloc->malloc (1); char *big_buf = (char *) myalloc->malloc (1024 * 4069); #endif /* ACE_TEST_REMAP_ON_FAULT */ int result = myalloc->bind ("bar", data); #if defined (ACE_TEST_REMAP_ON_FAULT) myalloc->free (big_buf); for (cntr = 0 ; cntr < 1024; ++cntr) myalloc->free (small_buf[cntr]); #endif /* ACE_TEST_REMAP_ON_FAULT */ ACE_TEST_ASSERT (result != -1); return 0; } static int child () { void *bar = 0; // Perform "busy waiting" here until the parent stores data under a // new name called "bar" in . This isn't a good design // -- it's just to test that synchronization is working across // processes via . for (ACE_Time_Value timeout (0, 1000 * 10); myallocator ()->find ("bar", bar) == -1; ) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P) sleeping for 10 milliseconds!\n"))); ACE_OS::sleep (timeout); } print ("child", reinterpret_cast (bar)); return 0; } #if defined (ACE_HAS_WIN32_GETVERSION) // On Win9x/Me, a shared address needs to be on the shared arena, // between the second and third megabyte in the virtual address space // of the process. Also, a mapped view of a file is shared on the same // virtual address on every 32 bit process. On WinNT/2k, memory above // 2Gb is reserved for the system. So, we need to check at runtime // (we want an ACE_HAS_WINNT4 == 0 ace to run on either). // To catch any odd case arising from Pharlap, do the // run time check and run the NT4-or-better code unless we're on // CE or something other than NT4 (Pharlap reports itself as NT 3.51). static void get_base_addrs () { # if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdeprecated-declarations" # endif /* __clang__ */ OSVERSIONINFO vinfo; vinfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); if (::GetVersionEx(&vinfo) == 0) return; # if defined(__clang__) # pragma clang diagnostic pop # endif /* __clang__ */ if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT && vinfo.dwMajorVersion >= 4) PARENT_BASE_ADDR = (char*) (64 * 1024*1024); else PARENT_BASE_ADDR = (char*) ((2048UL + 512UL)*(1024UL*1024UL)); CHILD_BASE_ADDR = CHILD_ADDR_DELTA + (char*) PARENT_BASE_ADDR; } #endif /* defined (ACE_HAS_WIN32_GETVERSION) */ int run_main (int argc, ACE_TCHAR *argv[]) { #if defined (ACE_HAS_WIN32_GETVERSION) get_base_addrs(); #endif if (argc == 1) { ACE_START_TEST (ACE_TEXT ("Malloc_Test")); ACE_INIT_LOG (ACE_TEXT ("Malloc_Test-child")); init_test (PARENT_BASE_ADDR); ACE_Control_Block::print_alignment_info (); # if (ACE_HAS_POSITION_INDEPENDENT_POINTERS == 1) ACE_PI_Control_Block::print_alignment_info (); # endif /* ACE_HAS_POSITION_INDEPENDENT_POINTERS == 1 */ // No arguments means we're the parent process. ACE_Process_Options options (1); options.command_line (ACE_TEXT ("%") ACE_TEXT_PRIs ACE_TEXT ("%") ACE_TEXT_PRIs ACE_TEXT ("%") ACE_TEXT_PRIs, EXE_LOCATION, argc > 0 ? argv[0] : ACE_TEXT ("Malloc_Test"), ACE_TEXT (" run_as_test")); MALLOC *myalloc = myallocator (PARENT_BASE_ADDR); Test_Data *data = initialize (myalloc); ACE_TEST_ASSERT (data != 0); ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P) PARENT allocator at = %@, ") ACE_TEXT ("data allocated at %@\n"), myalloc, data)); myalloc->dump (); int result = myalloc->bind ("foo", data); ACE_TEST_ASSERT (result != -1); ACE_Process p; pid_t pid = p.spawn (options); if (pid == -1) ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("spawn")), 1); parent (data); // Synchronize on the exit of the child. result = p.wait (); if (result == -1) ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("wait")), 1); ACE_TEST_ASSERT (myalloc->ref_counter () == 1); myalloc->remove (); ACE_Process_Mutex::unlink (MUTEX_NAME); ACE_END_TEST; return 0; } else { // In this case we're the child process. ACE_APPEND_LOG (ACE_TEXT ("Malloc_Test-child")); void *data = 0; MALLOC *myalloc = myallocator (CHILD_BASE_ADDR); int result = myalloc->find ("foo", data); ACE_TEST_ASSERT (result != -1); ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%P) CHILD allocator at = %@, ") ACE_TEXT ("data allocated at %@\n"), myalloc, data)); myalloc->dump (); child (); myalloc->release (); ACE_END_LOG; return 0; } } #else int run_main (int, ACE_TCHAR *[]) { ACE_START_TEST (ACE_TEXT ("Malloc_Test")); ACE_ERROR ((LM_INFO, ACE_TEXT ("process creation is not supported on this ") ACE_TEXT ("platform\n"))); ACE_END_TEST; return 0; } #endif /* ACE_HAS_PROCESS_SPAWN */