summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alex Light <allight@google.com> 2017-07-26 10:30:38 -0700
committer Alex Light <allight@google.com> 2017-07-26 10:47:21 -0700
commitce68cc6023ef929578bcff250dff150edd0ca5e9 (patch)
treecc07a3cc87c37d56b4b9d1ce9d5653220886bfd3
parentaed968d130d62d23cfe7f6051ca72c6a84fe1510 (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.cc8
-rw-r--r--runtime/openjdkjvmti/art_jvmti.h6
-rw-r--r--runtime/openjdkjvmti/ti_method.cc100
-rw-r--r--runtime/openjdkjvmti/ti_method.h4
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