Merge "Remove denver from art"
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index b3c23b3..f2e12f6 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -48,6 +48,13 @@
"libadbconnectiond",
]
+// Files associated with bionic / managed core library time zone APIs.
+art_runtime_time_zone_prebuilts = [
+ "apex_tz_version",
+ "apex_tzdata",
+ "apex_tzlookup.xml",
+]
+
// Modules listed in LOCAL_REQUIRED_MODULES for module art-tools in art/Android.mk.
art_tools_common_binaries = [
"dexdiag",
@@ -109,7 +116,8 @@
binaries: [],
}
},
- prebuilts: ["com.android.runtime.ld.config.txt"],
+ prebuilts: art_runtime_time_zone_prebuilts
+ + ["com.android.runtime.ld.config.txt"],
key: "com.android.runtime.key",
}
@@ -137,7 +145,8 @@
binaries: art_tools_binaries,
}
},
- prebuilts: ["com.android.runtime.ld.config.txt"],
+ prebuilts: art_runtime_time_zone_prebuilts
+ + ["com.android.runtime.ld.config.txt"],
key: "com.android.runtime.key",
}
@@ -170,4 +179,9 @@
}
},
key: "com.android.runtime.key",
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 97daafa..101e5c4 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -63,6 +63,12 @@
return expected == actual;
}
+ template <char Separator>
+ bool UsuallyEquals(const std::vector<std::string>& expected,
+ const ParseStringList<Separator>& actual) {
+ return expected == static_cast<std::vector<std::string>>(actual);
+ }
+
// Try to use memcmp to compare simple plain-old-data structs.
//
// This should *not* generate false positives, but it can generate false negatives.
@@ -218,8 +224,13 @@
}
EXPECT_SINGLE_PARSE_EXISTS("-Xzygote", M::Zygote);
- EXPECT_SINGLE_PARSE_VALUE_STR("/hello/world", "-Xbootclasspath:/hello/world", M::BootClassPath);
- EXPECT_SINGLE_PARSE_VALUE("/hello/world", "-Xbootclasspath:/hello/world", M::BootClassPath);
+ EXPECT_SINGLE_PARSE_VALUE(std::vector<std::string>({"/hello/world"}),
+ "-Xbootclasspath:/hello/world",
+ M::BootClassPath);
+ EXPECT_SINGLE_PARSE_VALUE(std::vector<std::string>({"/hello", "/world"}),
+ "-Xbootclasspath:/hello:/world",
+ M::BootClassPath);
+ EXPECT_SINGLE_PARSE_VALUE_STR("/hello/world", "-classpath /hello/world", M::ClassPath);
EXPECT_SINGLE_PARSE_VALUE(Memory<1>(234), "-Xss234", M::StackSize);
EXPECT_SINGLE_PARSE_VALUE(MemoryKiB(1234*MB), "-Xms1234m", M::MemoryInitialSize);
EXPECT_SINGLE_PARSE_VALUE(true, "-XX:EnableHSpaceCompactForOOM", M::EnableHSpaceCompactForOOM);
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 66421e2..be6da71 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -22,6 +22,7 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/callee_save_type.h"
+#include "base/casts.h"
#include "base/enums.h"
#include "base/utils.h"
#include "class_linker.h"
@@ -152,6 +153,10 @@
CreateCompilerDriver();
}
+ // Note: We cannot use MemMap because some tests tear down the Runtime and destroy
+ // the gMaps, so when destroying the MemMap, the test would crash.
+ inaccessible_page_ = mmap(nullptr, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ CHECK(inaccessible_page_ != MAP_FAILED) << strerror(errno);
}
void CommonCompilerTest::ApplyInstructionSet() {
@@ -190,9 +195,7 @@
compiler_options_->image_classes_.swap(*GetImageClasses());
compiler_options_->profile_compilation_info_ = GetProfileCompilationInfo();
compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
- verification_results_.get(),
compiler_kind_,
- &compiler_options_->image_classes_,
number_of_threads_,
/* swap_fd */ -1));
}
@@ -222,6 +225,10 @@
verification_results_.reset();
compiler_options_.reset();
image_reservation_.Reset();
+ if (inaccessible_page_ != nullptr) {
+ munmap(inaccessible_page_, kPageSize);
+ inaccessible_page_ = nullptr;
+ }
CommonRuntimeTest::TearDown();
}
@@ -267,8 +274,16 @@
compiler_driver_->InitializeThreadPools();
- compiler_driver_->PreCompile(class_loader, dex_files, &timings);
+ compiler_driver_->PreCompile(class_loader,
+ dex_files,
+ &timings,
+ &compiler_options_->image_classes_,
+ verification_results_.get());
+ // Verification results in the `callback_` should not be used during compilation.
+ down_cast<QuickCompilerCallbacks*>(callbacks_.get())->SetVerificationResults(
+ reinterpret_cast<VerificationResults*>(inaccessible_page_));
+ compiler_options_->verification_results_ = verification_results_.get();
compiler_driver_->CompileOne(self,
class_loader,
*dex_file,
@@ -279,6 +294,9 @@
code_item,
dex_cache,
h_class_loader);
+ compiler_options_->verification_results_ = nullptr;
+ down_cast<QuickCompilerCallbacks*>(callbacks_.get())->SetVerificationResults(
+ verification_results_.get());
compiler_driver_->FreeThreadPools();
@@ -334,6 +352,32 @@
CHECK(image_reservation_.IsValid()) << error_msg;
}
+void CommonCompilerTest::CompileAll(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) {
+ TimingLogger::ScopedTiming t(__FUNCTION__, timings);
+ SetDexFilesForOatFile(dex_files);
+
+ compiler_driver_->InitializeThreadPools();
+
+ compiler_driver_->PreCompile(class_loader,
+ dex_files,
+ timings,
+ &compiler_options_->image_classes_,
+ verification_results_.get());
+
+ // Verification results in the `callback_` should not be used during compilation.
+ down_cast<QuickCompilerCallbacks*>(callbacks_.get())->SetVerificationResults(
+ reinterpret_cast<VerificationResults*>(inaccessible_page_));
+ compiler_options_->verification_results_ = verification_results_.get();
+ compiler_driver_->CompileAll(class_loader, dex_files, timings);
+ compiler_options_->verification_results_ = nullptr;
+ down_cast<QuickCompilerCallbacks*>(callbacks_.get())->SetVerificationResults(
+ verification_results_.get());
+
+ compiler_driver_->FreeThreadPools();
+}
+
void CommonCompilerTest::UnreserveImageSpace() {
image_reservation_.Reset();
}
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index e6d1564..a71908e 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -20,6 +20,8 @@
#include <list>
#include <vector>
+#include <jni.h>
+
#include "arch/instruction_set.h"
#include "arch/instruction_set_features.h"
#include "base/hash_set.h"
@@ -37,6 +39,7 @@
class CumulativeLogger;
class DexFile;
class ProfileCompilationInfo;
+class TimingLogger;
class VerificationResults;
template<class T> class Handle;
@@ -88,6 +91,10 @@
const char* method_name, const char* signature)
REQUIRES_SHARED(Locks::mutator_lock_);
+ void CompileAll(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
+
void ApplyInstructionSet();
void OverrideInstructionSetFeatures(InstructionSet instruction_set, const std::string& variant);
@@ -116,6 +123,7 @@
private:
MemMap image_reservation_;
+ void* inaccessible_page_;
// Chunks must not move their storage after being created - use the node-based std::list.
std::list<std::vector<uint8_t>> header_code_and_maps_chunks_;
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index baf8643..1ecb1d8e 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -138,7 +138,7 @@
CHECK(builder->Good());
std::vector<uint8_t> compressed_buffer;
compressed_buffer.reserve(buffer.size() / 4);
- XzCompress(ArrayRef<uint8_t>(buffer), &compressed_buffer);
+ XzCompress(ArrayRef<const uint8_t>(buffer), &compressed_buffer);
return compressed_buffer;
}
diff --git a/compiler/debug/xz_utils.cc b/compiler/debug/xz_utils.cc
index a9e30a6..a8f60ac 100644
--- a/compiler/debug/xz_utils.cc
+++ b/compiler/debug/xz_utils.cc
@@ -17,13 +17,16 @@
#include "xz_utils.h"
#include <vector>
+#include <mutex>
#include "base/array_ref.h"
-#include "dwarf/writer.h"
+#include "base/bit_utils.h"
#include "base/leb128.h"
+#include "dwarf/writer.h"
// liblzma.
#include "7zCrc.h"
+#include "Xz.h"
#include "XzCrc64.h"
#include "XzEnc.h"
@@ -32,10 +35,17 @@
constexpr size_t kChunkSize = kPageSize;
-static void XzCompressChunk(ArrayRef<uint8_t> src, std::vector<uint8_t>* dst) {
+static void XzInitCrc() {
+ static std::once_flag crc_initialized;
+ std::call_once(crc_initialized, []() {
+ CrcGenerateTable();
+ Crc64GenerateTable();
+ });
+}
+
+static void XzCompressChunk(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst) {
// Configure the compression library.
- CrcGenerateTable();
- Crc64GenerateTable();
+ XzInitCrc();
CLzma2EncProps lzma2Props;
Lzma2EncProps_Init(&lzma2Props);
lzma2Props.lzmaProps.level = 1; // Fast compression.
@@ -62,7 +72,7 @@
return SZ_OK;
}
size_t src_pos_;
- ArrayRef<uint8_t> src_;
+ ArrayRef<const uint8_t> src_;
std::vector<uint8_t>* dst_;
};
XzCallbacks callbacks;
@@ -85,7 +95,7 @@
// In short, the file format is: [header] [compressed_block]* [index] [footer]
// Where [index] is: [num_records] ([compressed_size] [uncompressed_size])* [crc32]
//
-void XzCompress(ArrayRef<uint8_t> src, std::vector<uint8_t>* dst) {
+void XzCompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst) {
uint8_t header[] = { 0xFD, '7', 'z', 'X', 'Z', 0, 0, 1, 0x69, 0x22, 0xDE, 0x36 };
uint8_t footer[] = { 0, 1, 'Y', 'Z' };
dst->insert(dst->end(), header, header + sizeof(header));
@@ -138,6 +148,47 @@
writer.UpdateUint32(0, CrcCalc(tmp.data() + 4, 6));
dst->insert(dst->end(), tmp.begin(), tmp.end());
}
+
+ // Decompress the data back and check that we get the original.
+ if (kIsDebugBuild) {
+ std::vector<uint8_t> decompressed;
+ XzDecompress(ArrayRef<const uint8_t>(*dst), &decompressed);
+ DCHECK_EQ(decompressed.size(), src.size());
+ DCHECK_EQ(memcmp(decompressed.data(), src.data(), src.size()), 0);
+ }
+}
+
+void XzDecompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst) {
+ XzInitCrc();
+ std::unique_ptr<CXzUnpacker> state(new CXzUnpacker());
+ ISzAlloc alloc;
+ alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
+ alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
+ XzUnpacker_Construct(state.get(), &alloc);
+
+ size_t src_offset = 0;
+ size_t dst_offset = 0;
+ ECoderStatus status;
+ do {
+ dst->resize(RoundUp(dst_offset + kPageSize / 4, kPageSize));
+ size_t src_remaining = src.size() - src_offset;
+ size_t dst_remaining = dst->size() - dst_offset;
+ int return_val = XzUnpacker_Code(state.get(),
+ dst->data() + dst_offset,
+ &dst_remaining,
+ src.data() + src_offset,
+ &src_remaining,
+ true,
+ CODER_FINISH_ANY,
+ &status);
+ CHECK_EQ(return_val, SZ_OK);
+ src_offset += src_remaining;
+ dst_offset += dst_remaining;
+ } while (status == CODER_STATUS_NOT_FINISHED);
+ CHECK_EQ(src_offset, src.size());
+ CHECK(XzUnpacker_IsStreamWasFinished(state.get()));
+ XzUnpacker_Free(state.get());
+ dst->resize(dst_offset);
}
} // namespace debug
diff --git a/compiler/debug/xz_utils.h b/compiler/debug/xz_utils.h
index c4076c6..731b03c 100644
--- a/compiler/debug/xz_utils.h
+++ b/compiler/debug/xz_utils.h
@@ -24,7 +24,8 @@
namespace art {
namespace debug {
-void XzCompress(ArrayRef<uint8_t> src, std::vector<uint8_t>* dst);
+void XzCompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst);
+void XzDecompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst);
} // namespace debug
} // namespace art
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 04ad10c..c124ef5 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -528,7 +528,7 @@
class_def_idx,
method_idx,
access_flags,
- driver_->GetVerifiedMethod(&dex_file, method_idx),
+ driver_->GetCompilerOptions().GetVerifiedMethod(&dex_file, method_idx),
hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)));
std::vector<uint8_t> quicken_data;
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index f61e6c4..b055416 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -39,16 +39,15 @@
class DexToDexDecompilerTest : public CommonCompilerTest {
public:
void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
- TimingLogger timings("CompilerDriverTest::CompileAll", false, false);
- TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
+ TimingLogger timings("DexToDexDecompilerTest::CompileAll", false, false);
compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
compiler_options_->SetCompilerFilter(CompilerFilter::kQuicken);
// Create the main VerifierDeps, here instead of in the compiler since we want to aggregate
// the results for all the dex files, not just the results for the current dex file.
down_cast<QuickCompilerCallbacks*>(Runtime::Current()->GetCompilerCallbacks())->SetVerifierDeps(
new verifier::VerifierDeps(GetDexFiles(class_loader)));
- SetDexFilesForOatFile(GetDexFiles(class_loader));
- compiler_driver_->CompileAll(class_loader, GetDexFiles(class_loader), &timings);
+ std::vector<const DexFile*> dex_files = GetDexFiles(class_loader);
+ CommonCompilerTest::CompileAll(class_loader, dex_files, &timings);
}
void RunTest(const char* dex_name) {
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index dd947d9..5a34efb 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -97,7 +97,7 @@
}
}
-const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) {
+const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) const {
const VerifiedMethod* ret = nullptr;
if (atomic_verified_methods_.Get(ref, &ret)) {
return ret;
@@ -129,13 +129,13 @@
DCHECK(IsClassRejected(ref));
}
-bool VerificationResults::IsClassRejected(ClassReference ref) {
+bool VerificationResults::IsClassRejected(ClassReference ref) const {
ReaderMutexLock mu(Thread::Current(), rejected_classes_lock_);
return (rejected_classes_.find(ref) != rejected_classes_.end());
}
bool VerificationResults::IsCandidateForCompilation(MethodReference&,
- const uint32_t access_flags) {
+ const uint32_t access_flags) const {
if (!compiler_options_->IsAotCompilationEnabled()) {
return false;
}
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index 56f0030..04c4fa6 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -51,13 +51,13 @@
void CreateVerifiedMethodFor(MethodReference ref)
REQUIRES(!verified_methods_lock_);
- const VerifiedMethod* GetVerifiedMethod(MethodReference ref)
+ const VerifiedMethod* GetVerifiedMethod(MethodReference ref) const
REQUIRES(!verified_methods_lock_);
void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_);
- bool IsClassRejected(ClassReference ref) REQUIRES(!rejected_classes_lock_);
+ bool IsClassRejected(ClassReference ref) const REQUIRES(!rejected_classes_lock_);
- bool IsCandidateForCompilation(MethodReference& method_ref, const uint32_t access_flags);
+ bool IsCandidateForCompilation(MethodReference& method_ref, const uint32_t access_flags) const;
// Add a dex file to enable using the atomic map.
void AddDexFile(const DexFile* dex_file) REQUIRES(!verified_methods_lock_);
@@ -74,10 +74,12 @@
// GetVerifiedMethod.
AtomicMap atomic_verified_methods_;
- ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ // TODO: External locking during CompilerDriver::PreCompile(), no locking during compilation.
+ mutable ReaderWriterMutex verified_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
// Rejected classes.
- ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ // TODO: External locking during CompilerDriver::PreCompile(), no locking during compilation.
+ mutable ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_);
friend class verifier::VerifierDepsTest;
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 792f508..63dcb46 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -99,11 +99,6 @@
return std::make_pair(fast_get, fast_put);
}
-inline VerificationResults* CompilerDriver::GetVerificationResults() const {
- DCHECK(Runtime::Current()->IsAotCompiler());
- return verification_results_;
-}
-
} // namespace art
#endif // ART_COMPILER_DRIVER_COMPILER_DRIVER_INL_H_
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 67cabef..18f7105 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -245,16 +245,12 @@
CompilerDriver::CompilerDriver(
const CompilerOptions* compiler_options,
- VerificationResults* verification_results,
Compiler::Kind compiler_kind,
- HashSet<std::string>* image_classes,
size_t thread_count,
int swap_fd)
: compiler_options_(compiler_options),
- verification_results_(verification_results),
compiler_(Compiler::Create(this, compiler_kind)),
compiler_kind_(compiler_kind),
- image_classes_(std::move(image_classes)),
number_of_soft_verifier_failures_(0),
had_hard_verifier_failure_(false),
parallel_thread_count_(thread_count),
@@ -266,10 +262,6 @@
compiler_->Init();
- if (GetCompilerOptions().IsBootImage()) {
- CHECK(image_classes_ != nullptr) << "Expected image classes for boot image";
- }
-
compiled_method_storage_.SetDedupeEnabled(compiler_options_->DeduplicateCode());
}
@@ -325,9 +317,8 @@
TimingLogger* timings) {
DCHECK(!Runtime::Current()->IsStarted());
- InitializeThreadPools();
+ CheckThreadPools();
- PreCompile(class_loader, dex_files, timings);
if (GetCompilerOptions().IsBootImage()) {
// We don't need to setup the intrinsics for non boot image compilation, as
// those compilations will pick up a boot image that have the ArtMethod already
@@ -343,8 +334,6 @@
if (GetCompilerOptions().GetDumpStats()) {
stats_->Dump();
}
-
- FreeThreadPools();
}
static optimizer::DexToDexCompiler::CompilationLevel GetDexToDexCompilationLevel(
@@ -496,7 +485,7 @@
optimizer::DexToDexCompiler* const compiler = &driver->GetDexToDexCompiler();
if (compiler->ShouldCompileMethod(method_ref)) {
- VerificationResults* results = driver->GetVerificationResults();
+ const VerificationResults* results = driver->GetCompilerOptions().GetVerificationResults();
DCHECK(results != nullptr);
const VerifiedMethod* verified_method = results->GetVerifiedMethod(method_ref);
// Do not optimize if a VerifiedMethod is missing. SafeCast elision,
@@ -574,7 +563,7 @@
} else if ((access_flags & kAccAbstract) != 0) {
// Abstract methods don't have code.
} else {
- VerificationResults* results = driver->GetVerificationResults();
+ const VerificationResults* results = driver->GetCompilerOptions().GetVerificationResults();
DCHECK(results != nullptr);
const VerifiedMethod* verified_method = results->GetVerifiedMethod(method_ref);
bool compile =
@@ -881,7 +870,9 @@
void CompilerDriver::PreCompile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings) {
+ TimingLogger* timings,
+ /*inout*/ HashSet<std::string>* image_classes,
+ /*out*/ VerificationResults* verification_results) {
CheckThreadPools();
VLOG(compiler) << "Before precompile " << GetMemoryUsageString(false);
@@ -898,7 +889,7 @@
// 6) Update the set of image classes.
// 7) For deterministic boot image, initialize bitstrings for type checking.
- LoadImageClasses(timings);
+ LoadImageClasses(timings, image_classes);
VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false);
if (compiler_options_->IsAnyCompilationEnabled()) {
@@ -932,7 +923,7 @@
ResolveConstStrings(dex_files, /*only_startup_strings=*/ true, timings);
}
- Verify(class_loader, dex_files, timings);
+ Verify(class_loader, dex_files, timings, verification_results);
VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) {
@@ -957,7 +948,7 @@
VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
}
- UpdateImageClasses(timings);
+ UpdateImageClasses(timings, image_classes);
VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false);
if (kBitstringSubtypeCheckEnabled &&
@@ -1071,7 +1062,8 @@
};
// Make a list of descriptors for classes to include in the image
-void CompilerDriver::LoadImageClasses(TimingLogger* timings) {
+void CompilerDriver::LoadImageClasses(TimingLogger* timings,
+ /*inout*/ HashSet<std::string>* image_classes) {
CHECK(timings != nullptr);
if (!GetCompilerOptions().IsBootImage()) {
return;
@@ -1082,15 +1074,15 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- CHECK(image_classes_ != nullptr);
- for (auto it = image_classes_->begin(), end = image_classes_->end(); it != end;) {
+ CHECK(image_classes != nullptr);
+ for (auto it = image_classes->begin(), end = image_classes->end(); it != end;) {
const std::string& descriptor(*it);
StackHandleScope<1> hs(self);
Handle<mirror::Class> klass(
hs.NewHandle(class_linker->FindSystemClass(self, descriptor.c_str())));
if (klass == nullptr) {
VLOG(compiler) << "Failed to find class " << descriptor;
- it = image_classes_->erase(it);
+ it = image_classes->erase(it);
self->ClearException();
} else {
++it;
@@ -1140,10 +1132,10 @@
// We walk the roots looking for classes so that we'll pick up the
// above classes plus any classes them depend on such super
// classes, interfaces, and the required ClassLinker roots.
- RecordImageClassesVisitor visitor(image_classes_);
+ RecordImageClassesVisitor visitor(image_classes);
class_linker->VisitClasses(&visitor);
- CHECK(!image_classes_->empty());
+ CHECK(!image_classes->empty());
}
static void MaybeAddToImageClasses(Thread* self,
@@ -1312,7 +1304,8 @@
DISALLOW_COPY_AND_ASSIGN(ClinitImageUpdate);
};
-void CompilerDriver::UpdateImageClasses(TimingLogger* timings) {
+void CompilerDriver::UpdateImageClasses(TimingLogger* timings,
+ /*inout*/ HashSet<std::string>* image_classes) {
if (GetCompilerOptions().IsBootImage()) {
TimingLogger::ScopedTiming t("UpdateImageClasses", timings);
@@ -1324,7 +1317,7 @@
VariableSizedHandleScope hs(Thread::Current());
std::string error_msg;
std::unique_ptr<ClinitImageUpdate> update(ClinitImageUpdate::Create(hs,
- image_classes_,
+ image_classes,
Thread::Current(),
runtime->GetClassLinker()));
@@ -1393,12 +1386,6 @@
}
}
-const VerifiedMethod* CompilerDriver::GetVerifiedMethod(const DexFile* dex_file,
- uint32_t method_idx) const {
- MethodReference ref(dex_file, method_idx);
- return verification_results_->GetVerifiedMethod(ref);
-}
-
bool CompilerDriver::IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc) {
if (!compiler_options_->IsVerificationEnabled()) {
// If we didn't verify, every cast has to be treated as non-safe.
@@ -1764,7 +1751,8 @@
bool CompilerDriver::FastVerify(jobject jclass_loader,
const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings) {
+ TimingLogger* timings,
+ /*out*/ VerificationResults* verification_results) {
verifier::VerifierDeps* verifier_deps =
Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
// If there exist VerifierDeps that aren't the ones we just created to output, use them to verify.
@@ -1812,7 +1800,7 @@
// - Quickening will not do checkcast ellision.
// TODO(ngeoffray): Reconsider this once we refactor compiler filters.
for (const ClassAccessor::Method& method : accessor.GetMethods()) {
- verification_results_->CreateVerifiedMethodFor(method.GetReference());
+ verification_results->CreateVerifiedMethodFor(method.GetReference());
}
}
} else if (!compiler_only_verifies) {
@@ -1830,8 +1818,9 @@
void CompilerDriver::Verify(jobject jclass_loader,
const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings) {
- if (FastVerify(jclass_loader, dex_files, timings)) {
+ TimingLogger* timings,
+ /*out*/ VerificationResults* verification_results) {
+ if (FastVerify(jclass_loader, dex_files, timings, verification_results)) {
return;
}
@@ -2530,7 +2519,7 @@
// SetVerificationAttempted so that the access flags are set. If we do not do this they get
// changed at runtime resulting in more dirty image pages.
// Also create conflict tables.
- // Only useful if we are compiling an image (image_classes_ is not null).
+ // Only useful if we are compiling an image.
ScopedObjectAccess soa(Thread::Current());
VariableSizedHandleScope hs(soa.Self());
InitializeArrayClassesAndCreateConflictTablesVisitor visitor(hs);
@@ -2569,8 +2558,9 @@
ClassReference ref(&dex_file, class_def_index);
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
ClassAccessor accessor(dex_file, class_def_index);
+ CompilerDriver* const driver = context.GetCompiler();
// Skip compiling classes with generic verifier failures since they will still fail at runtime
- if (context.GetCompiler()->GetVerificationResults()->IsClassRejected(ref)) {
+ if (driver->GetCompilerOptions().GetVerificationResults()->IsClassRejected(ref)) {
return;
}
// Use a scoped object access to perform to the quick SkipClass check.
@@ -2602,8 +2592,6 @@
// Go to native so that we don't block GC during compilation.
ScopedThreadSuspension sts(soa.Self(), kNative);
- CompilerDriver* const driver = context.GetCompiler();
-
// Can we run DEX-to-DEX compiler on this class ?
optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level =
GetDexToDexCompilationLevel(soa.Self(), *driver, jclass_loader, dex_file, class_def);
@@ -2775,31 +2763,6 @@
return compiled_method;
}
-bool CompilerDriver::IsMethodVerifiedWithoutFailures(uint32_t method_idx,
- uint16_t class_def_idx,
- const DexFile& dex_file) const {
- const VerifiedMethod* verified_method = GetVerifiedMethod(&dex_file, method_idx);
- if (verified_method != nullptr) {
- return !verified_method->HasVerificationFailures();
- }
-
- // If we can't find verification metadata, check if this is a system class (we trust that system
- // classes have their methods verified). If it's not, be conservative and assume the method
- // has not been verified successfully.
-
- // TODO: When compiling the boot image it should be safe to assume that everything is verified,
- // even if methods are not found in the verification cache.
- const char* descriptor = dex_file.GetClassDescriptor(dex_file.GetClassDef(class_def_idx));
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- Thread* self = Thread::Current();
- ScopedObjectAccess soa(self);
- bool is_system_class = class_linker->FindSystemClass(self, descriptor) != nullptr;
- if (!is_system_class) {
- self->ClearException();
- }
- return is_system_class;
-}
-
std::string CompilerDriver::GetMemoryUsageString(bool extended) const {
std::ostringstream oss;
const gc::Heap* const heap = Runtime::Current()->GetHeap();
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 714b2d1..7c0fc64 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -76,7 +76,6 @@
class TimingLogger;
class VdexFile;
class VerificationResults;
-class VerifiedMethod;
enum EntryPointCallingConvention {
// ABI of invocations to a method's interpreter entry point.
@@ -95,9 +94,7 @@
// can assume will be in the image, with null implying all available
// classes.
CompilerDriver(const CompilerOptions* compiler_options,
- VerificationResults* verification_results,
Compiler::Kind compiler_kind,
- HashSet<std::string>* image_classes,
size_t thread_count,
int swap_fd);
@@ -106,6 +103,17 @@
// Set dex files classpath.
void SetClasspathDexFiles(const std::vector<const DexFile*>& dex_files);
+ // Initialize and destroy thread pools. This is exposed because we do not want
+ // to do this twice, for PreCompile() and CompileAll().
+ void InitializeThreadPools();
+ void FreeThreadPools();
+
+ void PreCompile(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings,
+ /*inout*/ HashSet<std::string>* image_classes,
+ /*out*/ VerificationResults* verification_results)
+ REQUIRES(!Locks::mutator_lock_);
void CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings)
@@ -124,8 +132,6 @@
Handle<mirror::ClassLoader> h_class_loader)
REQUIRES(!Locks::mutator_lock_);
- VerificationResults* GetVerificationResults() const;
-
const CompilerOptions& GetCompilerOptions() const {
return *compiler_options_;
}
@@ -194,7 +200,6 @@
REQUIRES_SHARED(Locks::mutator_lock_);
- const VerifiedMethod* GetVerifiedMethod(const DexFile* dex_file, uint32_t method_idx) const;
bool IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc);
size_t GetThreadCount() const {
@@ -219,12 +224,6 @@
void RecordClassStatus(const ClassReference& ref, ClassStatus status);
- // Checks if the specified method has been verified without failures. Returns
- // false if the method is not in the verification results (GetVerificationResults).
- bool IsMethodVerifiedWithoutFailures(uint32_t method_idx,
- uint16_t class_def_idx,
- const DexFile& dex_file) const;
-
// Get memory usage during compilation.
std::string GetMemoryUsageString(bool extended) const;
@@ -265,13 +264,9 @@
}
private:
- void PreCompile(jobject class_loader,
- const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings)
+ void LoadImageClasses(TimingLogger* timings, /*inout*/ HashSet<std::string>* image_classes)
REQUIRES(!Locks::mutator_lock_);
- void LoadImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
-
// Attempt to resolve all type, methods, fields, and strings
// referenced from code in the dex file following PathClassLoader
// ordering semantics.
@@ -291,11 +286,13 @@
// verification was successful.
bool FastVerify(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings);
+ TimingLogger* timings,
+ /*out*/ VerificationResults* verification_results);
void Verify(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings);
+ TimingLogger* timings,
+ /*out*/ VerificationResults* verification_results);
void VerifyDexFile(jobject class_loader,
const DexFile& dex_file,
@@ -326,14 +323,13 @@
TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
- void UpdateImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
+ void UpdateImageClasses(TimingLogger* timings, /*inout*/ HashSet<std::string>* image_classes)
+ REQUIRES(!Locks::mutator_lock_);
void Compile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings);
- void InitializeThreadPools();
- void FreeThreadPools();
void CheckThreadPools();
// Resolve const string literals that are loaded from dex code. If only_startup_strings is
@@ -343,7 +339,6 @@
/*inout*/ TimingLogger* timings);
const CompilerOptions* const compiler_options_;
- VerificationResults* const verification_results_;
std::unique_ptr<Compiler> compiler_;
Compiler::Kind compiler_kind_;
@@ -359,12 +354,6 @@
// All method references that this compiler has compiled.
MethodTable compiled_methods_;
- // Image classes to be updated by PreCompile().
- // TODO: Remove this member which is a non-const pointer to the CompilerOptions' data.
- // Pass this explicitly to the PreCompile() which should be called directly from
- // Dex2Oat rather than implicitly by CompileAll().
- HashSet<std::string>* image_classes_;
-
std::atomic<uint32_t> number_of_soft_verifier_failures_;
bool had_hard_verifier_failure_;
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index fe1568d..b924129 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -42,20 +42,18 @@
class CompilerDriverTest : public CommonCompilerTest {
protected:
- void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
- TimingLogger timings("CompilerDriverTest::CompileAll", false, false);
- TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
+ void CompileAllAndMakeExecutable(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
+ TimingLogger timings("CompilerDriverTest::CompileAllAndMakeExecutable", false, false);
dex_files_ = GetDexFiles(class_loader);
- SetDexFilesForOatFile(dex_files_);
- compiler_driver_->CompileAll(class_loader, dex_files_, &timings);
- t.NewTiming("MakeAllExecutable");
+ CompileAll(class_loader, dex_files_, &timings);
+ TimingLogger::ScopedTiming t("MakeAllExecutable", &timings);
MakeAllExecutable(class_loader);
}
void EnsureCompiled(jobject class_loader, const char* class_name, const char* method,
const char* signature, bool is_virtual)
REQUIRES(!Locks::mutator_lock_) {
- CompileAll(class_loader);
+ CompileAllAndMakeExecutable(class_loader);
Thread::Current()->TransitionFromSuspendedToRunnable();
bool started = runtime_->Start();
CHECK(started);
@@ -106,7 +104,7 @@
// Disabled due to 10 second runtime on host
// TODO: Update the test for hash-based dex cache arrays. Bug: 30627598
TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) {
- CompileAll(nullptr);
+ CompileAllAndMakeExecutable(nullptr);
// All libcore references should resolve
ScopedObjectAccess soa(Thread::Current());
@@ -266,7 +264,7 @@
ASSERT_TRUE(dex_file->EnableWrite());
}
- CompileAll(class_loader);
+ CompileAllAndMakeExecutable(class_loader);
std::unordered_set<std::string> m = GetExpectedMethodsForClass("Main");
std::unordered_set<std::string> s = GetExpectedMethodsForClass("Second");
@@ -310,7 +308,7 @@
}
ASSERT_NE(class_loader, nullptr);
- CompileAll(class_loader);
+ CompileAllAndMakeExecutable(class_loader);
CheckVerifiedClass(class_loader, "LMain;");
CheckVerifiedClass(class_loader, "LSecond;");
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index b28c7e0..8d1ae3d 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -24,9 +24,14 @@
#include "arch/instruction_set_features.h"
#include "base/runtime_debug.h"
#include "base/variant_map.h"
+#include "class_linker.h"
#include "cmdline_parser.h"
#include "compiler_options_map-inl.h"
+#include "dex/dex_file-inl.h"
+#include "dex/verification_results.h"
+#include "dex/verified_method.h"
#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
#include "simple_compiler_options_map.h"
namespace art {
@@ -44,6 +49,7 @@
no_inline_from_(),
dex_files_for_oat_file_(),
image_classes_(),
+ verification_results_(nullptr),
image_type_(ImageType::kNone),
compiling_with_core_image_(false),
baseline_(false),
@@ -141,4 +147,40 @@
return image_classes_.find(StringPiece(descriptor)) != image_classes_.end();
}
+const VerificationResults* CompilerOptions::GetVerificationResults() const {
+ DCHECK(Runtime::Current()->IsAotCompiler());
+ return verification_results_;
+}
+
+const VerifiedMethod* CompilerOptions::GetVerifiedMethod(const DexFile* dex_file,
+ uint32_t method_idx) const {
+ MethodReference ref(dex_file, method_idx);
+ return verification_results_->GetVerifiedMethod(ref);
+}
+
+bool CompilerOptions::IsMethodVerifiedWithoutFailures(uint32_t method_idx,
+ uint16_t class_def_idx,
+ const DexFile& dex_file) const {
+ const VerifiedMethod* verified_method = GetVerifiedMethod(&dex_file, method_idx);
+ if (verified_method != nullptr) {
+ return !verified_method->HasVerificationFailures();
+ }
+
+ // If we can't find verification metadata, check if this is a system class (we trust that system
+ // classes have their methods verified). If it's not, be conservative and assume the method
+ // has not been verified successfully.
+
+ // TODO: When compiling the boot image it should be safe to assume that everything is verified,
+ // even if methods are not found in the verification cache.
+ const char* descriptor = dex_file.GetClassDescriptor(dex_file.GetClassDef(class_def_idx));
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ bool is_system_class = class_linker->FindSystemClass(self, descriptor) != nullptr;
+ if (!is_system_class) {
+ self->ClearException();
+ }
+ return is_system_class;
+}
+
} // namespace art
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index a8f246d..bd12bf7 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -47,6 +47,8 @@
enum class InstructionSet;
class InstructionSetFeatures;
class ProfileCompilationInfo;
+class VerificationResults;
+class VerifiedMethod;
// Enum for CheckProfileMethodsCompiled. Outside CompilerOptions so it can be forward-declared.
enum class ProfileMethodsCheck : uint8_t {
@@ -283,6 +285,16 @@
bool IsImageClass(const char* descriptor) const;
+ const VerificationResults* GetVerificationResults() const;
+
+ const VerifiedMethod* GetVerifiedMethod(const DexFile* dex_file, uint32_t method_idx) const;
+
+ // Checks if the specified method has been verified without failures. Returns
+ // false if the method is not in the verification results (GetVerificationResults).
+ bool IsMethodVerifiedWithoutFailures(uint32_t method_idx,
+ uint16_t class_def_idx,
+ const DexFile& dex_file) const;
+
bool ParseCompilerOptions(const std::vector<std::string>& options,
bool ignore_unrecognized,
std::string* error_msg);
@@ -381,6 +393,9 @@
// Must not be empty for real boot image, only for tests pretending to compile boot image.
HashSet<std::string> image_classes_;
+ // Results of AOT verification.
+ const VerificationResults* verification_results_;
+
ImageType image_type_;
bool compiling_with_core_image_;
bool baseline_;
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 0eab835..b9fd868 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -167,9 +167,7 @@
compiler_driver_.reset(new CompilerDriver(
compiler_options_.get(),
- /* verification_results */ nullptr,
Compiler::kOptimizing,
- /* image_classes */ nullptr,
/* thread_count */ 1,
/* swap_fd */ -1));
// Disable dedupe so we can remove compiled methods.
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index ec93222..417d794 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -28,7 +28,6 @@
#include "dex/inline_method_analyser.h"
#include "dex/verification_results.h"
#include "dex/verified_method.h"
-#include "driver/compiler_driver-inl.h"
#include "driver/compiler_options.h"
#include "driver/dex_compilation_unit.h"
#include "instruction_simplifier.h"
@@ -408,7 +407,7 @@
return single_impl;
}
-static bool IsMethodUnverified(CompilerDriver* const compiler_driver, ArtMethod* method)
+static bool IsMethodUnverified(const CompilerOptions& compiler_options, ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (!method->GetDeclaringClass()->IsVerified()) {
if (Runtime::Current()->UseJitCompilation()) {
@@ -417,8 +416,9 @@
return true;
}
uint16_t class_def_idx = method->GetDeclaringClass()->GetDexClassDefIndex();
- if (!compiler_driver->IsMethodVerifiedWithoutFailures(
- method->GetDexMethodIndex(), class_def_idx, *method->GetDexFile())) {
+ if (!compiler_options.IsMethodVerifiedWithoutFailures(method->GetDexMethodIndex(),
+ class_def_idx,
+ *method->GetDexFile())) {
// Method has soft or hard failures, don't analyze.
return true;
}
@@ -426,11 +426,11 @@
return false;
}
-static bool AlwaysThrows(CompilerDriver* const compiler_driver, ArtMethod* method)
+static bool AlwaysThrows(const CompilerOptions& compiler_options, ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(method != nullptr);
// Skip non-compilable and unverified methods.
- if (!method->IsCompilable() || IsMethodUnverified(compiler_driver, method)) {
+ if (!method->IsCompilable() || IsMethodUnverified(compiler_options, method)) {
return false;
}
// Skip native methods, methods with try blocks, and methods that are too large.
@@ -518,7 +518,7 @@
MaybeRecordStat(stats_, MethodCompilationStat::kInlinedInvokeVirtualOrInterface);
}
}
- } else if (!cha_devirtualize && AlwaysThrows(compiler_driver_, actual_method)) {
+ } else if (!cha_devirtualize && AlwaysThrows(codegen_->GetCompilerOptions(), actual_method)) {
// Set always throws property for non-inlined method call with single target
// (unless it was obtained through CHA, because that would imply we have
// to add the CHA dependency, which seems not worth it).
@@ -1500,7 +1500,7 @@
return false;
}
- if (IsMethodUnverified(compiler_driver_, method)) {
+ if (IsMethodUnverified(codegen_->GetCompilerOptions(), method)) {
LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedNotVerified)
<< "Method " << method->PrettyMethod()
<< " couldn't be verified, so it cannot be inlined";
@@ -2069,7 +2069,6 @@
codegen_,
outer_compilation_unit_,
dex_compilation_unit,
- compiler_driver_,
handles_,
inline_stats_,
total_number_of_dex_registers_ + accessor.RegistersSize(),
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 6fd0c20..8ac2163 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -38,7 +38,6 @@
CodeGenerator* codegen,
const DexCompilationUnit& outer_compilation_unit,
const DexCompilationUnit& caller_compilation_unit,
- CompilerDriver* compiler_driver,
VariableSizedHandleScope* handles,
OptimizingCompilerStats* stats,
size_t total_number_of_dex_registers,
@@ -51,7 +50,6 @@
outer_compilation_unit_(outer_compilation_unit),
caller_compilation_unit_(caller_compilation_unit),
codegen_(codegen),
- compiler_driver_(compiler_driver),
total_number_of_dex_registers_(total_number_of_dex_registers),
total_number_of_instructions_(total_number_of_instructions),
parent_(parent),
@@ -280,7 +278,6 @@
const DexCompilationUnit& outer_compilation_unit_;
const DexCompilationUnit& caller_compilation_unit_;
CodeGenerator* const codegen_;
- CompilerDriver* const compiler_driver_;
const size_t total_number_of_dex_registers_;
size_t total_number_of_instructions_;
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 6d04b0e..1688ea7 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -2950,6 +2950,151 @@
__ Mvn(out, out);
}
+// The threshold for sizes of arrays to use the library provided implementation
+// of CRC32.updateBytes instead of the intrinsic.
+static constexpr int32_t kCRC32UpdateBytesThreshold = 64 * 1024;
+
+void IntrinsicLocationsBuilderARM64::VisitCRC32UpdateBytes(HInvoke* invoke) {
+ if (!codegen_->GetInstructionSetFeatures().HasCRC()) {
+ return;
+ }
+
+ LocationSummary* locations
+ = new (allocator_) LocationSummary(invoke,
+ LocationSummary::kCallOnSlowPath,
+ kIntrinsified);
+
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RegisterOrConstant(invoke->InputAt(2)));
+ locations->SetInAt(3, Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+// Lower the invoke of CRC32.updateBytes(int crc, byte[] b, int off, int len)
+//
+// Note: The intrinsic is not used if len exceeds a threshold.
+void IntrinsicCodeGeneratorARM64::VisitCRC32UpdateBytes(HInvoke* invoke) {
+ DCHECK(codegen_->GetInstructionSetFeatures().HasCRC());
+
+ auto masm = GetVIXLAssembler();
+ auto locations = invoke->GetLocations();
+
+ auto slow_path =
+ new (codegen_->GetScopedAllocator()) IntrinsicSlowPathARM64(invoke);
+ codegen_->AddSlowPath(slow_path);
+
+ Register length = WRegisterFrom(locations->InAt(3));
+ __ Cmp(length, kCRC32UpdateBytesThreshold);
+ __ B(slow_path->GetEntryLabel(), hi);
+
+ const uint32_t array_data_offset =
+ mirror::Array::DataOffset(Primitive::kPrimByte).Uint32Value();
+ Register ptr = XRegisterFrom(locations->GetTemp(0));
+ Register array = XRegisterFrom(locations->InAt(1));
+ auto offset = locations->InAt(2);
+ if (offset.IsConstant()) {
+ int32_t offset_value = offset.GetConstant()->AsIntConstant()->GetValue();
+ __ Add(ptr, array, array_data_offset + offset_value);
+ } else {
+ __ Add(ptr, array, array_data_offset);
+ __ Add(ptr, ptr, XRegisterFrom(offset));
+ }
+
+ // The algorithm of CRC32 of bytes is:
+ // crc = ~crc
+ // process a few first bytes to make the array 8-byte aligned
+ // while array has 8 bytes do:
+ // crc = crc32_of_8bytes(crc, 8_bytes(array))
+ // if array has 4 bytes:
+ // crc = crc32_of_4bytes(crc, 4_bytes(array))
+ // if array has 2 bytes:
+ // crc = crc32_of_2bytes(crc, 2_bytes(array))
+ // if array has a byte:
+ // crc = crc32_of_byte(crc, 1_byte(array))
+ // crc = ~crc
+
+ vixl::aarch64::Label loop, done;
+ vixl::aarch64::Label process_4bytes, process_2bytes, process_1byte;
+ vixl::aarch64::Label aligned2, aligned4, aligned8;
+
+ // Use VIXL scratch registers as the VIXL macro assembler won't use them in
+ // instructions below.
+ UseScratchRegisterScope temps(masm);
+ Register len = temps.AcquireW();
+ Register array_elem = temps.AcquireW();
+
+ Register out = WRegisterFrom(locations->Out());
+ __ Mvn(out, WRegisterFrom(locations->InAt(0)));
+ __ Mov(len, length);
+
+ __ Tbz(ptr, 0, &aligned2);
+ __ Subs(len, len, 1);
+ __ B(&done, lo);
+ __ Ldrb(array_elem, MemOperand(ptr, 1, PostIndex));
+ __ Crc32b(out, out, array_elem);
+
+ __ Bind(&aligned2);
+ __ Tbz(ptr, 1, &aligned4);
+ __ Subs(len, len, 2);
+ __ B(&process_1byte, lo);
+ __ Ldrh(array_elem, MemOperand(ptr, 2, PostIndex));
+ __ Crc32h(out, out, array_elem);
+
+ __ Bind(&aligned4);
+ __ Tbz(ptr, 2, &aligned8);
+ __ Subs(len, len, 4);
+ __ B(&process_2bytes, lo);
+ __ Ldr(array_elem, MemOperand(ptr, 4, PostIndex));
+ __ Crc32w(out, out, array_elem);
+
+ __ Bind(&aligned8);
+ __ Subs(len, len, 8);
+ // If len < 8 go to process data by 4 bytes, 2 bytes and a byte.
+ __ B(&process_4bytes, lo);
+
+ // The main loop processing data by 8 bytes.
+ __ Bind(&loop);
+ __ Ldr(array_elem.X(), MemOperand(ptr, 8, PostIndex));
+ __ Subs(len, len, 8);
+ __ Crc32x(out, out, array_elem.X());
+ // if len >= 8, process the next 8 bytes.
+ __ B(&loop, hs);
+
+ // Process the data which is less than 8 bytes.
+ // The code generated below works with values of len
+ // which come in the range [-8, 0].
+ // The first three bits are used to detect whether 4 bytes or 2 bytes or
+ // a byte can be processed.
+ // The checking order is from bit 2 to bit 0:
+ // bit 2 is set: at least 4 bytes available
+ // bit 1 is set: at least 2 bytes available
+ // bit 0 is set: at least a byte available
+ __ Bind(&process_4bytes);
+ // Goto process_2bytes if less than four bytes available
+ __ Tbz(len, 2, &process_2bytes);
+ __ Ldr(array_elem, MemOperand(ptr, 4, PostIndex));
+ __ Crc32w(out, out, array_elem);
+
+ __ Bind(&process_2bytes);
+ // Goto process_1bytes if less than two bytes available
+ __ Tbz(len, 1, &process_1byte);
+ __ Ldrh(array_elem, MemOperand(ptr, 2, PostIndex));
+ __ Crc32h(out, out, array_elem);
+
+ __ Bind(&process_1byte);
+ // Goto done if no bytes available
+ __ Tbz(len, 0, &done);
+ __ Ldrb(array_elem, MemOperand(ptr));
+ __ Crc32b(out, out, array_elem);
+
+ __ Bind(&done);
+ __ Mvn(out, out);
+
+ __ Bind(slow_path->GetExitLabel());
+}
+
UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf);
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 4d45a99..88f1457 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -3060,6 +3060,7 @@
UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, CRC32Update)
+UNIMPLEMENTED_INTRINSIC(ARMVIXL, CRC32UpdateBytes)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 21fb7d7..08ba0a0 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2697,6 +2697,7 @@
UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy)
UNIMPLEMENTED_INTRINSIC(MIPS, CRC32Update)
+UNIMPLEMENTED_INTRINSIC(MIPS, CRC32UpdateBytes)
UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 4b86f5d..59d3ba2 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -2347,6 +2347,7 @@
UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy)
UNIMPLEMENTED_INTRINSIC(MIPS64, CRC32Update)
+UNIMPLEMENTED_INTRINSIC(MIPS64, CRC32UpdateBytes)
UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index a73f4e8..1d94950 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -3071,6 +3071,7 @@
UNIMPLEMENTED_INTRINSIC(X86, IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(X86, LongHighestOneBit)
UNIMPLEMENTED_INTRINSIC(X86, CRC32Update)
+UNIMPLEMENTED_INTRINSIC(X86, CRC32UpdateBytes)
UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 88c766f..4f0b61d 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2738,6 +2738,7 @@
UNIMPLEMENTED_INTRINSIC(X86_64, FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite)
UNIMPLEMENTED_INTRINSIC(X86_64, CRC32Update)
+UNIMPLEMENTED_INTRINSIC(X86_64, CRC32UpdateBytes)
UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc
index 0f971e1..b75afad 100644
--- a/compiler/optimizing/optimization.cc
+++ b/compiler/optimizing/optimization.cc
@@ -181,7 +181,6 @@
HGraph* graph,
OptimizingCompilerStats* stats,
CodeGenerator* codegen,
- CompilerDriver* driver,
const DexCompilationUnit& dex_compilation_unit,
VariableSizedHandleScope* handles) {
ArenaVector<HOptimization*> optimizations(allocator->Adapter());
@@ -258,7 +257,6 @@
codegen,
dex_compilation_unit, // outer_compilation_unit
dex_compilation_unit, // outermost_compilation_unit
- driver,
handles,
stats,
accessor.RegistersSize(),
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
index 490007d..ce44b5f 100644
--- a/compiler/optimizing/optimization.h
+++ b/compiler/optimizing/optimization.h
@@ -147,7 +147,6 @@
HGraph* graph,
OptimizingCompilerStats* stats,
CodeGenerator* codegen,
- CompilerDriver* driver,
const DexCompilationUnit& dex_compilation_unit,
VariableSizedHandleScope* handles);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 641368b..92aaa19 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -321,7 +321,6 @@
graph,
compilation_stats_.get(),
codegen,
- GetCompilerDriver(),
dex_compilation_unit,
handles);
DCHECK_EQ(length, optimizations.size());
@@ -962,9 +961,9 @@
arena_stack,
dex_file,
method_idx,
- compiler_driver->GetCompilerOptions().GetInstructionSet(),
+ compiler_options.GetInstructionSet(),
kInvalidInvokeType,
- compiler_driver->GetCompilerOptions().GetDebuggable(),
+ compiler_options.GetDebuggable(),
/* osr */ false);
DCHECK(Runtime::Current()->IsAotCompiler());
@@ -1043,12 +1042,13 @@
const DexFile& dex_file,
Handle<mirror::DexCache> dex_cache) const {
CompilerDriver* compiler_driver = GetCompilerDriver();
+ const CompilerOptions& compiler_options = compiler_driver->GetCompilerOptions();
CompiledMethod* compiled_method = nullptr;
Runtime* runtime = Runtime::Current();
DCHECK(runtime->IsAotCompiler());
- const VerifiedMethod* verified_method = compiler_driver->GetVerifiedMethod(&dex_file, method_idx);
+ const VerifiedMethod* verified_method = compiler_options.GetVerifiedMethod(&dex_file, method_idx);
DCHECK(!verified_method->HasRuntimeThrow());
- if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file) ||
+ if (compiler_options.IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file) ||
verifier::CanCompilerHandleVerificationFailure(
verified_method->GetEncounteredVerificationFailures())) {
ArenaAllocator allocator(runtime->GetArenaPool());
@@ -1080,7 +1080,7 @@
// Go to native so that we don't block GC during compilation.
ScopedThreadSuspension sts(soa.Self(), kNative);
if (method != nullptr && UNLIKELY(method->IsIntrinsic())) {
- DCHECK(compiler_driver->GetCompilerOptions().IsBootImage());
+ DCHECK(compiler_options.IsBootImage());
codegen.reset(
TryCompileIntrinsic(&allocator,
&arena_stack,
@@ -1099,7 +1099,7 @@
&code_allocator,
dex_compilation_unit,
method,
- compiler_driver->GetCompilerOptions().IsBaseline(),
+ compiler_options.IsBaseline(),
/* osr= */ false,
&handles));
}
@@ -1128,7 +1128,7 @@
}
} else {
MethodCompilationStat method_stat;
- if (compiler_driver->GetCompilerOptions().VerifyAtRuntime()) {
+ if (compiler_options.VerifyAtRuntime()) {
method_stat = MethodCompilationStat::kNotCompiledVerifyAtRuntime;
} else {
method_stat = MethodCompilationStat::kNotCompiledVerificationError;
@@ -1137,8 +1137,8 @@
}
if (kIsDebugBuild &&
- compiler_driver->GetCompilerOptions().CompilingWithCoreImage() &&
- IsInstructionSetSupported(compiler_driver->GetCompilerOptions().GetInstructionSet())) {
+ compiler_options.CompilingWithCoreImage() &&
+ IsInstructionSetSupported(compiler_options.GetInstructionSet())) {
// For testing purposes, we put a special marker on method names
// that should be compiled with this compiler (when the
// instruction set is supported). This makes sure we're not
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index eb44dd7..8c90aa7 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -93,12 +93,12 @@
verifier_deps_.reset(deps);
}
callbacks_->SetVerifierDeps(deps);
- compiler_driver_->Verify(class_loader_, dex_files_, &timings);
+ compiler_driver_->Verify(class_loader_, dex_files_, &timings, verification_results_.get());
callbacks_->SetVerifierDeps(nullptr);
// Clear entries in the verification results to avoid hitting a DCHECK that
// we always succeed inserting a new entry after verifying.
AtomicDexRefMap<MethodReference, const VerifiedMethod*>* map =
- &compiler_driver_->GetVerificationResults()->atomic_verified_methods_;
+ &verification_results_->atomic_verified_methods_;
map->Visit([](const DexFileReference& ref ATTRIBUTE_UNUSED, const VerifiedMethod* method) {
delete method;
});
@@ -126,7 +126,7 @@
class_linker_->RegisterDexFile(*dex_file, loader.Get());
}
for (const DexFile* dex_file : dex_files_) {
- compiler_driver_->GetVerificationResults()->AddDexFile(dex_file);
+ verification_results_->AddDexFile(dex_file);
}
SetDexFilesForOatFile(dex_files_);
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index edd6189..a5bba9b 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -103,6 +103,7 @@
using android::base::StringAppendV;
using android::base::StringPrintf;
+using gc::space::ImageSpace;
static constexpr size_t kDefaultMinDexFilesForSwap = 2;
static constexpr size_t kDefaultMinDexFileCumulativeSizeForSwap = 20 * MB;
@@ -630,6 +631,7 @@
thread_count_(sysconf(_SC_NPROCESSORS_CONF)),
start_ns_(NanoTime()),
start_cputime_ns_(ProcessCpuNanoTime()),
+ strip_(false),
oat_fd_(-1),
input_vdex_fd_(-1),
output_vdex_fd_(-1),
@@ -963,89 +965,22 @@
}
void ExpandOatAndImageFilenames() {
- std::string base_oat = oat_filenames_[0];
- size_t last_oat_slash = base_oat.rfind('/');
- if (last_oat_slash == std::string::npos) {
- Usage("Unusable boot image oat filename %s", base_oat.c_str());
+ if (image_filenames_[0].rfind('/') == std::string::npos) {
+ Usage("Unusable boot image filename %s", image_filenames_[0].c_str());
}
- // We also need to honor path components that were encoded through '@'. Otherwise the loading
- // code won't be able to find the images.
- if (base_oat.find('@', last_oat_slash) != std::string::npos) {
- last_oat_slash = base_oat.rfind('@');
- }
- base_oat = base_oat.substr(0, last_oat_slash + 1);
+ image_filenames_ = ImageSpace::ExpandMultiImageLocations(dex_locations_, image_filenames_[0]);
- std::string base_img = image_filenames_[0];
- size_t last_img_slash = base_img.rfind('/');
- if (last_img_slash == std::string::npos) {
- Usage("Unusable boot image filename %s", base_img.c_str());
+ if (oat_filenames_[0].rfind('/') == std::string::npos) {
+ Usage("Unusable boot image oat filename %s", oat_filenames_[0].c_str());
}
- // We also need to honor path components that were encoded through '@'. Otherwise the loading
- // code won't be able to find the images.
- if (base_img.find('@', last_img_slash) != std::string::npos) {
- last_img_slash = base_img.rfind('@');
- }
+ oat_filenames_ = ImageSpace::ExpandMultiImageLocations(dex_locations_, oat_filenames_[0]);
- // Get the prefix, which is the primary image name (without path components). Strip the
- // extension.
- std::string prefix = base_img.substr(last_img_slash + 1);
- if (prefix.rfind('.') != std::string::npos) {
- prefix = prefix.substr(0, prefix.rfind('.'));
- }
- if (!prefix.empty()) {
- prefix = prefix + "-";
- }
-
- base_img = base_img.substr(0, last_img_slash + 1);
-
- std::string base_symbol_oat;
if (!oat_unstripped_.empty()) {
- base_symbol_oat = oat_unstripped_[0];
- size_t last_symbol_oat_slash = base_symbol_oat.rfind('/');
- if (last_symbol_oat_slash == std::string::npos) {
- Usage("Unusable boot image symbol filename %s", base_symbol_oat.c_str());
+ if (oat_unstripped_[0].rfind('/') == std::string::npos) {
+ Usage("Unusable boot image symbol filename %s", oat_unstripped_[0].c_str());
}
- base_symbol_oat = base_symbol_oat.substr(0, last_symbol_oat_slash + 1);
+ oat_unstripped_ = ImageSpace::ExpandMultiImageLocations(dex_locations_, oat_unstripped_[0]);
}
-
- // Now create the other names. Use a counted loop to skip the first one.
- for (size_t i = 1; i < dex_locations_.size(); ++i) {
- // TODO: Make everything properly std::string.
- std::string image_name = CreateMultiImageName(dex_locations_[i], prefix, ".art");
- char_backing_storage_.push_front(base_img + image_name);
- image_filenames_.push_back(char_backing_storage_.front().c_str());
-
- std::string oat_name = CreateMultiImageName(dex_locations_[i], prefix, ".oat");
- char_backing_storage_.push_front(base_oat + oat_name);
- oat_filenames_.push_back(char_backing_storage_.front().c_str());
-
- if (!base_symbol_oat.empty()) {
- char_backing_storage_.push_front(base_symbol_oat + oat_name);
- oat_unstripped_.push_back(char_backing_storage_.front().c_str());
- }
- }
- }
-
- // Modify the input string in the following way:
- // 0) Assume input is /a/b/c.d
- // 1) Strip the path -> c.d
- // 2) Inject prefix p -> pc.d
- // 3) Replace suffix with s if it's "jar" -> d == "jar" -> pc.s
- static std::string CreateMultiImageName(std::string in,
- const std::string& prefix,
- const char* replace_suffix) {
- size_t last_dex_slash = in.rfind('/');
- if (last_dex_slash != std::string::npos) {
- in = in.substr(last_dex_slash + 1);
- }
- if (!prefix.empty()) {
- in = prefix + in;
- }
- if (android::base::EndsWith(in, ".jar")) {
- in = in.substr(0, in.length() - strlen(".jar")) +
- (replace_suffix != nullptr ? replace_suffix : "");
- }
- return in;
}
void InsertCompileOptions(int argc, char** argv) {
@@ -1496,11 +1431,8 @@
if (IsBootImage()) {
// If we're compiling the boot image, store the boot classpath into the Key-Value store.
- // We need this for the multi-image case.
- key_value_store_->Put(OatHeader::kBootClassPathKey,
- gc::space::ImageSpace::GetMultiImageBootClassPath(dex_locations_,
- oat_filenames_,
- image_filenames_));
+ // We use this when loading the boot image.
+ key_value_store_->Put(OatHeader::kBootClassPathKey, android::base::Join(dex_locations_, ':'));
}
if (!IsBootImage()) {
@@ -1512,8 +1444,7 @@
if (CompilerFilter::DependsOnImageChecksum(compiler_options_->GetCompilerFilter())) {
TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
- std::vector<gc::space::ImageSpace*> image_spaces =
- Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ std::vector<ImageSpace*> image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces();
boot_image_checksum_ = image_spaces[0]->GetImageHeader().GetImageChecksum();
} else {
boot_image_checksum_ = 0u;
@@ -1788,9 +1719,7 @@
compiler_options_->profile_compilation_info_ = profile_compilation_info_.get();
driver_.reset(new CompilerDriver(compiler_options_.get(),
- verification_results_.get(),
compiler_kind_,
- &compiler_options_->image_classes_,
thread_count_,
swap_fd_));
if (!IsBootImage()) {
@@ -1862,7 +1791,16 @@
<< soa.Self()->GetException()->Dump();
}
}
+ driver_->InitializeThreadPools();
+ driver_->PreCompile(class_loader,
+ dex_files,
+ timings_,
+ &compiler_options_->image_classes_,
+ verification_results_.get());
+ callbacks_->SetVerificationResults(nullptr); // Should not be needed anymore.
+ compiler_options_->verification_results_ = verification_results_.get();
driver_->CompileAll(class_loader, dex_files, timings_);
+ driver_->FreeThreadPools();
return class_loader;
}
@@ -1945,7 +1883,7 @@
if (IsImage()) {
if (IsAppImage() && image_base_ == 0) {
gc::Heap* const heap = Runtime::Current()->GetHeap();
- for (gc::space::ImageSpace* image_space : heap->GetBootImageSpaces()) {
+ for (ImageSpace* image_space : heap->GetBootImageSpaces()) {
image_base_ = std::max(image_base_, RoundUp(
reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatFileEnd()),
kPageSize));
diff --git a/dex2oat/linker/image_test.cc b/dex2oat/linker/image_test.cc
index ebd829b..1a5701d 100644
--- a/dex2oat/linker/image_test.cc
+++ b/dex2oat/linker/image_test.cc
@@ -73,7 +73,10 @@
uint32_t oat_data_end = ART_BASE_ADDRESS + (9 * KB);
uint32_t oat_file_end = ART_BASE_ADDRESS + (10 * KB);
ImageSection sections[ImageHeader::kSectionCount];
- ImageHeader image_header(image_begin,
+ uint32_t image_reservation_size = RoundUp(oat_file_end - image_begin, kPageSize);
+ ImageHeader image_header(image_reservation_size,
+ /*component_count=*/ 1u,
+ image_begin,
image_size_,
sections,
image_roots,
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 6ffcef1..bd8cf5a 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -169,10 +169,11 @@
{
// Create a generic tmp file, to be the base of the .art and .oat temporary files.
ScratchFile location;
- for (int i = 0; i < static_cast<int>(class_path.size()); ++i) {
- std::string cur_location =
- android::base::StringPrintf("%s-%d.art", location.GetFilename().c_str(), i);
- out_helper.image_locations.push_back(ScratchFile(cur_location));
+ std::vector<std::string> image_locations =
+ gc::space::ImageSpace::ExpandMultiImageLocations(out_helper.dex_file_locations,
+ location.GetFilename() + ".art");
+ for (size_t i = 0u; i != class_path.size(); ++i) {
+ out_helper.image_locations.push_back(ScratchFile(image_locations[i]));
}
}
std::vector<std::string> image_filenames;
@@ -218,17 +219,12 @@
{
jobject class_loader = nullptr;
TimingLogger timings("ImageTest::WriteRead", false, false);
- TimingLogger::ScopedTiming t("CompileAll", &timings);
- SetDexFilesForOatFile(class_path);
- driver->CompileAll(class_loader, class_path, &timings);
+ CompileAll(class_loader, class_path, &timings);
- t.NewTiming("WriteElf");
+ TimingLogger::ScopedTiming t("WriteElf", &timings);
SafeMap<std::string, std::string> key_value_store;
key_value_store.Put(OatHeader::kBootClassPathKey,
- gc::space::ImageSpace::GetMultiImageBootClassPath(
- out_helper.dex_file_locations,
- oat_filenames,
- image_filenames));
+ android::base::Join(out_helper.dex_file_locations, ':'));
std::vector<std::unique_ptr<ElfWriter>> elf_writers;
std::vector<std::unique_ptr<OatWriter>> oat_writers;
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 231ac04..61d105f 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -2583,6 +2583,23 @@
const uint8_t* oat_file_end = oat_file_begin + image_info.oat_loaded_size_;
const uint8_t* oat_data_end = image_info.oat_data_begin_ + image_info.oat_size_;
+ uint32_t image_reservation_size = image_info.image_size_;
+ DCHECK_ALIGNED(image_reservation_size, kPageSize);
+ uint32_t component_count = 1u;
+ if (!compiler_options_.IsAppImage()) {
+ if (oat_index == 0u) {
+ const ImageInfo& last_info = image_infos_.back();
+ const uint8_t* end = last_info.oat_file_begin_ + last_info.oat_loaded_size_;
+ DCHECK_ALIGNED(image_info.image_begin_, kPageSize);
+ image_reservation_size =
+ dchecked_integral_cast<uint32_t>(RoundUp(end - image_info.image_begin_, kPageSize));
+ component_count = image_infos_.size();
+ } else {
+ image_reservation_size = 0u;
+ component_count = 0u;
+ }
+ }
+
// Create the image sections.
auto section_info_pair = image_info.CreateImageSections();
const size_t image_end = section_info_pair.first;
@@ -2619,6 +2636,8 @@
// Create the header, leave 0 for data size since we will fill this in as we are writing the
// image.
new (image_info.image_.Begin()) ImageHeader(
+ image_reservation_size,
+ component_count,
PointerToLowMemUInt32(image_info.image_begin_),
image_end,
sections.data(),
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 6b17ef6..d045698 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -968,7 +968,7 @@
ClassStatus status;
bool found = writer_->compiler_driver_->GetCompiledClass(class_ref, &status);
if (!found) {
- VerificationResults* results = writer_->compiler_driver_->GetVerificationResults();
+ const VerificationResults* results = writer_->compiler_options_.GetVerificationResults();
if (results != nullptr && results->IsClassRejected(class_ref)) {
// The oat class status is used only for verification of resolved classes,
// so use ClassStatus::kErrorResolved whether the class was resolved or unresolved
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 598a0f8..5de1540 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -391,8 +391,7 @@
jobject class_loader = nullptr;
if (kCompile) {
TimingLogger timings2("OatTest::WriteRead", false, false);
- SetDexFilesForOatFile(class_linker->GetBootClassPath());
- compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings2);
+ CompileAll(class_loader, class_linker->GetBootClassPath(), &timings2);
}
ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
@@ -405,7 +404,7 @@
ASSERT_TRUE(success);
if (kCompile) { // OatWriter strips the code, regenerate to compare
- compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
+ CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
}
std::unique_ptr<OatFile> oat_file(OatFile::Open(/*zip_fd=*/ -1,
tmp_oat.GetFilename(),
@@ -513,8 +512,7 @@
ScopedObjectAccess soa(Thread::Current());
class_linker->RegisterDexFile(*dex_file, soa.Decode<mirror::ClassLoader>(class_loader));
}
- SetDexFilesForOatFile(dex_files);
- compiler_driver_->CompileAll(class_loader, dex_files, &timings);
+ CompileAll(class_loader, dex_files, &timings);
ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
SafeMap<std::string, std::string> key_value_store;
diff --git a/libartbase/base/utils.h b/libartbase/base/utils.h
index 9c71055..11472a8 100644
--- a/libartbase/base/utils.h
+++ b/libartbase/base/utils.h
@@ -172,30 +172,6 @@
}
}
-// Returns a type cast pointer if object pointed to is within the provided bounds.
-// Otherwise returns nullptr.
-template <typename T>
-inline static T BoundsCheckedCast(const void* pointer,
- const void* lower,
- const void* upper) {
- const uint8_t* bound_begin = static_cast<const uint8_t*>(lower);
- const uint8_t* bound_end = static_cast<const uint8_t*>(upper);
- DCHECK(bound_begin <= bound_end);
-
- T result = reinterpret_cast<T>(pointer);
- const uint8_t* begin = static_cast<const uint8_t*>(pointer);
- const uint8_t* end = begin + sizeof(*result);
- if (begin < bound_begin || end > bound_end || begin > end) {
- return nullptr;
- }
- return result;
-}
-
-template <typename T, size_t size>
-constexpr size_t ArrayCount(const T (&)[size]) {
- return size;
-}
-
// Return -1 if <, 0 if ==, 1 if >.
template <typename T>
inline static int32_t Compare(T lhs, T rhs) {
diff --git a/libartbase/base/utils_test.cc b/libartbase/base/utils_test.cc
index 9bd50c3..c3b61ce 100644
--- a/libartbase/base/utils_test.cc
+++ b/libartbase/base/utils_test.cc
@@ -107,25 +107,6 @@
EXPECT_EQ(expected, actual);
}
-TEST_F(UtilsTest, ArrayCount) {
- int i[64];
- EXPECT_EQ(ArrayCount(i), 64u);
- char c[7];
- EXPECT_EQ(ArrayCount(c), 7u);
-}
-
-TEST_F(UtilsTest, BoundsCheckedCast) {
- char buffer[64];
- const char* buffer_end = buffer + ArrayCount(buffer);
- EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(nullptr, buffer, buffer_end), nullptr);
- EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer, buffer, buffer_end),
- reinterpret_cast<const uint64_t*>(buffer));
- EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 56, buffer, buffer_end),
- reinterpret_cast<const uint64_t*>(buffer + 56));
- EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer - 1, buffer, buffer_end), nullptr);
- EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 57, buffer, buffer_end), nullptr);
-}
-
TEST_F(UtilsTest, GetProcessStatus) {
EXPECT_EQ("utils_test", GetProcessStatus("Name"));
EXPECT_EQ("R (running)", GetProcessStatus("State"));
diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h
index f0f14c6..334b072 100644
--- a/libdexfile/dex/class_accessor-inl.h
+++ b/libdexfile/dex/class_accessor-inl.h
@@ -21,6 +21,7 @@
#include "base/hiddenapi_flags.h"
#include "base/leb128.h"
+#include "base/utils.h"
#include "class_iterator.h"
#include "code_item_accessors-inl.h"
diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h
index adfc864..bd7b912 100644
--- a/libdexfile/dex/class_accessor.h
+++ b/libdexfile/dex/class_accessor.h
@@ -17,7 +17,6 @@
#ifndef ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_H_
#define ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_H_
-#include "base/utils.h"
#include "code_item_accessors.h"
#include "dex_file.h"
#include "invoke_type.h"
diff --git a/libdexfile/dex/utf.cc b/libdexfile/dex/utf.cc
index d09da73..ed07568 100644
--- a/libdexfile/dex/utf.cc
+++ b/libdexfile/dex/utf.cc
@@ -194,9 +194,10 @@
uint32_t ComputeModifiedUtf8Hash(const char* chars) {
uint32_t hash = 0;
while (*chars != '\0') {
- hash = hash * 31 + *chars++;
+ hash = hash * 31 + static_cast<uint8_t>(*chars);
+ ++chars;
}
- return static_cast<int32_t>(hash);
+ return hash;
}
int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8, const uint16_t* utf16,
diff --git a/libdexfile/dex/utf_test.cc b/libdexfile/dex/utf_test.cc
index d2f22d1..c7a6a34 100644
--- a/libdexfile/dex/utf_test.cc
+++ b/libdexfile/dex/utf_test.cc
@@ -378,4 +378,11 @@
}
}
+TEST_F(UtfTest, NonAscii) {
+ const char kNonAsciiCharacter = '\x80';
+ const char input[] = { kNonAsciiCharacter, '\0' };
+ uint32_t hash = ComputeModifiedUtf8Hash(input);
+ EXPECT_EQ(static_cast<uint8_t>(kNonAsciiCharacter), hash);
+}
+
} // namespace art
diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h
index 728939f..dfa659b 100644
--- a/oatdump/oatdump_test.h
+++ b/oatdump/oatdump_test.h
@@ -178,6 +178,11 @@
expected_prefixes.push_back("InlineInfo");
}
if (mode == kModeArt) {
+ exec_argv.push_back("--runtime-arg");
+ exec_argv.push_back(GetClassPathOption("-Xbootclasspath:", GetLibCoreDexFileNames()));
+ exec_argv.push_back("--runtime-arg");
+ exec_argv.push_back(
+ GetClassPathOption("-Xbootclasspath-locations:", GetLibCoreDexLocations()));
exec_argv.push_back("--image=" + core_art_location_);
exec_argv.push_back("--instruction-set=" + std::string(
GetInstructionSetString(kRuntimeISA)));
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index c240017..fda269c 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -21,7 +21,6 @@
#include "art_field.h"
#include "base/callee_save_type.h"
-#include "base/utils.h"
#include "class_linker-inl.h"
#include "common_throws.h"
#include "dex/code_item_accessors-inl.h"
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 882291f..5f5361a 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -814,7 +814,12 @@
}
ArtMethod* m =
GetInterfaceMethodIfProxy(Runtime::Current()->GetClassLinker()->GetImagePointerSize());
- return m->GetDexFile()->PrettyMethod(m->GetDexMethodIndex(), with_signature);
+ std::string res(m->GetDexFile()->PrettyMethod(m->GetDexMethodIndex(), with_signature));
+ if (with_signature && m->IsObsolete()) {
+ return "<OBSOLETE> " + res;
+ } else {
+ return res;
+ }
}
std::string ArtMethod::JniShortName() {
diff --git a/runtime/barrier.cc b/runtime/barrier.cc
index 8d3cf45..a1a3659 100644
--- a/runtime/barrier.cc
+++ b/runtime/barrier.cc
@@ -27,15 +27,15 @@
Barrier::Barrier(int count)
: count_(count),
- lock_("GC barrier lock", kThreadSuspendCountLock),
- condition_("GC barrier condition", lock_) {
+ lock_(new Mutex("GC barrier lock", kThreadSuspendCountLock)),
+ condition_(new ConditionVariable("GC barrier condition", *lock_)) {
}
template void Barrier::Increment<Barrier::kAllowHoldingLocks>(Thread* self, int delta);
template void Barrier::Increment<Barrier::kDisallowHoldingLocks>(Thread* self, int delta);
void Barrier::Pass(Thread* self) {
- MutexLock mu(self, lock_);
+ MutexLock mu(self, *GetLock());
SetCountLocked(self, count_ - 1);
}
@@ -44,13 +44,13 @@
}
void Barrier::Init(Thread* self, int count) {
- MutexLock mu(self, lock_);
+ MutexLock mu(self, *GetLock());
SetCountLocked(self, count);
}
template <Barrier::LockHandling locks>
void Barrier::Increment(Thread* self, int delta) {
- MutexLock mu(self, lock_);
+ MutexLock mu(self, *GetLock());
SetCountLocked(self, count_ + delta);
// Increment the count. If it becomes zero after the increment
@@ -62,22 +62,22 @@
// condition variable, thus waking this up.
while (count_ != 0) {
if (locks == kAllowHoldingLocks) {
- condition_.WaitHoldingLocks(self);
+ condition_->WaitHoldingLocks(self);
} else {
- condition_.Wait(self);
+ condition_->Wait(self);
}
}
}
bool Barrier::Increment(Thread* self, int delta, uint32_t timeout_ms) {
- MutexLock mu(self, lock_);
+ MutexLock mu(self, *GetLock());
SetCountLocked(self, count_ + delta);
bool timed_out = false;
if (count_ != 0) {
uint32_t timeout_ns = 0;
uint64_t abs_timeout = NanoTime() + MsToNs(timeout_ms);
for (;;) {
- timed_out = condition_.TimedWait(self, timeout_ms, timeout_ns);
+ timed_out = condition_->TimedWait(self, timeout_ms, timeout_ns);
if (timed_out || count_ == 0) return timed_out;
// Compute time remaining on timeout.
uint64_t now = NanoTime();
@@ -91,14 +91,14 @@
}
int Barrier::GetCount(Thread* self) {
- MutexLock mu(self, lock_);
+ MutexLock mu(self, *GetLock());
return count_;
}
void Barrier::SetCountLocked(Thread* self, int count) {
count_ = count;
if (count == 0) {
- condition_.Broadcast(self);
+ condition_->Broadcast(self);
}
}
diff --git a/runtime/barrier.h b/runtime/barrier.h
index 8a38c4c..e21627e 100644
--- a/runtime/barrier.h
+++ b/runtime/barrier.h
@@ -28,10 +28,14 @@
#define ART_RUNTIME_BARRIER_H_
#include <memory>
-#include "base/mutex.h"
+
+#include "base/locks.h"
namespace art {
+class ConditionVariable;
+class LOCKABLE Mutex;
+
// TODO: Maybe give this a better name.
class Barrier {
public:
@@ -44,10 +48,10 @@
virtual ~Barrier();
// Pass through the barrier, decrement the count but do not block.
- void Pass(Thread* self) REQUIRES(!lock_);
+ void Pass(Thread* self) REQUIRES(!GetLock());
// Wait on the barrier, decrement the count.
- void Wait(Thread* self) REQUIRES(!lock_);
+ void Wait(Thread* self) REQUIRES(!GetLock());
// The following three calls are only safe if we somehow know that no other thread both
// - has been woken up, and
@@ -58,26 +62,30 @@
// Increment the count by delta, wait on condition if count is non zero. If LockHandling is
// kAllowHoldingLocks we will not check that all locks are released when waiting.
template <Barrier::LockHandling locks = kDisallowHoldingLocks>
- void Increment(Thread* self, int delta) REQUIRES(!lock_);
+ void Increment(Thread* self, int delta) REQUIRES(!GetLock());
// Increment the count by delta, wait on condition if count is non zero, with a timeout. Returns
// true if time out occurred.
- bool Increment(Thread* self, int delta, uint32_t timeout_ms) REQUIRES(!lock_);
+ bool Increment(Thread* self, int delta, uint32_t timeout_ms) REQUIRES(!GetLock());
// Set the count to a new value. This should only be used if there is no possibility that
// another thread is still in Wait(). See above.
- void Init(Thread* self, int count) REQUIRES(!lock_);
+ void Init(Thread* self, int count) REQUIRES(!GetLock());
- int GetCount(Thread* self) REQUIRES(!lock_);
+ int GetCount(Thread* self) REQUIRES(!GetLock());
private:
- void SetCountLocked(Thread* self, int count) REQUIRES(lock_);
+ void SetCountLocked(Thread* self, int count) REQUIRES(GetLock());
+
+ Mutex* GetLock() {
+ return lock_.get();
+ }
// Counter, when this reaches 0 all people blocked on the barrier are signalled.
- int count_ GUARDED_BY(lock_);
+ int count_ GUARDED_BY(GetLock());
- Mutex lock_ ACQUIRED_AFTER(Locks::abort_lock_);
- ConditionVariable condition_ GUARDED_BY(lock_);
+ std::unique_ptr<Mutex> lock_ ACQUIRED_AFTER(Locks::abort_lock_);
+ std::unique_ptr<ConditionVariable> condition_ GUARDED_BY(GetLock());
};
} // namespace art
diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc
index 23ec3e1..0a4cddd 100644
--- a/runtime/base/timing_logger.cc
+++ b/runtime/base/timing_logger.cc
@@ -21,6 +21,7 @@
#include <android-base/logging.h>
#include "base/histogram-inl.h"
+#include "base/mutex.h"
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/time_utils.h"
@@ -40,7 +41,7 @@
CumulativeLogger::CumulativeLogger(const std::string& name)
: name_(name),
lock_name_("CumulativeLoggerLock" + name),
- lock_(lock_name_.c_str(), kDefaultMutexLevel, true) {
+ lock_(new Mutex(lock_name_.c_str(), kDefaultMutexLevel, true)) {
Reset();
}
@@ -49,7 +50,7 @@
}
void CumulativeLogger::SetName(const std::string& name) {
- MutexLock mu(Thread::Current(), lock_);
+ MutexLock mu(Thread::Current(), *GetLock());
name_.assign(name);
}
@@ -57,19 +58,19 @@
}
void CumulativeLogger::End() {
- MutexLock mu(Thread::Current(), lock_);
+ MutexLock mu(Thread::Current(), *GetLock());
++iterations_;
}
void CumulativeLogger::Reset() {
- MutexLock mu(Thread::Current(), lock_);
+ MutexLock mu(Thread::Current(), *GetLock());
iterations_ = 0;
total_time_ = 0;
STLDeleteElements(&histograms_);
}
void CumulativeLogger::AddLogger(const TimingLogger &logger) {
- MutexLock mu(Thread::Current(), lock_);
+ MutexLock mu(Thread::Current(), *GetLock());
TimingLogger::TimingData timing_data(logger.CalculateTimingData());
const std::vector<TimingLogger::Timing>& timings = logger.GetTimings();
for (size_t i = 0; i < timings.size(); ++i) {
@@ -81,12 +82,12 @@
}
size_t CumulativeLogger::GetIterations() const {
- MutexLock mu(Thread::Current(), lock_);
+ MutexLock mu(Thread::Current(), *GetLock());
return iterations_;
}
void CumulativeLogger::Dump(std::ostream &os) const {
- MutexLock mu(Thread::Current(), lock_);
+ MutexLock mu(Thread::Current(), *GetLock());
DumpHistogram(os);
}
diff --git a/runtime/base/timing_logger.h b/runtime/base/timing_logger.h
index a8a6701..974a14d 100644
--- a/runtime/base/timing_logger.h
+++ b/runtime/base/timing_logger.h
@@ -18,10 +18,11 @@
#define ART_RUNTIME_BASE_TIMING_LOGGER_H_
#include "base/histogram.h"
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "base/time_utils.h"
+#include <memory>
#include <set>
#include <string>
#include <vector>
@@ -34,17 +35,17 @@
explicit CumulativeLogger(const std::string& name);
~CumulativeLogger();
void Start();
- void End() REQUIRES(!lock_);
- void Reset() REQUIRES(!lock_);
- void Dump(std::ostream& os) const REQUIRES(!lock_);
+ void End() REQUIRES(!GetLock());
+ void Reset() REQUIRES(!GetLock());
+ void Dump(std::ostream& os) const REQUIRES(!GetLock());
uint64_t GetTotalNs() const {
return GetTotalTime() * kAdjust;
}
// Allow the name to be modified, particularly when the cumulative logger is a field within a
// parent class that is unable to determine the "name" of a sub-class.
- void SetName(const std::string& name) REQUIRES(!lock_);
- void AddLogger(const TimingLogger& logger) REQUIRES(!lock_);
- size_t GetIterations() const REQUIRES(!lock_);
+ void SetName(const std::string& name) REQUIRES(!GetLock());
+ void AddLogger(const TimingLogger& logger) REQUIRES(!GetLock());
+ size_t GetIterations() const REQUIRES(!GetLock());
private:
class HistogramComparator {
@@ -58,18 +59,22 @@
static constexpr size_t kDefaultBucketCount = 100;
static constexpr size_t kInitialBucketSize = 50; // 50 microseconds.
- void AddPair(const std::string &label, uint64_t delta_time)
- REQUIRES(lock_);
- void DumpHistogram(std::ostream &os) const REQUIRES(lock_);
+ void AddPair(const std::string &label, uint64_t delta_time) REQUIRES(GetLock());
+ void DumpHistogram(std::ostream &os) const REQUIRES(GetLock());
uint64_t GetTotalTime() const {
return total_time_;
}
+
+ Mutex* GetLock() const {
+ return lock_.get();
+ }
+
static const uint64_t kAdjust = 1000;
- std::set<Histogram<uint64_t>*, HistogramComparator> histograms_ GUARDED_BY(lock_);
+ std::set<Histogram<uint64_t>*, HistogramComparator> histograms_ GUARDED_BY(GetLock());
std::string name_;
const std::string lock_name_;
- mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- size_t iterations_ GUARDED_BY(lock_);
+ mutable std::unique_ptr<Mutex> lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ size_t iterations_ GUARDED_BY(GetLock());
uint64_t total_time_;
DISALLOW_COPY_AND_ASSIGN(CumulativeLogger);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9b2e1a1..e31fe63 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1037,13 +1037,20 @@
runtime->SetSentinel(heap->AllocNonMovableObject<true>(
self, java_lang_Object, java_lang_Object->GetObjectSize(), VoidFunctor()));
- for (gc::space::ImageSpace* image_space : spaces) {
+ const std::vector<std::string>& boot_class_path = runtime->GetBootClassPath();
+ if (boot_class_path.size() != spaces.size()) {
+ *error_msg = StringPrintf("Boot class path has %zu components but there are %zu image spaces.",
+ boot_class_path.size(),
+ spaces.size());
+ return false;
+ }
+ for (size_t i = 0u, size = spaces.size(); i != size; ++i) {
// Boot class loader, use a null handle.
std::vector<std::unique_ptr<const DexFile>> dex_files;
- if (!AddImageSpace(image_space,
+ if (!AddImageSpace(spaces[i],
ScopedNullHandle<mirror::ClassLoader>(),
- /*dex_elements=*/nullptr,
- /*dex_location=*/nullptr,
+ /*dex_elements=*/ nullptr,
+ /*dex_location=*/ boot_class_path[i].c_str(),
/*out*/&dex_files,
error_msg)) {
return false;
@@ -1981,13 +1988,7 @@
std::string dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
// TODO: Only store qualified paths.
// If non qualified, qualify it.
- if (dex_file_location.find('/') == std::string::npos) {
- std::string dex_location_path = dex_location;
- const size_t pos = dex_location_path.find_last_of('/');
- CHECK_NE(pos, std::string::npos);
- dex_location_path = dex_location_path.substr(0, pos + 1); // Keep trailing '/'
- dex_file_location = dex_location_path + dex_file_location;
- }
+ dex_file_location = OatFile::ResolveRelativeEncodedDexLocation(dex_location, dex_file_location);
std::unique_ptr<const DexFile> dex_file = OpenOatDexFile(oat_file,
dex_file_location.c_str(),
error_msg);
@@ -2026,7 +2027,7 @@
// Image class loader [A][B][C][image dex files]
// Class loader = [???][dex_elements][image dex files]
// Need to ensure that [???][dex_elements] == [A][B][C].
- // For each class loader, PathClassLoader, the laoder checks the parent first. Also the logic
+ // For each class loader, PathClassLoader, the loader checks the parent first. Also the logic
// for PathClassLoader does this by looping through the array of dex files. To ensure they
// resolve the same way, simply flatten the hierarchy in the way the resolution order would be,
// and check that the dex file names are the same.
@@ -2662,7 +2663,7 @@
return true;
}
- if (IsPathOrDexClassLoader(soa, class_loader)) {
+ if (IsPathOrDexClassLoader(soa, class_loader) || IsInMemoryDexClassLoader(soa, class_loader)) {
// For regular path or dex class loader the search order is:
// - parent
// - shared libraries
@@ -2756,7 +2757,9 @@
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader) {
- DCHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader))
+ DCHECK(IsPathOrDexClassLoader(soa, class_loader) ||
+ IsInMemoryDexClassLoader(soa, class_loader) ||
+ IsDelegateLastClassLoader(soa, class_loader))
<< "Unexpected class loader for descriptor " << descriptor;
ObjPtr<mirror::Class> ret;
diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h
index 69476df..562dc47 100644
--- a/runtime/class_loader_utils.h
+++ b/runtime/class_loader_utils.h
@@ -29,7 +29,7 @@
namespace art {
// Returns true if the given class loader is either a PathClassLoader or a DexClassLoader.
-// (they both have the same behaviour with respect to class lockup order)
+// (they both have the same behaviour with respect to class lookup order)
inline bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -41,6 +41,15 @@
soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader));
}
+// Returns true if the given class loader is an InMemoryDexClassLoader.
+inline bool IsInMemoryDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+ Handle<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Class* class_loader_class = class_loader->GetClass();
+ return (class_loader_class ==
+ soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_InMemoryDexClassLoader));
+}
+
inline bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h
index 1e7d76c..df50682 100644
--- a/runtime/gc/accounting/card_table-inl.h
+++ b/runtime/gc/accounting/card_table-inl.h
@@ -127,14 +127,6 @@
return cards_scanned;
}
-/*
- * Visitor is expected to take in a card and return the new value. When a value is modified, the
- * modify visitor is called.
- * visitor: The visitor which modifies the cards. Returns the new value for a card given an old
- * value.
- * modified: Whenever the visitor modifies a card, this visitor is called on the card. Enables
- * us to know which cards got cleared.
- */
template <typename Visitor, typename ModifiedVisitor>
inline void CardTable::ModifyCardsAtomic(uint8_t* scan_begin,
uint8_t* scan_end,
@@ -144,6 +136,7 @@
uint8_t* card_end = CardFromAddr(AlignUp(scan_end, kCardSize));
CheckCardValid(card_cur);
CheckCardValid(card_end);
+ DCHECK(visitor(kCardClean) == kCardClean);
// Handle any unaligned cards at the start.
while (!IsAligned<sizeof(intptr_t)>(card_cur) && card_cur < card_end) {
@@ -188,7 +181,8 @@
while (word_cur < word_end) {
while (true) {
expected_word = *word_cur;
- if (LIKELY(expected_word == 0)) {
+ static_assert(kCardClean == 0);
+ if (LIKELY(expected_word == 0 /* All kCardClean */ )) {
break;
}
for (size_t i = 0; i < sizeof(uintptr_t); ++i) {
diff --git a/runtime/gc/accounting/card_table.h b/runtime/gc/accounting/card_table.h
index b84f22f..c99ed4b 100644
--- a/runtime/gc/accounting/card_table.h
+++ b/runtime/gc/accounting/card_table.h
@@ -95,12 +95,10 @@
}
/*
- * Visitor is expected to take in a card and return the new value. When a value is modified, the
- * modify visitor is called.
- * visitor: The visitor which modifies the cards. Returns the new value for a card given an old
- * value.
- * modified: Whenever the visitor modifies a card, this visitor is called on the card. Enables
- * us to know which cards got cleared.
+ * Modify cards in the range from scan_begin (inclusive) to scan_end (exclusive). Each card
+ * value v is replaced by visitor(v). Visitor() should not have side-effects.
+ * Whenever a card value is changed, modified(card_address, old_value, new_value) is invoked.
+ * For opportunistic performance reasons, this assumes that visitor(kCardClean) is kCardClean!
*/
template <typename Visitor, typename ModifiedVisitor>
void ModifyCardsAtomic(uint8_t* scan_begin,
diff --git a/runtime/gc/accounting/card_table_test.cc b/runtime/gc/accounting/card_table_test.cc
index 87965ed..12baaa4 100644
--- a/runtime/gc/accounting/card_table_test.cc
+++ b/runtime/gc/accounting/card_table_test.cc
@@ -60,7 +60,7 @@
uint8_t* HeapLimit() const {
return HeapBegin() + heap_size_;
}
- // Return a pseudo random card for an address.
+ // Return a non-zero pseudo random card for an address.
uint8_t PseudoRandomCard(const uint8_t* addr) const {
size_t offset = RoundDown(addr - heap_begin_, CardTable::kCardSize);
return 1 + offset % 254;
@@ -97,7 +97,8 @@
class UpdateVisitor {
public:
uint8_t operator()(uint8_t c) const {
- return c * 93 + 123;
+ // Must map zero to zero. Never applied to zero.
+ return c == 0 ? 0 : c * 93 + 123;
}
void operator()(uint8_t* /*card*/, uint8_t /*expected_value*/, uint8_t /*new_value*/) const {
}
diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc
index c29b79c..b0d09ba 100644
--- a/runtime/gc/collector/immune_spaces_test.cc
+++ b/runtime/gc/collector/immune_spaces_test.cc
@@ -112,6 +112,8 @@
// Create image header.
ImageSection sections[ImageHeader::kSectionCount];
new (image_map.Begin()) ImageHeader(
+ /*image_reservation_size=*/ image_size,
+ /*component_count=*/ 1u,
/*image_begin=*/ PointerToLowMemUInt32(image_map.Begin()),
/*image_size=*/ image_size,
sections,
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index e253dfb..9e1ba35 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -22,7 +22,6 @@
#include "allocation_listener.h"
#include "base/quasi_atomic.h"
#include "base/time_utils.h"
-#include "base/utils.h"
#include "gc/accounting/atomic_stack.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/allocation_record.h"
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 86135c1..bfb1019 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -36,6 +36,7 @@
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/time_utils.h"
+#include "base/utils.h"
#include "common_throws.h"
#include "debugger.h"
#include "dex/dex_file-inl.h"
@@ -173,6 +174,8 @@
double foreground_heap_growth_multiplier,
size_t capacity,
size_t non_moving_space_capacity,
+ const std::vector<std::string>& boot_class_path,
+ const std::vector<std::string>& boot_class_path_locations,
const std::string& image_file_name,
const InstructionSet image_instruction_set,
CollectorType foreground_collector_type,
@@ -212,7 +215,7 @@
long_gc_log_threshold_(long_gc_log_threshold),
process_cpu_start_time_ns_(ProcessCpuNanoTime()),
last_process_cpu_time_ns_(process_cpu_start_time_ns_),
- weighted_allocated_bytes_(0u),
+ weighted_allocated_bytes_(0.0),
ignore_max_footprint_(ignore_max_footprint),
zygote_creation_lock_("zygote creation lock", kZygoteCreationLock),
zygote_space_(nullptr),
@@ -350,7 +353,9 @@
// Load image space(s).
std::vector<std::unique_ptr<space::ImageSpace>> boot_image_spaces;
MemMap heap_reservation;
- if (space::ImageSpace::LoadBootImage(image_file_name,
+ if (space::ImageSpace::LoadBootImage(boot_class_path,
+ boot_class_path_locations,
+ image_file_name,
image_instruction_set,
heap_reservation_size,
&boot_image_spaces,
@@ -1068,7 +1073,7 @@
void Heap::CalculateWeightedAllocatedBytes() {
uint64_t current_process_cpu_time = ProcessCpuNanoTime();
uint64_t bytes_allocated = GetBytesAllocated();
- uint64_t weight = current_process_cpu_time - last_process_cpu_time_ns_;
+ double weight = current_process_cpu_time - last_process_cpu_time_ns_;
weighted_allocated_bytes_ += weight * bytes_allocated;
last_process_cpu_time_ns_ = current_process_cpu_time;
}
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 411a446..57c7376 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -174,7 +174,9 @@
double foreground_heap_growth_multiplier,
size_t capacity,
size_t non_moving_space_capacity,
- const std::string& original_image_file_name,
+ const std::vector<std::string>& boot_class_path,
+ const std::vector<std::string>& boot_class_path_locations,
+ const std::string& image_file_name,
InstructionSet image_instruction_set,
CollectorType foreground_collector_type,
CollectorType background_collector_type,
@@ -395,7 +397,7 @@
REQUIRES(!Locks::heap_bitmap_lock_)
REQUIRES(Locks::mutator_lock_);
- uint64_t GetWeightedAllocatedBytes() const {
+ double GetWeightedAllocatedBytes() const {
return weighted_allocated_bytes_;
}
@@ -1177,7 +1179,7 @@
uint64_t last_process_cpu_time_ns_;
// allocated_bytes * (current_process_cpu_time - last_process_cpu_time)
- uint64_t weighted_allocated_bytes_;
+ double weighted_allocated_bytes_;
// If we ignore the max footprint it lets the heap grow until it hits the heap capacity, this is
// useful for benchmarking since it reduces time spent in GC to a low %.
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index e494bd6..f45de27 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -21,6 +21,7 @@
#include <unistd.h>
#include <random>
+#include <thread>
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
@@ -103,9 +104,8 @@
static bool GenerateImage(const std::string& image_filename,
InstructionSet image_isa,
std::string* error_msg) {
- const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString());
- std::vector<std::string> boot_class_path;
- Split(boot_class_path_string, ':', &boot_class_path);
+ Runtime* runtime = Runtime::Current();
+ const std::vector<std::string>& boot_class_path = runtime->GetBootClassPath();
if (boot_class_path.empty()) {
*error_msg = "Failed to generate image because no boot class path specified";
return false;
@@ -125,8 +125,11 @@
image_option_string += image_filename;
arg_vector.push_back(image_option_string);
- for (size_t i = 0; i < boot_class_path.size(); i++) {
+ const std::vector<std::string>& boot_class_path_locations = runtime->GetBootClassPathLocations();
+ DCHECK_EQ(boot_class_path.size(), boot_class_path_locations.size());
+ for (size_t i = 0u; i < boot_class_path.size(); i++) {
arg_vector.push_back(std::string("--dex-file=") + boot_class_path[i]);
+ arg_vector.push_back(std::string("--dex-location=") + boot_class_path_locations[i]);
}
std::string oat_file_option_string("--oat-file=");
@@ -387,13 +390,40 @@
/*out*/std::string* error_msg)
REQUIRES_SHARED(Locks::mutator_lock_) {
TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
+
+ const bool create_thread_pool = true;
+ std::unique_ptr<ThreadPool> thread_pool;
+ if (create_thread_pool) {
+ TimingLogger::ScopedTiming timing("CreateThreadPool", &logger);
+ ScopedThreadStateChange stsc(Thread::Current(), kNative);
+ constexpr size_t kStackSize = 64 * KB;
+ constexpr size_t kMaxRuntimeWorkers = 4u;
+ const size_t num_workers =
+ std::min(static_cast<size_t>(std::thread::hardware_concurrency()), kMaxRuntimeWorkers);
+ thread_pool.reset(new ThreadPool("Runtime", num_workers, /*create_peers=*/false, kStackSize));
+ thread_pool->StartWorkers(Thread::Current());
+ }
+
std::unique_ptr<ImageSpace> space = Init(image_filename,
image_location,
oat_file,
&logger,
+ thread_pool.get(),
image_reservation,
error_msg);
+ if (thread_pool != nullptr) {
+ TimingLogger::ScopedTiming timing("CreateThreadPool", &logger);
+ ScopedThreadStateChange stsc(Thread::Current(), kNative);
+ thread_pool.reset();
+ }
if (space != nullptr) {
+ uint32_t expected_reservation_size =
+ RoundUp(space->GetImageHeader().GetImageSize(), kPageSize);
+ if (!CheckImageReservationSize(*space, expected_reservation_size, error_msg) ||
+ !CheckImageComponentCount(*space, /*expected_component_count=*/ 1u, error_msg)) {
+ return nullptr;
+ }
+
TimingLogger::ScopedTiming timing("RelocateImage", &logger);
ImageHeader* image_header = reinterpret_cast<ImageHeader*>(space->GetMemMap()->Begin());
if (!RelocateInPlace(*image_header,
@@ -435,6 +465,7 @@
const char* image_location,
const OatFile* oat_file,
TimingLogger* logger,
+ ThreadPool* thread_pool,
/*inout*/MemMap* image_reservation,
/*out*/std::string* error_msg)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -523,6 +554,7 @@
*image_header,
file->Fd(),
logger,
+ thread_pool,
image_reservation,
error_msg);
if (!map.IsValid()) {
@@ -579,12 +611,41 @@
return space;
}
+ static bool CheckImageComponentCount(const ImageSpace& space,
+ uint32_t expected_component_count,
+ /*out*/std::string* error_msg) {
+ const ImageHeader& header = space.GetImageHeader();
+ if (header.GetComponentCount() != expected_component_count) {
+ *error_msg = StringPrintf("Unexpected component count in %s, received %u, expected %u",
+ space.GetImageFilename().c_str(),
+ header.GetComponentCount(),
+ expected_component_count);
+ return false;
+ }
+ return true;
+ }
+
+ static bool CheckImageReservationSize(const ImageSpace& space,
+ uint32_t expected_reservation_size,
+ /*out*/std::string* error_msg) {
+ const ImageHeader& header = space.GetImageHeader();
+ if (header.GetImageReservationSize() != expected_reservation_size) {
+ *error_msg = StringPrintf("Unexpected reservation size in %s, received %u, expected %u",
+ space.GetImageFilename().c_str(),
+ header.GetImageReservationSize(),
+ expected_reservation_size);
+ return false;
+ }
+ return true;
+ }
+
private:
static MemMap LoadImageFile(const char* image_filename,
const char* image_location,
const ImageHeader& image_header,
int fd,
TimingLogger* logger,
+ ThreadPool* pool,
/*inout*/MemMap* image_reservation,
/*out*/std::string* error_msg) {
TimingLogger::ScopedTiming timing("MapImageFile", logger);
@@ -629,10 +690,9 @@
memcpy(map.Begin(), &image_header, sizeof(ImageHeader));
const uint64_t start = NanoTime();
- ThreadPool* pool = Runtime::Current()->GetThreadPool();
Thread* const self = Thread::Current();
const size_t kMinBlocks = 2;
- const bool use_parallel = pool != nullptr &&image_header.GetBlockCount() >= kMinBlocks;
+ const bool use_parallel = pool != nullptr && image_header.GetBlockCount() >= kMinBlocks;
for (const ImageHeader::Block& block : image_header.GetBlocks(temp_map.Begin())) {
auto function = [&](Thread*) {
const uint64_t start2 = NanoTime();
@@ -1205,8 +1265,13 @@
class ImageSpace::BootImageLoader {
public:
- BootImageLoader(const std::string& image_location, InstructionSet image_isa)
- : image_location_(image_location),
+ BootImageLoader(const std::vector<std::string>& boot_class_path,
+ const std::vector<std::string>& boot_class_path_locations,
+ const std::string& image_location,
+ InstructionSet image_isa)
+ : boot_class_path_(boot_class_path),
+ boot_class_path_locations_(boot_class_path_locations),
+ image_location_(image_location),
image_isa_(image_isa),
is_zygote_(Runtime::Current()->IsZygote()),
has_system_(false),
@@ -1254,57 +1319,17 @@
/*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
std::string filename = GetSystemImageFilename(image_location_.c_str(), image_isa_);
- std::vector<std::string> locations;
- if (!GetBootClassPathImageLocations(image_location_, filename, &locations, error_msg)) {
- return false;
- }
- uint32_t image_start;
- uint32_t image_end;
- if (!GetBootImageAddressRange(filename, &image_start, &image_end, error_msg)) {
- return false;
- }
- if (locations.size() > 1u) {
- std::string last_filename = GetSystemImageFilename(locations.back().c_str(), image_isa_);
- uint32_t dummy;
- if (!GetBootImageAddressRange(last_filename, &dummy, &image_end, error_msg)) {
- return false;
- }
- }
- MemMap image_reservation;
- MemMap local_extra_reservation;
- if (!ReserveBootImageMemory(/*reservation_size=*/ image_end - image_start,
- image_start,
- extra_reservation_size,
- &image_reservation,
- &local_extra_reservation,
- error_msg)) {
- return false;
- }
- std::vector<std::unique_ptr<ImageSpace>> spaces;
- spaces.reserve(locations.size());
- for (const std::string& location : locations) {
- filename = GetSystemImageFilename(location.c_str(), image_isa_);
- spaces.push_back(Load(location, filename, &logger, &image_reservation, error_msg));
- if (spaces.back() == nullptr) {
- return false;
- }
- }
- for (std::unique_ptr<ImageSpace>& space : spaces) {
- static constexpr bool kValidateOatFile = false;
- if (!OpenOatFile(space.get(), kValidateOatFile, &logger, &image_reservation, error_msg)) {
- return false;
- }
- }
- if (!CheckReservationExhausted(image_reservation, error_msg)) {
+ if (!LoadFromFile(filename,
+ /*validate_oat_file=*/ false,
+ extra_reservation_size,
+ &logger,
+ boot_image_spaces,
+ extra_reservation,
+ error_msg)) {
return false;
}
- MaybeRelocateSpaces(spaces, &logger);
- InitRuntimeMethods(spaces);
- boot_image_spaces->swap(spaces);
- *extra_reservation = std::move(local_extra_reservation);
-
if (VLOG_IS_ON(image)) {
LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromSystem exiting "
<< boot_image_spaces->front();
@@ -1321,65 +1346,17 @@
/*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
DCHECK(DalvikCacheExists());
- std::vector<std::string> locations;
- if (!GetBootClassPathImageLocations(image_location_, cache_filename_, &locations, error_msg)) {
- return false;
- }
- uint32_t image_start;
- uint32_t image_end;
- if (!GetBootImageAddressRange(cache_filename_, &image_start, &image_end, error_msg)) {
- return false;
- }
- if (locations.size() > 1u) {
- std::string last_filename;
- if (!GetDalvikCacheFilename(locations.back().c_str(),
- dalvik_cache_.c_str(),
- &last_filename,
- error_msg)) {
- return false;
- }
- uint32_t dummy;
- if (!GetBootImageAddressRange(last_filename, &dummy, &image_end, error_msg)) {
- return false;
- }
- }
- MemMap image_reservation;
- MemMap local_extra_reservation;
- if (!ReserveBootImageMemory(/*reservation_size=*/ image_end - image_start,
- image_start,
- extra_reservation_size,
- &image_reservation,
- &local_extra_reservation,
- error_msg)) {
- return false;
- }
- std::vector<std::unique_ptr<ImageSpace>> spaces;
- spaces.reserve(locations.size());
- for (const std::string& location : locations) {
- std::string filename;
- if (!GetDalvikCacheFilename(location.c_str(), dalvik_cache_.c_str(), &filename, error_msg)) {
- return false;
- }
- spaces.push_back(Load(location, filename, &logger, &image_reservation, error_msg));
- if (spaces.back() == nullptr) {
- return false;
- }
- }
- for (std::unique_ptr<ImageSpace>& space : spaces) {
- if (!OpenOatFile(space.get(), validate_oat_file, &logger, &image_reservation, error_msg)) {
- return false;
- }
- }
- if (!CheckReservationExhausted(image_reservation, error_msg)) {
+ if (!LoadFromFile(cache_filename_,
+ validate_oat_file,
+ extra_reservation_size,
+ &logger,
+ boot_image_spaces,
+ extra_reservation,
+ error_msg)) {
return false;
}
- MaybeRelocateSpaces(spaces, &logger);
- InitRuntimeMethods(spaces);
- boot_image_spaces->swap(spaces);
- *extra_reservation = std::move(local_extra_reservation);
-
if (VLOG_IS_ON(image)) {
LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromDalvikCache exiting "
<< boot_image_spaces->front();
@@ -1389,6 +1366,81 @@
}
private:
+ bool LoadFromFile(
+ const std::string& filename,
+ bool validate_oat_file,
+ size_t extra_reservation_size,
+ TimingLogger* logger,
+ /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/MemMap* extra_reservation,
+ /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ImageHeader system_hdr;
+ if (!ReadSpecificImageHeader(filename.c_str(), &system_hdr)) {
+ *error_msg = StringPrintf("Cannot read header of %s", filename.c_str());
+ return false;
+ }
+ if (system_hdr.GetComponentCount() != boot_class_path_.size()) {
+ *error_msg = StringPrintf("Unexpected component count in %s, received %u, expected %zu",
+ filename.c_str(),
+ system_hdr.GetComponentCount(),
+ boot_class_path_.size());
+ return false;
+ }
+ MemMap image_reservation;
+ MemMap local_extra_reservation;
+ if (!ReserveBootImageMemory(system_hdr.GetImageReservationSize(),
+ reinterpret_cast32<uint32_t>(system_hdr.GetImageBegin()),
+ extra_reservation_size,
+ &image_reservation,
+ &local_extra_reservation,
+ error_msg)) {
+ return false;
+ }
+
+ std::vector<std::string> locations =
+ ExpandMultiImageLocations(boot_class_path_locations_, image_location_);
+ std::vector<std::string> filenames =
+ ExpandMultiImageLocations(boot_class_path_locations_, filename);
+ DCHECK_EQ(locations.size(), filenames.size());
+ std::vector<std::unique_ptr<ImageSpace>> spaces;
+ spaces.reserve(locations.size());
+ for (std::size_t i = 0u, size = locations.size(); i != size; ++i) {
+ spaces.push_back(Load(locations[i], filenames[i], logger, &image_reservation, error_msg));
+ const ImageSpace* space = spaces.back().get();
+ if (space == nullptr) {
+ return false;
+ }
+ uint32_t expected_component_count = (i == 0u) ? system_hdr.GetComponentCount() : 0u;
+ uint32_t expected_reservation_size = (i == 0u) ? system_hdr.GetImageReservationSize() : 0u;
+ if (!Loader::CheckImageReservationSize(*space, expected_reservation_size, error_msg) ||
+ !Loader::CheckImageComponentCount(*space, expected_component_count, error_msg)) {
+ return false;
+ }
+ }
+ for (size_t i = 0u, size = spaces.size(); i != size; ++i) {
+ std::string expected_boot_class_path =
+ (i == 0u) ? android::base::Join(boot_class_path_locations_, ':') : std::string();
+ if (!OpenOatFile(spaces[i].get(),
+ boot_class_path_[i],
+ expected_boot_class_path,
+ validate_oat_file,
+ logger,
+ &image_reservation,
+ error_msg)) {
+ return false;
+ }
+ }
+ if (!CheckReservationExhausted(image_reservation, error_msg)) {
+ return false;
+ }
+
+ MaybeRelocateSpaces(spaces, logger);
+ InitRuntimeMethods(spaces);
+ boot_image_spaces->swap(spaces);
+ *extra_reservation = std::move(local_extra_reservation);
+ return true;
+ }
+
template <typename T>
ALWAYS_INLINE static T* RelocatedAddress(T* src, uint32_t diff) {
DCHECK(src != nullptr);
@@ -1887,8 +1939,6 @@
DCHECK(!spaces.empty());
ImageSpace* space = spaces[0].get();
const ImageHeader& image_header = space->GetImageHeader();
- // Use oat_file_non_owned_ from the `space` to set the runtime methods.
- runtime->SetInstructionSet(space->oat_file_non_owned_->GetOatHeader().GetInstructionSet());
runtime->SetResolutionMethod(image_header.GetImageMethod(ImageHeader::kResolutionMethod));
runtime->SetImtConflictMethod(image_header.GetImageMethod(ImageHeader::kImtConflictMethod));
runtime->SetImtUnimplementedMethod(
@@ -1947,11 +1997,14 @@
image_location.c_str(),
/*oat_file=*/ nullptr,
logger,
+ /*thread_pool=*/ nullptr,
image_reservation,
error_msg);
}
bool OpenOatFile(ImageSpace* space,
+ const std::string& dex_filename,
+ const std::string& expected_boot_class_path,
bool validate_oat_file,
TimingLogger* logger,
/*inout*/MemMap* image_reservation,
@@ -1967,13 +2020,15 @@
TimingLogger::ScopedTiming timing("OpenOatFile", logger);
std::string oat_filename =
ImageHeader::GetOatLocationFromImageLocation(space->GetImageFilename());
+ std::string oat_location =
+ ImageHeader::GetOatLocationFromImageLocation(space->GetImageLocation());
oat_file.reset(OatFile::Open(/*zip_fd=*/ -1,
oat_filename,
- oat_filename,
+ oat_location,
!Runtime::Current()->IsAotCompiler(),
/*low_4gb=*/ false,
- /*abs_dex_location=*/ nullptr,
+ /*abs_dex_location=*/ dex_filename.c_str(),
image_reservation,
error_msg));
if (oat_file == nullptr) {
@@ -1994,6 +2049,17 @@
space->GetName());
return false;
}
+ const char* oat_boot_class_path =
+ oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathKey);
+ oat_boot_class_path = (oat_boot_class_path != nullptr) ? oat_boot_class_path : "";
+ if (expected_boot_class_path != oat_boot_class_path) {
+ *error_msg = StringPrintf("Failed to match oat boot class path %s to expected "
+ "boot class path %s in image %s",
+ oat_boot_class_path,
+ expected_boot_class_path.c_str(),
+ space->GetName());
+ return false;
+ }
ptrdiff_t relocation_diff = space->Begin() - image_header.GetImageBegin();
CHECK(image_header.GetOatDataBegin() != nullptr);
uint8_t* oat_data_begin = image_header.GetOatDataBegin() + relocation_diff;
@@ -2019,58 +2085,14 @@
return true;
}
- // Extract boot class path from oat file associated with `image_filename`
- // and list all associated image locations.
- static bool GetBootClassPathImageLocations(const std::string& image_location,
- const std::string& image_filename,
- /*out*/ std::vector<std::string>* all_locations,
- /*out*/ std::string* error_msg) {
- std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_filename);
- std::unique_ptr<OatFile> oat_file(OatFile::Open(/*zip_fd=*/ -1,
- oat_filename,
- oat_filename,
- /*executable=*/ false,
- /*low_4gb=*/ false,
- /*abs_dex_location=*/ nullptr,
- /*reservation=*/ nullptr,
- error_msg));
- if (oat_file == nullptr) {
- *error_msg = StringPrintf("Failed to open oat file '%s' for image file %s: %s",
- oat_filename.c_str(),
- image_filename.c_str(),
- error_msg->c_str());
- return false;
- }
- const OatHeader& oat_header = oat_file->GetOatHeader();
- const char* boot_classpath = oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
- all_locations->push_back(image_location);
- if (boot_classpath != nullptr && boot_classpath[0] != 0) {
- ExtractMultiImageLocations(image_location, boot_classpath, all_locations);
- }
- return true;
- }
-
- bool GetBootImageAddressRange(const std::string& filename,
- /*out*/uint32_t* start,
- /*out*/uint32_t* end,
- /*out*/std::string* error_msg) {
- ImageHeader system_hdr;
- if (!ReadSpecificImageHeader(filename.c_str(), &system_hdr)) {
- *error_msg = StringPrintf("Cannot read header of %s", filename.c_str());
- return false;
- }
- *start = reinterpret_cast32<uint32_t>(system_hdr.GetImageBegin());
- CHECK_ALIGNED(*start, kPageSize);
- *end = RoundUp(reinterpret_cast32<uint32_t>(system_hdr.GetOatFileEnd()), kPageSize);
- return true;
- }
-
bool ReserveBootImageMemory(uint32_t reservation_size,
uint32_t image_start,
size_t extra_reservation_size,
/*out*/MemMap* image_reservation,
/*out*/MemMap* extra_reservation,
/*out*/std::string* error_msg) {
+ DCHECK_ALIGNED(reservation_size, kPageSize);
+ DCHECK_ALIGNED(image_start, kPageSize);
DCHECK(!image_reservation->IsValid());
DCHECK_LT(extra_reservation_size, std::numeric_limits<uint32_t>::max() - reservation_size);
size_t total_size = reservation_size + extra_reservation_size;
@@ -2116,6 +2138,8 @@
return true;
}
+ const std::vector<std::string>& boot_class_path_;
+ const std::vector<std::string>& boot_class_path_locations_;
const std::string& image_location_;
InstructionSet image_isa_;
bool is_zygote_;
@@ -2163,6 +2187,8 @@
}
bool ImageSpace::LoadBootImage(
+ const std::vector<std::string>& boot_class_path,
+ const std::vector<std::string>& boot_class_path_locations,
const std::string& image_location,
const InstructionSet image_isa,
size_t extra_reservation_size,
@@ -2180,7 +2206,7 @@
return false;
}
- BootImageLoader loader(image_location, image_isa);
+ BootImageLoader loader(boot_class_path, boot_class_path_locations, image_location, image_isa);
// Step 0: Extra zygote work.
@@ -2341,57 +2367,6 @@
<< ",name=\"" << GetName() << "\"]";
}
-std::string ImageSpace::GetMultiImageBootClassPath(
- const std::vector<std::string>& dex_locations,
- const std::vector<std::string>& oat_filenames,
- const std::vector<std::string>& image_filenames) {
- DCHECK_GT(oat_filenames.size(), 1u);
- // If the image filename was adapted (e.g., for our tests), we need to change this here,
- // too, but need to strip all path components (they will be re-established when loading).
- // For example, dex location
- // /system/framework/core-libart.art
- // with image name
- // out/target/product/taimen/dex_bootjars/system/framework/arm64/boot-core-libart.art
- // yields boot class path component
- // /system/framework/boot-core-libart.art .
- std::ostringstream bootcp_oss;
- bool first_bootcp = true;
- for (size_t i = 0; i < dex_locations.size(); ++i) {
- if (!first_bootcp) {
- bootcp_oss << ":";
- }
-
- std::string dex_loc = dex_locations[i];
- std::string image_filename = image_filenames[i];
-
- // Use the dex_loc path, but the image_filename name (without path elements).
- size_t dex_last_slash = dex_loc.rfind('/');
-
- // npos is max(size_t). That makes this a bit ugly.
- size_t image_last_slash = image_filename.rfind('/');
- size_t image_last_at = image_filename.rfind('@');
- size_t image_last_sep = (image_last_slash == std::string::npos)
- ? image_last_at
- : (image_last_at == std::string::npos)
- ? image_last_slash
- : std::max(image_last_slash, image_last_at);
- // Note: whenever image_last_sep == npos, +1 overflow means using the full string.
-
- if (dex_last_slash == std::string::npos) {
- dex_loc = image_filename.substr(image_last_sep + 1);
- } else {
- dex_loc = dex_loc.substr(0, dex_last_slash + 1) +
- image_filename.substr(image_last_sep + 1);
- }
-
- // Image filenames already end with .art, no need to replace.
-
- bootcp_oss << dex_loc;
- first_bootcp = false;
- }
- return bootcp_oss.str();
-}
-
bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg) {
const ArtDexFileLoader dex_file_loader;
for (const OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) {
@@ -2452,46 +2427,55 @@
return true;
}
-void ImageSpace::ExtractMultiImageLocations(const std::string& input_image_file_name,
- const std::string& boot_classpath,
- std::vector<std::string>* image_file_names) {
- DCHECK(image_file_names != nullptr);
+std::vector<std::string> ImageSpace::ExpandMultiImageLocations(
+ const std::vector<std::string>& dex_locations,
+ const std::string& image_location) {
+ DCHECK(!dex_locations.empty());
- std::vector<std::string> images;
- Split(boot_classpath, ':', &images);
+ // Find the path.
+ size_t last_slash = image_location.rfind('/');
+ CHECK_NE(last_slash, std::string::npos);
- // Add the rest into the list. We have to adjust locations, possibly:
- //
- // For example, image_file_name is /a/b/c/d/e.art
- // images[0] is f/c/d/e.art
- // ----------------------------------------------
- // images[1] is g/h/i/j.art -> /a/b/h/i/j.art
- const std::string& first_image = images[0];
- // Length of common suffix.
- size_t common = 0;
- while (common < input_image_file_name.size() &&
- common < first_image.size() &&
- *(input_image_file_name.end() - common - 1) == *(first_image.end() - common - 1)) {
- ++common;
+ // We also need to honor path components that were encoded through '@'. Otherwise the loading
+ // code won't be able to find the images.
+ if (image_location.find('@', last_slash) != std::string::npos) {
+ last_slash = image_location.rfind('@');
}
- // We want to replace the prefix of the input image with the prefix of the boot class path.
- // This handles the case where the image file contains @ separators.
- // Example image_file_name is oats/system@framework@boot.art
- // images[0] is .../arm/boot.art
- // means that the image name prefix will be oats/system@framework@
- // so that the other images are openable.
- const size_t old_prefix_length = first_image.size() - common;
- const std::string new_prefix = input_image_file_name.substr(
- 0,
- input_image_file_name.size() - common);
- // Apply pattern to images[1] .. images[n].
- for (size_t i = 1; i < images.size(); ++i) {
- const std::string& image = images[i];
- CHECK_GT(image.length(), old_prefix_length);
- std::string suffix = image.substr(old_prefix_length);
- image_file_names->push_back(new_prefix + suffix);
+ // Find the dot separating the primary image name from the extension.
+ size_t last_dot = image_location.rfind('.');
+ // Extract the extension and base (the path and primary image name).
+ std::string extension;
+ std::string base = image_location;
+ if (last_dot != std::string::npos && last_dot > last_slash) {
+ extension = image_location.substr(last_dot); // Including the dot.
+ base.resize(last_dot);
}
+ // For non-empty primary image name, add '-' to the `base`.
+ if (last_slash + 1u != base.size()) {
+ base += '-';
+ }
+
+ std::vector<std::string> locations;
+ locations.reserve(dex_locations.size());
+ locations.push_back(image_location);
+
+ // Now create the other names. Use a counted loop to skip the first one.
+ for (size_t i = 1u; i < dex_locations.size(); ++i) {
+ // Replace path with `base` (i.e. image path and prefix) and replace the original
+ // extension (if any) with `extension`.
+ std::string name = dex_locations[i];
+ size_t last_dex_slash = name.rfind('/');
+ if (last_dex_slash != std::string::npos) {
+ name = name.substr(last_dex_slash + 1);
+ }
+ size_t last_dex_dot = name.rfind('.');
+ if (last_dex_dot != std::string::npos) {
+ name.resize(last_dex_dot);
+ }
+ locations.push_back(base + name + extension);
+ }
+ return locations;
}
void ImageSpace::DumpSections(std::ostream& os) const {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index aa45ed3..05e7fa5 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -39,9 +39,11 @@
// Load boot image spaces from a primary image file for a specified instruction set.
//
// On successful return, the loaded spaces are added to boot_image_spaces (which must be
- // empty on entry) and oat_file_end is updated with the (page-aligned) end of the last
- // oat file.
+ // empty on entry) and `extra_reservation` is set to the requested reservation located
+ // after the end of the last loaded oat file.
static bool LoadBootImage(
+ const std::vector<std::string>& boot_class_path,
+ const std::vector<std::string>& boot_class_path_locations,
const std::string& image_location,
const InstructionSet image_isa,
size_t extra_reservation_size,
@@ -122,15 +124,10 @@
bool* has_data,
bool *is_global_cache);
- // Use the input image filename to adapt the names in the given boot classpath to establish
- // complete locations for secondary images.
- static void ExtractMultiImageLocations(const std::string& input_image_file_name,
- const std::string& boot_classpath,
- std::vector<std::string>* image_filenames);
-
- static std::string GetMultiImageBootClassPath(const std::vector<std::string>& dex_locations,
- const std::vector<std::string>& oat_filenames,
- const std::vector<std::string>& image_filenames);
+ // Expand a single image location to multi-image locations based on the dex locations.
+ static std::vector<std::string> ExpandMultiImageLocations(
+ const std::vector<std::string>& dex_locations,
+ const std::string& image_location);
// Returns true if the dex checksums in the given oat file match the
// checksums of the original dex files on disk. This is intended to be used
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index d9d81f0..13bead2 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -235,6 +235,7 @@
case Intrinsics::kUnsafeStoreFence:
case Intrinsics::kUnsafeFullFence:
case Intrinsics::kCRC32Update:
+ case Intrinsics::kCRC32UpdateBytes:
case Intrinsics::kStringNewStringFromBytes:
case Intrinsics::kStringNewStringFromChars:
case Intrinsics::kStringNewStringFromString:
diff --git a/runtime/image.cc b/runtime/image.cc
index ae3d8e3..fe1d88f 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -29,9 +29,11 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '7', '1', '\0' }; // Add image blocks.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '7', '3', '\0' }; // Image reservation.
-ImageHeader::ImageHeader(uint32_t image_begin,
+ImageHeader::ImageHeader(uint32_t image_reservation_size,
+ uint32_t component_count,
+ uint32_t image_begin,
uint32_t image_size,
ImageSection* sections,
uint32_t image_roots,
@@ -43,7 +45,9 @@
uint32_t boot_image_begin,
uint32_t boot_image_size,
uint32_t pointer_size)
- : image_begin_(image_begin),
+ : image_reservation_size_(image_reservation_size),
+ component_count_(component_count),
+ image_begin_(image_begin),
image_size_(image_size),
image_checksum_(0u),
oat_checksum_(oat_checksum),
@@ -96,6 +100,9 @@
if (memcmp(version_, kImageVersion, sizeof(kImageVersion)) != 0) {
return false;
}
+ if (!IsAligned<kPageSize>(image_reservation_size_)) {
+ return false;
+ }
// Unsigned so wraparound is well defined.
if (image_begin_ >= image_begin_ + image_size_) {
return false;
diff --git a/runtime/image.h b/runtime/image.h
index 9d98431..140f504 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -138,7 +138,9 @@
};
ImageHeader() {}
- ImageHeader(uint32_t image_begin,
+ ImageHeader(uint32_t image_reservation_size,
+ uint32_t component_count,
+ uint32_t image_begin,
uint32_t image_size,
ImageSection* sections,
uint32_t image_roots,
@@ -154,6 +156,14 @@
bool IsValid() const;
const char* GetMagic() const;
+ uint32_t GetImageReservationSize() const {
+ return image_reservation_size_;
+ }
+
+ uint32_t GetComponentCount() const {
+ return component_count_;
+ }
+
uint8_t* GetImageBegin() const {
return reinterpret_cast<uint8_t*>(image_begin_);
}
@@ -423,6 +433,19 @@
uint8_t magic_[4];
uint8_t version_[4];
+ // The total memory reservation size for the image.
+ // For boot image or boot image extension, the primary image includes the reservation
+ // for all image files and oat files, secondary images have the reservation set to 0.
+ // App images have reservation equal to `image_size_` rounded up to page size because
+ // their oat files are mmapped independently.
+ uint32_t image_reservation_size_ = 0u;
+
+ // The number of components.
+ // For boot image or boot image extension, the primary image stores the total number
+ // of images, secondary images have this set to 0.
+ // App images have 1 component.
+ uint32_t component_count_ = 0u;
+
// Required base address for mapping the image.
uint32_t image_begin_ = 0u;
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 12f1522..27c47bc 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -164,7 +164,8 @@
have_watched_frame_pop_listeners_(false),
have_branch_listeners_(false),
have_exception_handled_listeners_(false),
- deoptimized_methods_lock_("deoptimized methods lock", kGenericBottomLock),
+ deoptimized_methods_lock_(new ReaderWriterMutex("deoptimized methods lock",
+ kGenericBottomLock)),
deoptimization_enabled_(false),
interpreter_handler_table_(kMainHandlerTable),
quick_alloc_entry_points_instrumentation_counter_(0),
@@ -323,8 +324,9 @@
const InstrumentationStackFrame& frame =
(*instrumentation_stack_)[instrumentation_stack_depth_];
- CHECK_EQ(m, frame.method_) << "Expected " << ArtMethod::PrettyMethod(m)
- << ", Found " << ArtMethod::PrettyMethod(frame.method_);
+ CHECK_EQ(m->GetNonObsoleteMethod(), frame.method_->GetNonObsoleteMethod())
+ << "Expected " << ArtMethod::PrettyMethod(m)
+ << ", Found " << ArtMethod::PrettyMethod(frame.method_);
return_pc = frame.return_pc_;
if (kVerboseInstrumentation) {
LOG(INFO) << "Ignoring already instrumented " << frame.Dump();
@@ -470,7 +472,9 @@
if (instrumentation_frame.interpreter_entry_) {
CHECK(m == Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
} else {
- CHECK(m == instrumentation_frame.method_) << ArtMethod::PrettyMethod(m);
+ CHECK_EQ(m->GetNonObsoleteMethod(),
+ instrumentation_frame.method_->GetNonObsoleteMethod())
+ << ArtMethod::PrettyMethod(m);
}
SetReturnPc(instrumentation_frame.return_pc_);
if (instrumentation_->ShouldNotifyMethodEnterExitEvents() &&
@@ -743,7 +747,7 @@
// Restore stack only if there is no method currently deoptimized.
bool empty;
{
- ReaderMutexLock mu(self, deoptimized_methods_lock_);
+ ReaderMutexLock mu(self, *GetDeoptimizedMethodsLock());
empty = IsDeoptimizedMethodsEmpty(); // Avoid lock violation.
}
if (empty) {
@@ -931,7 +935,7 @@
Thread* self = Thread::Current();
{
- WriterMutexLock mu(self, deoptimized_methods_lock_);
+ WriterMutexLock mu(self, *GetDeoptimizedMethodsLock());
bool has_not_been_deoptimized = AddDeoptimizedMethod(method);
CHECK(has_not_been_deoptimized) << "Method " << ArtMethod::PrettyMethod(method)
<< " is already deoptimized";
@@ -955,7 +959,7 @@
Thread* self = Thread::Current();
bool empty;
{
- WriterMutexLock mu(self, deoptimized_methods_lock_);
+ WriterMutexLock mu(self, *GetDeoptimizedMethodsLock());
bool found_and_erased = RemoveDeoptimizedMethod(method);
CHECK(found_and_erased) << "Method " << ArtMethod::PrettyMethod(method)
<< " is not deoptimized";
@@ -987,12 +991,12 @@
bool Instrumentation::IsDeoptimized(ArtMethod* method) {
DCHECK(method != nullptr);
- ReaderMutexLock mu(Thread::Current(), deoptimized_methods_lock_);
+ ReaderMutexLock mu(Thread::Current(), *GetDeoptimizedMethodsLock());
return IsDeoptimizedMethod(method);
}
void Instrumentation::EnableDeoptimization() {
- ReaderMutexLock mu(Thread::Current(), deoptimized_methods_lock_);
+ ReaderMutexLock mu(Thread::Current(), *GetDeoptimizedMethodsLock());
CHECK(IsDeoptimizedMethodsEmpty());
CHECK_EQ(deoptimization_enabled_, false);
deoptimization_enabled_ = true;
@@ -1009,7 +1013,7 @@
while (true) {
ArtMethod* method;
{
- ReaderMutexLock mu(Thread::Current(), deoptimized_methods_lock_);
+ ReaderMutexLock mu(Thread::Current(), *GetDeoptimizedMethodsLock());
if (IsDeoptimizedMethodsEmpty()) {
break;
}
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 3bd4fb5..d4c3c29 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -19,12 +19,13 @@
#include <stdint.h>
#include <list>
+#include <memory>
#include <unordered_set>
#include "arch/instruction_set.h"
#include "base/enums.h"
+#include "base/locks.h"
#include "base/macros.h"
-#include "base/mutex.h"
#include "base/safe_map.h"
#include "gc_root.h"
@@ -39,6 +40,7 @@
template <typename T> class Handle;
template <typename T> class MutableHandle;
union JValue;
+class SHARED_LOCKABLE ReaderWriterMutex;
class ShadowFrame;
class Thread;
enum class DeoptimizationMethodType;
@@ -211,11 +213,11 @@
// Deoptimization.
void EnableDeoptimization()
REQUIRES(Locks::mutator_lock_)
- REQUIRES(!deoptimized_methods_lock_);
+ REQUIRES(!GetDeoptimizedMethodsLock());
// Calls UndeoptimizeEverything which may visit class linker classes through ConfigureStubs.
void DisableDeoptimization(const char* key)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
- REQUIRES(!deoptimized_methods_lock_);
+ REQUIRES(!GetDeoptimizedMethodsLock());
bool AreAllMethodsDeoptimized() const {
return interpreter_stubs_installed_;
@@ -231,7 +233,7 @@
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_,
- !deoptimized_methods_lock_);
+ !GetDeoptimizedMethodsLock());
// Executes everything with compiled code (or interpreter if there is no code). May visit class
// linker classes through ConfigureStubs.
@@ -239,23 +241,23 @@
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_,
- !deoptimized_methods_lock_);
+ !GetDeoptimizedMethodsLock());
// Deoptimize a method by forcing its execution with the interpreter. Nevertheless, a static
// method (except a class initializer) set to the resolution trampoline will be deoptimized only
// once its declaring class is initialized.
void Deoptimize(ArtMethod* method)
- REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !deoptimized_methods_lock_);
+ REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !GetDeoptimizedMethodsLock());
// Undeoptimze the method by restoring its entrypoints. Nevertheless, a static method
// (except a class initializer) set to the resolution trampoline will be updated only once its
// declaring class is initialized.
void Undeoptimize(ArtMethod* method)
- REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !deoptimized_methods_lock_);
+ REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !GetDeoptimizedMethodsLock());
// Indicates whether the method has been deoptimized so it is executed with the interpreter.
bool IsDeoptimized(ArtMethod* method)
- REQUIRES(!deoptimized_methods_lock_) REQUIRES_SHARED(Locks::mutator_lock_);
+ REQUIRES(!GetDeoptimizedMethodsLock()) REQUIRES_SHARED(Locks::mutator_lock_);
// Enable method tracing by installing instrumentation entry/exit stubs or interpreter.
void EnableMethodTracing(const char* key,
@@ -263,14 +265,14 @@
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_,
- !deoptimized_methods_lock_);
+ !GetDeoptimizedMethodsLock());
// Disable method tracing by uninstalling instrumentation entry/exit stubs or interpreter.
void DisableMethodTracing(const char* key)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_,
- !deoptimized_methods_lock_);
+ !GetDeoptimizedMethodsLock());
InterpreterHandlerTable GetInterpreterHandlerTable() const
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -289,19 +291,19 @@
// Update the code of a method respecting any installed stubs.
void UpdateMethodsCode(ArtMethod* method, const void* quick_code)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
// Update the code of a native method to a JITed stub.
void UpdateNativeMethodsCodeToJitCode(ArtMethod* method, const void* quick_code)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
// Update the code of a method to the interpreter respecting any installed stubs from debugger.
void UpdateMethodsCodeToInterpreterEntryPoint(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
// Update the code of a method respecting any installed stubs from debugger.
void UpdateMethodsCodeForJavaDebuggable(ArtMethod* method, const void* quick_code)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
// Return the code that we can execute for an invoke including from the JIT.
const void* GetCodeForInvoke(ArtMethod* method) const
@@ -483,7 +485,7 @@
// being returned from.
TwoWordReturn PopInstrumentationStackFrame(Thread* self, uintptr_t* return_pc,
uint64_t* gpr_result, uint64_t* fpr_result)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
// Pops nframes instrumentation frames from the current thread. Returns the return pc for the last
// instrumentation frame that's popped.
@@ -492,10 +494,10 @@
// Call back for configure stubs.
void InstallStubsForClass(mirror::Class* klass) REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!deoptimized_methods_lock_);
+ REQUIRES(!GetDeoptimizedMethodsLock());
void InstallStubsForMethod(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
// Install instrumentation exit stub on every method of the stack of the given thread.
// This is used by the debugger to cause a deoptimization of the thread's stack after updating
@@ -528,7 +530,7 @@
// becomes the highest instrumentation level required by a client.
void ConfigureStubs(const char* key, InstrumentationLevel desired_instrumentation_level)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
- REQUIRES(!deoptimized_methods_lock_,
+ REQUIRES(!GetDeoptimizedMethodsLock(),
!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_);
@@ -583,18 +585,21 @@
// Read barrier-aware utility functions for accessing deoptimized_methods_
bool AddDeoptimizedMethod(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(deoptimized_methods_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(GetDeoptimizedMethodsLock());
bool IsDeoptimizedMethod(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_, deoptimized_methods_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_, GetDeoptimizedMethodsLock());
bool RemoveDeoptimizedMethod(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(deoptimized_methods_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(GetDeoptimizedMethodsLock());
ArtMethod* BeginDeoptimizedMethod()
- REQUIRES_SHARED(Locks::mutator_lock_, deoptimized_methods_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_, GetDeoptimizedMethodsLock());
bool IsDeoptimizedMethodsEmpty() const
- REQUIRES_SHARED(Locks::mutator_lock_, deoptimized_methods_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_, GetDeoptimizedMethodsLock());
void UpdateMethodsCodeImpl(ArtMethod* method, const void* quick_code)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!deoptimized_methods_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
+ ReaderWriterMutex* GetDeoptimizedMethodsLock() const {
+ return deoptimized_methods_lock_.get();
+ }
// Have we hijacked ArtMethod::code_ so that it calls instrumentation/interpreter code?
bool instrumentation_stubs_installed_;
@@ -676,8 +681,8 @@
// The set of methods being deoptimized (by the debugger) which must be executed with interpreter
// only.
- mutable ReaderWriterMutex deoptimized_methods_lock_ BOTTOM_MUTEX_ACQUIRED_AFTER;
- std::unordered_set<ArtMethod*> deoptimized_methods_ GUARDED_BY(deoptimized_methods_lock_);
+ mutable std::unique_ptr<ReaderWriterMutex> deoptimized_methods_lock_ BOTTOM_MUTEX_ACQUIRED_AFTER;
+ std::unordered_set<ArtMethod*> deoptimized_methods_ GUARDED_BY(GetDeoptimizedMethodsLock());
bool deoptimization_enabled_;
// Current interpreter handler table. This is updated each time the thread state flags are
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
index 24a026a..16e118c 100644
--- a/runtime/interpreter/interpreter_intrinsics.cc
+++ b/runtime/interpreter/interpreter_intrinsics.cc
@@ -559,6 +559,7 @@
UNIMPLEMENTED_CASE(IntegerValueOf /* (I)Ljava/lang/Integer; */)
UNIMPLEMENTED_CASE(ThreadInterrupted /* ()Z */)
UNIMPLEMENTED_CASE(CRC32Update /* (II)I */)
+ UNIMPLEMENTED_CASE(CRC32UpdateBytes /* (I[BII)I */)
INTRINSIC_CASE(VarHandleFullFence)
INTRINSIC_CASE(VarHandleAcquireFence)
INTRINSIC_CASE(VarHandleReleaseFence)
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index e292a76..4fa7271 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -571,12 +571,9 @@
Runtime* runtime = Runtime::Current();
- std::vector<std::string> split;
- Split(runtime->GetBootClassPathString(), ':', &split);
- if (split.empty()) {
- AbortTransactionOrFail(self,
- "Boot classpath not set or split error:: %s",
- runtime->GetBootClassPathString().c_str());
+ const std::vector<std::string>& boot_class_path = Runtime::Current()->GetBootClassPath();
+ if (boot_class_path.empty()) {
+ AbortTransactionOrFail(self, "Boot classpath not set");
return;
}
@@ -584,7 +581,7 @@
size_t map_size;
std::string last_error_msg; // Only store the last message (we could concatenate).
- for (const std::string& jar_file : split) {
+ for (const std::string& jar_file : boot_class_path) {
mem_map = FindAndExtractEntry(jar_file, resource_cstr, &map_size, &last_error_msg);
if (mem_map.IsValid()) {
break;
diff --git a/runtime/intrinsics_list.h b/runtime/intrinsics_list.h
index 093dd7f..82ea476 100644
--- a/runtime/intrinsics_list.h
+++ b/runtime/intrinsics_list.h
@@ -220,6 +220,7 @@
V(VarHandleStoreStoreFence, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "storeStoreFence", "()V") \
V(ReachabilityFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/ref/Reference;", "reachabilityFence", "(Ljava/lang/Object;)V") \
V(CRC32Update, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/util/zip/CRC32;", "update", "(II)I") \
+ V(CRC32UpdateBytes, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/util/zip/CRC32;", "updateBytes", "(I[BII)I") \
SIGNATURE_POLYMORPHIC_INTRINSICS_LIST(V)
#endif // ART_RUNTIME_INTRINSICS_LIST_H_
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index d31f166..37365ff 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -292,8 +292,7 @@
expandBufAddUtf8String(pReply, str);
}
- std::vector<std::string> boot_class_path;
- Split(Runtime::Current()->GetBootClassPathString(), ':', &boot_class_path);
+ std::vector<std::string> boot_class_path = Runtime::Current()->GetBootClassPath();
expandBufAdd4BE(pReply, boot_class_path.size());
for (const std::string& str : boot_class_path) {
expandBufAddUtf8String(pReply, str);
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 4a3ef07..e43d771 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -291,22 +291,6 @@
return success;
}
-void Jit::CreateThreadPool() {
- if (Runtime::Current()->IsSafeMode()) {
- // Never create the pool in safe mode.
- return;
- }
- // There is a DCHECK in the 'AddSamples' method to ensure the thread pool
- // is not null when we instrument.
-
- // We need peers as we may report the JIT thread, e.g., in the debugger.
- constexpr bool kJitPoolNeedsPeers = true;
- thread_pool_.reset(new ThreadPool("Jit thread pool", 1, kJitPoolNeedsPeers));
-
- thread_pool_->SetPthreadPriority(options_->GetThreadPoolPthreadPriority());
- Start();
-}
-
void Jit::DeleteThreadPool() {
Thread* self = Thread::Current();
DCHECK(Runtime::Current()->IsShuttingDown(self));
@@ -562,10 +546,10 @@
class JitCompileTask final : public Task {
public:
- enum TaskKind {
+ enum class TaskKind {
kAllocateProfile,
kCompile,
- kCompileOsr
+ kCompileOsr,
};
JitCompileTask(ArtMethod* method, TaskKind kind) : method_(method), kind_(kind) {
@@ -582,14 +566,20 @@
void Run(Thread* self) override {
ScopedObjectAccess soa(self);
- if (kind_ == kCompile) {
- Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr= */ false);
- } else if (kind_ == kCompileOsr) {
- Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr= */ true);
- } else {
- DCHECK(kind_ == kAllocateProfile);
- if (ProfilingInfo::Create(self, method_, /* retry_allocation= */ true)) {
- VLOG(jit) << "Start profiling " << ArtMethod::PrettyMethod(method_);
+ switch (kind_) {
+ case TaskKind::kCompile:
+ case TaskKind::kCompileOsr: {
+ Runtime::Current()->GetJit()->CompileMethod(
+ method_,
+ self,
+ /* osr= */ (kind_ == TaskKind::kCompileOsr));
+ break;
+ }
+ case TaskKind::kAllocateProfile: {
+ if (ProfilingInfo::Create(self, method_, /* retry_allocation= */ true)) {
+ VLOG(jit) << "Start profiling " << ArtMethod::PrettyMethod(method_);
+ }
+ break;
}
}
ProfileSaver::NotifyJitActivity();
@@ -607,6 +597,18 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask);
};
+void Jit::CreateThreadPool() {
+ // There is a DCHECK in the 'AddSamples' method to ensure the tread pool
+ // is not null when we instrument.
+
+ // We need peers as we may report the JIT thread, e.g., in the debugger.
+ constexpr bool kJitPoolNeedsPeers = true;
+ thread_pool_.reset(new ThreadPool("Jit thread pool", 1, kJitPoolNeedsPeers));
+
+ thread_pool_->SetPthreadPriority(options_->GetThreadPoolPthreadPriority());
+ Start();
+}
+
static bool IgnoreSamplesForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
if (method->IsClassInitializer() || !method->IsCompilable()) {
// We do not want to compile such methods.
@@ -630,11 +632,10 @@
void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_backedges) {
if (thread_pool_ == nullptr) {
- // Should only see this when shutting down, starting up, or in zygote, which doesn't
- // have a thread pool.
+ // Should only see this when shutting down, starting up, or in safe mode.
DCHECK(Runtime::Current()->IsShuttingDown(self) ||
!Runtime::Current()->IsFinishedStarting() ||
- Runtime::Current()->IsZygote());
+ Runtime::Current()->IsSafeMode());
return;
}
if (IgnoreSamplesForMethod(method)) {
@@ -675,7 +676,8 @@
if (!success) {
// We failed allocating. Instead of doing the collection on the Java thread, we push
// an allocation to a compiler thread, that will do the collection.
- thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kAllocateProfile));
+ thread_pool_->AddTask(
+ self, new JitCompileTask(method, JitCompileTask::TaskKind::kAllocateProfile));
}
}
// Avoid jumping more than one state at a time.
@@ -685,7 +687,7 @@
if ((new_count >= HotMethodThreshold()) &&
!code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
DCHECK(thread_pool_ != nullptr);
- thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
+ thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::TaskKind::kCompile));
}
// Avoid jumping more than one state at a time.
new_count = std::min(new_count, static_cast<uint32_t>(OSRMethodThreshold() - 1));
@@ -697,7 +699,8 @@
DCHECK(!method->IsNative()); // No back edges reported for native methods.
if ((new_count >= OSRMethodThreshold()) && !code_cache_->IsOsrCompiled(method)) {
DCHECK(thread_pool_ != nullptr);
- thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
+ thread_pool_->AddTask(
+ self, new JitCompileTask(method, JitCompileTask::TaskKind::kCompileOsr));
}
}
}
@@ -730,7 +733,7 @@
// The compiler requires a ProfilingInfo object for non-native methods.
ProfilingInfo::Create(thread, np_method, /* retry_allocation= */ true);
}
- JitCompileTask compile_task(method, JitCompileTask::kCompile);
+ JitCompileTask compile_task(method, JitCompileTask::TaskKind::kCompile);
// Fake being in a runtime thread so that class-load behavior will be the same as normal jit.
ScopedSetRuntimeThread ssrt(thread);
compile_task.Run(thread);
@@ -798,7 +801,16 @@
}
}
-void Jit::PostForkChildAction() {
+void Jit::PostForkChildAction(bool is_zygote) {
+ if (is_zygote) {
+ // Don't transition if this is for a child zygote.
+ return;
+ }
+ if (Runtime::Current()->IsSafeMode()) {
+ // Delete the thread pool, we are not going to JIT.
+ thread_pool_.reset(nullptr);
+ return;
+ }
// At this point, the compiler options have been adjusted to the particular configuration
// of the forked child. Parse them again.
jit_update_options_(jit_compiler_handle_);
@@ -806,6 +818,28 @@
// Adjust the status of code cache collection: the status from zygote was to not collect.
code_cache_->SetGarbageCollectCode(!jit_generate_debug_info_(jit_compiler_handle_) &&
!Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled());
+
+ if (thread_pool_ != nullptr) {
+ // Remove potential tasks that have been inherited from the zygote.
+ thread_pool_->RemoveAllTasks(Thread::Current());
+
+ // Resume JIT compilation.
+ thread_pool_->CreateThreads();
+ }
+}
+
+void Jit::PreZygoteFork() {
+ if (thread_pool_ == nullptr) {
+ return;
+ }
+ thread_pool_->DeleteThreads();
+}
+
+void Jit::PostZygoteFork() {
+ if (thread_pool_ == nullptr) {
+ return;
+ }
+ thread_pool_->CreateThreads();
}
} // namespace jit
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index e12b032..7ce5f07 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -285,8 +285,14 @@
// Start JIT threads.
void Start();
- // Transition to a zygote child state.
- void PostForkChildAction();
+ // Transition to a child state.
+ void PostForkChildAction(bool is_zygote);
+
+ // Prepare for forking.
+ void PreZygoteFork();
+
+ // Adjust state after forking.
+ void PostZygoteFork();
private:
Jit(JitCodeCache* code_cache, JitOptions* options);
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 97887cc..749758a 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -436,6 +436,12 @@
initial_capacity = RoundDown(initial_capacity, 2 * kPageSize);
max_capacity = RoundDown(max_capacity, 2 * kPageSize);
+ used_memory_for_data_ = 0;
+ used_memory_for_code_ = 0;
+ number_of_compilations_ = 0;
+ number_of_osr_compilations_ = 0;
+ number_of_collections_ = 0;
+
data_pages_ = MemMap();
exec_pages_ = MemMap();
non_exec_pages_ = MemMap();
@@ -477,7 +483,7 @@
JitCodeCache::~JitCodeCache() {}
bool JitCodeCache::ContainsPc(const void* ptr) const {
- return exec_pages_.Begin() <= ptr && ptr < exec_pages_.End();
+ return exec_pages_.HasAddress(ptr) || zygote_exec_pages_.HasAddress(ptr);
}
bool JitCodeCache::WillExecuteJitCode(ArtMethod* method) {
@@ -752,6 +758,10 @@
}
void JitCodeCache::FreeCodeAndData(const void* code_ptr) {
+ if (IsInZygoteExecSpace(code_ptr)) {
+ // No need to free, this is shared memory.
+ return;
+ }
uintptr_t allocation = FromCodeToAllocation(code_ptr);
// Notify native debugger that we are about to remove the code.
// It does nothing if we are not using native debugger.
@@ -1321,7 +1331,7 @@
return true;
}
const void* code = method_header->GetCode();
- if (code_cache_->ContainsPc(code)) {
+ if (code_cache_->ContainsPc(code) && !code_cache_->IsInZygoteExecSpace(code)) {
// Use the atomic set version, as multiple threads are executing this code.
bitmap_->AtomicTestAndSet(FromCodeToAllocation(code));
}
@@ -1493,7 +1503,7 @@
// interpreter will update its entry point to the compiled code and call it.
for (ProfilingInfo* info : profiling_infos_) {
const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode();
- if (ContainsPc(entry_point)) {
+ if (!IsInZygoteDataSpace(info) && ContainsPc(entry_point)) {
info->SetSavedEntryPoint(entry_point);
// Don't call Instrumentation::UpdateMethodsCode(), as it can check the declaring
// class of the method. We may be concurrently running a GC which makes accessing
@@ -1508,7 +1518,7 @@
// Change entry points of native methods back to the GenericJNI entrypoint.
for (const auto& entry : jni_stubs_map_) {
const JniStubData& data = entry.second;
- if (!data.IsCompiled()) {
+ if (!data.IsCompiled() || IsInZygoteExecSpace(data.GetCode())) {
continue;
}
// Make sure a single invocation of the GenericJNI trampoline tries to recompile.
@@ -1540,7 +1550,9 @@
// Iterate over all compiled code and remove entries that are not marked.
for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) {
JniStubData* data = &it->second;
- if (!data->IsCompiled() || GetLiveBitmap()->Test(FromCodeToAllocation(data->GetCode()))) {
+ if (IsInZygoteExecSpace(data->GetCode()) ||
+ !data->IsCompiled() ||
+ GetLiveBitmap()->Test(FromCodeToAllocation(data->GetCode()))) {
++it;
} else {
method_headers.insert(OatQuickMethodHeader::FromCodePointer(data->GetCode()));
@@ -1550,7 +1562,7 @@
for (auto it = method_code_map_.begin(); it != method_code_map_.end();) {
const void* code_ptr = it->first;
uintptr_t allocation = FromCodeToAllocation(code_ptr);
- if (GetLiveBitmap()->Test(allocation)) {
+ if (IsInZygoteExecSpace(code_ptr) || GetLiveBitmap()->Test(allocation)) {
++it;
} else {
OatQuickMethodHeader* header = OatQuickMethodHeader::FromCodePointer(code_ptr);
@@ -1571,7 +1583,7 @@
// Also remove the saved entry point from the ProfilingInfo objects.
for (ProfilingInfo* info : profiling_infos_) {
const void* ptr = info->GetMethod()->GetEntryPointFromQuickCompiledCode();
- if (!ContainsPc(ptr) && !info->IsInUseByCompiler()) {
+ if (!ContainsPc(ptr) && !info->IsInUseByCompiler() && !IsInZygoteDataSpace(info)) {
info->GetMethod()->SetProfilingInfo(nullptr);
}
@@ -1596,6 +1608,9 @@
for (const auto& entry : jni_stubs_map_) {
const JniStubData& data = entry.second;
const void* code_ptr = data.GetCode();
+ if (IsInZygoteExecSpace(code_ptr)) {
+ continue;
+ }
const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
for (ArtMethod* method : data.GetMethods()) {
if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) {
@@ -1607,6 +1622,9 @@
for (const auto& it : method_code_map_) {
ArtMethod* method = it.second;
const void* code_ptr = it.first;
+ if (IsInZygoteExecSpace(code_ptr)) {
+ continue;
+ }
const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) {
GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr));
@@ -1953,6 +1971,7 @@
instrumentation->UpdateNativeMethodsCodeToJitCode(m, entrypoint);
}
if (collection_in_progress_) {
+ CHECK(!IsInZygoteExecSpace(data->GetCode()));
GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(data->GetCode()));
}
}
@@ -2057,6 +2076,10 @@
}
void JitCodeCache::FreeCode(uint8_t* code) {
+ if (IsInZygoteExecSpace(code)) {
+ // No need to free, this is shared memory.
+ return;
+ }
used_memory_for_code_ -= mspace_usable_size(code);
mspace_free(exec_mspace_, code);
}
@@ -2068,6 +2091,10 @@
}
void JitCodeCache::FreeData(uint8_t* data) {
+ if (IsInZygoteDataSpace(data)) {
+ // No need to free, this is shared memory.
+ return;
+ }
used_memory_for_data_ -= mspace_usable_size(data);
mspace_free(data_mspace_, data);
}
@@ -2091,13 +2118,11 @@
}
void JitCodeCache::PostForkChildAction(bool is_system_server, bool is_zygote) {
+ if (is_zygote) {
+ // Don't transition if this is for a child zygote.
+ return;
+ }
MutexLock mu(Thread::Current(), lock_);
- // Currently, we don't expect any compilations from zygote.
- CHECK_EQ(number_of_compilations_, 0u);
- CHECK_EQ(number_of_osr_compilations_, 0u);
- CHECK(jni_stubs_map_.empty());
- CHECK(method_code_map_.empty());
- CHECK(osr_code_map_.empty());
zygote_data_pages_ = std::move(data_pages_);
zygote_exec_pages_ = std::move(exec_pages_);
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 7a838fd..e2f3357 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -71,6 +71,7 @@
namespace jit {
+class MarkCodeClosure;
class ScopedCodeCacheWrite;
// Alignment in bits that will suit all architectures.
@@ -387,6 +388,14 @@
const MemMap* GetUpdatableCodeMapping() const;
+ bool IsInZygoteDataSpace(const void* ptr) const {
+ return zygote_data_pages_.HasAddress(ptr);
+ }
+
+ bool IsInZygoteExecSpace(const void* ptr) const {
+ return zygote_exec_pages_.HasAddress(ptr);
+ }
+
bool IsWeakAccessEnabled(Thread* self) const;
void WaitUntilInlineCacheAccessible(Thread* self)
REQUIRES(!lock_)
@@ -487,6 +496,7 @@
friend class art::JitJniStubTestHelper;
friend class ScopedCodeCacheWrite;
+ friend class MarkCodeClosure;
DISALLOW_COPY_AND_ASSIGN(JitCodeCache);
};
diff --git a/runtime/mirror/call_site.h b/runtime/mirror/call_site.h
index 9b6afca..be5bdc9 100644
--- a/runtime/mirror/call_site.h
+++ b/runtime/mirror/call_site.h
@@ -17,7 +17,6 @@
#ifndef ART_RUNTIME_MIRROR_CALL_SITE_H_
#define ART_RUNTIME_MIRROR_CALL_SITE_H_
-#include "base/utils.h"
#include "mirror/method_handle_impl.h"
namespace art {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 4e551ad..66b1405 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -22,7 +22,6 @@
#include "base/enums.h"
#include "base/iteration_range.h"
#include "base/stride_iterator.h"
-#include "base/utils.h"
#include "class_flags.h"
#include "class_status.h"
#include "dex/dex_file.h"
diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h
index 014b211..9cceff9 100644
--- a/runtime/mirror/method_type.h
+++ b/runtime/mirror/method_type.h
@@ -17,7 +17,6 @@
#ifndef ART_RUNTIME_MIRROR_METHOD_TYPE_H_
#define ART_RUNTIME_MIRROR_METHOD_TYPE_H_
-#include "base/utils.h"
#include "object_array.h"
#include "object.h"
#include "string.h"
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index 98cc4a8..b984474 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -24,7 +24,6 @@
#include "android-base/stringprintf.h"
#include "array-inl.h"
-#include "base/utils.h"
#include "class.h"
#include "obj_ptr-inl.h"
#include "object-inl.h"
diff --git a/runtime/mirror/string-alloc-inl.h b/runtime/mirror/string-alloc-inl.h
index c026c67..c31eccf 100644
--- a/runtime/mirror/string-alloc-inl.h
+++ b/runtime/mirror/string-alloc-inl.h
@@ -23,7 +23,6 @@
#include "array.h"
#include "base/bit_utils.h"
#include "base/globals.h"
-#include "base/utils.h"
#include "class.h"
#include "class_root.h"
#include "gc/heap-inl.h"
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index d89ef1e..e11906a 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -21,7 +21,6 @@
#include "android-base/stringprintf.h"
#include "base/globals.h"
-#include "base/utils.h"
#include "class-inl.h"
#include "common_throws.h"
#include "dex/utf.h"
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index e213dc7..3e5003c 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -24,7 +24,8 @@
#include <limits.h>
#include "nativehelper/scoped_utf_chars.h"
-#include "android-base/stringprintf.h"
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include "arch/instruction_set.h"
#include "art_method-inl.h"
@@ -222,7 +223,8 @@
}
static jstring VMRuntime_bootClassPath(JNIEnv* env, jobject) {
- return env->NewStringUTF(DefaultToDot(Runtime::Current()->GetBootClassPathString()));
+ std::string boot_class_path = android::base::Join(Runtime::Current()->GetBootClassPath(), ':');
+ return env->NewStringUTF(DefaultToDot(boot_class_path));
}
static jstring VMRuntime_classPath(JNIEnv* env, jobject) {
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 0f655b9..b7ac1e8 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -249,6 +249,13 @@
return reinterpret_cast<jlong>(ThreadForEnv(env));
}
+static void ZygoteHooks_nativePostZygoteFork(JNIEnv*, jclass) {
+ Runtime* runtime = Runtime::Current();
+ if (runtime->IsZygote()) {
+ runtime->PostZygoteFork();
+ }
+}
+
static void ZygoteHooks_nativePostForkSystemServer(JNIEnv* env ATTRIBUTE_UNUSED,
jclass klass ATTRIBUTE_UNUSED) {
// This JIT code cache for system server is created whilst the runtime is still single threaded.
@@ -305,7 +312,7 @@
/* is_system_server= */ false, is_zygote);
}
// This must be called after EnableDebugFeatures.
- Runtime::Current()->GetJit()->PostForkChildAction();
+ Runtime::Current()->GetJit()->PostForkChildAction(is_zygote);
}
// Update tracing.
@@ -403,6 +410,7 @@
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(ZygoteHooks, nativePreFork, "()J"),
+ NATIVE_METHOD(ZygoteHooks, nativePostZygoteFork, "()V"),
NATIVE_METHOD(ZygoteHooks, nativePostForkSystemServer, "()V"),
NATIVE_METHOD(ZygoteHooks, nativePostForkChild, "(JIZZLjava/lang/String;)V"),
NATIVE_METHOD(ZygoteHooks, startZygoteNoThreadCreation, "()V"),
diff --git a/runtime/oat.h b/runtime/oat.h
index ee46f42..b09c81e 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -31,8 +31,8 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- // Last oat version changed reason: Image checksums.
- static constexpr uint8_t kOatVersion[] = { '1', '6', '4', '\0' };
+ // Last oat version changed reason: Pass boot class path to LoadBootImage.
+ static constexpr uint8_t kOatVersion[] = { '1', '6', '5', '\0' };
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
static constexpr const char* kDebuggableKey = "debuggable";
diff --git a/runtime/oat_file-inl.h b/runtime/oat_file-inl.h
index 721fab9..b71c4e8 100644
--- a/runtime/oat_file-inl.h
+++ b/runtime/oat_file-inl.h
@@ -18,6 +18,8 @@
#define ART_RUNTIME_OAT_FILE_INL_H_
#include "oat_file.h"
+
+#include "base/utils.h"
#include "oat_quick_method_header.h"
namespace art {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 4294baf..ab6e62d 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -27,7 +27,6 @@
#include "base/safe_map.h"
#include "base/stringpiece.h"
#include "base/tracking_safe_map.h"
-#include "base/utils.h"
#include "class_status.h"
#include "compiler_filter.h"
#include "dex/dex_file.h"
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 29b5690..17ff3a2 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -20,6 +20,7 @@
#include <sstream>
#include <android-base/logging.h>
+#include <android-base/strings.h>
#include "base/file_utils.h"
#include "base/macros.h"
@@ -78,7 +79,7 @@
.Define("-showversion")
.IntoKey(M::ShowVersion)
.Define("-Xbootclasspath:_")
- .WithType<std::string>()
+ .WithType<ParseStringList<':'>>() // std::vector<std::string>, split by :
.IntoKey(M::BootClassPath)
.Define("-Xbootclasspath-locations:_")
.WithType<ParseStringList<':'>>() // std::vector<std::string>, split by :
@@ -513,7 +514,7 @@
GetInstructionSetString(kRuntimeISA));
Exit(0);
} else if (args.Exists(M::BootClassPath)) {
- LOG(INFO) << "setting boot class path to " << *args.Get(M::BootClassPath);
+ LOG(INFO) << "setting boot class path to " << args.Get(M::BootClassPath)->Join();
}
if (args.GetOrDefault(M::Interpret)) {
@@ -525,8 +526,9 @@
}
// Set a default boot class path if we didn't get an explicit one via command line.
- if (getenv("BOOTCLASSPATH") != nullptr) {
- args.SetIfMissing(M::BootClassPath, std::string(getenv("BOOTCLASSPATH")));
+ const char* env_bcp = getenv("BOOTCLASSPATH");
+ if (env_bcp != nullptr) {
+ args.SetIfMissing(M::BootClassPath, ParseStringList<':'>::Split(env_bcp));
}
// Set a default class path if we didn't get an explicit one via command line.
@@ -586,22 +588,20 @@
args.Set(M::BackgroundGc, BackgroundGcOption { background_collector_type_ });
}
- auto boot_class_path_string = args.GetOrDefault(M::BootClassPath);
- {
- auto&& boot_class_path = args.GetOrDefault(M::BootClassPath);
- auto&& boot_class_path_locations = args.GetOrDefault(M::BootClassPathLocations);
- if (args.Exists(M::BootClassPathLocations)) {
- size_t boot_class_path_count = ParseStringList<':'>::Split(boot_class_path).Size();
-
- if (boot_class_path_count != boot_class_path_locations.Size()) {
- Usage("The number of boot class path files does not match"
- " the number of boot class path locations given\n"
- " boot class path files (%zu): %s\n"
- " boot class path locations (%zu): %s\n",
- boot_class_path.size(), boot_class_path_string.c_str(),
- boot_class_path_locations.Size(), boot_class_path_locations.Join().c_str());
- return false;
- }
+ const ParseStringList<':'>* boot_class_path_locations = args.Get(M::BootClassPathLocations);
+ if (boot_class_path_locations != nullptr && boot_class_path_locations->Size() != 0u) {
+ const ParseStringList<':'>* boot_class_path = args.Get(M::BootClassPath);
+ if (boot_class_path == nullptr ||
+ boot_class_path_locations->Size() != boot_class_path->Size()) {
+ Usage("The number of boot class path files does not match"
+ " the number of boot class path locations given\n"
+ " boot class path files (%zu): %s\n"
+ " boot class path locations (%zu): %s\n",
+ (boot_class_path != nullptr) ? boot_class_path->Size() : 0u,
+ (boot_class_path != nullptr) ? boot_class_path->Join().c_str() : "<nil>",
+ boot_class_path_locations->Size(),
+ boot_class_path_locations->Join().c_str());
+ return false;
}
}
diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc
index 705cc6c..cbb7b82 100644
--- a/runtime/parsed_options_test.cc
+++ b/runtime/parsed_options_test.cc
@@ -40,8 +40,7 @@
boot_class_path += "-Xbootclasspath:";
bool first_dex_file = true;
- for (const std::string &dex_file_name :
- CommonRuntimeTest::GetLibCoreDexFileNames()) {
+ for (const std::string &dex_file_name : CommonRuntimeTest::GetLibCoreDexFileNames()) {
if (!first_dex_file) {
class_path += ":";
} else {
@@ -50,6 +49,8 @@
class_path += dex_file_name;
}
boot_class_path += class_path;
+ std::vector<std::string> expected_boot_class_path;
+ Split(class_path, ':', &expected_boot_class_path);
RuntimeOptions options;
options.push_back(std::make_pair(boot_class_path.c_str(), nullptr));
@@ -78,9 +79,11 @@
using Opt = RuntimeArgumentMap;
#define EXPECT_PARSED_EQ(expected, actual_key) EXPECT_EQ(expected, map.GetOrDefault(actual_key))
+#define EXPECT_PARSED_EQ_AS_STRING_VECTOR(expected, actual_key) \
+ EXPECT_EQ(expected, static_cast<std::vector<std::string>>(map.GetOrDefault(actual_key)))
#define EXPECT_PARSED_EXISTS(actual_key) EXPECT_TRUE(map.Exists(actual_key))
- EXPECT_PARSED_EQ(class_path, Opt::BootClassPath);
+ EXPECT_PARSED_EQ_AS_STRING_VECTOR(expected_boot_class_path, Opt::BootClassPath);
EXPECT_PARSED_EQ(class_path, Opt::ClassPath);
EXPECT_PARSED_EQ(std::string("boot_image"), Opt::Image);
EXPECT_PARSED_EXISTS(Opt::CheckJni);
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index 672303a..1bcbcff 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -19,7 +19,6 @@
#include "read_barrier.h"
-#include "base/utils.h"
#include "gc/accounting/read_barrier_table.h"
#include "gc/collector/concurrent_copying-inl.h"
#include "gc/heap.h"
diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h
index 9fe4bca..8ad61f0 100644
--- a/runtime/reflection-inl.h
+++ b/runtime/reflection-inl.h
@@ -21,7 +21,6 @@
#include "android-base/stringprintf.h"
-#include "base/utils.h"
#include "common_throws.h"
#include "dex/descriptors_names.h"
#include "dex/primitive.h"
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index d53cc07..69ef2fb 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -34,7 +34,6 @@
#include <cstdio>
#include <cstdlib>
#include <limits>
-#include <thread>
#include <vector>
#include "android-base/strings.h"
@@ -336,8 +335,7 @@
<< " out of process CPU time " << PrettyDuration(process_cpu_time)
<< " (" << ratio << ")"
<< "\n";
- float weighted_allocated_bytes =
- static_cast<float>(heap_->GetWeightedAllocatedBytes()) / process_cpu_time;
+ double weighted_allocated_bytes = heap_->GetWeightedAllocatedBytes() / process_cpu_time;
LOG_STREAM(INFO) << "Weighted bytes allocated over CPU time: "
<< " (" << PrettySize(weighted_allocated_bytes) << ")"
<< "\n";
@@ -393,11 +391,6 @@
jit_->DeleteThreadPool();
}
- // Thread pools must be deleted before the runtime shuts down to avoid hanging.
- if (thread_pool_ != nullptr) {
- thread_pool_.reset();
- }
-
// Make sure our internal threads are dead before we start tearing down things they're using.
GetRuntimeCallbacks()->StopDebugger();
delete signal_catcher_;
@@ -548,18 +541,18 @@
void Runtime::Abort(const char* msg) {
auto old_value = gAborting.fetch_add(1); // set before taking any locks
-#ifdef ART_TARGET_ANDROID
+ // Only set the first abort message.
if (old_value == 0) {
- // Only set the first abort message.
- android_set_abort_message(msg);
- }
-#else
- UNUSED(old_value);
-#endif
-
#ifdef ART_TARGET_ANDROID
- android_set_abort_message(msg);
+ android_set_abort_message(msg);
+#else
+ // Set the runtime fault message in case our unexpected-signal code will run.
+ Runtime* current = Runtime::Current();
+ if (current != nullptr) {
+ current->SetFaultMessage(msg);
+ }
#endif
+ }
// Ensure that we don't have multiple threads trying to abort at once,
// which would result in significantly worse diagnostics.
@@ -600,9 +593,18 @@
}
void Runtime::PreZygoteFork() {
+ if (GetJit() != nullptr) {
+ GetJit()->PreZygoteFork();
+ }
heap_->PreZygoteFork();
}
+void Runtime::PostZygoteFork() {
+ if (GetJit() != nullptr) {
+ GetJit()->PostZygoteFork();
+ }
+}
+
void Runtime::CallExitHook(jint status) {
if (exit_ != nullptr) {
ScopedThreadStateChange tsc(Thread::Current(), kNative);
@@ -916,19 +918,6 @@
}
}
- if (jit_ != nullptr) {
- jit_->CreateThreadPool();
- }
-
- if (thread_pool_ == nullptr) {
- constexpr size_t kStackSize = 64 * KB;
- constexpr size_t kMaxRuntimeWorkers = 4u;
- const size_t num_workers =
- std::min(static_cast<size_t>(std::thread::hardware_concurrency()), kMaxRuntimeWorkers);
- thread_pool_.reset(new ThreadPool("Runtime", num_workers, /*create_peers=*/false, kStackSize));
- thread_pool_->StartWorkers(Thread::Current());
- }
-
// Create the thread pools.
heap_->CreateThreadPool();
// Reset the gc performance data at zygote fork so that the GCs
@@ -1092,7 +1081,45 @@
Monitor::Init(runtime_options.GetOrDefault(Opt::LockProfThreshold),
runtime_options.GetOrDefault(Opt::StackDumpLockProfThreshold));
- boot_class_path_string_ = runtime_options.ReleaseOrDefault(Opt::BootClassPath);
+ image_location_ = runtime_options.GetOrDefault(Opt::Image);
+ SetInstructionSet(runtime_options.GetOrDefault(Opt::ImageInstructionSet));
+ boot_class_path_ = runtime_options.ReleaseOrDefault(Opt::BootClassPath);
+ boot_class_path_locations_ = runtime_options.ReleaseOrDefault(Opt::BootClassPathLocations);
+ DCHECK(boot_class_path_locations_.empty() ||
+ boot_class_path_locations_.size() == boot_class_path_.size());
+ if (boot_class_path_.empty()) {
+ // Try to extract the boot class path from the system boot image.
+ if (image_location_.empty()) {
+ LOG(ERROR) << "Empty boot class path, cannot continue without image.";
+ return false;
+ }
+ std::string system_oat_filename = ImageHeader::GetOatLocationFromImageLocation(
+ GetSystemImageFilename(image_location_.c_str(), instruction_set_));
+ std::string system_oat_location = ImageHeader::GetOatLocationFromImageLocation(image_location_);
+ std::string error_msg;
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(/*zip_fd=*/ -1,
+ system_oat_filename,
+ system_oat_location,
+ /*executable=*/ false,
+ /*low_4gb=*/ false,
+ /*abs_dex_location=*/ nullptr,
+ /*reservation=*/ nullptr,
+ &error_msg));
+ if (oat_file == nullptr) {
+ LOG(ERROR) << "Could not open boot oat file for extracting boot class path: " << error_msg;
+ return false;
+ }
+ const OatHeader& oat_header = oat_file->GetOatHeader();
+ const char* oat_boot_class_path = oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
+ if (oat_boot_class_path != nullptr) {
+ Split(oat_boot_class_path, ':', &boot_class_path_);
+ }
+ if (boot_class_path_.empty()) {
+ LOG(ERROR) << "Boot class path missing from boot image oat file " << oat_file->GetLocation();
+ return false;
+ }
+ }
+
class_path_string_ = runtime_options.ReleaseOrDefault(Opt::ClassPath);
properties_ = runtime_options.ReleaseOrDefault(Opt::PropertiesList);
@@ -1118,7 +1145,6 @@
}
}
image_compiler_options_ = runtime_options.ReleaseOrDefault(Opt::ImageCompilerOptions);
- image_location_ = runtime_options.GetOrDefault(Opt::Image);
max_spins_before_thin_lock_inflation_ =
runtime_options.GetOrDefault(Opt::MaxSpinsBeforeThinLockInflation);
@@ -1187,8 +1213,10 @@
foreground_heap_growth_multiplier,
runtime_options.GetOrDefault(Opt::MemoryMaximumSize),
runtime_options.GetOrDefault(Opt::NonMovingSpaceCapacity),
- runtime_options.GetOrDefault(Opt::Image),
- runtime_options.GetOrDefault(Opt::ImageInstructionSet),
+ GetBootClassPath(),
+ GetBootClassPathLocations(),
+ image_location_,
+ instruction_set_,
// Override the collector type to CC if the read barrier config.
kUseReadBarrier ? gc::kCollectorTypeCC : xgc_option.collector_type_,
kUseReadBarrier ? BackgroundGcOption(gc::kCollectorTypeCCBackground)
@@ -1388,16 +1416,6 @@
image_space->VerifyImageAllocations();
}
}
- if (boot_class_path_string_.empty()) {
- // The bootclasspath is not explicitly specified: construct it from the loaded dex files.
- const std::vector<const DexFile*>& boot_class_path = GetClassLinker()->GetBootClassPath();
- std::vector<std::string> dex_locations;
- dex_locations.reserve(boot_class_path.size());
- for (const DexFile* dex_file : boot_class_path) {
- dex_locations.push_back(dex_file->GetLocation());
- }
- boot_class_path_string_ = android::base::Join(dex_locations, ':');
- }
{
ScopedTrace trace2("AddImageStringsToTable");
for (gc::space::ImageSpace* image_space : heap_->GetBootImageSpaces()) {
@@ -1410,24 +1428,12 @@
DeoptimizeBootImage();
}
} else {
- std::vector<std::string> dex_filenames;
- Split(boot_class_path_string_, ':', &dex_filenames);
-
- std::vector<std::string> dex_locations;
- if (!runtime_options.Exists(Opt::BootClassPathLocations)) {
- dex_locations = dex_filenames;
- } else {
- dex_locations = runtime_options.GetOrDefault(Opt::BootClassPathLocations);
- CHECK_EQ(dex_filenames.size(), dex_locations.size());
- }
-
std::vector<std::unique_ptr<const DexFile>> boot_class_path;
if (runtime_options.Exists(Opt::BootClassPathDexList)) {
boot_class_path.swap(*runtime_options.GetOrDefault(Opt::BootClassPathDexList));
} else {
- OpenDexFiles(dex_filenames, dex_locations, &boot_class_path);
+ OpenDexFiles(GetBootClassPath(), GetBootClassPathLocations(), &boot_class_path);
}
- instruction_set_ = runtime_options.GetOrDefault(Opt::ImageInstructionSet);
if (!class_linker_->InitWithoutImage(std::move(boot_class_path), &error_msg)) {
LOG(ERROR) << "Could not initialize without image: " << error_msg;
return false;
@@ -2448,6 +2454,8 @@
LOG(WARNING) << "Failed to allocate JIT";
// Release JIT code cache resources (several MB of memory).
jit_code_cache_.reset();
+ } else {
+ jit->CreateThreadPool();
}
}
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 5450c0f..76cfcd1 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -243,8 +243,14 @@
~Runtime();
- const std::string& GetBootClassPathString() const {
- return boot_class_path_string_;
+ const std::vector<std::string>& GetBootClassPath() const {
+ return boot_class_path_;
+ }
+
+ const std::vector<std::string>& GetBootClassPathLocations() const {
+ DCHECK(boot_class_path_locations_.empty() ||
+ boot_class_path_locations_.size() == boot_class_path_.size());
+ return boot_class_path_locations_.empty() ? boot_class_path_ : boot_class_path_locations_;
}
const std::string& GetClassPathString() const {
@@ -445,6 +451,7 @@
bool UseJitCompilation() const;
void PreZygoteFork();
+ void PostZygoteFork();
void InitNonZygoteOrPostFork(
JNIEnv* env,
bool is_system_server,
@@ -785,10 +792,6 @@
return verifier_logging_threshold_ms_;
}
- ThreadPool* GetThreadPool() {
- return thread_pool_.get();
- }
-
private:
static void InitPlatformSignalHandlers();
@@ -865,7 +868,8 @@
std::vector<std::string> image_compiler_options_;
std::string image_location_;
- std::string boot_class_path_string_;
+ std::vector<std::string> boot_class_path_;
+ std::vector<std::string> boot_class_path_locations_;
std::string class_path_string_;
std::vector<std::string> properties_;
@@ -888,9 +892,6 @@
// Shared linear alloc for now.
std::unique_ptr<LinearAlloc> linear_alloc_;
- // Thread pool
- std::unique_ptr<ThreadPool> thread_pool_;
-
// The number of spins that are done before thread suspension is used to forcibly inflate.
size_t max_spins_before_thin_lock_inflation_;
MonitorList* monitor_list_;
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 5cec309..2b2919e 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -37,7 +37,7 @@
RUNTIME_OPTIONS_KEY (Unit, Zygote)
RUNTIME_OPTIONS_KEY (Unit, Help)
RUNTIME_OPTIONS_KEY (Unit, ShowVersion)
-RUNTIME_OPTIONS_KEY (std::string, BootClassPath)
+RUNTIME_OPTIONS_KEY (ParseStringList<':'>,BootClassPath) // std::vector<std::string>
RUNTIME_OPTIONS_KEY (ParseStringList<':'>,BootClassPathLocations) // std::vector<std::string>
RUNTIME_OPTIONS_KEY (std::string, ClassPath)
RUNTIME_OPTIONS_KEY (std::string, Image)
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 6c41ae4..8bec2d9 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -39,6 +39,7 @@
#include <sstream>
#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
#include "arch/context-inl.h"
#include "arch/context.h"
@@ -1875,8 +1876,9 @@
// Grab the scheduler stats for this thread.
std::string scheduler_stats;
- if (ReadFileToString(StringPrintf("/proc/self/task/%d/schedstat", tid), &scheduler_stats)) {
- scheduler_stats.resize(scheduler_stats.size() - 1); // Lose the trailing '\n'.
+ if (ReadFileToString(StringPrintf("/proc/self/task/%d/schedstat", tid), &scheduler_stats)
+ && !scheduler_stats.empty()) {
+ scheduler_stats = android::base::Trim(scheduler_stats); // Lose the trailing '\n'.
} else {
scheduler_stats = "0 0 0";
}
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index 0f96510..de698c2 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -136,26 +136,33 @@
waiting_count_(0),
start_time_(0),
total_wait_time_(0),
- // Add one since the caller of constructor waits on the barrier too.
- creation_barier_(num_threads + 1),
+ creation_barier_(0),
max_active_workers_(num_threads),
- create_peers_(create_peers) {
- while (GetThreadCount() < num_threads) {
- const std::string worker_name = StringPrintf("%s worker thread %zu", name_.c_str(),
- GetThreadCount());
- threads_.push_back(new ThreadPoolWorker(this, worker_name, worker_stack_size));
+ create_peers_(create_peers),
+ worker_stack_size_(worker_stack_size) {
+ CreateThreads();
+}
+
+void ThreadPool::CreateThreads() {
+ CHECK(threads_.empty());
+ Thread* self = Thread::Current();
+ {
+ MutexLock mu(self, task_queue_lock_);
+ shutting_down_ = false;
+ // Add one since the caller of constructor waits on the barrier too.
+ creation_barier_.Init(self, max_active_workers_ + 1);
+ while (GetThreadCount() < max_active_workers_) {
+ const std::string worker_name = StringPrintf("%s worker thread %zu", name_.c_str(),
+ GetThreadCount());
+ threads_.push_back(
+ new ThreadPoolWorker(this, worker_name, worker_stack_size_));
+ }
}
// Wait for all of the threads to attach.
creation_barier_.Wait(Thread::Current());
}
-void ThreadPool::SetMaxActiveWorkers(size_t threads) {
- MutexLock mu(Thread::Current(), task_queue_lock_);
- CHECK_LE(threads, GetThreadCount());
- max_active_workers_ = threads;
-}
-
-ThreadPool::~ThreadPool() {
+void ThreadPool::DeleteThreads() {
{
Thread* self = Thread::Current();
MutexLock mu(self, task_queue_lock_);
@@ -165,10 +172,22 @@
task_queue_condition_.Broadcast(self);
completion_condition_.Broadcast(self);
}
- // Wait for the threads to finish.
+ // Wait for the threads to finish. We expect the user of the pool
+ // not to run multi-threaded calls to `CreateThreads` and `DeleteThreads`,
+ // so we don't guard the field here.
STLDeleteElements(&threads_);
}
+void ThreadPool::SetMaxActiveWorkers(size_t max_workers) {
+ MutexLock mu(Thread::Current(), task_queue_lock_);
+ CHECK_LE(max_workers, GetThreadCount());
+ max_active_workers_ = max_workers;
+}
+
+ThreadPool::~ThreadPool() {
+ DeleteThreads();
+}
+
void ThreadPool::StartWorkers(Thread* self) {
MutexLock mu(self, task_queue_lock_);
started_ = true;
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index fee009b..f55d72e 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -129,6 +129,12 @@
size_t worker_stack_size = ThreadPoolWorker::kDefaultStackSize);
virtual ~ThreadPool();
+ // Create the threads of this pool.
+ void CreateThreads();
+
+ // Stops and deletes all threads in this pool.
+ void DeleteThreads();
+
// Wait for all tasks currently on queue to get completed. If the pool has been stopped, only
// wait till all already running tasks are done.
// When the pool was created with peers for workers, do_work must not be true (see ThreadPool()).
@@ -174,7 +180,6 @@
// How many worker threads are waiting on the condition.
volatile size_t waiting_count_ GUARDED_BY(task_queue_lock_);
std::deque<Task*> tasks_ GUARDED_BY(task_queue_lock_);
- // TODO: make this immutable/const?
std::vector<ThreadPoolWorker*> threads_;
// Work balance detection.
uint64_t start_time_ GUARDED_BY(task_queue_lock_);
@@ -182,6 +187,7 @@
Barrier creation_barier_;
size_t max_active_workers_ GUARDED_BY(task_queue_lock_);
const bool create_peers_;
+ const size_t worker_stack_size_;
private:
friend class ThreadPoolWorker;
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index cde885c..a19cc92 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -48,6 +48,7 @@
jclass WellKnownClasses::dalvik_system_DexPathList;
jclass WellKnownClasses::dalvik_system_DexPathList__Element;
jclass WellKnownClasses::dalvik_system_EmulatedStackFrame;
+jclass WellKnownClasses::dalvik_system_InMemoryDexClassLoader;
jclass WellKnownClasses::dalvik_system_PathClassLoader;
jclass WellKnownClasses::dalvik_system_VMRuntime;
jclass WellKnownClasses::java_lang_annotation_Annotation__array;
@@ -306,6 +307,7 @@
dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
dalvik_system_DexPathList__Element = CacheClass(env, "dalvik/system/DexPathList$Element");
dalvik_system_EmulatedStackFrame = CacheClass(env, "dalvik/system/EmulatedStackFrame");
+ dalvik_system_InMemoryDexClassLoader = CacheClass(env, "dalvik/system/InMemoryDexClassLoader");
dalvik_system_PathClassLoader = CacheClass(env, "dalvik/system/PathClassLoader");
dalvik_system_VMRuntime = CacheClass(env, "dalvik/system/VMRuntime");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 66cbbec..f0e98a8 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -57,6 +57,7 @@
static jclass dalvik_system_DexPathList;
static jclass dalvik_system_DexPathList__Element;
static jclass dalvik_system_EmulatedStackFrame;
+ static jclass dalvik_system_InMemoryDexClassLoader;
static jclass dalvik_system_PathClassLoader;
static jclass dalvik_system_VMRuntime;
static jclass java_lang_annotation_Annotation__array;
diff --git a/test/1959-redefine-object-instrument/expected.txt b/test/1959-redefine-object-instrument/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/1959-redefine-object-instrument/expected.txt
diff --git a/test/1959-redefine-object-instrument/fake_redef_object.cc b/test/1959-redefine-object-instrument/fake_redef_object.cc
new file mode 100644
index 0000000..b1201ab
--- /dev/null
+++ b/test/1959-redefine-object-instrument/fake_redef_object.cc
@@ -0,0 +1,134 @@
+/*
+ * 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 <limits>
+#include <memory>
+
+#include "jni.h"
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+// Slicer's headers have code that triggers these warnings. b/65298177
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wsign-compare"
+#pragma clang diagnostic ignored "-Wunused-parameter"
+#include "slicer/instrumentation.h"
+#include "slicer/reader.h"
+#include "slicer/writer.h"
+#pragma clang diagnostic pop
+
+namespace art {
+namespace Test1959RedefineObjectInstrument {
+
+// Just pull it out of the dex file but don't bother changing anything.
+static void JNICALL RedefineObjectHook(jvmtiEnv *jvmti_env,
+ JNIEnv* env,
+ jclass class_being_redefined ATTRIBUTE_UNUSED,
+ jobject loader ATTRIBUTE_UNUSED,
+ const char* name,
+ jobject protection_domain ATTRIBUTE_UNUSED,
+ jint class_data_len,
+ const unsigned char* class_data,
+ jint* new_class_data_len,
+ unsigned char** new_class_data) {
+ if (strcmp(name, "java/lang/Object") != 0) {
+ return;
+ }
+
+ dex::Reader reader(class_data, class_data_len);
+ dex::u4 class_index = reader.FindClassIndex("Ljava/lang/Object;");
+ if (class_index == dex::kNoIndex) {
+ env->ThrowNew(env->FindClass("java/lang/RuntimeException"),
+ "Failed to find object in dex file!");
+ return;
+ }
+
+ reader.CreateClassIr(class_index);
+ auto dex_ir = reader.GetIr();
+ dex::Writer writer(dex_ir);
+
+ class JvmtiAllocator : public dex::Writer::Allocator {
+ public:
+ explicit JvmtiAllocator(jvmtiEnv* jvmti) : jvmti_(jvmti) {}
+
+ void* Allocate(size_t size) override {
+ unsigned char* res = nullptr;
+ jvmti_->Allocate(size, &res);
+ return res;
+ }
+
+ void Free(void* ptr) override {
+ jvmti_->Deallocate(reinterpret_cast<unsigned char*>(ptr));
+ }
+
+ private:
+ jvmtiEnv* jvmti_;
+ };
+ JvmtiAllocator allocator(jvmti_env);
+ size_t new_size;
+ *new_class_data = writer.CreateImage(&allocator, &new_size);
+ if (new_size > std::numeric_limits<jint>::max()) {
+ *new_class_data = nullptr;
+ env->ThrowNew(env->FindClass("java/lang/RuntimeException"),
+ "transform result is too large!");
+ return;
+ }
+ *new_class_data_len = static_cast<jint>(new_size);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_forceRedefine(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jclass obj_class,
+ jthread thr) {
+ if (IsJVM()) {
+ // RI so don't do anything.
+ return;
+ }
+ jvmtiCapabilities caps {.can_retransform_classes = 1};
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) {
+ return;
+ }
+ jvmtiEventCallbacks cb {.ClassFileLoadHook = RedefineObjectHook };
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->RetransformClasses(1, &obj_class))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
+ thr))) {
+ return;
+ }
+}
+
+} // namespace Test1959RedefineObjectInstrument
+} // namespace art
+
diff --git a/test/1959-redefine-object-instrument/info.txt b/test/1959-redefine-object-instrument/info.txt
new file mode 100644
index 0000000..d15c0e0
--- /dev/null
+++ b/test/1959-redefine-object-instrument/info.txt
@@ -0,0 +1,9 @@
+Regression test for bug related to interaction between instrumentation
+installation and class redefinition.
+
+Redefining a class does not update the instrumentation stack of a thread.
+This is generally fine because the method pointer in the instrumentation
+stack is only used for some sanity-checks, logging and method-exit events
+(where it being the non-obsolete version is advantageous). Unfortunately some
+of the checks fail to account for obsolete methods and can fail sanity
+checks.
\ No newline at end of file
diff --git a/test/1959-redefine-object-instrument/run b/test/1959-redefine-object-instrument/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/1959-redefine-object-instrument/run
@@ -0,0 +1,17 @@
+#!/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/1959-redefine-object-instrument/src/Main.java b/test/1959-redefine-object-instrument/src/Main.java
new file mode 100644
index 0000000..b3201f6
--- /dev/null
+++ b/test/1959-redefine-object-instrument/src/Main.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.LinkedList;
+import java.lang.reflect.Executable;
+
+import art.*;
+
+public class Main {
+ /**
+ * NB This test cannot be run on the RI.
+ * TODO We should make this run on the RI.
+ */
+
+ public static void main(String[] args) throws Exception {
+ doTest();
+ }
+
+ public static volatile boolean started = false;
+
+ public static void doNothing() {}
+ public static void notifyBreakpointReached(Thread thr, Executable e, long l) {}
+
+ public static void doTest() throws Exception {
+ final Object lock = new Object();
+ Breakpoint.Manager man = new Breakpoint.Manager();
+ Breakpoint.startBreakpointWatch(
+ Main.class,
+ Main.class.getDeclaredMethod("notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
+ null);
+ Thread thr = new Thread(() -> {
+ synchronized (lock) {
+ started = true;
+ // Wait basically forever.
+ try {
+ lock.wait(Integer.MAX_VALUE - 1);
+ } catch (Exception e) {
+ throw new Error("WAIT EXCEPTION", e);
+ }
+ }
+ });
+ // set the breakpoint.
+ man.setBreakpoint(Main.class.getDeclaredMethod("doNothing"), 0l);
+ thr.start();
+ while (!started || thr.getState() != Thread.State.TIMED_WAITING);
+ // Redefine while thread is paused.
+ forceRedefine(Object.class, Thread.currentThread());
+ // Clear breakpoints.
+ man.clearAllBreakpoints();
+ // set the breakpoint again.
+ man.setBreakpoint(Main.class.getDeclaredMethod("doNothing"), 0l);
+ // Wakeup
+ synchronized(lock) {
+ lock.notifyAll();
+ }
+ thr.join();
+ }
+
+ private static native void forceRedefine(Class c, Thread thr);
+}
diff --git a/test/1959-redefine-object-instrument/src/art/Breakpoint.java b/test/1959-redefine-object-instrument/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1959-redefine-object-instrument/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/580-crc32/src/Main.java b/test/580-crc32/src/Main.java
index 7fc1273..6199e9b 100644
--- a/test/580-crc32/src/Main.java
+++ b/test/580-crc32/src/Main.java
@@ -15,29 +15,29 @@
*/
import java.util.zip.CRC32;
+import java.util.Random;
/**
- * The ART compiler can use intrinsics for the java.util.zip.CRC32 method:
- * private native static int update(int crc, int b)
+ * The ART compiler can use intrinsics for the java.util.zip.CRC32 methods:
+ * private native static int update(int crc, int b)
+ * private native static int updateBytes(int crc, byte[] b, int off, int len)
*
- * As the method is private it is not possible to check the use of intrinsics
- * for it directly.
+ * As the methods are private it is not possible to check the use of intrinsics
+ * for them directly.
* The tests check that correct checksums are produced.
*/
public class Main {
- private static CRC32 crc32 = new CRC32();
-
public Main() {
}
- public static long TestInt(int value) {
- crc32.reset();
+ public static long CRC32Byte(int value) {
+ CRC32 crc32 = new CRC32();
crc32.update(value);
return crc32.getValue();
}
- public static long TestInt(int... values) {
- crc32.reset();
+ public static long CRC32BytesUsingUpdateInt(int... values) {
+ CRC32 crc32 = new CRC32();
for (int value : values) {
crc32.update(value);
}
@@ -50,82 +50,301 @@
}
}
- public static void main(String args[]) {
+ private static void assertEqual(boolean expected, boolean actual) {
+ if (expected != actual) {
+ throw new Error("Expected: " + expected + ", found: " + actual);
+ }
+ }
+
+ private static void TestCRC32Update() {
// public void update(int b)
//
// Tests for checksums of the byte 0x0
- assertEqual(0xD202EF8DL, TestInt(0x0));
- assertEqual(0xD202EF8DL, TestInt(0x0100));
- assertEqual(0xD202EF8DL, TestInt(0x010000));
- assertEqual(0xD202EF8DL, TestInt(0x01000000));
- assertEqual(0xD202EF8DL, TestInt(0xff00));
- assertEqual(0xD202EF8DL, TestInt(0xffff00));
- assertEqual(0xD202EF8DL, TestInt(0xffffff00));
- assertEqual(0xD202EF8DL, TestInt(0x1200));
- assertEqual(0xD202EF8DL, TestInt(0x123400));
- assertEqual(0xD202EF8DL, TestInt(0x12345600));
- assertEqual(0xD202EF8DL, TestInt(Integer.MIN_VALUE));
+ // Check that only the low eight bits of the argument are used.
+ assertEqual(0xD202EF8DL, CRC32Byte(0x0));
+ assertEqual(0xD202EF8DL, CRC32Byte(0x0100));
+ assertEqual(0xD202EF8DL, CRC32Byte(0x010000));
+ assertEqual(0xD202EF8DL, CRC32Byte(0x01000000));
+ assertEqual(0xD202EF8DL, CRC32Byte(0xff00));
+ assertEqual(0xD202EF8DL, CRC32Byte(0xffff00));
+ assertEqual(0xD202EF8DL, CRC32Byte(0xffffff00));
+ assertEqual(0xD202EF8DL, CRC32Byte(0x1200));
+ assertEqual(0xD202EF8DL, CRC32Byte(0x123400));
+ assertEqual(0xD202EF8DL, CRC32Byte(0x12345600));
+ assertEqual(0xD202EF8DL, CRC32Byte(Integer.MIN_VALUE));
// Tests for checksums of the byte 0x1
- assertEqual(0xA505DF1BL, TestInt(0x1));
- assertEqual(0xA505DF1BL, TestInt(0x0101));
- assertEqual(0xA505DF1BL, TestInt(0x010001));
- assertEqual(0xA505DF1BL, TestInt(0x01000001));
- assertEqual(0xA505DF1BL, TestInt(0xff01));
- assertEqual(0xA505DF1BL, TestInt(0xffff01));
- assertEqual(0xA505DF1BL, TestInt(0xffffff01));
- assertEqual(0xA505DF1BL, TestInt(0x1201));
- assertEqual(0xA505DF1BL, TestInt(0x123401));
- assertEqual(0xA505DF1BL, TestInt(0x12345601));
+ // Check that only the low eight bits of the argument are used.
+ assertEqual(0xA505DF1BL, CRC32Byte(0x1));
+ assertEqual(0xA505DF1BL, CRC32Byte(0x0101));
+ assertEqual(0xA505DF1BL, CRC32Byte(0x010001));
+ assertEqual(0xA505DF1BL, CRC32Byte(0x01000001));
+ assertEqual(0xA505DF1BL, CRC32Byte(0xff01));
+ assertEqual(0xA505DF1BL, CRC32Byte(0xffff01));
+ assertEqual(0xA505DF1BL, CRC32Byte(0xffffff01));
+ assertEqual(0xA505DF1BL, CRC32Byte(0x1201));
+ assertEqual(0xA505DF1BL, CRC32Byte(0x123401));
+ assertEqual(0xA505DF1BL, CRC32Byte(0x12345601));
// Tests for checksums of the byte 0x0f
- assertEqual(0x42BDF21CL, TestInt(0x0f));
- assertEqual(0x42BDF21CL, TestInt(0x010f));
- assertEqual(0x42BDF21CL, TestInt(0x01000f));
- assertEqual(0x42BDF21CL, TestInt(0x0100000f));
- assertEqual(0x42BDF21CL, TestInt(0xff0f));
- assertEqual(0x42BDF21CL, TestInt(0xffff0f));
- assertEqual(0x42BDF21CL, TestInt(0xffffff0f));
- assertEqual(0x42BDF21CL, TestInt(0x120f));
- assertEqual(0x42BDF21CL, TestInt(0x12340f));
- assertEqual(0x42BDF21CL, TestInt(0x1234560f));
+ // Check that only the low eight bits of the argument are used.
+ assertEqual(0x42BDF21CL, CRC32Byte(0x0f));
+ assertEqual(0x42BDF21CL, CRC32Byte(0x010f));
+ assertEqual(0x42BDF21CL, CRC32Byte(0x01000f));
+ assertEqual(0x42BDF21CL, CRC32Byte(0x0100000f));
+ assertEqual(0x42BDF21CL, CRC32Byte(0xff0f));
+ assertEqual(0x42BDF21CL, CRC32Byte(0xffff0f));
+ assertEqual(0x42BDF21CL, CRC32Byte(0xffffff0f));
+ assertEqual(0x42BDF21CL, CRC32Byte(0x120f));
+ assertEqual(0x42BDF21CL, CRC32Byte(0x12340f));
+ assertEqual(0x42BDF21CL, CRC32Byte(0x1234560f));
// Tests for checksums of the byte 0xff
- assertEqual(0xFF000000L, TestInt(0x00ff));
- assertEqual(0xFF000000L, TestInt(0x01ff));
- assertEqual(0xFF000000L, TestInt(0x0100ff));
- assertEqual(0xFF000000L, TestInt(0x010000ff));
- assertEqual(0xFF000000L, TestInt(0x0000ffff));
- assertEqual(0xFF000000L, TestInt(0x00ffffff));
- assertEqual(0xFF000000L, TestInt(0xffffffff));
- assertEqual(0xFF000000L, TestInt(0x12ff));
- assertEqual(0xFF000000L, TestInt(0x1234ff));
- assertEqual(0xFF000000L, TestInt(0x123456ff));
- assertEqual(0xFF000000L, TestInt(Integer.MAX_VALUE));
+ // Check that only the low eight bits of the argument are used.
+ assertEqual(0xFF000000L, CRC32Byte(0x00ff));
+ assertEqual(0xFF000000L, CRC32Byte(0x01ff));
+ assertEqual(0xFF000000L, CRC32Byte(0x0100ff));
+ assertEqual(0xFF000000L, CRC32Byte(0x010000ff));
+ assertEqual(0xFF000000L, CRC32Byte(0x0000ffff));
+ assertEqual(0xFF000000L, CRC32Byte(0x00ffffff));
+ assertEqual(0xFF000000L, CRC32Byte(0xffffffff));
+ assertEqual(0xFF000000L, CRC32Byte(0x12ff));
+ assertEqual(0xFF000000L, CRC32Byte(0x1234ff));
+ assertEqual(0xFF000000L, CRC32Byte(0x123456ff));
+ assertEqual(0xFF000000L, CRC32Byte(Integer.MAX_VALUE));
// Tests for sequences
- assertEqual(0xFF41D912L, TestInt(0, 0, 0));
- assertEqual(0xFF41D912L, TestInt(0x0100, 0x010000, 0x01000000));
- assertEqual(0xFF41D912L, TestInt(0xff00, 0xffff00, 0xffffff00));
- assertEqual(0xFF41D912L, TestInt(0x1200, 0x123400, 0x12345600));
+ // Check that only the low eight bits of the values are used.
+ assertEqual(0xFF41D912L, CRC32BytesUsingUpdateInt(0, 0, 0));
+ assertEqual(0xFF41D912L,
+ CRC32BytesUsingUpdateInt(0x0100, 0x010000, 0x01000000));
+ assertEqual(0xFF41D912L,
+ CRC32BytesUsingUpdateInt(0xff00, 0xffff00, 0xffffff00));
+ assertEqual(0xFF41D912L,
+ CRC32BytesUsingUpdateInt(0x1200, 0x123400, 0x12345600));
- assertEqual(0x909FB2F2L, TestInt(1, 1, 1));
- assertEqual(0x909FB2F2L, TestInt(0x0101, 0x010001, 0x01000001));
- assertEqual(0x909FB2F2L, TestInt(0xff01, 0xffff01, 0xffffff01));
- assertEqual(0x909FB2F2L, TestInt(0x1201, 0x123401, 0x12345601));
+ assertEqual(0x909FB2F2L, CRC32BytesUsingUpdateInt(1, 1, 1));
+ assertEqual(0x909FB2F2L,
+ CRC32BytesUsingUpdateInt(0x0101, 0x010001, 0x01000001));
+ assertEqual(0x909FB2F2L,
+ CRC32BytesUsingUpdateInt(0xff01, 0xffff01, 0xffffff01));
+ assertEqual(0x909FB2F2L,
+ CRC32BytesUsingUpdateInt(0x1201, 0x123401, 0x12345601));
- assertEqual(0xE33A9F71L, TestInt(0x0f, 0x0f, 0x0f));
- assertEqual(0xE33A9F71L, TestInt(0x010f, 0x01000f, 0x0100000f));
- assertEqual(0xE33A9F71L, TestInt(0xff0f, 0xffff0f, 0xffffff0f));
- assertEqual(0xE33A9F71L, TestInt(0x120f, 0x12340f, 0x1234560f));
+ assertEqual(0xE33A9F71L, CRC32BytesUsingUpdateInt(0x0f, 0x0f, 0x0f));
+ assertEqual(0xE33A9F71L,
+ CRC32BytesUsingUpdateInt(0x010f, 0x01000f, 0x0100000f));
+ assertEqual(0xE33A9F71L,
+ CRC32BytesUsingUpdateInt(0xff0f, 0xffff0f, 0xffffff0f));
+ assertEqual(0xE33A9F71L,
+ CRC32BytesUsingUpdateInt(0x120f, 0x12340f, 0x1234560f));
- assertEqual(0xFFFFFF00L, TestInt(0x0ff, 0x0ff, 0x0ff));
- assertEqual(0xFFFFFF00L, TestInt(0x01ff, 0x0100ff, 0x010000ff));
- assertEqual(0xFFFFFF00L, TestInt(0x00ffff, 0x00ffffff, 0xffffffff));
- assertEqual(0xFFFFFF00L, TestInt(0x12ff, 0x1234ff, 0x123456ff));
+ assertEqual(0xFFFFFF00L, CRC32BytesUsingUpdateInt(0x0ff, 0x0ff, 0x0ff));
+ assertEqual(0xFFFFFF00L,
+ CRC32BytesUsingUpdateInt(0x01ff, 0x0100ff, 0x010000ff));
+ assertEqual(0xFFFFFF00L,
+ CRC32BytesUsingUpdateInt(0x00ffff, 0x00ffffff, 0xffffffff));
+ assertEqual(0xFFFFFF00L,
+ CRC32BytesUsingUpdateInt(0x12ff, 0x1234ff, 0x123456ff));
- assertEqual(0xB6CC4292L, TestInt(0x01, 0x02));
+ assertEqual(0xB6CC4292L, CRC32BytesUsingUpdateInt(0x01, 0x02));
- assertEqual(0xB2DE047CL, TestInt(0x0, -1, Integer.MIN_VALUE, Integer.MAX_VALUE));
+ assertEqual(0xB2DE047CL,
+ CRC32BytesUsingUpdateInt(0x0, -1, Integer.MIN_VALUE, Integer.MAX_VALUE));
+ }
+
+ private static long CRC32ByteArray(byte[] bytes, int off, int len) {
+ CRC32 crc32 = new CRC32();
+ crc32.update(bytes, off, len);
+ return crc32.getValue();
+ }
+
+ // This is used to test we generate correct code for constant offsets.
+ // In this case the offset is 0.
+ private static long CRC32ByteArray(byte[] bytes) {
+ CRC32 crc32 = new CRC32();
+ crc32.update(bytes);
+ return crc32.getValue();
+ }
+
+ private static long CRC32ByteAndByteArray(int value, byte[] bytes) {
+ CRC32 crc32 = new CRC32();
+ crc32.update(value);
+ crc32.update(bytes);
+ return crc32.getValue();
+ }
+
+ private static long CRC32ByteArrayAndByte(byte[] bytes, int value) {
+ CRC32 crc32 = new CRC32();
+ crc32.update(bytes);
+ crc32.update(value);
+ return crc32.getValue();
+ }
+
+ private static boolean CRC32ByteArrayThrowsAIOOBE(byte[] bytes, int off, int len) {
+ try {
+ CRC32 crc32 = new CRC32();
+ crc32.update(bytes, off, len);
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean CRC32ByteArrayThrowsNPE() {
+ try {
+ CRC32 crc32 = new CRC32();
+ crc32.update(null, 0, 0);
+ return false;
+ } catch (NullPointerException e) {}
+
+ try {
+ CRC32 crc32 = new CRC32();
+ crc32.update(null, 1, 2);
+ return false;
+ } catch (NullPointerException e) {}
+
+ try {
+ CRC32 crc32 = new CRC32();
+ crc32.update((byte[])null);
+ return false;
+ } catch (NullPointerException e) {}
+
+ return true;
+ }
+
+ private static long CRC32BytesUsingUpdateInt(byte[] bytes, int off, int len) {
+ CRC32 crc32 = new CRC32();
+ while (len-- > 0) {
+ crc32.update(bytes[off++]);
+ }
+ return crc32.getValue();
+ }
+
+ private static void TestCRC32UpdateBytes() {
+ assertEqual(0L, CRC32ByteArray(new byte[] {}));
+ assertEqual(0L, CRC32ByteArray(new byte[] {}, 0, 0));
+ assertEqual(0L, CRC32ByteArray(new byte[] {0}, 0, 0));
+ assertEqual(0L, CRC32ByteArray(new byte[] {0}, 1, 0));
+ assertEqual(0L, CRC32ByteArray(new byte[] {0, 0}, 1, 0));
+
+ assertEqual(true, CRC32ByteArrayThrowsNPE());
+ assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, -1, 0));
+ assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {0}, -1, 1));
+ assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {0}, 0, -1));
+ assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 0, -1));
+ assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 1, 0));
+ assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, -1, 1));
+ assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 1, -1));
+ assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 0, 1));
+ assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 0, 10));
+ assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {0}, 0, 10));
+ assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {}, 10, 10));
+ assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {0, 0, 0, 0}, 2, 3));
+ assertEqual(true, CRC32ByteArrayThrowsAIOOBE(new byte[] {0, 0, 0, 0}, 3, 2));
+
+ assertEqual(CRC32Byte(0), CRC32ByteArray(new byte[] {0}));
+ assertEqual(CRC32Byte(0), CRC32ByteArray(new byte[] {0}, 0, 1));
+ assertEqual(CRC32Byte(1), CRC32ByteArray(new byte[] {1}));
+ assertEqual(CRC32Byte(1), CRC32ByteArray(new byte[] {1}, 0, 1));
+ assertEqual(CRC32Byte(0x0f), CRC32ByteArray(new byte[] {0x0f}));
+ assertEqual(CRC32Byte(0x0f), CRC32ByteArray(new byte[] {0x0f}, 0, 1));
+ assertEqual(CRC32Byte(0xff), CRC32ByteArray(new byte[] {-1}));
+ assertEqual(CRC32Byte(0xff), CRC32ByteArray(new byte[] {-1}, 0, 1));
+ assertEqual(CRC32BytesUsingUpdateInt(0, 0, 0),
+ CRC32ByteArray(new byte[] {0, 0, 0}));
+ assertEqual(CRC32BytesUsingUpdateInt(0, 0, 0),
+ CRC32ByteArray(new byte[] {0, 0, 0}, 0, 3));
+ assertEqual(CRC32BytesUsingUpdateInt(1, 1, 1),
+ CRC32ByteArray(new byte[] {1, 1, 1}));
+ assertEqual(CRC32BytesUsingUpdateInt(1, 1, 1),
+ CRC32ByteArray(new byte[] {1, 1, 1}, 0, 3));
+ assertEqual(CRC32BytesUsingUpdateInt(0x0f, 0x0f, 0x0f),
+ CRC32ByteArray(new byte[] {0x0f, 0x0f, 0x0f}));
+ assertEqual(CRC32BytesUsingUpdateInt(0x0f, 0x0f, 0x0f),
+ CRC32ByteArray(new byte[] {0x0f, 0x0f, 0x0f}, 0, 3));
+ assertEqual(CRC32BytesUsingUpdateInt(0xff, 0xff, 0xff),
+ CRC32ByteArray(new byte[] {-1, -1, -1}));
+ assertEqual(CRC32BytesUsingUpdateInt(0xff, 0xff, 0xff),
+ CRC32ByteArray(new byte[] {-1, -1, -1}, 0, 3));
+ assertEqual(CRC32BytesUsingUpdateInt(1, 2),
+ CRC32ByteArray(new byte[] {1, 2}));
+ assertEqual(CRC32BytesUsingUpdateInt(1, 2),
+ CRC32ByteArray(new byte[] {1, 2}, 0, 2));
+ assertEqual(
+ CRC32BytesUsingUpdateInt(0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE),
+ CRC32ByteArray(new byte[] {0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE}));
+ assertEqual(
+ CRC32BytesUsingUpdateInt(0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE),
+ CRC32ByteArray(new byte[] {0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE}, 0, 4));
+
+ assertEqual(CRC32BytesUsingUpdateInt(0, 0, 0),
+ CRC32ByteAndByteArray(0, new byte[] {0, 0}));
+ assertEqual(CRC32BytesUsingUpdateInt(1, 1, 1),
+ CRC32ByteAndByteArray(1, new byte[] {1, 1}));
+ assertEqual(CRC32BytesUsingUpdateInt(0x0f, 0x0f, 0x0f),
+ CRC32ByteAndByteArray(0x0f, new byte[] {0x0f, 0x0f}));
+ assertEqual(CRC32BytesUsingUpdateInt(0xff, 0xff, 0xff),
+ CRC32ByteAndByteArray(-1, new byte[] {-1, -1}));
+ assertEqual(CRC32BytesUsingUpdateInt(1, 2, 3),
+ CRC32ByteAndByteArray(1, new byte[] {2, 3}));
+ assertEqual(
+ CRC32BytesUsingUpdateInt(0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE),
+ CRC32ByteAndByteArray(0, new byte[] {-1, Byte.MIN_VALUE, Byte.MAX_VALUE}));
+
+ assertEqual(CRC32BytesUsingUpdateInt(0, 0, 0),
+ CRC32ByteArrayAndByte(new byte[] {0, 0}, 0));
+ assertEqual(CRC32BytesUsingUpdateInt(1, 1, 1),
+ CRC32ByteArrayAndByte(new byte[] {1, 1}, 1));
+ assertEqual(CRC32BytesUsingUpdateInt(0x0f, 0x0f, 0x0f),
+ CRC32ByteArrayAndByte(new byte[] {0x0f, 0x0f}, 0x0f));
+ assertEqual(CRC32BytesUsingUpdateInt(0xff, 0xff, 0xff),
+ CRC32ByteArrayAndByte(new byte[] {-1, -1}, -1));
+ assertEqual(CRC32BytesUsingUpdateInt(1, 2, 3),
+ CRC32ByteArrayAndByte(new byte[] {1, 2}, 3));
+ assertEqual(
+ CRC32BytesUsingUpdateInt(0, -1, Byte.MIN_VALUE, Byte.MAX_VALUE),
+ CRC32ByteArrayAndByte(new byte[] {0, -1, Byte.MIN_VALUE}, Byte.MAX_VALUE));
+
+ byte[] bytes = new byte[128 * 1024];
+ Random rnd = new Random(0);
+ rnd.nextBytes(bytes);
+
+ assertEqual(CRC32BytesUsingUpdateInt(bytes, 0, bytes.length),
+ CRC32ByteArray(bytes));
+ assertEqual(CRC32BytesUsingUpdateInt(bytes, 0, 8 * 1024),
+ CRC32ByteArray(bytes, 0, 8 * 1024));
+
+ int off = rnd.nextInt(bytes.length / 2);
+ for (int len = 0; len <= 16; ++len) {
+ assertEqual(CRC32BytesUsingUpdateInt(bytes, off, len),
+ CRC32ByteArray(bytes, off, len));
+ }
+
+ // Check there are no issues with unaligned accesses.
+ for (int o = 1; o < 8; ++o) {
+ for (int l = 0; l <= 16; ++l) {
+ assertEqual(CRC32BytesUsingUpdateInt(bytes, o, l),
+ CRC32ByteArray(bytes, o, l));
+ }
+ }
+
+ int len = bytes.length / 2;
+ assertEqual(CRC32BytesUsingUpdateInt(bytes, 0, len - 1),
+ CRC32ByteArray(bytes, 0, len - 1));
+ assertEqual(CRC32BytesUsingUpdateInt(bytes, 0, len),
+ CRC32ByteArray(bytes, 0, len));
+ assertEqual(CRC32BytesUsingUpdateInt(bytes, 0, len + 1),
+ CRC32ByteArray(bytes, 0, len + 1));
+
+ len = rnd.nextInt(bytes.length + 1);
+ off = rnd.nextInt(bytes.length - len);
+ assertEqual(CRC32BytesUsingUpdateInt(bytes, off, len),
+ CRC32ByteArray(bytes, off, len));
+ }
+
+ public static void main(String args[]) {
+ TestCRC32Update();
+ TestCRC32UpdateBytes();
}
}
diff --git a/test/Android.bp b/test/Android.bp
index d85e2a6..57fcd86 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -322,6 +322,7 @@
"1940-ddms-ext/ddm_ext.cc",
"1944-sudden-exit/sudden_exit.cc",
// "1952-pop-frame-jit/pop_frame.cc",
+ "1959-redefine-object-instrument/fake_redef_object.cc",
],
static_libs: [
"libz",
diff --git a/tools/class2greylist/src/com/android/class2greylist/AnnotatedClassContext.java b/tools/class2greylist/src/com/android/class2greylist/AnnotatedClassContext.java
new file mode 100644
index 0000000..1dd74dd
--- /dev/null
+++ b/tools/class2greylist/src/com/android/class2greylist/AnnotatedClassContext.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+package com.android.class2greylist;
+
+import java.util.Formatter;
+import java.util.Locale;
+import org.apache.bcel.Const;
+import org.apache.bcel.classfile.FieldOrMethod;
+import org.apache.bcel.classfile.JavaClass;
+
+/**
+ * Encapsulates context for a single annotation on a class.
+ */
+public class AnnotatedClassContext extends AnnotationContext {
+
+ public final String signatureFormatString;
+
+ public AnnotatedClassContext(
+ Status status,
+ JavaClass definingClass,
+ String signatureFormatString) {
+ super(status, definingClass);
+ this.signatureFormatString = signatureFormatString;
+ }
+
+ @Override
+ public String getMemberDescriptor() {
+ return String.format(Locale.US, signatureFormatString, getClassDescriptor());
+ }
+
+ @Override
+ public void reportError(String message, Object... args) {
+ Formatter error = new Formatter();
+ error
+ .format("%s: %s: ", definingClass.getSourceFileName(), definingClass.getClassName())
+ .format(Locale.US, message, args);
+
+ status.error(error.toString());
+ }
+
+}
diff --git a/tools/class2greylist/src/com/android/class2greylist/AnnotatedMemberContext.java b/tools/class2greylist/src/com/android/class2greylist/AnnotatedMemberContext.java
new file mode 100644
index 0000000..4802788
--- /dev/null
+++ b/tools/class2greylist/src/com/android/class2greylist/AnnotatedMemberContext.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+package com.android.class2greylist;
+
+import java.util.Formatter;
+import org.apache.bcel.Const;
+import org.apache.bcel.classfile.FieldOrMethod;
+import org.apache.bcel.classfile.JavaClass;
+
+import java.util.Locale;
+
+/**
+ * Encapsulates context for a single annotation on a class member.
+ */
+public class AnnotatedMemberContext extends AnnotationContext {
+
+ public final FieldOrMethod member;
+ public final String signatureFormatString;
+
+ public AnnotatedMemberContext(
+ Status status,
+ JavaClass definingClass,
+ FieldOrMethod member,
+ String signatureFormatString) {
+ super(status, definingClass);
+ this.member = member;
+ this.signatureFormatString = signatureFormatString;
+ }
+
+ @Override
+ public String getMemberDescriptor() {
+ return String.format(Locale.US, signatureFormatString,
+ getClassDescriptor(), member.getName(), member.getSignature());
+ }
+
+ @Override
+ public void reportError(String message, Object... args) {
+ Formatter error = new Formatter();
+ error
+ .format("%s: %s.%s: ", definingClass.getSourceFileName(),
+ definingClass.getClassName(), member.getName())
+ .format(Locale.US, message, args);
+
+ status.error(error.toString());
+ }
+}
diff --git a/tools/class2greylist/src/com/android/class2greylist/AnnotationContext.java b/tools/class2greylist/src/com/android/class2greylist/AnnotationContext.java
index eb54a33..73b74a9 100644
--- a/tools/class2greylist/src/com/android/class2greylist/AnnotationContext.java
+++ b/tools/class2greylist/src/com/android/class2greylist/AnnotationContext.java
@@ -1,66 +1,51 @@
+/*
+ * 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.
+ */
package com.android.class2greylist;
import org.apache.bcel.Const;
-import org.apache.bcel.classfile.FieldOrMethod;
import org.apache.bcel.classfile.JavaClass;
-import java.util.Locale;
-
/**
- * Encapsulates context for a single annotation on a class member.
*/
-public class AnnotationContext {
+public abstract class AnnotationContext {
- public final Status status;
- public final FieldOrMethod member;
- public final JavaClass definingClass;
- public final String signatureFormatString;
+ public final Status status;
+ public final JavaClass definingClass;
- public AnnotationContext(
- Status status,
- FieldOrMethod member,
- JavaClass definingClass,
- String signatureFormatString) {
- this.status = status;
- this.member = member;
- this.definingClass = definingClass;
- this.signatureFormatString = signatureFormatString;
- }
+ public AnnotationContext(Status status, JavaClass definingClass) {
+ this.status = status;
+ this.definingClass = definingClass;
+ }
- /**
- * @return the full descriptor of enclosing class.
- */
- public String getClassDescriptor() {
- // JavaClass.getName() returns the Java-style name (with . not /), so we must fetch
- // the original class name from the constant pool.
- return definingClass.getConstantPool().getConstantString(
- definingClass.getClassNameIndex(), Const.CONSTANT_Class);
- }
+ public String getClassDescriptor() {
+ // JavaClass.getName() returns the Java-style name (with . not /), so we must fetch
+ // the original class name from the constant pool.
+ return definingClass.getConstantPool().getConstantString(
+ definingClass.getClassNameIndex(), Const.CONSTANT_Class);
+ }
- /**
- * @return the full descriptor of this member, in the format expected in
- * the greylist.
- */
- public String getMemberDescriptor() {
- return String.format(Locale.US, signatureFormatString,
- getClassDescriptor(), member.getName(), member.getSignature());
- }
+ /**
+ * @return the full descriptor of this member, in the format expected in
+ * the greylist.
+ */
+ public abstract String getMemberDescriptor();
- /**
- * Report an error in this context. The final error message will include
- * the class and member names, and the source file name.
- */
- public void reportError(String message, Object... args) {
- StringBuilder error = new StringBuilder();
- error.append(definingClass.getSourceFileName())
- .append(": ")
- .append(definingClass.getClassName())
- .append(".")
- .append(member.getName())
- .append(": ")
- .append(String.format(Locale.US, message, args));
-
- status.error(error.toString());
- }
-
+ /**
+ * Report an error in this context. The final error message will include
+ * the class and member names, and the source file name.
+ */
+ public abstract void reportError(String message, Object... args);
}
diff --git a/tools/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java b/tools/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java
index 57ccbdc..3a58cf1 100644
--- a/tools/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java
+++ b/tools/class2greylist/src/com/android/class2greylist/AnnotationVisitor.java
@@ -55,6 +55,10 @@
public void visit() {
mStatus.debug("Visit class %s", mClass.getClassName());
+ AnnotationContext context = new AnnotatedClassContext(mStatus, mClass, "L%s;");
+ AnnotationEntry[] annotationEntries = mClass.getAnnotationEntries();
+ handleAnnotations(context, annotationEntries);
+
mDescendingVisitor.visit();
}
@@ -70,9 +74,15 @@
private void visitMember(FieldOrMethod member, String signatureFormatString) {
mStatus.debug("Visit member %s : %s", member.getName(), member.getSignature());
- AnnotationContext context = new AnnotationContext(mStatus, member,
- (JavaClass) mDescendingVisitor.predecessor(), signatureFormatString);
- for (AnnotationEntry a : member.getAnnotationEntries()) {
+ AnnotationContext context = new AnnotatedMemberContext(mStatus,
+ (JavaClass) mDescendingVisitor.predecessor(), member,
+ signatureFormatString);
+ AnnotationEntry[] annotationEntries = member.getAnnotationEntries();
+ handleAnnotations(context, annotationEntries);
+ }
+
+ private void handleAnnotations(AnnotationContext context, AnnotationEntry[] annotationEntries) {
+ for (AnnotationEntry a : annotationEntries) {
if (mAnnotationHandlers.containsKey(a.getAnnotationType())) {
mStatus.debug("Member has annotation %s for which we have a handler",
a.getAnnotationType());
diff --git a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
index 6305185..1da848b 100644
--- a/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
+++ b/tools/class2greylist/src/com/android/class2greylist/Class2Greylist.java
@@ -17,7 +17,6 @@
package com.android.class2greylist;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.ImmutableSet;
@@ -35,16 +34,10 @@
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
import java.util.Map;
-import java.util.HashMap;
import java.util.Set;
-import java.util.function.Predicate;
/**
* Build time tool for extracting a list of members from jar files that have the @UsedByApps
@@ -54,8 +47,8 @@
private static final Set<String> GREYLIST_ANNOTATIONS =
ImmutableSet.of(
- "Landroid/annotation/UnsupportedAppUsage;",
- "Ldalvik/annotation/compat/UnsupportedAppUsage;");
+ "android.annotation.UnsupportedAppUsage",
+ "dalvik.annotation.compat.UnsupportedAppUsage");
private static final Set<String> WHITELIST_ANNOTATIONS = ImmutableSet.of();
public static final String FLAG_WHITELIST = "whitelist";
@@ -192,13 +185,41 @@
UnsupportedAppUsageAnnotationHandler greylistAnnotationHandler =
new UnsupportedAppUsageAnnotationHandler(
mStatus, mOutput, mPublicApis, TARGET_SDK_TO_LIST_MAP);
- GREYLIST_ANNOTATIONS.forEach(a -> builder.put(a, greylistAnnotationHandler));
+ GREYLIST_ANNOTATIONS
+ .forEach(a -> addRepeatedAnnotationHandlers(
+ builder,
+ classNameToSignature(a),
+ classNameToSignature(a + "$Container"),
+ greylistAnnotationHandler));
+
+ CovariantReturnTypeHandler covariantReturnTypeHandler = new CovariantReturnTypeHandler(
+ mOutput, mPublicApis, FLAG_WHITELIST);
+
+ return addRepeatedAnnotationHandlers(builder, CovariantReturnTypeHandler.ANNOTATION_NAME,
+ CovariantReturnTypeHandler.REPEATED_ANNOTATION_NAME, covariantReturnTypeHandler)
+ .build();
+ }
+
+ private String classNameToSignature(String a) {
+ return "L" + a.replace('.', '/') + ";";
+ }
+
+ /**
+ * Add a handler for an annotation as well as an handler for the container annotation that is
+ * used when the annotation is repeated.
+ *
+ * @param builder the builder for the map to which the handlers will be added.
+ * @param annotationName the name of the annotation.
+ * @param containerAnnotationName the name of the annotation container.
+ * @param handler the handler for the annotation.
+ */
+ private static Builder<String, AnnotationHandler> addRepeatedAnnotationHandlers(
+ Builder<String, AnnotationHandler> builder,
+ String annotationName, String containerAnnotationName,
+ AnnotationHandler handler) {
return builder
- .put(CovariantReturnTypeHandler.ANNOTATION_NAME,
- new CovariantReturnTypeHandler(mOutput, mPublicApis, FLAG_WHITELIST))
- .put(CovariantReturnTypeMultiHandler.ANNOTATION_NAME,
- new CovariantReturnTypeMultiHandler(mOutput, mPublicApis, FLAG_WHITELIST))
- .build();
+ .put(annotationName, handler)
+ .put(containerAnnotationName, new RepeatedAnnotationHandler(annotationName, handler));
}
private void main() throws IOException {
diff --git a/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeHandler.java b/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeHandler.java
index b8de7e9..eb2e42d 100644
--- a/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeHandler.java
+++ b/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeHandler.java
@@ -4,15 +4,10 @@
import com.google.common.collect.ImmutableSet;
import org.apache.bcel.classfile.AnnotationEntry;
-import org.apache.bcel.classfile.Constant;
-import org.apache.bcel.classfile.ConstantPool;
-import org.apache.bcel.classfile.ElementValue;
import org.apache.bcel.classfile.ElementValuePair;
import org.apache.bcel.classfile.Method;
-import java.util.List;
import java.util.Locale;
-import java.util.Map;
import java.util.Set;
/**
@@ -30,6 +25,8 @@
private static final String SHORT_NAME = "CovariantReturnType";
public static final String ANNOTATION_NAME = "Ldalvik/annotation/codegen/CovariantReturnType;";
+ public static final String REPEATED_ANNOTATION_NAME =
+ "Ldalvik/annotation/codegen/CovariantReturnType$CovariantReturnTypes;";
private static final String RETURN_TYPE = "returnType";
@@ -46,6 +43,13 @@
@Override
public void handleAnnotation(AnnotationEntry annotation, AnnotationContext context) {
+ if (context instanceof AnnotatedClassContext) {
+ return;
+ }
+ handleAnnotation(annotation, (AnnotatedMemberContext) context);
+ }
+
+ private void handleAnnotation(AnnotationEntry annotation, AnnotatedMemberContext context) {
// Verify that the annotation has been applied to what we expect, and
// has the right form. Note, this should not strictly be necessary, as
// the annotation has a target of just 'method' and the property
diff --git a/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeMultiHandler.java b/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeMultiHandler.java
deleted file mode 100644
index f2bc525..0000000
--- a/tools/class2greylist/src/com/android/class2greylist/CovariantReturnTypeMultiHandler.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package com.android.class2greylist;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-
-import org.apache.bcel.classfile.AnnotationElementValue;
-import org.apache.bcel.classfile.AnnotationEntry;
-import org.apache.bcel.classfile.ArrayElementValue;
-import org.apache.bcel.classfile.ElementValue;
-import org.apache.bcel.classfile.ElementValuePair;
-
-import java.util.Set;
-
-/**
- * Handles {@code CovariantReturnType$CovariantReturnTypes} annotations, which
- * are generated by the compiler when multiple {@code CovariantReturnType}
- * annotations appear on a single method.
- *
- * <p>The enclosed annotations are passed to {@link CovariantReturnTypeHandler}.
- */
-public class CovariantReturnTypeMultiHandler extends AnnotationHandler {
-
- public static final String ANNOTATION_NAME =
- "Ldalvik/annotation/codegen/CovariantReturnType$CovariantReturnTypes;";
-
- private static final String VALUE = "value";
-
- private final CovariantReturnTypeHandler mWrappedHandler;
- private final String mInnerAnnotationName;
-
- public CovariantReturnTypeMultiHandler(AnnotationConsumer consumer, Set<String> publicApis,
- String hiddenapiFlag) {
- this(consumer, publicApis, hiddenapiFlag, CovariantReturnTypeHandler.ANNOTATION_NAME);
- }
-
- @VisibleForTesting
- public CovariantReturnTypeMultiHandler(AnnotationConsumer consumer, Set<String> publicApis,
- String hiddenapiFlag, String innerAnnotationName) {
- mWrappedHandler = new CovariantReturnTypeHandler(consumer, publicApis, hiddenapiFlag);
- mInnerAnnotationName = innerAnnotationName;
- }
-
- @Override
- public void handleAnnotation(AnnotationEntry annotation, AnnotationContext context) {
- // Verify that the annotation has the form we expect
- ElementValuePair value = findValue(annotation);
- if (value == null) {
- context.reportError("No value found on CovariantReturnType$CovariantReturnTypes");
- return;
- }
- Preconditions.checkArgument(value.getValue() instanceof ArrayElementValue);
- ArrayElementValue array = (ArrayElementValue) value.getValue();
-
- // call wrapped handler on each enclosed annotation:
- for (ElementValue v : array.getElementValuesArray()) {
- Preconditions.checkArgument(v instanceof AnnotationElementValue);
- AnnotationElementValue aev = (AnnotationElementValue) v;
- Preconditions.checkArgument(
- aev.getAnnotationEntry().getAnnotationType().equals(mInnerAnnotationName));
- mWrappedHandler.handleAnnotation(aev.getAnnotationEntry(), context);
- }
- }
-
- private ElementValuePair findValue(AnnotationEntry a) {
- for (ElementValuePair property : a.getElementValuePairs()) {
- if (property.getNameString().equals(VALUE)) {
- return property;
- }
- }
- // not found
- return null;
- }
-}
diff --git a/tools/class2greylist/src/com/android/class2greylist/MemberDumpingVisitor.java b/tools/class2greylist/src/com/android/class2greylist/MemberDumpingVisitor.java
index 6677a3f..89c8bd7 100644
--- a/tools/class2greylist/src/com/android/class2greylist/MemberDumpingVisitor.java
+++ b/tools/class2greylist/src/com/android/class2greylist/MemberDumpingVisitor.java
@@ -40,8 +40,9 @@
}
private void visitMember(FieldOrMethod member, String signatureFormatString) {
- AnnotationContext context = new AnnotationContext(mStatus, member,
- (JavaClass) mDescendingVisitor.predecessor(), signatureFormatString);
+ AnnotationContext context = new AnnotatedMemberContext(mStatus,
+ (JavaClass) mDescendingVisitor.predecessor(), member,
+ signatureFormatString);
System.out.println(context.getMemberDescriptor());
}
}
diff --git a/tools/class2greylist/src/com/android/class2greylist/RepeatedAnnotationHandler.java b/tools/class2greylist/src/com/android/class2greylist/RepeatedAnnotationHandler.java
new file mode 100644
index 0000000..61949e3
--- /dev/null
+++ b/tools/class2greylist/src/com/android/class2greylist/RepeatedAnnotationHandler.java
@@ -0,0 +1,57 @@
+package com.android.class2greylist;
+
+import com.google.common.base.Preconditions;
+import org.apache.bcel.classfile.AnnotationElementValue;
+import org.apache.bcel.classfile.AnnotationEntry;
+import org.apache.bcel.classfile.ArrayElementValue;
+import org.apache.bcel.classfile.ElementValue;
+import org.apache.bcel.classfile.ElementValuePair;
+
+/**
+ * Handles a repeated annotation container.
+ *
+ * <p>The enclosed annotations are passed to the {@link #mWrappedHandler}.
+ */
+public class RepeatedAnnotationHandler extends AnnotationHandler {
+
+ private static final String VALUE = "value";
+
+ private final AnnotationHandler mWrappedHandler;
+ private final String mInnerAnnotationName;
+
+ RepeatedAnnotationHandler(String innerAnnotationName, AnnotationHandler wrappedHandler) {
+ mWrappedHandler = wrappedHandler;
+ mInnerAnnotationName = innerAnnotationName;
+ }
+
+ @Override
+ public void handleAnnotation(AnnotationEntry annotation, AnnotationContext context) {
+ // Verify that the annotation has the form we expect
+ ElementValuePair value = findValue(annotation);
+ if (value == null) {
+ context.reportError("No value found on %s", annotation.getAnnotationType());
+ return;
+ }
+ Preconditions.checkArgument(value.getValue() instanceof ArrayElementValue);
+ ArrayElementValue array = (ArrayElementValue) value.getValue();
+
+ // call wrapped handler on each enclosed annotation:
+ for (ElementValue v : array.getElementValuesArray()) {
+ Preconditions.checkArgument(v instanceof AnnotationElementValue);
+ AnnotationElementValue aev = (AnnotationElementValue) v;
+ Preconditions.checkArgument(
+ aev.getAnnotationEntry().getAnnotationType().equals(mInnerAnnotationName));
+ mWrappedHandler.handleAnnotation(aev.getAnnotationEntry(), context);
+ }
+ }
+
+ private ElementValuePair findValue(AnnotationEntry a) {
+ for (ElementValuePair property : a.getElementValuePairs()) {
+ if (property.getNameString().equals(VALUE)) {
+ return property;
+ }
+ }
+ // not found
+ return null;
+ }
+}
diff --git a/tools/class2greylist/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandler.java b/tools/class2greylist/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandler.java
index d1f3e77..b45e1b3 100644
--- a/tools/class2greylist/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandler.java
+++ b/tools/class2greylist/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandler.java
@@ -1,7 +1,6 @@
package com.android.class2greylist;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
import org.apache.bcel.Const;
@@ -20,11 +19,11 @@
* Processes {@code UnsupportedAppUsage} annotations to generate greylist
* entries.
*
- * Any annotations with a {@link #EXPECTED_SIGNATURE} property will have their
+ * Any annotations with a {@link #EXPECTED_SIGNATURE_PROPERTY} property will have their
* generated signature verified against this, and an error will be reported if
* it does not match. Exclusions are made for bridge methods.
*
- * Any {@link #MAX_TARGET_SDK} properties will be validated against the given
+ * Any {@link #MAX_TARGET_SDK_PROPERTY} properties will be validated against the given
* set of valid values, then passed through to the greylist consumer.
*/
public class UnsupportedAppUsageAnnotationHandler extends AnnotationHandler {
@@ -32,6 +31,7 @@
// properties of greylist annotations:
private static final String EXPECTED_SIGNATURE_PROPERTY = "expectedSignature";
private static final String MAX_TARGET_SDK_PROPERTY = "maxTargetSdk";
+ private static final String IMPLICIT_MEMBER_PROPERTY = "implicitMember";
private final Status mStatus;
private final Predicate<ClassMember> mClassMemberFilter;
@@ -80,16 +80,20 @@
@Override
public void handleAnnotation(AnnotationEntry annotation, AnnotationContext context) {
- FieldOrMethod member = context.member;
-
- boolean isBridgeMethod = (member instanceof Method) &&
+ boolean isBridgeMethod = false;
+ if (context instanceof AnnotatedMemberContext) {
+ AnnotatedMemberContext memberContext = (AnnotatedMemberContext) context;
+ FieldOrMethod member = memberContext.member;
+ isBridgeMethod = (member instanceof Method) &&
(member.getAccessFlags() & Const.ACC_BRIDGE) != 0;
- if (isBridgeMethod) {
- mStatus.debug("Member is a bridge method");
+ if (isBridgeMethod) {
+ mStatus.debug("Member is a bridge method");
+ }
}
String signature = context.getMemberDescriptor();
Integer maxTargetSdk = null;
+ String implicitMemberSignature = null;
for (ElementValuePair property : annotation.getElementValuePairs()) {
switch (property.getNameString()) {
@@ -113,9 +117,30 @@
maxTargetSdk = ((SimpleElementValue) property.getValue()).getValueInt();
break;
+ case IMPLICIT_MEMBER_PROPERTY:
+ implicitMemberSignature = property.getValue().stringifyValue();
+ if (context instanceof AnnotatedClassContext) {
+ signature = String.format("L%s;->%s",
+ context.getClassDescriptor(), implicitMemberSignature);
+ } else {
+ context.reportError(
+ "Expected annotation with an %s property to be on a class but is on %s",
+ IMPLICIT_MEMBER_PROPERTY,
+ signature);
+ return;
+ }
+ break;
}
}
+ if (context instanceof AnnotatedClassContext && implicitMemberSignature == null) {
+ context.reportError(
+ "Missing property %s on annotation on class %s",
+ IMPLICIT_MEMBER_PROPERTY,
+ signature);
+ return;
+ }
+
// Verify that maxTargetSdk is valid.
if (!mSdkVersionToFlagMap.containsKey(maxTargetSdk)) {
context.reportError("Invalid value for %s: got %d, expected one of [%s]",
diff --git a/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeMultiHandlerTest.java b/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeMultiHandlerTest.java
deleted file mode 100644
index 25f2844..0000000
--- a/tools/class2greylist/test/src/com/android/class2greylist/CovariantReturnTypeMultiHandlerTest.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.class2greylist;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import static java.util.Collections.emptySet;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import java.io.IOException;
-import java.util.Map;
-
-public class CovariantReturnTypeMultiHandlerTest extends AnnotationHandlerTestBase {
-
- private static final String FLAG = "test-flag";
-
- @Before
- public void setup() throws IOException {
- // To keep the test simpler and more concise, we don't use the real
- // @CovariantReturnType annotation here, but use our own @Annotation
- // and @Annotation.Multi that have the same semantics. It doesn't have
- // to match the real annotation, just have the same properties
- // (returnType and value).
- mJavac.addSource("annotation.Annotation", Joiner.on('\n').join(
- "package annotation;",
- "import static java.lang.annotation.RetentionPolicy.CLASS;",
- "import java.lang.annotation.Repeatable;",
- "import java.lang.annotation.Retention;",
- "@Repeatable(Annotation.Multi.class)",
- "@Retention(CLASS)",
- "public @interface Annotation {",
- " Class<?> returnType();",
- " @Retention(CLASS)",
- " @interface Multi {",
- " Annotation[] value();",
- " }",
- "}"));
- }
-
- @Test
- public void testReturnTypeMulti() throws IOException {
- mJavac.addSource("a.b.Class", Joiner.on('\n').join(
- "package a.b;",
- "import annotation.Annotation;",
- "public class Class {",
- " @Annotation(returnType=Integer.class)",
- " @Annotation(returnType=Long.class)",
- " public String method() {return null;}",
- "}"));
- mJavac.compile();
-
- Map<String, AnnotationHandler> handlerMap =
- ImmutableMap.of("Lannotation/Annotation$Multi;",
- new CovariantReturnTypeMultiHandler(
- mConsumer,
- ImmutableSet.of("La/b/Class;->method()Ljava/lang/String;"),
- FLAG,
- "Lannotation/Annotation;"));
- new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
-
- assertNoErrors();
- ArgumentCaptor<String> whitelist = ArgumentCaptor.forClass(String.class);
- verify(mConsumer, times(2)).consume(whitelist.capture(), any(),
- eq(ImmutableSet.of(FLAG)));
- assertThat(whitelist.getAllValues()).containsExactly(
- "La/b/Class;->method()Ljava/lang/Integer;",
- "La/b/Class;->method()Ljava/lang/Long;");
- }
-
- @Test
- public void testReturnTypeMultiNotPublicApi() throws IOException {
- mJavac.addSource("a.b.Class", Joiner.on('\n').join(
- "package a.b;",
- "import annotation.Annotation;",
- "public class Class {",
- " @Annotation(returnType=Integer.class)",
- " @Annotation(returnType=Long.class)",
- " public String method() {return null;}",
- "}"));
- mJavac.compile();
-
- Map<String, AnnotationHandler> handlerMap =
- ImmutableMap.of("Lannotation/Annotation$Multi;",
- new CovariantReturnTypeMultiHandler(
- mConsumer,
- emptySet(),
- FLAG,
- "Lannotation/Annotation;"));
- new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
-
- verify(mStatus, atLeastOnce()).error(any(), any());
- }
-}
diff --git a/tools/class2greylist/test/src/com/android/class2greylist/RepeatedAnnotationHandlerTest.java b/tools/class2greylist/test/src/com/android/class2greylist/RepeatedAnnotationHandlerTest.java
new file mode 100644
index 0000000..f2f70ee
--- /dev/null
+++ b/tools/class2greylist/test/src/com/android/class2greylist/RepeatedAnnotationHandlerTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package com.android.class2greylist;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.apache.bcel.classfile.AnnotationEntry;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RepeatedAnnotationHandlerTest extends AnnotationHandlerTestBase {
+
+ @Before
+ public void setup() {
+ // To keep the test simpler and more concise, we don't use a real annotation here, but use
+ // our own @Annotation and @Annotation.Multi that have the same relationship.
+ mJavac.addSource("annotation.Annotation", Joiner.on('\n').join(
+ "package annotation;",
+ "import static java.lang.annotation.RetentionPolicy.CLASS;",
+ "import java.lang.annotation.Repeatable;",
+ "import java.lang.annotation.Retention;",
+ "@Repeatable(Annotation.Multi.class)",
+ "@Retention(CLASS)",
+ "public @interface Annotation {",
+ " Class<?> clazz();",
+ " @Retention(CLASS)",
+ " @interface Multi {",
+ " Annotation[] value();",
+ " }",
+ "}"));
+ }
+
+ @Test
+ public void testRepeated() throws IOException {
+ mJavac.addSource("a.b.Class", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Annotation;",
+ "public class Class {",
+ " @Annotation(clazz=Integer.class)",
+ " @Annotation(clazz=Long.class)",
+ " public String method() {return null;}",
+ "}"));
+ mJavac.compile();
+
+ TestAnnotationHandler handler = new TestAnnotationHandler();
+ Map<String, AnnotationHandler> handlerMap =
+ ImmutableMap.of("Lannotation/Annotation$Multi;",
+ new RepeatedAnnotationHandler("Lannotation/Annotation;", handler));
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), mStatus, handlerMap).visit();
+
+ assertNoErrors();
+ assertThat(handler.getClasses()).containsExactly(
+ "Ljava/lang/Integer;",
+ "Ljava/lang/Long;");
+ }
+
+ private static class TestAnnotationHandler extends AnnotationHandler {
+
+ private final List<String> classes;
+
+ private TestAnnotationHandler() {
+ this.classes = new ArrayList<>();
+ }
+
+ @Override
+ void handleAnnotation(AnnotationEntry annotation,
+ AnnotationContext context) {
+ classes.add(annotation.getElementValuePairs()[0].getValue().stringifyValue());
+ }
+
+ private List<String> getClasses() {
+ return classes;
+ }
+ }
+}
diff --git a/tools/class2greylist/test/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandlerTest.java b/tools/class2greylist/test/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandlerTest.java
index dc767fe..a6d6a16 100644
--- a/tools/class2greylist/test/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandlerTest.java
+++ b/tools/class2greylist/test/src/com/android/class2greylist/UnsupportedAppUsageAnnotationHandlerTest.java
@@ -59,10 +59,17 @@
"package annotation;",
"import static java.lang.annotation.RetentionPolicy.CLASS;",
"import java.lang.annotation.Retention;",
+ "import java.lang.annotation.Repeatable;",
"@Retention(CLASS)",
+ "@Repeatable(Anno.Container.class)",
"public @interface Anno {",
" String expectedSignature() default \"\";",
" int maxTargetSdk() default Integer.MAX_VALUE;",
+ " String implicitMember() default \"\";",
+ " @Retention(CLASS)",
+ " public @interface Container {",
+ " Anno[] value();",
+ " }",
"}"));
}
@@ -137,6 +144,70 @@
}
@Test
+ public void testGreylistImplicit() throws IOException {
+ mJavac.addSource("a.b.EnumClass", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "@Anno(implicitMember=\"values()[La/b/EnumClass;\")",
+ "public enum EnumClass {",
+ " VALUE",
+ "}"));
+ mJavac.compile();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.EnumClass"), mStatus,
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP))
+ ).visit();
+
+ assertNoErrors();
+ ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
+ verify(mConsumer, times(1)).consume(greylist.capture(), any(), any());
+ assertThat(greylist.getValue()).isEqualTo("La/b/EnumClass;->values()[La/b/EnumClass;");
+ }
+
+ @Test
+ public void testGreylistImplicit_Invalid_MissingOnClass() throws IOException {
+ mJavac.addSource("a.b.EnumClass", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "@Anno",
+ "public enum EnumClass {",
+ " VALUE",
+ "}"));
+ mJavac.compile();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.EnumClass"), mStatus,
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP))
+ ).visit();
+
+ ArgumentCaptor<String> format = ArgumentCaptor.forClass(String.class);
+ verify(mStatus, times(1)).error(format.capture(), any());
+ // Ensure that the correct error is reported.
+ assertThat(format.getValue())
+ .contains("Missing property implicitMember on annotation on class");
+ }
+
+ @Test
+ public void testGreylistImplicit_Invalid_PresentOnMember() throws IOException {
+ mJavac.addSource("a.b.EnumClass", Joiner.on('\n').join(
+ "package a.b;",
+ "import annotation.Anno;",
+ "public enum EnumClass {",
+ " @Anno(implicitMember=\"values()[La/b/EnumClass;\")",
+ " VALUE",
+ "}"));
+ mJavac.compile();
+
+ new AnnotationVisitor(mJavac.getCompiledClass("a.b.EnumClass"), mStatus,
+ ImmutableMap.of(ANNOTATION, createGreylistHandler(x -> true, NULL_SDK_MAP))
+ ).visit();
+
+ ArgumentCaptor<String> format = ArgumentCaptor.forClass(String.class);
+ verify(mStatus, times(1)).error(format.capture(), any());
+ assertThat(format.getValue())
+ .contains("Expected annotation with an implicitMember property to be on a class");
+ }
+
+ @Test
public void testGreylistMethodExpectedSignature() throws IOException {
mJavac.addSource("a.b.Class", Joiner.on('\n').join(
"package a.b;",
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index 20e5c64..f4a2dc1 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -43,6 +43,22 @@
java_lib_location="${ANDROID_HOST_OUT}/../common/obj/JAVA_LIBRARIES"
make_target_name="apache-harmony-jdwp-tests-hostdex"
+function boot_classpath_arg {
+ local dir="$1"
+ local suffix="$2"
+ shift 2
+ local separator=""
+ for var
+ do
+ printf -- "${separator}${dir}/${var}${suffix}.jar";
+ separator=":"
+ done
+}
+
+# Note: This must match the TEST_CORE_JARS in Android.common_path.mk
+# because that's what we use for compiling the core.art image.
+BOOT_CLASSPATH_JARS="core-oj core-libart core-simple conscrypt okhttp bouncycastle"
+
vm_args=""
art="$android_root/bin/art"
art_debugee="sh $android_root/bin/art"
@@ -59,6 +75,8 @@
explicit_debug="no"
verbose="no"
image="-Ximage:/data/art-test/core.art"
+boot_classpath="$(boot_classpath_arg /system/framework -testdex $BOOT_CLASSPATH_JARS)"
+boot_classpath_locations=""
with_jdwp_path=""
agent_wrapper=""
vm_args=""
@@ -90,6 +108,17 @@
art_debugee="bash ${OUT_DIR-out}/host/linux-x86/bin/art"
# We force generation of a new image to avoid build-time and run-time classpath differences.
image="-Ximage:/system/non/existent/vogar.art"
+ # Pass the host boot classpath.
+ if [ "${ANDROID_HOST_OUT:0:${#ANDROID_BUILD_TOP}+1}" = "${ANDROID_BUILD_TOP}/" ]; then
+ framework_location="${ANDROID_HOST_OUT:${#ANDROID_BUILD_TOP}+1}/framework"
+ else
+ echo "error: ANDROID_BUILD_TOP/ is not a prefix of ANDROID_HOST_OUT"
+ echo "ANDROID_BUILD_TOP=${ANDROID_BUILD_TOP}"
+ echo "ANDROID_HOST_OUT=${ANDROID_HOST_OUT}"
+ exit
+ fi
+ boot_classpath="$(boot_classpath_arg ${ANDROID_HOST_OUT}/framework -hostdex $BOOT_CLASSPATH_JARS)"
+ boot_classpath_locations="$(boot_classpath_arg ${framework_location} -hostdex $BOOT_CLASSPATH_JARS)"
# We do not need a device directory on host.
device_dir=""
# Vogar knows which VM to use on host.
@@ -104,6 +133,8 @@
debuggee_args=""
# No image. On the RI.
image=""
+ boot_classpath=""
+ boot_classpath_locations=""
# We do not need a device directory on RI.
device_dir=""
# Vogar knows which VM to use on RI.
@@ -305,6 +336,15 @@
if [[ "$image" != "" ]]; then
vm_args="$vm_args --vm-arg $image"
+ debuggee_args="$debuggee_args $image"
+fi
+if [[ "$boot_classpath" != "" ]]; then
+ vm_args="$vm_args --vm-arg -Xbootclasspath:${boot_classpath}"
+ debuggee_args="$debuggee_args -Xbootclasspath:${boot_classpath}"
+fi
+if [[ "$boot_classpath_locations" != "" ]]; then
+ vm_args="$vm_args --vm-arg -Xbootclasspath-locations:${boot_classpath_locations}"
+ debuggee_args="$debuggee_args -Xbootclasspath-locations:${boot_classpath_locations}"
fi
if [[ "$plugin" != "" ]]; then
@@ -363,7 +403,7 @@
--vm-arg -Djpda.settings.waitingTime=$jdwp_test_timeout \
--vm-arg -Djpda.settings.transportAddress=127.0.0.1:55107 \
--vm-arg -Djpda.settings.dumpProcess="$dump_command" \
- --vm-arg -Djpda.settings.debuggeeJavaPath="$art_debugee $plugin $image $debuggee_args" \
+ --vm-arg -Djpda.settings.debuggeeJavaPath="$art_debugee $plugin $debuggee_args" \
--classpath "$test_jar" \
$toolchain_args \
$test
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 2d39b2a..63f1fce 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -46,6 +46,21 @@
done
}
+function boot_classpath_arg {
+ local dir="$1"
+ local suffix="$2"
+ shift 2
+ printf -- "--vm-arg -Xbootclasspath"
+ for var
+ do
+ printf -- ":${dir}/${var}${suffix}.jar";
+ done
+}
+
+# Note: This must match the TEST_CORE_JARS in Android.common_path.mk
+# because that's what we use for compiling the core.art image.
+BOOT_CLASSPATH_JARS="core-oj core-libart core-simple conscrypt okhttp bouncycastle"
+
DEPS="core-tests jsr166-tests mockito-target"
for lib in $DEPS
@@ -110,6 +125,7 @@
if [[ "$1" == "--mode=device" ]]; then
device_mode=true
vogar_args="$vogar_args --vm-arg -Ximage:/data/art-test/core.art"
+ vogar_args="$vogar_args $(boot_classpath_arg /system/framework -testdex $BOOT_CLASSPATH_JARS)"
shift
elif [[ "$1" == "--mode=host" ]]; then
# We explicitly give a wrong path for the image, to ensure vogar