Home | History | Annotate | Download | only in libbacktrace
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <errno.h>
     18 #include <inttypes.h>
     19 #include <limits.h>
     20 #include <pthread.h>
     21 #include <signal.h>
     22 #include <stdlib.h>
     23 #include <string.h>
     24 #include <sys/syscall.h>
     25 #include <sys/time.h>
     26 #include <sys/types.h>
     27 #include <ucontext.h>
     28 #include <unistd.h>
     29 
     30 #include <cutils/atomic.h>
     31 
     32 #include "BacktraceLog.h"
     33 #include "BacktraceThread.h"
     34 #include "thread_utils.h"
     35 
     36 //-------------------------------------------------------------------------
     37 // ThreadEntry implementation.
     38 //-------------------------------------------------------------------------
     39 ThreadEntry* ThreadEntry::list_ = NULL;
     40 pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER;
     41 
     42 // Assumes that ThreadEntry::list_mutex_ has already been locked before
     43 // creating a ThreadEntry object.
     44 ThreadEntry::ThreadEntry(pid_t pid, pid_t tid)
     45     : pid_(pid), tid_(tid), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER),
     46       wait_mutex_(PTHREAD_MUTEX_INITIALIZER), wait_value_(0),
     47       next_(ThreadEntry::list_), prev_(NULL) {
     48   pthread_condattr_t attr;
     49   pthread_condattr_init(&attr);
     50   pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
     51   pthread_cond_init(&wait_cond_, &attr);
     52 
     53   // Add ourselves to the list.
     54   if (ThreadEntry::list_) {
     55     ThreadEntry::list_->prev_ = this;
     56   }
     57   ThreadEntry::list_ = this;
     58 }
     59 
     60 ThreadEntry* ThreadEntry::Get(pid_t pid, pid_t tid, bool create) {
     61   pthread_mutex_lock(&ThreadEntry::list_mutex_);
     62   ThreadEntry* entry = list_;
     63   while (entry != NULL) {
     64     if (entry->Match(pid, tid)) {
     65       break;
     66     }
     67     entry = entry->next_;
     68   }
     69 
     70   if (!entry) {
     71     if (create) {
     72       entry = new ThreadEntry(pid, tid);
     73     }
     74   } else {
     75     entry->ref_count_++;
     76   }
     77   pthread_mutex_unlock(&ThreadEntry::list_mutex_);
     78 
     79   return entry;
     80 }
     81 
     82 void ThreadEntry::Remove(ThreadEntry* entry) {
     83   pthread_mutex_unlock(&entry->mutex_);
     84 
     85   pthread_mutex_lock(&ThreadEntry::list_mutex_);
     86   if (--entry->ref_count_ == 0) {
     87     delete entry;
     88   }
     89   pthread_mutex_unlock(&ThreadEntry::list_mutex_);
     90 }
     91 
     92 // Assumes that ThreadEntry::list_mutex_ has already been locked before
     93 // deleting a ThreadEntry object.
     94 ThreadEntry::~ThreadEntry() {
     95   if (list_ == this) {
     96     list_ = next_;
     97   } else {
     98     if (next_) {
     99       next_->prev_ = prev_;
    100     }
    101     prev_->next_ = next_;
    102   }
    103 
    104   next_ = NULL;
    105   prev_ = NULL;
    106 
    107   pthread_cond_destroy(&wait_cond_);
    108 }
    109 
    110 void ThreadEntry::Wait(int value) {
    111   timespec ts;
    112   if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) {
    113     BACK_LOGW("clock_gettime failed: %s", strerror(errno));
    114     abort();
    115   }
    116   ts.tv_sec += 10;
    117 
    118   pthread_mutex_lock(&wait_mutex_);
    119   while (wait_value_ != value) {
    120     int ret = pthread_cond_timedwait(&wait_cond_, &wait_mutex_, &ts);
    121     if (ret != 0) {
    122       BACK_LOGW("pthread_cond_timedwait failed: %s", strerror(ret));
    123       break;
    124     }
    125   }
    126   pthread_mutex_unlock(&wait_mutex_);
    127 }
    128 
    129 void ThreadEntry::Wake() {
    130   pthread_mutex_lock(&wait_mutex_);
    131   wait_value_++;
    132   pthread_mutex_unlock(&wait_mutex_);
    133 
    134   pthread_cond_signal(&wait_cond_);
    135 }
    136 
    137 void ThreadEntry::CopyUcontextFromSigcontext(void* sigcontext) {
    138   ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(sigcontext);
    139   // The only thing the unwinder cares about is the mcontext data.
    140   memcpy(&ucontext_.uc_mcontext, &ucontext->uc_mcontext, sizeof(ucontext->uc_mcontext));
    141 }
    142 
    143 //-------------------------------------------------------------------------
    144 // BacktraceThread functions.
    145 //-------------------------------------------------------------------------
    146 static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER;
    147 
    148 static void SignalHandler(int, siginfo_t*, void* sigcontext) {
    149   ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
    150   if (!entry) {
    151     BACK_LOGW("Unable to find pid %d tid %d information", getpid(), gettid());
    152     return;
    153   }
    154 
    155   entry->CopyUcontextFromSigcontext(sigcontext);
    156 
    157   // Indicate the ucontext is now valid.
    158   entry->Wake();
    159 
    160   // Pause the thread until the unwind is complete. This avoids having
    161   // the thread run ahead causing problems.
    162   entry->Wait(2);
    163 
    164   ThreadEntry::Remove(entry);
    165 }
    166 
    167 BacktraceThread::BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map)
    168     : BacktraceCurrent(impl, map) {
    169   tid_ = tid;
    170 }
    171 
    172 BacktraceThread::~BacktraceThread() {
    173 }
    174 
    175 bool BacktraceThread::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
    176   if (ucontext) {
    177     // Unwind using an already existing ucontext.
    178     return impl_->Unwind(num_ignore_frames, ucontext);
    179   }
    180 
    181   // Prevent multiple threads trying to set the trigger action on different
    182   // threads at the same time.
    183   if (pthread_mutex_lock(&g_sigaction_mutex) < 0) {
    184     BACK_LOGW("sigaction failed: %s", strerror(errno));
    185     return false;
    186   }
    187 
    188   ThreadEntry* entry = ThreadEntry::Get(Pid(), Tid());
    189   entry->Lock();
    190 
    191   struct sigaction act, oldact;
    192   memset(&act, 0, sizeof(act));
    193   act.sa_sigaction = SignalHandler;
    194   act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
    195   sigemptyset(&act.sa_mask);
    196   if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) {
    197     BACK_LOGW("sigaction failed %s", strerror(errno));
    198     entry->Unlock();
    199     ThreadEntry::Remove(entry);
    200     pthread_mutex_unlock(&g_sigaction_mutex);
    201     return false;
    202   }
    203 
    204   if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
    205     BACK_LOGW("tgkill %d failed: %s", Tid(), strerror(errno));
    206     sigaction(THREAD_SIGNAL, &oldact, NULL);
    207     entry->Unlock();
    208     ThreadEntry::Remove(entry);
    209     pthread_mutex_unlock(&g_sigaction_mutex);
    210     return false;
    211   }
    212 
    213   // Wait for the thread to get the ucontext.
    214   entry->Wait(1);
    215 
    216   // After the thread has received the signal, allow other unwinders to
    217   // continue.
    218   sigaction(THREAD_SIGNAL, &oldact, NULL);
    219   pthread_mutex_unlock(&g_sigaction_mutex);
    220 
    221   bool unwind_done = impl_->Unwind(num_ignore_frames, entry->GetUcontext());
    222 
    223   // Tell the signal handler to exit and release the entry.
    224   entry->Wake();
    225 
    226   return unwind_done;
    227 }
    228