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