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