Merge "Refactor JVMTI ClassFileLoadHook handling"
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 1e79fdf..3cb9731 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -369,7 +369,7 @@
*/
TEST_F(CmdlineParserTest, TestJdwpProviderEmpty) {
{
- EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kInternal, "", M::JdwpProvider);
+ EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kNone, "", M::JdwpProvider);
}
} // TEST_F
diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc
index 6c66f12..a8d2a37 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -33,6 +33,7 @@
#include "dex/art_dex_file_loader.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_loader.h"
+#include "dex/dex_file_verifier.h"
// Runtime includes.
#include "dex_container.h"
@@ -67,6 +68,20 @@
vdex->UnquickenDexFile(new_dex_file, original_dex_file, /* decompile_return_instruction */true);
}
+static void DCheckVerifyDexFile(const art::DexFile& dex) {
+ if (art::kIsDebugBuild) {
+ std::string error;
+ if (!art::DexFileVerifier::Verify(&dex,
+ dex.Begin(),
+ dex.Size(),
+ "FixedUpDexFile_Verification.dex",
+ /*verify_checksum*/ true,
+ &error)) {
+ LOG(FATAL) << "Failed to verify de-quickened dex file: " << error;
+ }
+ }
+}
+
std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& original,
const char* descriptor) {
// Copy the data into mutable memory.
@@ -121,6 +136,7 @@
DoDexUnquicken(*new_dex_file, original);
RecomputeDexChecksum(const_cast<art::DexFile*>(new_dex_file.get()));
+ DCheckVerifyDexFile(*new_dex_file);
std::unique_ptr<FixedUpDexFile> ret(new FixedUpDexFile(std::move(new_dex_file), std::move(data)));
return ret;
}
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 642d26e..c75f3e9 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -209,7 +209,8 @@
bool CreateProfile(const std::string& profile_file_contents,
const std::string& filename,
- const std::string& dex_location) {
+ const std::string& dex_location,
+ bool skip_verification) {
ScratchFile class_names_file;
File* file = class_names_file.GetFile();
EXPECT_TRUE(file->WriteFully(profile_file_contents.c_str(), profile_file_contents.length()));
@@ -222,6 +223,9 @@
argv_str.push_back("--reference-profile-file=" + filename);
argv_str.push_back("--apk=" + dex_location);
argv_str.push_back("--dex-location=" + dex_location);
+ if (skip_verification) {
+ argv_str.push_back("--skip-apk-verification");
+ }
std::string error;
EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
return true;
@@ -238,6 +242,7 @@
argv_str.push_back("--profile-file=" + filename);
argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]);
argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]);
+ argv_str.push_back("--skip-apk-verification");
argv_str.push_back("--dump-output-to-fd=" + std::to_string(GetFd(output_file)));
std::string error;
EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
@@ -268,7 +273,8 @@
ScratchFile profile_file;
EXPECT_TRUE(CreateProfile(input_file_contents,
profile_file.GetFilename(),
- GetLibCoreDexFileNames()[0]));
+ GetLibCoreDexFileNames()[0],
+ /* skip_verification */ true));
profile_file.GetFile()->ResetOffset();
EXPECT_TRUE(DumpClassesAndMethods(profile_file.GetFilename(), output_file_contents));
return true;
@@ -675,7 +681,8 @@
ScratchFile profile_file;
EXPECT_TRUE(CreateProfile(input_file_contents,
profile_file.GetFilename(),
- GetLibCoreDexFileNames()[0]));
+ GetLibCoreDexFileNames()[0],
+ /* skip_verification */ true));
ProfileCompilationInfo info;
profile_file.GetFile()->ResetOffset();
ASSERT_TRUE(info.Load(GetFd(profile_file)));
@@ -731,7 +738,8 @@
"H" + kHotMethod + "\n" +
kUncommonDirtyClass;
profiles.emplace_back(ScratchFile());
- EXPECT_TRUE(CreateProfile(dex1, profiles.back().GetFilename(), core_dex));
+ EXPECT_TRUE(CreateProfile(
+ dex1, profiles.back().GetFilename(), core_dex, /* skip_verification */ true));
// Create a bunch of boot profiles.
std::string dex2 =
@@ -741,7 +749,8 @@
"P" + kMultiMethod + "\n" +
kUncommonDirtyClass;
profiles.emplace_back(ScratchFile());
- EXPECT_TRUE(CreateProfile(dex2, profiles.back().GetFilename(), core_dex));
+ EXPECT_TRUE(CreateProfile(
+ dex2, profiles.back().GetFilename(), core_dex, /* skip_verification */ true));
// Create a bunch of boot profiles.
std::string dex3 =
@@ -750,7 +759,8 @@
"P" + kMultiMethod + "\n" +
kDirtyClass + "\n";
profiles.emplace_back(ScratchFile());
- EXPECT_TRUE(CreateProfile(dex3, profiles.back().GetFilename(), core_dex));
+ EXPECT_TRUE(CreateProfile(
+ dex3, profiles.back().GetFilename(), core_dex, /* skip_verification */ true));
// Generate the boot profile.
ScratchFile out_profile;
@@ -763,6 +773,7 @@
args.push_back("--reference-profile-file=" + out_profile.GetFilename());
args.push_back("--apk=" + core_dex);
args.push_back("--dex-location=" + core_dex);
+ args.push_back("--skip-apk-verification");
for (const ScratchFile& profile : profiles) {
args.push_back("--profile-file=" + profile.GetFilename());
}
@@ -858,7 +869,8 @@
ScratchFile profile_file;
ASSERT_TRUE(CreateProfile(input_file_contents,
profile_file.GetFilename(),
- GetTestDexFileName("ProfileTestMultiDex")));
+ GetTestDexFileName("ProfileTestMultiDex"),
+ /* skip_verification */ false));
// Load the profile from disk.
ProfileCompilationInfo info;
@@ -1008,7 +1020,8 @@
std::string dex_filename = GetTestDexFileName("ProfileTestMultiDex");
ASSERT_TRUE(CreateProfile(input_file_contents,
profile_file.GetFilename(),
- dex_filename));
+ dex_filename,
+ /* skip_verification */ false));
// Load the profile from disk.
ProfileCompilationInfo info;
diff --git a/profman/profman.cc b/profman/profman.cc
index ffc3c01..ea6c382 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -137,6 +137,7 @@
UsageError(" --apk-fd=<number>: file descriptor containing an open APK to");
UsageError(" search for dex files");
UsageError(" --apk-=<filename>: an APK to search for dex files");
+ UsageError(" --skip-apk-verification: do not attempt to verify APKs");
UsageError("");
UsageError(" --generate-boot-image-profile: Generate a boot image profile based on input");
UsageError(" profiles. Requires passing in dex files to inspect properties of classes.");
@@ -185,6 +186,7 @@
dump_only_(false),
dump_classes_and_methods_(false),
generate_boot_image_profile_(false),
+ skip_apk_verification_(false),
dump_output_to_fd_(kInvalidFd),
test_profile_num_dex_(kDefaultTestProfileNumDex),
test_profile_method_percerntage_(kDefaultTestProfileMethodPercentage),
@@ -227,6 +229,8 @@
ParseUintOption(option, "--dump-output-to-fd", &dump_output_to_fd_, Usage);
} else if (option == "--generate-boot-image-profile") {
generate_boot_image_profile_ = true;
+ } else if (option == "--skip-apk-verification") {
+ skip_apk_verification_ = true;
} else if (option.starts_with("--boot-image-class-threshold=")) {
ParseUintOption(option,
"--boot-image-class-threshold",
@@ -321,6 +325,10 @@
return result;
}
+ bool ShouldSkipApkVerification() const {
+ return skip_apk_verification_;
+ }
+
void OpenApkFilesFromLocations(std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
bool use_apk_fd_list = !apks_fd_.empty();
if (use_apk_fd_list) {
@@ -342,7 +350,7 @@
if (use_apk_fd_list) {
if (dex_file_loader.OpenZip(apks_fd_[i],
dex_locations_[i],
- /* verify */ true,
+ /* verify */ !ShouldSkipApkVerification(),
kVerifyChecksum,
&error_msg,
&dex_files_for_location)) {
@@ -353,7 +361,7 @@
} else {
if (dex_file_loader.Open(apk_files_[i].c_str(),
dex_locations_[i],
- /* verify */ true,
+ /* verify */ !ShouldSkipApkVerification(),
kVerifyChecksum,
&error_msg,
&dex_files_for_location)) {
@@ -1148,6 +1156,7 @@
bool dump_only_;
bool dump_classes_and_methods_;
bool generate_boot_image_profile_;
+ bool skip_apk_verification_;
int dump_output_to_fd_;
BootImageOptions boot_image_options_;
std::string test_profile_;
diff --git a/runtime/dex/dex_file_loader.cc b/runtime/dex/dex_file_loader.cc
index 7dde0a4..0f2758e 100644
--- a/runtime/dex/dex_file_loader.cc
+++ b/runtime/dex/dex_file_loader.cc
@@ -222,8 +222,8 @@
std::string* error_msg) const {
return OpenCommon(base,
size,
- /*data_base*/ base,
- /*data_size*/ size,
+ /*data_base*/ nullptr,
+ /*data_size*/ 0,
location,
location_checksum,
oat_dex_file,
diff --git a/runtime/leb128.h b/runtime/leb128.h
index 9fb09d8..07eadc1 100644
--- a/runtime/leb128.h
+++ b/runtime/leb128.h
@@ -55,6 +55,10 @@
return static_cast<uint32_t>(result);
}
+static inline uint32_t DecodeUnsignedLeb128WithoutMovingCursor(const uint8_t* data) {
+ return DecodeUnsignedLeb128(&data);
+}
+
static inline bool DecodeUnsignedLeb128Checked(const uint8_t** data,
const void* end,
uint32_t* out) {
@@ -203,6 +207,34 @@
return (x * 37) >> 8;
}
+static inline bool IsLeb128Terminator(const uint8_t* ptr) {
+ return *ptr <= 0x7f;
+}
+
+// Returns the first byte of a Leb128 value assuming that:
+// (1) `end_ptr` points to the first byte after the Leb128 value, and
+// (2) there is another Leb128 value before this one.
+template <typename T>
+static inline T* ReverseSearchUnsignedLeb128(T* end_ptr) {
+ static_assert(std::is_same<typename std::remove_const<T>::type, uint8_t>::value,
+ "T must be a uint8_t");
+ T* ptr = end_ptr;
+
+ // Move one byte back, check that this is the terminating byte.
+ ptr--;
+ DCHECK(IsLeb128Terminator(ptr));
+
+ // Keep moving back while the previous byte is not a terminating byte.
+ // Fail after reading five bytes in case there isn't another Leb128 value
+ // before this one.
+ while (!IsLeb128Terminator(ptr - 1)) {
+ ptr--;
+ DCHECK_LE(static_cast<ptrdiff_t>(end_ptr - ptr), 5);
+ }
+
+ return ptr;
+}
+
// Returns the number of bytes needed to encode the value in unsigned LEB128.
static inline uint32_t SignedLeb128Size(int32_t data) {
// Like UnsignedLeb128Size(), but we need one bit beyond the highest bit that differs from sign.
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 6d065d6..b1286da 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1253,7 +1253,20 @@
jdwp_provider_ = runtime_options.GetOrDefault(Opt::JdwpProvider);
switch (jdwp_provider_) {
case JdwpProvider::kNone: {
- LOG(WARNING) << "Disabling all JDWP support.";
+ LOG(INFO) << "Disabling all JDWP support.";
+ if (!jdwp_options_.empty()) {
+ bool has_transport = jdwp_options_.find("transport") != std::string::npos;
+ const char* transport_internal = !has_transport ? "transport=dt_android_adb," : "";
+ std::string adb_connection_args =
+ std::string(" -XjdwpProvider:adbconnection -XjdwpOptions:") + jdwp_options_;
+ LOG(WARNING) << "Jdwp options given when jdwp is disabled! You probably want to enable "
+ << "jdwp with one of:" << std::endl
+ << " -XjdwpProvider:internal "
+ << "-XjdwpOptions:" << transport_internal << jdwp_options_ << std::endl
+ << " -Xplugin:libopenjdkjvmti" << (kIsDebugBuild ? "d" : "") << ".so "
+ << "-agentpath:libjdwp.so=" << jdwp_options_ << std::endl
+ << (has_transport ? "" : adb_connection_args);
+ }
break;
}
case JdwpProvider::kInternal: {
@@ -1855,7 +1868,13 @@
bool Runtime::AttachCurrentThread(const char* thread_name, bool as_daemon, jobject thread_group,
bool create_peer) {
ScopedTrace trace(__FUNCTION__);
- return Thread::Attach(thread_name, as_daemon, thread_group, create_peer) != nullptr;
+ Thread* self = Thread::Attach(thread_name, as_daemon, thread_group, create_peer);
+ // Run ThreadGroup.add to notify the group that this thread is now started.
+ if (self != nullptr && create_peer && !IsAotCompiler()) {
+ ScopedObjectAccess soa(self);
+ self->NotifyThreadGroup(soa, thread_group);
+ }
+ return self != nullptr;
}
void Runtime::DetachCurrentThread() {
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 6e1a68b..e78d952 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -44,7 +44,7 @@
RUNTIME_OPTIONS_KEY (Unit, CheckJni)
RUNTIME_OPTIONS_KEY (Unit, JniOptsForceCopy)
RUNTIME_OPTIONS_KEY (std::string, JdwpOptions, "")
-RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kInternal)
+RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kNone)
RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryMaximumSize, gc::Heap::kDefaultMaximumSize) // -Xmx
RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryInitialSize, gc::Heap::kDefaultInitialSize) // -Xms
RUNTIME_OPTIONS_KEY (MemoryKiB, HeapGrowthLimit) // Default is 0 for unlimited
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 46cb751..14375f7 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2049,20 +2049,8 @@
// The thread counts as started from now on. We need to add it to the ThreadGroup. For regular
// threads, this is done in Thread.start() on the Java side.
- {
- // This is only ever done once. There's no benefit in caching the method.
- jmethodID thread_group_add = soa.Env()->GetMethodID(WellKnownClasses::java_lang_ThreadGroup,
- "add",
- "(Ljava/lang/Thread;)V");
- CHECK(thread_group_add != nullptr);
- ScopedLocalRef<jobject> thread_jobject(
- soa.Env(), soa.Env()->AddLocalReference<jobject>(Thread::Current()->GetPeer()));
- soa.Env()->CallNonvirtualVoidMethod(runtime->GetMainThreadGroup(),
- WellKnownClasses::java_lang_ThreadGroup,
- thread_group_add,
- thread_jobject.get());
- Thread::Current()->AssertNoPendingException();
- }
+ Thread::Current()->NotifyThreadGroup(soa, runtime->GetMainThreadGroup());
+ Thread::Current()->AssertNoPendingException();
}
void Thread::Shutdown() {
@@ -2076,6 +2064,28 @@
}
}
+void Thread::NotifyThreadGroup(ScopedObjectAccessAlreadyRunnable& soa, jobject thread_group) {
+ ScopedLocalRef<jobject> thread_jobject(
+ soa.Env(), soa.Env()->AddLocalReference<jobject>(Thread::Current()->GetPeer()));
+ ScopedLocalRef<jobject> thread_group_jobject_scoped(
+ soa.Env(), nullptr);
+ jobject thread_group_jobject = thread_group;
+ if (thread_group == nullptr || kIsDebugBuild) {
+ // There is always a group set. Retrieve it.
+ thread_group_jobject_scoped.reset(
+ soa.Env()->GetObjectField(thread_jobject.get(),
+ WellKnownClasses::java_lang_Thread_group));
+ thread_group_jobject = thread_group_jobject_scoped.get();
+ if (kIsDebugBuild && thread_group != nullptr) {
+ CHECK(soa.Env()->IsSameObject(thread_group, thread_group_jobject));
+ }
+ }
+ soa.Env()->CallNonvirtualVoidMethod(thread_group_jobject,
+ WellKnownClasses::java_lang_ThreadGroup,
+ WellKnownClasses::java_lang_ThreadGroup_add,
+ thread_jobject.get());
+}
+
Thread::Thread(bool daemon)
: tls32_(daemon),
wait_monitor_(nullptr),
diff --git a/runtime/thread.h b/runtime/thread.h
index 426d27d..295685e 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -511,6 +511,12 @@
static void FinishStartup();
static void Shutdown();
+ // Notify this thread's thread-group that this thread has started.
+ // Note: the given thread-group is used as a fast path and verified in debug build. If the value
+ // is null, the thread's thread-group is loaded from the peer.
+ void NotifyThreadGroup(ScopedObjectAccessAlreadyRunnable& soa, jobject thread_group = nullptr)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// JNI methods
JNIEnvExt* GetJniEnv() const {
return tlsPtr_.jni_env;
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 36ebb17..7428e98 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -30,6 +30,8 @@
#include "dex/dex_file.h"
#include "dex/dex_file_loader.h"
#include "dex_to_dex_decompiler.h"
+#include "hidden_api_access_flags.h"
+#include "leb128.h"
#include "quicken_info.h"
namespace art {
@@ -262,6 +264,18 @@
UnquickenDexFile(target_dex_file, source_dex_file.Begin(), decompile_return_instruction);
}
+static void UpdateAccessFlags(uint8_t* data, uint32_t new_flag, bool is_method) {
+ // Go back 1 uleb to start.
+ data = ReverseSearchUnsignedLeb128(data);
+ if (is_method) {
+ // Methods have another uleb field before the access flags
+ data = ReverseSearchUnsignedLeb128(data);
+ }
+ DCHECK_EQ(HiddenApiAccessFlags::RemoveFromDex(DecodeUnsignedLeb128WithoutMovingCursor(data)),
+ new_flag);
+ UpdateUnsignedLeb128(data, new_flag);
+}
+
void VdexFile::UnquickenDexFile(const DexFile& target_dex_file,
const uint8_t* source_dex_begin,
bool decompile_return_instruction) const {
@@ -280,27 +294,32 @@
for (ClassDataItemIterator class_it(target_dex_file, class_data);
class_it.HasNext();
class_it.Next()) {
- if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) {
+ if (class_it.IsAtMethod()) {
const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem();
- if (!unquickened_code_item.emplace(code_item).second) {
- // Already unquickened this code item, do not do it again.
- continue;
+ if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) {
+ ArrayRef<const uint8_t> quicken_data;
+ if (!quickening_info.empty()) {
+ const uint32_t quickening_offset = GetQuickeningInfoOffset(
+ GetQuickenInfoOffsetTable(source_dex_begin,
+ target_dex_file.NumMethodIds(),
+ quickening_info),
+ class_it.GetMemberIndex(),
+ quickening_info);
+ quicken_data = GetQuickeningInfoAt(quickening_info, quickening_offset);
+ }
+ optimizer::ArtDecompileDEX(
+ target_dex_file,
+ *code_item,
+ quicken_data,
+ decompile_return_instruction);
}
- ArrayRef<const uint8_t> quicken_data;
- if (!quickening_info.empty()) {
- const uint32_t quickening_offset = GetQuickeningInfoOffset(
- GetQuickenInfoOffsetTable(source_dex_begin,
- target_dex_file.NumMethodIds(),
- quickening_info),
- class_it.GetMemberIndex(),
- quickening_info);
- quicken_data = GetQuickeningInfoAt(quickening_info, quickening_offset);
- }
- optimizer::ArtDecompileDEX(
- target_dex_file,
- *code_item,
- quicken_data,
- decompile_return_instruction);
+ UpdateAccessFlags(const_cast<uint8_t*>(class_it.DataPointer()),
+ class_it.GetMemberAccessFlags(),
+ /*is_method*/ true);
+ } else {
+ UpdateAccessFlags(const_cast<uint8_t*>(class_it.DataPointer()),
+ class_it.GetMemberAccessFlags(),
+ /*is_method*/ false);
}
}
}
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index dc57f81..5fe10f5 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -110,6 +110,7 @@
jmethodID WellKnownClasses::java_lang_Thread_dispatchUncaughtException;
jmethodID WellKnownClasses::java_lang_Thread_init;
jmethodID WellKnownClasses::java_lang_Thread_run;
+jmethodID WellKnownClasses::java_lang_ThreadGroup_add;
jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread;
jmethodID WellKnownClasses::java_nio_DirectByteBuffer_init;
jmethodID WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation;
@@ -347,6 +348,7 @@
java_lang_Thread_dispatchUncaughtException = CacheMethod(env, java_lang_Thread, false, "dispatchUncaughtException", "(Ljava/lang/Throwable;)V");
java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
java_lang_Thread_run = CacheMethod(env, java_lang_Thread, false, "run", "()V");
+ java_lang_ThreadGroup_add = CacheMethod(env, java_lang_ThreadGroup, false, "add", "(Ljava/lang/Thread;)V");
java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "threadTerminated", "(Ljava/lang/Thread;)V");
java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "<init>", "(JI)V");
libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(env, libcore_reflect_AnnotationFactory, true, "createAnnotation", "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;");
@@ -496,6 +498,7 @@
java_lang_Thread_dispatchUncaughtException = nullptr;
java_lang_Thread_init = nullptr;
java_lang_Thread_run = nullptr;
+ java_lang_ThreadGroup_add = nullptr;
java_lang_ThreadGroup_removeThread = nullptr;
java_nio_DirectByteBuffer_init = nullptr;
libcore_reflect_AnnotationFactory_createAnnotation = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 024971a..9e0b079 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -121,6 +121,7 @@
static jmethodID java_lang_Thread_dispatchUncaughtException;
static jmethodID java_lang_Thread_init;
static jmethodID java_lang_Thread_run;
+ static jmethodID java_lang_ThreadGroup_add;
static jmethodID java_lang_ThreadGroup_removeThread;
static jmethodID java_nio_DirectByteBuffer_init;
static jmethodID libcore_reflect_AnnotationFactory_createAnnotation;
diff --git a/test/004-ThreadStress/src/Main.java b/test/004-ThreadStress/src/Main.java
index 6ad160c..c03a912 100644
--- a/test/004-ThreadStress/src/Main.java
+++ b/test/004-ThreadStress/src/Main.java
@@ -38,6 +38,8 @@
// -t X ............ number of operations per thread
// -p X ............ number of permits granted by semaphore
// --dumpmap ....... print the frequency map
+// --locks-only .... select a pre-set frequency map with lock-related operations only
+// --allocs-only ... select a pre-set frequency map with allocation-related operations only
// -oom:X .......... frequency of OOM (double)
// -sigquit:X ...... frequency of SigQuit (double)
// -alloc:X ........ frequency of Alloc (double)
@@ -289,26 +291,35 @@
private final static Map<Operation, Double> createDefaultFrequencyMap(Object lock,
Semaphore semaphore) {
Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>();
- frequencyMap.put(new OOM(), 0.005); // 1/200
- frequencyMap.put(new SigQuit(), 0.095); // 19/200
- frequencyMap.put(new Alloc(), 0.225); // 45/200
- frequencyMap.put(new LargeAlloc(), 0.05); // 10/200
- frequencyMap.put(new StackTrace(), 0.1); // 20/200
- frequencyMap.put(new Exit(), 0.225); // 45/200
- frequencyMap.put(new Sleep(), 0.125); // 25/200
- frequencyMap.put(new TimedWait(lock), 0.05); // 10/200
- frequencyMap.put(new Wait(lock), 0.075); // 15/200
- frequencyMap.put(new QueuedWait(semaphore), 0.05); // 10/200
+ frequencyMap.put(new OOM(), 0.005); // 1/200
+ frequencyMap.put(new SigQuit(), 0.095); // 19/200
+ frequencyMap.put(new Alloc(), 0.225); // 45/200
+ frequencyMap.put(new LargeAlloc(), 0.05); // 10/200
+ frequencyMap.put(new StackTrace(), 0.1); // 20/200
+ frequencyMap.put(new Exit(), 0.225); // 45/200
+ frequencyMap.put(new Sleep(), 0.125); // 25/200
+ frequencyMap.put(new TimedWait(lock), 0.05); // 10/200
+ frequencyMap.put(new Wait(lock), 0.075); // 15/200
+ frequencyMap.put(new QueuedWait(semaphore), 0.05); // 10/200
+
+ return frequencyMap;
+ }
+
+ private final static Map<Operation, Double> createAllocFrequencyMap() {
+ Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>();
+ frequencyMap.put(new Sleep(), 0.2); // 40/200
+ frequencyMap.put(new Alloc(), 0.65); // 130/200
+ frequencyMap.put(new LargeAlloc(), 0.15); // 30/200
return frequencyMap;
}
private final static Map<Operation, Double> createLockFrequencyMap(Object lock) {
Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>();
- frequencyMap.put(new Sleep(), 0.2); // 40/200
- frequencyMap.put(new TimedWait(lock), 0.2); // 40/200
- frequencyMap.put(new Wait(lock), 0.2); // 40/200
- frequencyMap.put(new SyncAndWork(lock), 0.4); // 80/200
+ frequencyMap.put(new Sleep(), 0.2); // 40/200
+ frequencyMap.put(new TimedWait(lock), 0.2); // 40/200
+ frequencyMap.put(new Wait(lock), 0.2); // 40/200
+ frequencyMap.put(new SyncAndWork(lock), 0.4); // 80/200
return frequencyMap;
}
@@ -414,11 +425,14 @@
i++;
permits = Integer.parseInt(args[i]);
} else if (args[i].equals("--locks-only")) {
- lock = new Object();
frequencyMap = createLockFrequencyMap(lock);
+ } else if (args[i].equals("--allocs-only")) {
+ frequencyMap = createAllocFrequencyMap();
} else if (args[i].equals("--dumpmap")) {
dumpMap = true;
} else {
+ // Processing an argument of the form "-<operation>:X"
+ // (where X is a double value).
Semaphore semaphore = getSemaphore(permits);
frequencyMap = updateFrequencyMap(frequencyMap, lock, semaphore, args[i]);
}
diff --git a/test/169-threadgroup-jni/expected.txt b/test/169-threadgroup-jni/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/169-threadgroup-jni/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/169-threadgroup-jni/info.txt b/test/169-threadgroup-jni/info.txt
new file mode 100644
index 0000000..b4c77e2
--- /dev/null
+++ b/test/169-threadgroup-jni/info.txt
@@ -0,0 +1 @@
+Ensure that attached threads are correctly handled in ThreadGroups.
diff --git a/test/169-threadgroup-jni/jni_daemon_thread.cc b/test/169-threadgroup-jni/jni_daemon_thread.cc
new file mode 100644
index 0000000..94902dc
--- /dev/null
+++ b/test/169-threadgroup-jni/jni_daemon_thread.cc
@@ -0,0 +1,65 @@
+/*
+ * 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 <jni.h>
+#include <nativehelper/scoped_local_ref.h>
+#include <pthread.h>
+
+#include <android-base/logging.h>
+
+namespace art {
+
+static JavaVM* vm = nullptr;
+
+static void* Runner(void* arg) {
+ CHECK(vm != nullptr);
+
+ jobject thread_group = reinterpret_cast<jobject>(arg);
+ JNIEnv* env = nullptr;
+ JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, thread_group };
+ int attach_result = vm->AttachCurrentThread(&env, &args);
+ CHECK_EQ(attach_result, 0);
+
+ {
+ ScopedLocalRef<jclass> klass(env, env->FindClass("Main"));
+ CHECK(klass != nullptr);
+
+ jmethodID id = env->GetStaticMethodID(klass.get(), "runFromNative", "()V");
+ CHECK(id != nullptr);
+
+ env->CallStaticVoidMethod(klass.get(), id);
+ }
+
+ int detach_result = vm->DetachCurrentThread();
+ CHECK_EQ(detach_result, 0);
+ return nullptr;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_testNativeThread(
+ JNIEnv* env, jclass, jobject thread_group) {
+ CHECK_EQ(env->GetJavaVM(&vm), 0);
+ jobject global_thread_group = env->NewGlobalRef(thread_group);
+
+ pthread_t pthread;
+ int pthread_create_result = pthread_create(&pthread, nullptr, Runner, global_thread_group);
+ CHECK_EQ(pthread_create_result, 0);
+ int pthread_join_result = pthread_join(pthread, nullptr);
+ CHECK_EQ(pthread_join_result, 0);
+
+ env->DeleteGlobalRef(global_thread_group);
+}
+
+} // namespace art
diff --git a/test/169-threadgroup-jni/src/Main.java b/test/169-threadgroup-jni/src/Main.java
new file mode 100644
index 0000000..2cd1fcf
--- /dev/null
+++ b/test/169-threadgroup-jni/src/Main.java
@@ -0,0 +1,39 @@
+/*
+ * 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 {
+ System.loadLibrary(args[0]);
+
+ ThreadGroup group = new ThreadGroup("Test group");
+ group.setDaemon(true);
+
+ testNativeThread(group);
+
+ if (!executed) {
+ throw new IllegalStateException("Expected runFromNative to be done.");
+ }
+ if (!group.isDestroyed()) {
+ throw new IllegalStateException("Threadgroup should be destroyed.");
+ }
+ }
+
+ private static boolean executed = false;
+ private static void runFromNative() {
+ executed = true;
+ }
+ private static native void testNativeThread(ThreadGroup group);
+}
diff --git a/test/983-source-transform-verify/expected.txt b/test/983-source-transform-verify/expected.txt
index abcdf3a..aa51ea0 100644
--- a/test/983-source-transform-verify/expected.txt
+++ b/test/983-source-transform-verify/expected.txt
@@ -1,2 +1,3 @@
Dex file hook for art/Test983$Transform
Dex file hook for java/lang/Object
+Dex file hook for java/lang/ClassLoader
diff --git a/test/983-source-transform-verify/src/art/Test983.java b/test/983-source-transform-verify/src/art/Test983.java
index b81e7f4..faae96a 100644
--- a/test/983-source-transform-verify/src/art/Test983.java
+++ b/test/983-source-transform-verify/src/art/Test983.java
@@ -16,7 +16,6 @@
package art;
-import java.util.Base64;
public class Test983 {
static class Transform {
public void sayHi() {
@@ -29,10 +28,11 @@
}
public static void doTest() {
- Transform abc = new Transform();
Redefinition.enableCommonRetransformation(true);
Redefinition.doCommonClassRetransformation(Transform.class);
Redefinition.doCommonClassRetransformation(Object.class);
+ // NB java.lang.ClassLoader has hidden fields.
+ Redefinition.doCommonClassRetransformation(ClassLoader.class);
Redefinition.enableCommonRetransformation(false);
}
}
diff --git a/test/Android.bp b/test/Android.bp
index 470a68f..730818c 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -368,6 +368,7 @@
"149-suspend-all-stress/suspend_all.cc",
"154-gc-loop/heap_interface.cc",
"167-visit-locks/visit_locks.cc",
+ "169-threadgroup-jni/jni_daemon_thread.cc",
"203-multi-checkpoint/multi_checkpoint.cc",
"305-other-fault-handler/fault_handler.cc",
"454-get-vreg/get_vreg_jni.cc",
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 5e40b86..ca24471 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -436,7 +436,13 @@
msg " adb forward tcp:$PORT tcp:$PORT"
fi
msg " jdb -attach localhost:$PORT"
- DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
+ if [ "$USE_JVM" = "n" ]; then
+ # TODO We should switch over to using the jvmti agent by default.
+ # Need to tell the runtime to enable the internal jdwp implementation.
+ DEBUGGER_OPTS="-XjdwpOptions:transport=dt_socket,address=$PORT,server=y,suspend=y -XjdwpProvider:internal"
+ else
+ DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
+ fi
elif [ "$DEBUGGER" = "agent" ]; then
PORT=12345
# TODO Support ddms connection and support target.
diff --git a/tools/generate-boot-image-profile.sh b/tools/generate-boot-image-profile.sh
index d87123a..ee53f43 100755
--- a/tools/generate-boot-image-profile.sh
+++ b/tools/generate-boot-image-profile.sh
@@ -46,7 +46,9 @@
fi
done
-jar_args=()
+# Boot jars have hidden API access flags which do not pass dex file
+# verification. Skip it.
+jar_args=("--skip-apk-verification")
boot_jars=$("$ANDROID_BUILD_TOP"/art/tools/bootjars.sh --target)
jar_dir=$ANDROID_BUILD_TOP/$(get_build_var TARGET_OUT_JAVA_LIBRARIES)
for file in $boot_jars; do
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index a755fdb..c893da6 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -118,9 +118,11 @@
// until we hit the terminating byte of the previous Leb128 value.
const uint8_t* ptr = it_.DataPointer();
if (it_.IsAtMethod()) {
- ptr = ReverseSearchUnsignedLeb128(ptr, it_.GetMethodCodeItemOffset());
+ ptr = ReverseSearchUnsignedLeb128(ptr);
+ DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), it_.GetMethodCodeItemOffset());
}
- ptr = ReverseSearchUnsignedLeb128(ptr, old_flags);
+ ptr = ReverseSearchUnsignedLeb128(ptr);
+ DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), old_flags);
// Overwrite the access flags.
UpdateUnsignedLeb128(const_cast<uint8_t*>(ptr), new_flags);
@@ -158,38 +160,6 @@
return klass_.GetDexFile().GetFieldId(it_.GetMemberIndex());
}
- static inline bool IsLeb128Terminator(const uint8_t* ptr) {
- return *ptr <= 0x7f;
- }
-
- // Returns the first byte of a Leb128 value assuming that:
- // (1) `end_ptr` points to the first byte after the Leb128 value, and
- // (2) there is another Leb128 value before this one.
- // The function will fail after reading 5 bytes (the longest supported Leb128
- // encoding) to protect against situations when (2) is not satisfied.
- // When a Leb128 value is discovered, it is decoded and CHECKed against `value`.
- static const uint8_t* ReverseSearchUnsignedLeb128(const uint8_t* end_ptr, uint32_t expected) {
- const uint8_t* ptr = end_ptr;
-
- // Move one byte back, check that this is the terminating byte.
- ptr--;
- CHECK(IsLeb128Terminator(ptr));
-
- // Keep moving back while the previous byte is not a terminating byte.
- // Fail after reading five bytes in case there isn't another Leb128 value
- // before this one.
- while (!IsLeb128Terminator(ptr - 1)) {
- ptr--;
- CHECK_LE((size_t) (end_ptr - ptr), 5u);
- }
-
- // Check that the decoded value matches the `expected` value.
- const uint8_t* tmp_ptr = ptr;
- CHECK_EQ(DecodeUnsignedLeb128(&tmp_ptr), expected);
-
- return ptr;
- }
-
const DexClass& klass_;
const ClassDataItemIterator& it_;
};
diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py
index bddce32..4a54a3a 100755
--- a/tools/jfuzz/run_jfuzz_test.py
+++ b/tools/jfuzz/run_jfuzz_test.py
@@ -169,7 +169,7 @@
def CompileAndRunTest(self):
dbg = '-g' if self._debug_info else '-g:none'
- if RunCommand(['javac', dbg, 'Test.java'],
+ if RunCommand(['javac', '--release=8', dbg, 'Test.java'],
out=None, err=None, timeout=30) == RetCode.SUCCESS:
retc = RunCommand(['java', 'Test'], self.output_file, err=None)
else: