diff options
| -rw-r--r-- | runtime/Android.bp | 1 | ||||
| -rw-r--r-- | runtime/parsed_options.cc | 6 | ||||
| -rw-r--r-- | runtime/runtime.cc | 8 | ||||
| -rw-r--r-- | runtime/runtime.h | 8 | ||||
| -rw-r--r-- | runtime/runtime_options.def | 2 | ||||
| -rw-r--r-- | runtime/signal_catcher.cc | 93 | ||||
| -rw-r--r-- | runtime/signal_catcher.h | 26 | ||||
| -rw-r--r-- | test/987-stack-trace-dumping/expected.txt | 0 | ||||
| -rw-r--r-- | test/987-stack-trace-dumping/info.txt | 0 | ||||
| -rwxr-xr-x | test/987-stack-trace-dumping/run | 18 | ||||
| -rw-r--r-- | test/987-stack-trace-dumping/src/Main.java | 60 | ||||
| -rw-r--r-- | test/knownfailures.json | 3 |
12 files changed, 88 insertions, 137 deletions
diff --git a/runtime/Android.bp b/runtime/Android.bp index 8ee5498115..aa7dc65871 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -352,6 +352,7 @@ cc_defaults { "libdl", // For android::FileMap used by libziparchive. "libutils", + "libtombstoned_client" ], static_libs: [ // ZipArchive support, the order matters here to get all symbols. diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index fc91efa6ac..ef4957c0ba 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -238,9 +238,9 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize .Define("-Xlockprofthreshold:_") .WithType<unsigned int>() .IntoKey(M::LockProfThreshold) - .Define("-Xstacktracedir:_") - .WithType<std::string>() - .IntoKey(M::StackTraceDir) + .Define("-Xusetombstonedtraces") + .WithValue(true) + .IntoKey(M::UseTombstonedTraces) .Define("-Xstacktracefile:_") .WithType<std::string>() .IntoKey(M::StackTraceFile) diff --git a/runtime/runtime.cc b/runtime/runtime.cc index c46bd8d2b9..968f02a359 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -834,7 +834,7 @@ void Runtime::InitNonZygoteOrPostFork( void Runtime::StartSignalCatcher() { if (!is_zygote_) { - signal_catcher_ = new SignalCatcher(stack_trace_dir_, stack_trace_file_); + signal_catcher_ = new SignalCatcher(stack_trace_file_, use_tombstoned_traces_); } } @@ -1069,7 +1069,11 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { abort_ = runtime_options.GetOrDefault(Opt::HookAbort); default_stack_size_ = runtime_options.GetOrDefault(Opt::StackSize); - stack_trace_dir_ = runtime_options.ReleaseOrDefault(Opt::StackTraceDir); + use_tombstoned_traces_ = runtime_options.GetOrDefault(Opt::UseTombstonedTraces); +#if !defined(ART_TARGET_ANDROID) + CHECK(!use_tombstoned_traces_) + << "-Xusetombstonedtraces is only supported in an Android environment"; +#endif stack_trace_file_ = runtime_options.ReleaseOrDefault(Opt::StackTraceFile); compiler_executable_ = runtime_options.ReleaseOrDefault(Opt::Compiler); diff --git a/runtime/runtime.h b/runtime/runtime.h index 2e3b8d7bae..483d25593f 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -783,7 +783,13 @@ class Runtime { ClassLinker* class_linker_; SignalCatcher* signal_catcher_; - std::string stack_trace_dir_; + + // If true, the runtime will connect to tombstoned via a socket to + // request an open file descriptor to write its traces to. + bool use_tombstoned_traces_; + + // Location to which traces must be written on SIGQUIT. Only used if + // tombstoned_traces_ == false. std::string stack_trace_file_; std::unique_ptr<JavaVMExt> java_vm_; diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 77132a8bae..cfc681f23f 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -100,7 +100,7 @@ RUNTIME_OPTIONS_KEY (Unit, NoSigChain) RUNTIME_OPTIONS_KEY (Unit, ForceNativeBridge) RUNTIME_OPTIONS_KEY (LogVerbosity, Verbose) RUNTIME_OPTIONS_KEY (unsigned int, LockProfThreshold) -RUNTIME_OPTIONS_KEY (std::string, StackTraceDir) +RUNTIME_OPTIONS_KEY (bool, UseTombstonedTraces, false) RUNTIME_OPTIONS_KEY (std::string, StackTraceFile) RUNTIME_OPTIONS_KEY (Unit, MethodTrace) RUNTIME_OPTIONS_KEY (std::string, MethodTraceFile, "/data/misc/trace/method-trace-file.bin") diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc index faea7b3821..e3dfc74628 100644 --- a/runtime/signal_catcher.cc +++ b/runtime/signal_catcher.cc @@ -42,6 +42,10 @@ #include "thread_list.h" #include "utils.h" +#if defined(ART_TARGET_ANDROID) +#include "tombstoned/tombstoned.h" +#endif + namespace art { static void DumpCmdLine(std::ostream& os) { @@ -66,13 +70,19 @@ static void DumpCmdLine(std::ostream& os) { #endif } -SignalCatcher::SignalCatcher(const std::string& stack_trace_dir, - const std::string& stack_trace_file) - : stack_trace_dir_(stack_trace_dir), - stack_trace_file_(stack_trace_file), +SignalCatcher::SignalCatcher(const std::string& stack_trace_file, + bool use_tombstoned_stack_trace_fd) + : stack_trace_file_(stack_trace_file), + use_tombstoned_stack_trace_fd_(use_tombstoned_stack_trace_fd), lock_("SignalCatcher lock"), cond_("SignalCatcher::cond_", lock_), thread_(nullptr) { +#if !defined(ART_TARGET_ANDROID) + // We're not running on Android, so we can't communicate with tombstoned + // to ask for an open file. + CHECK(!use_tombstoned_stack_trace_fd_); +#endif + SetHaltFlag(false); // Create a raw pthread; its start routine will attach to the runtime. @@ -103,62 +113,65 @@ bool SignalCatcher::ShouldHalt() { return halt_; } -std::string SignalCatcher::GetStackTraceFileName() { - if (!stack_trace_dir_.empty()) { - // We'll try a maximum of ten times (arbitrarily selected) to create a file - // with a unique name, seeding the pseudo random generator each time. - // - // If this doesn't work, give up and log to stdout. Note that we could try - // indefinitely, but that would make problems in this code harder to detect - // since we'd be spinning in the signal catcher thread. - static constexpr uint32_t kMaxRetries = 10; - - for (uint32_t i = 0; i < kMaxRetries; ++i) { - std::srand(NanoTime()); - // Sample output for PID 1234 : /data/anr/anr-pid1234-cafeffee.txt - const std::string file_name = android::base::StringPrintf( - "%s/anr-pid%" PRId32 "-%08" PRIx32 ".txt", - stack_trace_dir_.c_str(), - static_cast<int32_t>(getpid()), - static_cast<uint32_t>(std::rand())); - - if (!OS::FileExists(file_name.c_str())) { - return file_name; - } - } +bool SignalCatcher::OpenStackTraceFile(android::base::unique_fd* tombstone_fd, + android::base::unique_fd* output_fd) { + if (use_tombstoned_stack_trace_fd_) { +#if defined(ART_TARGET_ANDROID) + return tombstoned_connect(getpid(), tombstone_fd, output_fd, false /* is_native_crash */); +#else + UNUSED(tombstone_fd); + UNUSED(output_fd); +#endif + } + + // The runtime is not configured to dump traces to a file, will LOG(INFO) + // instead. + if (stack_trace_file_.empty()) { + return false; + } - LOG(ERROR) << "Unable to obtain stack trace filename at path : " << stack_trace_dir_; - return ""; + int fd = open(stack_trace_file_.c_str(), O_APPEND | O_CREAT | O_WRONLY, 0666); + if (fd == -1) { + PLOG(ERROR) << "Unable to open stack trace file '" << stack_trace_file_ << "'"; + return false; } - return stack_trace_file_; + output_fd->reset(fd); + return true; } void SignalCatcher::Output(const std::string& s) { - const std::string output_file = GetStackTraceFileName(); - if (output_file.empty()) { + android::base::unique_fd tombstone_fd; + android::base::unique_fd output_fd; + if (!OpenStackTraceFile(&tombstone_fd, &output_fd)) { LOG(INFO) << s; return; } ScopedThreadStateChange tsc(Thread::Current(), kWaitingForSignalCatcherOutput); - int fd = open(output_file.c_str(), O_APPEND | O_CREAT | O_WRONLY, 0666); - if (fd == -1) { - PLOG(ERROR) << "Unable to open stack trace file '" << output_file << "'"; - return; - } - std::unique_ptr<File> file(new File(fd, output_file, true)); + + std::unique_ptr<File> file(new File(output_fd.release(), true /* check_usage */)); bool success = file->WriteFully(s.data(), s.size()); if (success) { success = file->FlushCloseOrErase() == 0; } else { file->Erase(); } + + const std::string output_path_msg = (use_tombstoned_stack_trace_fd_) ? + "[tombstoned]" : stack_trace_file_; + if (success) { - LOG(INFO) << "Wrote stack traces to '" << output_file << "'"; + LOG(INFO) << "Wrote stack traces to '" << output_path_msg << "'"; } else { - PLOG(ERROR) << "Failed to write stack traces to '" << output_file << "'"; + PLOG(ERROR) << "Failed to write stack traces to '" << output_path_msg << "'"; } + +#if defined(ART_TARGET_ANDROID) + if (!tombstoned_notify_completion(tombstone_fd)) { + LOG(WARNING) << "Unable to notify tombstoned of dump completion."; + } +#endif } void SignalCatcher::HandleSigQuit() { diff --git a/runtime/signal_catcher.h b/runtime/signal_catcher.h index 4cd7a98795..8a2a7289de 100644 --- a/runtime/signal_catcher.h +++ b/runtime/signal_catcher.h @@ -17,6 +17,7 @@ #ifndef ART_RUNTIME_SIGNAL_CATCHER_H_ #define ART_RUNTIME_SIGNAL_CATCHER_H_ +#include "android-base/unique_fd.h" #include "base/mutex.h" namespace art { @@ -32,15 +33,17 @@ class Thread; */ class SignalCatcher { public: - // If |stack_trace_dir| is non empty, traces will be written to a - // unique file under that directory. + // If |use_tombstoned_stack_trace_fd| is |true|, traces will be + // written to a file descriptor provided by tombstoned. The process + // will communicate with tombstoned via a unix domain socket. This + // mode of stack trace dumping is only supported in an Android + // environment. // - // If |stack_trace_dir| is empty, and |stack_frace_file| is non-empty, - // traces will be appended to |stack_trace_file|. - // - // If both are empty, all traces will be written to the log buffer. - explicit SignalCatcher(const std::string& stack_trace_dir, - const std::string& stack_trace_file); + // If false, all traces will be dumped to |stack_trace_file| if it's + // non-empty. If |stack_trace_file| is empty, all traces will be written + // to the log buffer. + SignalCatcher(const std::string& stack_trace_file, + const bool use_tombstoned_stack_trace_fd); ~SignalCatcher(); void HandleSigQuit() REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, @@ -51,15 +54,18 @@ class SignalCatcher { // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock. static void* Run(void* arg) NO_THREAD_SAFETY_ANALYSIS; - std::string GetStackTraceFileName(); + // NOTE: We're using android::base::unique_fd here for easier + // interoperability with tombstoned client APIs. + bool OpenStackTraceFile(android::base::unique_fd* tombstone_fd, + android::base::unique_fd* output_fd); void HandleSigUsr1(); void Output(const std::string& s); void SetHaltFlag(bool new_value) REQUIRES(!lock_); bool ShouldHalt() REQUIRES(!lock_); int WaitForSignal(Thread* self, SignalSet& signals) REQUIRES(!lock_); - std::string stack_trace_dir_; std::string stack_trace_file_; + const bool use_tombstoned_stack_trace_fd_; mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; ConditionVariable cond_ GUARDED_BY(lock_); diff --git a/test/987-stack-trace-dumping/expected.txt b/test/987-stack-trace-dumping/expected.txt deleted file mode 100644 index e69de29bb2..0000000000 --- a/test/987-stack-trace-dumping/expected.txt +++ /dev/null diff --git a/test/987-stack-trace-dumping/info.txt b/test/987-stack-trace-dumping/info.txt deleted file mode 100644 index e69de29bb2..0000000000 --- a/test/987-stack-trace-dumping/info.txt +++ /dev/null diff --git a/test/987-stack-trace-dumping/run b/test/987-stack-trace-dumping/run deleted file mode 100755 index dee3e8ba04..0000000000 --- a/test/987-stack-trace-dumping/run +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# -# Copyright 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Ask for stack traces to be dumped to a file rather than to stdout. -./default-run "$@" --set-stack-trace-dump-dir diff --git a/test/987-stack-trace-dumping/src/Main.java b/test/987-stack-trace-dumping/src/Main.java deleted file mode 100644 index d1e8a1b56b..0000000000 --- a/test/987-stack-trace-dumping/src/Main.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.io.File; - -public class Main { - public static void main(String[] args) throws Exception { - if (args.length != 3) { - throw new AssertionError("Unexpected number of args: " + args.length); - } - - if (!"--stack-trace-dir".equals(args[1])) { - throw new AssertionError("Unexpected argument in position 1: " + args[1]); - } - - // Send ourselves signal 3, which forces stack traces to be written to disk. - android.system.Os.kill(android.system.Os.getpid(), 3); - - File[] files = null; - final String stackTraceDir = args[2]; - for (int i = 0; i < 5; ++i) { - // Give the signal handler some time to run and dump traces - up to a maximum - // of 5 seconds. This is a kludge, but it's hard to do this without using things - // like inotify / WatchService and the like. - Thread.sleep(1000); - - files = (new File(stackTraceDir)).listFiles(); - if (files != null && files.length == 1) { - break; - } - } - - - if (files == null) { - throw new AssertionError("Gave up waiting for traces: " + java.util.Arrays.toString(files)); - } - - final String fileName = files[0].getName(); - if (!fileName.startsWith("anr-pid")) { - throw new AssertionError("Unexpected prefix: " + fileName); - } - - if (!fileName.contains(String.valueOf(android.system.Os.getpid()))) { - throw new AssertionError("File name does not contain process PID: " + fileName); - } - } -} diff --git a/test/knownfailures.json b/test/knownfailures.json index 4b44df7958..96c2967f86 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -651,8 +651,7 @@ "969-iface-super", "981-dedup-original-dex", "984-obsolete-invoke", - "985-re-obsolete", - "987-stack-trace-dumping" + "985-re-obsolete" ], "description": "The tests above fail with --build-with-javac-dx.", "env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"}, |