ART: Support per PID stack trace files.
Introduce an -Xstacktracedir argument that supplies a directory
under which stack traces are written, with a unique file created
per trace. The location of the actual directory in a production
system is still not decided, and follow up changes might be
introduced to supply a per process override.
Bug: 32064548
Test: test-art-host, test-art-target
Change-Id: If377ce6a2abe8b325f6441d8de222b1ea3f40ec9
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 8ffd8bb..fc91efa 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -238,6 +238,9 @@
.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 a48a58d..bfe9f9c 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -825,7 +825,7 @@
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 @@
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 20db628..86d8e4f 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -767,6 +767,7 @@
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 16190cd..77132a8 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -100,6 +100,7 @@
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 0b7ea2f..3826433 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 @@
#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 @@
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 @@
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 de6a212..4cd7a98 100644
--- a/runtime/signal_catcher.h
+++ b/runtime/signal_catcher.h
@@ -32,7 +32,15 @@
*/
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 @@
// 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;