Add JNI state change performance test

This test is only for measuring the JNI performance. The test prints
timing information on each run so we do not compare stdout with
expected.txt. This test is mostly intended to be run manually with
the run-test script using the -O flag.

Change-Id: I68aa1473c425d9ef6f00796bdb47bea2a956d011
diff --git a/test/999-jni-perf/check b/test/999-jni-perf/check
new file mode 100755
index 0000000..ffbb8cf
--- /dev/null
+++ b/test/999-jni-perf/check
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+# Only compare the last line.
+tail -n 1 "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
\ No newline at end of file
diff --git a/test/999-jni-perf/expected.txt b/test/999-jni-perf/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/test/999-jni-perf/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/999-jni-perf/info.txt b/test/999-jni-perf/info.txt
new file mode 100644
index 0000000..010b57b
--- /dev/null
+++ b/test/999-jni-perf/info.txt
@@ -0,0 +1 @@
+Tests for measuring performance of JNI state changes.
diff --git a/test/999-jni-perf/perf-jni.cc b/test/999-jni-perf/perf-jni.cc
new file mode 100644
index 0000000..51eeb83
--- /dev/null
+++ b/test/999-jni-perf/perf-jni.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <assert.h>
+
+#include "jni.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+
+namespace art {
+
+namespace {
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_perfJniEmptyCall(JNIEnv*, jobject) {
+  return 0;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_perfSOACall(JNIEnv*, jobject) {
+  ScopedObjectAccess soa(Thread::Current());
+  return 0;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_perfSOAUncheckedCall(JNIEnv*, jobject) {
+  ScopedObjectAccessUnchecked soa(Thread::Current());
+  return 0;
+}
+
+}  // namespace
+
+}  // namespace art
diff --git a/test/999-jni-perf/src/Main.java b/test/999-jni-perf/src/Main.java
new file mode 100644
index 0000000..032e700
--- /dev/null
+++ b/test/999-jni-perf/src/Main.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+public class Main {
+  public Main() {
+  }
+
+  private static final String MSG = "ABCDE";
+
+  native int perfJniEmptyCall();
+  native int perfSOACall();
+  native int perfSOAUncheckedCall();
+
+  int runPerfTest(long N) {
+    long start = System.nanoTime();
+    for (long i = 0; i < N; i++) {
+      char c = MSG.charAt(2);
+    }
+    long elapse = System.nanoTime() - start;
+    System.out.println("Fast JNI (charAt): " + (double)elapse / N);
+
+    start = System.nanoTime();
+    for (long i = 0; i < N; i++) {
+      perfJniEmptyCall();
+    }
+    elapse = System.nanoTime() - start;
+    System.out.println("Empty call: " + (double)elapse / N);
+
+    start = System.nanoTime();
+    for (long i = 0; i < N; i++) {
+      perfSOACall();
+    }
+    elapse = System.nanoTime() - start;
+    System.out.println("SOA call: " + (double)elapse / N);
+
+    start = System.nanoTime();
+    for (long i = 0; i < N; i++) {
+      perfSOAUncheckedCall();
+    }
+    elapse = System.nanoTime() - start;
+    System.out.println("SOA unchecked call: " + (double)elapse / N);
+
+    return 0;
+  }
+
+  public static void main(String[] args) {
+    System.loadLibrary(args[0]);
+    long iterations = 1000000;
+    if (args.length > 1) {
+      iterations = Long.parseLong(args[1], 10);
+    }
+    Main m = new Main();
+    m.runPerfTest(iterations);
+    System.out.println("Done");
+  }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index e51e444..90bf5b5 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -37,7 +37,8 @@
   457-regs/regs_jni.cc \
   461-get-reference-vreg/get_reference_vreg_jni.cc \
   466-get-live-vreg/get_live_vreg_jni.cc \
-  497-inlining-and-class-loader/clear_dex_cache.cc
+  497-inlining-and-class-loader/clear_dex_cache.cc \
+  999-jni-perf/perf-jni.cc
 
 ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
 ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so