Home | History | Annotate | Download | only in tests
      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