ART: Add stack-dump mode for contention logging

Add a second threshold to contention logging. If this threshold
is reached, log the owner and the contender thread to the logcat.

Requires a bit of refactor in the contention logging code to hold
the locks correctly (different requirements for owner and contender).
Add -Xstackdumplockprofthreshold:ms to control the threshold value.
As with general contention logging, default to 0 = off. Use ThreadStress
with --locks-only to get test coverage.

Bug: 62353392
Test: m test-art-host
Change-Id: I1b5a5130c4f908497ac06464130844f5ca42a5fb
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 4d309bf..940afc8 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -73,9 +73,12 @@
  */
 
 uint32_t Monitor::lock_profiling_threshold_ = 0;
+uint32_t Monitor::stack_dump_lock_profiling_threshold_ = 0;
 
-void Monitor::Init(uint32_t lock_profiling_threshold) {
+void Monitor::Init(uint32_t lock_profiling_threshold,
+                   uint32_t stack_dump_lock_profiling_threshold) {
   lock_profiling_threshold_ = lock_profiling_threshold;
+  stack_dump_lock_profiling_threshold_ = stack_dump_lock_profiling_threshold;
 }
 
 Monitor::Monitor(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_code)
@@ -411,19 +414,71 @@
           if (sample_percent != 0 && (static_cast<uint32_t>(rand() % 100) < sample_percent)) {
             // Reacquire mutator_lock_ for logging.
             ScopedObjectAccess soa(self);
-            // Acquire thread-list lock to find thread and keep it from dying until we're done.
-            MutexLock mu2(Thread::Current(), *Locks::thread_list_lock_);
 
-            // Re-find the owner in case the thread got killed.
-            Thread* original_owner = Runtime::Current()->GetThreadList()->FindThreadByThreadId(
-                original_owner_thread_id);
+            bool owner_alive = false;
+            pid_t original_owner_tid = 0;
+            std::string original_owner_name;
 
-            if (original_owner != nullptr) {
-              pid_t original_owner_tid = original_owner->GetTid();
-              std::string original_owner_name;
-              original_owner->GetThreadName(original_owner_name);
+            const bool should_dump_stacks = stack_dump_lock_profiling_threshold_ > 0 &&
+                wait_ms > stack_dump_lock_profiling_threshold_;
+            std::string owner_stack_dump;
 
-              if (wait_ms > kLongWaitMs && owners_method != nullptr) {
+            // Acquire thread-list lock to find thread and keep it from dying until we've got all
+            // the info we need.
+            {
+              MutexLock mu2(Thread::Current(), *Locks::thread_list_lock_);
+
+              // Re-find the owner in case the thread got killed.
+              Thread* original_owner = Runtime::Current()->GetThreadList()->FindThreadByThreadId(
+                  original_owner_thread_id);
+
+              if (original_owner != nullptr) {
+                owner_alive = true;
+                original_owner_tid = original_owner->GetTid();
+                original_owner->GetThreadName(original_owner_name);
+
+                if (should_dump_stacks) {
+                  // Very long contention. Dump stacks.
+                  struct CollectStackTrace : public Closure {
+                    void Run(art::Thread* thread) OVERRIDE
+                        REQUIRES_SHARED(art::Locks::mutator_lock_) {
+                      thread->DumpJavaStack(oss);
+                    }
+
+                    std::ostringstream oss;
+                  };
+                  CollectStackTrace owner_trace;
+                  original_owner->RequestSynchronousCheckpoint(&owner_trace);
+                  owner_stack_dump = owner_trace.oss.str();
+                }
+              }
+              // This is all the data we need. Now drop the thread-list lock, it's OK for the
+              // owner to go away now.
+            }
+
+            // If we found the owner (and thus have owner data), go and log now.
+            if (owner_alive) {
+              // Give the detailed traces for really long contention.
+              if (should_dump_stacks) {
+                // This must be here (and not above) because we cannot hold the thread-list lock
+                // while running the checkpoint.
+                std::ostringstream self_trace_oss;
+                self->DumpJavaStack(self_trace_oss);
+
+                uint32_t pc;
+                ArtMethod* m = self->GetCurrentMethod(&pc);
+
+                LOG(WARNING) << "Long "
+                    << PrettyContentionInfo(original_owner_name,
+                                            original_owner_tid,
+                                            owners_method,
+                                            owners_dex_pc,
+                                            num_waiters)
+                    << " in " << ArtMethod::PrettyMethod(m) << " for "
+                    << PrettyDuration(MsToNs(wait_ms)) << "\n"
+                    << "Current owner stack:\n" << owner_stack_dump
+                    << "Contender stack:\n" << self_trace_oss.str();
+              } else if (wait_ms > kLongWaitMs && owners_method != nullptr) {
                 uint32_t pc;
                 ArtMethod* m = self->GetCurrentMethod(&pc);
                 // TODO: We should maybe check that original_owner is still a live thread.