summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alex Light <allight@google.com> 2018-10-22 16:40:01 +0000
committer Gerrit Code Review <noreply-gerritcodereview@google.com> 2018-10-22 16:40:01 +0000
commit7f1fe5d78af99e19ffebc1e5c03f095ea6301ea2 (patch)
treeae7ba62144d778fe87a9e23ccc208a233e2df628
parent1cb95b1f532e795fa7527328cae7cf46b879e426 (diff)
parentae45cbb288ddcaab3fd518cd00b9e6c1604ec4e1 (diff)
Merge "Add a get_last_error_message JVMTI extension."
-rw-r--r--openjdkjvmti/Android.bp1
-rw-r--r--openjdkjvmti/OpenjdkJvmTi.cc8
-rw-r--r--openjdkjvmti/art_jvmti.h4
-rw-r--r--openjdkjvmti/ti_class.cc5
-rw-r--r--openjdkjvmti/ti_ddms.cc3
-rw-r--r--openjdkjvmti/ti_extension.cc40
-rw-r--r--openjdkjvmti/ti_logging.cc71
-rw-r--r--openjdkjvmti/ti_logging.h102
-rw-r--r--openjdkjvmti/ti_search.cc6
-rw-r--r--openjdkjvmti/ti_stack.cc10
-rw-r--r--test/1957-error-ext/expected.txt4
-rw-r--r--test/1957-error-ext/info.txt1
-rw-r--r--test/1957-error-ext/lasterror.cc112
-rwxr-xr-xtest/1957-error-ext/run18
-rw-r--r--test/1957-error-ext/src/Main.java21
-rw-r--r--test/1957-error-ext/src/art/Redefinition.java91
-rw-r--r--test/1957-error-ext/src/art/Test1957.java86
-rw-r--r--test/Android.bp1
-rw-r--r--test/knownfailures.json3
19 files changed, 574 insertions, 13 deletions
diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp
index d8902d60d1..7621d48f6e 100644
--- a/openjdkjvmti/Android.bp
+++ b/openjdkjvmti/Android.bp
@@ -41,6 +41,7 @@ cc_defaults {
"ti_field.cc",
"ti_heap.cc",
"ti_jni.cc",
+ "ti_logging.cc",
"ti_method.cc",
"ti_monitor.cc",
"ti_object.cc",
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index 4bc33b68ca..a2fabbfe83 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -58,6 +58,7 @@
#include "ti_field.h"
#include "ti_heap.h"
#include "ti_jni.h"
+#include "ti_logging.h"
#include "ti_method.h"
#include "ti_monitor.h"
#include "ti_object.h"
@@ -787,7 +788,7 @@ class JvmtiFunctions {
classes,
&error_msg);
if (res != OK) {
- LOG(WARNING) << "FAILURE TO RETRANFORM " << error_msg;
+ JVMTI_LOG(WARNING, env) << "FAILURE TO RETRANFORM " << error_msg;
}
return res;
}
@@ -806,7 +807,7 @@ class JvmtiFunctions {
class_definitions,
&error_msg);
if (res != OK) {
- LOG(WARNING) << "FAILURE TO REDEFINE " << error_msg;
+ JVMTI_LOG(WARNING, env) << "FAILURE TO REDEFINE " << error_msg;
}
return res;
}
@@ -1489,7 +1490,8 @@ ArtJvmTiEnv::ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler, j
local_data(nullptr),
ti_version(version),
capabilities(),
- event_info_mutex_("jvmtiEnv_EventInfoMutex") {
+ event_info_mutex_("jvmtiEnv_EventInfoMutex"),
+ last_error_mutex_("jvmtiEnv_LastErrorMutex", art::LockLevel::kGenericBottomLock) {
object_tag_table = std::unique_ptr<ObjectTagTable>(new ObjectTagTable(event_handler, this));
functions = &gJvmtiInterface;
}
diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
index 1218e3b9a7..7433e54eda 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -102,6 +102,10 @@ struct ArtJvmTiEnv : public jvmtiEnv {
// RW lock to protect access to all of the event data.
art::ReaderWriterMutex event_info_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ std::string last_error_ GUARDED_BY(last_error_mutex_);
+ // Lock to touch the last-error-message.
+ art::Mutex last_error_mutex_ BOTTOM_MUTEX_ACQUIRED_AFTER;
+
ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler, jint ti_version);
static ArtJvmTiEnv* AsArtJvmTiEnv(jvmtiEnv* env) {
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index 3d33487970..e4692707f4 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -73,6 +73,7 @@
#include "thread_list.h"
#include "ti_class_definition.h"
#include "ti_class_loader-inl.h"
+#include "ti_logging.h"
#include "ti_phase.h"
#include "ti_redefine.h"
#include "transform.h"
@@ -932,8 +933,8 @@ jvmtiError ClassUtil::GetClassLoaderClassDescriptors(jvmtiEnv* env,
return ERR(ILLEGAL_ARGUMENT);
} else if (!jnienv->IsInstanceOf(loader,
art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
- LOG(ERROR) << "GetClassLoaderClassDescriptors is only implemented for BootClassPath and "
- << "dalvik.system.BaseDexClassLoader class loaders";
+ JVMTI_LOG(ERROR, env) << "GetClassLoaderClassDescriptors is only implemented for "
+ << "BootClassPath and dalvik.system.BaseDexClassLoader class loaders";
// TODO Possibly return OK With no classes would be better since these ones cannot have any
// real classes associated with them.
return ERR(NOT_IMPLEMENTED);
diff --git a/openjdkjvmti/ti_ddms.cc b/openjdkjvmti/ti_ddms.cc
index bf063faf7b..9de5cbc3ea 100644
--- a/openjdkjvmti/ti_ddms.cc
+++ b/openjdkjvmti/ti_ddms.cc
@@ -39,6 +39,7 @@
#include "debugger.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
+#include "ti_logging.h"
namespace openjdkjvmti {
@@ -69,7 +70,7 @@ jvmtiError DDMSUtil::HandleChunk(jvmtiEnv* env,
data_arr,
/*out*/reinterpret_cast<uint32_t*>(type_out),
/*out*/&out_data)) {
- LOG(WARNING) << "Something went wrong with handling the ddm chunk.";
+ JVMTI_LOG(WARNING, env) << "Something went wrong with handling the ddm chunk.";
return ERR(INTERNAL);
} else {
jvmtiError error = OK;
diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc
index c628a325eb..5d398844b2 100644
--- a/openjdkjvmti/ti_extension.cc
+++ b/openjdkjvmti/ti_extension.cc
@@ -39,7 +39,9 @@
#include "ti_class.h"
#include "ti_ddms.h"
#include "ti_heap.h"
+#include "ti_logging.h"
#include "ti_monitor.h"
+
#include "thread-inl.h"
namespace openjdkjvmti {
@@ -272,6 +274,44 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env,
if (error != ERR(NONE)) {
return error;
}
+
+ // GetLastError extension
+ error = add_extension(
+ reinterpret_cast<jvmtiExtensionFunction>(LogUtil::GetLastError),
+ "com.android.art.misc.get_last_error_message",
+ "In some cases the jvmti plugin will log data about errors to the android logcat. These can"
+ " be useful to tools so we make (some) of the messages available here as well. This will"
+ " fill the given 'msg' buffer with the last non-fatal message associated with this"
+ " jvmti-env. Note this is best-effort only, not all log messages will be accessible through"
+ " this API. This will return the last error-message from all threads. Care should be taken"
+ " interpreting the return value when used with a multi-threaded program. The error message"
+ " will only be cleared by a call to 'com.android.art.misc.clear_last_error_message' and will"
+ " not be cleared by intervening successful calls. If no (tracked) error message has been"
+ " sent since the last call to clear_last_error_message this API will return"
+ " JVMTI_ERROR_ABSENT_INFORMATION. Not all failures will cause an error message to be"
+ " recorded.",
+ {
+ { "msg", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_CCHAR, false },
+ },
+ {
+ ERR(NULL_POINTER),
+ ERR(ABSENT_INFORMATION),
+ });
+ if (error != ERR(NONE)) {
+ return error;
+ }
+
+ // ClearLastError extension
+ error = add_extension(
+ reinterpret_cast<jvmtiExtensionFunction>(LogUtil::ClearLastError),
+ "com.android.art.misc.clear_last_error_message",
+ "Clears the error message returned by 'com.android.art.misc.get_last_error_message'.",
+ { },
+ { });
+ if (error != ERR(NONE)) {
+ return error;
+ }
+
// Copy into output buffer.
*extension_count_ptr = ext_vector.size();
diff --git a/openjdkjvmti/ti_logging.cc b/openjdkjvmti/ti_logging.cc
new file mode 100644
index 0000000000..1d24d3b6b6
--- /dev/null
+++ b/openjdkjvmti/ti_logging.cc
@@ -0,0 +1,71 @@
+/* Copyright (C) 2018 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_logging.h"
+
+#include "art_jvmti.h"
+
+#include "base/mutex.h"
+#include "thread-current-inl.h"
+
+namespace openjdkjvmti {
+
+jvmtiError LogUtil::GetLastError(jvmtiEnv* env, char** data) {
+ if (env == nullptr || data == nullptr) {
+ return ERR(INVALID_ENVIRONMENT);
+ }
+ ArtJvmTiEnv* tienv = ArtJvmTiEnv::AsArtJvmTiEnv(env);
+ art::MutexLock mu(art::Thread::Current(), tienv->last_error_mutex_);
+ if (tienv->last_error_.empty()) {
+ return ERR(ABSENT_INFORMATION);
+ }
+ char* out;
+ jvmtiError err = tienv->Allocate(tienv->last_error_.size() + 1,
+ reinterpret_cast<unsigned char**>(&out));
+ if (err != OK) {
+ return err;
+ }
+ strcpy(out, tienv->last_error_.c_str());
+ *data = out;
+ return OK;
+}
+
+jvmtiError LogUtil::ClearLastError(jvmtiEnv* env) {
+ if (env == nullptr) {
+ return ERR(INVALID_ENVIRONMENT);
+ }
+ ArtJvmTiEnv* tienv = ArtJvmTiEnv::AsArtJvmTiEnv(env);
+ art::MutexLock mu(art::Thread::Current(), tienv->last_error_mutex_);
+ tienv->last_error_.clear();
+ return OK;
+}
+
+} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_logging.h b/openjdkjvmti/ti_logging.h
new file mode 100644
index 0000000000..31b51bb126
--- /dev/null
+++ b/openjdkjvmti/ti_logging.h
@@ -0,0 +1,102 @@
+/* Copyright (C) 2018 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_OPENJDKJVMTI_TI_LOGGING_H_
+#define ART_OPENJDKJVMTI_TI_LOGGING_H_
+
+#include "art_jvmti.h"
+
+#include <ostream>
+#include <sstream>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "thread-current-inl.h"
+
+namespace openjdkjvmti {
+
+// NB Uses implementation details of android-base/logging.h.
+#define JVMTI_LOG(severity, env) \
+ ::openjdkjvmti::JvmtiLogMessage((env), \
+ __FILE__, \
+ __LINE__, \
+ ::android::base::DEFAULT, \
+ SEVERITY_LAMBDA(severity), \
+ _LOG_TAG_INTERNAL, \
+ -1)
+
+class JvmtiLogMessage {
+ public:
+ JvmtiLogMessage(jvmtiEnv* env,
+ const char* file,
+ unsigned int line,
+ android::base::LogId id,
+ android::base::LogSeverity severity,
+ const char* tag,
+ int error)
+ : env_(ArtJvmTiEnv::AsArtJvmTiEnv(env)),
+ real_log_(file, line, id, severity, tag, error),
+ real_log_stream_(real_log_.stream()) {
+ DCHECK(env_ != nullptr);
+ }
+
+ ~JvmtiLogMessage() {
+ art::MutexLock mu(art::Thread::Current(), env_->last_error_mutex_);
+ env_->last_error_ = save_stream_.str();
+ }
+
+ template<typename T>
+ JvmtiLogMessage& operator<<(T t) {
+ (real_log_stream_ << t);
+ (save_stream_ << t);
+ return *this;
+ }
+
+ private:
+ ArtJvmTiEnv* env_;
+ android::base::LogMessage real_log_;
+ // Lifetime of real_log_stream_ is lifetime of real_log_.
+ std::ostream& real_log_stream_;
+ std::ostringstream save_stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(JvmtiLogMessage);
+};
+
+class LogUtil {
+ public:
+ static jvmtiError ClearLastError(jvmtiEnv* env);
+ static jvmtiError GetLastError(jvmtiEnv* env, char** data);
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_OPENJDKJVMTI_TI_LOGGING_H_
diff --git a/openjdkjvmti/ti_search.cc b/openjdkjvmti/ti_search.cc
index 427869e798..2187825746 100644
--- a/openjdkjvmti/ti_search.cc
+++ b/openjdkjvmti/ti_search.cc
@@ -52,6 +52,7 @@
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
#include "thread_list.h"
+#include "ti_logging.h"
#include "ti_phase.h"
#include "well_known_classes.h"
@@ -213,7 +214,7 @@ void SearchUtil::Unregister() {
runtime->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&gSearchCallback);
}
-jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env ATTRIBUTE_UNUSED,
+jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env,
const char* segment) {
art::Runtime* current = art::Runtime::Current();
if (current == nullptr) {
@@ -235,7 +236,8 @@ jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env ATTRIBUTE_U
/* verify_checksum= */ true,
&error_msg,
&dex_files)) {
- LOG(WARNING) << "Could not open " << segment << " for boot classpath extension: " << error_msg;
+ JVMTI_LOG(WARNING, env) << "Could not open " << segment << " for boot classpath extension: "
+ << error_msg;
return ERR(ILLEGAL_ARGUMENT);
}
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
index 1279f3bde5..5de4a81f5e 100644
--- a/openjdkjvmti/ti_stack.cc
+++ b/openjdkjvmti/ti_stack.cc
@@ -57,6 +57,7 @@
#include "nativehelper/scoped_local_ref.h"
#include "scoped_thread_state_change-inl.h"
#include "stack.h"
+#include "ti_logging.h"
#include "ti_thread.h"
#include "thread-current-inl.h"
#include "thread_list.h"
@@ -1097,7 +1098,7 @@ jvmtiError StackUtil::NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth)
} while (true);
}
-jvmtiError StackUtil::PopFrame(jvmtiEnv* env ATTRIBUTE_UNUSED, jthread thread) {
+jvmtiError StackUtil::PopFrame(jvmtiEnv* env, jthread thread) {
art::Thread* self = art::Thread::Current();
art::Thread* target;
do {
@@ -1131,9 +1132,10 @@ jvmtiError StackUtil::PopFrame(jvmtiEnv* env ATTRIBUTE_UNUSED, jthread thread) {
tls_data->disable_pop_frame_depth != JvmtiGlobalTLSData::kNoDisallowedPopFrame &&
tls_data->disable_pop_frame_depth == art::StackVisitor::ComputeNumFrames(target,
kWalkKind)) {
- LOG(WARNING) << "Disallowing frame pop due to in-progress class-load/prepare. Frame at depth "
- << tls_data->disable_pop_frame_depth << " was marked as un-poppable by the "
- << "jvmti plugin. See b/117615146 for more information.";
+ JVMTI_LOG(WARNING, env) << "Disallowing frame pop due to in-progress class-load/prepare. "
+ << "Frame at depth " << tls_data->disable_pop_frame_depth << " was "
+ << "marked as un-poppable by the jvmti plugin. See b/117615146 for "
+ << "more information.";
return ERR(OPAQUE_FRAME);
}
// We hold the user_code_suspension_lock_ so the target thread is staying suspended until we are
diff --git a/test/1957-error-ext/expected.txt b/test/1957-error-ext/expected.txt
new file mode 100644
index 0000000000..bfe7033c67
--- /dev/null
+++ b/test/1957-error-ext/expected.txt
@@ -0,0 +1,4 @@
+LastError is: <call returned error: class java.lang.RuntimeException: JVMTI_ERROR_ABSENT_INFORMATION>
+Got class java.lang.Exception: Failed to redefine class <Lart/Test1957$Transform;> due to JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED
+LastError is: FAILURE TO REDEFINE Unable to perform redefinition of 'Lart/Test1957$Transform;': Total number of declared methods changed from 2 to 1
+LastError is: <call returned error: class java.lang.RuntimeException: JVMTI_ERROR_ABSENT_INFORMATION>
diff --git a/test/1957-error-ext/info.txt b/test/1957-error-ext/info.txt
new file mode 100644
index 0000000000..ef772d9ae8
--- /dev/null
+++ b/test/1957-error-ext/info.txt
@@ -0,0 +1 @@
+Test for get_last_error_message extension function.
diff --git a/test/1957-error-ext/lasterror.cc b/test/1957-error-ext/lasterror.cc
new file mode 100644
index 0000000000..5aa3fbe9fb
--- /dev/null
+++ b/test/1957-error-ext/lasterror.cc
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2018 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 <cstdio>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
+#include "jni.h"
+#include "jvmti.h"
+#include "scoped_local_ref.h"
+#include "scoped_utf_chars.h"
+
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "ti_macros.h"
+
+namespace art {
+namespace Test1957ErrorExt {
+
+using GetLastError = jvmtiError(*)(jvmtiEnv* env, char** msg);
+using ClearLastError = jvmtiError(*)(jvmtiEnv* env);
+
+template <typename T>
+static void Dealloc(T* t) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(t));
+}
+
+template <typename T, typename ...Rest>
+static void Dealloc(T* t, Rest... rs) {
+ Dealloc(t);
+ Dealloc(rs...);
+}
+
+static void DeallocParams(jvmtiParamInfo* params, jint n_params) {
+ for (jint i = 0; i < n_params; i++) {
+ Dealloc(params[i].name);
+ }
+}
+
+static jvmtiExtensionFunction FindExtensionMethod(JNIEnv* env, const std::string& name) {
+ jint n_ext;
+ jvmtiExtensionFunctionInfo* infos;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionFunctions(&n_ext, &infos))) {
+ return nullptr;
+ }
+ jvmtiExtensionFunction res = nullptr;
+ for (jint i = 0; i < n_ext; i++) {
+ jvmtiExtensionFunctionInfo* cur_info = &infos[i];
+ if (strcmp(name.c_str(), cur_info->id) == 0) {
+ res = cur_info->func;
+ }
+ // Cleanup the cur_info
+ DeallocParams(cur_info->params, cur_info->param_count);
+ Dealloc(cur_info->id, cur_info->short_description, cur_info->params, cur_info->errors);
+ }
+ // Cleanup the array.
+ Dealloc(infos);
+ if (res == nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), (name + " extensions not found").c_str());
+ return nullptr;
+ }
+ return res;
+}
+
+extern "C" JNIEXPORT
+jstring JNICALL Java_art_Test1957_getLastError(JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ GetLastError get_last_error = reinterpret_cast<GetLastError>(
+ FindExtensionMethod(env, "com.android.art.misc.get_last_error_message"));
+ if (get_last_error == nullptr) {
+ return nullptr;
+ }
+ char* msg;
+ if (JvmtiErrorToException(env, jvmti_env, get_last_error(jvmti_env, &msg))) {
+ return nullptr;
+ }
+
+ return env->NewStringUTF(msg);
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1957_clearLastError(JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ ClearLastError clear_last_error = reinterpret_cast<ClearLastError>(
+ FindExtensionMethod(env, "com.android.art.misc.clear_last_error_message"));
+ if (clear_last_error == nullptr) {
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, clear_last_error(jvmti_env));
+}
+
+} // namespace Test1957ErrorExt
+} // namespace art
diff --git a/test/1957-error-ext/run b/test/1957-error-ext/run
new file mode 100755
index 0000000000..8be0ed4aed
--- /dev/null
+++ b/test/1957-error-ext/run
@@ -0,0 +1,18 @@
+#!/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/1957-error-ext/src/Main.java b/test/1957-error-ext/src/Main.java
new file mode 100644
index 0000000000..7e5e075fd1
--- /dev/null
+++ b/test/1957-error-ext/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.Test1957.run();
+ }
+}
diff --git a/test/1957-error-ext/src/art/Redefinition.java b/test/1957-error-ext/src/art/Redefinition.java
new file mode 100644
index 0000000000..56d2938a01
--- /dev/null
+++ b/test/1957-error-ext/src/art/Redefinition.java
@@ -0,0 +1,91 @@
+/*
+ * 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.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+ public static final class CommonClassDefinition {
+ public final Class<?> target;
+ public final byte[] class_file_bytes;
+ public final byte[] dex_file_bytes;
+
+ public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+ this.target = target;
+ this.class_file_bytes = class_file_bytes;
+ this.dex_file_bytes = dex_file_bytes;
+ }
+ }
+
+ // A set of possible test configurations. Test should set this if they need to.
+ // This must be kept in sync with the defines in ti-agent/common_helper.cc
+ public static enum Config {
+ COMMON_REDEFINE(0),
+ COMMON_RETRANSFORM(1),
+ COMMON_TRANSFORM(2);
+
+ private final int val;
+ private Config(int val) {
+ this.val = val;
+ }
+ }
+
+ public static void setTestConfiguration(Config type) {
+ nativeSetTestConfiguration(type.val);
+ }
+
+ private static native void nativeSetTestConfiguration(int type);
+
+ // Transforms the class
+ public static native void doCommonClassRedefinition(Class<?> target,
+ byte[] classfile,
+ byte[] dexfile);
+
+ public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+ ArrayList<Class<?>> classes = new ArrayList<>();
+ ArrayList<byte[]> class_files = new ArrayList<>();
+ ArrayList<byte[]> dex_files = new ArrayList<>();
+
+ for (CommonClassDefinition d : defs) {
+ classes.add(d.target);
+ class_files.add(d.class_file_bytes);
+ dex_files.add(d.dex_file_bytes);
+ }
+ doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+ class_files.toArray(new byte[0][]),
+ dex_files.toArray(new byte[0][]));
+ }
+
+ public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+ for (CommonClassDefinition d : defs) {
+ addCommonTransformationResult(d.target.getCanonicalName(),
+ d.class_file_bytes,
+ d.dex_file_bytes);
+ }
+ }
+
+ public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+ byte[][] classfiles,
+ byte[][] dexfiles);
+ public static native void doCommonClassRetransformation(Class<?>... target);
+ public static native void setPopRetransformations(boolean pop);
+ public static native void popTransformationFor(String name);
+ public static native void enableCommonRetransformation(boolean enable);
+ public static native void addCommonTransformationResult(String target_name,
+ byte[] class_bytes,
+ byte[] dex_bytes);
+}
diff --git a/test/1957-error-ext/src/art/Test1957.java b/test/1957-error-ext/src/art/Test1957.java
new file mode 100644
index 0000000000..ffb68be478
--- /dev/null
+++ b/test/1957-error-ext/src/art/Test1957.java
@@ -0,0 +1,86 @@
+/*
+ * 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.util.Base64;
+public class Test1957 {
+
+ static class Transform {
+ public void sayHi() {
+ // Use lower 'h' to make sure the string will have a different string id
+ // than the transformation (the transformation code is the same except
+ // the actual printed String, which was making the test inacurately passing
+ // in JIT mode when loading the string from the dex cache, as the string ids
+ // of the two different strings were the same).
+ // We know the string ids will be different because lexicographically:
+ // "Goodbye" < "LTransform;" < "hello".
+ System.out.println("hello");
+ }
+ }
+
+ /**
+ * base64 encoded class/dex file for
+ * class Transform {
+ * }
+ */
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADUAEQoAAwAKBwAMBwAPAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJU" +
+ "YWJsZQEAClNvdXJjZUZpbGUBAA1UZXN0MTk1Ny5qYXZhDAAEAAUHABABABZhcnQvVGVzdDE5NTck" +
+ "VHJhbnNmb3JtAQAJVHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9sYW5nL09iamVjdAEA" +
+ "DGFydC9UZXN0MTk1NwAgAAIAAwAAAAAAAQAAAAQABQABAAYAAAAdAAEAAQAAAAUqtwABsQAAAAEA" +
+ "BwAAAAYAAQAAAAYAAgAIAAAAAgAJAA4AAAAKAAEAAgALAA0ACA==");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAQiK+oahCb4T18bDge0pSvp7rka4UQ2AY0AwAAcAAAAHhWNBIAAAAAAAAAAIgCAAAN" +
+ "AAAAcAAAAAYAAACkAAAAAQAAALwAAAAAAAAAAAAAAAIAAADIAAAAAQAAANgAAAA8AgAA+AAAABQB" +
+ "AAAcAQAANgEAAEYBAABqAQAAigEAAJ4BAACtAQAAuAEAALsBAADIAQAAzgEAANUBAAABAAAAAgAA" +
+ "AAMAAAAEAAAABQAAAAgAAAAIAAAABQAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAA" +
+ "AAAAAAYAAAB4AgAAWwIAAAAAAAABAAEAAQAAABABAAAEAAAAcBABAAAADgAGAA4ABjxpbml0PgAY" +
+ "TGFydC9UZXN0MTk1NyRUcmFuc2Zvcm07AA5MYXJ0L1Rlc3QxOTU3OwAiTGRhbHZpay9hbm5vdGF0" +
+ "aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABJMamF2" +
+ "YS9sYW5nL09iamVjdDsADVRlc3QxOTU3LmphdmEACVRyYW5zZm9ybQABVgALYWNjZXNzRmxhZ3MA" +
+ "BG5hbWUABXZhbHVlAHV+fkQ4eyJjb21waWxhdGlvbi1tb2RlIjoiZGVidWciLCJtaW4tYXBpIjox" +
+ "LCJzaGEtMSI6Ijg0NjI2ZDE0MmRiMmY4NzVhY2E2YjVlOWVmYWU3OThjYWQ5ZDlhNTAiLCJ2ZXJz" +
+ "aW9uIjoiMS40LjItZGV2In0AAgIBCxgBAgMCCQQIChcHAAABAACAgAT4AQAAAAAAAAACAAAATAIA" +
+ "AFICAABsAgAAAAAAAAAAAAAAAAAADgAAAAAAAAABAAAAAAAAAAEAAAANAAAAcAAAAAIAAAAGAAAA" +
+ "pAAAAAMAAAABAAAAvAAAAAUAAAACAAAAyAAAAAYAAAABAAAA2AAAAAEgAAABAAAA+AAAAAMgAAAB" +
+ "AAAAEAEAAAIgAAANAAAAFAEAAAQgAAACAAAATAIAAAAgAAABAAAAWwIAAAMQAAACAAAAaAIAAAYg" +
+ "AAABAAAAeAIAAAAQAAABAAAAiAIAAA==");
+
+ public static void run() {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ Transform t = new Transform();
+ System.out.println("LastError is: " + getLastErrorOrException());
+ try {
+ Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ } catch (Throwable e) {
+ System.out.println("Got " + e.getClass().toString() + ": " + e.getMessage());
+ }
+ System.out.println("LastError is: " + getLastErrorOrException());
+ clearLastError();
+ System.out.println("LastError is: " + getLastErrorOrException());
+ }
+
+ public static String getLastErrorOrException() {
+ try {
+ return getLastError();
+ } catch (Throwable t) {
+ return "<call returned error: " + t.getClass().toString() + ": " + t.getMessage() + ">";
+ }
+ }
+ public static native String getLastError();
+ public static native void clearLastError();
+}
diff --git a/test/Android.bp b/test/Android.bp
index 8c1c1bf32e..561f95eb47 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -292,6 +292,7 @@ art_cc_defaults {
"1950-unprepared-transform/unprepared_transform.cc",
"1951-monitor-enter-no-suspend/raw_monitor.cc",
"1953-pop-frame/pop_frame.cc",
+ "1957-error-ext/lasterror.cc",
],
// Use NDK-compatible headers for ctstiagent.
header_libs: [
diff --git a/test/knownfailures.json b/test/knownfailures.json
index f4f45ce177..d831993707 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1027,7 +1027,8 @@
"679-locks",
"999-redefine-hiddenapi",
"1000-non-moving-space-stress",
- "1951-monitor-enter-no-suspend"],
+ "1951-monitor-enter-no-suspend",
+ "1957-error-ext"],
"variant": "jvm",
"description": ["Doesn't run on RI."]
},