Merge "Add feature flag for loading app image startup cache"
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 2e1f364..148fdba 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1449,6 +1449,7 @@
static void UpdateInternStrings(
gc::space::ImageSpace* space,
+ bool use_preresolved_strings,
const SafeMap<mirror::String*, mirror::String*>& intern_remap)
REQUIRES_SHARED(Locks::mutator_lock_);
};
@@ -1464,8 +1465,10 @@
ScopedTrace app_image_timing("AppImage:Updating");
Thread* const self = Thread::Current();
- gc::Heap* const heap = Runtime::Current()->GetHeap();
+ Runtime* const runtime = Runtime::Current();
+ gc::Heap* const heap = runtime->GetHeap();
const ImageHeader& header = space->GetImageHeader();
+ bool load_app_image_startup_cache = runtime->LoadAppImageStartupCache();
{
// Register dex caches with the class loader.
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
@@ -1479,6 +1482,10 @@
class_linker->RegisterDexFileLocked(*dex_file, dex_cache, class_loader.Get());
}
+ if (!load_app_image_startup_cache) {
+ dex_cache->ClearPreResolvedStrings();
+ }
+
if (kIsDebugBuild) {
CHECK(new_class_set != nullptr);
mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
@@ -1545,11 +1552,13 @@
void AppImageLoadingHelper::UpdateInternStrings(
gc::space::ImageSpace* space,
+ bool use_preresolved_strings,
const SafeMap<mirror::String*, mirror::String*>& intern_remap) {
const uint8_t* target_base = space->Begin();
const ImageSection& sro_section =
space->GetImageHeader().GetImageStringReferenceOffsetsSection();
const size_t num_string_offsets = sro_section.Size() / sizeof(AppImageReferenceOffsetInfo);
+ InternTable* const intern_table = Runtime::Current()->GetInternTable();
VLOG(image)
<< "ClassLinker:AppImage:InternStrings:imageStringReferenceOffsetCount = "
@@ -1582,24 +1591,29 @@
WriteBarrier::ForEveryFieldWrite(dex_cache);
dex_cache->GetStrings()[string_index].store(
mirror::StringDexCachePair(it->second, source.index));
+ } else if (!use_preresolved_strings) {
+ dex_cache->GetStrings()[string_index].store(
+ mirror::StringDexCachePair(intern_table->InternStrong(referred_string), source.index));
}
} else if (HasDexCachePreResolvedStringNativeRefTag(base_offset)) {
- base_offset = ClearDexCacheNativeRefTags(base_offset);
- DCHECK_ALIGNED(base_offset, 2);
+ if (use_preresolved_strings) {
+ base_offset = ClearDexCacheNativeRefTags(base_offset);
+ DCHECK_ALIGNED(base_offset, 2);
- ObjPtr<mirror::DexCache> dex_cache =
- reinterpret_cast<mirror::DexCache*>(space->Begin() + base_offset);
- uint32_t string_index = sro_base[offset_index].second;
+ ObjPtr<mirror::DexCache> dex_cache =
+ reinterpret_cast<mirror::DexCache*>(space->Begin() + base_offset);
+ uint32_t string_index = sro_base[offset_index].second;
- ObjPtr<mirror::String> referred_string =
- dex_cache->GetPreResolvedStrings()[string_index].Read();
- DCHECK(referred_string != nullptr);
+ ObjPtr<mirror::String> referred_string =
+ dex_cache->GetPreResolvedStrings()[string_index].Read();
+ DCHECK(referred_string != nullptr);
- auto it = intern_remap.find(referred_string.Ptr());
- if (it != intern_remap.end()) {
- // Because we are not using a helper function we need to mark the GC card manually.
- WriteBarrier::ForEveryFieldWrite(dex_cache);
- dex_cache->GetPreResolvedStrings()[string_index] = GcRoot<mirror::String>(it->second);
+ auto it = intern_remap.find(referred_string.Ptr());
+ if (it != intern_remap.end()) {
+ // Because we are not using a helper function we need to mark the GC card manually.
+ WriteBarrier::ForEveryFieldWrite(dex_cache);
+ dex_cache->GetPreResolvedStrings()[string_index] = GcRoot<mirror::String>(it->second);
+ }
}
} else {
uint32_t raw_member_offset = sro_base[offset_index].second;
@@ -1622,6 +1636,13 @@
/* kCheckTransaction= */ false,
kVerifyNone,
/* kIsVolatile= */ false>(member_offset, it->second);
+ } else if (!use_preresolved_strings) {
+ obj_ptr->SetFieldObject</* kTransactionActive= */ false,
+ /* kCheckTransaction= */ false,
+ kVerifyNone,
+ /* kIsVolatile= */ false>(
+ member_offset,
+ intern_table->InternStrong(referred_string));
}
}
}
@@ -1632,13 +1653,16 @@
// the strings they point to.
ScopedTrace timing("AppImage:InternString");
- InternTable* const intern_table = Runtime::Current()->GetInternTable();
+ Runtime* const runtime = Runtime::Current();
+ InternTable* const intern_table = runtime->GetInternTable();
+
+ const bool load_startup_cache = runtime->LoadAppImageStartupCache();
// Add the intern table, removing any conflicts. For conflicts, store the new address in a map
// for faster lookup.
// TODO: Optimize with a bitmap or bloom filter
SafeMap<mirror::String*, mirror::String*> intern_remap;
- intern_table->AddImageStringsToTable(space, [&](InternTable::UnorderedSet& interns)
+ auto func = [&](InternTable::UnorderedSet& interns)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(Locks::intern_table_lock_) {
const size_t non_boot_image_strings = intern_table->CountInterns(
@@ -1681,14 +1705,23 @@
CHECK(intern_table->LookupStrongLocked(string) == nullptr) << string->ToModifiedUtf8();
}
}
- });
+ };
- VLOG(image) << "AppImage:conflictingInternStrings = " << intern_remap.size();
+ bool update_intern_strings;
+ if (load_startup_cache) {
+ // Only add the intern table if we are using the startup cache. Otherwise,
+ // UpdateInternStrings adds the strings to the intern table.
+ intern_table->AddImageStringsToTable(space, func);
+ update_intern_strings = kIsDebugBuild || !intern_remap.empty();
+ VLOG(image) << "AppImage:conflictingInternStrings = " << intern_remap.size();
+ } else {
+ update_intern_strings = true;
+ }
// For debug builds, always run the code below to get coverage.
- if (kIsDebugBuild || !intern_remap.empty()) {
+ if (update_intern_strings) {
// Slow path case is when there are conflicting intern strings to fix up.
- UpdateInternStrings(space, intern_remap);
+ UpdateInternStrings(space, /*use_preresolved_strings=*/ load_startup_cache, intern_remap);
}
}
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 47b621a..f0ad931 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -110,8 +110,7 @@
WriteBarrier::ForEveryFieldWrite(this);
}
-inline void DexCache::SetPreResolvedString(dex::StringIndex string_idx,
- ObjPtr<String> resolved) {
+inline void DexCache::SetPreResolvedString(dex::StringIndex string_idx, ObjPtr<String> resolved) {
DCHECK(resolved != nullptr);
DCHECK_LT(string_idx.index_, GetDexFile()->NumStringIds());
GetPreResolvedStrings()[string_idx.index_] = GcRoot<mirror::String>(resolved);
@@ -122,6 +121,17 @@
WriteBarrier::ForEveryFieldWrite(this);
}
+inline void DexCache::ClearPreResolvedStrings() {
+ SetFieldPtr64</*kTransactionActive=*/false,
+ /*kCheckTransaction=*/false,
+ kVerifyNone,
+ GcRoot<mirror::String>*>(PreResolvedStringsOffset(), nullptr);
+ SetField32</*kTransactionActive=*/false,
+ /*bool kCheckTransaction=*/false,
+ kVerifyNone,
+ /*kIsVolatile=*/false>(NumPreResolvedStringsOffset(), 0);
+}
+
inline void DexCache::ClearString(dex::StringIndex string_idx) {
DCHECK(Runtime::Current()->IsAotCompiler());
uint32_t slot_idx = StringSlotIndex(string_idx);
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index c742928..b5619f8 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -283,6 +283,11 @@
ObjPtr<mirror::String> resolved)
ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
+ // Clear the preresolved string cache to prevent further usage. Not thread safe, so should only
+ // be called when the string cache is guaranteed to not be accessed.
+ void ClearPreResolvedStrings()
+ ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
+
// Clear a string for a string_idx, used to undo string intern transactions to make sure
// the string isn't kept live.
void ClearString(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 26fc5e9..891ecef 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -149,6 +149,7 @@
HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12)
| (1 << 13),
PROFILE_SYSTEM_SERVER = 1 << 14,
+ USE_APP_IMAGE_STARTUP_CACHE = 1 << 16,
// bits to shift (flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) by to get a value
// corresponding to hiddenapi::EnforcementPolicy
@@ -298,6 +299,10 @@
bool profile_system_server = (runtime_flags & PROFILE_SYSTEM_SERVER) == PROFILE_SYSTEM_SERVER;
runtime_flags &= ~PROFILE_SYSTEM_SERVER;
+ Runtime::Current()->SetLoadAppImageStartupCacheEnabled(
+ (runtime_flags & USE_APP_IMAGE_STARTUP_CACHE) != 0u);
+ runtime_flags &= ~USE_APP_IMAGE_STARTUP_CACHE;
+
if (runtime_flags != 0) {
LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags);
}
diff --git a/runtime/runtime.h b/runtime/runtime.h
index ee2c514..81c17a5 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -823,6 +823,14 @@
ThreadPool* const thread_pool_;
};
+ bool LoadAppImageStartupCache() const {
+ return load_app_image_startup_cache_;
+ }
+
+ void SetLoadAppImageStartupCacheEnabled(bool enabled) {
+ load_app_image_startup_cache_ = enabled;
+ }
+
private:
static void InitPlatformSignalHandlers();
@@ -1145,6 +1153,8 @@
uint32_t verifier_logging_threshold_ms_;
+ bool load_app_image_startup_cache_ = false;
+
// Note: See comments on GetFaultMessage.
friend std::string GetFaultMessageForAbortLogging();
friend class ScopedThreadPoolUsage;