Merge "Reduce number of physical pages used for DexCache's arrays"
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 72d7df3..13a3235 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -407,7 +407,7 @@
Result ParseAndAppend(const std::string& args,
std::vector<ti::Agent>& existing_value) {
- existing_value.push_back(ti::Agent::Create(args));
+ existing_value.emplace_back(args);
return Result::SuccessNoValue();
}
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 61b7f08..1e71d06 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -2830,7 +2830,7 @@
void Thumb2Assembler::clrex(Condition cond) {
CheckCondition(cond);
- int32_t encoding = B31 | B30 | B29 | B27 | B28 | B25 | B24 | B23 |
+ int32_t encoding = B31 | B30 | B29 | B28 | B25 | B24 | B23 |
B21 | B20 |
0xf << 16 |
B15 |
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index d0799d6..30e8f4e 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -207,6 +207,13 @@
DriverStr(expected, "strexd");
}
+TEST_F(AssemblerThumb2Test, clrex) {
+ __ clrex();
+
+ const char* expected = "clrex\n";
+ DriverStr(expected, "clrex");
+}
+
TEST_F(AssemblerThumb2Test, LdrdStrd) {
__ ldrd(arm::R0, arm::Address(arm::R2, 8));
__ ldrd(arm::R0, arm::Address(arm::R12));
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 15b6e17..30de28e 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -1581,10 +1581,15 @@
/*
* Dumps the requested sections of the file.
*/
-static void processDexFile(const char* fileName, const DexFile* pDexFile) {
+static void processDexFile(const char* fileName,
+ const DexFile* pDexFile, size_t i, size_t n) {
if (gOptions.verbose) {
- fprintf(gOutFile, "Opened '%s', DEX version '%.3s'\n",
- fileName, pDexFile->GetHeader().magic_ + 4);
+ fputs("Opened '", gOutFile);
+ fputs(fileName, gOutFile);
+ if (n > 1) {
+ fprintf(gOutFile, ":%s", DexFile::GetMultiDexClassesDexName(i).c_str());
+ }
+ fprintf(gOutFile, "', DEX version '%.3s'\n", pDexFile->GetHeader().magic_ + 4);
}
// Headers.
@@ -1642,8 +1647,8 @@
if (gOptions.checksumOnly) {
fprintf(gOutFile, "Checksum verified\n");
} else {
- for (size_t i = 0; i < dex_files.size(); i++) {
- processDexFile(fileName, dex_files[i].get());
+ for (size_t i = 0, n = dex_files.size(); i < n; i++) {
+ processDexFile(fileName, dex_files[i].get(), i, n);
}
}
return 0;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 4e077c0..3ae36de 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2266,8 +2266,7 @@
}
CHECK(h_class->IsRetired());
// Get the updated class from class table.
- klass = LookupClass(self, descriptor, ComputeModifiedUtf8Hash(descriptor),
- h_class.Get()->GetClassLoader());
+ klass = LookupClass(self, descriptor, h_class.Get()->GetClassLoader());
}
// Wait for the class if it has not already been linked.
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 239e973..f2bf581 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -171,20 +171,6 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!dex_lock_);
- // Finds a class in the path class loader, loading it if necessary without using JNI. Hash
- // function is supposed to be ComputeModifiedUtf8Hash(descriptor). Returns true if the
- // class-loader chain could be handled, false otherwise, i.e., a non-supported class-loader
- // was encountered while walking the parent chain (currently only BootClassLoader and
- // PathClassLoader are supported).
- bool FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
- const char* descriptor,
- size_t hash,
- Handle<mirror::ClassLoader> class_loader,
- ObjPtr<mirror::Class>* result)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!dex_lock_);
-
// Finds a class by its descriptor using the "system" class loader, ie by searching the
// boot_class_path_.
mirror::Class* FindSystemClass(Thread* self, const char* descriptor)
@@ -215,10 +201,11 @@
// by the given 'class_loader'.
mirror::Class* LookupClass(Thread* self,
const char* descriptor,
- size_t hash,
ObjPtr<mirror::ClassLoader> class_loader)
REQUIRES(!Locks::classlinker_classes_lock_)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return LookupClass(self, descriptor, ComputeModifiedUtf8Hash(descriptor), class_loader);
+ }
// Finds all the classes with the given descriptor, regardless of ClassLoader.
void LookupClasses(const char* descriptor, std::vector<ObjPtr<mirror::Class>>& classes)
@@ -804,6 +791,29 @@
void FixupStaticTrampolines(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Finds a class in the path class loader, loading it if necessary without using JNI. Hash
+ // function is supposed to be ComputeModifiedUtf8Hash(descriptor). Returns true if the
+ // class-loader chain could be handled, false otherwise, i.e., a non-supported class-loader
+ // was encountered while walking the parent chain (currently only BootClassLoader and
+ // PathClassLoader are supported).
+ bool FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+ Thread* self,
+ const char* descriptor,
+ size_t hash,
+ Handle<mirror::ClassLoader> class_loader,
+ ObjPtr<mirror::Class>* result)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
+
+ // Finds a class by its descriptor, returning NULL if it isn't wasn't loaded
+ // by the given 'class_loader'. Uses the provided hash for the descriptor.
+ mirror::Class* LookupClass(Thread* self,
+ const char* descriptor,
+ size_t hash,
+ ObjPtr<mirror::ClassLoader> class_loader)
+ REQUIRES(!Locks::classlinker_classes_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
void RegisterDexFileLocked(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
REQUIRES(dex_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1187,6 +1197,7 @@
friend struct CompilationHelper; // For Compile in ImageTest.
friend class ImageDumper; // for DexLock
friend class ImageWriter; // for GetClassRoots
+ friend class VMClassLoader; // for LookupClass and FindClassInPathClassLoader.
friend class JniCompilerTest; // for GetRuntimeQuickGenericJniStub
friend class JniInternalTest; // for GetRuntimeQuickGenericJniStub
ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName); // for DexLock, and RegisterDexFileLocked
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index b0aba59..0251776 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -686,6 +686,15 @@
va_end(args);
}
+// SecurityException
+
+void ThrowSecurityException(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ ThrowException("Ljava/lang/SecurityException;", nullptr, fmt, &args);
+ va_end(args);
+}
+
// Stack overflow.
void ThrowStackOverflowError(Thread* self) {
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 5d0bc12..76ea2ae 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -215,6 +215,12 @@
__attribute__((__format__(__printf__, 1, 2)))
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+// SecurityException
+
+void ThrowSecurityException(const char* fmt, ...)
+ __attribute__((__format__(__printf__, 1, 2)))
+ REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
// Stack overflow.
void ThrowStackOverflowError(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index e2b8f51..1da888e 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -551,6 +551,10 @@
gJdwpAllowed = allowed;
}
+bool Dbg::IsJdwpAllowed() {
+ return gJdwpAllowed;
+}
+
DebugInvokeReq* Dbg::GetInvokeReq() {
return Thread::Current()->GetInvokeReq();
}
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 5d0315e..3b4a5e1 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -202,6 +202,7 @@
class Dbg {
public:
static void SetJdwpAllowed(bool allowed);
+ static bool IsJdwpAllowed();
static void StartJdwp();
static void StopJdwp();
diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h
index f72f219..6ff5359 100644
--- a/runtime/gc/accounting/card_table-inl.h
+++ b/runtime/gc/accounting/card_table-inl.h
@@ -50,13 +50,17 @@
}
template <bool kClearCard, typename Visitor>
-inline size_t CardTable::Scan(ContinuousSpaceBitmap* bitmap, uint8_t* scan_begin, uint8_t* scan_end,
- const Visitor& visitor, const uint8_t minimum_age) const {
+inline size_t CardTable::Scan(ContinuousSpaceBitmap* bitmap,
+ uint8_t* const scan_begin,
+ uint8_t* const scan_end,
+ const Visitor& visitor,
+ const uint8_t minimum_age) {
DCHECK_GE(scan_begin, reinterpret_cast<uint8_t*>(bitmap->HeapBegin()));
// scan_end is the byte after the last byte we scan.
DCHECK_LE(scan_end, reinterpret_cast<uint8_t*>(bitmap->HeapLimit()));
- uint8_t* card_cur = CardFromAddr(scan_begin);
- uint8_t* card_end = CardFromAddr(AlignUp(scan_end, kCardSize));
+ uint8_t* const card_begin = CardFromAddr(scan_begin);
+ uint8_t* const card_end = CardFromAddr(AlignUp(scan_end, kCardSize));
+ uint8_t* card_cur = card_begin;
CheckCardValid(card_cur);
CheckCardValid(card_end);
size_t cards_scanned = 0;
@@ -67,9 +71,6 @@
uintptr_t start = reinterpret_cast<uintptr_t>(AddrFromCard(card_cur));
bitmap->VisitMarkedRange(start, start + kCardSize, visitor);
++cards_scanned;
- if (kClearCard) {
- *card_cur = 0;
- }
}
++card_cur;
}
@@ -99,9 +100,6 @@
<< "card " << static_cast<size_t>(*card) << " intptr_t " << (start_word & 0xFF);
bitmap->VisitMarkedRange(start, start + kCardSize, visitor);
++cards_scanned;
- if (kClearCard) {
- *card = 0;
- }
}
start_word >>= 8;
start += kCardSize;
@@ -116,13 +114,14 @@
uintptr_t start = reinterpret_cast<uintptr_t>(AddrFromCard(card_cur));
bitmap->VisitMarkedRange(start, start + kCardSize, visitor);
++cards_scanned;
- if (kClearCard) {
- *card_cur = 0;
- }
}
++card_cur;
}
+ if (kClearCard) {
+ ClearCardRange(scan_begin, scan_end);
+ }
+
return cards_scanned;
}
@@ -135,7 +134,9 @@
* us to know which cards got cleared.
*/
template <typename Visitor, typename ModifiedVisitor>
-inline void CardTable::ModifyCardsAtomic(uint8_t* scan_begin, uint8_t* scan_end, const Visitor& visitor,
+inline void CardTable::ModifyCardsAtomic(uint8_t* scan_begin,
+ uint8_t* scan_end,
+ const Visitor& visitor,
const ModifiedVisitor& modified) {
uint8_t* card_cur = CardFromAddr(scan_begin);
uint8_t* card_end = CardFromAddr(AlignUp(scan_end, kCardSize));
diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc
index 121da37..4506597 100644
--- a/runtime/gc/accounting/card_table.cc
+++ b/runtime/gc/accounting/card_table.cc
@@ -97,36 +97,18 @@
// Destroys MemMap via std::unique_ptr<>.
}
-void CardTable::ClearSpaceCards(space::ContinuousSpace* space) {
- // TODO: clear just the range of the table that has been modified
- uint8_t* card_start = CardFromAddr(space->Begin());
- uint8_t* card_end = CardFromAddr(space->End()); // Make sure to round up.
- memset(reinterpret_cast<void*>(card_start), kCardClean, card_end - card_start);
-}
-
void CardTable::ClearCardTable() {
static_assert(kCardClean == 0, "kCardClean must be 0");
mem_map_->MadviseDontNeedAndZero();
}
void CardTable::ClearCardRange(uint8_t* start, uint8_t* end) {
- if (!kMadviseZeroes) {
- memset(start, 0, end - start);
- return;
- }
CHECK_ALIGNED(reinterpret_cast<uintptr_t>(start), kCardSize);
CHECK_ALIGNED(reinterpret_cast<uintptr_t>(end), kCardSize);
static_assert(kCardClean == 0, "kCardClean must be 0");
uint8_t* start_card = CardFromAddr(start);
uint8_t* end_card = CardFromAddr(end);
- uint8_t* round_start = AlignUp(start_card, kPageSize);
- uint8_t* round_end = AlignDown(end_card, kPageSize);
- if (round_start < round_end) {
- madvise(round_start, round_end - round_start, MADV_DONTNEED);
- }
- // Handle unaligned regions at start / end.
- memset(start_card, 0, std::min(round_start, end_card) - start_card);
- memset(std::max(round_end, start_card), 0, end_card - std::max(round_end, start_card));
+ ZeroAndReleasePages(start_card, end_card - start_card);
}
bool CardTable::AddrIsInCardTable(const void* addr) const {
diff --git a/runtime/gc/accounting/card_table.h b/runtime/gc/accounting/card_table.h
index 969bfb7..68ef15d 100644
--- a/runtime/gc/accounting/card_table.h
+++ b/runtime/gc/accounting/card_table.h
@@ -98,15 +98,19 @@
* us to know which cards got cleared.
*/
template <typename Visitor, typename ModifiedVisitor>
- void ModifyCardsAtomic(uint8_t* scan_begin, uint8_t* scan_end, const Visitor& visitor,
+ void ModifyCardsAtomic(uint8_t* scan_begin,
+ uint8_t* scan_end,
+ const Visitor& visitor,
const ModifiedVisitor& modified);
// For every dirty at least minumum age between begin and end invoke the visitor with the
// specified argument. Returns how many cards the visitor was run on.
template <bool kClearCard, typename Visitor>
- size_t Scan(SpaceBitmap<kObjectAlignment>* bitmap, uint8_t* scan_begin, uint8_t* scan_end,
+ size_t Scan(SpaceBitmap<kObjectAlignment>* bitmap,
+ uint8_t* scan_begin,
+ uint8_t* scan_end,
const Visitor& visitor,
- const uint8_t minimum_age = kCardDirty) const
+ const uint8_t minimum_age = kCardDirty)
REQUIRES(Locks::heap_bitmap_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -119,9 +123,6 @@
// Clear a range of cards that covers start to end, start and end must be aligned to kCardSize.
void ClearCardRange(uint8_t* start, uint8_t* end);
- // Resets all of the bytes in the card table which do not map to the image space.
- void ClearSpaceCards(space::ContinuousSpace* space);
-
// Returns the first address in the heap which maps to this card.
void* AddrFromCard(const uint8_t *card_addr) const ALWAYS_INLINE;
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index 14f5997..0325535 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -168,7 +168,7 @@
bool* const contains_reference_to_other_space_;
};
-void ModUnionTableReferenceCache::ClearCards() {
+void ModUnionTableReferenceCache::ProcessCards() {
CardTable* card_table = GetHeap()->GetCardTable();
ModUnionAddToCardSetVisitor visitor(&cleared_cards_);
// Clear dirty cards in the this space and update the corresponding mod-union bits.
@@ -525,7 +525,7 @@
ModUnionTable::CardBitmap* const card_bitmap_;
};
-void ModUnionTableCardCache::ClearCards() {
+void ModUnionTableCardCache::ProcessCards() {
CardTable* const card_table = GetHeap()->GetCardTable();
ModUnionAddToCardBitmapVisitor visitor(card_bitmap_.get(), card_table);
// Clear dirty cards in the this space and update the corresponding mod-union bits.
diff --git a/runtime/gc/accounting/mod_union_table.h b/runtime/gc/accounting/mod_union_table.h
index b6792c4..591365f 100644
--- a/runtime/gc/accounting/mod_union_table.h
+++ b/runtime/gc/accounting/mod_union_table.h
@@ -55,10 +55,10 @@
virtual ~ModUnionTable() {}
- // Clear cards which map to a memory range of a space. This doesn't immediately update the
- // mod-union table, as updating the mod-union table may have an associated cost, such as
- // determining references to track.
- virtual void ClearCards() = 0;
+ // Process cards for a memory range of a space. This doesn't immediately update the mod-union
+ // table, as updating the mod-union table may have an associated cost, such as determining
+ // references to track.
+ virtual void ProcessCards() = 0;
// Set all the cards.
virtual void SetCards() = 0;
@@ -66,9 +66,9 @@
// Clear all of the table.
virtual void ClearTable() = 0;
- // Update the mod-union table using data stored by ClearCards. There may be multiple ClearCards
- // before a call to update, for example, back-to-back sticky GCs. Also mark references to other
- // spaces which are stored in the mod-union table.
+ // Update the mod-union table using data stored by ProcessCards. There may be multiple
+ // ProcessCards before a call to update, for example, back-to-back sticky GCs. Also mark
+ // references to other spaces which are stored in the mod-union table.
virtual void UpdateAndMarkReferences(MarkObjectVisitor* visitor) = 0;
// Visit all of the objects that may contain references to other spaces.
@@ -117,7 +117,7 @@
virtual ~ModUnionTableReferenceCache() {}
// Clear and store cards for a space.
- void ClearCards() OVERRIDE;
+ void ProcessCards() OVERRIDE;
// Update table based on cleared cards and mark all references to the other spaces.
void UpdateAndMarkReferences(MarkObjectVisitor* visitor) OVERRIDE
@@ -164,7 +164,7 @@
virtual ~ModUnionTableCardCache() {}
// Clear and store cards for a space.
- virtual void ClearCards() OVERRIDE;
+ virtual void ProcessCards() OVERRIDE;
// Mark all references to the alloc space(s).
virtual void UpdateAndMarkReferences(MarkObjectVisitor* visitor) OVERRIDE
diff --git a/runtime/gc/accounting/mod_union_table_test.cc b/runtime/gc/accounting/mod_union_table_test.cc
index 2810f58..cf63b30 100644
--- a/runtime/gc/accounting/mod_union_table_test.cc
+++ b/runtime/gc/accounting/mod_union_table_test.cc
@@ -214,7 +214,7 @@
ASSERT_TRUE(other_space_ref2 != nullptr);
obj1->Set(1, other_space_ref1);
obj2->Set(3, other_space_ref2);
- table->ClearCards();
+ table->ProcessCards();
std::set<mirror::Object*> visited_before;
CollectVisitedVisitor collector_before(&visited_before);
table->UpdateAndMarkReferences(&collector_before);
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index e2f5a1d..f4d0bc7 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -118,31 +118,8 @@
}
const uintptr_t start_index = OffsetToIndex(begin_offset);
const uintptr_t end_index = OffsetToIndex(end_offset);
- Atomic<uintptr_t>* const mem_begin = &bitmap_begin_[start_index];
- Atomic<uintptr_t>* const mem_end = &bitmap_begin_[end_index];
- Atomic<uintptr_t>* const page_begin = AlignUp(mem_begin, kPageSize);
- Atomic<uintptr_t>* const page_end = AlignDown(mem_end, kPageSize);
- if (!kMadviseZeroes || page_begin >= page_end) {
- // No possible area to madvise.
- std::fill(reinterpret_cast<uint8_t*>(mem_begin),
- reinterpret_cast<uint8_t*>(mem_end),
- 0);
- } else {
- // Spans one or more pages.
- DCHECK_LE(mem_begin, page_begin);
- DCHECK_LE(page_begin, page_end);
- DCHECK_LE(page_end, mem_end);
- std::fill(reinterpret_cast<uint8_t*>(mem_begin),
- reinterpret_cast<uint8_t*>(page_begin),
- 0);
- CHECK_NE(madvise(page_begin,
- reinterpret_cast<uint8_t*>(page_end) - reinterpret_cast<uint8_t*>(page_begin),
- MADV_DONTNEED),
- -1) << "madvise failed";
- std::fill(reinterpret_cast<uint8_t*>(page_end),
- reinterpret_cast<uint8_t*>(mem_end),
- 0);
- }
+ ZeroAndReleasePages(reinterpret_cast<uint8_t*>(&bitmap_begin_[start_index]),
+ (end_index - start_index) * sizeof(*bitmap_begin_));
}
template<size_t kAlignment>
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 13af67e..8bb90e1 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -486,9 +486,14 @@
// Table is non null for boot image and zygote spaces. It is only null for application image
// spaces.
if (table != nullptr) {
- // TODO: Add preclean outside the pause.
- table->ClearCards();
+ // TODO: Consider adding precleaning outside the pause.
+ table->ProcessCards();
table->VisitObjects(GrayImmuneObjectVisitor::Callback, &visitor);
+ // Since the cards are recorded in the mod-union table and this is paused, we can clear
+ // the cards for the space (to madvise).
+ TimingLogger::ScopedTiming split2("(Paused)ClearCards", GetTimings());
+ card_table->ClearCardRange(space->Begin(),
+ AlignDown(space->End(), accounting::CardTable::kCardSize));
} else {
// TODO: Consider having a mark bitmap for app image spaces and avoid scanning during the
// pause because app image spaces are all dirty pages anyways.
@@ -1513,8 +1518,9 @@
accounting::LargeObjectBitmap* const live_bitmap = los->GetLiveBitmap();
accounting::LargeObjectBitmap* const mark_bitmap = los->GetMarkBitmap();
// Walk through all of the objects and explicitly mark the zygote ones so they don't get swept.
- live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(los->Begin()),
- reinterpret_cast<uintptr_t>(los->End()),
+ std::pair<uint8_t*, uint8_t*> range = los->GetBeginEndAtomic();
+ live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(range.first),
+ reinterpret_cast<uintptr_t>(range.second),
[mark_bitmap, los, self](mirror::Object* obj)
REQUIRES(Locks::heap_bitmap_lock_)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -2325,9 +2331,14 @@
MutexLock mu(self, mark_stack_lock_);
CHECK_EQ(pooled_mark_stacks_.size(), kMarkStackPoolSize);
}
- region_space_ = nullptr;
{
- MutexLock mu(Thread::Current(), skipped_blocks_lock_);
+ TimingLogger::ScopedTiming split("ClearRegionSpaceCards", GetTimings());
+ // We do not currently use the region space cards at all, madvise them away to save ram.
+ heap_->GetCardTable()->ClearCardRange(region_space_->Begin(), region_space_->Limit());
+ region_space_ = nullptr;
+ }
+ {
+ MutexLock mu(self, skipped_blocks_lock_);
skipped_blocks_map_.clear();
}
{
@@ -2339,10 +2350,9 @@
if (kUseBakerReadBarrier && kFilterModUnionCards) {
TimingLogger::ScopedTiming split("FilterModUnionCards", GetTimings());
ReaderMutexLock mu2(self, *Locks::heap_bitmap_lock_);
- gc::Heap* const heap = Runtime::Current()->GetHeap();
for (space::ContinuousSpace* space : immune_spaces_.GetSpaces()) {
DCHECK(space->IsImageSpace() || space->IsZygoteSpace());
- accounting::ModUnionTable* table = heap->FindModUnionTableFromSpace(space);
+ accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space);
// Filter out cards that don't need to be set.
if (table != nullptr) {
table->FilterCards();
@@ -2351,7 +2361,7 @@
}
if (kUseBakerReadBarrier) {
TimingLogger::ScopedTiming split("EmptyRBMarkBitStack", GetTimings());
- DCHECK(rb_mark_bit_stack_.get() != nullptr);
+ DCHECK(rb_mark_bit_stack_ != nullptr);
const auto* limit = rb_mark_bit_stack_->End();
for (StackReference<mirror::Object>* it = rb_mark_bit_stack_->Begin(); it != limit; ++it) {
CHECK(it->AsMirrorPtr()->AtomicSetMarkBit(1, 0));
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 2cb1767..2ff4a3f 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -408,8 +408,9 @@
// classes (primitive array classes) that could move though they
// don't contain any other references.
accounting::LargeObjectBitmap* large_live_bitmap = los->GetLiveBitmap();
- large_live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(los->Begin()),
- reinterpret_cast<uintptr_t>(los->End()),
+ std::pair<uint8_t*, uint8_t*> range = los->GetBeginEndAtomic();
+ large_live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(range.first),
+ reinterpret_cast<uintptr_t>(range.second),
[this](mirror::Object* obj)
REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
ScanObject(obj);
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 3b9abd2..ffad80d 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -3327,7 +3327,7 @@
const char* name = space->IsZygoteSpace() ? "ZygoteModUnionClearCards" :
"ImageModUnionClearCards";
TimingLogger::ScopedTiming t2(name, timings);
- table->ClearCards();
+ table->ProcessCards();
} else if (use_rem_sets && rem_set != nullptr) {
DCHECK(collector::SemiSpace::kUseRememberedSet && collector_type_ == kCollectorTypeGSS)
<< static_cast<int>(collector_type_);
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index 2d5d7cb..e71a397 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -606,9 +606,12 @@
std::swap(live_bitmap, mark_bitmap);
}
AllocSpace::SweepCallbackContext scc(swap_bitmaps, this);
+ std::pair<uint8_t*, uint8_t*> range = GetBeginEndAtomic();
accounting::LargeObjectBitmap::SweepWalk(*live_bitmap, *mark_bitmap,
- reinterpret_cast<uintptr_t>(Begin()),
- reinterpret_cast<uintptr_t>(End()), SweepCallback, &scc);
+ reinterpret_cast<uintptr_t>(range.first),
+ reinterpret_cast<uintptr_t>(range.second),
+ SweepCallback,
+ &scc);
return scc.freed;
}
@@ -617,6 +620,16 @@
UNIMPLEMENTED(FATAL);
}
+std::pair<uint8_t*, uint8_t*> LargeObjectMapSpace::GetBeginEndAtomic() const {
+ MutexLock mu(Thread::Current(), lock_);
+ return std::make_pair(Begin(), End());
+}
+
+std::pair<uint8_t*, uint8_t*> FreeListSpace::GetBeginEndAtomic() const {
+ MutexLock mu(Thread::Current(), lock_);
+ return std::make_pair(Begin(), End());
+}
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h
index 0320e79..38e28b1 100644
--- a/runtime/gc/space/large_object_space.h
+++ b/runtime/gc/space/large_object_space.h
@@ -104,6 +104,10 @@
// objects.
virtual void SetAllLargeObjectsAsZygoteObjects(Thread* self) = 0;
+ // GetRangeAtomic returns Begin() and End() atomically, that is, it never returns Begin() and
+ // End() from different allocations.
+ virtual std::pair<uint8_t*, uint8_t*> GetBeginEndAtomic() const = 0;
+
protected:
explicit LargeObjectSpace(const std::string& name, uint8_t* begin, uint8_t* end);
static void SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg);
@@ -139,6 +143,8 @@
// TODO: disabling thread safety analysis as this may be called when we already hold lock_.
bool Contains(const mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS;
+ std::pair<uint8_t*, uint8_t*> GetBeginEndAtomic() const OVERRIDE REQUIRES(!lock_);
+
protected:
struct LargeObject {
MemMap* mem_map;
@@ -172,6 +178,8 @@
void Walk(DlMallocSpace::WalkCallback callback, void* arg) OVERRIDE REQUIRES(!lock_);
void Dump(std::ostream& os) const REQUIRES(!lock_);
+ std::pair<uint8_t*, uint8_t*> GetBeginEndAtomic() const OVERRIDE REQUIRES(!lock_);
+
protected:
FreeListSpace(const std::string& name, MemMap* mem_map, uint8_t* begin, uint8_t* end);
size_t GetSlotIndexForAddress(uintptr_t address) const {
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index bc0f73a..c737119 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -84,6 +84,7 @@
table_ = nullptr;
}
segment_state_ = kIRTFirstSegment;
+ last_known_previous_state_ = kIRTFirstSegment;
}
IndirectReferenceTable::~IndirectReferenceTable() {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 72722dd..a0d712e 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -33,6 +33,7 @@
#include "stack.h"
#include "unstarted_runtime.h"
#include "verifier/method_verifier.h"
+#include "well_known_classes.h"
namespace art {
namespace interpreter {
@@ -491,7 +492,12 @@
Runtime::Current()->AbortTransactionAndThrowAbortError(self, abort_msg);
}
-// Separate declaration is required solely for the attributes.
+// START DECLARATIONS :
+//
+// These additional declarations are required because clang complains
+// about ALWAYS_INLINE (-Werror, -Wgcc-compat) in definitions.
+//
+
template <bool is_range, bool do_assignability_check>
REQUIRES_SHARED(Locks::mutator_lock_)
static inline bool DoCallCommon(ArtMethod* called_method,
@@ -502,7 +508,6 @@
uint32_t (&arg)[Instruction::kMaxVarArgRegs],
uint32_t vregC) ALWAYS_INLINE;
-// Separate declaration is required solely for the attributes.
template <bool is_range> REQUIRES_SHARED(Locks::mutator_lock_)
static inline bool DoCallPolymorphic(ArtMethod* called_method,
Handle<mirror::MethodType> callsite_type,
@@ -513,6 +518,33 @@
uint32_t (&arg)[Instruction::kMaxVarArgRegs],
uint32_t vregC) ALWAYS_INLINE;
+REQUIRES_SHARED(Locks::mutator_lock_)
+static inline bool DoCallTransform(ArtMethod* called_method,
+ Handle<mirror::MethodType> callsite_type,
+ Thread* self,
+ ShadowFrame& shadow_frame,
+ Handle<mirror::MethodHandleImpl> receiver,
+ JValue* result) ALWAYS_INLINE;
+
+REQUIRES_SHARED(Locks::mutator_lock_)
+inline void PerformCall(Thread* self,
+ const DexFile::CodeItem* code_item,
+ ArtMethod* caller_method,
+ const size_t first_dest_reg,
+ ShadowFrame* callee_frame,
+ JValue* result) ALWAYS_INLINE;
+
+template <bool is_range>
+REQUIRES_SHARED(Locks::mutator_lock_)
+inline void CopyRegisters(ShadowFrame& caller_frame,
+ ShadowFrame* callee_frame,
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
+ const size_t first_src_reg,
+ const size_t first_dest_reg,
+ const size_t num_regs) ALWAYS_INLINE;
+
+// END DECLARATIONS.
+
void ArtInterpreterToCompiledCodeBridge(Thread* self,
ArtMethod* caller,
const DexFile::CodeItem* code_item,
@@ -635,12 +667,6 @@
CHECK(called_method != nullptr);
CHECK(handle_type.Get() != nullptr);
- // We now have to massage the number of inputs to the target function.
- // It's always one less than the number of inputs to the signature polymorphic
- // invoke, the first input being a reference to the MethodHandle itself.
- const uint16_t number_of_inputs =
- ((is_range) ? inst->VRegA_4rcc(inst_data) : inst->VRegA_45cc(inst_data)) - 1;
-
uint32_t arg[Instruction::kMaxVarArgRegs] = {};
uint32_t receiver_vregC = 0;
if (is_range) {
@@ -702,18 +728,22 @@
CHECK(called_method != nullptr);
}
- // NOTE: handle_kind == kInvokeStatic needs no special treatment here. We
- // can directly make the call. handle_kind == kInvokeSuper doesn't have any
- // particular use and can probably be dropped.
-
- if (callsite_type->IsExactMatch(handle_type.Get())) {
- return DoCallCommon<is_range, do_access_check>(
- called_method, self, shadow_frame, result, number_of_inputs,
- arg, receiver_vregC);
+ if (handle_kind == kInvokeTransform) {
+ return DoCallTransform(called_method,
+ callsite_type,
+ self,
+ shadow_frame,
+ method_handle /* receiver */,
+ result);
} else {
- return DoCallPolymorphic<is_range>(
- called_method, callsite_type, handle_type, self, shadow_frame,
- result, arg, receiver_vregC);
+ return DoCallPolymorphic<is_range>(called_method,
+ callsite_type,
+ handle_type,
+ self,
+ shadow_frame,
+ result,
+ arg,
+ receiver_vregC);
}
} else {
// TODO(narayan): Implement field getters and setters.
@@ -749,6 +779,64 @@
return num_ins;
}
+
+inline void PerformCall(Thread* self,
+ const DexFile::CodeItem* code_item,
+ ArtMethod* caller_method,
+ const size_t first_dest_reg,
+ ShadowFrame* callee_frame,
+ JValue* result) {
+ if (LIKELY(Runtime::Current()->IsStarted())) {
+ ArtMethod* target = callee_frame->GetMethod();
+ if (ClassLinker::ShouldUseInterpreterEntrypoint(
+ target,
+ target->GetEntryPointFromQuickCompiledCode())) {
+ ArtInterpreterToInterpreterBridge(self, code_item, callee_frame, result);
+ } else {
+ ArtInterpreterToCompiledCodeBridge(
+ self, caller_method, code_item, callee_frame, result);
+ }
+ } else {
+ UnstartedRuntime::Invoke(self, code_item, callee_frame, result, first_dest_reg);
+ }
+}
+
+template <bool is_range>
+inline void CopyRegisters(ShadowFrame& caller_frame,
+ ShadowFrame* callee_frame,
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
+ const size_t first_src_reg,
+ const size_t first_dest_reg,
+ const size_t num_regs) {
+ if (is_range) {
+ const size_t dest_reg_bound = first_dest_reg + num_regs;
+ for (size_t src_reg = first_src_reg, dest_reg = first_dest_reg; dest_reg < dest_reg_bound;
+ ++dest_reg, ++src_reg) {
+ AssignRegister(callee_frame, caller_frame, dest_reg, src_reg);
+ }
+ } else {
+ DCHECK_LE(num_regs, arraysize(arg));
+
+ for (size_t arg_index = 0; arg_index < num_regs; ++arg_index) {
+ AssignRegister(callee_frame, caller_frame, first_dest_reg + arg_index, arg[arg_index]);
+ }
+ }
+}
+
+// Returns true iff. the callsite type for a polymorphic invoke is transformer
+// like, i.e that it has a single input argument whose type is
+// dalvik.system.EmulatedStackFrame.
+static inline bool IsCallerTransformer(Handle<mirror::MethodType> callsite_type)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::ObjectArray<mirror::Class>> param_types(callsite_type->GetPTypes());
+ if (param_types->GetLength() == 1) {
+ ObjPtr<mirror::Class> param(param_types->GetWithoutChecks(0));
+ return param == WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_EmulatedStackFrame);
+ }
+
+ return false;
+}
+
template <bool is_range>
static inline bool DoCallPolymorphic(ArtMethod* called_method,
Handle<mirror::MethodType> callsite_type,
@@ -757,7 +845,7 @@
ShadowFrame& shadow_frame,
JValue* result,
uint32_t (&arg)[Instruction::kMaxVarArgRegs],
- uint32_t vregC) {
+ uint32_t first_src_reg) {
// TODO(narayan): Wire in the String.init hacks.
// Compute method information.
@@ -770,16 +858,18 @@
// some transformations (such as boxing a long -> Long or wideining an
// int -> long will change that number.
uint16_t num_regs;
+ size_t num_input_regs;
size_t first_dest_reg;
if (LIKELY(code_item != nullptr)) {
num_regs = code_item->registers_size_;
first_dest_reg = num_regs - code_item->ins_size_;
+ num_input_regs = code_item->ins_size_;
// Parameter registers go at the end of the shadow frame.
DCHECK_NE(first_dest_reg, (size_t)-1);
} else {
// No local regs for proxy and native methods.
DCHECK(called_method->IsNative() || called_method->IsProxyMethod());
- num_regs = GetInsForProxyOrNativeMethod(called_method);
+ num_regs = num_input_regs = GetInsForProxyOrNativeMethod(called_method);
first_dest_reg = 0;
}
@@ -793,35 +883,109 @@
{
ScopedStackedShadowFramePusher pusher(
self, new_shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction);
- if (!PerformArgumentConversions<is_range>(self, callsite_type, target_type,
- shadow_frame, vregC, first_dest_reg,
- arg, new_shadow_frame)) {
- DCHECK(self->IsExceptionPending());
- result->SetL(0);
- return false;
+ if (callsite_type->IsExactMatch(target_type.Get())) {
+ // This is an exact invoke, we can take the fast path of just copying all
+ // registers without performing any argument conversions.
+ CopyRegisters<is_range>(shadow_frame,
+ new_shadow_frame,
+ arg,
+ first_src_reg,
+ first_dest_reg,
+ num_input_regs);
+ } else {
+ // This includes the case where we're entering this invoke-polymorphic
+ // from a transformer method. In that case, the callsite_type will contain
+ // a single argument of type dalvik.system.EmulatedStackFrame. In that
+ // case, we'll have to unmarshal the EmulatedStackFrame into the
+ // new_shadow_frame and perform argument conversions on it.
+ if (IsCallerTransformer(callsite_type)) {
+ // The emulated stack frame will be the first ahnd only argument
+ // when we're coming through from a transformer.
+ //
+ // TODO(narayan): This should be a mirror::EmulatedStackFrame after that
+ // type is introduced.
+ ObjPtr<mirror::Object> emulated_stack_frame(
+ shadow_frame.GetVRegReference(first_src_reg));
+ if (!ConvertAndCopyArgumentsFromEmulatedStackFrame<is_range>(self,
+ emulated_stack_frame,
+ target_type,
+ first_dest_reg,
+ new_shadow_frame)) {
+ DCHECK(self->IsExceptionPending());
+ result->SetL(0);
+ return false;
+ }
+ } else if (!ConvertAndCopyArgumentsFromCallerFrame<is_range>(self,
+ callsite_type,
+ target_type,
+ shadow_frame,
+ first_src_reg,
+ first_dest_reg,
+ arg,
+ new_shadow_frame)) {
+ DCHECK(self->IsExceptionPending());
+ result->SetL(0);
+ return false;
+ }
}
}
- // Do the call now.
- if (LIKELY(Runtime::Current()->IsStarted())) {
- ArtMethod* target = new_shadow_frame->GetMethod();
- if (ClassLinker::ShouldUseInterpreterEntrypoint(
- target,
- target->GetEntryPointFromQuickCompiledCode())) {
- ArtInterpreterToInterpreterBridge(self, code_item, new_shadow_frame, result);
- } else {
- ArtInterpreterToCompiledCodeBridge(
- self, shadow_frame.GetMethod(), code_item, new_shadow_frame, result);
- }
- } else {
- UnstartedRuntime::Invoke(self, code_item, new_shadow_frame, result, first_dest_reg);
- }
+ PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result);
// TODO(narayan): Perform return value conversions.
return !self->IsExceptionPending();
}
+static inline bool DoCallTransform(ArtMethod* called_method,
+ Handle<mirror::MethodType> callsite_type,
+ Thread* self,
+ ShadowFrame& shadow_frame,
+ Handle<mirror::MethodHandleImpl> receiver,
+ JValue* result) {
+ // This can be fixed, because the method we're calling here
+ // (MethodHandle.transformInternal) doesn't have any locals and the signature
+ // is known :
+ //
+ // private MethodHandle.transformInternal(EmulatedStackFrame sf);
+ //
+ // This means we need only two vregs :
+ // - One for the receiver object.
+ // - One for the only method argument (an EmulatedStackFrame).
+ static constexpr size_t kNumRegsForTransform = 2;
+
+ const DexFile::CodeItem* code_item = called_method->GetCodeItem();
+ DCHECK(code_item != nullptr);
+ DCHECK_EQ(kNumRegsForTransform, code_item->registers_size_);
+ DCHECK_EQ(kNumRegsForTransform, code_item->ins_size_);
+
+ ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
+ CREATE_SHADOW_FRAME(kNumRegsForTransform, &shadow_frame, called_method, /* dex pc */ 0);
+ ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get();
+
+ // TODO(narayan): Perform argument conversions first (if this is an inexact invoke), and
+ // then construct an argument list object that's passed through to the
+ // method. Note that the ArgumentList reference is currently a nullptr.
+ //
+ // NOTE(narayan): If the caller is a transformer method (i.e, there is only
+ // one argument and its type is EmulatedStackFrame), we can directly pass that
+ // through without having to do any additional work.
+ UNUSED(callsite_type);
+
+ new_shadow_frame->SetVRegReference(0, receiver.Get());
+ // TODO(narayan): This is the EmulatedStackFrame, currently nullptr.
+ new_shadow_frame->SetVRegReference(1, nullptr);
+
+ PerformCall(self,
+ code_item,
+ shadow_frame.GetMethod(),
+ 0 /* first dest reg */,
+ new_shadow_frame,
+ result);
+
+ return !self->IsExceptionPending();
+}
+
template <bool is_range,
bool do_assignability_check>
static inline bool DoCallCommon(ArtMethod* called_method,
@@ -982,40 +1146,20 @@
}
}
} else {
- size_t arg_index = 0;
-
- // Fast path: no extra checks.
if (is_range) {
- uint16_t first_src_reg = vregC;
-
- for (size_t src_reg = first_src_reg, dest_reg = first_dest_reg; dest_reg < num_regs;
- ++dest_reg, ++src_reg) {
- AssignRegister(new_shadow_frame, shadow_frame, dest_reg, src_reg);
- }
- } else {
- DCHECK_LE(number_of_inputs, arraysize(arg));
-
- for (; arg_index < number_of_inputs; ++arg_index) {
- AssignRegister(new_shadow_frame, shadow_frame, first_dest_reg + arg_index, arg[arg_index]);
- }
+ DCHECK_EQ(num_regs, first_dest_reg + number_of_inputs);
}
+
+ CopyRegisters<is_range>(shadow_frame,
+ new_shadow_frame,
+ arg,
+ vregC,
+ first_dest_reg,
+ number_of_inputs);
self->EndAssertNoThreadSuspension(old_cause);
}
- // Do the call now.
- if (LIKELY(Runtime::Current()->IsStarted())) {
- ArtMethod* target = new_shadow_frame->GetMethod();
- if (ClassLinker::ShouldUseInterpreterEntrypoint(
- target,
- target->GetEntryPointFromQuickCompiledCode())) {
- ArtInterpreterToInterpreterBridge(self, code_item, new_shadow_frame, result);
- } else {
- ArtInterpreterToCompiledCodeBridge(
- self, shadow_frame.GetMethod(), code_item, new_shadow_frame, result);
- }
- } else {
- UnstartedRuntime::Invoke(self, code_item, new_shadow_frame, result, first_dest_reg);
- }
+ PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result);
if (string_init && !self->IsExceptionPending()) {
SetStringInitValueToAllAliases(&shadow_frame, string_init_vreg_this, *result);
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index bb07fcb..1ec59b3 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -318,11 +318,18 @@
debug_friendly_name += name;
fd.Reset(ashmem_create_region(debug_friendly_name.c_str(), page_aligned_byte_count),
/* check_usage */ false);
+
if (fd.Fd() == -1) {
- *error_msg = StringPrintf("ashmem_create_region failed for '%s': %s", name, strerror(errno));
- return nullptr;
+ // We failed to create the ashmem region. Print a warning, but continue
+ // anyway by creating a true anonymous mmap with an fd of -1. It is
+ // better to use an unlabelled anonymous map than to fail to create a
+ // map at all.
+ PLOG(WARNING) << "ashmem_create_region failed for '" << name << "'";
+ } else {
+ // We succeeded in creating the ashmem region. Use the created ashmem
+ // region as backing for the mmap.
+ flags &= ~MAP_ANONYMOUS;
}
- flags &= ~MAP_ANONYMOUS;
}
// We need to store and potentially set an error number for pretty printing of errors
@@ -354,7 +361,6 @@
}
return nullptr;
}
- std::ostringstream check_map_request_error_msg;
if (!CheckMapRequest(expected_ptr, actual, page_aligned_byte_count, error_msg)) {
return nullptr;
}
@@ -441,7 +447,6 @@
}
return nullptr;
}
- std::ostringstream check_map_request_error_msg;
if (!CheckMapRequest(expected_ptr, actual, page_aligned_byte_count, error_msg)) {
return nullptr;
}
@@ -918,4 +923,23 @@
}
}
+void ZeroAndReleasePages(void* address, size_t length) {
+ uint8_t* const mem_begin = reinterpret_cast<uint8_t*>(address);
+ uint8_t* const mem_end = mem_begin + length;
+ uint8_t* const page_begin = AlignUp(mem_begin, kPageSize);
+ uint8_t* const page_end = AlignDown(mem_end, kPageSize);
+ if (!kMadviseZeroes || page_begin >= page_end) {
+ // No possible area to madvise.
+ std::fill(mem_begin, mem_end, 0);
+ } else {
+ // Spans one or more pages.
+ DCHECK_LE(mem_begin, page_begin);
+ DCHECK_LE(page_begin, page_end);
+ DCHECK_LE(page_end, mem_end);
+ std::fill(mem_begin, page_begin, 0);
+ CHECK_NE(madvise(page_begin, page_end - page_begin, MADV_DONTNEED), -1) << "madvise failed";
+ std::fill(page_end, mem_end, 0);
+ }
+}
+
} // namespace art
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index 597f0d4..049ae12 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -241,9 +241,13 @@
friend class MemMapTest; // To allow access to base_begin_ and base_size_.
};
+
std::ostream& operator<<(std::ostream& os, const MemMap& mem_map);
std::ostream& operator<<(std::ostream& os, const MemMap::Maps& mem_maps);
+// Zero and release pages if possible, no requirements on alignments.
+void ZeroAndReleasePages(void* address, size_t length);
+
} // namespace art
#endif // ART_RUNTIME_MEM_MAP_H_
diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h
index 7a77bda..b488133 100644
--- a/runtime/method_handles-inl.h
+++ b/runtime/method_handles-inl.h
@@ -162,14 +162,14 @@
}
template <bool is_range>
-bool PerformArgumentConversions(Thread* self,
- Handle<mirror::MethodType> callsite_type,
- Handle<mirror::MethodType> callee_type,
- const ShadowFrame& caller_frame,
- uint32_t first_src_reg,
- uint32_t first_dest_reg,
- const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
- ShadowFrame* callee_frame) {
+bool ConvertAndCopyArgumentsFromCallerFrame(Thread* self,
+ Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
+ const ShadowFrame& caller_frame,
+ uint32_t first_src_reg,
+ uint32_t first_dest_reg,
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
+ ShadowFrame* callee_frame) {
StackHandleScope<4> hs(self);
Handle<mirror::ObjectArray<mirror::Class>> from_types(hs.NewHandle(callsite_type->GetPTypes()));
Handle<mirror::ObjectArray<mirror::Class>> to_types(hs.NewHandle(callee_type->GetPTypes()));
@@ -244,6 +244,25 @@
return true;
}
+// Similar to |ConvertAndCopyArgumentsFromCallerFrame|, except that the
+// arguments are copied from an |EmulatedStackFrame|.
+template <bool is_range>
+bool ConvertAndCopyArgumentsFromEmulatedStackFrame(Thread* self,
+ ObjPtr<mirror::Object> emulated_stack_frame,
+ Handle<mirror::MethodType> callee_type,
+ const uint32_t first_dest_reg,
+ ShadowFrame* callee_frame) {
+ UNUSED(self);
+ UNUSED(emulated_stack_frame);
+ UNUSED(callee_type);
+ UNUSED(first_dest_reg);
+ UNUSED(callee_frame);
+
+ UNIMPLEMENTED(FATAL) << "ConvertAndCopyArgumentsFromEmulatedStackFrame is unimplemented";
+ return false;
+}
+
+
} // namespace art
#endif // ART_RUNTIME_METHOD_HANDLES_INL_H_
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 26a29b3..5175dce 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -42,12 +42,13 @@
kInvokeDirect,
kInvokeStatic,
kInvokeInterface,
+ kInvokeTransform,
kInstanceGet,
kInstancePut,
kStaticGet,
kStaticPut,
kLastValidKind = kStaticPut,
- kLastInvokeKind = kInvokeInterface
+ kLastInvokeKind = kInvokeTransform
};
// Whether the given method handle kind is some variant of an invoke.
@@ -69,14 +70,24 @@
// boxing and unboxing. Returns true on success, on false on failure. A
// pending exception will always be set on failure.
template <bool is_range> REQUIRES_SHARED(Locks::mutator_lock_)
-bool PerformArgumentConversions(Thread* self,
- Handle<mirror::MethodType> callsite_type,
- Handle<mirror::MethodType> callee_type,
- const ShadowFrame& caller_frame,
- uint32_t first_src_reg,
- uint32_t first_dest_reg,
- const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
- ShadowFrame* callee_frame);
+bool ConvertAndCopyArgumentsFromCallerFrame(Thread* self,
+ Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
+ const ShadowFrame& caller_frame,
+ uint32_t first_src_reg,
+ uint32_t first_dest_reg,
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
+ ShadowFrame* callee_frame);
+
+// Similar to |ConvertAndCopyArgumentsFromCallerFrame|, except that the
+// arguments are copied from an |EmulatedStackFrame|.
+template <bool is_range> REQUIRES_SHARED(Locks::mutator_lock_)
+bool ConvertAndCopyArgumentsFromEmulatedStackFrame(Thread* self,
+ ObjPtr<mirror::Object> emulated_stack_frame,
+ Handle<mirror::MethodType> callee_type,
+ const uint32_t first_dest_reg,
+ ShadowFrame* callee_frame);
+
} // namespace art
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 1852956..8d85425 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -482,6 +482,31 @@
return result;
}
+static void VMDebug_attachAgent(JNIEnv* env, jclass, jstring agent) {
+ if (agent == nullptr) {
+ ScopedObjectAccess soa(env);
+ ThrowNullPointerException("agent is null");
+ return;
+ }
+
+ if (!Dbg::IsJdwpAllowed()) {
+ ScopedObjectAccess soa(env);
+ ThrowSecurityException("Can't attach agent, process is not debuggable.");
+ return;
+ }
+
+ std::string filename;
+ {
+ ScopedUtfChars chars(env, agent);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ filename = chars.c_str();
+ }
+
+ Runtime::Current()->AttachAgent(filename);
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"),
@@ -514,7 +539,8 @@
NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"),
NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "!()J"),
NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"),
- NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;")
+ NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"),
+ NATIVE_METHOD(VMDebug, attachAgent, "(Ljava/lang/String;)V"),
};
void register_dalvik_system_VMDebug(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 5421410..866dc7f 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -317,7 +317,7 @@
if (class_name[1] == '\0') {
klass = linker->FindPrimitiveClass(class_name[0]);
} else {
- klass = linker->LookupClass(self, class_name, ComputeModifiedUtf8Hash(class_name), nullptr);
+ klass = linker->LookupClass(self, class_name, nullptr);
}
if (klass == nullptr) {
return;
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index ff08284..e5bab36 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -20,12 +20,41 @@
#include "jni_internal.h"
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
+#include "obj_ptr.h"
#include "scoped_fast_native_object_access-inl.h"
#include "ScopedUtfChars.h"
#include "zip_archive.h"
namespace art {
+// A class so we can be friends with ClassLinker and access internal methods.
+class VMClassLoader {
+ public:
+ static mirror::Class* LookupClass(ClassLinker* cl,
+ Thread* self,
+ const char* descriptor,
+ size_t hash,
+ ObjPtr<mirror::ClassLoader> class_loader)
+ REQUIRES(!Locks::classlinker_classes_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return cl->LookupClass(self, descriptor, hash, class_loader);
+ }
+
+ static ObjPtr<mirror::Class> FindClassInPathClassLoader(ClassLinker* cl,
+ ScopedObjectAccessAlreadyRunnable& soa,
+ Thread* self,
+ const char* descriptor,
+ size_t hash,
+ Handle<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> result;
+ if (cl->FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader, &result)) {
+ return result;
+ }
+ return nullptr;
+ }
+};
+
static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoader,
jstring javaName) {
ScopedFastNativeObjectAccess soa(env);
@@ -35,12 +64,16 @@
return nullptr;
}
ClassLinker* cl = Runtime::Current()->GetClassLinker();
+
+ // Compute hash once.
std::string descriptor(DotToDescriptor(name.c_str()));
const size_t descriptor_hash = ComputeModifiedUtf8Hash(descriptor.c_str());
- ObjPtr<mirror::Class> c = cl->LookupClass(soa.Self(),
- descriptor.c_str(),
- descriptor_hash,
- loader.Ptr());
+
+ ObjPtr<mirror::Class> c = VMClassLoader::LookupClass(cl,
+ soa.Self(),
+ descriptor.c_str(),
+ descriptor_hash,
+ loader);
if (c != nullptr && c->IsResolved()) {
return soa.AddLocalReference<jclass>(c);
}
@@ -61,17 +94,26 @@
}
return nullptr;
}
+
+ // Hard-coded performance optimization: We know that all failed libcore calls to findLoadedClass
+ // are followed by a call to the the classloader to actually
+ // load the class.
if (loader != nullptr) {
// Try the common case.
StackHandleScope<1> hs(soa.Self());
- cl->FindClassInPathClassLoader(soa, soa.Self(), descriptor.c_str(), descriptor_hash,
- hs.NewHandle(loader), &c);
+ c = VMClassLoader::FindClassInPathClassLoader(cl,
+ soa,
+ soa.Self(),
+ descriptor.c_str(),
+ descriptor_hash,
+ hs.NewHandle(loader));
if (c != nullptr) {
return soa.AddLocalReference<jclass>(c);
}
}
- // Class wasn't resolved so it may be erroneous or not yet ready, force the caller to go into
- // the regular loadClass code.
+
+ // The class wasn't loaded, yet, and our fast-path did not apply (e.g., we didn't understand the
+ // classloader chain).
return nullptr;
}
diff --git a/runtime/openjdkjvmti/heap.cc b/runtime/openjdkjvmti/heap.cc
index decbfc9..1799e19 100644
--- a/runtime/openjdkjvmti/heap.cc
+++ b/runtime/openjdkjvmti/heap.cc
@@ -21,7 +21,6 @@
#include "base/mutex.h"
#include "class_linker.h"
#include "gc/heap.h"
-#include "java_vm_ext.h"
#include "jni_env_ext.h"
#include "mirror/class.h"
#include "object_callbacks.h"
@@ -179,8 +178,7 @@
bool operator()(art::ObjPtr<art::mirror::Class> klass)
OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
- art::JNIEnvExt* jni_env = self_->GetJniEnv();
- classes_.push_back(reinterpret_cast<jclass>(jni_env->vm->AddGlobalRef(self_, klass)));
+ classes_.push_back(self_->GetJniEnv()->AddLocalReference<jclass>(klass));
return true;
}
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index f937ca7..56eab5e 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -601,7 +601,7 @@
<< "runtime plugins.";
} else if (!args.GetOrDefault(M::Plugins).empty()) {
LOG(WARNING) << "Experimental runtime plugin support has not been enabled. Ignored options: ";
- for (auto& op : args.GetOrDefault(M::Plugins)) {
+ for (const auto& op : args.GetOrDefault(M::Plugins)) {
LOG(WARNING) << " -plugin:" << op.GetLibrary();
}
}
@@ -614,14 +614,14 @@
} else if (!args.GetOrDefault(M::AgentLib).empty() || !args.GetOrDefault(M::AgentPath).empty()) {
LOG(WARNING) << "agent support has not been enabled. Enable experimental agent "
<< " support with '-XExperimental:agent'. Ignored options are:";
- for (auto op : args.GetOrDefault(M::AgentLib)) {
+ for (const auto& op : args.GetOrDefault(M::AgentLib)) {
if (op.HasArgs()) {
LOG(WARNING) << " -agentlib:" << op.GetName() << "=" << op.GetArgs();
} else {
LOG(WARNING) << " -agentlib:" << op.GetName();
}
}
- for (auto op : args.GetOrDefault(M::AgentPath)) {
+ for (const auto& op : args.GetOrDefault(M::AgentPath)) {
if (op.HasArgs()) {
LOG(WARNING) << " -agentpath:" << op.GetName() << "=" << op.GetArgs();
} else {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index e8f41d4..4e600ae 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1311,6 +1311,28 @@
return true;
}
+// Attach a new agent and add it to the list of runtime agents
+//
+// TODO: once we decide on the threading model for agents,
+// revisit this and make sure we're doing this on the right thread
+// (and we synchronize access to any shared data structures like "agents_")
+//
+void Runtime::AttachAgent(const std::string& agent_arg) {
+ ti::Agent agent(agent_arg);
+
+ int res = 0;
+ std::string err;
+ ti::Agent::LoadError result = agent.Attach(&res, &err);
+
+ if (result == ti::Agent::kNoError) {
+ agents_.push_back(std::move(agent));
+ } else {
+ LOG(ERROR) << "Agent attach failed (result=" << result << ") : " << err;
+ ScopedObjectAccess soa(Thread::Current());
+ ThrowWrappedIOException("%s", err.c_str());
+ }
+}
+
void Runtime::InitNativeMethods() {
VLOG(startup) << "Runtime::InitNativeMethods entering";
Thread* self = Thread::Current();
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 7cb87ab..b25ec23 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -665,6 +665,8 @@
NO_RETURN
static void Aborter(const char* abort_message);
+ void AttachAgent(const std::string& agent_arg);
+
private:
static void InitPlatformSignalHandlers();
diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc
index 7c0ea64..d21ff77 100644
--- a/runtime/ti/agent.cc
+++ b/runtime/ti/agent.cc
@@ -25,17 +25,10 @@
const char* AGENT_ON_ATTACH_FUNCTION_NAME = "Agent_OnAttach";
const char* AGENT_ON_UNLOAD_FUNCTION_NAME = "Agent_OnUnload";
-Agent Agent::Create(std::string arg) {
- size_t eq = arg.find_first_of('=');
- if (eq == std::string::npos) {
- return Agent(arg, "");
- } else {
- return Agent(arg.substr(0, eq), arg.substr(eq + 1, arg.length()));
- }
-}
-
// TODO We need to acquire some locks probably.
-Agent::LoadError Agent::Load(/*out*/jint* call_res, /*out*/ std::string* error_msg) {
+Agent::LoadError Agent::DoLoadHelper(bool attaching,
+ /*out*/jint* call_res,
+ /*out*/std::string* error_msg) {
DCHECK(call_res != nullptr);
DCHECK(error_msg != nullptr);
@@ -49,8 +42,10 @@
VLOG(agents) << "err: " << *error_msg;
return err;
}
- if (onload_ == nullptr) {
- *error_msg = StringPrintf("Unable to start agent %s: No Agent_OnLoad function found",
+ AgentOnLoadFunction callback = attaching ? onattach_ : onload_;
+ if (callback == nullptr) {
+ *error_msg = StringPrintf("Unable to start agent %s: No %s callback found",
+ (attaching ? "attach" : "load"),
name_.c_str());
VLOG(agents) << "err: " << *error_msg;
return kLoadingError;
@@ -59,9 +54,9 @@
std::unique_ptr<char[]> copied_args(new char[args_.size() + 1]);
strcpy(copied_args.get(), args_.c_str());
// TODO Need to do some checks that we are at a good spot etc.
- *call_res = onload_(static_cast<JavaVM*>(Runtime::Current()->GetJavaVM()),
- copied_args.get(),
- nullptr);
+ *call_res = callback(Runtime::Current()->GetJavaVM(),
+ copied_args.get(),
+ nullptr);
if (*call_res != 0) {
*error_msg = StringPrintf("Initialization of %s returned non-zero value of %d",
name_.c_str(), *call_res);
@@ -74,6 +69,12 @@
Agent::LoadError Agent::DoDlOpen(/*out*/std::string* error_msg) {
DCHECK(error_msg != nullptr);
+
+ DCHECK(dlopen_handle_ == nullptr);
+ DCHECK(onload_ == nullptr);
+ DCHECK(onattach_ == nullptr);
+ DCHECK(onunload_ == nullptr);
+
dlopen_handle_ = dlopen(name_.c_str(), RTLD_LAZY);
if (dlopen_handle_ == nullptr) {
*error_msg = StringPrintf("Unable to dlopen %s: %s", name_.c_str(), dlerror());
@@ -85,7 +86,7 @@
if (onload_ == nullptr) {
VLOG(agents) << "Unable to find 'Agent_OnLoad' symbol in " << this;
}
- onattach_ = reinterpret_cast<AgentOnAttachFunction>(dlsym(dlopen_handle_,
+ onattach_ = reinterpret_cast<AgentOnLoadFunction>(dlsym(dlopen_handle_,
AGENT_ON_ATTACH_FUNCTION_NAME));
if (onattach_ == nullptr) {
VLOG(agents) << "Unable to find 'Agent_OnAttach' symbol in " << this;
@@ -106,23 +107,93 @@
}
dlclose(dlopen_handle_);
dlopen_handle_ = nullptr;
+ onload_ = nullptr;
+ onattach_ = nullptr;
+ onunload_ = nullptr;
} else {
VLOG(agents) << this << " is not currently loaded!";
}
}
-Agent::Agent(const Agent& other)
- : name_(other.name_),
- args_(other.args_),
- dlopen_handle_(other.dlopen_handle_),
- onload_(other.onload_),
- onattach_(other.onattach_),
- onunload_(other.onunload_) {
- if (other.dlopen_handle_ != nullptr) {
- dlopen(other.name_.c_str(), 0);
+Agent::Agent(std::string arg)
+ : dlopen_handle_(nullptr),
+ onload_(nullptr),
+ onattach_(nullptr),
+ onunload_(nullptr) {
+ size_t eq = arg.find_first_of('=');
+ if (eq == std::string::npos) {
+ name_ = arg;
+ } else {
+ name_ = arg.substr(0, eq);
+ args_ = arg.substr(eq + 1, arg.length());
}
}
+Agent::Agent(const Agent& other)
+ : dlopen_handle_(nullptr),
+ onload_(nullptr),
+ onattach_(nullptr),
+ onunload_(nullptr) {
+ *this = other;
+}
+
+// Attempting to copy to/from loaded/started agents is a fatal error
+Agent& Agent::operator=(const Agent& other) {
+ if (this != &other) {
+ if (other.dlopen_handle_ != nullptr) {
+ LOG(FATAL) << "Attempting to copy a loaded agent!";
+ }
+
+ if (dlopen_handle_ != nullptr) {
+ LOG(FATAL) << "Attempting to assign into a loaded agent!";
+ }
+
+ DCHECK(other.onload_ == nullptr);
+ DCHECK(other.onattach_ == nullptr);
+ DCHECK(other.onunload_ == nullptr);
+
+ DCHECK(onload_ == nullptr);
+ DCHECK(onattach_ == nullptr);
+ DCHECK(onunload_ == nullptr);
+
+ name_ = other.name_;
+ args_ = other.args_;
+
+ dlopen_handle_ = nullptr;
+ onload_ = nullptr;
+ onattach_ = nullptr;
+ onunload_ = nullptr;
+ }
+ return *this;
+}
+
+Agent::Agent(Agent&& other)
+ : dlopen_handle_(nullptr),
+ onload_(nullptr),
+ onattach_(nullptr),
+ onunload_(nullptr) {
+ *this = std::move(other);
+}
+
+Agent& Agent::operator=(Agent&& other) {
+ if (this != &other) {
+ if (dlopen_handle_ != nullptr) {
+ dlclose(dlopen_handle_);
+ }
+ name_ = std::move(other.name_);
+ args_ = std::move(other.args_);
+ dlopen_handle_ = other.dlopen_handle_;
+ onload_ = other.onload_;
+ onattach_ = other.onattach_;
+ onunload_ = other.onunload_;
+ other.dlopen_handle_ = nullptr;
+ other.onload_ = nullptr;
+ other.onattach_ = nullptr;
+ other.onunload_ = nullptr;
+ }
+ return *this;
+}
+
Agent::~Agent() {
if (dlopen_handle_ != nullptr) {
dlclose(dlopen_handle_);
diff --git a/runtime/ti/agent.h b/runtime/ti/agent.h
index 521e21e..6561756 100644
--- a/runtime/ti/agent.h
+++ b/runtime/ti/agent.h
@@ -28,9 +28,10 @@
namespace ti {
using AgentOnLoadFunction = jint (*)(JavaVM*, const char*, void*);
-using AgentOnAttachFunction = jint (*)(JavaVM*, const char*, void*);
using AgentOnUnloadFunction = void (*)(JavaVM*);
+// TODO: consider splitting ti::Agent into command line, agent and shared library handler classes
+
class Agent {
public:
enum LoadError {
@@ -56,65 +57,44 @@
return !GetArgs().empty();
}
- // TODO We need to acquire some locks probably.
- LoadError Load(/*out*/jint* call_res, /*out*/std::string* error_msg);
+ LoadError Load(/*out*/jint* call_res, /*out*/std::string* error_msg) {
+ VLOG(agents) << "Loading agent: " << name_ << " " << args_;
+ return DoLoadHelper(false, call_res, error_msg);
+ }
// TODO We need to acquire some locks probably.
void Unload();
// Tries to attach the agent using its OnAttach method. Returns true on success.
- // TODO We need to acquire some locks probably.
- LoadError Attach(std::string* error_msg) {
- // TODO
- *error_msg = "Attach has not yet been implemented!";
- return kLoadingError;
+ LoadError Attach(/*out*/jint* call_res, /*out*/std::string* error_msg) {
+ VLOG(agents) << "Attaching agent: " << name_ << " " << args_;
+ return DoLoadHelper(true, call_res, error_msg);
}
- static Agent Create(std::string arg);
+ explicit Agent(std::string arg);
- static Agent Create(std::string name, std::string args) {
- return Agent(name, args);
- }
+ Agent(const Agent& other);
+ Agent& operator=(const Agent& other);
+
+ Agent(Agent&& other);
+ Agent& operator=(Agent&& other);
~Agent();
- // We need move constructor and copy for vectors
- Agent(const Agent& other);
-
- Agent(Agent&& other)
- : name_(other.name_),
- args_(other.args_),
- dlopen_handle_(nullptr),
- onload_(nullptr),
- onattach_(nullptr),
- onunload_(nullptr) {
- other.dlopen_handle_ = nullptr;
- other.onload_ = nullptr;
- other.onattach_ = nullptr;
- other.onunload_ = nullptr;
- }
-
- // We don't need an operator=
- void operator=(const Agent&) = delete;
-
private:
- Agent(std::string name, std::string args)
- : name_(name),
- args_(args),
- dlopen_handle_(nullptr),
- onload_(nullptr),
- onattach_(nullptr),
- onunload_(nullptr) { }
-
LoadError DoDlOpen(/*out*/std::string* error_msg);
- const std::string name_;
- const std::string args_;
+ LoadError DoLoadHelper(bool attaching,
+ /*out*/jint* call_res,
+ /*out*/std::string* error_msg);
+
+ std::string name_;
+ std::string args_;
void* dlopen_handle_;
// The entrypoints.
AgentOnLoadFunction onload_;
- AgentOnAttachFunction onattach_;
+ AgentOnLoadFunction onattach_;
AgentOnUnloadFunction onunload_;
friend std::ostream& operator<<(std::ostream &os, Agent const& m);
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index d0493e5..93286ea 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -154,8 +154,7 @@
if (can_load_classes_) {
klass = class_linker->FindClass(self, descriptor, class_loader);
} else {
- klass = class_linker->LookupClass(self, descriptor, ComputeModifiedUtf8Hash(descriptor),
- loader);
+ klass = class_linker->LookupClass(self, descriptor, loader);
if (klass != nullptr && !klass->IsResolved()) {
// We found the class but without it being loaded its not safe for use.
klass = nullptr;
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 78fc53a..153c7ef 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -37,6 +37,7 @@
jclass WellKnownClasses::dalvik_system_DexFile;
jclass WellKnownClasses::dalvik_system_DexPathList;
jclass WellKnownClasses::dalvik_system_DexPathList__Element;
+jclass WellKnownClasses::dalvik_system_EmulatedStackFrame;
jclass WellKnownClasses::dalvik_system_PathClassLoader;
jclass WellKnownClasses::dalvik_system_VMRuntime;
jclass WellKnownClasses::java_lang_annotation_Annotation__array;
@@ -266,6 +267,7 @@
dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
dalvik_system_DexPathList__Element = CacheClass(env, "dalvik/system/DexPathList$Element");
+ dalvik_system_EmulatedStackFrame = CacheClass(env, "dalvik/system/EmulatedStackFrame");
dalvik_system_PathClassLoader = CacheClass(env, "dalvik/system/PathClassLoader");
dalvik_system_VMRuntime = CacheClass(env, "dalvik/system/VMRuntime");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 248ba7f..2fb5bb4 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -50,6 +50,7 @@
static jclass dalvik_system_DexFile;
static jclass dalvik_system_DexPathList;
static jclass dalvik_system_DexPathList__Element;
+ static jclass dalvik_system_EmulatedStackFrame;
static jclass dalvik_system_PathClassLoader;
static jclass dalvik_system_VMRuntime;
static jclass java_lang_annotation_Annotation__array;
diff --git a/test/151-OpenFileLimit/expected.txt b/test/151-OpenFileLimit/expected.txt
index 971e472..6bc45ef 100644
--- a/test/151-OpenFileLimit/expected.txt
+++ b/test/151-OpenFileLimit/expected.txt
@@ -1,3 +1,3 @@
Message includes "Too many open files"
-Message includes "Too many open files"
+thread run.
done.
diff --git a/test/151-OpenFileLimit/info.txt b/test/151-OpenFileLimit/info.txt
index 56ed396..9af393d 100644
--- a/test/151-OpenFileLimit/info.txt
+++ b/test/151-OpenFileLimit/info.txt
@@ -1,3 +1,2 @@
-This test verifies the exception message is informative for failure to launch
-a thread due to the number of available file descriptors in the process being
-exceeded.
+This test verifies that running out of file descriptors in the process doesn't
+prevent us from launching a new thread.
diff --git a/test/151-OpenFileLimit/src/Main.java b/test/151-OpenFileLimit/src/Main.java
index 01a9a4e..9fe47c8 100644
--- a/test/151-OpenFileLimit/src/Main.java
+++ b/test/151-OpenFileLimit/src/Main.java
@@ -52,11 +52,7 @@
thread.start();
thread.join();
} catch (Throwable e) {
- if (e.getMessage().contains("Too many open files")) {
- System.out.println("Message includes \"Too many open files\"");
- } else {
- System.out.println(e.getMessage());
- }
+ System.out.println(e.getMessage());
}
for (int i = 0; i < files.size(); i++) {
diff --git a/test/907-get-loaded-classes/get_loaded_classes.cc b/test/907-get-loaded-classes/get_loaded_classes.cc
index 9ceefcb..0e09d1b 100644
--- a/test/907-get-loaded-classes/get_loaded_classes.cc
+++ b/test/907-get-loaded-classes/get_loaded_classes.cc
@@ -70,7 +70,7 @@
// 1) Free the local references.
// 2) Deallocate.
for (size_t i = 0; i < static_cast<size_t>(count); ++i) {
- env->DeleteGlobalRef(classes[i]);
+ env->DeleteLocalRef(classes[i]);
}
jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(classes));
diff --git a/test/909-attach-agent/attach.cc b/test/909-attach-agent/attach.cc
new file mode 100644
index 0000000..2b50eb8
--- /dev/null
+++ b/test/909-attach-agent/attach.cc
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "909-attach-agent/attach.h"
+
+#include <jni.h>
+#include <stdio.h>
+#include <string.h>
+#include "base/macros.h"
+#include "openjdkjvmti/jvmti.h"
+
+namespace art {
+namespace Test909AttachAgent {
+
+jint OnAttach(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ printf("Attached Agent for test 909-attach-agent\n");
+ fsync(1);
+ jvmtiEnv* env = nullptr;
+ jvmtiEnv* env2 = nullptr;
+
+#define CHECK_CALL_SUCCESS(c) \
+ do { \
+ if ((c) != JNI_OK) { \
+ printf("call " #c " did not succeed\n"); \
+ return -1; \
+ } \
+ } while (false)
+
+ CHECK_CALL_SUCCESS(vm->GetEnv(reinterpret_cast<void**>(&env), JVMTI_VERSION_1_0));
+ CHECK_CALL_SUCCESS(vm->GetEnv(reinterpret_cast<void**>(&env2), JVMTI_VERSION_1_0));
+ if (env == env2) {
+ printf("GetEnv returned same environment twice!\n");
+ return -1;
+ }
+ unsigned char* local_data = nullptr;
+ CHECK_CALL_SUCCESS(env->Allocate(8, &local_data));
+ strcpy(reinterpret_cast<char*>(local_data), "hello!!");
+ CHECK_CALL_SUCCESS(env->SetEnvironmentLocalStorage(local_data));
+ unsigned char* get_data = nullptr;
+ CHECK_CALL_SUCCESS(env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&get_data)));
+ if (get_data != local_data) {
+ printf("Got different data from local storage then what was set!\n");
+ return -1;
+ }
+ CHECK_CALL_SUCCESS(env2->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&get_data)));
+ if (get_data != nullptr) {
+ printf("env2 did not have nullptr local storage.\n");
+ return -1;
+ }
+ CHECK_CALL_SUCCESS(env->Deallocate(local_data));
+ jint version = 0;
+ CHECK_CALL_SUCCESS(env->GetVersionNumber(&version));
+ if ((version & JVMTI_VERSION_1) != JVMTI_VERSION_1) {
+ printf("Unexpected version number!\n");
+ return -1;
+ }
+ CHECK_CALL_SUCCESS(env->DisposeEnvironment());
+ CHECK_CALL_SUCCESS(env2->DisposeEnvironment());
+#undef CHECK_CALL_SUCCESS
+ return JNI_OK;
+}
+
+} // namespace Test909AttachAgent
+} // namespace art
diff --git a/test/909-attach-agent/attach.h b/test/909-attach-agent/attach.h
new file mode 100644
index 0000000..3e6fe6c
--- /dev/null
+++ b/test/909-attach-agent/attach.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_TEST_909_ATTACH_AGENT_ATTACH_H_
+#define ART_TEST_909_ATTACH_AGENT_ATTACH_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test909AttachAgent {
+
+jint OnAttach(JavaVM* vm, char* options, void* reserved);
+
+} // namespace Test909AttachAgent
+} // namespace art
+
+#endif // ART_TEST_909_ATTACH_AGENT_ATTACH_H_
diff --git a/test/909-attach-agent/build b/test/909-attach-agent/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/909-attach-agent/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-build "$@" --experimental agents
diff --git a/test/909-attach-agent/expected.txt b/test/909-attach-agent/expected.txt
new file mode 100644
index 0000000..eacc595
--- /dev/null
+++ b/test/909-attach-agent/expected.txt
@@ -0,0 +1,3 @@
+Hello, world!
+Attached Agent for test 909-attach-agent
+Goodbye!
diff --git a/test/909-attach-agent/info.txt b/test/909-attach-agent/info.txt
new file mode 100644
index 0000000..06f3c8c
--- /dev/null
+++ b/test/909-attach-agent/info.txt
@@ -0,0 +1 @@
+Tests jvmti plugin attaching during live phase.
diff --git a/test/909-attach-agent/run b/test/909-attach-agent/run
new file mode 100755
index 0000000..aed6e83
--- /dev/null
+++ b/test/909-attach-agent/run
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+if [[ "$@" == *"-O"* ]]; then
+ agent=libtiagent.so
+ plugin=libopenjdkjvmti.so
+fi
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --android-runtime-option -Xplugin:${plugin} \
+ --args agent:${agent}=909-attach-agent
diff --git a/test/909-attach-agent/src/Main.java b/test/909-attach-agent/src/Main.java
new file mode 100644
index 0000000..8a8a087
--- /dev/null
+++ b/test/909-attach-agent/src/Main.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import dalvik.system.VMDebug;
+import java.io.IOException;
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ for(String a : args) {
+ if(a.startsWith("agent:")) {
+ String agent = a.substring(6);
+ try {
+ VMDebug.attachAgent(agent);
+ } catch(IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ System.out.println("Goodbye!");
+ }
+}
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index 2802dfa..badea53 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -57,6 +57,8 @@
public static void main(String[] args) throws Throwable {
testfindSpecial_invokeSuperBehaviour();
testfindSpecial_invokeDirectBehaviour();
+
+ testThrowException();
}
public static void testfindSpecial_invokeSuperBehaviour() throws Throwable {
@@ -131,6 +133,21 @@
} catch (IllegalAccessException expected) {
}
}
+
+ public static void testThrowException() throws Throwable {
+ MethodHandle handle = MethodHandles.throwException(String.class,
+ IllegalArgumentException.class);
+ if (handle.type().returnType() != String.class) {
+ System.out.println("Unexpected return type for handle: " + handle
+ + " [ " + handle.type() + "]");
+ }
+
+ try {
+ handle.invoke();
+ System.out.println("Expected an exception of type: java.lang.IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
}
diff --git a/test/Android.bp b/test/Android.bp
index aaa1343..be1864c 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -252,6 +252,7 @@
"906-iterate-heap/iterate_heap.cc",
"907-get-loaded-classes/get_loaded_classes.cc",
"908-gc-start-finish/gc_callbacks.cc",
+ "909-attach-agent/attach.cc",
],
shared_libs: [
"libbase",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index d962472..1e3a997 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -232,11 +232,9 @@
# Disable 149-suspend-all-stress, its output is flaky (b/28988206).
-# Disable 151-OpenFileLimit (b/32302133)
# Disable 577-profile-foreign-dex (b/27454772).
TEST_ART_BROKEN_ALL_TARGET_TESTS := \
149-suspend-all-stress \
- 151-OpenFileLimit \
577-profile-foreign-dex \
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -277,6 +275,7 @@
906-iterate-heap \
907-get-loaded-classes \
908-gc-start-finish \
+ 909-attach-agent \
ifneq (,$(filter target,$(TARGET_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 79b41ec..90d0a66 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -32,6 +32,7 @@
#include "906-iterate-heap/iterate_heap.h"
#include "907-get-loaded-classes/get_loaded_classes.h"
#include "908-gc-start-finish/gc_callbacks.h"
+#include "909-attach-agent/attach.h"
namespace art {
@@ -56,6 +57,7 @@
{ "906-iterate-heap", Test906IterateHeap::OnLoad, nullptr },
{ "907-get-loaded-classes", Test907GetLoadedClasses::OnLoad, nullptr },
{ "908-gc-start-finish", Test908GcStartFinish::OnLoad, nullptr },
+ { "909-attach-agent", nullptr, Test909AttachAgent::OnAttach },
};
static AgentLib* FindAgent(char* name) {
@@ -105,7 +107,6 @@
return lib->load(vm, remaining_options, reserved);
}
-
extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
char* remaining_options = nullptr;
char* name_option = nullptr;
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 5ef66d1..12e0338 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -45,16 +45,6 @@
fi
done
-# Workaround for repo incompatibilities on the Chromium buildbot.
-# TODO: Remove this workaround once https://bugs.chromium.org/p/chromium/issues/detail?id=646329
-# is addressed.
-repo=$(which repo)
-if [[ $repo == *"depot_tools"* ]]; then
- ln -s build/soong/root.bp Android.bp
- ln -s build/soong/bootstrap.bash bootstrap.bash
- echo "include build/core/main.mk" > Makefile
-fi
-
if [[ $mode == "host" ]]; then
make_command="make $j_arg $showcommands build-art-host-tests $common_targets"
make_command+=" ${out_dir}/host/linux-x86/lib/libjavacoretests.so "