Increase debug information for failed monitor exits

CTS has shown IllegalMonitorStateExceptions where they shouldn't occur.
Ratchet up the debug information in the case of unlocks so the kind of
problem can be better diagnosed. This code is only executed in
exceptional cases and so better debugging wins out over performance.

Also, add helper to dump thread state as something human readable.

Also, JNI internal unit test of monitor enter/exit.

Change-Id: I9c06fbce7e6c9ebbb950a4e400f65c791ebe2ba4
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index 62f55b7..97cd969 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -1460,4 +1460,51 @@
   ASSERT_TRUE(env_->GetDirectBufferCapacity(buffer) == sizeof(bytes));
 }
 
+TEST_F(JniInternalTest, MonitorEnterExit) {
+  // Create an object to torture
+  jclass object_class = env_->FindClass("java/lang/Object");
+  ASSERT_TRUE(object_class != NULL);
+  jobject object = env_->AllocObject(object_class);
+  ASSERT_TRUE(object != NULL);
+
+  // Expected class of exceptions
+  jclass imse_class = env_->FindClass("java/lang/IllegalMonitorStateException");
+  ASSERT_TRUE(imse_class != NULL);
+
+  jthrowable thrown_exception;
+
+  // Unlock of unowned monitor
+  env_->MonitorExit(object);
+  EXPECT_TRUE(env_->ExceptionCheck());
+  thrown_exception = env_->ExceptionOccurred();
+  env_->ExceptionClear();
+  EXPECT_TRUE(env_->IsInstanceOf(thrown_exception, imse_class));
+
+  // Lock of unowned monitor
+  env_->MonitorEnter(object);
+  EXPECT_FALSE(env_->ExceptionCheck());
+  // Regular unlock
+  env_->MonitorExit(object);
+  EXPECT_FALSE(env_->ExceptionCheck());
+
+  // Recursively lock a lot
+  size_t max_recursive_lock = 1024;
+  for (size_t i = 0; i < max_recursive_lock; i++) {
+    env_->MonitorEnter(object);
+    EXPECT_FALSE(env_->ExceptionCheck());
+  }
+  // Recursively unlock a lot
+  for (size_t i = 0; i < max_recursive_lock; i++) {
+    env_->MonitorExit(object);
+    EXPECT_FALSE(env_->ExceptionCheck());
+  }
+
+  // Unlock of unowned monitor
+  env_->MonitorExit(object);
+  EXPECT_TRUE(env_->ExceptionCheck());
+  thrown_exception = env_->ExceptionOccurred();
+  env_->ExceptionClear();
+  EXPECT_TRUE(env_->IsInstanceOf(thrown_exception, imse_class));
+}
+
 }  // namespace art