BacktraceThread.cpp revision e48460762273586744a79d146c2916bcfca7e9e4
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
35static 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//-------------------------------------------------------------------------
42ThreadEntry* ThreadEntry::list_ = NULL;
43pthread_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.
47ThreadEntry::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
56ThreadEntry* 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
78void 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.
90ThreadEntry::~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
104void 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
115void ThreadEntry::Wake() {
116  futex_++;
117  futex(&futex_, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
118}
119
120void 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//-------------------------------------------------------------------------
129static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER;
130
131static 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
150BacktraceThread::BacktraceThread(BacktraceImpl* impl, pid_t tid, BacktraceMap* map)
151    : BacktraceCurrent(impl, map) {
152  tid_ = tid;
153}
154
155BacktraceThread::~BacktraceThread() {
156}
157
158bool 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