• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <linux/futex.h>
21 #include <pthread.h>
22 #include <signal.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 
29 #include <cutils/atomic.h>
30 
31 #include "BacktraceLog.h"
32 #include "BacktraceThread.h"
33 #include "thread_utils.h"
34 
futex(volatile int * uaddr,int op,int val,const struct timespec * ts,volatile int * uaddr2,int val3)35 static inline int futex(volatile int* uaddr, int op, int val, const struct timespec* ts, volatile int* uaddr2, int val3) {
36   return syscall(__NR_futex, uaddr, op, val, ts, uaddr2, val3);
37 }
38 
39 //-------------------------------------------------------------------------
40 // ThreadEntry implementation.
41 //-------------------------------------------------------------------------
42 ThreadEntry* ThreadEntry::list_ = NULL;
43 pthread_mutex_t ThreadEntry::list_mutex_ = PTHREAD_MUTEX_INITIALIZER;
44 
45 // Assumes that ThreadEntry::list_mutex_ has already been locked before
46 // creating a ThreadEntry object.
ThreadEntry(pid_t pid,pid_t tid)47 ThreadEntry::ThreadEntry(pid_t pid, pid_t tid)
48     : pid_(pid), tid_(tid), futex_(0), ref_count_(1), mutex_(PTHREAD_MUTEX_INITIALIZER), next_(ThreadEntry::list_), prev_(NULL) {
49   // Add ourselves to the list.
50   if (ThreadEntry::list_) {
51     ThreadEntry::list_->prev_ = this;
52   }
53   ThreadEntry::list_ = this;
54 }
55 
Get(pid_t pid,pid_t tid,bool create)56 ThreadEntry* ThreadEntry::Get(pid_t pid, pid_t tid, bool create) {
57   pthread_mutex_lock(&ThreadEntry::list_mutex_);
58   ThreadEntry* entry = list_;
59   while (entry != NULL) {
60     if (entry->Match(pid, tid)) {
61       break;
62     }
63     entry = entry->next_;
64   }
65 
66   if (!entry) {
67     if (create) {
68       entry = new ThreadEntry(pid, tid);
69     }
70   } else {
71     entry->ref_count_++;
72   }
73   pthread_mutex_unlock(&ThreadEntry::list_mutex_);
74 
75   return entry;
76 }
77 
Remove(ThreadEntry * entry)78 void ThreadEntry::Remove(ThreadEntry* entry) {
79   pthread_mutex_unlock(&entry->mutex_);
80 
81   pthread_mutex_lock(&ThreadEntry::list_mutex_);
82   if (--entry->ref_count_ == 0) {
83     delete entry;
84   }
85   pthread_mutex_unlock(&ThreadEntry::list_mutex_);
86 }
87 
88 // Assumes that ThreadEntry::list_mutex_ has already been locked before
89 // deleting a ThreadEntry object.
~ThreadEntry()90 ThreadEntry::~ThreadEntry() {
91   if (list_ == this) {
92     list_ = next_;
93   } else {
94     if (next_) {
95       next_->prev_ = prev_;
96     }
97     prev_->next_ = next_;
98   }
99 
100   next_ = NULL;
101   prev_ = NULL;
102 }
103 
Wait(int value)104 void ThreadEntry::Wait(int value) {
105   timespec ts;
106   ts.tv_sec = 10;
107   ts.tv_nsec = 0;
108   errno = 0;
109   futex(&futex_, FUTEX_WAIT, value, &ts, NULL, 0);
110   if (errno != 0 && errno != EWOULDBLOCK) {
111     BACK_LOGW("futex wait failed, futex = %d: %s", futex_, strerror(errno));
112   }
113 }
114 
Wake()115 void ThreadEntry::Wake() {
116   futex_++;
117   futex(&futex_, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
118 }
119 
CopyUcontextFromSigcontext(void * sigcontext)120 void ThreadEntry::CopyUcontextFromSigcontext(void* sigcontext) {
121   ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(sigcontext);
122   // The only thing the unwinder cares about is the mcontext data.
123   memcpy(&ucontext_.uc_mcontext, &ucontext->uc_mcontext, sizeof(ucontext->uc_mcontext));
124 }
125 
126 //-------------------------------------------------------------------------
127 // BacktraceThread functions.
128 //-------------------------------------------------------------------------
129 static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER;
130 
SignalHandler(int,siginfo_t *,void * sigcontext)131 static void SignalHandler(int, siginfo_t*, void* sigcontext) {
132   ThreadEntry* entry = ThreadEntry::Get(getpid(), gettid(), false);
133   if (!entry) {
134     BACK_LOGW("Unable to find pid %d tid %d information", getpid(), gettid());
135     return;
136   }
137 
138   entry->CopyUcontextFromSigcontext(sigcontext);
139 
140   // Indicate the ucontext is now valid.
141   entry->Wake();
142 
143   // Pause the thread until the unwind is complete. This avoids having
144   // the thread run ahead causing problems.
145   entry->Wait(1);
146 
147   ThreadEntry::Remove(entry);
148 }
149 
BacktraceThread(BacktraceImpl * impl,pid_t tid,BacktraceMap * map)150 BacktraceThread::BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map)
151     : BacktraceCurrent(impl, map) {
152   tid_ = tid;
153 }
154 
~BacktraceThread()155 BacktraceThread::~BacktraceThread() {
156 }
157 
Unwind(size_t num_ignore_frames,ucontext_t * ucontext)158 bool BacktraceThread::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
159   if (ucontext) {
160     // Unwind using an already existing ucontext.
161     return impl_->Unwind(num_ignore_frames, ucontext);
162   }
163 
164   // Prevent multiple threads trying to set the trigger action on different
165   // threads at the same time.
166   if (pthread_mutex_lock(&g_sigaction_mutex) < 0) {
167     BACK_LOGW("sigaction failed: %s", strerror(errno));
168     return false;
169   }
170 
171   ThreadEntry* entry = ThreadEntry::Get(Pid(), Tid());
172   entry->Lock();
173 
174   struct sigaction act, oldact;
175   memset(&act, 0, sizeof(act));
176   act.sa_sigaction = SignalHandler;
177   act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
178   sigemptyset(&act.sa_mask);
179   if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) {
180     BACK_LOGW("sigaction failed %s", strerror(errno));
181     entry->Unlock();
182     ThreadEntry::Remove(entry);
183     pthread_mutex_unlock(&g_sigaction_mutex);
184     return false;
185   }
186 
187   if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
188     BACK_LOGW("tgkill %d failed: %s", Tid(), strerror(errno));
189     sigaction(THREAD_SIGNAL, &oldact, NULL);
190     entry->Unlock();
191     ThreadEntry::Remove(entry);
192     pthread_mutex_unlock(&g_sigaction_mutex);
193     return false;
194   }
195 
196   // Wait for the thread to get the ucontext.
197   entry->Wait(0);
198 
199   // After the thread has received the signal, allow other unwinders to
200   // continue.
201   sigaction(THREAD_SIGNAL, &oldact, NULL);
202   pthread_mutex_unlock(&g_sigaction_mutex);
203 
204   bool unwind_done = impl_->Unwind(num_ignore_frames, entry->GetUcontext());
205 
206   // Tell the signal handler to exit and release the entry.
207   entry->Wake();
208 
209   return unwind_done;
210 }
211