Merge "Fix some indentation errors in compiler driver"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index bcf48fd..b6d4ef6 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -121,7 +121,7 @@
ART_GTEST_proxy_test_DEX_DEPS := Interfaces
ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods
ART_GTEST_profile_assistant_test_DEX_DEPS := ProfileTestMultiDex
-ART_GTEST_profile_compilation_info_test_DEX_DEPS := ProfileTestMultiDex
+ART_GTEST_profile_compilation_info_test_DEX_DEPS := ManyMethods ProfileTestMultiDex
ART_GTEST_runtime_callbacks_test_DEX_DEPS := XandY
ART_GTEST_stub_test_DEX_DEPS := AllFields
ART_GTEST_transaction_test_DEX_DEPS := Transaction
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 07639e8..b224ec7 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -484,7 +484,7 @@
* -Xps-*
*/
TEST_F(CmdlineParserTest, ProfileSaverOptions) {
- ProfileSaverOptions opt = ProfileSaverOptions(true, 1, 2, 3, 4, 5, 6, 7, "abc");
+ ProfileSaverOptions opt = ProfileSaverOptions(true, 1, 2, 3, 4, 5, 6, 7, "abc", true);
EXPECT_SINGLE_PARSE_VALUE(opt,
"-Xjitsaveprofilinginfo "
@@ -495,7 +495,8 @@
"-Xps-min-classes-to-save:5 "
"-Xps-min-notification-before-wake:6 "
"-Xps-max-notification-before-wake:7 "
- "-Xps-profile-path:abc",
+ "-Xps-profile-path:abc "
+ "-Xps-profile-boot-class-path",
M::ProfileSaverOpts);
} // TEST_F
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 185a0e4..4de8a48 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -712,6 +712,11 @@
return Result::SuccessNoValue();
}
+ if (option == "profile-boot-class-path") {
+ existing.profile_boot_class_path_ = true;
+ return Result::SuccessNoValue();
+ }
+
// The rest of these options are always the wildcard from '-Xps-*'
std::string suffix = RemovePrefix(option);
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 104dc35..91b58e1 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2272,6 +2272,8 @@
const char* descriptor = dex_file.StringDataByIdx(class_type_id.descriptor_idx_);
ScopedObjectAccessUnchecked soa(Thread::Current());
StackHandleScope<3> hs(soa.Self());
+ const bool is_boot_image = manager_->GetCompiler()->GetCompilerOptions().IsBootImage();
+ const bool is_app_image = manager_->GetCompiler()->GetCompilerOptions().IsAppImage();
mirror::Class::Status old_status = klass->GetStatus();;
// Only try to initialize classes that were successfully verified.
@@ -2293,32 +2295,28 @@
ObjectLock<mirror::Class> lock(soa.Self(), h_klass);
// Attempt to initialize allowing initialization of parent classes but still not static
// fields.
- bool is_superclass_initialized = true;
- if (!manager_->GetCompiler()->GetCompilerOptions().IsAppImage()) {
- // If not an app image case, the compiler won't initialize too much things and do a fast
- // fail, don't check dependencies.
- manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, true);
- } else {
- // For app images, do the initialization recursively and resolve types encountered to make
- // sure the compiler runs without error.
- is_superclass_initialized = InitializeDependencies(klass, class_loader, soa.Self());
- if (is_superclass_initialized) {
- manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, true);
- }
- }
+ manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, true);
old_status = klass->GetStatus();
- // If superclass cannot be initialized, no need to proceed.
+ // If the class was not initialized, we can proceed to see if we can initialize static
+ // fields.
if (!klass->IsInitialized() &&
- is_superclass_initialized &&
+ (is_app_image || is_boot_image) &&
manager_->GetCompiler()->IsImageClass(descriptor)) {
bool can_init_static_fields = false;
- if (manager_->GetCompiler()->GetCompilerOptions().IsBootImage()) {
+ if (is_boot_image) {
// We need to initialize static fields, we only do this for image classes that aren't
- // marked with the $NoPreloadHolder (which implies this should not be initialized early).
+ // marked with the $NoPreloadHolder (which implies this should not be initialized
+ // early).
can_init_static_fields = !StringPiece(descriptor).ends_with("$NoPreloadHolder;");
} else {
- can_init_static_fields = manager_->GetCompiler()->GetCompilerOptions().IsAppImage() &&
+ CHECK(is_app_image);
+ // The boot image case doesn't need to recursively initialize the dependencies with
+ // special logic since the class linker already does this.
+ bool is_superclass_initialized =
+ InitializeDependencies(klass, class_loader, soa.Self());
+ can_init_static_fields =
!soa.Self()->IsExceptionPending() &&
+ is_superclass_initialized &&
NoClinitInDependency(klass, soa.Self(), &class_loader);
// TODO The checking for clinit can be removed since it's already
// checked when init superclass. Currently keep it because it contains
@@ -2361,6 +2359,10 @@
soa.Self()->ClearException();
transaction.Rollback();
CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored";
+ } else if (is_boot_image) {
+ // For boot image, we want to put the updated status in the oat class since we can't
+ // reject the image anyways.
+ old_status = klass->GetStatus();
}
}
@@ -2370,6 +2372,8 @@
// above as we will allocate strings, so must be allowed to suspend.
if (&klass->GetDexFile() == manager_->GetDexFile()) {
InternStrings(klass, class_loader);
+ } else {
+ DCHECK(!is_boot_image) << "Boot image must have equal dex files";
}
}
}
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 1274a36..0197703 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -132,8 +132,7 @@
std::vector<std::unique_ptr<const DexFile>> OpenTestDexFiles(const char* name);
- std::unique_ptr<const DexFile> OpenTestDexFile(const char* name)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ std::unique_ptr<const DexFile> OpenTestDexFile(const char* name);
jobject LoadDex(const char* dex_name) REQUIRES_SHARED(Locks::mutator_lock_);
jobject LoadMultiDex(const char* first_dex_name, const char* second_dex_name)
diff --git a/runtime/dex_reference_collection.h b/runtime/dex_reference_collection.h
index 76355d6..01b9b97 100644
--- a/runtime/dex_reference_collection.h
+++ b/runtime/dex_reference_collection.h
@@ -63,12 +63,13 @@
private:
DexFileMap map_;
- // Optimize for adding to same vector in succession.
const DexFile* current_dex_file_ = nullptr;
IndexVector* current_vector_ = nullptr;
VectorAllocator vector_allocator_;
ALWAYS_INLINE IndexVector* GetOrInsertVector(const DexFile* dex) {
+ // Optimize for adding to same vector in succession, the cached dex file and vector aims to
+ // prevent map lookups.
if (UNLIKELY(current_dex_file_ != dex)) {
// There is an assumption that constructing an empty vector wont do any allocations. If this
// incorrect, this might leak for the arena case.
diff --git a/runtime/jit/profile_compilation_info-inl.h b/runtime/jit/profile_compilation_info-inl.h
index 8a067a5..b71a95e 100644
--- a/runtime/jit/profile_compilation_info-inl.h
+++ b/runtime/jit/profile_compilation_info-inl.h
@@ -22,10 +22,11 @@
namespace art {
template <class Iterator>
-inline bool ProfileCompilationInfo::AddSampledMethodsForDex(bool startup,
- const DexFile* dex_file,
- Iterator index_begin,
- Iterator index_end) {
+inline bool ProfileCompilationInfo::AddMethodsForDex(bool startup,
+ bool hot,
+ const DexFile* dex_file,
+ Iterator index_begin,
+ Iterator index_end) {
DexFileData* data = GetOrAddDexFileData(dex_file);
if (data == nullptr) {
return false;
@@ -33,21 +34,9 @@
for (auto it = index_begin; it != index_end; ++it) {
DCHECK_LT(*it, data->num_method_ids);
data->AddSampledMethod(startup, *it);
- }
- return true;
-}
-
-template <class Iterator>
-inline bool ProfileCompilationInfo::AddHotMethodsForDex(const DexFile* dex_file,
- Iterator index_begin,
- Iterator index_end) {
- DexFileData* data = GetOrAddDexFileData(dex_file);
- if (data == nullptr) {
- return false;
- }
- for (auto it = index_begin; it != index_end; ++it) {
- DCHECK_LT(*it, data->num_method_ids);
- data->FindOrAddMethod(*it);
+ if (hot) {
+ data->FindOrAddMethod(*it);
+ }
}
return true;
}
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index ea27d3b..a67fb38 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -1223,6 +1223,15 @@
method_ref.dex_method_index);
}
+const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData(
+ const DexFile* dex_file) const {
+ const DexFileData* dex_data = FindDexData(GetProfileDexFileKey(dex_file->GetLocation()));
+ if (dex_data == nullptr || !ChecksumMatch(*dex_file, dex_data->checksum)) {
+ return nullptr;
+ }
+ return dex_data;
+}
+
bool ProfileCompilationInfo::IsStartupOrHotMethod(const std::string& dex_location,
uint32_t dex_checksum,
uint16_t dex_method_index) const {
@@ -1238,6 +1247,15 @@
return method_it != methods.end();
}
+bool ProfileCompilationInfo::ContainsSampledMethod(bool startup,
+ const MethodReference& method_ref) const {
+ const DexFileData* dex_data = FindDexData(method_ref.dex_file);
+ if (dex_data == nullptr) {
+ return false;
+ }
+ return dex_data->HasSampledMethod(startup, method_ref.dex_method_index);
+}
+
bool ProfileCompilationInfo::ContainsHotMethod(const MethodReference& method_ref) const {
return FindMethod(method_ref.dex_file->GetLocation(),
method_ref.dex_file->GetLocationChecksum(),
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index bd1b9d6..7bcaffb 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -211,24 +211,22 @@
bool AddMethod(const ProfileMethodInfo& pmi);
// Add methods that have samples but are are not necessarily hot. These are partitioned into two
- // possibly intersecting sets startup and post startup.
+ // possibly intersecting sets startup and post startup. Sampled methods are used for layout but
+ // not necessarily determining what gets compiled.
bool AddSampledMethod(bool startup,
const std::string& dex_location,
uint32_t checksum,
uint16_t method_idx,
uint32_t num_method_ids);
- // Bulk add sampled methods for a single dex, fast since it only has one GetOrAddDexFileData call.
- template <class Iterator>
- bool AddSampledMethodsForDex(bool startup,
- const DexFile* dex_file,
- Iterator index_begin,
- Iterator index_end);
- // Bulk add hot methods for a single dex, fast since it only has one GetOrAddDexFileData call.
+ // Bulk add sampled methods and/or hot methods for a single dex, fast since it only has one
+ // GetOrAddDexFileData call.
template <class Iterator>
- bool AddHotMethodsForDex(const DexFile* dex_file,
- Iterator index_begin,
- Iterator index_end);
+ ALWAYS_INLINE bool AddMethodsForDex(bool startup,
+ bool hot,
+ const DexFile* dex_file,
+ Iterator index_begin,
+ Iterator index_end);
// Load profile information from the given file descriptor.
// If the current profile is non-empty the load will fail.
@@ -264,6 +262,10 @@
// Return true if the method reference iS present and hot in the profiling info.
bool ContainsHotMethod(const MethodReference& method_ref) const;
+
+ // Return true if the profile contains a startup or post startup method.
+ bool ContainsSampledMethod(bool startup, const MethodReference& method_ref) const;
+
// Return true if the class's type is present in the profiling info.
bool ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const;
@@ -358,7 +360,7 @@
class_set(std::less<dex::TypeIndex>(), arena->Adapter(kArenaAllocProfile)),
num_method_ids(num_methods),
bitmap_storage(arena->Adapter(kArenaAllocProfile)) {
- const size_t num_bits = num_method_ids * kBitmapCount;
+ const size_t num_bits = num_method_ids * kBitmapIndexCount;
bitmap_storage.resize(RoundUp(num_bits, kBitsPerByte) / kBitsPerByte);
if (!bitmap_storage.empty()) {
method_bitmap =
@@ -409,9 +411,9 @@
private:
enum BitmapIndex {
- kBitmapStartup,
- kBitmapPostStartup,
- kBitmapCount,
+ kBitmapIndexStartup,
+ kBitmapIndexPostStartup,
+ kBitmapIndexCount,
};
size_t MethodBitIndex(bool startup, size_t index) const {
@@ -420,8 +422,8 @@
// This compresses better than ([startup bit][post statup bit])*
return index + (startup
- ? kBitmapStartup * num_method_ids
- : kBitmapPostStartup * num_method_ids);
+ ? kBitmapIndexStartup * num_method_ids
+ : kBitmapIndexPostStartup * num_method_ids);
}
};
@@ -468,6 +470,10 @@
// doesn't contain the key.
const DexFileData* FindDexData(const std::string& profile_key) const;
+ // Return the dex data associated with the given dex file or null if the profile doesn't contain
+ // the key or the checksum mismatches.
+ const DexFileData* FindDexData(const DexFile* dex_file) const;
+
// Checks if the profile is empty.
bool IsEmpty() const;
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index 39670af..5528366 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -25,7 +25,7 @@
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "handle_scope-inl.h"
-#include "jit/profile_compilation_info.h"
+#include "jit/profile_compilation_info-inl.h"
#include "linear_alloc.h"
#include "scoped_thread_state_change-inl.h"
#include "type_reference.h"
@@ -891,6 +891,49 @@
test_info.MergeWith(merge_info);
}
EXPECT_TRUE(test_info.IsStartupOrHotMethod(kDex1, kChecksum1, 11));
+
+ // Test bulk adding.
+ {
+ std::unique_ptr<const DexFile> dex(OpenTestDexFile("ManyMethods"));
+ ProfileCompilationInfo info;
+ std::vector<uint16_t> hot_methods = {1, 3, 5};
+ std::vector<uint16_t> startup_methods = {1, 2};
+ std::vector<uint16_t> post_methods = {0, 2, 6};
+ ASSERT_GE(dex->NumMethodIds(), 7u);
+ info.AddMethodsForDex(/*startup*/true,
+ /*hot*/true,
+ dex.get(),
+ hot_methods.begin(),
+ hot_methods.end());
+ info.AddMethodsForDex(/*startup*/true,
+ /*hot*/false,
+ dex.get(),
+ startup_methods.begin(),
+ startup_methods.end());
+ info.AddMethodsForDex(/*startup*/false,
+ /*hot*/false,
+ dex.get(),
+ post_methods.begin(),
+ post_methods.end());
+ for (uint16_t id : hot_methods) {
+ EXPECT_TRUE(info.ContainsHotMethod(MethodReference(dex.get(), id)));
+ EXPECT_TRUE(info.ContainsSampledMethod(/*startup*/true, MethodReference(dex.get(), id)));
+ }
+ for (uint16_t id : startup_methods) {
+ EXPECT_TRUE(info.ContainsSampledMethod(/*startup*/true, MethodReference(dex.get(), id)));
+ }
+ for (uint16_t id : post_methods) {
+ EXPECT_TRUE(info.ContainsSampledMethod(/*startup*/false, MethodReference(dex.get(), id)));
+ }
+ EXPECT_TRUE(info.ContainsSampledMethod(/*startup*/false, MethodReference(dex.get(), 6)));
+ // Check that methods that shouldn't have been touched are OK.
+ EXPECT_FALSE(info.ContainsHotMethod(MethodReference(dex.get(), 0)));
+ EXPECT_FALSE(info.ContainsHotMethod(MethodReference(dex.get(), 2)));
+ EXPECT_FALSE(info.ContainsHotMethod(MethodReference(dex.get(), 4)));
+ EXPECT_FALSE(info.ContainsSampledMethod(/*startup*/false, MethodReference(dex.get(), 1)));
+ EXPECT_FALSE(info.ContainsSampledMethod(/*startup*/true, MethodReference(dex.get(), 4)));
+ EXPECT_FALSE(info.ContainsSampledMethod(/*startup*/true, MethodReference(dex.get(), 6)));
+ }
}
} // namespace art
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index edce9cd..c6c46de 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -194,21 +194,24 @@
GetClassesAndMethodsVisitor(MethodReferenceCollection* hot_methods,
MethodReferenceCollection* sampled_methods,
TypeReferenceCollection* resolved_classes,
- uint32_t hot_method_sample_threshold)
+ uint32_t hot_method_sample_threshold,
+ bool profile_boot_class_path)
: hot_methods_(hot_methods),
sampled_methods_(sampled_methods),
resolved_classes_(resolved_classes),
- hot_method_sample_threshold_(hot_method_sample_threshold) {}
+ hot_method_sample_threshold_(hot_method_sample_threshold),
+ profile_boot_class_path_(profile_boot_class_path) {}
virtual bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
if (klass->IsProxyClass() ||
klass->IsArrayClass() ||
+ klass->IsPrimitive() ||
!klass->IsResolved() ||
klass->IsErroneousResolved() ||
- klass->GetClassLoader() == nullptr) {
+ (!profile_boot_class_path_ && klass->GetClassLoader() == nullptr)) {
return true;
}
- DCHECK(klass->GetDexCache() != nullptr) << klass->PrettyClass();
+ CHECK(klass->GetDexCache() != nullptr) << klass->PrettyClass();
resolved_classes_->AddReference(&klass->GetDexFile(), klass->GetDexTypeIndex());
for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) {
if (!method.IsNative()) {
@@ -235,6 +238,7 @@
MethodReferenceCollection* const sampled_methods_;
TypeReferenceCollection* const resolved_classes_;
uint32_t hot_method_sample_threshold_;
+ const bool profile_boot_class_path_;
};
void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
@@ -263,7 +267,8 @@
GetClassesAndMethodsVisitor visitor(&hot_methods,
&startup_methods,
&resolved_classes,
- hot_threshold);
+ hot_threshold,
+ options_.GetProfileBootClassPath());
runtime->GetClassLinker()->VisitClasses(&visitor);
}
}
@@ -283,21 +288,23 @@
for (const auto& pair : hot_methods.GetMap()) {
const DexFile* const dex_file = pair.first;
if (locations.find(dex_file->GetBaseLocation()) != locations.end()) {
- cached_info->AddSampledMethodsForDex(/*startup*/ true,
- dex_file,
- pair.second.begin(),
- pair.second.end());
- // Adding hot methods is a bit slow, TODO: optimize.
- cached_info->AddHotMethodsForDex(dex_file, pair.second.begin(), pair.second.end());
+ const MethodReferenceCollection::IndexVector& indices = pair.second;
+ cached_info->AddMethodsForDex(/*startup*/ true,
+ /*hot*/ true,
+ dex_file,
+ indices.begin(),
+ indices.end());
}
}
for (const auto& pair : startup_methods.GetMap()) {
const DexFile* const dex_file = pair.first;
if (locations.find(dex_file->GetBaseLocation()) != locations.end()) {
- cached_info->AddSampledMethodsForDex(/*startup*/ true,
- dex_file,
- pair.second.begin(),
- pair.second.end());
+ const MethodReferenceCollection::IndexVector& indices = pair.second;
+ cached_info->AddMethodsForDex(/*startup*/ true,
+ /*hot*/ false,
+ dex_file,
+ indices.begin(),
+ indices.end());
}
}
for (const auto& pair : resolved_classes.GetMap()) {
@@ -469,24 +476,49 @@
const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
const std::vector<std::string>& code_paths) {
+ Runtime* const runtime = Runtime::Current();
DCHECK(options.IsEnabled());
- DCHECK(Runtime::Current()->GetJit() != nullptr);
+ DCHECK(runtime->GetJit() != nullptr);
DCHECK(!output_filename.empty());
DCHECK(jit_code_cache != nullptr);
std::vector<std::string> code_paths_to_profile;
-
for (const std::string& location : code_paths) {
if (ShouldProfileLocation(location)) {
code_paths_to_profile.push_back(location);
}
}
+
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ // Support getting profile samples for the boot class path. This will be used to generate the boot
+ // image profile. The intention is to use this code to generate to boot image but not use it in
+ // production. b/37966211
+ if (options.GetProfileBootClassPath()) {
+ std::set<std::string> code_paths_keys;
+ for (const std::string& location : code_paths) {
+ code_paths_keys.insert(ProfileCompilationInfo::GetProfileDexFileKey(location));
+ }
+ for (const DexFile* dex_file : runtime->GetClassLinker()->GetBootClassPath()) {
+ // Don't check ShouldProfileLocation since the boot class path may be speed compiled.
+ const std::string& location = dex_file->GetLocation();
+ const std::string key = ProfileCompilationInfo::GetProfileDexFileKey(location);
+ VLOG(profiler) << "Registering boot dex file " << location;
+ if (code_paths_keys.find(key) != code_paths_keys.end()) {
+ LOG(WARNING) << "Boot class path location key conflicts with code path " << location;
+ } else if (instance_ == nullptr) {
+ // Only add the boot class path once since Start may be called multiple times for secondary
+ // dexes.
+ // We still do the collision check above. This handles any secondary dexes that conflict
+ // with the boot class path dex files.
+ code_paths_to_profile.push_back(location);
+ }
+ }
+ }
if (code_paths_to_profile.empty()) {
VLOG(profiler) << "No code paths should be profiled.";
return;
}
- MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
if (instance_ != nullptr) {
// If we already have an instance, make sure it uses the same jit_code_cache.
// This may be called multiple times via Runtime::registerAppInfo (e.g. for
diff --git a/runtime/jit/profile_saver_options.h b/runtime/jit/profile_saver_options.h
index 44550f4..251227e 100644
--- a/runtime/jit/profile_saver_options.h
+++ b/runtime/jit/profile_saver_options.h
@@ -40,7 +40,8 @@
min_classes_to_save_(kMinClassesToSave),
min_notification_before_wake_(kMinNotificationBeforeWake),
max_notification_before_wake_(kMaxNotificationBeforeWake),
- profile_path_("") {}
+ profile_path_(""),
+ profile_boot_class_path_(false) {}
ProfileSaverOptions(
bool enabled,
@@ -51,8 +52,9 @@
uint32_t min_classes_to_save,
uint32_t min_notification_before_wake,
uint32_t max_notification_before_wake,
- const std::string& profile_path):
- enabled_(enabled),
+ const std::string& profile_path,
+ bool profile_boot_class_path)
+ : enabled_(enabled),
min_save_period_ms_(min_save_period_ms),
save_resolved_classes_delay_ms_(save_resolved_classes_delay_ms),
hot_startup_method_samples_(hot_startup_method_samples),
@@ -60,7 +62,8 @@
min_classes_to_save_(min_classes_to_save),
min_notification_before_wake_(min_notification_before_wake),
max_notification_before_wake_(max_notification_before_wake),
- profile_path_(profile_path) {}
+ profile_path_(profile_path),
+ profile_boot_class_path_(profile_boot_class_path) {}
bool IsEnabled() const {
return enabled_;
@@ -97,6 +100,9 @@
std::string GetProfilePath() const {
return profile_path_;
}
+ bool GetProfileBootClassPath() const {
+ return profile_boot_class_path_;
+ }
friend std::ostream & operator<<(std::ostream &os, const ProfileSaverOptions& pso) {
os << "enabled_" << pso.enabled_
@@ -106,7 +112,8 @@
<< ", min_methods_to_save_" << pso.min_methods_to_save_
<< ", min_classes_to_save_" << pso.min_classes_to_save_
<< ", min_notification_before_wake_" << pso.min_notification_before_wake_
- << ", max_notification_before_wake_" << pso.max_notification_before_wake_;
+ << ", max_notification_before_wake_" << pso.max_notification_before_wake_
+ << ", profile_boot_class_path_" << pso.profile_boot_class_path_;
return os;
}
@@ -121,6 +128,7 @@
uint32_t min_notification_before_wake_;
uint32_t max_notification_before_wake_;
std::string profile_path_;
+ bool profile_boot_class_path_;
};
} // namespace art
diff --git a/runtime/openjdkjvmti/ti_stack.cc b/runtime/openjdkjvmti/ti_stack.cc
index 22da2d2..550b972 100644
--- a/runtime/openjdkjvmti/ti_stack.cc
+++ b/runtime/openjdkjvmti/ti_stack.cc
@@ -59,13 +59,18 @@
namespace openjdkjvmti {
+template <typename FrameFn>
struct GetStackTraceVisitor : public art::StackVisitor {
GetStackTraceVisitor(art::Thread* thread_in,
size_t start_,
- size_t stop_)
+ size_t stop_,
+ FrameFn fn_)
: StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ fn(fn_),
start(start_),
stop(stop_) {}
+ GetStackTraceVisitor(const GetStackTraceVisitor&) = default;
+ GetStackTraceVisitor(GetStackTraceVisitor&&) = default;
bool VisitFrame() REQUIRES_SHARED(art::Locks::mutator_lock_) {
art::ArtMethod* m = GetMethod();
@@ -81,7 +86,7 @@
jlong dex_location = (dex_pc == art::DexFile::kDexNoIndex) ? -1 : static_cast<jlong>(dex_pc);
jvmtiFrameInfo info = { id, dex_location };
- frames.push_back(info);
+ fn(info);
if (stop == 1) {
return false; // We're done.
@@ -95,24 +100,34 @@
return true;
}
- std::vector<jvmtiFrameInfo> frames;
+ FrameFn fn;
size_t start;
size_t stop;
};
-struct GetStackTraceClosure : public art::Closure {
+template <typename FrameFn>
+GetStackTraceVisitor<FrameFn> MakeStackTraceVisitor(art::Thread* thread_in,
+ size_t start,
+ size_t stop,
+ FrameFn fn) {
+ return GetStackTraceVisitor<FrameFn>(thread_in, start, stop, fn);
+}
+
+struct GetStackTraceVectorClosure : public art::Closure {
public:
- GetStackTraceClosure(size_t start, size_t stop)
+ GetStackTraceVectorClosure(size_t start, size_t stop)
: start_input(start),
stop_input(stop),
start_result(0),
stop_result(0) {}
void Run(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
- GetStackTraceVisitor visitor(self, start_input, stop_input);
- visitor.WalkStack(false);
+ auto frames_fn = [&](jvmtiFrameInfo info) {
+ frames.push_back(info);
+ };
+ auto visitor = MakeStackTraceVisitor(self, start_input, stop_input, frames_fn);
+ visitor.WalkStack(/* include_transitions */ false);
- frames.swap(visitor.frames);
start_result = visitor.start;
stop_result = visitor.stop;
}
@@ -163,6 +178,33 @@
return ERR(NONE);
}
+struct GetStackTraceDirectClosure : public art::Closure {
+ public:
+ GetStackTraceDirectClosure(jvmtiFrameInfo* frame_buffer_, size_t start, size_t stop)
+ : frame_buffer(frame_buffer_),
+ start_input(start),
+ stop_input(stop),
+ index(0) {
+ DCHECK_GE(start_input, 0u);
+ }
+
+ void Run(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ auto frames_fn = [&](jvmtiFrameInfo info) {
+ frame_buffer[index] = info;
+ ++index;
+ };
+ auto visitor = MakeStackTraceVisitor(self, start_input, stop_input, frames_fn);
+ visitor.WalkStack(/* include_transitions */ false);
+ }
+
+ jvmtiFrameInfo* frame_buffer;
+
+ const size_t start_input;
+ const size_t stop_input;
+
+ size_t index = 0;
+};
+
static jvmtiError GetThread(JNIEnv* env, jthread java_thread, art::Thread** thread) {
if (java_thread == nullptr) {
*thread = art::Thread::Current();
@@ -220,8 +262,20 @@
return ERR(NONE);
}
- GetStackTraceClosure closure(start_depth >= 0 ? static_cast<size_t>(start_depth) : 0,
- start_depth >= 0 ? static_cast<size_t>(max_frame_count) : 0);
+ if (start_depth >= 0) {
+ // Fast path: Regular order of stack trace. Fill into the frame_buffer directly.
+ GetStackTraceDirectClosure closure(frame_buffer,
+ static_cast<size_t>(start_depth),
+ static_cast<size_t>(max_frame_count));
+ thread->RequestSynchronousCheckpoint(&closure);
+ *count_ptr = static_cast<jint>(closure.index);
+ if (closure.index < static_cast<size_t>(start_depth)) {
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ return ERR(NONE);
+ }
+
+ GetStackTraceVectorClosure closure(0, 0);
thread->RequestSynchronousCheckpoint(&closure);
return TranslateFrameVector(closure.frames,
@@ -232,42 +286,6 @@
count_ptr);
}
-struct GetAllStackTraceClosure : public art::Closure {
- public:
- explicit GetAllStackTraceClosure(size_t stop)
- : start_input(0),
- stop_input(stop),
- frames_lock("GetAllStackTraceGuard", art::LockLevel::kAbortLock),
- start_result(0),
- stop_result(0) {}
-
- void Run(art::Thread* self)
- OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!frames_lock) {
- // self should be live here (so it could be suspended). No need to filter.
-
- art::Thread* current = art::Thread::Current();
- std::vector<jvmtiFrameInfo> self_frames;
-
- GetStackTraceVisitor visitor(self, start_input, stop_input);
- visitor.WalkStack(false);
-
- self_frames.swap(visitor.frames);
-
- art::MutexLock mu(current, frames_lock);
- frames.emplace(self, self_frames);
- }
-
- const size_t start_input;
- const size_t stop_input;
-
- art::Mutex frames_lock;
- std::unordered_map<art::Thread*, std::vector<jvmtiFrameInfo>> frames GUARDED_BY(frames_lock);
- size_t start_result;
- size_t stop_result;
-};
-
-
-
jvmtiError StackUtil::GetAllStackTraces(jvmtiEnv* env,
jint max_frame_count,
jvmtiStackInfo** stack_info_ptr,
@@ -300,7 +318,7 @@
continue;
}
- GetStackTraceClosure closure(0u, static_cast<size_t>(max_frame_count));
+ GetStackTraceVectorClosure closure(0u, static_cast<size_t>(max_frame_count));
thread->RequestSynchronousCheckpoint(&closure);
threads.push_back(thread);
@@ -460,7 +478,7 @@
for (size_t index = 0; index != handles.size(); ++index) {
if (peer == handles[index].Get()) {
// Found the thread.
- GetStackTraceClosure closure(0u, static_cast<size_t>(max_frame_count));
+ GetStackTraceVectorClosure closure(0u, static_cast<size_t>(max_frame_count));
thread->RequestSynchronousCheckpoint(&closure);
threads.push_back(thread);
diff --git a/runtime/stack.h b/runtime/stack.h
index 8c74a8c..fd86f5d 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -532,6 +532,8 @@
public:
virtual ~StackVisitor() {}
+ StackVisitor(const StackVisitor&) = default;
+ StackVisitor(StackVisitor&&) = default;
// Return 'true' if we should continue to visit more frames, 'false' to stop.
virtual bool VisitFrame() REQUIRES_SHARED(Locks::mutator_lock_) = 0;
diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc
index 019ddad..0bdbade 100644
--- a/test/595-profile-saving/profile-saving.cc
+++ b/test/595-profile-saving/profile-saving.cc
@@ -22,63 +22,41 @@
#include "jni.h"
#include "method_reference.h"
#include "mirror/class-inl.h"
+#include "mirror/executable.h"
#include "oat_file_assistant.h"
#include "oat_file_manager.h"
#include "scoped_thread_state_change-inl.h"
#include "ScopedUtfChars.h"
-#include "stack.h"
#include "thread.h"
namespace art {
namespace {
-class CreateProfilingInfoVisitor : public StackVisitor {
- public:
- explicit CreateProfilingInfoVisitor(Thread* thread, const char* method_name)
- REQUIRES_SHARED(Locks::mutator_lock_)
- : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
- method_name_(method_name) {}
-
- bool VisitFrame() REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtMethod* m = GetMethod();
- std::string m_name(m->GetName());
-
- if (m_name.compare(method_name_) == 0) {
- ProfilingInfo::Create(Thread::Current(), m, /* retry_allocation */ true);
- method_index_ = m->GetDexMethodIndex();
- return false;
- }
- return true;
- }
-
- int method_index_ = -1;
- const char* const method_name_;
-};
-
-extern "C" JNIEXPORT jint JNICALL Java_Main_ensureProfilingInfo(JNIEnv* env,
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfilingInfo(JNIEnv* env,
jclass,
- jstring method_name) {
- ScopedUtfChars chars(env, method_name);
- CHECK(chars.c_str() != nullptr);
- ScopedObjectAccess soa(Thread::Current());
- CreateProfilingInfoVisitor visitor(soa.Self(), chars.c_str());
- visitor.WalkStack();
- return visitor.method_index_;
+ jobject method) {
+ CHECK(method != nullptr);
+ ScopedObjectAccess soa(env);
+ ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method);
+ ProfilingInfo::Create(soa.Self(), exec->GetArtMethod(), /* retry_allocation */ true);
}
extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfileProcessing(JNIEnv*, jclass) {
ProfileSaver::ForceProcessProfiles();
}
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(
- JNIEnv* env, jclass cls, jstring filename, jint method_index) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(JNIEnv* env,
+ jclass,
+ jstring filename,
+ jobject method) {
ScopedUtfChars filename_chars(env, filename);
CHECK(filename_chars.c_str() != nullptr);
- ScopedObjectAccess soa(Thread::Current());
- const DexFile* dex_file = soa.Decode<mirror::Class>(cls)->GetDexCache()->GetDexFile();
+ ScopedObjectAccess soa(env);
+ ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method);
+ ArtMethod* art_method = exec->GetArtMethod();
return ProfileSaver::HasSeenMethod(std::string(filename_chars.c_str()),
- dex_file,
- static_cast<uint16_t>(method_index));
+ art_method->GetDexFile(),
+ art_method->GetDexMethodIndex());
}
} // namespace
diff --git a/test/595-profile-saving/run b/test/595-profile-saving/run
index fce6ac1..055035b 100644
--- a/test/595-profile-saving/run
+++ b/test/595-profile-saving/run
@@ -24,4 +24,5 @@
--runtime-option '-Xcompiler-option --compiler-filter=quicken' \
--runtime-option -Xjitsaveprofilinginfo \
--runtime-option -Xusejit:false \
+ --runtime-option -Xps-profile-boot-class-path \
"${@}"
diff --git a/test/595-profile-saving/src/Main.java b/test/595-profile-saving/src/Main.java
index faf94c4..18c0598 100644
--- a/test/595-profile-saving/src/Main.java
+++ b/test/595-profile-saving/src/Main.java
@@ -31,11 +31,17 @@
VMRuntime.registerAppInfo(file.getPath(),
new String[] {codePath});
- int methodIdx = $opt$noinline$testProfile();
- ensureProfileProcessing();
- if (!presentInProfile(file.getPath(), methodIdx)) {
- throw new RuntimeException("Method with index " + methodIdx + " not in the profile");
+ // Test that the profile saves an app method with a profiling info.
+ Method appMethod = Main.class.getDeclaredMethod("testAddMethodToProfile",
+ File.class, Method.class);
+ testAddMethodToProfile(file, appMethod);
+
+ // Test that the profile saves a boot class path method with a profiling info.
+ Method bootMethod = File.class.getDeclaredMethod("delete");
+ if (bootMethod.getDeclaringClass().getClassLoader() != Object.class.getClassLoader()) {
+ System.out.println("Class loader does not match boot class");
}
+ testAddMethodToProfile(file, bootMethod);
} finally {
if (file != null) {
file.delete();
@@ -43,20 +49,24 @@
}
}
- public static int $opt$noinline$testProfile() {
- if (doThrow) throw new Error();
+ static void testAddMethodToProfile(File file, Method m) {
// Make sure we have a profile info for this method without the need to loop.
- return ensureProfilingInfo("$opt$noinline$testProfile");
+ ensureProfilingInfo(m);
+ // Make sure the profile gets saved.
+ ensureProfileProcessing();
+ // Verify that the profile was saved and contains the method.
+ if (!presentInProfile(file.getPath(), m)) {
+ throw new RuntimeException("Method with index " + m + " not in the profile");
+ }
}
- // Return the dex method index.
- public static native int ensureProfilingInfo(String methodName);
+ // Ensure a method has a profiling info.
+ public static native void ensureProfilingInfo(Method method);
// Ensures the profile saver does its usual processing.
public static native void ensureProfileProcessing();
// Checks if the profiles saver knows about the method.
- public static native boolean presentInProfile(String profile, int methodIdx);
+ public static native boolean presentInProfile(String profile, Method method);
- public static boolean doThrow = false;
private static final String TEMP_FILE_NAME_PREFIX = "dummy";
private static final String TEMP_FILE_NAME_SUFFIX = "-file";
diff --git a/test/knownfailures.json b/test/knownfailures.json
index f515226..67ca316 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -706,13 +706,6 @@
"description": "Test disabled due to redefine-stress disabling intrinsics which changes the trace output slightly."
},
{
- "tests": "137-cfi",
- "description": [ "ASan is reporting out-of-bounds reads in libunwind."],
- "variant": "host",
- "env_vars": {"SANITIZE_HOST": "address"},
- "bug": "b/62350406"
- },
- {
"tests": ["137-cfi", "629-vdex-speed"],
"description": [ "Tests require speed compilation which is no longer the default for",
"no-prebuild or no-image configs."],
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 963efa4..bf7692a 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -30,8 +30,13 @@
out_dir=${OUT_DIR}
fi
+using_jack=true
+if [[ $ANDROID_COMPILE_WITH_JACK == false ]]; then
+ using_jack=false
+fi
+
java_libraries_dir=${out_dir}/target/common/obj/JAVA_LIBRARIES
-common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests mockito-target ${out_dir}/host/linux-x86/bin/jack"
+common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests mockito-target"
mode="target"
j_arg="-j$(nproc)"
showcommands=
@@ -58,6 +63,10 @@
fi
done
+if $using_jack; then
+ common_targets="$common_targets ${out_dir}/host/linux-x86/bin/jack"
+fi
+
if [[ $mode == "host" ]]; then
make_command="make $j_arg $showcommands build-art-host-tests $common_targets"
make_command+=" ${out_dir}/host/linux-x86/lib/libjavacoretests.so "
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index d48d857..f742767 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -23,10 +23,24 @@
ANDROID_HOST_OUT=${OUT_DIR-$ANDROID_BUILD_TOP/out}/host/linux-x86
fi
-# Jar containing all the tests.
-test_jack=${ANDROID_HOST_OUT}/../common/obj/JAVA_LIBRARIES/apache-harmony-jdwp-tests-hostdex_intermediates/classes.jack
+using_jack=true
+if [[ $ANDROID_COMPILE_WITH_JACK == false ]]; then
+ using_jack=false
+fi
-if [ ! -f $test_jack ]; then
+function jlib_suffix {
+ local str=$1
+ local suffix="jar"
+ if $using_jack; then
+ suffix="jack"
+ fi
+ echo "$str.$suffix"
+}
+
+# Jar containing all the tests.
+test_jar=$(jlib_suffix "${ANDROID_HOST_OUT}/../common/obj/JAVA_LIBRARIES/apache-harmony-jdwp-tests-hostdex_intermediates/classes")
+
+if [ ! -f $test_jar ]; then
echo "Before running, you must build jdwp tests and vogar:" \
"make apache-harmony-jdwp-tests-hostdex vogar"
exit 1
@@ -147,6 +161,12 @@
art_debugee="$art_debugee -verbose:jdwp"
fi
+if $using_jack; then
+ toolchain_args="--toolchain jack --language JN --jack-arg -g"
+else
+ toolchain_args="--toolchain jdk --language CUR"
+fi
+
# Run the tests using vogar.
vogar $vm_command \
$vm_args \
@@ -160,10 +180,9 @@
--vm-arg -Djpda.settings.waitingTime=$jdwp_test_timeout \
--vm-arg -Djpda.settings.transportAddress=127.0.0.1:55107 \
--vm-arg -Djpda.settings.debuggeeJavaPath="$art_debugee $image $debuggee_args" \
- --classpath $test_jack \
- --toolchain jack --language JN \
+ --classpath "$test_jar" \
+ $toolchain_args \
--vm-arg -Xcompiler-option --vm-arg --debuggable \
- --jack-arg -g \
$test
vogar_exit_status=$?
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index b860a62..f9f3754 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -25,10 +25,26 @@
JAVA_LIBRARIES=${ANDROID_PRODUCT_OUT}/../../common/obj/JAVA_LIBRARIES
fi
+using_jack=true
+if [[ $ANDROID_COMPILE_WITH_JACK == false ]]; then
+ using_jack=false
+fi
+
+function classes_jar_path {
+ local var="$1"
+ local suffix="jar"
+
+ if $using_jack; then
+ suffix="jack"
+ fi
+
+ echo "${JAVA_LIBRARIES}/${var}_intermediates/classes.${suffix}"
+}
+
function cparg {
for var
do
- printf -- "--classpath ${JAVA_LIBRARIES}/${var}_intermediates/classes.jack ";
+ printf -- "--classpath $(classes_jar_path "$var") ";
done
}
@@ -36,7 +52,7 @@
for lib in $DEPS
do
- if [ ! -f "${JAVA_LIBRARIES}/${lib}_intermediates/classes.jack" ]; then
+ if [[ ! -f "$(classes_jar_path "$lib")" ]]; then
echo "${lib} is missing. Before running, you must run art/tools/buildbot-build.sh"
exit 1
fi
@@ -122,8 +138,12 @@
# the default timeout.
vogar_args="$vogar_args --timeout 480"
-# Use Jack with "1.8" configuration.
-vogar_args="$vogar_args --toolchain jack --language JO"
+# Switch between using jack or javac+desugar+dx
+if $using_jack; then
+ vogar_args="$vogar_args --toolchain jack --language JO"
+else
+ vogar_args="$vogar_args --toolchain jdk --language CUR"
+fi
# JIT settings.
if $use_jit; then