1 // Copyright (C) 2013 The Android Open Source Project 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions 6 // are met: 7 // 1. Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // 2. Redistributions in binary form must reproduce the above copyright 10 // notice, this list of conditions and the following disclaimer in the 11 // documentation and/or other materials provided with the distribution. 12 // 3. Neither the name of the project nor the names of its contributors 13 // may be used to endorse or promote products derived from this software 14 // without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 // ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 20 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 // SUCH DAMAGE. 27 28 // A test used to check that __cxa_get_globals() does not use malloc. 29 // This will do the following: 30 // 31 // - Lazily load libtest_malloc_lockup.so, which includes a copy of 32 // GAbi++ linked with malloc() / free() functions that exit() with 33 // an error if called. 34 // 35 // - Create a large number of concurrent threads, and have each one 36 // call the library's 'get_globals' function, which returns the 37 // result of __cxa_get_globals() linked against the special mallocs, 38 // then store the value in a global array. 39 // 40 // - Tell all the threads to stop, wait for them to complete. 41 // 42 // - Look at the values stored in the global arrays. They should not be NULL 43 // (to indicate succesful allocation), and all different (each one should 44 // correspond to a thread-specific instance of __cxa_eh_globals). 45 // 46 // - Unload the library. 47 // 48 49 #include <dlfcn.h> 50 #include <errno.h> 51 #include <pthread.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 56 typedef void* (*get_globals_fn)(); 57 58 static get_globals_fn g_get_globals; 59 60 // Number of threads to create. Must be > 4096 to really check slab allocation. 61 static const size_t kMaxThreads = 5000; 62 63 static pthread_t g_threads[kMaxThreads]; 64 static void* g_thread_objects[kMaxThreads]; 65 66 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER; 67 static pthread_cond_t g_cond_exit = PTHREAD_COND_INITIALIZER; 68 static pthread_cond_t g_cond_counter = PTHREAD_COND_INITIALIZER; 69 static unsigned g_thread_count = 0; 70 static bool g_can_exit = false; 71 72 // Thread routine, just call 'get_globals' and store the result in our global 73 // array, then wait for an event from the main thread. This guarantees that 74 // no thread exits before another one starts, and thus that allocation slots 75 // are not reused. 76 static void* my_thread(void* param) { 77 // Get thread-specific object pointer, store it in global array. 78 int id = (int)(intptr_t)param; 79 g_thread_objects[id] = (*g_get_globals)(); 80 81 // Increment global thread counter and tell the main thread about this. 82 pthread_mutex_lock(&g_lock); 83 g_thread_count += 1; 84 pthread_cond_signal(&g_cond_counter); 85 86 // The thread object will be automatically released/recycled when the thread 87 // exits. Wait here until signaled by the main thread to avoid this. 88 while (!g_can_exit) 89 pthread_cond_wait(&g_cond_exit, &g_lock); 90 pthread_mutex_unlock(&g_lock); 91 92 return NULL; 93 } 94 95 int main(void) { 96 // Load the library. 97 void* lib = dlopen("libtest_malloc_lockup.so", RTLD_NOW); 98 if (!lib) { 99 fprintf(stderr, "ERROR: Can't find library: %s\n", strerror(errno)); 100 return 1; 101 } 102 103 // Extract 'get_globals' function address. 104 g_get_globals = reinterpret_cast<get_globals_fn>(dlsym(lib, "get_globals")); 105 if (!g_get_globals) { 106 fprintf(stderr, "ERROR: Could not find 'get_globals' function: %s\n", 107 dlerror()); 108 dlclose(lib); 109 return 1; 110 } 111 112 // Use a smaller stack per thread to be able to create lots of them. 113 pthread_attr_t attr; 114 pthread_attr_init(&attr); 115 pthread_attr_setstacksize(&attr, 16384); 116 117 // Start as many threads as needed. 118 printf("Creating %d threads\n", kMaxThreads); 119 for (size_t n = 0; n < kMaxThreads; ++n) { 120 int ret = pthread_create(&g_threads[n], &attr, my_thread, (void*)n); 121 if (ret != 0) { 122 fprintf(stderr, "ERROR: Thread #%d creation error: %s\n", 123 n + 1, strerror(errno)); 124 return 2; 125 } 126 } 127 128 // Wait until they all ran, then tell them to exit. 129 printf("Waiting for all threads to run\n"); 130 pthread_mutex_lock(&g_lock); 131 while (g_thread_count < kMaxThreads) 132 pthread_cond_wait(&g_cond_counter, &g_lock); 133 134 printf("Waking up threads\n"); 135 g_can_exit = true; 136 pthread_cond_broadcast(&g_cond_exit); 137 pthread_mutex_unlock(&g_lock); 138 139 // Wait for them to complete. 140 printf("Waiting for all threads to complete\n"); 141 for (size_t n = 0; n < kMaxThreads; ++n) { 142 void* dummy; 143 pthread_join(g_threads[n], &dummy); 144 } 145 146 // Verify that the thread objects are all non-NULL and different. 147 printf("Checking results\n"); 148 size_t failures = 0; 149 const size_t kMaxFailures = 16; 150 for (size_t n = 0; n < kMaxThreads; ++n) { 151 void* obj = g_thread_objects[n]; 152 if (obj == NULL) { 153 if (++failures < kMaxFailures) 154 printf("Thread %d got a NULL object!\n", n + 1); 155 } else { 156 for (size_t m = n + 1; m < kMaxThreads; ++m) { 157 if (g_thread_objects[m] == obj) { 158 if (++failures < kMaxFailures) 159 printf("Thread %d has same object as thread %d (%p)\n", 160 n + 1, m + 1, obj); 161 } 162 } 163 } 164 } 165 166 // We're done. 167 dlclose(lib); 168 if (failures > 0) { 169 fprintf(stderr, "%d failures detected!\n", failures); 170 return 1; 171 } 172 173 printf("All OK!\n"); 174 return 0; 175 } 176