diff options
-rw-r--r-- | runtime/parsed_options.cc | 3 | ||||
-rw-r--r-- | runtime/runtime.cc | 3 | ||||
-rw-r--r-- | runtime/runtime.h | 1 | ||||
-rw-r--r-- | runtime/runtime_options.def | 1 | ||||
-rw-r--r-- | runtime/signal_catcher.cc | 49 | ||||
-rw-r--r-- | runtime/signal_catcher.h | 12 | ||||
-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 | ||||
-rwxr-xr-x | test/etc/run-test-jar | 20 |
11 files changed, 157 insertions, 10 deletions
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 8ffd8bbc90..fc91efa6ac 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -238,6 +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("-Xstacktracefile:_") .WithType<std::string>() .IntoKey(M::StackTraceFile) diff --git a/runtime/runtime.cc b/runtime/runtime.cc index a48a58d235..bfe9f9c354 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -825,7 +825,7 @@ void Runtime::InitNonZygoteOrPostFork( void Runtime::StartSignalCatcher() { if (!is_zygote_) { - signal_catcher_ = new SignalCatcher(stack_trace_file_); + signal_catcher_ = new SignalCatcher(stack_trace_dir_, stack_trace_file_); } } @@ -1035,6 +1035,7 @@ 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); 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 20db628a75..86d8e4f513 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -767,6 +767,7 @@ class Runtime { ClassLinker* class_linker_; SignalCatcher* signal_catcher_; + std::string stack_trace_dir_; std::string stack_trace_file_; std::unique_ptr<JavaVMExt> java_vm_; diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 16190cd3c4..77132a8bae 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -100,6 +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 (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 0b7ea2ff02..382643314c 100644 --- a/runtime/signal_catcher.cc +++ b/runtime/signal_catcher.cc @@ -27,6 +27,7 @@ #include <sstream> +#include "android-base/stringprintf.h" #include "arch/instruction_set.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" @@ -65,8 +66,10 @@ static void DumpCmdLine(std::ostream& os) { #endif } -SignalCatcher::SignalCatcher(const std::string& stack_trace_file) - : stack_trace_file_(stack_trace_file), +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), lock_("SignalCatcher lock"), cond_("SignalCatcher::cond_", lock_), thread_(nullptr) { @@ -100,19 +103,51 @@ 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-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; + } + } + + LOG(ERROR) << "Unable to obtain stack trace filename at path : " << stack_trace_dir_; + return ""; + } + + return stack_trace_file_; +} + void SignalCatcher::Output(const std::string& s) { - if (stack_trace_file_.empty()) { + const std::string stack_trace_file = GetStackTraceFileName(); + if (stack_trace_file.empty()) { LOG(INFO) << s; return; } ScopedThreadStateChange tsc(Thread::Current(), kWaitingForSignalCatcherOutput); - int fd = open(stack_trace_file_.c_str(), O_APPEND | O_CREAT | O_WRONLY, 0666); + 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; } - std::unique_ptr<File> file(new File(fd, stack_trace_file_, true)); + std::unique_ptr<File> file(new File(fd, stack_trace_file, true)); bool success = file->WriteFully(s.data(), s.size()); if (success) { success = file->FlushCloseOrErase() == 0; @@ -120,9 +155,9 @@ void SignalCatcher::Output(const std::string& s) { file->Erase(); } if (success) { - LOG(INFO) << "Wrote stack traces to '" << stack_trace_file_ << "'"; + LOG(INFO) << "Wrote stack traces to '" << stack_trace_file << "'"; } else { - PLOG(ERROR) << "Failed to write stack traces to '" << stack_trace_file_ << "'"; + PLOG(ERROR) << "Failed to write stack traces to '" << stack_trace_file << "'"; } } diff --git a/runtime/signal_catcher.h b/runtime/signal_catcher.h index de6a212df4..4cd7a98795 100644 --- a/runtime/signal_catcher.h +++ b/runtime/signal_catcher.h @@ -32,7 +32,15 @@ class Thread; */ class SignalCatcher { public: - explicit SignalCatcher(const std::string& stack_trace_file); + // If |stack_trace_dir| is non empty, traces will be written to a + // unique file under that directory. + // + // 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); ~SignalCatcher(); void HandleSigQuit() REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, @@ -43,12 +51,14 @@ 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(); 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_; mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; diff --git a/test/987-stack-trace-dumping/expected.txt b/test/987-stack-trace-dumping/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/987-stack-trace-dumping/expected.txt diff --git a/test/987-stack-trace-dumping/info.txt b/test/987-stack-trace-dumping/info.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/987-stack-trace-dumping/info.txt diff --git a/test/987-stack-trace-dumping/run b/test/987-stack-trace-dumping/run new file mode 100755 index 0000000000..dee3e8ba04 --- /dev/null +++ b/test/987-stack-trace-dumping/run @@ -0,0 +1,18 @@ +#!/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 new file mode 100644 index 0000000000..d1e8a1b56b --- /dev/null +++ b/test/987-stack-trace-dumping/src/Main.java @@ -0,0 +1,60 @@ +/* + * 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/etc/run-test-jar b/test/etc/run-test-jar index 56cfd244e6..c243af5423 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -67,6 +67,11 @@ VDEX_FILTER="" PROFILE="n" RANDOM_PROFILE="n" +# if "y", set -Xstacktracedir and inform the test of its location. When +# this is set, stack trace dumps (from signal 3) will be written to a file +# under this directory instead of stdout. +SET_STACK_TRACE_DUMP_DIR="n" + # if "y", run 'sync' before dalvikvm to make sure all files from # build step (e.g. dex2oat) were finished writing. SYNC_BEFORE_RUN="n" @@ -283,6 +288,9 @@ while true; do elif [ "x$1" = "x--random-profile" ]; then RANDOM_PROFILE="y" shift + elif [ "x$1" = "x--set-stack-trace-dump-dir" ]; then + SET_STACK_TRACE_DUMP_DIR="y" + shift elif expr "x$1" : "x--" >/dev/null 2>&1; then echo "unknown $0 option: $1" 1>&2 exit 1 @@ -291,12 +299,22 @@ while true; do fi done +mkdir_locations="" + if [ "$USE_JVM" = "n" ]; then FLAGS="${FLAGS} ${ANDROID_FLAGS}" for feature in ${EXPERIMENTAL}; do FLAGS="${FLAGS} -Xexperimental:${feature} -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:${feature}" COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xexperimental:${feature}" done + + if [ "$SET_STACK_TRACE_DUMP_DIR" = "y" ]; then + # Note that DEX_LOCATION is used as a proxy for tmpdir throughout this + # file (it will be under the test specific folder). + mkdir_locations="${mkdir_locations} $DEX_LOCATION/stack_traces" + FLAGS="${FLAGS} -Xstacktracedir:$DEX_LOCATION/stack_traces" + ARGS="${ARGS} --stack-trace-dir $DEX_LOCATION/stack_traces" + fi fi if [ "x$1" = "x" ] ; then @@ -534,7 +552,7 @@ fi profman_cmdline="true" dex2oat_cmdline="true" vdex_cmdline="true" -mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA" +mkdir_locations="${mkdir_locations} ${DEX_LOCATION}/dalvik-cache/$ISA" strip_cmdline="true" sync_cmdline="true" |