signal_catcher.cc revision c02213080bdda3d99b62dcd66af4941e703a8547
1/*
2 * Copyright (C) 2008 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 "signal_catcher.h"
18
19#include <fcntl.h>
20#include <pthread.h>
21#include <signal.h>
22#include <stdlib.h>
23#include <sys/stat.h>
24#include <sys/time.h>
25#include <sys/types.h>
26#include <unistd.h>
27
28#include "base/unix_file/fd_file.h"
29#include "class_linker.h"
30#include "gc/heap.h"
31#include "os.h"
32#include "runtime.h"
33#include "scoped_thread_state_change.h"
34#include "signal_set.h"
35#include "thread.h"
36#include "thread_list.h"
37#include "utils.h"
38
39namespace art {
40
41static void DumpCmdLine(std::ostream& os) {
42#if defined(__linux__)
43  // Show the original command line, and the current command line too if it's changed.
44  // On Android, /proc/self/cmdline will have been rewritten to something like "system_server".
45  std::string current_cmd_line;
46  if (ReadFileToString("/proc/self/cmdline", &current_cmd_line)) {
47    current_cmd_line.resize(current_cmd_line.size() - 1);  // Lose the trailing '\0'.
48    std::replace(current_cmd_line.begin(), current_cmd_line.end(), '\0', ' ');
49
50    os << "Cmdline: " << current_cmd_line;
51    const char* stashed_cmd_line = GetCmdLine();
52    if (stashed_cmd_line != NULL && current_cmd_line != stashed_cmd_line) {
53      os << "Original command line: " << stashed_cmd_line;
54    }
55  }
56  os << "\n";
57#else
58  os << "Cmdline: " << GetCmdLine() << "\n";
59#endif
60}
61
62SignalCatcher::SignalCatcher(const std::string& stack_trace_file)
63    : stack_trace_file_(stack_trace_file),
64      lock_("SignalCatcher lock"),
65      cond_("SignalCatcher::cond_", lock_),
66      thread_(NULL) {
67  SetHaltFlag(false);
68
69  // Create a raw pthread; its start routine will attach to the runtime.
70  CHECK_PTHREAD_CALL(pthread_create, (&pthread_, NULL, &Run, this), "signal catcher thread");
71
72  Thread* self = Thread::Current();
73  MutexLock mu(self, lock_);
74  while (thread_ == NULL) {
75    cond_.Wait(self);
76  }
77}
78
79SignalCatcher::~SignalCatcher() {
80  // Since we know the thread is just sitting around waiting for signals
81  // to arrive, send it one.
82  SetHaltFlag(true);
83  CHECK_PTHREAD_CALL(pthread_kill, (pthread_, SIGQUIT), "signal catcher shutdown");
84  CHECK_PTHREAD_CALL(pthread_join, (pthread_, NULL), "signal catcher shutdown");
85}
86
87void SignalCatcher::SetHaltFlag(bool new_value) {
88  MutexLock mu(Thread::Current(), lock_);
89  halt_ = new_value;
90}
91
92bool SignalCatcher::ShouldHalt() {
93  MutexLock mu(Thread::Current(), lock_);
94  return halt_;
95}
96
97void SignalCatcher::Output(const std::string& s) {
98  if (stack_trace_file_.empty()) {
99    LOG(INFO) << s;
100    return;
101  }
102
103  ScopedThreadStateChange tsc(Thread::Current(), kWaitingForSignalCatcherOutput);
104  int fd = open(stack_trace_file_.c_str(), O_APPEND | O_CREAT | O_WRONLY, 0666);
105  if (fd == -1) {
106    PLOG(ERROR) << "Unable to open stack trace file '" << stack_trace_file_ << "'";
107    return;
108  }
109  std::unique_ptr<File> file(new File(fd, stack_trace_file_));
110  if (!file->WriteFully(s.data(), s.size())) {
111    PLOG(ERROR) << "Failed to write stack traces to '" << stack_trace_file_ << "'";
112  } else {
113    LOG(INFO) << "Wrote stack traces to '" << stack_trace_file_ << "'";
114  }
115}
116
117void SignalCatcher::HandleSigQuit() {
118  Runtime* runtime = Runtime::Current();
119  ThreadList* thread_list = runtime->GetThreadList();
120
121  // Grab exclusively the mutator lock, set state to Runnable without checking for a pending
122  // suspend request as we're going to suspend soon anyway. We set the state to Runnable to avoid
123  // giving away the mutator lock.
124  thread_list->SuspendAll();
125  Thread* self = Thread::Current();
126  Locks::mutator_lock_->AssertExclusiveHeld(self);
127  const char* old_cause = self->StartAssertNoThreadSuspension("Handling SIGQUIT");
128  ThreadState old_state = self->SetStateUnsafe(kRunnable);
129
130  std::ostringstream os;
131  os << "\n"
132      << "----- pid " << getpid() << " at " << GetIsoDate() << " -----\n";
133
134  DumpCmdLine(os);
135
136  os << "Build type: " << (kIsDebugBuild ? "debug" : "optimized") << "\n";
137
138  runtime->DumpForSigQuit(os);
139
140  if (false) {
141    std::string maps;
142    if (ReadFileToString("/proc/self/maps", &maps)) {
143      os << "/proc/self/maps:\n" << maps;
144    }
145  }
146  os << "----- end " << getpid() << " -----\n";
147  CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
148  self->EndAssertNoThreadSuspension(old_cause);
149  thread_list->ResumeAll();
150  // Run the checkpoints after resuming the threads to prevent deadlocks if the checkpoint function
151  // acquires the mutator lock.
152  if (self->ReadFlag(kCheckpointRequest)) {
153    self->RunCheckpointFunction();
154  }
155  Output(os.str());
156}
157
158void SignalCatcher::HandleSigUsr1() {
159  LOG(INFO) << "SIGUSR1 forcing GC (no HPROF)";
160  Runtime::Current()->GetHeap()->CollectGarbage(false);
161}
162
163int SignalCatcher::WaitForSignal(Thread* self, SignalSet& signals) {
164  ScopedThreadStateChange tsc(self, kWaitingInMainSignalCatcherLoop);
165
166  // Signals for sigwait() must be blocked but not ignored.  We
167  // block signals like SIGQUIT for all threads, so the condition
168  // is met.  When the signal hits, we wake up, without any signal
169  // handlers being invoked.
170  int signal_number = signals.Wait();
171  if (!ShouldHalt()) {
172    // Let the user know we got the signal, just in case the system's too screwed for us to
173    // actually do what they want us to do...
174    LOG(INFO) << *self << ": reacting to signal " << signal_number;
175
176    // If anyone's holding locks (which might prevent us from getting back into state Runnable), say so...
177    Runtime::Current()->DumpLockHolders(LOG(INFO));
178  }
179
180  return signal_number;
181}
182
183void* SignalCatcher::Run(void* arg) {
184  SignalCatcher* signal_catcher = reinterpret_cast<SignalCatcher*>(arg);
185  CHECK(signal_catcher != NULL);
186
187  Runtime* runtime = Runtime::Current();
188  CHECK(runtime->AttachCurrentThread("Signal Catcher", true, runtime->GetSystemThreadGroup(),
189                                     !runtime->IsCompiler()));
190
191  Thread* self = Thread::Current();
192  DCHECK_NE(self->GetState(), kRunnable);
193  {
194    MutexLock mu(self, signal_catcher->lock_);
195    signal_catcher->thread_ = self;
196    signal_catcher->cond_.Broadcast(self);
197  }
198
199  // Set up mask with signals we want to handle.
200  SignalSet signals;
201  signals.Add(SIGQUIT);
202  signals.Add(SIGUSR1);
203
204  while (true) {
205    int signal_number = signal_catcher->WaitForSignal(self, signals);
206    if (signal_catcher->ShouldHalt()) {
207      runtime->DetachCurrentThread();
208      return NULL;
209    }
210
211    switch (signal_number) {
212    case SIGQUIT:
213      signal_catcher->HandleSigQuit();
214      break;
215    case SIGUSR1:
216      signal_catcher->HandleSigUsr1();
217      break;
218    default:
219      LOG(ERROR) << "Unexpected signal %d" << signal_number;
220      break;
221    }
222  }
223}
224
225}  // namespace art
226