Add jvmti GetBytecodes function.

Enables the can_get_bytecodes capability.

Test: ./test.py --host -j40
Bug: 34414073
Change-Id: If74cfcb9ecab1b4e53fa0708cf09285b9f64b5be
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index e3768b3..df870bf 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -922,12 +922,12 @@
   }
 
   static jvmtiError GetBytecodes(jvmtiEnv* env,
-                                 jmethodID method ATTRIBUTE_UNUSED,
-                                 jint* bytecode_count_ptr ATTRIBUTE_UNUSED,
-                                 unsigned char** bytecodes_ptr ATTRIBUTE_UNUSED) {
+                                 jmethodID method,
+                                 jint* bytecode_count_ptr,
+                                 unsigned char** bytecodes_ptr) {
     ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_bytecodes);
-    return ERR(NOT_IMPLEMENTED);
+    return MethodUtil::GetBytecodes(env, method, bytecode_count_ptr, bytecodes_ptr);
   }
 
   static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr) {
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 2d5d527..c63e502 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -216,7 +216,7 @@
     .can_tag_objects                                 = 1,
     .can_generate_field_modification_events          = 1,
     .can_generate_field_access_events                = 1,
-    .can_get_bytecodes                               = 0,
+    .can_get_bytecodes                               = 1,
     .can_get_synthetic_attribute                     = 1,
     .can_get_owned_monitor_info                      = 0,
     .can_get_current_contended_monitor               = 0,
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index beb639e..9b5b964 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -91,6 +91,40 @@
   runtime->GetRuntimeCallbacks()->RemoveMethodCallback(&gMethodCallback);
 }
 
+jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env,
+                                    jmethodID method,
+                                    jint* size_ptr,
+                                    unsigned char** bytecode_ptr) {
+  if (method == nullptr) {
+    return ERR(INVALID_METHODID);
+  }
+  art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+  if (art_method->IsNative()) {
+    return ERR(NATIVE_METHOD);
+  }
+
+  if (size_ptr == nullptr || bytecode_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  const art::DexFile::CodeItem* code_item = art_method->GetCodeItem();
+  if (code_item == nullptr) {
+    *size_ptr = 0;
+    *bytecode_ptr = nullptr;
+    return OK;
+  }
+  // 2 bytes per instruction for dex code.
+  *size_ptr = code_item->insns_size_in_code_units_ * 2;
+  jvmtiError err = env->Allocate(*size_ptr, bytecode_ptr);
+  if (err != OK) {
+    return err;
+  }
+  memcpy(*bytecode_ptr, code_item->insns_, *size_ptr);
+  return OK;
+}
+
 jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
                                         jmethodID method,
                                         jint* size_ptr) {
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
index cc161c8..d95a81b 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -44,6 +44,11 @@
   static void Register(EventHandler* event_handler);
   static void Unregister();
 
+  static jvmtiError GetBytecodes(jvmtiEnv* env,
+                                 jmethodID method,
+                                 jint* count_ptr,
+                                 unsigned char** bytecodes);
+
   static jvmtiError GetArgumentsSize(jvmtiEnv* env, jmethodID method, jint* size_ptr);
 
   static jvmtiError GetMaxLocals(jvmtiEnv* env, jmethodID method, jint* max_ptr);
diff --git a/test/1901-get-bytecodes/bytecodes.cc b/test/1901-get-bytecodes/bytecodes.cc
new file mode 100644
index 0000000..edcb734
--- /dev/null
+++ b/test/1901-get-bytecodes/bytecodes.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 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 <iostream>
+#include <pthread.h>
+#include <stdio.h>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1901Bytecodes {
+
+extern "C" JNIEXPORT jbyteArray JNICALL Java_art_Test1901_getBytecodes(JNIEnv* env,
+                                                                       jclass,
+                                                                       jobject jmethod) {
+  jmethodID method = env->FromReflectedMethod(jmethod);
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  }
+  unsigned char* bytecodes = nullptr;
+  jint bytecodes_size = 0;
+  if (JvmtiErrorToException(env,
+                            jvmti_env,
+                            jvmti_env->GetBytecodes(method, &bytecodes_size, &bytecodes))) {
+    return nullptr;
+  }
+  jbyteArray out = env->NewByteArray(bytecodes_size);
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  } else if (bytecodes_size == 0) {
+    return out;
+  }
+  jbyte* bytes = env->GetByteArrayElements(out, /* is_copy */ nullptr);
+  memcpy(bytes, bytecodes, bytecodes_size);
+  env->ReleaseByteArrayElements(out, bytes, 0);
+  return out;
+}
+
+}  // namespace Test1901Bytecodes
+}  // namespace art
diff --git a/test/1901-get-bytecodes/expected.txt b/test/1901-get-bytecodes/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/1901-get-bytecodes/expected.txt
diff --git a/test/1901-get-bytecodes/info.txt b/test/1901-get-bytecodes/info.txt
new file mode 100644
index 0000000..c8c9189
--- /dev/null
+++ b/test/1901-get-bytecodes/info.txt
@@ -0,0 +1,3 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that the GetBytecodes function works as expected.
diff --git a/test/1901-get-bytecodes/run b/test/1901-get-bytecodes/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/1901-get-bytecodes/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --jvmti
diff --git a/test/1901-get-bytecodes/src/Main.java b/test/1901-get-bytecodes/src/Main.java
new file mode 100644
index 0000000..d37fcdb
--- /dev/null
+++ b/test/1901-get-bytecodes/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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 static void main(String[] args) throws Exception {
+    art.Test1901.run();
+  }
+}
diff --git a/test/1901-get-bytecodes/src/art/Test1901.java b/test/1901-get-bytecodes/src/art/Test1901.java
new file mode 100644
index 0000000..6940491
--- /dev/null
+++ b/test/1901-get-bytecodes/src/art/Test1901.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Base64;
+
+public class Test1901 {
+  // Class & Dex file containing the following class.
+  // Using this representation to prevent any changes to the compiler or the file formats from
+  // changing the output of this test.
+  //
+  // package art;
+  // public class Target {
+  //   public void doNothing() {
+  //     return;
+  //   }
+  //
+  //   public static void staticNothing() {
+  //     return;
+  //   }
+  //
+  //   public void sayHi() {
+  //     System.out.println("hello");
+  //   }
+  // }
+  public static byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAHgoABgAQCQARABIIABMKABQAFQcAFgcAFwEABjxpbml0PgEAAygpVgEABENvZGUB" +
+    "AA9MaW5lTnVtYmVyVGFibGUBAAlkb05vdGhpbmcBAA1zdGF0aWNOb3RoaW5nAQAFc2F5SGkBAApT" +
+    "b3VyY2VGaWxlAQALVGFyZ2V0LmphdmEMAAcACAcAGAwAGQAaAQAFaGVsbG8HABsMABwAHQEACmFy" +
+    "dC9UYXJnZXQBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxq" +
+    "YXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExq" +
+    "YXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAAEAAEABwAIAAEACQAAAB0AAQABAAAABSq3AAGx" +
+    "AAAAAQAKAAAABgABAAAAAgABAAsACAABAAkAAAAZAAAAAQAAAAGxAAAAAQAKAAAABgABAAAABAAJ" +
+    "AAwACAABAAkAAAAZAAAAAAAAAAGxAAAAAQAKAAAABgABAAAACAABAA0ACAABAAkAAAAlAAIAAQAA" +
+    "AAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAMAAgADQABAA4AAAACAA8=");
+  public static byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQAbYkxNjiZ8a+fNWF4smR2+uXbrq88/FNoYAwAAcAAAAHhWNBIAAAAAAAAAAHgCAAAP" +
+    "AAAAcAAAAAYAAACsAAAAAgAAAMQAAAABAAAA3AAAAAYAAADkAAAAAQAAABQBAADkAQAANAEAAJoB" +
+    "AACiAQAAsAEAAMcBAADbAQAA7wEAAAMCAAAQAgAAEwIAABcCAAAiAgAAKQIAAC4CAAA3AgAAPgIA" +
+    "AAEAAAACAAAAAwAAAAQAAAAFAAAABwAAAAcAAAAFAAAAAAAAAAgAAAAFAAAAlAEAAAQAAQALAAAA" +
+    "AAAAAAAAAAAAAAAACQAAAAAAAAANAAAAAAAAAA4AAAABAAEADAAAAAIAAAAAAAAAAAAAAAEAAAAC" +
+    "AAAAAAAAAAYAAAAAAAAAYgIAAAAAAAABAAEAAQAAAE0CAAAEAAAAcBAFAAAADgAAAAAAAAAAAFIC" +
+    "AAABAAAADgAAAAEAAQAAAAAAVwIAAAEAAAAOAAAAAwABAAIAAABcAgAACAAAAGIAAAAaAQoAbiAE" +
+    "ABAADgABAAAAAwAGPGluaXQ+AAxMYXJ0L1RhcmdldDsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwAS" +
+    "TGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" +
+    "OwALVGFyZ2V0LmphdmEAAVYAAlZMAAlkb05vdGhpbmcABWhlbGxvAANvdXQAB3ByaW50bG4ABXNh" +
+    "eUhpAA1zdGF0aWNOb3RoaW5nAAIABw4ACAAHDgAEAAcOAAwABw54AAAAAgIAgYAEtAIDCcwCAQHg" +
+    "AgEB9AINAAAAAAAAAAEAAAAAAAAAAQAAAA8AAABwAAAAAgAAAAYAAACsAAAAAwAAAAIAAADEAAAA" +
+    "BAAAAAEAAADcAAAABQAAAAYAAADkAAAABgAAAAEAAAAUAQAAASAAAAQAAAA0AQAAARAAAAEAAACU" +
+    "AQAAAiAAAA8AAACaAQAAAyAAAAQAAABNAgAAACAAAAEAAABiAgAAABAAAAEAAAB4AgAA");
+
+  public static byte[][] DO_NOTHING_BYTECODES = new byte[][] {
+    // Dex Bytecodes for doNothing
+    // 0e00           |0000: return-void
+    new byte[] { 14, 0 },
+    // Java bytecodes
+    // 0: return
+    new byte[] { -79 },
+  };
+
+  public static byte[][] STATIC_NOTHING_BYTECODES = new byte[][] {
+    // Dex Bytecodes for staticNothing
+    // 0e00           |0000: return-void
+    new byte[] { 14, 0 },
+    // Java bytecodes
+    // 0: return
+    new byte[] { -79 },
+  };
+
+  public static byte[][] SAY_HI_NOTHING_BYTECODES = new byte[][] {
+    // Dex Bytecodes for sayHi
+    // 6200 0000      |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000
+    // 1a01 0a00      |0002: const-string v1, "hello" // string@000a
+    // 6e20 0400 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0004
+    // 0e00           |0007: return-void
+    new byte[] { 98, 0, 0, 0, 26, 1, 10, 0, 110, 32, 4, 0, 16, 0, 14, 0 },
+    // Java bytecodes
+    // 0: getstatic     #2  // Field java/lang/System.out:Ljava/io/PrintStream;
+    // 3: ldc           #3  // String hello
+    // 5: invokevirtual #4  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
+    // 8: return
+    new byte[] { -78, 0, 2, 18, 3, -74, 0, 4, -79 },
+  };
+
+  public static ClassLoader getClassLoader() throws Exception {
+    try {
+      Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader");
+      Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class);
+      // We are on art since we got the InMemoryDexClassLoader.
+      return (ClassLoader)ctor.newInstance(
+          ByteBuffer.wrap(DEX_BYTES), Test1901.class.getClassLoader());
+    } catch (ClassNotFoundException e) {
+      // Running on RI.
+      return new ClassLoader(Test1901.class.getClassLoader()) {
+        protected Class<?> findClass(String name) throws ClassNotFoundException {
+          if (name.equals("art.Target")) {
+            return defineClass(name, CLASS_BYTES, 0, CLASS_BYTES.length);
+          } else {
+            return super.findClass(name);
+          }
+        }
+      };
+    }
+  }
+
+  public static void CheckMethodBytes(Method m, byte[][] possible_bytecodes) {
+    byte[] real_codes = getBytecodes(m);
+    for (byte[] pos : possible_bytecodes) {
+      if (Arrays.equals(pos, real_codes)) {
+        return;
+      }
+    }
+    System.out.println("Unexpected bytecodes for " + m);
+    System.out.println("Received: " + Arrays.toString(real_codes));
+    System.out.println("Expected one of:");
+    for (byte[] pos : possible_bytecodes) {
+      System.out.println("\t" + Arrays.toString(pos));
+    }
+  }
+
+  public static void run() throws Exception {
+    Class<?> target = getClassLoader().loadClass("art.Target");
+    CheckMethodBytes(target.getDeclaredMethod("doNothing"), DO_NOTHING_BYTECODES);
+    CheckMethodBytes(target.getDeclaredMethod("staticNothing"), STATIC_NOTHING_BYTECODES);
+    CheckMethodBytes(target.getDeclaredMethod("sayHi"), SAY_HI_NOTHING_BYTECODES);
+  }
+
+  public static native byte[] getBytecodes(Method m);
+}
diff --git a/test/Android.bp b/test/Android.bp
index f893531..e5d2ca0 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -286,6 +286,7 @@
         "992-source-data/source_file.cc",
         "993-breakpoints/breakpoints.cc",
         "996-breakpoint-obsolete/obsolete_breakpoints.cc",
+        "1901-get-bytecodes/bytecodes.cc",
     ],
     shared_libs: [
         "libbase",