diff options
author | 2017-07-26 10:30:38 -0700 | |
---|---|---|
committer | 2017-07-26 10:47:21 -0700 | |
commit | ce68cc6023ef929578bcff250dff150edd0ca5e9 (patch) | |
tree | cc07a3cc87c37d56b4b9d1ce9d5653220886bfd3 | |
parent | aed968d130d62d23cfe7f6051ca72c6a84fe1510 (diff) |
JVMTI GetLocalVariableTable function support
Adds support for the JVMTI GetLocalVariableTable function. Also
enables the can_access_local_variables capability. Other functions
behind this capability will be added in next CL.
Tests follow in later CL.
Test: ./test.py --host -j50
Bug: 34414073
Bug: 36892980
Change-Id: I5a6746d3ae58f1c2b3c79c578f2622ead71d8990
-rw-r--r-- | runtime/openjdkjvmti/OpenjdkJvmTi.cc | 8 | ||||
-rw-r--r-- | runtime/openjdkjvmti/art_jvmti.h | 6 | ||||
-rw-r--r-- | runtime/openjdkjvmti/ti_method.cc | 100 | ||||
-rw-r--r-- | runtime/openjdkjvmti/ti_method.h | 4 |
4 files changed, 113 insertions, 5 deletions
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 3c1311b18a..aff2ea4c57 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -904,12 +904,12 @@ class JvmtiFunctions { } static jvmtiError GetLocalVariableTable(jvmtiEnv* env, - jmethodID method ATTRIBUTE_UNUSED, - jint* entry_count_ptr ATTRIBUTE_UNUSED, - jvmtiLocalVariableEntry** table_ptr ATTRIBUTE_UNUSED) { + jmethodID method, + jint* entry_count_ptr, + jvmtiLocalVariableEntry** table_ptr) { ENSURE_VALID_ENV(env); ENSURE_HAS_CAP(env, can_access_local_variables); - return ERR(NOT_IMPLEMENTED); + return MethodUtil::GetLocalVariableTable(env, method, entry_count_ptr, table_ptr); } static jvmtiError GetBytecodes(jvmtiEnv* env, diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h index 4d5bb95f2c..a2259c74cf 100644 --- a/runtime/openjdkjvmti/art_jvmti.h +++ b/runtime/openjdkjvmti/art_jvmti.h @@ -204,6 +204,10 @@ static inline jvmtiError CopyDataIntoJvmtiBuffer(ArtJvmTiEnv* env, ALWAYS_INLINE static inline JvmtiUniquePtr<char[]> CopyString(jvmtiEnv* env, const char* src, jvmtiError* error) { + if (src == nullptr) { + JvmtiUniquePtr<char[]> ret = AllocJvmtiUniquePtr<char[]>(env, 0, error); + return ret; + } size_t len = strlen(src) + 1; JvmtiUniquePtr<char[]> ret = AllocJvmtiUniquePtr<char[]>(env, len, error); if (ret != nullptr) { @@ -227,7 +231,7 @@ const jvmtiCapabilities kPotentialCapabilities = { .can_get_source_file_name = 1, .can_get_line_numbers = 1, .can_get_source_debug_extension = 1, - .can_access_local_variables = 0, + .can_access_local_variables = 1, .can_maintain_original_method_order = 0, .can_generate_single_step_events = 1, .can_generate_exception_events = 0, diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc index ab434d7d9a..ab25319732 100644 --- a/runtime/openjdkjvmti/ti_method.cc +++ b/runtime/openjdkjvmti/ti_method.cc @@ -159,6 +159,106 @@ jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED, return ERR(NONE); } +jvmtiError MethodUtil::GetLocalVariableTable(jvmtiEnv* env, + jmethodID method, + jint* entry_count_ptr, + jvmtiLocalVariableEntry** table_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 (entry_count_ptr == nullptr || table_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + art::ScopedObjectAccess soa(art::Thread::Current()); + const art::DexFile* dex_file = art_method->GetDexFile(); + const art::DexFile::CodeItem* code_item = art_method->GetCodeItem(); + // TODO code_item == nullptr means that the method is abstract (or native, but we check that + // earlier). We should check what is returned by the RI in this situation since it's not clear + // what the appropriate return value is from the spec. + if (dex_file == nullptr || code_item == nullptr) { + return ERR(ABSENT_INFORMATION); + } + + struct LocalVariableContext { + explicit LocalVariableContext(jvmtiEnv* jenv) : env_(jenv), variables_(), err_(OK) {} + + static void Callback(void* raw_ctx, const art::DexFile::LocalInfo& entry) { + reinterpret_cast<LocalVariableContext*>(raw_ctx)->Insert(entry); + } + + void Insert(const art::DexFile::LocalInfo& entry) { + if (err_ != OK) { + return; + } + JvmtiUniquePtr<char[]> name_str = CopyString(env_, entry.name_, &err_); + if (err_ != OK) { + return; + } + JvmtiUniquePtr<char[]> sig_str = CopyString(env_, entry.descriptor_, &err_); + if (err_ != OK) { + return; + } + JvmtiUniquePtr<char[]> generic_sig_str = CopyString(env_, entry.signature_, &err_); + if (err_ != OK) { + return; + } + variables_.push_back({ + .start_location = static_cast<jlocation>(entry.start_address_), + .length = static_cast<jint>(entry.end_address_ - entry.start_address_), + .name = name_str.release(), + .signature = sig_str.release(), + .generic_signature = generic_sig_str.release(), + .slot = entry.reg_, + }); + } + + jvmtiError Release(jint* out_entry_count_ptr, jvmtiLocalVariableEntry** out_table_ptr) { + jlong table_size = sizeof(jvmtiLocalVariableEntry) * variables_.size(); + if (err_ != OK || + (err_ = env_->Allocate(table_size, + reinterpret_cast<unsigned char**>(out_table_ptr))) != OK) { + Cleanup(); + return err_; + } else { + *out_entry_count_ptr = variables_.size(); + memcpy(*out_table_ptr, variables_.data(), table_size); + return OK; + } + } + + void Cleanup() { + for (jvmtiLocalVariableEntry& e : variables_) { + env_->Deallocate(reinterpret_cast<unsigned char*>(e.name)); + env_->Deallocate(reinterpret_cast<unsigned char*>(e.signature)); + env_->Deallocate(reinterpret_cast<unsigned char*>(e.generic_signature)); + } + } + + jvmtiEnv* env_; + std::vector<jvmtiLocalVariableEntry> variables_; + jvmtiError err_; + }; + + LocalVariableContext context(env); + if (!dex_file->DecodeDebugLocalInfo(code_item, + art_method->IsStatic(), + art_method->GetDexMethodIndex(), + LocalVariableContext::Callback, + &context)) { + // Something went wrong with decoding the debug information. It might as well not be there. + return ERR(ABSENT_INFORMATION); + } else { + return context.Release(entry_count_ptr, table_ptr); + } +} + jvmtiError MethodUtil::GetMaxLocals(jvmtiEnv* env ATTRIBUTE_UNUSED, jmethodID method, jint* max_ptr) { diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h index d95a81b63b..f506594c64 100644 --- a/runtime/openjdkjvmti/ti_method.h +++ b/runtime/openjdkjvmti/ti_method.h @@ -80,6 +80,10 @@ class MethodUtil { static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr); static jvmtiError IsMethodObsolete(jvmtiEnv* env, jmethodID method, jboolean* is_obsolete_ptr); static jvmtiError IsMethodSynthetic(jvmtiEnv* env, jmethodID method, jboolean* is_synthetic_ptr); + static jvmtiError GetLocalVariableTable(jvmtiEnv* env, + jmethodID method, + jint* entry_count_ptr, + jvmtiLocalVariableEntry** table_ptr); }; } // namespace openjdkjvmti |