signal_catcher.cc revision c7dd295a4e0cc1d15c0c96088e55a85389bade74
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 "base/unix_file/fd_file.h"
31#include "class_linker.h"
32#include "gc/heap.h"
33#include "instruction_set.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_));
114  if (!file->WriteFully(s.data(), s.size())) {
115    PLOG(ERROR) << "Failed to write stack traces to '" << stack_trace_file_ << "'";
116  } else {
117    LOG(INFO) << "Wrote stack traces to '" << stack_trace_file_ << "'";
118  }
119}
120
121void SignalCatcher::HandleSigQuit() {
122  Runtime* runtime = Runtime::Current();
123  std::ostringstream os;
124  os << "\n"
125      << "----- pid " << getpid() << " at " << GetIsoDate() << " -----\n";
126
127  DumpCmdLine(os);
128
129  // Note: The string "ABI:" is chosen to match the format used by debuggerd.
130  os << "ABI: " << GetInstructionSetString(runtime->GetInstructionSet()) << "\n";
131
132  os << "Build type: " << (kIsDebugBuild ? "debug" : "optimized") << "\n";
133
134  runtime->DumpForSigQuit(os);
135
136  if (false) {
137    std::string maps;
138    if (ReadFileToString("/proc/self/maps", &maps)) {
139      os << "/proc/self/maps:\n" << maps;
140    }
141  }
142  os << "----- end " << getpid() << " -----\n";
143  Output(os.str());
144}
145
146void SignalCatcher::HandleSigUsr1() {
147  LOG(INFO) << "SIGUSR1 forcing GC (no HPROF)";
148  Runtime::Current()->GetHeap()->CollectGarbage(false);
149}
150
151int SignalCatcher::WaitForSignal(Thread* self, SignalSet& signals) {
152  ScopedThreadStateChange tsc(self, kWaitingInMainSignalCatcherLoop);
153
154  // Signals for sigwait() must be blocked but not ignored.  We
155  // block signals like SIGQUIT for all threads, so the condition
156  // is met.  When the signal hits, we wake up, without any signal
157  // handlers being invoked.
158  int signal_number = signals.Wait();
159  if (!ShouldHalt()) {
160    // Let the user know we got the signal, just in case the system's too screwed for us to
161    // actually do what they want us to do...
162    LOG(INFO) << *self << ": reacting to signal " << signal_number;
163
164    // If anyone's holding locks (which might prevent us from getting back into state Runnable), say so...
165    Runtime::Current()->DumpLockHolders(LOG(INFO));
166  }
167
168  return signal_number;
169}
170
171void* SignalCatcher::Run(void* arg) {
172  SignalCatcher* signal_catcher = reinterpret_cast<SignalCatcher*>(arg);
173  CHECK(signal_catcher != NULL);
174
175  Runtime* runtime = Runtime::Current();
176  CHECK(runtime->AttachCurrentThread("Signal Catcher", true, runtime->GetSystemThreadGroup(),
177                                     !runtime->IsCompiler()));
178
179  Thread* self = Thread::Current();
180  DCHECK_NE(self->GetState(), kRunnable);
181  {
182    MutexLock mu(self, signal_catcher->lock_);
183    signal_catcher->thread_ = self;
184    signal_catcher->cond_.Broadcast(self);
185  }
186
187  // Set up mask with signals we want to handle.
188  SignalSet signals;
189  signals.Add(SIGQUIT);
190  signals.Add(SIGUSR1);
191
192  while (true) {
193    int signal_number = signal_catcher->WaitForSignal(self, signals);
194    if (signal_catcher->ShouldHalt()) {
195      runtime->DetachCurrentThread();
196      return NULL;
197    }
198
199    switch (signal_number) {
200    case SIGQUIT:
201      signal_catcher->HandleSigQuit();
202      break;
203    case SIGUSR1:
204      signal_catcher->HandleSigUsr1();
205      break;
206    default:
207      LOG(ERROR) << "Unexpected signal %d" << signal_number;
208      break;
209    }
210  }
211}
212
213}  // namespace art
214