Home | History | Annotate | Download | only in tests
      1 // Copyright (c) 2011, Google Inc.
      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 are
      6 // met:
      7 //
      8 //     * Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 //     * Redistributions in binary form must reproduce the above
     11 // copyright notice, this list of conditions and the following disclaimer
     12 // in the documentation and/or other materials provided with the
     13 // distribution.
     14 //     * Neither the name of Google Inc. nor the names of its
     15 // contributors may be used to endorse or promote products derived from
     16 // this software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 // crash_generator.cc: Implement google_breakpad::CrashGenerator.
     31 // See crash_generator.h for details.
     32 
     33 #include "common/linux/tests/crash_generator.h"
     34 
     35 #include <pthread.h>
     36 #include <signal.h>
     37 #include <stdio.h>
     38 #include <sys/mman.h>
     39 #include <sys/resource.h>
     40 #include <sys/syscall.h>
     41 #include <sys/wait.h>
     42 #include <unistd.h>
     43 
     44 #include <string>
     45 
     46 #if defined(__ANDROID__)
     47 #include "common/android/testing/pthread_fixes.h"
     48 #endif
     49 #include "common/linux/eintr_wrapper.h"
     50 #include "common/tests/auto_tempdir.h"
     51 #include "common/tests/file_utils.h"
     52 #include "common/using_std_string.h"
     53 
     54 namespace {
     55 
     56 struct ThreadData {
     57   pthread_t thread;
     58   pthread_barrier_t* barrier;
     59   pid_t* thread_id_ptr;
     60 };
     61 
     62 const char* const kProcFilesToCopy[] = {
     63   "auxv", "cmdline", "environ", "maps", "status"
     64 };
     65 const size_t kNumProcFilesToCopy =
     66     sizeof(kProcFilesToCopy) / sizeof(kProcFilesToCopy[0]);
     67 
     68 int gettid() {
     69   // Glibc does not provide a wrapper for this.
     70   return syscall(__NR_gettid);
     71 }
     72 
     73 int tkill(pid_t tid, int sig) {
     74   // Glibc does not provide a wrapper for this.
     75   return syscall(__NR_tkill, tid, sig);
     76 }
     77 
     78 // Core file size limit set to 1 MB, which is big enough for test purposes.
     79 const rlim_t kCoreSizeLimit = 1024 * 1024;
     80 
     81 void *thread_function(void *data) {
     82   ThreadData* thread_data = reinterpret_cast<ThreadData*>(data);
     83   volatile pid_t thread_id = gettid();
     84   *(thread_data->thread_id_ptr) = thread_id;
     85   int result = pthread_barrier_wait(thread_data->barrier);
     86   if (result != 0 && result != PTHREAD_BARRIER_SERIAL_THREAD) {
     87     perror("Failed to wait for sync barrier");
     88     exit(1);
     89   }
     90   while (true) {
     91     pthread_yield();
     92   }
     93 }
     94 
     95 }  // namespace
     96 
     97 namespace google_breakpad {
     98 
     99 CrashGenerator::CrashGenerator()
    100     : shared_memory_(NULL),
    101       shared_memory_size_(0) {
    102 }
    103 
    104 CrashGenerator::~CrashGenerator() {
    105   UnmapSharedMemory();
    106 }
    107 
    108 bool CrashGenerator::HasDefaultCorePattern() const {
    109   char buffer[8];
    110   ssize_t buffer_size = sizeof(buffer);
    111   return ReadFile("/proc/sys/kernel/core_pattern", buffer, &buffer_size) &&
    112          buffer_size == 5 && memcmp(buffer, "core", 4) == 0;
    113 }
    114 
    115 string CrashGenerator::GetCoreFilePath() const {
    116   return temp_dir_.path() + "/core";
    117 }
    118 
    119 string CrashGenerator::GetDirectoryOfProcFilesCopy() const {
    120   return temp_dir_.path() + "/proc";
    121 }
    122 
    123 pid_t CrashGenerator::GetThreadId(unsigned index) const {
    124   return reinterpret_cast<pid_t*>(shared_memory_)[index];
    125 }
    126 
    127 pid_t* CrashGenerator::GetThreadIdPointer(unsigned index) {
    128   return reinterpret_cast<pid_t*>(shared_memory_) + index;
    129 }
    130 
    131 bool CrashGenerator::MapSharedMemory(size_t memory_size) {
    132   if (!UnmapSharedMemory())
    133     return false;
    134 
    135   void* mapped_memory = mmap(0, memory_size, PROT_READ | PROT_WRITE,
    136                              MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    137   if (mapped_memory == MAP_FAILED) {
    138     perror("CrashGenerator: Failed to map shared memory");
    139     return false;
    140   }
    141 
    142   memset(mapped_memory, 0, memory_size);
    143   shared_memory_ = mapped_memory;
    144   shared_memory_size_ = memory_size;
    145   return true;
    146 }
    147 
    148 bool CrashGenerator::UnmapSharedMemory() {
    149   if (!shared_memory_)
    150     return true;
    151 
    152   if (munmap(shared_memory_, shared_memory_size_) == 0) {
    153     shared_memory_ = NULL;
    154     shared_memory_size_ = 0;
    155     return true;
    156   }
    157 
    158   perror("CrashGenerator: Failed to unmap shared memory");
    159   return false;
    160 }
    161 
    162 bool CrashGenerator::SetCoreFileSizeLimit(rlim_t limit) const {
    163   struct rlimit limits = { limit, limit };
    164   if (setrlimit(RLIMIT_CORE, &limits) == -1) {
    165     perror("CrashGenerator: Failed to set core file size limit");
    166     return false;
    167   }
    168   return true;
    169 }
    170 
    171 bool CrashGenerator::CreateChildCrash(
    172     unsigned num_threads, unsigned crash_thread, int crash_signal,
    173     pid_t* child_pid) {
    174   if (num_threads == 0 || crash_thread >= num_threads) {
    175     fprintf(stderr, "CrashGenerator: Invalid thread counts; num_threads=%u"
    176                     " crash_thread=%u\n", num_threads, crash_thread);
    177     return false;
    178   }
    179 
    180   if (!MapSharedMemory(num_threads * sizeof(pid_t))) {
    181     perror("CrashGenerator: Unable to map shared memory");
    182     return false;
    183   }
    184 
    185   pid_t pid = fork();
    186   if (pid == 0) {
    187     if (chdir(temp_dir_.path().c_str()) == -1) {
    188       perror("CrashGenerator: Failed to change directory");
    189       exit(1);
    190     }
    191     if (SetCoreFileSizeLimit(kCoreSizeLimit)) {
    192       CreateThreadsInChildProcess(num_threads);
    193       string proc_dir = GetDirectoryOfProcFilesCopy();
    194       if (mkdir(proc_dir.c_str(), 0755) == -1) {
    195         perror("CrashGenerator: Failed to create proc directory");
    196         exit(1);
    197       }
    198       if (!CopyProcFiles(getpid(), proc_dir.c_str())) {
    199         fprintf(stderr, "CrashGenerator: Failed to copy proc files\n");
    200         exit(1);
    201       }
    202       // On Android the signal sometimes doesn't seem to get sent even though
    203       // tkill returns '0'.  Retry a couple of times if the signal doesn't get
    204       // through on the first go:
    205       // https://code.google.com/p/google-breakpad/issues/detail?id=579
    206 #if defined(__ANDROID__)
    207       const int kRetries = 60;
    208       const unsigned int kSleepTimeInSeconds = 1;
    209 #else
    210       const int kRetries = 1;
    211       const unsigned int kSleepTimeInSeconds = 600;
    212 #endif
    213       for (int i = 0; i < kRetries; i++) {
    214         if (tkill(*GetThreadIdPointer(crash_thread), crash_signal) == -1) {
    215           perror("CrashGenerator: Failed to kill thread by signal");
    216         } else {
    217           // At this point, we've queued the signal for delivery, but there's no
    218           // guarantee when it'll be delivered.  We don't want the main thread to
    219           // race and exit before the thread we signaled is processed.  So sleep
    220           // long enough that we won't flake even under fairly high load.
    221           // TODO: See if we can't be a bit more deterministic.  There doesn't
    222           // seem to be an API to check on signal delivery status, so we can't
    223           // really poll and wait for the kernel to declare the signal has been
    224           // delivered.  If it has, and things worked, we'd be killed, so the
    225           // sleep length doesn't really matter.
    226           sleep(kSleepTimeInSeconds);
    227         }
    228       }
    229     } else {
    230       perror("CrashGenerator: Failed to set core limit");
    231     }
    232     exit(1);
    233   } else if (pid == -1) {
    234     perror("CrashGenerator: Failed to create child process");
    235     return false;
    236   }
    237 
    238   int status;
    239   if (HANDLE_EINTR(waitpid(pid, &status, 0)) == -1) {
    240     perror("CrashGenerator: Failed to wait for child process");
    241     return false;
    242   }
    243   if (!WIFSIGNALED(status) || WTERMSIG(status) != crash_signal) {
    244     fprintf(stderr, "CrashGenerator: Child process not killed by the expected signal\n"
    245                     "  exit status=0x%x pid=%u signaled=%s sig=%d expected=%d\n",
    246                     status, pid, WIFSIGNALED(status) ? "true" : "false",
    247                     WTERMSIG(status), crash_signal);
    248     return false;
    249   }
    250 
    251   if (child_pid)
    252     *child_pid = pid;
    253   return true;
    254 }
    255 
    256 bool CrashGenerator::CopyProcFiles(pid_t pid, const char* path) const {
    257   char from_path[PATH_MAX], to_path[PATH_MAX];
    258   for (size_t i = 0; i < kNumProcFilesToCopy; ++i) {
    259     int num_chars = snprintf(from_path, PATH_MAX, "/proc/%d/%s",
    260                              pid, kProcFilesToCopy[i]);
    261     if (num_chars < 0 || num_chars >= PATH_MAX)
    262       return false;
    263 
    264     num_chars = snprintf(to_path, PATH_MAX, "%s/%s",
    265                          path, kProcFilesToCopy[i]);
    266     if (num_chars < 0 || num_chars >= PATH_MAX)
    267       return false;
    268 
    269     if (!CopyFile(from_path, to_path))
    270       return false;
    271   }
    272   return true;
    273 }
    274 
    275 void CrashGenerator::CreateThreadsInChildProcess(unsigned num_threads) {
    276   *GetThreadIdPointer(0) = getpid();
    277 
    278   if (num_threads <= 1)
    279     return;
    280 
    281   // This method does not clean up any pthread resource, as the process
    282   // is expected to be killed anyway.
    283   ThreadData* thread_data = new ThreadData[num_threads];
    284 
    285   // Create detached threads so that we do not worry about pthread_join()
    286   // later being called or not.
    287   pthread_attr_t thread_attributes;
    288   if (pthread_attr_init(&thread_attributes) != 0 ||
    289       pthread_attr_setdetachstate(&thread_attributes,
    290                                   PTHREAD_CREATE_DETACHED) != 0) {
    291     fprintf(stderr, "CrashGenerator: Failed to initialize thread attribute\n");
    292     exit(1);
    293   }
    294 
    295   pthread_barrier_t thread_barrier;
    296   if (pthread_barrier_init(&thread_barrier, NULL, num_threads) != 0) {
    297     fprintf(stderr, "CrashGenerator: Failed to initialize thread barrier\n");
    298     exit(1);
    299   }
    300 
    301   for (unsigned i = 1; i < num_threads; ++i) {
    302     thread_data[i].barrier = &thread_barrier;
    303     thread_data[i].thread_id_ptr = GetThreadIdPointer(i);
    304     if (pthread_create(&thread_data[i].thread, &thread_attributes,
    305                        thread_function, &thread_data[i]) != 0) {
    306       fprintf(stderr, "CrashGenerator: Failed to create thread %d\n", i);
    307       exit(1);
    308     }
    309   }
    310 
    311   int result = pthread_barrier_wait(&thread_barrier);
    312   if (result != 0 && result != PTHREAD_BARRIER_SERIAL_THREAD) {
    313     fprintf(stderr, "CrashGenerator: Failed to wait for thread barrier\n");
    314     exit(1);
    315   }
    316 
    317   pthread_barrier_destroy(&thread_barrier);
    318   pthread_attr_destroy(&thread_attributes);
    319   delete[] thread_data;
    320 }
    321 
    322 }  // namespace google_breakpad
    323