diff options
| author | 2018-10-22 16:40:01 +0000 | |
|---|---|---|
| committer | 2018-10-22 16:40:01 +0000 | |
| commit | 7f1fe5d78af99e19ffebc1e5c03f095ea6301ea2 (patch) | |
| tree | ae7ba62144d778fe87a9e23ccc208a233e2df628 | |
| parent | 1cb95b1f532e795fa7527328cae7cf46b879e426 (diff) | |
| parent | ae45cbb288ddcaab3fd518cd00b9e6c1604ec4e1 (diff) | |
Merge "Add a get_last_error_message JVMTI extension."
| -rw-r--r-- | openjdkjvmti/Android.bp | 1 | ||||
| -rw-r--r-- | openjdkjvmti/OpenjdkJvmTi.cc | 8 | ||||
| -rw-r--r-- | openjdkjvmti/art_jvmti.h | 4 | ||||
| -rw-r--r-- | openjdkjvmti/ti_class.cc | 5 | ||||
| -rw-r--r-- | openjdkjvmti/ti_ddms.cc | 3 | ||||
| -rw-r--r-- | openjdkjvmti/ti_extension.cc | 40 | ||||
| -rw-r--r-- | openjdkjvmti/ti_logging.cc | 71 | ||||
| -rw-r--r-- | openjdkjvmti/ti_logging.h | 102 | ||||
| -rw-r--r-- | openjdkjvmti/ti_search.cc | 6 | ||||
| -rw-r--r-- | openjdkjvmti/ti_stack.cc | 10 | ||||
| -rw-r--r-- | test/1957-error-ext/expected.txt | 4 | ||||
| -rw-r--r-- | test/1957-error-ext/info.txt | 1 | ||||
| -rw-r--r-- | test/1957-error-ext/lasterror.cc | 112 | ||||
| -rwxr-xr-x | test/1957-error-ext/run | 18 | ||||
| -rw-r--r-- | test/1957-error-ext/src/Main.java | 21 | ||||
| -rw-r--r-- | test/1957-error-ext/src/art/Redefinition.java | 91 | ||||
| -rw-r--r-- | test/1957-error-ext/src/art/Test1957.java | 86 | ||||
| -rw-r--r-- | test/Android.bp | 1 | ||||
| -rw-r--r-- | test/knownfailures.json | 3 |
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."] }, |