diff options
author | 2017-06-16 09:04:29 -0700 | |
---|---|---|
committer | 2017-06-20 09:05:14 -0700 | |
commit | 6fa7b81b09d5f1c68cec074cdf56dbc007e16baa (patch) | |
tree | 5943d81dcc452c705e2b69ad378b9d061b504513 | |
parent | 18b4ed19eb7970b9dfcc3246562c6fabe57176ee (diff) |
Add GetSourceFile and GetSourceDebugExt JVMTI functions
Also add associated capabilities.
Test: ./test.py --host -j40
Bug: 62821960
Change-Id: Icc534b2789287fc9f0daddb747c0c0fa81a7728b
-rw-r--r-- | runtime/openjdkjvmti/OpenjdkJvmTi.cc | 12 | ||||
-rw-r--r-- | runtime/openjdkjvmti/art_jvmti.h | 4 | ||||
-rw-r--r-- | runtime/openjdkjvmti/ti_class.cc | 57 | ||||
-rw-r--r-- | runtime/openjdkjvmti/ti_class.h | 6 | ||||
-rw-r--r-- | test/992-source-data/expected.txt | 10 | ||||
-rw-r--r-- | test/992-source-data/info.txt | 1 | ||||
-rwxr-xr-x | test/992-source-data/run | 17 | ||||
-rw-r--r-- | test/992-source-data/source_file.cc | 53 | ||||
-rw-r--r-- | test/992-source-data/src/Main.java | 21 | ||||
-rw-r--r-- | test/992-source-data/src/art/Test2.java | 19 | ||||
-rw-r--r-- | test/992-source-data/src/art/Test992.java | 47 | ||||
-rw-r--r-- | test/Android.bp | 1 | ||||
-rw-r--r-- | test/ti-stress/stress.cc | 54 |
13 files changed, 289 insertions, 13 deletions
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 8daf6f74b3..0896210f1c 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -686,12 +686,10 @@ class JvmtiFunctions { return ClassUtil::GetClassStatus(env, klass, status_ptr); } - static jvmtiError GetSourceFileName(jvmtiEnv* env, - jclass klass ATTRIBUTE_UNUSED, - char** source_name_ptr ATTRIBUTE_UNUSED) { + static jvmtiError GetSourceFileName(jvmtiEnv* env, jclass klass, char** source_name_ptr) { ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_source_file_name); - return ERR(NOT_IMPLEMENTED); + return ClassUtil::GetSourceFileName(env, klass, source_name_ptr); } static jvmtiError GetClassModifiers(jvmtiEnv* env, jclass klass, jint* modifiers_ptr) { @@ -766,11 +764,11 @@ class JvmtiFunctions { } static jvmtiError GetSourceDebugExtension(jvmtiEnv* env, - jclass klass ATTRIBUTE_UNUSED, - char** source_debug_extension_ptr ATTRIBUTE_UNUSED) { + jclass klass, + char** source_debug_extension_ptr) { ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_get_source_debug_extension); - return ERR(NOT_IMPLEMENTED); + return ClassUtil::GetSourceDebugExtension(env, klass, source_debug_extension_ptr); } static jvmtiError RetransformClasses(jvmtiEnv* env, jint class_count, const jclass* classes) { diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h index 8ba3527564..b5f12191e6 100644 --- a/runtime/openjdkjvmti/art_jvmti.h +++ b/runtime/openjdkjvmti/art_jvmti.h @@ -218,9 +218,9 @@ const jvmtiCapabilities kPotentialCapabilities = { .can_pop_frame = 0, .can_redefine_classes = 1, .can_signal_thread = 0, - .can_get_source_file_name = 0, + .can_get_source_file_name = 1, .can_get_line_numbers = 1, - .can_get_source_debug_extension = 0, + .can_get_source_debug_extension = 1, .can_access_local_variables = 0, .can_maintain_original_method_order = 0, .can_generate_single_step_events = 0, diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc index 332181497c..0ac08d9cb8 100644 --- a/runtime/openjdkjvmti/ti_class.cc +++ b/runtime/openjdkjvmti/ti_class.cc @@ -1017,4 +1017,61 @@ jvmtiError ClassUtil::GetClassVersionNumbers(jvmtiEnv* env ATTRIBUTE_UNUSED, return ERR(NONE); } +jvmtiError ClassUtil::GetSourceFileName(jvmtiEnv* env, jclass jklass, char** source_name_ptr) { + art::ScopedObjectAccess soa(art::Thread::Current()); + if (jklass == nullptr) { + return ERR(INVALID_CLASS); + } + art::ObjPtr<art::mirror::Object> jklass_obj = soa.Decode<art::mirror::Object>(jklass); + if (!jklass_obj->IsClass()) { + return ERR(INVALID_CLASS); + } + art::ObjPtr<art::mirror::Class> klass = jklass_obj->AsClass(); + if (klass->IsPrimitive() || klass->IsArrayClass()) { + return ERR(ABSENT_INFORMATION); + } + JvmtiUniquePtr<char[]> source_copy; + const char* file_name = klass->GetSourceFile(); + if (file_name == nullptr) { + return ERR(ABSENT_INFORMATION); + } + jvmtiError ret; + source_copy = CopyString(env, file_name, &ret); + if (source_copy == nullptr) { + return ret; + } + *source_name_ptr = source_copy.release(); + return OK; +} + +jvmtiError ClassUtil::GetSourceDebugExtension(jvmtiEnv* env, + jclass jklass, + char** source_debug_extension_ptr) { + art::ScopedObjectAccess soa(art::Thread::Current()); + if (jklass == nullptr) { + return ERR(INVALID_CLASS); + } + art::ObjPtr<art::mirror::Object> jklass_obj = soa.Decode<art::mirror::Object>(jklass); + if (!jklass_obj->IsClass()) { + return ERR(INVALID_CLASS); + } + art::StackHandleScope<1> hs(art::Thread::Current()); + art::Handle<art::mirror::Class> klass(hs.NewHandle(jklass_obj->AsClass())); + if (klass->IsPrimitive() || klass->IsArrayClass()) { + return ERR(ABSENT_INFORMATION); + } + JvmtiUniquePtr<char[]> ext_copy; + const char* data = art::annotations::GetSourceDebugExtension(klass); + if (data == nullptr) { + return ERR(ABSENT_INFORMATION); + } + jvmtiError ret; + ext_copy = CopyString(env, data, &ret); + if (ext_copy == nullptr) { + return ret; + } + *source_debug_extension_ptr = ext_copy.release(); + return OK; +} + } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h index aa2260f035..7bb6b3e5de 100644 --- a/runtime/openjdkjvmti/ti_class.h +++ b/runtime/openjdkjvmti/ti_class.h @@ -82,6 +82,12 @@ class ClassUtil { jclass klass, jint* minor_version_ptr, jint* major_version_ptr); + + static jvmtiError GetSourceFileName(jvmtiEnv* env, jclass klass, char** source_name_ptr); + + static jvmtiError GetSourceDebugExtension(jvmtiEnv* env, + jclass klass, + char** source_debug_extension_ptr); }; } // namespace openjdkjvmti diff --git a/test/992-source-data/expected.txt b/test/992-source-data/expected.txt new file mode 100644 index 0000000000..480d8a4fe7 --- /dev/null +++ b/test/992-source-data/expected.txt @@ -0,0 +1,10 @@ +class art.Test992 is defined in file "Test992.java" +class art.Test992$Target1 is defined in file "Test992.java" +class art.Test2 is defined in file "Test2.java" +int does not have a known source file because java.lang.RuntimeException: JVMTI_ERROR_ABSENT_INFORMATION +class java.lang.Integer is defined in file "Integer.java" +class java.lang.Object is defined in file "Object.java" +interface java.lang.Runnable is defined in file "Runnable.java" +class [Ljava.lang.Object; does not have a known source file because java.lang.RuntimeException: JVMTI_ERROR_ABSENT_INFORMATION +class [I does not have a known source file because java.lang.RuntimeException: JVMTI_ERROR_ABSENT_INFORMATION +null does not have a known source file because java.lang.RuntimeException: JVMTI_ERROR_INVALID_CLASS diff --git a/test/992-source-data/info.txt b/test/992-source-data/info.txt new file mode 100644 index 0000000000..5d487a4bde --- /dev/null +++ b/test/992-source-data/info.txt @@ -0,0 +1 @@ +Tests that we can get the source file of a class from JVMTI. diff --git a/test/992-source-data/run b/test/992-source-data/run new file mode 100755 index 0000000000..e92b873956 --- /dev/null +++ b/test/992-source-data/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 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. + +./default-run "$@" --jvmti diff --git a/test/992-source-data/source_file.cc b/test/992-source-data/source_file.cc new file mode 100644 index 0000000000..3e8989e403 --- /dev/null +++ b/test/992-source-data/source_file.cc @@ -0,0 +1,53 @@ +/* + * 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 <inttypes.h> +#include <memory> +#include <stdio.h> + +#include "android-base/logging.h" +#include "android-base/stringprintf.h" + +#include "jni.h" +#include "jvmti.h" +#include "scoped_local_ref.h" + +// Test infrastructure +#include "jni_binder.h" +#include "jni_helper.h" +#include "jvmti_helper.h" +#include "test_env.h" +#include "ti_macros.h" + +namespace art { +namespace Test992SourceFile { + +extern "C" JNIEXPORT +jstring JNICALL Java_art_Test992_getSourceFileName(JNIEnv* env, + jclass klass ATTRIBUTE_UNUSED, + jclass target) { + char* file = nullptr; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetSourceFileName(target, &file))) { + return nullptr; + } + jstring ret = env->NewStringUTF(file); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(file)); + return ret; +} + +} // namespace Test992SourceFile +} // namespace art + diff --git a/test/992-source-data/src/Main.java b/test/992-source-data/src/Main.java new file mode 100644 index 0000000000..31106f41fb --- /dev/null +++ b/test/992-source-data/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.Test992.run(); + } +} diff --git a/test/992-source-data/src/art/Test2.java b/test/992-source-data/src/art/Test2.java new file mode 100644 index 0000000000..dbb1089c5e --- /dev/null +++ b/test/992-source-data/src/art/Test2.java @@ -0,0 +1,19 @@ +/* + * 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. + */ + +package art; + +public class Test2 {} diff --git a/test/992-source-data/src/art/Test992.java b/test/992-source-data/src/art/Test992.java new file mode 100644 index 0000000000..db6ea73856 --- /dev/null +++ b/test/992-source-data/src/art/Test992.java @@ -0,0 +1,47 @@ +/* + * 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. + */ + +package art; + +import java.util.Base64; + +public class Test992 { + + static class Target1 { } + + public static void run() { + doTest(Test992.class); + doTest(Target1.class); + doTest(Test2.class); + doTest(Integer.TYPE); + doTest(Integer.class); + doTest(Object.class); + doTest(Runnable.class); + doTest(new Object[0].getClass()); + doTest(new int[0].getClass()); + doTest(null); + } + + public static void doTest(Class<?> k) { + try { + System.out.println(k + " is defined in file \"" + getSourceFileName(k) + "\""); + } catch (Exception e) { + System.out.println(k + " does not have a known source file because " + e); + } + } + + public static native String getSourceFileName(Class<?> k) throws Exception; +} diff --git a/test/Android.bp b/test/Android.bp index 8da415f0f6..9e6ecffe79 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -280,6 +280,7 @@ art_cc_defaults { "987-agent-bind/agent_bind.cc", "989-method-trace-throw/method_trace.cc", "991-field-trace-2/field_trace.cc", + "992-source-data/source_file.cc", ], shared_libs: [ "libbase", diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc index 8d4cc89139..40fcc4f11d 100644 --- a/test/ti-stress/stress.cc +++ b/test/ti-stress/stress.cc @@ -128,12 +128,16 @@ class ScopedClassInfo { : jvmtienv_(jvmtienv), class_(c), name_(nullptr), - generic_(nullptr) {} + generic_(nullptr), + file_(nullptr), + debug_ext_(nullptr) {} ~ScopedClassInfo() { if (class_ != nullptr) { jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_)); jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(generic_)); + jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(file_)); + jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(debug_ext_)); } } @@ -143,7 +147,13 @@ class ScopedClassInfo { generic_ = const_cast<char*>("<NONE>"); return true; } else { - return jvmtienv_->GetClassSignature(class_, &name_, &generic_) == JVMTI_ERROR_NONE; + jvmtiError ret1 = jvmtienv_->GetSourceFileName(class_, &file_); + jvmtiError ret2 = jvmtienv_->GetSourceDebugExtension(class_, &debug_ext_); + return jvmtienv_->GetClassSignature(class_, &name_, &generic_) == JVMTI_ERROR_NONE && + ret1 != JVMTI_ERROR_MUST_POSSESS_CAPABILITY && + ret1 != JVMTI_ERROR_INVALID_CLASS && + ret2 != JVMTI_ERROR_MUST_POSSESS_CAPABILITY && + ret2 != JVMTI_ERROR_INVALID_CLASS; } } @@ -156,12 +166,28 @@ class ScopedClassInfo { const char* GetGeneric() const { return generic_; } + const char* GetSourceDebugExtension() const { + if (debug_ext_ == nullptr) { + return "<UNKNOWN_SOURCE_DEBUG_EXTENSION>"; + } else { + return debug_ext_; + } + } + const char* GetSourceFileName() const { + if (file_ == nullptr) { + return "<UNKNOWN_FILE>"; + } else { + return file_; + } + } private: jvmtiEnv* jvmtienv_; jclass class_; char* name_; char* generic_; + char* file_; + char* debug_ext_; }; class ScopedMethodInfo { @@ -174,7 +200,8 @@ class ScopedMethodInfo { class_info_(nullptr), name_(nullptr), signature_(nullptr), - generic_(nullptr) {} + generic_(nullptr), + first_line_(-1) {} ~ScopedMethodInfo() { env_->DeleteLocalRef(declaring_class_); @@ -188,6 +215,18 @@ class ScopedMethodInfo { return false; } class_info_.reset(new ScopedClassInfo(jvmtienv_, declaring_class_)); + jint nlines; + jvmtiLineNumberEntry* lines; + jvmtiError err = jvmtienv_->GetLineNumberTable(method_, &nlines, &lines); + if (err == JVMTI_ERROR_NONE) { + if (nlines > 0) { + first_line_ = lines[0].line_number; + } + jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(lines)); + } else if (err != JVMTI_ERROR_ABSENT_INFORMATION && + err != JVMTI_ERROR_NATIVE_METHOD) { + return false; + } return class_info_->Init() && (jvmtienv_->GetMethodName(method_, &name_, &signature_, &generic_) == JVMTI_ERROR_NONE); } @@ -212,6 +251,10 @@ class ScopedMethodInfo { return generic_; } + jint GetFirstLine() const { + return first_line_; + } + private: jvmtiEnv* jvmtienv_; JNIEnv* env_; @@ -221,6 +264,7 @@ class ScopedMethodInfo { char* name_; char* signature_; char* generic_; + jint first_line_; friend std::ostream& operator<<(std::ostream &os, ScopedMethodInfo const& m); }; @@ -295,7 +339,9 @@ std::ostream& operator<<(std::ostream &os, const ScopedMethodInfo* m) { } std::ostream& operator<<(std::ostream &os, ScopedMethodInfo const& m) { - return os << m.GetDeclaringClassInfo().GetName() << "->" << m.GetName() << m.GetSignature(); + return os << m.GetDeclaringClassInfo().GetName() << "->" << m.GetName() << m.GetSignature() + << " (source: " << m.GetDeclaringClassInfo().GetSourceFileName() << ":" + << m.GetFirstLine() << ")"; } static void doJvmtiMethodBind(jvmtiEnv* jvmtienv, |