diff options
author | 2017-07-06 17:45:38 +0000 | |
---|---|---|
committer | 2017-07-06 17:45:38 +0000 | |
commit | e32097c4d7f068673b531384340bb55f4b2ac011 (patch) | |
tree | db61cfbf71702d960f659a1bb57b41ef25d7b5f1 | |
parent | 209b4c7141d7da61790844cd58bd0a9bab2951d8 (diff) | |
parent | 4c174286c5a87183ace4ed46a3f811096669367a (diff) |
Merge "Add jvmti GetBytecodes function."
-rw-r--r-- | runtime/openjdkjvmti/OpenjdkJvmTi.cc | 8 | ||||
-rw-r--r-- | runtime/openjdkjvmti/art_jvmti.h | 2 | ||||
-rw-r--r-- | runtime/openjdkjvmti/ti_method.cc | 34 | ||||
-rw-r--r-- | runtime/openjdkjvmti/ti_method.h | 5 | ||||
-rw-r--r-- | test/1901-get-bytecodes/bytecodes.cc | 63 | ||||
-rw-r--r-- | test/1901-get-bytecodes/expected.txt | 0 | ||||
-rw-r--r-- | test/1901-get-bytecodes/info.txt | 3 | ||||
-rwxr-xr-x | test/1901-get-bytecodes/run | 17 | ||||
-rw-r--r-- | test/1901-get-bytecodes/src/Main.java | 21 | ||||
-rw-r--r-- | test/1901-get-bytecodes/src/art/Test1901.java | 147 | ||||
-rw-r--r-- | test/Android.bp | 1 |
11 files changed, 296 insertions, 5 deletions
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 21239fe3de..505e844c92 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -913,12 +913,12 @@ class JvmtiFunctions { } 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 2d5d527e68..c63e50252b 100644 --- a/runtime/openjdkjvmti/art_jvmti.h +++ b/runtime/openjdkjvmti/art_jvmti.h @@ -216,7 +216,7 @@ const jvmtiCapabilities kPotentialCapabilities = { .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 beb639e208..9b5b964a4d 100644 --- a/runtime/openjdkjvmti/ti_method.cc +++ b/runtime/openjdkjvmti/ti_method.cc @@ -91,6 +91,40 @@ void MethodUtil::Unregister() { 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 cc161c8fed..d95a81b63b 100644 --- a/runtime/openjdkjvmti/ti_method.h +++ b/runtime/openjdkjvmti/ti_method.h @@ -44,6 +44,11 @@ class MethodUtil { 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 0000000000..edcb734788 --- /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 0000000000..e69de29bb2 --- /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 0000000000..c8c91893e5 --- /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 0000000000..c6e62ae6cd --- /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 0000000000..d37fcdb0cb --- /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 0000000000..6940491e3c --- /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 f893531c41..e5d2ca0863 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -286,6 +286,7 @@ art_cc_defaults { "992-source-data/source_file.cc", "993-breakpoints/breakpoints.cc", "996-breakpoint-obsolete/obsolete_breakpoints.cc", + "1901-get-bytecodes/bytecodes.cc", ], shared_libs: [ "libbase", |