C++程序  |  176行  |  6.15 KB

// Copyright (C) 2013 The Android Open Source Project
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.

// A test used to check that __cxa_get_globals() does not use malloc.
// This will do the following:
//
//  - Lazily load libtest_malloc_lockup.so, which includes a copy of
//    GAbi++ linked with malloc() / free() functions that exit() with
//    an error if called.
//
//  - Create a large number of concurrent threads, and have each one
//    call the library's 'get_globals' function, which returns the
//    result of __cxa_get_globals() linked against the special mallocs,
//    then store the value in a global array.
//
//  - Tell all the threads to stop, wait for them to complete.
//
//  - Look at the values stored in the global arrays. They should not be NULL
//    (to indicate succesful allocation), and all different (each one should
//    correspond to a thread-specific instance of __cxa_eh_globals).
//
//  - Unload the library.
//

#include <dlfcn.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef void* (*get_globals_fn)();

static get_globals_fn g_get_globals;

// Number of threads to create. Must be > 4096 to really check slab allocation.
static const size_t kMaxThreads = 5000;

static pthread_t g_threads[kMaxThreads];
static void* g_thread_objects[kMaxThreads];

static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t g_cond_exit = PTHREAD_COND_INITIALIZER;
static pthread_cond_t g_cond_counter = PTHREAD_COND_INITIALIZER;
static unsigned g_thread_count = 0;
static bool g_can_exit = false;

// Thread routine, just call 'get_globals' and store the result in our global
// array, then wait for an event from the main thread. This guarantees that
// no thread exits before another one starts, and thus that allocation slots
// are not reused.
static void* my_thread(void* param) {
  // Get thread-specific object pointer, store it in global array.
  int id = (int)(intptr_t)param;
  g_thread_objects[id] = (*g_get_globals)();

  // Increment global thread counter and tell the main thread about this.
  pthread_mutex_lock(&g_lock);
  g_thread_count += 1;
  pthread_cond_signal(&g_cond_counter);

  // The thread object will be automatically released/recycled when the thread
  // exits. Wait here until signaled by the main thread to avoid this.
  while (!g_can_exit)
    pthread_cond_wait(&g_cond_exit, &g_lock);
  pthread_mutex_unlock(&g_lock);

  return NULL;
}

int main(void) {
  // Load the library.
  void* lib = dlopen("libtest_malloc_lockup.so", RTLD_NOW);
  if (!lib) {
    fprintf(stderr, "ERROR: Can't find library: %s\n", strerror(errno));
    return 1;
  }

  // Extract 'get_globals' function address.
  g_get_globals = reinterpret_cast<get_globals_fn>(dlsym(lib, "get_globals"));
  if (!g_get_globals) {
    fprintf(stderr, "ERROR: Could not find 'get_globals' function: %s\n",
            dlerror());
    dlclose(lib);
    return 1;
  }

  // Use a smaller stack per thread to be able to create lots of them.
  pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setstacksize(&attr, 16384);

  // Start as many threads as needed.
  printf("Creating %d threads\n", kMaxThreads);
  for (size_t n = 0; n < kMaxThreads; ++n) {
    int ret = pthread_create(&g_threads[n], &attr, my_thread, (void*)n);
    if (ret != 0) {
      fprintf(stderr, "ERROR: Thread #%d creation error: %s\n",
              n + 1, strerror(errno));
      return 2;
    }
  }

  // Wait until they all ran, then tell them to exit.
  printf("Waiting for all threads to run\n");
  pthread_mutex_lock(&g_lock);
  while (g_thread_count < kMaxThreads)
    pthread_cond_wait(&g_cond_counter, &g_lock);

  printf("Waking up threads\n");
  g_can_exit = true;
  pthread_cond_broadcast(&g_cond_exit);
  pthread_mutex_unlock(&g_lock);

  // Wait for them to complete.
  printf("Waiting for all threads to complete\n");
  for (size_t n = 0; n < kMaxThreads; ++n) {
    void* dummy;
    pthread_join(g_threads[n], &dummy);
  }

  // Verify that the thread objects are all non-NULL and different.
  printf("Checking results\n");
  size_t failures = 0;
  const size_t kMaxFailures = 16;
  for (size_t n = 0; n < kMaxThreads; ++n) {
    void* obj = g_thread_objects[n];
    if (obj == NULL) {
      if (++failures < kMaxFailures)
        printf("Thread %d got a NULL object!\n", n + 1);
    } else {
      for (size_t m = n + 1; m < kMaxThreads; ++m) {
        if (g_thread_objects[m] == obj) {
          if (++failures < kMaxFailures)
            printf("Thread %d has same object as thread %d (%p)\n",
                   n + 1, m + 1, obj);
        }
      }
    }
  }

  // We're done.
  dlclose(lib);
  if (failures > 0) {
    fprintf(stderr, "%d failures detected!\n", failures);
    return 1;
  }

  printf("All OK!\n");
  return 0;
}