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/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 << "'";
   }
 }