Merge "Reduce memory lost by ArenaAllocator for large allocations."
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 2294ddb..dde3cdb 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -363,7 +363,12 @@
ifndef LIBART_IMG_TARGET_BASE_ADDRESS
$(error LIBART_IMG_TARGET_BASE_ADDRESS unset)
endif
-ART_TARGET_CFLAGS += $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS)
+# The ART_TARGET_ANDROID macro is passed to target builds, which check
+# against it instead of against __ANDROID__ (which is provided by target
+# toolchains).
+ART_TARGET_CFLAGS += $(art_cflags) -DART_TARGET -DART_TARGET_ANDROID \
+ -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS) \
+
ART_TARGET_CFLAGS += $(art_target_cflags)
ART_TARGET_ASFLAGS += $(art_asflags)
diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc
index 2ba3af5..866bf43 100644
--- a/compiler/debug/dwarf/dwarf_test.cc
+++ b/compiler/debug/dwarf/dwarf_test.cc
@@ -27,7 +27,7 @@
namespace dwarf {
// Run the tests only on host since we need objdump.
-#ifndef __ANDROID__
+#ifndef ART_TARGET_ANDROID
constexpr CFIFormat kCFIFormat = DW_DEBUG_FRAME_FORMAT;
@@ -341,7 +341,7 @@
CheckObjdumpOutput(is64bit, "-W");
}
-#endif // __ANDROID__
+#endif // ART_TARGET_ANDROID
} // namespace dwarf
} // namespace art
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index c8dfc93..5de9842 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -171,7 +171,7 @@
size_t thread_count = compiler_driver_->GetThreadCount();
if (compiler_options_->GetGenerateDebugInfo()) {
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
const char* prefix = "/data/misc/trace";
#else
const char* prefix = "/tmp";
diff --git a/compiler/jni/jni_cfi_test.cc b/compiler/jni/jni_cfi_test.cc
index 05c85e0..371019a 100644
--- a/compiler/jni/jni_cfi_test.cc
+++ b/compiler/jni/jni_cfi_test.cc
@@ -29,7 +29,7 @@
namespace art {
// Run the tests only on host.
-#ifndef __ANDROID__
+#ifndef ART_TARGET_ANDROID
class JNICFITest : public CFITest {
public:
@@ -94,6 +94,6 @@
TEST_ISA(kMips)
TEST_ISA(kMips64)
-#endif // __ANDROID__
+#endif // ART_TARGET_ANDROID
} // namespace art
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 836bcfa..ff4b9a7 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -795,6 +795,11 @@
return false;
}
+ if (!method->IsCompilable()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method)
+ << " has soft failures un-handled by the compiler, so it cannot be inlined";
+ }
+
if (!method->GetDeclaringClass()->IsVerified()) {
uint16_t class_def_idx = method->GetDeclaringClass()->GetDexClassDefIndex();
if (Runtime::Current()->UseJit() ||
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index cf973aa..1524e1e 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -385,6 +385,92 @@
locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
}
+static void GenBitCount(LocationSummary* locations,
+ const Primitive::Type type,
+ Mips64Assembler* assembler) {
+ GpuRegister out = locations->Out().AsRegister<GpuRegister>();
+ GpuRegister in = locations->InAt(0).AsRegister<GpuRegister>();
+
+ DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+ // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
+ //
+ // A generalization of the best bit counting method to integers of
+ // bit-widths up to 128 (parameterized by type T) is this:
+ //
+ // v = v - ((v >> 1) & (T)~(T)0/3); // temp
+ // v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp
+ // v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp
+ // c = (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * BITS_PER_BYTE; // count
+ //
+ // For comparison, for 32-bit quantities, this algorithm can be executed
+ // using 20 MIPS instructions (the calls to LoadConst32() generate two
+ // machine instructions each for the values being used in this algorithm).
+ // A(n unrolled) loop-based algorithm requires 25 instructions.
+ //
+ // For a 64-bit operand this can be performed in 24 instructions compared
+ // to a(n unrolled) loop based algorithm which requires 38 instructions.
+ //
+ // There are algorithms which are faster in the cases where very few
+ // bits are set but the algorithm here attempts to minimize the total
+ // number of instructions executed even when a large number of bits
+ // are set.
+
+ if (type == Primitive::kPrimInt) {
+ __ Srl(TMP, in, 1);
+ __ LoadConst32(AT, 0x55555555);
+ __ And(TMP, TMP, AT);
+ __ Subu(TMP, in, TMP);
+ __ LoadConst32(AT, 0x33333333);
+ __ And(out, TMP, AT);
+ __ Srl(TMP, TMP, 2);
+ __ And(TMP, TMP, AT);
+ __ Addu(TMP, out, TMP);
+ __ Srl(out, TMP, 4);
+ __ Addu(out, out, TMP);
+ __ LoadConst32(AT, 0x0F0F0F0F);
+ __ And(out, out, AT);
+ __ LoadConst32(TMP, 0x01010101);
+ __ MulR6(out, out, TMP);
+ __ Srl(out, out, 24);
+ } else if (type == Primitive::kPrimLong) {
+ __ Dsrl(TMP, in, 1);
+ __ LoadConst64(AT, 0x5555555555555555L);
+ __ And(TMP, TMP, AT);
+ __ Dsubu(TMP, in, TMP);
+ __ LoadConst64(AT, 0x3333333333333333L);
+ __ And(out, TMP, AT);
+ __ Dsrl(TMP, TMP, 2);
+ __ And(TMP, TMP, AT);
+ __ Daddu(TMP, out, TMP);
+ __ Dsrl(out, TMP, 4);
+ __ Daddu(out, out, TMP);
+ __ LoadConst64(AT, 0x0F0F0F0F0F0F0F0FL);
+ __ And(out, out, AT);
+ __ LoadConst64(TMP, 0x0101010101010101L);
+ __ Dmul(out, out, TMP);
+ __ Dsrl32(out, out, 24);
+ }
+}
+
+// int java.lang.Integer.bitCount(int)
+void IntrinsicLocationsBuilderMIPS64::VisitIntegerBitCount(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitIntegerBitCount(HInvoke* invoke) {
+ GenBitCount(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
+}
+
+// int java.lang.Long.bitCount(long)
+void IntrinsicLocationsBuilderMIPS64::VisitLongBitCount(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitLongBitCount(HInvoke* invoke) {
+ GenBitCount(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
+}
+
static void MathAbsFP(LocationSummary* locations, bool is64bit, Mips64Assembler* assembler) {
FpuRegister in = locations->InAt(0).AsFpuRegister<FpuRegister>();
FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
@@ -1693,9 +1779,6 @@
GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
}
-UNIMPLEMENTED_INTRINSIC(MIPS64, IntegerBitCount)
-UNIMPLEMENTED_INTRINSIC(MIPS64, LongBitCount)
-
UNIMPLEMENTED_INTRINSIC(MIPS64, MathRoundDouble)
UNIMPLEMENTED_INTRINSIC(MIPS64, MathRoundFloat)
diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc
index 400686d..a6d234d 100644
--- a/compiler/optimizing/optimizing_cfi_test.cc
+++ b/compiler/optimizing/optimizing_cfi_test.cc
@@ -32,7 +32,7 @@
namespace art {
// Run the tests only on host.
-#ifndef __ANDROID__
+#ifndef ART_TARGET_ANDROID
class OptimizingCFITest : public CFITest {
public:
@@ -241,6 +241,6 @@
Check(kMips64, "kMips64_adjust", expected_asm, expected_cfi);
}
-#endif // __ANDROID__
+#endif // ART_TARGET_ANDROID
} // namespace art
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 3670ce2..37197af 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -769,15 +769,6 @@
return codegen.release();
}
-static bool CanHandleVerificationFailure(const VerifiedMethod* verified_method) {
- // For access errors the compiler will use the unresolved helpers (e.g. HInvokeUnresolved).
- uint32_t unresolved_mask = verifier::VerifyError::VERIFY_ERROR_NO_CLASS
- | verifier::VerifyError::VERIFY_ERROR_ACCESS_CLASS
- | verifier::VerifyError::VERIFY_ERROR_ACCESS_FIELD
- | verifier::VerifyError::VERIFY_ERROR_ACCESS_METHOD;
- return (verified_method->GetEncounteredVerificationFailures() & (~unresolved_mask)) == 0;
-}
-
CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
uint32_t access_flags,
InvokeType invoke_type,
@@ -792,7 +783,8 @@
const VerifiedMethod* verified_method = compiler_driver->GetVerifiedMethod(&dex_file, method_idx);
DCHECK(!verified_method->HasRuntimeThrow());
if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file)
- || CanHandleVerificationFailure(verified_method)) {
+ || verifier::MethodVerifier::CanCompilerHandleVerificationFailure(
+ verified_method->GetEncounteredVerificationFailures())) {
ArenaAllocator arena(Runtime::Current()->GetArenaPool());
CodeVectorAllocator code_allocator(&arena);
std::unique_ptr<CodeGenerator> codegen(
@@ -865,6 +857,7 @@
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
method->GetDeclaringClass()->GetClassLoader()));
Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
+ DCHECK(method->IsCompilable());
jobject jclass_loader = class_loader.ToJObject();
const DexFile* dex_file = method->GetDexFile();
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 3a4c23d..2c73fb8 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -37,9 +37,6 @@
const FixupId end_id = assembler->fixups_.size();
Fixup* fixups = assembler->fixups_.data();
for (FixupId fixup_id = 0u; fixup_id != end_id; ++fixup_id) {
- if (!fixups[fixup_id].CanExpand()) {
- continue;
- }
uint32_t target = fixups[fixup_id].target_;
if (target > fixups[fixup_id].location_) {
for (FixupId id = fixup_id + 1u; id != end_id && fixups[id].location_ < target; ++id) {
@@ -65,9 +62,6 @@
assembler->fixup_dependents_.resize(number_of_dependents);
FixupId* dependents = assembler->fixup_dependents_.data();
for (FixupId fixup_id = 0u; fixup_id != end_id; ++fixup_id) {
- if (!fixups[fixup_id].CanExpand()) {
- continue;
- }
uint32_t target = fixups[fixup_id].target_;
if (target > fixups[fixup_id].location_) {
for (FixupId id = fixup_id + 1u; id != end_id && fixups[id].location_ < target; ++id) {
@@ -121,7 +115,6 @@
std::deque<FixupId>* fixups_to_recalculate) {
uint32_t adjustment = fixup->AdjustSizeIfNeeded(*current_code_size);
if (adjustment != 0u) {
- DCHECK(fixup->CanExpand());
*current_code_size += adjustment;
for (FixupId dependent_id : fixup->Dependents(*this)) {
Fixup* dependent = GetFixup(dependent_id);
@@ -2553,19 +2546,9 @@
}
} else {
branch_type = Fixup::kUnconditional; // B.
- // The T2 encoding offset is `SignExtend(imm11:'0', 32)` and there is a PC adjustment of 4.
- static constexpr size_t kMaxT2BackwardDistance = (1u << 11) - 4u;
- if (!use32bit && label->IsBound() && pc - label->Position() > kMaxT2BackwardDistance) {
- use32bit = true;
- }
}
} else {
branch_type = Fixup::kConditional; // B<cond>.
- // The T1 encoding offset is `SignExtend(imm8:'0', 32)` and there is a PC adjustment of 4.
- static constexpr size_t kMaxT1BackwardDistance = (1u << 8) - 4u;
- if (!use32bit && label->IsBound() && pc - label->Position() > kMaxT1BackwardDistance) {
- use32bit = true;
- }
}
Fixup::Size size = use32bit ? Fixup::kBranch32Bit : Fixup::kBranch16Bit;
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index bc5b708..111a6b0 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -538,20 +538,6 @@
return GetType() >= kLoadLiteralNarrow;
}
- // Returns whether the Fixup can expand from the original size.
- bool CanExpand() const {
- switch (GetOriginalSize()) {
- case kBranch32Bit:
- case kCbxz48Bit:
- case kLiteralFar:
- case kLiteralAddrFar:
- case kLongOrFPLiteralFar:
- return false;
- default:
- return true;
- }
- }
-
Size GetOriginalSize() const {
return original_size_;
}
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index c67cb5a..9c9271d 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -32,7 +32,7 @@
// Include results file (generated manually)
#include "assembler_thumb_test_expected.cc.inc"
-#ifndef __ANDROID__
+#ifndef ART_TARGET_ANDROID
// This controls whether the results are printed to the
// screen or compared against the expected output.
// To generate new expected output, set this to true and
@@ -72,7 +72,7 @@
}
std::string GetToolsDir() {
-#ifndef __ANDROID__
+#ifndef ART_TARGET_ANDROID
// This will only work on the host. There is no as, objcopy or objdump on the device.
static std::string toolsdir;
@@ -89,7 +89,7 @@
}
void DumpAndCheck(std::vector<uint8_t>& code, const char* testname, const char* const* results) {
-#ifndef __ANDROID__
+#ifndef ART_TARGET_ANDROID
static std::string toolsdir = GetToolsDir();
ScratchFile file;
@@ -169,7 +169,7 @@
snprintf(buf, sizeof(buf), "%s.oo", filename);
unlink(buf);
-#endif
+#endif // ART_TARGET_ANDROID
}
#define __ assembler->
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index b19e616..afe9207 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -37,7 +37,7 @@
ASSERT_EQ(static_cast<size_t>(5), buffer.Size());
}
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
static constexpr size_t kRandomIterations = 1000; // Devices might be puny, don't stress them...
#else
static constexpr size_t kRandomIterations = 100000; // Hosts are pretty powerful.
diff --git a/profman/profile_assistant.cc b/profman/profile_assistant.cc
index 58e8a3a..a25460e 100644
--- a/profman/profile_assistant.cc
+++ b/profman/profile_assistant.cc
@@ -21,44 +21,41 @@
namespace art {
-// Minimum number of new methods that profiles must contain to enable recompilation.
+// Minimum number of new methods/classes that profiles
+// must contain to enable recompilation.
static constexpr const uint32_t kMinNewMethodsForCompilation = 10;
+static constexpr const uint32_t kMinNewClassesForCompilation = 10;
ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfilesInternal(
const std::vector<ScopedFlock>& profile_files,
const ScopedFlock& reference_profile_file) {
DCHECK(!profile_files.empty());
- std::vector<ProfileCompilationInfo> new_info(profile_files.size());
- bool should_compile = false;
- // Read the main profile files.
- for (size_t i = 0; i < new_info.size(); i++) {
- if (!new_info[i].Load(profile_files[i].GetFile()->Fd())) {
- LOG(WARNING) << "Could not load profile file at index " << i;
- return kErrorBadProfiles;
- }
- // Do we have enough new profiled methods that will make the compilation worthwhile?
- should_compile |= (new_info[i].GetNumberOfMethods() > kMinNewMethodsForCompilation);
- }
-
- if (!should_compile) {
- return kSkipCompilation;
- }
-
- // Merge information.
ProfileCompilationInfo info;
+ // Load the reference profile.
if (!info.Load(reference_profile_file.GetFile()->Fd())) {
LOG(WARNING) << "Could not load reference profile file";
return kErrorBadProfiles;
}
- for (size_t i = 0; i < new_info.size(); i++) {
- // Merge all data into a single object.
- if (!info.Load(new_info[i])) {
- LOG(WARNING) << "Could not merge profile data at index " << i;
+ // Store the current state of the reference profile before merging with the current profiles.
+ uint32_t number_of_methods = info.GetNumberOfMethods();
+ uint32_t number_of_classes = info.GetNumberOfResolvedClasses();
+
+ // Merge all current profiles.
+ for (size_t i = 0; i < profile_files.size(); i++) {
+ if (!info.Load(profile_files[i].GetFile()->Fd())) {
+ LOG(WARNING) << "Could not load profile file at index " << i;
return kErrorBadProfiles;
}
}
+
+ // Check if there is enough new information added by the current profiles.
+ if (((info.GetNumberOfMethods() - number_of_methods) < kMinNewMethodsForCompilation) &&
+ ((info.GetNumberOfResolvedClasses() - number_of_classes) < kMinNewClassesForCompilation)) {
+ return kSkipCompilation;
+ }
+
// We were successful in merging all profile information. Update the reference profile.
if (!reference_profile_file.GetFile()->ClearContent()) {
PLOG(WARNING) << "Could not clear reference profile file";
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index b0d5df2..462c397 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -29,6 +29,7 @@
void SetupProfile(const std::string& id,
uint32_t checksum,
uint16_t number_of_methods,
+ uint16_t number_of_classes,
const ScratchFile& profile,
ProfileCompilationInfo* info,
uint16_t start_method_index = 0) {
@@ -40,6 +41,10 @@
ASSERT_TRUE(info->AddMethodIndex(dex_location1, dex_location_checksum1, i));
ASSERT_TRUE(info->AddMethodIndex(dex_location2, dex_location_checksum2, i));
}
+ for (uint16_t i = 0; i < number_of_classes; i++) {
+ ASSERT_TRUE(info->AddClassIndex(dex_location1, dex_location_checksum1, i));
+ }
+
ASSERT_TRUE(info->Save(GetFd(profile)));
ASSERT_EQ(0, profile.GetFile()->Flush());
ASSERT_TRUE(profile.GetFile()->ResetOffset());
@@ -89,9 +94,9 @@
const uint16_t kNumberOfMethodsToEnableCompilation = 100;
ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
+ SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
ProfileCompilationInfo info2;
- SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2);
+ SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
// We should advise compilation.
ASSERT_EQ(ProfileAssistant::kCompile,
@@ -102,8 +107,8 @@
ASSERT_TRUE(result.Load(reference_profile_fd));
ProfileCompilationInfo expected;
- ASSERT_TRUE(expected.Load(info1));
- ASSERT_TRUE(expected.Load(info2));
+ ASSERT_TRUE(expected.MergeWith(info1));
+ ASSERT_TRUE(expected.MergeWith(info2));
ASSERT_TRUE(expected.Equals(result));
// The information from profiles must remain the same.
@@ -111,6 +116,35 @@
CheckProfileInfo(profile2, info2);
}
+// TODO(calin): Add more tests for classes.
+TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferencesBecauseOfClasses) {
+ ScratchFile profile1;
+ ScratchFile reference_profile;
+
+ std::vector<int> profile_fds({
+ GetFd(profile1)});
+ int reference_profile_fd = GetFd(reference_profile);
+
+ const uint16_t kNumberOfClassesToEnableCompilation = 100;
+ ProfileCompilationInfo info1;
+ SetupProfile("p1", 1, 0, kNumberOfClassesToEnableCompilation, profile1, &info1);
+
+ // We should advise compilation.
+ ASSERT_EQ(ProfileAssistant::kCompile,
+ ProcessProfiles(profile_fds, reference_profile_fd));
+ // The resulting compilation info must be equal to the merge of the inputs.
+ ProfileCompilationInfo result;
+ ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(result.Load(reference_profile_fd));
+
+ ProfileCompilationInfo expected;
+ ASSERT_TRUE(expected.MergeWith(info1));
+ ASSERT_TRUE(expected.Equals(result));
+
+ // The information from profiles must remain the same.
+ CheckProfileInfo(profile1, info1);
+}
+
TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) {
ScratchFile profile1;
ScratchFile profile2;
@@ -124,15 +158,15 @@
// The new profile info will contain the methods with indices 0-100.
const uint16_t kNumberOfMethodsToEnableCompilation = 100;
ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
+ SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
ProfileCompilationInfo info2;
- SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2);
+ SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
// The reference profile info will contain the methods with indices 50-150.
const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
ProfileCompilationInfo reference_info;
- SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, reference_profile,
+ SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, 0, reference_profile,
&reference_info, kNumberOfMethodsToEnableCompilation / 2);
// We should advise compilation.
@@ -145,9 +179,9 @@
ASSERT_TRUE(result.Load(reference_profile_fd));
ProfileCompilationInfo expected;
- ASSERT_TRUE(expected.Load(info1));
- ASSERT_TRUE(expected.Load(info2));
- ASSERT_TRUE(expected.Load(reference_info));
+ ASSERT_TRUE(expected.MergeWith(info1));
+ ASSERT_TRUE(expected.MergeWith(info2));
+ ASSERT_TRUE(expected.MergeWith(reference_info));
ASSERT_TRUE(expected.Equals(result));
// The information from profiles must remain the same.
@@ -167,9 +201,9 @@
const uint16_t kNumberOfMethodsToSkipCompilation = 1;
ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToSkipCompilation, profile1, &info1);
+ SetupProfile("p1", 1, kNumberOfMethodsToSkipCompilation, 0, profile1, &info1);
ProfileCompilationInfo info2;
- SetupProfile("p2", 2, kNumberOfMethodsToSkipCompilation, profile2, &info2);
+ SetupProfile("p2", 2, kNumberOfMethodsToSkipCompilation, 0, profile2, &info2);
// We should not advise compilation.
ASSERT_EQ(ProfileAssistant::kSkipCompilation,
@@ -207,9 +241,9 @@
const uint16_t kNumberOfMethodsToEnableCompilation = 100;
// Assign different hashes for the same dex file. This will make merging of information to fail.
ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
+ SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
ProfileCompilationInfo info2;
- SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2);
+ SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
// We should fail processing.
ASSERT_EQ(ProfileAssistant::kErrorBadProfiles,
@@ -234,9 +268,9 @@
const uint16_t kNumberOfMethodsToEnableCompilation = 100;
// Assign different hashes for the same dex file. This will make merging of information to fail.
ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
+ SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
ProfileCompilationInfo reference_info;
- SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, reference_profile, &reference_info);
+ SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, 0, reference_profile, &reference_info);
// We should not advise compilation.
ASSERT_TRUE(profile1.GetFile()->ResetOffset());
diff --git a/profman/profman.cc b/profman/profman.cc
index 7c9e449..3e632bc 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -14,12 +14,14 @@
* limitations under the License.
*/
+#include "errno.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h>
+#include <iostream>
#include <string>
#include <vector>
@@ -68,6 +70,9 @@
UsageError("Command: %s", CommandLine().c_str());
UsageError("Usage: profman [options]...");
UsageError("");
+ UsageError(" --dump-info-for=<filename>: dumps the content of the profile file");
+ UsageError(" to standard output in a human readable form.");
+ UsageError("");
UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation.");
UsageError(" Can be specified multiple time, in which case the data from the different");
UsageError(" profiles will be aggregated.");
@@ -117,9 +122,11 @@
const StringPiece option(argv[i]);
const bool log_options = false;
if (log_options) {
- LOG(INFO) << "patchoat: option[" << i << "]=" << argv[i];
+ LOG(INFO) << "profman: option[" << i << "]=" << argv[i];
}
- if (option.starts_with("--profile-file=")) {
+ if (option.starts_with("--dump-info-for=")) {
+ dump_info_for_ = option.substr(strlen("--dump-info-for=")).ToString();
+ } else if (option.starts_with("--profile-file=")) {
profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString());
} else if (option.starts_with("--profile-file-fd=")) {
ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_);
@@ -132,13 +139,23 @@
}
}
- if (profile_files_.empty() && profile_files_fd_.empty()) {
+ bool has_profiles = !profile_files_.empty() || !profile_files_fd_.empty();
+ bool has_reference_profile = !reference_profile_file_.empty() ||
+ (reference_profile_file_fd_ != -1);
+
+ if (!dump_info_for_.empty()) {
+ if (has_profiles || has_reference_profile) {
+ Usage("dump-info-for cannot be specified together with other options");
+ }
+ return;
+ }
+ if (!has_profiles) {
Usage("No profile files specified.");
}
if (!profile_files_.empty() && !profile_files_fd_.empty()) {
Usage("Profile files should not be specified with both --profile-file-fd and --profile-file");
}
- if (!reference_profile_file_.empty() && (reference_profile_file_fd_ != -1)) {
+ if (!has_reference_profile) {
Usage("--reference-profile-file-fd should only be supplied with --profile-file-fd");
}
if (reference_profile_file_.empty() && (reference_profile_file_fd_ == -1)) {
@@ -160,6 +177,27 @@
return result;
}
+ int DumpProfileInfo() {
+ int fd = open(dump_info_for_.c_str(), O_RDWR);
+ if (fd < 0) {
+ std::cerr << "Cannot open " << dump_info_for_ << strerror(errno);
+ return -1;
+ }
+ ProfileCompilationInfo info;
+ if (!info.Load(fd)) {
+ std::cerr << "Cannot load profile info from " << dump_info_for_;
+ return -1;
+ }
+ std::string dump = info.DumpInfo(/*dex_files*/ nullptr);
+ info.Save(fd);
+ std::cout << dump << "\n";
+ return 0;
+ }
+
+ bool ShouldOnlyDumpProfile() {
+ return !dump_info_for_.empty();
+ }
+
private:
static void ParseFdForCollection(const StringPiece& option,
const char* arg_name,
@@ -186,6 +224,7 @@
std::string reference_profile_file_;
int reference_profile_file_fd_;
uint64_t start_ns_;
+ std::string dump_info_for_;
};
// See ProfileAssistant::ProcessingResult for return codes.
@@ -195,6 +234,9 @@
// Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
profman.ParseArgs(argc, argv);
+ if (profman.ShouldOnlyDumpProfile()) {
+ return profman.DumpProfileInfo();
+ }
// Process profile information and assess if we need to do a profile guided compilation.
// This operation involves I/O.
return profman.ProcessProfiles();
diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc
index 51f992b..ffac030 100644
--- a/runtime/arch/arm/instruction_set_features_arm.cc
+++ b/runtime/arch/arm/instruction_set_features_arm.cc
@@ -16,7 +16,7 @@
#include "instruction_set_features_arm.h"
-#if defined(__ANDROID__) && defined(__arm__)
+#if defined(ART_TARGET_ANDROID) && defined(__arm__)
#include <sys/auxv.h>
#include <asm/hwcap.h>
#endif
@@ -166,7 +166,7 @@
bool has_div = false;
bool has_lpae = false;
-#if defined(__ANDROID__) && defined(__arm__)
+#if defined(ART_TARGET_ANDROID) && defined(__arm__)
uint64_t hwcaps = getauxval(AT_HWCAP);
LOG(INFO) << "hwcaps=" << hwcaps;
if ((hwcaps & HWCAP_IDIVT) != 0) {
diff --git a/runtime/arch/instruction_set_features_test.cc b/runtime/arch/instruction_set_features_test.cc
index 99c2d4d..fb38b47 100644
--- a/runtime/arch/instruction_set_features_test.cc
+++ b/runtime/arch/instruction_set_features_test.cc
@@ -18,7 +18,7 @@
#include <gtest/gtest.h>
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
#include "cutils/properties.h"
#endif
@@ -26,7 +26,7 @@
namespace art {
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
#if defined(__aarch64__)
TEST(InstructionSetFeaturesTest, DISABLED_FeaturesFromSystemPropertyVariant) {
LOG(WARNING) << "Test disabled due to no CPP define for A53 erratum 835769";
@@ -111,7 +111,7 @@
}
#endif
-#ifndef __ANDROID__
+#ifndef ART_TARGET_ANDROID
TEST(InstructionSetFeaturesTest, HostFeaturesFromCppDefines) {
std::string error_msg;
std::unique_ptr<const InstructionSetFeatures> default_features(
diff --git a/runtime/art_method.h b/runtime/art_method.h
index d1ef019..08f0285 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -220,6 +220,10 @@
return !IsAbstract() && !IsDefaultConflicting();
}
+ bool IsCompilable() {
+ return (GetAccessFlags() & kAccCompileDontBother) == 0;
+ }
+
// A default conflict method is a special sentinel method that stands for a conflict between
// multiple default methods. It cannot be invoked, throwing an IncompatibleClassChangeError if one
// attempts to do so.
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index 212e5bd..3ee15a2 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -26,7 +26,7 @@
#include "utils.h"
// Headers for LogMessage::LogLine.
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
#include "cutils/log.h"
#else
#include <sys/types.h>
@@ -47,7 +47,7 @@
// Print INTERNAL_FATAL messages directly instead of at destruction time. This only works on the
// host right now: for the device, a stream buf collating output into lines and calling LogLine or
// lower-level logging is necessary.
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
static constexpr bool kPrintInternalFatalDirectly = false;
#else
static constexpr bool kPrintInternalFatalDirectly = !kIsTargetBuild;
@@ -235,7 +235,7 @@
return data_->GetBuffer();
}
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
static const android_LogPriority kLogSeverityToAndroidLogPriority[] = {
ANDROID_LOG_VERBOSE, // NONE, use verbose as stand-in, will never be printed.
ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN,
@@ -251,7 +251,7 @@
return;
}
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
const char* tag = ProgramInvocationShortName();
int priority = kLogSeverityToAndroidLogPriority[static_cast<size_t>(log_severity)];
if (priority == ANDROID_LOG_FATAL) {
@@ -274,7 +274,7 @@
return;
}
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
// Use android_writeLog() to avoid stack-based buffers used by android_printLog().
const char* tag = ProgramInvocationShortName();
int priority = kLogSeverityToAndroidLogPriority[static_cast<size_t>(log_severity)];
@@ -311,7 +311,7 @@
TEMP_FAILURE_RETRY(write(STDERR_FILENO, "] ", 2));
TEMP_FAILURE_RETRY(write(STDERR_FILENO, message, strlen(message)));
TEMP_FAILURE_RETRY(write(STDERR_FILENO, "\n", 1));
-#endif
+#endif // ART_TARGET_ANDROID
}
ScopedLogSeverity::ScopedLogSeverity(LogSeverity level) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 1e7ee65..fa0107a 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4442,7 +4442,20 @@
// We failed to verify, expect either the klass to be erroneous or verification failed at
// compile time.
if (klass->IsErroneous()) {
- CHECK(self->IsExceptionPending());
+ // The class is erroneous. This may be a verifier error, or another thread attempted
+ // verification and/or initialization and failed. We can distinguish those cases by
+ // whether an exception is already pending.
+ if (self->IsExceptionPending()) {
+ // Check that it's a VerifyError.
+ DCHECK_EQ("java.lang.Class<java.lang.VerifyError>",
+ PrettyClass(self->GetException()->GetClass()));
+ } else {
+ // Check that another thread attempted initialization.
+ DCHECK_NE(0, klass->GetClinitThreadId());
+ DCHECK_NE(self->GetTid(), klass->GetClinitThreadId());
+ // Need to rethrow the previous failure now.
+ ThrowEarlierClassFailure(klass.Get(), true);
+ }
VlogClassInitializationFailure(klass);
} else {
CHECK(Runtime::Current()->IsAotCompiler());
@@ -4452,6 +4465,14 @@
} else {
self->AssertNoPendingException();
}
+
+ // A separate thread could have moved us all the way to initialized. A "simple" example
+ // involves a subclass of the current class being initialized at the same time (which
+ // will implicitly initialize the superclass, if scheduled that way). b/28254258
+ DCHECK_NE(mirror::Class::kStatusError, klass->GetStatus());
+ if (klass->IsInitialized()) {
+ return true;
+ }
}
// If the class is kStatusInitializing, either this thread is
diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc
index bd023b3..d9f1507 100644
--- a/runtime/gc/allocation_record.cc
+++ b/runtime/gc/allocation_record.cc
@@ -20,7 +20,7 @@
#include "base/stl_util.h"
#include "stack.h"
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
#include "cutils/properties.h"
#endif
@@ -38,7 +38,7 @@
}
void AllocRecordObjectMap::SetProperties() {
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
// Check whether there's a system property overriding the max number of records.
const char* propertyName = "dalvik.vm.allocTrackerMax";
char allocMaxString[PROPERTY_VALUE_MAX];
@@ -88,7 +88,7 @@
max_stack_depth_ = value;
}
}
-#endif
+#endif // ART_TARGET_ANDROID
}
AllocRecordObjectMap::~AllocRecordObjectMap() {
diff --git a/runtime/gc/allocator/dlmalloc.h b/runtime/gc/allocator/dlmalloc.h
index 50e2622..c07da5d 100644
--- a/runtime/gc/allocator/dlmalloc.h
+++ b/runtime/gc/allocator/dlmalloc.h
@@ -35,7 +35,7 @@
#include "../../external/dlmalloc/malloc.h"
#pragma GCC diagnostic pop
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
// Define dlmalloc routines from bionic that cannot be included directly because of redefining
// symbols from the include above.
extern "C" void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), void* arg);
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index bd84d0d..375d869 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -1021,7 +1021,7 @@
// First mark slots to free in the bulk free bit map without locking the
// size bracket locks. On host, unordered_set is faster than vector + flag.
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
std::vector<Run*> runs;
#else
std::unordered_set<Run*, hash_run, eq_run> runs;
@@ -1088,7 +1088,7 @@
DCHECK_EQ(run->magic_num_, kMagicNum);
// Set the bit in the bulk free bit map.
freed_bytes += run->AddToBulkFreeList(ptr);
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
if (!run->to_be_bulk_freed_) {
run->to_be_bulk_freed_ = true;
runs.push_back(run);
@@ -1103,7 +1103,7 @@
// union the bulk free bit map into the thread-local free bit map
// (for thread-local runs.)
for (Run* run : runs) {
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
DCHECK(run->to_be_bulk_freed_);
run->to_be_bulk_freed_ = false;
#endif
diff --git a/runtime/jdwp/jdwp_adb.cc b/runtime/jdwp/jdwp_adb.cc
index 51952c4..e9d6d07 100644
--- a/runtime/jdwp/jdwp_adb.cc
+++ b/runtime/jdwp/jdwp_adb.cc
@@ -24,7 +24,7 @@
#include "base/stringprintf.h"
#include "jdwp/jdwp_priv.h"
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
#include "cutils/sockets.h"
#endif
@@ -224,7 +224,7 @@
*/
int ret = connect(control_sock_, &control_addr_.controlAddrPlain, control_addr_len_);
if (!ret) {
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
if (!socket_peer_is_trusted(control_sock_)) {
if (shutdown(control_sock_, SHUT_RDWR)) {
PLOG(ERROR) << "trouble shutting down socket";
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index 668d5dc..dbf04fe 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -251,7 +251,7 @@
case kJdwpTransportSocket:
InitSocketTransport(state.get(), options);
break;
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
case kJdwpTransportAndroidAdb:
InitAdbTransport(state.get(), options);
break;
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 2a66847..c36543f 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -119,6 +119,11 @@
memory_use_.PrintMemoryUse(os);
}
+void Jit::DumpForSigQuit(std::ostream& os) {
+ DumpInfo(os);
+ ProfileSaver::DumpInstanceInfo(os);
+}
+
void Jit::AddTimingLogger(const TimingLogger& logger) {
cumulative_timings_.AddLogger(logger);
}
@@ -297,7 +302,7 @@
void Jit::StopProfileSaver() {
if (save_profiling_info_ && ProfileSaver::IsStarted()) {
- ProfileSaver::Stop();
+ ProfileSaver::Stop(dump_info_on_shutdown_);
}
}
@@ -562,7 +567,7 @@
return;
}
- if (method->IsClassInitializer() || method->IsNative()) {
+ if (method->IsClassInitializer() || method->IsNative() || !method->IsCompilable()) {
// We do not want to compile such methods.
return;
}
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index ff3acf6..8198c18 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -127,9 +127,7 @@
const std::string& app_dir);
void StopProfileSaver();
- void DumpForSigQuit(std::ostream& os) REQUIRES(!lock_) {
- DumpInfo(os);
- }
+ void DumpForSigQuit(std::ostream& os) REQUIRES(!lock_);
static void NewTypeLoadedIfUsingJit(mirror::Class* type)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 752d4ba..6b6f5a5 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -887,13 +887,15 @@
}
}
-void JitCodeCache::GetCompiledArtMethods(const std::set<std::string>& dex_base_locations,
- std::vector<ArtMethod*>& methods) {
+void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_locations,
+ std::vector<MethodReference>& methods) {
ScopedTrace trace(__FUNCTION__);
MutexLock mu(Thread::Current(), lock_);
- for (auto it : method_code_map_) {
- if (ContainsElement(dex_base_locations, it.second->GetDexFile()->GetBaseLocation())) {
- methods.push_back(it.second);
+ for (const ProfilingInfo* info : profiling_infos_) {
+ ArtMethod* method = info->GetMethod();
+ const DexFile* dex_file = method->GetDexFile();
+ if (ContainsElement(dex_base_locations, dex_file->GetBaseLocation())) {
+ methods.emplace_back(dex_file, method->GetDexMethodIndex());
}
}
}
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index f31cc51..4df6762 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -26,6 +26,7 @@
#include "gc/accounting/bitmap.h"
#include "gc_root.h"
#include "jni.h"
+#include "method_reference.h"
#include "oat_file.h"
#include "object_callbacks.h"
#include "safe_map.h"
@@ -165,9 +166,9 @@
void* MoreCore(const void* mspace, intptr_t increment);
- // Adds to `methods` all the compiled ArtMethods which are part of any of the given dex locations.
- void GetCompiledArtMethods(const std::set<std::string>& dex_base_locations,
- std::vector<ArtMethod*>& methods)
+ // Adds to `methods` all profiled methods which are part of any of the given dex locations.
+ void GetProfiledMethods(const std::set<std::string>& dex_base_locations,
+ std::vector<MethodReference>& methods)
REQUIRES(!lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index f181ca3..a79bcf0 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -16,7 +16,8 @@
#include "offline_profiling_info.h"
-#include <fstream>
+#include "errno.h"
+#include <limits.h>
#include <vector>
#include <sys/file.h>
#include <sys/stat.h>
@@ -34,6 +35,11 @@
namespace art {
+const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' };
+const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '1', '\0' };
+
+static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
+
// Transform the actual dex location into relative paths.
// Note: this is OK because we don't store profiles of different apps into the same file.
// Apps with split apks don't cause trouble because each split has a different name and will not
@@ -49,15 +55,27 @@
}
}
-bool ProfileCompilationInfo::SaveProfilingInfo(
- const std::string& filename,
- const std::vector<ArtMethod*>& methods,
+bool ProfileCompilationInfo::AddMethodsAndClasses(
+ const std::vector<MethodReference>& methods,
const std::set<DexCacheResolvedClasses>& resolved_classes) {
- if (methods.empty() && resolved_classes.empty()) {
- VLOG(profiler) << "No info to save to " << filename;
- return true;
+ for (const MethodReference& method : methods) {
+ if (!AddMethodIndex(GetProfileDexFileKey(method.dex_file->GetLocation()),
+ method.dex_file->GetLocationChecksum(),
+ method.dex_method_index)) {
+ return false;
+ }
}
+ for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
+ if (!AddResolvedClasses(dex_cache)) {
+ return false;
+ }
+ }
+ return true;
+}
+bool ProfileCompilationInfo::MergeAndSave(const std::string& filename,
+ uint64_t* bytes_written,
+ bool force) {
ScopedTrace trace(__PRETTY_FUNCTION__);
ScopedFlock flock;
std::string error;
@@ -68,26 +86,37 @@
int fd = flock.GetFile()->Fd();
- ProfileCompilationInfo info;
- if (!info.Load(fd)) {
- LOG(WARNING) << "Could not load previous profile data from file " << filename;
- return false;
- }
- {
- ScopedObjectAccess soa(Thread::Current());
- for (ArtMethod* method : methods) {
- const DexFile* dex_file = method->GetDexFile();
- if (!info.AddMethodIndex(GetProfileDexFileKey(dex_file->GetLocation()),
- dex_file->GetLocationChecksum(),
- method->GetDexMethodIndex())) {
+ // Load the file but keep a copy around to be able to infer if the content has changed.
+ ProfileCompilationInfo fileInfo;
+ ProfileLoadSatus status = fileInfo.LoadInternal(fd, &error);
+ if (status == kProfileLoadSuccess) {
+ // Merge the content of file into the current object.
+ if (MergeWith(fileInfo)) {
+ // If after the merge we have the same data as what is the file there's no point
+ // in actually doing the write. The file will be exactly the same as before.
+ if (Equals(fileInfo)) {
+ if (bytes_written != nullptr) {
+ *bytes_written = 0;
+ }
+ return true;
+ }
+ } else {
+ LOG(WARNING) << "Could not merge previous profile data from file " << filename;
+ if (!force) {
return false;
}
}
- for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
- info.AddResolvedClasses(dex_cache);
- }
+ } else if (force &&
+ ((status == kProfileLoadVersionMismatch) || (status == kProfileLoadBadData))) {
+ // Log a warning but don't return false. We will clear the profile anyway.
+ LOG(WARNING) << "Clearing bad or obsolete profile data from file "
+ << filename << ": " << error;
+ } else {
+ LOG(WARNING) << "Could not load profile data from file " << filename << ": " << error;
+ return false;
}
+ // We need to clear the data because we don't support appending to the profiles yet.
if (!flock.GetFile()->ClearContent()) {
PLOG(WARNING) << "Could not clear profile file: " << filename;
return false;
@@ -95,95 +124,118 @@
// This doesn't need locking because we are trying to lock the file for exclusive
// access and fail immediately if we can't.
- bool result = info.Save(fd);
+ bool result = Save(fd);
if (result) {
VLOG(profiler) << "Successfully saved profile info to " << filename
<< " Size: " << GetFileSizeBytes(filename);
+ if (bytes_written != nullptr) {
+ *bytes_written = GetFileSizeBytes(filename);
+ }
} else {
VLOG(profiler) << "Failed to save profile info to " << filename;
}
return result;
}
-static bool WriteToFile(int fd, const std::ostringstream& os) {
- std::string data(os.str());
- const char *p = data.c_str();
- size_t length = data.length();
- do {
- int n = TEMP_FAILURE_RETRY(write(fd, p, length));
- if (n < 0) {
- PLOG(WARNING) << "Failed to write to descriptor: " << fd;
+// Returns true if all the bytes were successfully written to the file descriptor.
+static bool WriteBuffer(int fd, const uint8_t* buffer, size_t byte_count) {
+ while (byte_count > 0) {
+ int bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer, byte_count));
+ if (bytes_written == -1) {
return false;
}
- p += n;
- length -= n;
- } while (length > 0);
+ byte_count -= bytes_written; // Reduce the number of remaining bytes.
+ buffer += bytes_written; // Move the buffer forward.
+ }
return true;
}
-static constexpr const char kFieldSeparator = ',';
-static constexpr const char kLineSeparator = '\n';
-static constexpr const char* kClassesMarker = "classes";
+// Add the string bytes to the buffer.
+static void AddStringToBuffer(std::vector<uint8_t>* buffer, const std::string& value) {
+ buffer->insert(buffer->end(), value.begin(), value.end());
+}
+
+// Insert each byte, from low to high into the buffer.
+template <typename T>
+static void AddUintToBuffer(std::vector<uint8_t>* buffer, T value) {
+ for (size_t i = 0; i < sizeof(T); i++) {
+ buffer->push_back((value >> (i * kBitsPerByte)) & 0xff);
+ }
+}
+
+static constexpr size_t kLineHeaderSize =
+ 3 * sizeof(uint16_t) + // method_set.size + class_set.size + dex_location.size
+ sizeof(uint32_t); // checksum
/**
* Serialization format:
- * dex_location1,dex_location_checksum1,method_id11,method_id12...,classes,class_id1,class_id2...
- * dex_location2,dex_location_checksum2,method_id21,method_id22...,classes,class_id1,class_id2...
- * e.g.
- * app.apk,131232145,11,23,454,54,classes,1,2,4,1234
- * app.apk:classes5.dex,218490184,39,13,49,1
+ * magic,version,number_of_lines
+ * dex_location1,number_of_methods1,number_of_classes1,dex_location_checksum1, \
+ * method_id11,method_id12...,class_id1,class_id2...
+ * dex_location2,number_of_methods2,number_of_classes2,dex_location_checksum2, \
+ * method_id21,method_id22...,,class_id1,class_id2...
+ * .....
**/
bool ProfileCompilationInfo::Save(int fd) {
ScopedTrace trace(__PRETTY_FUNCTION__);
DCHECK_GE(fd, 0);
- // TODO(calin): Profile this and see how much memory it takes. If too much,
- // write to file directly.
- std::ostringstream os;
+
+ // Cache at most 5KB before writing.
+ static constexpr size_t kMaxSizeToKeepBeforeWriting = 5 * KB;
+ // Use a vector wrapper to avoid keeping track of offsets when we add elements.
+ std::vector<uint8_t> buffer;
+ WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic));
+ WriteBuffer(fd, kProfileVersion, sizeof(kProfileVersion));
+ AddUintToBuffer(&buffer, static_cast<uint16_t>(info_.size()));
+
for (const auto& it : info_) {
+ if (buffer.size() > kMaxSizeToKeepBeforeWriting) {
+ if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
+ return false;
+ }
+ buffer.clear();
+ }
const std::string& dex_location = it.first;
const DexFileData& dex_data = it.second;
if (dex_data.method_set.empty() && dex_data.class_set.empty()) {
continue;
}
- os << dex_location << kFieldSeparator << dex_data.checksum;
+ if (dex_location.size() >= kMaxDexFileKeyLength) {
+ LOG(WARNING) << "DexFileKey exceeds allocated limit";
+ return false;
+ }
+
+ // Make sure that the buffer has enough capacity to avoid repeated resizings
+ // while we add data.
+ size_t required_capacity = buffer.size() +
+ kLineHeaderSize +
+ dex_location.size() +
+ sizeof(uint16_t) * (dex_data.class_set.size() + dex_data.method_set.size());
+
+ buffer.reserve(required_capacity);
+
+ DCHECK_LE(dex_location.size(), std::numeric_limits<uint16_t>::max());
+ DCHECK_LE(dex_data.method_set.size(), std::numeric_limits<uint16_t>::max());
+ DCHECK_LE(dex_data.class_set.size(), std::numeric_limits<uint16_t>::max());
+ AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_location.size()));
+ AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.method_set.size()));
+ AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.class_set.size()));
+ AddUintToBuffer(&buffer, dex_data.checksum); // uint32_t
+
+ AddStringToBuffer(&buffer, dex_location);
+
for (auto method_it : dex_data.method_set) {
- os << kFieldSeparator << method_it;
+ AddUintToBuffer(&buffer, method_it);
}
- if (!dex_data.class_set.empty()) {
- os << kFieldSeparator << kClassesMarker;
- for (auto class_id : dex_data.class_set) {
- os << kFieldSeparator << class_id;
- }
+ for (auto class_id : dex_data.class_set) {
+ AddUintToBuffer(&buffer, class_id);
}
- os << kLineSeparator;
+ DCHECK_EQ(required_capacity, buffer.size())
+ << "Failed to add the expected number of bytes in the buffer";
}
- return WriteToFile(fd, os);
-}
-
-// TODO(calin): This a duplicate of Utils::Split fixing the case where the first character
-// is the separator. Merge the fix into Utils::Split once verified that it doesn't break its users.
-static void SplitString(const std::string& s, char separator, std::vector<std::string>* result) {
- const char* p = s.data();
- const char* end = p + s.size();
- // Check if the first character is the separator.
- if (p != end && *p ==separator) {
- result->push_back("");
- ++p;
- }
- // Process the rest of the characters.
- while (p != end) {
- if (*p == separator) {
- ++p;
- } else {
- const char* start = p;
- while (++p != end && *p != separator) {
- // Skip to the next occurrence of the separator.
- }
- result->push_back(std::string(start, p - start));
- }
- }
+ return WriteBuffer(fd, buffer.data(), buffer.size());
}
ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData(
@@ -233,120 +285,259 @@
return true;
}
-bool ProfileCompilationInfo::ProcessLine(const std::string& line) {
- std::vector<std::string> parts;
- SplitString(line, kFieldSeparator, &parts);
- if (parts.size() < 3) {
- LOG(WARNING) << "Invalid line: " << line;
- return false;
- }
-
- const std::string& dex_location = parts[0];
- uint32_t checksum;
- if (!ParseInt(parts[1].c_str(), &checksum)) {
- return false;
- }
-
- for (size_t i = 2; i < parts.size(); i++) {
- if (parts[i] == kClassesMarker) {
- ++i;
- // All of the remaining idx are class def indexes.
- for (++i; i < parts.size(); ++i) {
- uint32_t class_def_idx;
- if (!ParseInt(parts[i].c_str(), &class_def_idx)) {
- LOG(WARNING) << "Cannot parse class_def_idx " << parts[i];
- return false;
- } else if (class_def_idx >= std::numeric_limits<uint16_t>::max()) {
- LOG(WARNING) << "Class def idx " << class_def_idx << " is larger than uint16_t max";
- return false;
- }
- if (!AddClassIndex(dex_location, checksum, class_def_idx)) {
- return false;
- }
- }
- break;
- }
- uint32_t method_idx;
- if (!ParseInt(parts[i].c_str(), &method_idx)) {
- LOG(WARNING) << "Cannot parse method_idx " << parts[i];
- return false;
- }
+bool ProfileCompilationInfo::ProcessLine(SafeBuffer& line_buffer,
+ uint16_t method_set_size,
+ uint16_t class_set_size,
+ uint32_t checksum,
+ const std::string& dex_location) {
+ for (uint16_t i = 0; i < method_set_size; i++) {
+ uint16_t method_idx = line_buffer.ReadUintAndAdvance<uint16_t>();
if (!AddMethodIndex(dex_location, checksum, method_idx)) {
return false;
}
}
+
+ for (uint16_t i = 0; i < class_set_size; i++) {
+ uint16_t class_def_idx = line_buffer.ReadUintAndAdvance<uint16_t>();
+ if (!AddClassIndex(dex_location, checksum, class_def_idx)) {
+ return false;
+ }
+ }
return true;
}
-// Parses the buffer (of length n) starting from start_from and identify new lines
-// based on kLineSeparator marker.
-// Returns the first position after kLineSeparator in the buffer (starting from start_from),
-// or -1 if the marker doesn't appear.
-// The processed characters are appended to the given line.
-static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& line) {
- if (start_from >= n) {
- return -1;
+// Tests for EOF by trying to read 1 byte from the descriptor.
+// Returns:
+// 0 if the descriptor is at the EOF,
+// -1 if there was an IO error
+// 1 if the descriptor has more content to read
+static int testEOF(int fd) {
+ uint8_t buffer[1];
+ return TEMP_FAILURE_RETRY(read(fd, buffer, 1));
+}
+
+// Reads an uint value previously written with AddUintToBuffer.
+template <typename T>
+T ProfileCompilationInfo::SafeBuffer::ReadUintAndAdvance() {
+ static_assert(std::is_unsigned<T>::value, "Type is not unsigned");
+ CHECK_LE(ptr_current_ + sizeof(T), ptr_end_);
+ T value = 0;
+ for (size_t i = 0; i < sizeof(T); i++) {
+ value += ptr_current_[i] << (i * kBitsPerByte);
}
- int new_line_pos = -1;
- for (int i = start_from; i < n; i++) {
- if (buffer[i] == kLineSeparator) {
- new_line_pos = i;
- break;
+ ptr_current_ += sizeof(T);
+ return value;
+}
+
+bool ProfileCompilationInfo::SafeBuffer::CompareAndAdvance(const uint8_t* data, size_t data_size) {
+ if (ptr_current_ + data_size > ptr_end_) {
+ return false;
+ }
+ if (memcmp(ptr_current_, data, data_size) == 0) {
+ ptr_current_ += data_size;
+ return true;
+ }
+ return false;
+}
+
+ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::SafeBuffer::FillFromFd(
+ int fd,
+ const std::string& source,
+ /*out*/std::string* error) {
+ size_t byte_count = ptr_end_ - ptr_current_;
+ uint8_t* buffer = ptr_current_;
+ while (byte_count > 0) {
+ int bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, byte_count));
+ if (bytes_read == 0) {
+ *error += "Profile EOF reached prematurely for " + source;
+ return kProfileLoadBadData;
+ } else if (bytes_read < 0) {
+ *error += "Profile IO error for " + source + strerror(errno);
+ return kProfileLoadIOError;
}
+ byte_count -= bytes_read;
+ buffer += bytes_read;
}
- int append_limit = new_line_pos == -1 ? n : new_line_pos;
- line.append(buffer + start_from, append_limit - start_from);
- // Jump over kLineSeparator and return the position of the next character.
- return new_line_pos == -1 ? new_line_pos : new_line_pos + 1;
+ return kProfileLoadSuccess;
+}
+
+ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileHeader(
+ int fd,
+ /*out*/uint16_t* number_of_lines,
+ /*out*/std::string* error) {
+ // Read magic and version
+ const size_t kMagicVersionSize =
+ sizeof(kProfileMagic) +
+ sizeof(kProfileVersion) +
+ sizeof(uint16_t); // number of lines
+
+ SafeBuffer safe_buffer(kMagicVersionSize);
+
+ ProfileLoadSatus status = safe_buffer.FillFromFd(fd, "ReadProfileHeader", error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+
+ if (!safe_buffer.CompareAndAdvance(kProfileMagic, sizeof(kProfileMagic))) {
+ *error = "Profile missing magic";
+ return kProfileLoadVersionMismatch;
+ }
+ if (!safe_buffer.CompareAndAdvance(kProfileVersion, sizeof(kProfileVersion))) {
+ *error = "Profile version mismatch";
+ return kProfileLoadVersionMismatch;
+ }
+ *number_of_lines = safe_buffer.ReadUintAndAdvance<uint16_t>();
+ return kProfileLoadSuccess;
+}
+
+ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLineHeader(
+ int fd,
+ /*out*/ProfileLineHeader* line_header,
+ /*out*/std::string* error) {
+ SafeBuffer header_buffer(kLineHeaderSize);
+ ProfileLoadSatus status = header_buffer.FillFromFd(fd, "ReadProfileHeader", error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+
+ uint16_t dex_location_size = header_buffer.ReadUintAndAdvance<uint16_t>();
+ line_header->method_set_size = header_buffer.ReadUintAndAdvance<uint16_t>();
+ line_header->class_set_size = header_buffer.ReadUintAndAdvance<uint16_t>();
+ line_header->checksum = header_buffer.ReadUintAndAdvance<uint32_t>();
+
+ if (dex_location_size == 0 || dex_location_size > kMaxDexFileKeyLength) {
+ *error = "DexFileKey has an invalid size: " + std::to_string(dex_location_size);
+ return kProfileLoadBadData;
+ }
+
+ SafeBuffer location_buffer(dex_location_size);
+ status = location_buffer.FillFromFd(fd, "ReadProfileHeaderDexLocation", error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+ line_header->dex_location.assign(
+ reinterpret_cast<char*>(location_buffer.Get()), dex_location_size);
+ return kProfileLoadSuccess;
+}
+
+ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine(
+ int fd,
+ const ProfileLineHeader& line_header,
+ /*out*/std::string* error) {
+ // Make sure that we don't try to read everything in memory (in case the profile if full).
+ // Split readings in chunks of at most 10kb.
+ static constexpr uint16_t kMaxNumberOfEntriesToRead = 5120;
+ uint16_t methods_left_to_read = line_header.method_set_size;
+ uint16_t classes_left_to_read = line_header.class_set_size;
+
+ while ((methods_left_to_read > 0) || (classes_left_to_read > 0)) {
+ uint16_t methods_to_read = std::min(kMaxNumberOfEntriesToRead, methods_left_to_read);
+ uint16_t max_classes_to_read = kMaxNumberOfEntriesToRead - methods_to_read;
+ uint16_t classes_to_read = std::min(max_classes_to_read, classes_left_to_read);
+
+ size_t line_size = sizeof(uint16_t) * (methods_to_read + classes_to_read);
+ SafeBuffer line_buffer(line_size);
+
+ ProfileLoadSatus status = line_buffer.FillFromFd(fd, "ReadProfileLine", error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+ if (!ProcessLine(line_buffer,
+ methods_to_read,
+ classes_to_read,
+ line_header.checksum,
+ line_header.dex_location)) {
+ *error = "Error when reading profile file line";
+ return kProfileLoadBadData;
+ }
+ methods_left_to_read -= methods_to_read;
+ classes_left_to_read -= classes_to_read;
+ }
+ return kProfileLoadSuccess;
}
bool ProfileCompilationInfo::Load(int fd) {
+ std::string error;
+ ProfileLoadSatus status = LoadInternal(fd, &error);
+
+ if (status == kProfileLoadSuccess) {
+ return true;
+ } else {
+ PLOG(WARNING) << "Error when reading profile " << error;
+ return false;
+ }
+}
+
+ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal(
+ int fd, std::string* error) {
ScopedTrace trace(__PRETTY_FUNCTION__);
DCHECK_GE(fd, 0);
- std::string current_line;
- const int kBufferSize = 1024;
- char buffer[kBufferSize];
-
- while (true) {
- int n = TEMP_FAILURE_RETRY(read(fd, buffer, kBufferSize));
- if (n < 0) {
- PLOG(WARNING) << "Error when reading profile file";
- return false;
- } else if (n == 0) {
- break;
- }
- // Detect the new lines from the buffer. If we manage to complete a line,
- // process it. Otherwise append to the current line.
- int current_start_pos = 0;
- while (current_start_pos < n) {
- current_start_pos = GetLineFromBuffer(buffer, n, current_start_pos, current_line);
- if (current_start_pos == -1) {
- break;
- }
- if (!ProcessLine(current_line)) {
- return false;
- }
- // Reset the current line (we just processed it).
- current_line.clear();
- }
+ struct stat stat_buffer;
+ if (fstat(fd, &stat_buffer) != 0) {
+ return kProfileLoadIOError;
}
- return true;
+ // We allow empty profile files.
+ // Profiles may be created by ActivityManager or installd before we manage to
+ // process them in the runtime or profman.
+ if (stat_buffer.st_size == 0) {
+ return kProfileLoadSuccess;
+ }
+ // Read profile header: magic + version + number_of_lines.
+ uint16_t number_of_lines;
+ ProfileLoadSatus status = ReadProfileHeader(fd, &number_of_lines, error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+
+ while (number_of_lines > 0) {
+ ProfileLineHeader line_header;
+ // First, read the line header to get the amount of data we need to read.
+ status = ReadProfileLineHeader(fd, &line_header, error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+
+ // Now read the actual profile line.
+ status = ReadProfileLine(fd, line_header, error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+ number_of_lines--;
+ }
+
+ // Check that we read everything and that profiles don't contain junk data.
+ int result = testEOF(fd);
+ if (result == 0) {
+ return kProfileLoadSuccess;
+ } else if (result < 0) {
+ return kProfileLoadIOError;
+ } else {
+ *error = "Unexpected content in the profile file";
+ return kProfileLoadBadData;
+ }
}
-bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) {
+bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) {
+ // First verify that all checksums match. This will avoid adding garbage to
+ // the current profile info.
+ // Note that the number of elements should be very small, so this should not
+ // be a performance issue.
+ for (const auto& other_it : other.info_) {
+ auto info_it = info_.find(other_it.first);
+ if ((info_it != info_.end()) && (info_it->second.checksum != other_it.second.checksum)) {
+ LOG(WARNING) << "Checksum mismatch for dex " << other_it.first;
+ return false;
+ }
+ }
+ // All checksums match. Import the data.
for (const auto& other_it : other.info_) {
const std::string& other_dex_location = other_it.first;
const DexFileData& other_dex_data = other_it.second;
-
auto info_it = info_.find(other_dex_location);
if (info_it == info_.end()) {
info_it = info_.Put(other_dex_location, DexFileData(other_dex_data.checksum));
}
- if (info_it->second.checksum != other_dex_data.checksum) {
- LOG(WARNING) << "Checksum mismatch for dex " << other_dex_location;
- return false;
- }
info_it->second.method_set.insert(other_dex_data.method_set.begin(),
other_dex_data.method_set.end());
info_it->second.class_set.insert(other_dex_data.class_set.begin(),
@@ -387,6 +578,14 @@
return total;
}
+uint32_t ProfileCompilationInfo::GetNumberOfResolvedClasses() const {
+ uint32_t total = 0;
+ for (const auto& it : info_) {
+ total += it.second.class_set.size();
+ }
+ return total;
+}
+
std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
bool print_full_dex_location) const {
std::ostringstream os;
@@ -408,19 +607,29 @@
std::string multidex_suffix = DexFile::GetMultiDexSuffix(location);
os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
}
- for (const auto method_it : dex_data.method_set) {
- if (dex_files != nullptr) {
- const DexFile* dex_file = nullptr;
- for (size_t i = 0; i < dex_files->size(); i++) {
- if (location == (*dex_files)[i]->GetLocation()) {
- dex_file = (*dex_files)[i];
- }
- }
- if (dex_file != nullptr) {
- os << "\n " << PrettyMethod(method_it, *dex_file, true);
+ const DexFile* dex_file = nullptr;
+ if (dex_files != nullptr) {
+ for (size_t i = 0; i < dex_files->size(); i++) {
+ if (location == (*dex_files)[i]->GetLocation()) {
+ dex_file = (*dex_files)[i];
}
}
- os << "\n " << method_it;
+ }
+ os << "\n\tmethods: ";
+ for (const auto method_it : dex_data.method_set) {
+ if (dex_file != nullptr) {
+ os << "\n\t\t" << PrettyMethod(method_it, *dex_file, true);
+ } else {
+ os << method_it << ",";
+ }
+ }
+ os << "\n\tclasses: ";
+ for (const auto class_it : dex_data.class_set) {
+ if (dex_file != nullptr) {
+ os << "\n\t\t" << PrettyType(class_it, *dex_file);
+ } else {
+ os << class_it << ",";
+ }
}
}
return os.str();
@@ -442,4 +651,10 @@
return ret;
}
+void ProfileCompilationInfo::ClearResolvedClasses() {
+ for (auto& pair : info_) {
+ pair.second.class_set.clear();
+ }
+}
+
} // namespace art
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h
index df03244..5a07da7 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -28,9 +28,6 @@
namespace art {
-class ArtMethod;
-class DexCacheProfileData;
-
// TODO: rename file.
/**
* Profile information in a format suitable to be queried by the compiler and
@@ -41,21 +38,29 @@
*/
class ProfileCompilationInfo {
public:
- // Saves profile information about the given methods in the given file.
- // Note that the saving proceeds only if the file can be locked for exclusive access.
- // If not (the locking is not blocking), the function does not save and returns false.
- static bool SaveProfilingInfo(const std::string& filename,
- const std::vector<ArtMethod*>& methods,
- const std::set<DexCacheResolvedClasses>& resolved_classes);
+ static const uint8_t kProfileMagic[];
+ static const uint8_t kProfileVersion[];
+ // Add the given methods and classes to the current profile object.
+ bool AddMethodsAndClasses(const std::vector<MethodReference>& methods,
+ const std::set<DexCacheResolvedClasses>& resolved_classes);
// Loads profile information from the given file descriptor.
bool Load(int fd);
- // Loads the data from another ProfileCompilationInfo object.
- bool Load(const ProfileCompilationInfo& info);
+ // Merge the data from another ProfileCompilationInfo into the current object.
+ bool MergeWith(const ProfileCompilationInfo& info);
// Saves the profile data to the given file descriptor.
bool Save(int fd);
+ // Loads and merges profile information from the given file into the current
+ // object and tries to save it back to disk.
+ // If `force` is true then the save will go through even if the given file
+ // has bad data or its version does not match. In this cases the profile content
+ // is ignored.
+ bool MergeAndSave(const std::string& filename, uint64_t* bytes_written, bool force);
+
// Returns the number of methods that were profiled.
uint32_t GetNumberOfMethods() const;
+ // Returns the number of resolved classes that were profiled.
+ uint32_t GetNumberOfResolvedClasses() const;
// Returns true if the method reference is present in the profiling info.
bool ContainsMethod(const MethodReference& method_ref) const;
@@ -70,8 +75,8 @@
std::string DumpInfo(const std::vector<const DexFile*>* dex_files,
bool print_full_dex_location = true) const;
- // For testing purposes.
bool Equals(const ProfileCompilationInfo& other);
+
static std::string GetProfileDexFileKey(const std::string& dex_location);
// Returns the class descriptors for all of the classes in the profiles' class sets.
@@ -79,7 +84,17 @@
// profile info stuff to generate a map back to the dex location.
std::set<DexCacheResolvedClasses> GetResolvedClasses() const;
+ // Clears the resolved classes from the current object.
+ void ClearResolvedClasses();
+
private:
+ enum ProfileLoadSatus {
+ kProfileLoadIOError,
+ kProfileLoadVersionMismatch,
+ kProfileLoadBadData,
+ kProfileLoadSuccess
+ };
+
struct DexFileData {
explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {}
uint32_t checksum;
@@ -96,9 +111,65 @@
DexFileData* GetOrAddDexFileData(const std::string& dex_location, uint32_t checksum);
bool AddMethodIndex(const std::string& dex_location, uint32_t checksum, uint16_t method_idx);
bool AddClassIndex(const std::string& dex_location, uint32_t checksum, uint16_t class_idx);
- bool AddResolvedClasses(const DexCacheResolvedClasses& classes)
- SHARED_REQUIRES(Locks::mutator_lock_);
- bool ProcessLine(const std::string& line);
+ bool AddResolvedClasses(const DexCacheResolvedClasses& classes);
+
+ // Parsing functionality.
+
+ struct ProfileLineHeader {
+ std::string dex_location;
+ uint16_t method_set_size;
+ uint16_t class_set_size;
+ uint32_t checksum;
+ };
+
+ // A helper structure to make sure we don't read past our buffers in the loops.
+ struct SafeBuffer {
+ public:
+ explicit SafeBuffer(size_t size) : storage_(new uint8_t[size]) {
+ ptr_current_ = storage_.get();
+ ptr_end_ = ptr_current_ + size;
+ }
+
+ // Reads the content of the descriptor at the current position.
+ ProfileLoadSatus FillFromFd(int fd,
+ const std::string& source,
+ /*out*/std::string* error);
+
+ // Reads an uint value (high bits to low bits) and advances the current pointer
+ // with the number of bits read.
+ template <typename T> T ReadUintAndAdvance();
+
+ // Compares the given data with the content current pointer. If the contents are
+ // equal it advances the current pointer by data_size.
+ bool CompareAndAdvance(const uint8_t* data, size_t data_size);
+
+ // Get the underlying raw buffer.
+ uint8_t* Get() { return storage_.get(); }
+
+ private:
+ std::unique_ptr<uint8_t> storage_;
+ uint8_t* ptr_current_;
+ uint8_t* ptr_end_;
+ };
+
+ ProfileLoadSatus LoadInternal(int fd, std::string* error);
+
+ ProfileLoadSatus ReadProfileHeader(int fd,
+ /*out*/uint16_t* number_of_lines,
+ /*out*/std::string* error);
+
+ ProfileLoadSatus ReadProfileLineHeader(int fd,
+ /*out*/ProfileLineHeader* line_header,
+ /*out*/std::string* error);
+ ProfileLoadSatus ReadProfileLine(int fd,
+ const ProfileLineHeader& line_header,
+ /*out*/std::string* error);
+
+ bool ProcessLine(SafeBuffer& line_buffer,
+ uint16_t method_set_size,
+ uint16_t class_set_size,
+ uint32_t checksum,
+ const std::string& dex_location);
friend class ProfileCompilationInfoTest;
friend class CompilerDriverProfileTest;
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index fdd8c6e..c8f4d94 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -21,6 +21,7 @@
#include "class_linker-inl.h"
#include "common_runtime_test.h"
#include "dex_file.h"
+#include "method_reference.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "handle_scope-inl.h"
@@ -49,16 +50,44 @@
return methods;
}
- bool AddData(const std::string& dex_location,
- uint32_t checksum,
- uint16_t method_index,
- ProfileCompilationInfo* info) {
+ bool AddMethod(const std::string& dex_location,
+ uint32_t checksum,
+ uint16_t method_index,
+ ProfileCompilationInfo* info) {
return info->AddMethodIndex(dex_location, checksum, method_index);
}
+ bool AddClass(const std::string& dex_location,
+ uint32_t checksum,
+ uint16_t class_index,
+ ProfileCompilationInfo* info) {
+ return info->AddMethodIndex(dex_location, checksum, class_index);
+ }
+
uint32_t GetFd(const ScratchFile& file) {
return static_cast<uint32_t>(file.GetFd());
}
+
+ bool SaveProfilingInfo(
+ const std::string& filename,
+ const std::vector<ArtMethod*>& methods,
+ const std::set<DexCacheResolvedClasses>& resolved_classes) {
+ ProfileCompilationInfo info;
+ std::vector<MethodReference> method_refs;
+ ScopedObjectAccess soa(Thread::Current());
+ for (ArtMethod* method : methods) {
+ method_refs.emplace_back(method->GetDexFile(), method->GetDexMethodIndex());
+ }
+ if (!info.AddMethodsAndClasses(method_refs, resolved_classes)) {
+ return false;
+ }
+ return info.MergeAndSave(filename, nullptr, false);
+ }
+
+ // Cannot sizeof the actual arrays so hardcode the values here.
+ // They should not change anyway.
+ static constexpr int kProfileMagicSize = 4;
+ static constexpr int kProfileVersionSize = 4;
};
TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
@@ -75,9 +104,7 @@
// Save virtual methods from Main.
std::set<DexCacheResolvedClasses> resolved_classes;
std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
- ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(),
- main_methods,
- resolved_classes));
+ ASSERT_TRUE(SaveProfilingInfo(profile.GetFilename(), main_methods, resolved_classes));
// Check that what we saved is in the profile.
ProfileCompilationInfo info1;
@@ -92,9 +119,7 @@
// Save virtual methods from Second.
std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;");
- ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(),
- second_methods,
- resolved_classes));
+ ASSERT_TRUE(SaveProfilingInfo(profile.GetFilename(), second_methods, resolved_classes));
// Check that what we saved is in the profile (methods form Main and Second).
ProfileCompilationInfo info2;
@@ -118,8 +143,8 @@
ProfileCompilationInfo saved_info;
// Save a few methods.
for (uint16_t i = 0; i < 10; i++) {
- ASSERT_TRUE(AddData("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
- ASSERT_TRUE(AddData("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
}
ASSERT_TRUE(saved_info.Save(GetFd(profile)));
ASSERT_EQ(0, profile.GetFile()->Flush());
@@ -132,9 +157,9 @@
// Save more methods.
for (uint16_t i = 0; i < 100; i++) {
- ASSERT_TRUE(AddData("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
- ASSERT_TRUE(AddData("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
- ASSERT_TRUE(AddData("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info));
}
ASSERT_TRUE(profile.GetFile()->ResetOffset());
ASSERT_TRUE(saved_info.Save(GetFd(profile)));
@@ -147,25 +172,156 @@
ASSERT_TRUE(loaded_info2.Equals(saved_info));
}
-TEST_F(ProfileCompilationInfoTest, AddDataFail) {
+TEST_F(ProfileCompilationInfoTest, AddMethodsAndClassesFail) {
ScratchFile profile;
ProfileCompilationInfo info;
- ASSERT_TRUE(AddData("dex_location", /* checksum */ 1, /* method_idx */ 1, &info));
+ ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info));
// Trying to add info for an existing file but with a different checksum.
- ASSERT_FALSE(AddData("dex_location", /* checksum */ 2, /* method_idx */ 2, &info));
+ ASSERT_FALSE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info));
}
-TEST_F(ProfileCompilationInfoTest, LoadFail) {
+TEST_F(ProfileCompilationInfoTest, MergeFail) {
ScratchFile profile;
ProfileCompilationInfo info1;
- ASSERT_TRUE(AddData("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1));
+ ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1));
// Use the same file, change the checksum.
ProfileCompilationInfo info2;
- ASSERT_TRUE(AddData("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2));
+ ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2));
- ASSERT_FALSE(info1.Load(info2));
+ ASSERT_FALSE(info1.MergeWith(info2));
+}
+
+TEST_F(ProfileCompilationInfoTest, SaveMaxMethods) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo saved_info;
+ // Save the maximum number of methods
+ for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) {
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
+ }
+ // Save the maximum number of classes
+ for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) {
+ ASSERT_TRUE(AddClass("dex_location1", /* checksum */ 1, /* class_idx */ i, &saved_info));
+ ASSERT_TRUE(AddClass("dex_location2", /* checksum */ 2, /* class_idx */ i, &saved_info));
+ }
+
+ ASSERT_TRUE(saved_info.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Check that we get back what we saved.
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
+ ASSERT_TRUE(loaded_info.Equals(saved_info));
+}
+
+TEST_F(ProfileCompilationInfoTest, SaveEmpty) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo saved_info;
+ ASSERT_TRUE(saved_info.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Check that we get back what we saved.
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
+ ASSERT_TRUE(loaded_info.Equals(saved_info));
+}
+
+TEST_F(ProfileCompilationInfoTest, LoadEmpty) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo empyt_info;
+
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
+ ASSERT_TRUE(loaded_info.Equals(empyt_info));
+}
+
+TEST_F(ProfileCompilationInfoTest, BadMagic) {
+ ScratchFile profile;
+ uint8_t buffer[] = { 1, 2, 3, 4 };
+ ASSERT_TRUE(profile.GetFile()->WriteFully(buffer, sizeof(buffer)));
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
+}
+
+TEST_F(ProfileCompilationInfoTest, BadVersion) {
+ ScratchFile profile;
+
+ ASSERT_TRUE(profile.GetFile()->WriteFully(
+ ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
+ uint8_t version[] = { 'v', 'e', 'r', 's', 'i', 'o', 'n' };
+ ASSERT_TRUE(profile.GetFile()->WriteFully(version, sizeof(version)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
+}
+
+TEST_F(ProfileCompilationInfoTest, Incomplete) {
+ ScratchFile profile;
+ ASSERT_TRUE(profile.GetFile()->WriteFully(
+ ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
+ ASSERT_TRUE(profile.GetFile()->WriteFully(
+ ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
+ // Write that we have at least one line.
+ uint8_t line_number[] = { 0, 1 };
+ ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
+}
+
+TEST_F(ProfileCompilationInfoTest, TooLongDexLocation) {
+ ScratchFile profile;
+ ASSERT_TRUE(profile.GetFile()->WriteFully(
+ ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
+ ASSERT_TRUE(profile.GetFile()->WriteFully(
+ ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
+ // Write that we have at least one line.
+ uint8_t line_number[] = { 0, 1 };
+ ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
+
+ // dex_location_size, methods_size, classes_size, checksum.
+ // Dex location size is too big and should be rejected.
+ uint8_t line[] = { 255, 255, 0, 1, 0, 1, 0, 0, 0, 0 };
+ ASSERT_TRUE(profile.GetFile()->WriteFully(line, sizeof(line)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
+}
+
+TEST_F(ProfileCompilationInfoTest, UnexpectedContent) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo saved_info;
+ // Save the maximum number of methods
+ for (uint16_t i = 0; i < 10; i++) {
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
+ }
+ ASSERT_TRUE(saved_info.Save(GetFd(profile)));
+
+ uint8_t random_data[] = { 1, 2, 3};
+ ASSERT_TRUE(profile.GetFile()->WriteFully(random_data, sizeof(random_data)));
+
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Check that we fail because of unexpected data at the end of the file.
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
}
} // namespace art
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 6fe17db..7a9d250 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -22,25 +22,25 @@
#include "art_method-inl.h"
#include "base/systrace.h"
-#include "scoped_thread_state_change.h"
+#include "base/time_utils.h"
+#include "compiler_filter.h"
#include "oat_file_manager.h"
+#include "scoped_thread_state_change.h"
+
namespace art {
-// An arbitrary value to throttle save requests. Set to 2s for now.
-static constexpr const uint64_t kMilisecondsToNano = 1000000;
-static constexpr const uint64_t kMinimumTimeBetweenCodeCacheUpdatesNs = 2000 * kMilisecondsToNano;
-
// TODO: read the constants from ProfileOptions,
// Add a random delay each time we go to sleep so that we don't hammer the CPU
// with all profile savers running at the same time.
-static constexpr const uint64_t kRandomDelayMaxMs = 20 * 1000; // 20 seconds
-static constexpr const uint64_t kMaxBackoffMs = 5 * 60 * 1000; // 5 minutes
-static constexpr const uint64_t kSavePeriodMs = 10 * 1000; // 10 seconds
-static constexpr const uint64_t kInitialDelayMs = 2 * 1000; // 2 seconds
-static constexpr const double kBackoffCoef = 1.5;
+static constexpr const uint64_t kRandomDelayMaxMs = 30 * 1000; // 30 seconds
+static constexpr const uint64_t kMaxBackoffMs = 10 * 60 * 1000; // 10 minutes
+static constexpr const uint64_t kSavePeriodMs = 20 * 1000; // 20 seconds
+static constexpr const uint64_t kSaveResolvedClassesDelayMs = 2 * 1000; // 2 seconds
+static constexpr const double kBackoffCoef = 2.0;
-static constexpr const uint32_t kMinimumNrOrMethodsToSave = 10;
+static constexpr const uint32_t kMinimumNumberOfMethodsToSave = 10;
+static constexpr const uint32_t kMinimumNumberOfClassesToSave = 10;
ProfileSaver* ProfileSaver::instance_ = nullptr;
pthread_t ProfileSaver::profiler_pthread_ = 0U;
@@ -52,13 +52,21 @@
const std::string& app_data_dir)
: jit_code_cache_(jit_code_cache),
foreign_dex_profile_path_(foreign_dex_profile_path),
- code_cache_last_update_time_ns_(0),
shutting_down_(false),
- first_profile_(true),
+ last_save_number_of_methods_(0),
+ last_save_number_of_classes_(0),
wait_lock_("ProfileSaver wait lock"),
- period_condition_("ProfileSaver period condition", wait_lock_) {
- AddTrackedLocations(output_filename, code_paths);
- app_data_dir_ = "";
+ period_condition_("ProfileSaver period condition", wait_lock_),
+ total_bytes_written_(0),
+ total_number_of_writes_(0),
+ total_number_of_code_cache_queries_(0),
+ total_number_of_skipped_writes_(0),
+ total_number_of_failed_writes_(0),
+ total_ms_of_sleep_(0),
+ total_ns_of_work_(0),
+ total_number_of_foreign_dex_marks_(0),
+ max_number_of_profile_entries_cached_(0) {
+ AddTrackedLocations(output_filename, app_data_dir, code_paths);
if (!app_data_dir.empty()) {
// The application directory is used to determine which dex files are owned by app.
// Since it could be a symlink (e.g. /data/data instead of /data/user/0), and we
@@ -66,9 +74,9 @@
// store it's canonical form to be sure we use the same base when comparing.
UniqueCPtr<const char[]> app_data_dir_real_path(realpath(app_data_dir.c_str(), nullptr));
if (app_data_dir_real_path != nullptr) {
- app_data_dir_.assign(app_data_dir_real_path.get());
+ app_data_dirs_.emplace(app_data_dir_real_path.get());
} else {
- LOG(WARNING) << "Failed to get the real path for app dir: " << app_data_dir_
+ LOG(WARNING) << "Failed to get the real path for app dir: " << app_data_dir
<< ". The app dir will not be used to determine which dex files belong to the app";
}
}
@@ -80,14 +88,13 @@
uint64_t save_period_ms = kSavePeriodMs;
VLOG(profiler) << "Save profiling information every " << save_period_ms << " ms";
-
- bool first_iteration = true;
+ bool cache_resolved_classes = true;
while (!ShuttingDown(self)) {
uint64_t sleep_time_ms;
- if (first_iteration) {
+ if (cache_resolved_classes) {
// Sleep less long for the first iteration since we want to record loaded classes shortly
// after app launch.
- sleep_time_ms = kInitialDelayMs;
+ sleep_time_ms = kSaveResolvedClassesDelayMs;
} else {
const uint64_t random_sleep_delay_ms = rand() % kRandomDelayMaxMs;
sleep_time_ms = save_period_ms + random_sleep_delay_ms;
@@ -96,76 +103,146 @@
MutexLock mu(self, wait_lock_);
period_condition_.TimedWait(self, sleep_time_ms, 0);
}
-
+ total_ms_of_sleep_ += sleep_time_ms;
if (ShuttingDown(self)) {
break;
}
- if (!ProcessProfilingInfo() && save_period_ms < kMaxBackoffMs) {
- // If we don't need to save now it is less likely that we will need to do
- // so in the future. Increase the time between saves according to the
- // kBackoffCoef, but make it no larger than kMaxBackoffMs.
- save_period_ms = static_cast<uint64_t>(kBackoffCoef * save_period_ms);
+ uint64_t start = NanoTime();
+ if (cache_resolved_classes) {
+ // TODO(calin) This only considers the case of the primary profile file.
+ // Anything that gets loaded in the same VM will not have their resolved
+ // classes save (unless they started before the initial saving was done).
+ FetchAndCacheResolvedClasses();
} else {
- // Reset the period to the initial value as it's highly likely to JIT again.
- save_period_ms = kSavePeriodMs;
+ bool profile_saved_to_disk = ProcessProfilingInfo();
+ if (profile_saved_to_disk) {
+ // Reset the period to the initial value as it's highly likely to JIT again.
+ save_period_ms = kSavePeriodMs;
+ VLOG(profiler) << "Profile saver: saved something, period reset to: " << save_period_ms;
+ } else {
+ // If we don't need to save now it is less likely that we will need to do
+ // so in the future. Increase the time between saves according to the
+ // kBackoffCoef, but make it no larger than kMaxBackoffMs.
+ save_period_ms = std::min(kMaxBackoffMs,
+ static_cast<uint64_t>(kBackoffCoef * save_period_ms));
+ VLOG(profiler) << "Profile saver: nothing to save, delaying period to: " << save_period_ms;
+ }
}
- first_iteration = false;
+ cache_resolved_classes = false;
+
+ total_ns_of_work_ += (NanoTime() - start);
}
}
+ProfileCompilationInfo* ProfileSaver::GetCachedProfiledInfo(const std::string& filename) {
+ auto info_it = profile_cache_.find(filename);
+ if (info_it == profile_cache_.end()) {
+ info_it = profile_cache_.Put(filename, ProfileCompilationInfo());
+ }
+ return &info_it->second;
+}
+
+void ProfileSaver::FetchAndCacheResolvedClasses() {
+ ScopedTrace trace(__PRETTY_FUNCTION__);
+
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ std::set<DexCacheResolvedClasses> resolved_classes =
+ class_linker->GetResolvedClasses(/*ignore boot classes*/ true);
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ uint64_t total_number_of_profile_entries_cached = 0;
+ for (const auto& it : tracked_dex_base_locations_) {
+ std::set<DexCacheResolvedClasses> resolved_classes_for_location;
+ const std::string& filename = it.first;
+ const std::set<std::string>& locations = it.second;
+
+ for (const DexCacheResolvedClasses& classes : resolved_classes) {
+ if (locations.find(classes.GetDexLocation()) != locations.end()) {
+ resolved_classes_for_location.insert(classes);
+ }
+ }
+ ProfileCompilationInfo* info = GetCachedProfiledInfo(filename);
+ info->AddMethodsAndClasses(std::vector<MethodReference>(), resolved_classes_for_location);
+ total_number_of_profile_entries_cached += resolved_classes_for_location.size();
+ }
+ max_number_of_profile_entries_cached_ = std::max(
+ max_number_of_profile_entries_cached_,
+ total_number_of_profile_entries_cached);
+}
+
bool ProfileSaver::ProcessProfilingInfo() {
ScopedTrace trace(__PRETTY_FUNCTION__);
- uint64_t last_update_time_ns = jit_code_cache_->GetLastUpdateTimeNs();
- if (!first_profile_ && last_update_time_ns - code_cache_last_update_time_ns_
- < kMinimumTimeBetweenCodeCacheUpdatesNs) {
- VLOG(profiler) << "Not enough time has passed since the last code cache update."
- << "Last update: " << last_update_time_ns
- << " Last save: " << code_cache_last_update_time_ns_;
- return false;
- }
-
- uint64_t start = NanoTime();
- code_cache_last_update_time_ns_ = last_update_time_ns;
SafeMap<std::string, std::set<std::string>> tracked_locations;
{
// Make a copy so that we don't hold the lock while doing I/O.
MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
tracked_locations = tracked_dex_base_locations_;
}
+
+ bool profile_file_saved = false;
+ uint64_t total_number_of_profile_entries_cached = 0;
for (const auto& it : tracked_locations) {
if (ShuttingDown(Thread::Current())) {
return true;
}
const std::string& filename = it.first;
const std::set<std::string>& locations = it.second;
- std::vector<ArtMethod*> methods;
+ std::vector<MethodReference> methods;
{
ScopedObjectAccess soa(Thread::Current());
- jit_code_cache_->GetCompiledArtMethods(locations, methods);
+ jit_code_cache_->GetProfiledMethods(locations, methods);
+ total_number_of_code_cache_queries_++;
}
- // Always save for the first one for loaded classes profile.
- if (methods.size() < kMinimumNrOrMethodsToSave && !first_profile_) {
+
+ ProfileCompilationInfo* cached_info = GetCachedProfiledInfo(filename);
+ cached_info->AddMethodsAndClasses(methods, std::set<DexCacheResolvedClasses>());
+ int64_t delta_number_of_methods =
+ cached_info->GetNumberOfMethods() -
+ static_cast<int64_t>(last_save_number_of_methods_);
+ int64_t delta_number_of_classes =
+ cached_info->GetNumberOfResolvedClasses() -
+ static_cast<int64_t>(last_save_number_of_classes_);
+
+ if (delta_number_of_methods < kMinimumNumberOfMethodsToSave &&
+ delta_number_of_classes < kMinimumNumberOfClassesToSave) {
VLOG(profiler) << "Not enough information to save to: " << filename
- <<" Nr of methods: " << methods.size();
- return false;
+ << " Nr of methods: " << delta_number_of_methods
+ << " Nr of classes: " << delta_number_of_classes;
+ total_number_of_skipped_writes_++;
+ continue;
}
-
- std::set<DexCacheResolvedClasses> resolved_classes;
- if (first_profile_) {
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- resolved_classes = class_linker->GetResolvedClasses(/*ignore boot classes*/true);
- }
-
- if (!ProfileCompilationInfo::SaveProfilingInfo(filename, methods, resolved_classes)) {
+ uint64_t bytes_written;
+ // Force the save. In case the profile data is corrupted or the the profile
+ // has the wrong version this will "fix" the file to the correct format.
+ if (cached_info->MergeAndSave(filename, &bytes_written, /*force*/ true)) {
+ last_save_number_of_methods_ = cached_info->GetNumberOfMethods();
+ last_save_number_of_classes_ = cached_info->GetNumberOfResolvedClasses();
+ // Clear resolved classes. No need to store them around as
+ // they don't change after the first write.
+ cached_info->ClearResolvedClasses();
+ if (bytes_written > 0) {
+ total_number_of_writes_++;
+ total_bytes_written_ += bytes_written;
+ profile_file_saved = true;
+ } else {
+ // At this point we could still have avoided the write.
+ // We load and merge the data from the file lazily at its first ever
+ // save attempt. So, whatever we are trying to save could already be
+ // in the file.
+ total_number_of_skipped_writes_++;
+ }
+ } else {
LOG(WARNING) << "Could not save profiling info to " << filename;
- return false;
+ total_number_of_failed_writes_++;
}
-
- VLOG(profiler) << "Profile process time: " << PrettyDuration(NanoTime() - start);
+ total_number_of_profile_entries_cached +=
+ cached_info->GetNumberOfMethods() +
+ cached_info->GetNumberOfResolvedClasses();
}
- first_profile_ = false;
- return true;
+ max_number_of_profile_entries_cached_ = std::max(
+ max_number_of_profile_entries_cached_,
+ total_number_of_profile_entries_cached);
+ return profile_file_saved;
}
void* ProfileSaver::RunProfileSaverThread(void* arg) {
@@ -183,6 +260,26 @@
return nullptr;
}
+static bool ShouldProfileLocation(const std::string& location) {
+ OatFileManager& oat_manager = Runtime::Current()->GetOatFileManager();
+ const OatFile* oat_file = oat_manager.FindOpenedOatFileFromDexLocation(location);
+ if (oat_file == nullptr) {
+ // This can happen if we fallback to run code directly from the APK.
+ // Profile it with the hope that the background dexopt will get us back into
+ // a good state.
+ VLOG(profiler) << "Asked to profile a location without an oat file:" << location;
+ return true;
+ }
+ CompilerFilter::Filter filter = oat_file->GetCompilerFilter();
+ if ((filter == CompilerFilter::kSpeed) || (filter == CompilerFilter::kEverything)) {
+ VLOG(profiler)
+ << "Skip profiling oat file because it's already speed|everything compiled: "
+ << location << " oat location: " << oat_file->GetLocation();
+ return false;
+ }
+ return true;
+}
+
void ProfileSaver::Start(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
const std::vector<std::string>& code_paths,
@@ -192,6 +289,18 @@
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);
+ }
+ }
+ 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.
@@ -199,16 +308,16 @@
// apps which share the same runtime).
DCHECK_EQ(instance_->jit_code_cache_, jit_code_cache);
// Add the code_paths to the tracked locations.
- instance_->AddTrackedLocations(output_filename, code_paths);
+ instance_->AddTrackedLocations(output_filename, app_data_dir, code_paths_to_profile);
return;
}
VLOG(profiler) << "Starting profile saver using output file: " << output_filename
- << ". Tracking: " << Join(code_paths, ':');
+ << ". Tracking: " << Join(code_paths_to_profile, ':');
instance_ = new ProfileSaver(output_filename,
jit_code_cache,
- code_paths,
+ code_paths_to_profile,
foreign_dex_profile_path,
app_data_dir);
@@ -219,7 +328,7 @@
"Profile saver thread");
}
-void ProfileSaver::Stop() {
+void ProfileSaver::Stop(bool dump_info) {
ProfileSaver* profile_saver = nullptr;
pthread_t profiler_pthread = 0U;
@@ -237,6 +346,9 @@
return;
}
instance_->shutting_down_ = true;
+ if (dump_info) {
+ instance_->DumpInfo(LOG(INFO));
+ }
}
{
@@ -267,49 +379,62 @@
}
void ProfileSaver::AddTrackedLocations(const std::string& output_filename,
+ const std::string& app_data_dir,
const std::vector<std::string>& code_paths) {
auto it = tracked_dex_base_locations_.find(output_filename);
if (it == tracked_dex_base_locations_.end()) {
tracked_dex_base_locations_.Put(output_filename,
std::set<std::string>(code_paths.begin(), code_paths.end()));
+ app_data_dirs_.insert(app_data_dir);
} else {
it->second.insert(code_paths.begin(), code_paths.end());
}
}
void ProfileSaver::NotifyDexUse(const std::string& dex_location) {
+ if (!ShouldProfileLocation(dex_location)) {
+ return;
+ }
std::set<std::string> app_code_paths;
std::string foreign_dex_profile_path;
- std::string app_data_dir;
+ std::set<std::string> app_data_dirs;
{
MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
- DCHECK(instance_ != nullptr);
+ if (instance_ == nullptr) {
+ return;
+ }
// Make a copy so that we don't hold the lock while doing I/O.
for (const auto& it : instance_->tracked_dex_base_locations_) {
app_code_paths.insert(it.second.begin(), it.second.end());
}
foreign_dex_profile_path = instance_->foreign_dex_profile_path_;
- app_data_dir = instance_->app_data_dir_;
+ app_data_dirs.insert(instance_->app_data_dirs_.begin(), instance_->app_data_dirs_.end());
}
- MaybeRecordDexUseInternal(dex_location,
- app_code_paths,
- foreign_dex_profile_path,
- app_data_dir);
+ bool mark_created = MaybeRecordDexUseInternal(dex_location,
+ app_code_paths,
+ foreign_dex_profile_path,
+ app_data_dirs);
+ if (mark_created) {
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ if (instance_ != nullptr) {
+ instance_->total_number_of_foreign_dex_marks_++;
+ }
+ }
}
-void ProfileSaver::MaybeRecordDexUseInternal(
+bool ProfileSaver::MaybeRecordDexUseInternal(
const std::string& dex_location,
const std::set<std::string>& app_code_paths,
const std::string& foreign_dex_profile_path,
- const std::string& app_data_dir) {
+ const std::set<std::string>& app_data_dirs) {
if (dex_location.empty()) {
LOG(WARNING) << "Asked to record foreign dex use with an empty dex location.";
- return;
+ return false;
}
if (foreign_dex_profile_path.empty()) {
LOG(WARNING) << "Asked to record foreign dex use without a valid profile path ";
- return;
+ return false;
}
UniqueCPtr<const char[]> dex_location_real_path(realpath(dex_location.c_str(), nullptr));
@@ -320,14 +445,14 @@
? dex_location.c_str()
: dex_location_real_path.get());
- if (dex_location_real_path_str.compare(0, app_data_dir.length(), app_data_dir) == 0) {
+ if (app_data_dirs.find(dex_location_real_path_str) != app_data_dirs.end()) {
// The dex location is under the application folder. Nothing to record.
- return;
+ return false;
}
if (app_code_paths.find(dex_location) != app_code_paths.end()) {
// The dex location belongs to the application code paths. Nothing to record.
- return;
+ return false;
}
// Do another round of checks with the real paths.
// Note that we could cache all the real locations in the saver (since it's an expensive
@@ -344,7 +469,7 @@
: real_app_code_location.get());
if (real_app_code_location_str == dex_location_real_path_str) {
// The dex location belongs to the application code paths. Nothing to record.
- return;
+ return false;
}
}
@@ -362,12 +487,37 @@
if (close(fd) != 0) {
PLOG(WARNING) << "Could not close file after flagging foreign dex use " << flag_path;
}
+ return true;
} else {
if (errno != EEXIST) {
// Another app could have already created the file.
PLOG(WARNING) << "Could not create foreign dex use mark " << flag_path;
+ return false;
}
+ return true;
}
}
+void ProfileSaver::DumpInstanceInfo(std::ostream& os) {
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ if (instance_ != nullptr) {
+ instance_->DumpInfo(os);
+ }
+}
+
+void ProfileSaver::DumpInfo(std::ostream& os) {
+ os << "ProfileSaver total_bytes_written=" << total_bytes_written_ << '\n'
+ << "ProfileSaver total_number_of_writes=" << total_number_of_writes_ << '\n'
+ << "ProfileSaver total_number_of_code_cache_queries="
+ << total_number_of_code_cache_queries_ << '\n'
+ << "ProfileSaver total_number_of_skipped_writes=" << total_number_of_skipped_writes_ << '\n'
+ << "ProfileSaver total_number_of_failed_writes=" << total_number_of_failed_writes_ << '\n'
+ << "ProfileSaver total_ms_of_sleep=" << total_ms_of_sleep_ << '\n'
+ << "ProfileSaver total_ms_of_work=" << NsToMs(total_ns_of_work_) << '\n'
+ << "ProfileSaver total_number_of_foreign_dex_marks="
+ << total_number_of_foreign_dex_marks_ << '\n'
+ << "ProfileSaver max_number_profile_entries_cached="
+ << max_number_of_profile_entries_cached_ << '\n';
+}
+
} // namespace art
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index e7eab95..0a222bf 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -37,7 +37,7 @@
// Stops the profile saver thread.
// NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
- static void Stop()
+ static void Stop(bool dump_info_)
REQUIRES(!Locks::profiler_lock_, !wait_lock_)
NO_THREAD_SAFETY_ANALYSIS;
@@ -46,6 +46,9 @@
static void NotifyDexUse(const std::string& dex_location);
+ // If the profile saver is running, dumps statistics to the `os`. Otherwise it does nothing.
+ static void DumpInstanceInfo(std::ostream& os);
+
private:
ProfileSaver(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
@@ -67,14 +70,25 @@
bool ShuttingDown(Thread* self) REQUIRES(!Locks::profiler_lock_);
void AddTrackedLocations(const std::string& output_filename,
+ const std::string& app_data_dir,
const std::vector<std::string>& code_paths)
REQUIRES(Locks::profiler_lock_);
- static void MaybeRecordDexUseInternal(
+ // Retrieves the cached profile compilation info for the given profile file.
+ // If no entry exists, a new empty one will be created, added to the cache and
+ // then returned.
+ ProfileCompilationInfo* GetCachedProfiledInfo(const std::string& filename);
+ // Fetches the current resolved classes from the ClassLinker and stores them
+ // in the profile_cache_ for later save.
+ void FetchAndCacheResolvedClasses();
+
+ static bool MaybeRecordDexUseInternal(
const std::string& dex_location,
const std::set<std::string>& tracked_locations,
const std::string& foreign_dex_profile_path,
- const std::string& app_data_dir);
+ const std::set<std::string>& app_data_dirs);
+
+ void DumpInfo(std::ostream& os);
// The only instance of the saver.
static ProfileSaver* instance_ GUARDED_BY(Locks::profiler_lock_);
@@ -82,18 +96,44 @@
static pthread_t profiler_pthread_ GUARDED_BY(Locks::profiler_lock_);
jit::JitCodeCache* jit_code_cache_;
+
+ // Collection of code paths that the profiles tracks.
+ // It maps profile locations to code paths (dex base locations).
SafeMap<std::string, std::set<std::string>> tracked_dex_base_locations_
GUARDED_BY(Locks::profiler_lock_);
+ // The directory were the we should store the code paths.
std::string foreign_dex_profile_path_;
- std::string app_data_dir_;
- uint64_t code_cache_last_update_time_ns_;
+
+ // A list of application directories, used to infer if a loaded dex belongs
+ // to the application or not. Multiple application data directories are possible when
+ // different apps share the same runtime.
+ std::set<std::string> app_data_dirs_ GUARDED_BY(Locks::profiler_lock_);
+
bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);
- bool first_profile_ = true;
+ uint32_t last_save_number_of_methods_;
+ uint32_t last_save_number_of_classes_;
+
+ // A local cache for the profile information. Maps each tracked file to its
+ // profile information. The size of this cache is usually very small and tops
+ // to just a few hundreds entries in the ProfileCompilationInfo objects.
+ // It helps avoiding unnecessary writes to disk.
+ SafeMap<std::string, ProfileCompilationInfo> profile_cache_;
// Save period condition support.
Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
ConditionVariable period_condition_ GUARDED_BY(wait_lock_);
+ uint64_t total_bytes_written_;
+ uint64_t total_number_of_writes_;
+ uint64_t total_number_of_code_cache_queries_;
+ uint64_t total_number_of_skipped_writes_;
+ uint64_t total_number_of_failed_writes_;
+ uint64_t total_ms_of_sleep_;
+ uint64_t total_ns_of_work_;
+ uint64_t total_number_of_foreign_dex_marks_;
+ // TODO(calin): replace with an actual size.
+ uint64_t max_number_of_profile_entries_cached_;
+
DISALLOW_COPY_AND_ASSIGN(ProfileSaver);
};
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index c31b22e..6dd182a 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -62,6 +62,9 @@
// invoking this method will throw an exception.
static constexpr uint32_t kAccDefaultConflict = 0x00800000; // method (runtime)
+// Set by the verifier for a method we do not want the compiler to compile.
+static constexpr uint32_t kAccCompileDontBother = 0x01000000; // method (runtime)
+
// Special runtime-only flags.
// Interface and all its super-interfaces with default methods have been recursively initialized.
static constexpr uint32_t kAccRecursivelyInitialized = 0x20000000;
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index d22c0c7..f355c2a 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -16,7 +16,7 @@
#include "dalvik_system_VMRuntime.h"
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
extern "C" void android_set_application_target_sdk_version(uint32_t version);
#endif
#include <limits.h>
@@ -196,7 +196,7 @@
// Note that targetSdkVersion may be 0, meaning "current".
Runtime::Current()->SetTargetSdkVersion(target_sdk_version);
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
// This part is letting libc/dynamic linker know about current app's
// target sdk version to enable compatibility workarounds.
android_set_application_target_sdk_version(static_cast<uint32_t>(target_sdk_version));
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index ccb8b29..ae84019 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -28,7 +28,7 @@
#include <sstream>
// dlopen_ext support from bionic.
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
#include "android/dlext.h"
#endif
@@ -623,7 +623,7 @@
*error_msg = StringPrintf("Failed to find absolute path for '%s'", elf_filename.c_str());
return false;
}
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
android_dlextinfo extinfo;
extinfo.flags = ANDROID_DLEXT_FORCE_LOAD | // Force-load, don't reuse handle
// (open oat files multiple
@@ -638,7 +638,7 @@
#else
dlopen_handle_ = dlopen(absolute_path.get(), RTLD_NOW);
UNUSED(oat_file_begin);
-#endif
+#endif // ART_TARGET_ANDROID
}
if (dlopen_handle_ == nullptr) {
*error_msg = StringPrintf("Failed to dlopen '%s': %s", elf_filename.c_str(), dlerror());
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 9894353..9ab0072 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -74,6 +74,20 @@
compare.release();
}
+const OatFile* OatFileManager::FindOpenedOatFileFromDexLocation(
+ const std::string& dex_base_location) const {
+ ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
+ for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) {
+ const std::vector<const OatDexFile*>& oat_dex_files = oat_file->GetOatDexFiles();
+ for (const OatDexFile* oat_dex_file : oat_dex_files) {
+ if (DexFile::GetBaseLocation(oat_dex_file->GetDexFileLocation()) == dex_base_location) {
+ return oat_file.get();
+ }
+ }
+ }
+ return nullptr;
+}
+
const OatFile* OatFileManager::FindOpenedOatFileFromOatLocation(const std::string& oat_location)
const {
ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index 574d0e2..f98102e 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -60,6 +60,11 @@
const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location) const
REQUIRES(!Locks::oat_file_manager_lock_);
+ // Find the oat file which contains a dex files with the given dex base location,
+ // returns null if there are none.
+ const OatFile* FindOpenedOatFileFromDexLocation(const std::string& dex_base_location) const
+ REQUIRES(!Locks::oat_file_manager_lock_);
+
// Attempt to reserve a location, returns false if it is already reserved or already in used by
// an oat file.
bool RegisterOatFileLocation(const std::string& oat_location)
diff --git a/runtime/openjdkjvm/OpenjdkJvm.cc b/runtime/openjdkjvm/OpenjdkJvm.cc
index 1f33651..ca5efe5 100644
--- a/runtime/openjdkjvm/OpenjdkJvm.cc
+++ b/runtime/openjdkjvm/OpenjdkJvm.cc
@@ -58,10 +58,10 @@
#include <sys/socket.h>
#include <sys/ioctl.h>
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
// This function is provided by android linker.
extern "C" void android_update_LD_LIBRARY_PATH(const char* ld_library_path);
-#endif // __ANDROID__
+#endif // ART_TARGET_ANDROID
#undef LOG_TAG
#define LOG_TAG "artopenjdk"
@@ -325,7 +325,7 @@
}
static void SetLdLibraryPath(JNIEnv* env, jstring javaLdLibraryPath) {
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
if (javaLdLibraryPath != nullptr) {
ScopedUtfChars ldLibraryPath(env, javaLdLibraryPath);
if (ldLibraryPath.c_str() != nullptr) {
diff --git a/runtime/prebuilt_tools_test.cc b/runtime/prebuilt_tools_test.cc
index eb226d4..c2b34c8 100644
--- a/runtime/prebuilt_tools_test.cc
+++ b/runtime/prebuilt_tools_test.cc
@@ -23,7 +23,7 @@
namespace art {
// Run the tests only on host.
-#ifndef __ANDROID__
+#ifndef ART_TARGET_ANDROID
class PrebuiltToolsTest : public CommonRuntimeTest {
};
@@ -61,6 +61,6 @@
}
}
-#endif // __ANDROID__
+#endif // ART_TARGET_ANDROID
} // namespace art
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 2489e45..a4d31ef 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -545,7 +545,7 @@
// If a debug host build, disable ptrace restriction for debugging and test timeout thread dump.
// Only 64-bit as prctl() may fail in 32 bit userspace on a 64-bit kernel.
-#if defined(__linux__) && !defined(__ANDROID__) && defined(__x86_64__)
+#if defined(__linux__) && !defined(ART_TARGET_ANDROID) && defined(__x86_64__)
if (kIsDebugBuild) {
CHECK_EQ(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY), 0);
}
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index f5d20bd..d98f82a 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -19,7 +19,7 @@
#include "thread.h"
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
#include <bionic_tls.h> // Access to our own TLS slot.
#endif
@@ -45,7 +45,7 @@
if (!is_started_) {
return nullptr;
} else {
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
void* thread = __get_tls()[TLS_SLOT_ART_THREAD_SELF];
#else
void* thread = pthread_getspecific(Thread::pthread_key_self_);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index e015833..7922b60 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -704,7 +704,7 @@
InitTid();
interpreter::InitInterpreterTls(this);
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
__get_tls()[TLS_SLOT_ART_THREAD_SELF] = this;
#else
CHECK_PTHREAD_CALL(pthread_setspecific, (Thread::pthread_key_self_, this), "attach self");
@@ -1542,7 +1542,7 @@
LOG(WARNING) << "Native thread exiting without having called DetachCurrentThread (maybe it's "
"going to use a pthread_key_create destructor?): " << *self;
CHECK(is_started_);
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
__get_tls()[TLS_SLOT_ART_THREAD_SELF] = self;
#else
CHECK_PTHREAD_CALL(pthread_setspecific, (Thread::pthread_key_self_, self), "reattach self");
@@ -2392,7 +2392,7 @@
std::string str(ss.str());
// log to stderr for debugging command line processes
std::cerr << str;
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
// log to logcat for debugging frameworks processes
LOG(INFO) << str;
#endif
diff --git a/runtime/thread_linux.cc b/runtime/thread_linux.cc
index 9563b99..b922d94 100644
--- a/runtime/thread_linux.cc
+++ b/runtime/thread_linux.cc
@@ -44,7 +44,7 @@
void Thread::SetUpAlternateSignalStack() {
// Create and set an alternate signal stack.
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
LOG(FATAL) << "Invalid use of alternate signal stack on Android";
#endif
stack_t ss;
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 1e5264c..da21479 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -1277,7 +1277,7 @@
// Clear the TLS data, so that the underlying native thread is recognizably detached.
// (It may wish to reattach later.)
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
__get_tls()[TLS_SLOT_ART_THREAD_SELF] = nullptr;
#else
CHECK_PTHREAD_CALL(pthread_setspecific, (Thread::pthread_key_self_, nullptr), "detach self");
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index 2fba805..b14f340 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -61,7 +61,7 @@
void ThreadPoolWorker::SetPthreadPriority(int priority) {
CHECK_GE(priority, PRIO_MIN);
CHECK_LE(priority, PRIO_MAX);
-#if defined(__ANDROID__)
+#if defined(ART_TARGET_ANDROID)
int result = setpriority(PRIO_PROCESS, pthread_gettid_np(pthread_), priority);
if (result != 0) {
PLOG(ERROR) << "Failed to setpriority to :" << priority;
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 472a85c..6a50b8e 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1459,6 +1459,14 @@
return stat(filename.c_str(), &buffer) == 0;
}
+bool FileExistsAndNotEmpty(const std::string& filename) {
+ struct stat buffer;
+ if (stat(filename.c_str(), &buffer) != 0) {
+ return false;
+ }
+ return buffer.st_size > 0;
+}
+
std::string PrettyDescriptor(Primitive::Type type) {
return PrettyDescriptor(Primitive::Descriptor(type));
}
diff --git a/runtime/utils.h b/runtime/utils.h
index 83ac0b8..c1e88a4 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -296,6 +296,7 @@
// Returns true if the file exists.
bool FileExists(const std::string& filename);
+bool FileExistsAndNotEmpty(const std::string& filename);
class VoidFunctor {
public:
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 647578e..8802e62 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -396,6 +396,13 @@
<< PrettyMethod(method_idx, *dex_file) << "\n");
}
result.kind = kSoftFailure;
+ if (method != nullptr &&
+ !CanCompilerHandleVerificationFailure(verifier.encountered_failure_types_)) {
+ method->SetAccessFlags(method->GetAccessFlags() | kAccCompileDontBother);
+ }
+ }
+ if (method != nullptr && verifier.HasInstructionThatWillThrow()) {
+ method->SetAccessFlags(method->GetAccessFlags() | kAccCompileDontBother);
}
} else {
// Bad method data.
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index ebb0b8c..2592a21 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -142,6 +142,14 @@
kHardFailure,
};
+ static bool CanCompilerHandleVerificationFailure(uint32_t encountered_failure_types) {
+ constexpr uint32_t unresolved_mask = verifier::VerifyError::VERIFY_ERROR_NO_CLASS
+ | verifier::VerifyError::VERIFY_ERROR_ACCESS_CLASS
+ | verifier::VerifyError::VERIFY_ERROR_ACCESS_FIELD
+ | verifier::VerifyError::VERIFY_ERROR_ACCESS_METHOD;
+ return (encountered_failure_types & (~unresolved_mask)) == 0;
+ }
+
// Verify a class. Returns "kNoFailure" on success.
static FailureKind VerifyClass(Thread* self,
mirror::Class* klass,
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index b76555b..f29301d 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
#include <android/log.h>
#else
#include <stdarg.h>
@@ -103,7 +103,7 @@
va_list ap;
va_start(ap, format);
vsnprintf(buf, sizeof(buf), format, ap);
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
__android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf);
#else
std::cout << buf << "\n";
diff --git a/sigchainlib/sigchain_dummy.cc b/sigchainlib/sigchain_dummy.cc
index dfe0c6f..aa3c360 100644
--- a/sigchainlib/sigchain_dummy.cc
+++ b/sigchainlib/sigchain_dummy.cc
@@ -17,7 +17,7 @@
#include <stdio.h>
#include <stdlib.h>
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
#include <android/log.h>
#else
#include <stdarg.h>
@@ -38,7 +38,7 @@
va_list ap;
va_start(ap, format);
vsnprintf(buf, sizeof(buf), format, ap);
-#ifdef __ANDROID__
+#ifdef ART_TARGET_ANDROID
__android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf);
#else
std::cout << buf << "\n";
diff --git a/test/051-thread/thread_test.cc b/test/051-thread/thread_test.cc
index 4215207..079ad40 100644
--- a/test/051-thread/thread_test.cc
+++ b/test/051-thread/thread_test.cc
@@ -28,7 +28,7 @@
extern "C" JNIEXPORT jboolean JNICALL Java_Main_supportsThreadPriorities(
JNIEnv* env ATTRIBUTE_UNUSED,
jclass clazz ATTRIBUTE_UNUSED) {
-#if defined(__ANDROID__)
+#if defined(ART_TARGET_ANDROID)
return JNI_TRUE;
#else
return JNI_FALSE;
diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt
index d9b26bc..0cd77ab 100644
--- a/tools/ahat/README.txt
+++ b/tools/ahat/README.txt
@@ -77,7 +77,12 @@
* Instance.isRoot and Instance.getRootTypes.
Release History:
- 0.4 Pending
+ 0.6 Pending
+
+ 0.5 Apr 19, 2016
+ Update perflib to perflib-25.0.0 to improve processing performance.
+
+ 0.4 Feb 23, 2016
Annotate char[] objects with their string values.
Show registered native allocations for heap dumps that support it.
diff --git a/tools/ahat/src/AhatSnapshot.java b/tools/ahat/src/AhatSnapshot.java
index 2adec6f..d088e8c 100644
--- a/tools/ahat/src/AhatSnapshot.java
+++ b/tools/ahat/src/AhatSnapshot.java
@@ -25,8 +25,8 @@
import com.android.tools.perflib.heap.StackFrame;
import com.android.tools.perflib.heap.StackTrace;
import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
+import gnu.trove.TObjectProcedure;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -85,49 +85,59 @@
ClassObj javaLangClass = mSnapshot.findClass("java.lang.Class");
for (Heap heap : mHeaps) {
- long total = 0;
- for (Instance inst : Iterables.concat(heap.getClasses(), heap.getInstances())) {
- Instance dominator = inst.getImmediateDominator();
- if (dominator != null) {
- total += inst.getSize();
+ // Use a single element array for the total to act as a reference to a
+ // long.
+ final long[] total = new long[]{0};
+ TObjectProcedure<Instance> processInstance = new TObjectProcedure<Instance>() {
+ @Override
+ public boolean execute(Instance inst) {
+ Instance dominator = inst.getImmediateDominator();
+ if (dominator != null) {
+ total[0] += inst.getSize();
- if (dominator == Snapshot.SENTINEL_ROOT) {
- mRooted.add(inst);
- }
+ if (dominator == Snapshot.SENTINEL_ROOT) {
+ mRooted.add(inst);
+ }
- // Properly label the class of a class object.
- if (inst instanceof ClassObj && javaLangClass != null && inst.getClassObj() == null) {
- inst.setClassId(javaLangClass.getId());
- }
+ // Properly label the class of a class object.
+ if (inst instanceof ClassObj && javaLangClass != null && inst.getClassObj() == null) {
+ inst.setClassId(javaLangClass.getId());
+ }
- // Update dominated instances.
- List<Instance> instances = mDominated.get(dominator);
- if (instances == null) {
- instances = new ArrayList<Instance>();
- mDominated.put(dominator, instances);
- }
- instances.add(inst);
+ // Update dominated instances.
+ List<Instance> instances = mDominated.get(dominator);
+ if (instances == null) {
+ instances = new ArrayList<Instance>();
+ mDominated.put(dominator, instances);
+ }
+ instances.add(inst);
- // Update sites.
- List<StackFrame> path = Collections.emptyList();
- StackTrace stack = getStack(inst);
- int stackId = getStackTraceSerialNumber(stack);
- if (stack != null) {
- StackFrame[] frames = getStackFrames(stack);
- if (frames != null && frames.length > 0) {
- path = Lists.reverse(Arrays.asList(frames));
+ // Update sites.
+ List<StackFrame> path = Collections.emptyList();
+ StackTrace stack = getStack(inst);
+ int stackId = getStackTraceSerialNumber(stack);
+ if (stack != null) {
+ StackFrame[] frames = getStackFrames(stack);
+ if (frames != null && frames.length > 0) {
+ path = Lists.reverse(Arrays.asList(frames));
+ }
+ }
+ mRootSite.add(stackId, 0, path.iterator(), inst);
+
+ // Update native allocations.
+ InstanceUtils.NativeAllocation alloc = InstanceUtils.getNativeAllocation(inst);
+ if (alloc != null) {
+ mNativeAllocations.add(alloc);
}
}
- mRootSite.add(stackId, 0, path.iterator(), inst);
-
- // Update native allocations.
- InstanceUtils.NativeAllocation alloc = InstanceUtils.getNativeAllocation(inst);
- if (alloc != null) {
- mNativeAllocations.add(alloc);
- }
+ return true;
}
+ };
+ for (Instance instance : heap.getClasses()) {
+ processInstance.execute(instance);
}
- mHeapSizes.put(heap, total);
+ heap.forEachInstance(processInstance);
+ mHeapSizes.put(heap, total[0]);
}
// Record the roots and their types.
diff --git a/tools/ahat/src/InstanceUtils.java b/tools/ahat/src/InstanceUtils.java
index d7b64e2..8defba2 100644
--- a/tools/ahat/src/InstanceUtils.java
+++ b/tools/ahat/src/InstanceUtils.java
@@ -244,8 +244,8 @@
if (inst instanceof ArrayInstance) {
ArrayInstance array = (ArrayInstance)inst;
- if (array.getArrayType() == Type.BYTE && inst.getHardReferences().size() == 1) {
- Instance ref = inst.getHardReferences().get(0);
+ if (array.getArrayType() == Type.BYTE && inst.getHardReverseReferences().size() == 1) {
+ Instance ref = inst.getHardReverseReferences().get(0);
ClassObj clsref = ref.getClassObj();
if (clsref != null && "android.graphics.Bitmap".equals(clsref.getClassName())) {
return ref;
@@ -344,7 +344,7 @@
}
Instance referent = null;
- for (Instance ref : inst.getHardReferences()) {
+ for (Instance ref : inst.getHardReverseReferences()) {
if (isInstanceOfClass(ref, "sun.misc.Cleaner")) {
referent = InstanceUtils.getReferent(ref);
if (referent != null) {
diff --git a/tools/ahat/src/ObjectHandler.java b/tools/ahat/src/ObjectHandler.java
index 06023da..4df1be5 100644
--- a/tools/ahat/src/ObjectHandler.java
+++ b/tools/ahat/src/ObjectHandler.java
@@ -160,11 +160,11 @@
private static void printReferences(
Doc doc, Query query, AhatSnapshot snapshot, Instance inst) {
doc.section("Objects with References to this Object");
- if (inst.getHardReferences().isEmpty()) {
+ if (inst.getHardReverseReferences().isEmpty()) {
doc.println(DocString.text("(none)"));
} else {
doc.table(new Column("Object"));
- List<Instance> references = inst.getHardReferences();
+ List<Instance> references = inst.getHardReverseReferences();
SubsetSelector<Instance> selector = new SubsetSelector(query, HARD_REFS_ID, references);
for (Instance ref : selector.selected()) {
doc.row(Value.render(snapshot, ref));
@@ -173,10 +173,10 @@
selector.render(doc);
}
- if (inst.getSoftReferences() != null) {
+ if (inst.getSoftReverseReferences() != null) {
doc.section("Objects with Soft References to this Object");
doc.table(new Column("Object"));
- List<Instance> references = inst.getSoftReferences();
+ List<Instance> references = inst.getSoftReverseReferences();
SubsetSelector<Instance> selector = new SubsetSelector(query, SOFT_REFS_ID, references);
for (Instance ref : selector.selected()) {
doc.row(Value.render(snapshot, ref));