class StubTest : public CommonRuntimeTest { protected: // We need callee-save methods set up in the Runtime for exceptions. void SetUp() override { // Do the normal setup.
CommonRuntimeTest::SetUp();
{ // Create callee-save methods
ScopedObjectAccess soa(Thread::Current());
runtime_->SetInstructionSet(kRuntimeISA); for (uint32_t i = 0; i < static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType); ++i) {
CalleeSaveType type = CalleeSaveType(i); if (!runtime_->HasCalleeSaveMethod(type)) {
runtime_->SetCalleeSaveMethod(runtime_->CreateCalleeSaveMethod(), type);
}
}
}
}
void SetUpRuntimeOptions(RuntimeOptions *options) override { // Use a smaller heap for (std::pair<std::string, constvoid*>& pair : *options) { if (pair.first.find("-Xmx") == 0) {
pair.first = "-Xmx4M"; // Smallest we can go.
}
}
options->push_back(std::make_pair("-Xint", nullptr));
}
// Helper function needed since TEST_F makes a new class.
Thread::tls_ptr_sized_values* GetTlsPtr(Thread* self) { return &self->tlsPtr_;
}
// TODO: Set up a frame according to referrer's specs.
size_t Invoke3WithReferrer(size_t arg0, size_t arg1, size_t arg2, uintptr_t code, Thread* self,
ArtMethod* referrer) { return Invoke3WithReferrerAndHidden(arg0, arg1, arg2, code, self, referrer, 0);
}
// TODO: Set up a frame according to referrer's specs.
size_t Invoke3WithReferrerAndHidden(size_t arg0, size_t arg1, size_t arg2, uintptr_t code,
Thread* self, ArtMethod* referrer, size_t hidden) { // Push a transition back into managed code onto the linked list in thread.
ManagedStack fragment;
ScopedManagedStackFragment smsf(self, &fragment);
size_t result;
size_t fpr_result = 0; #ifdefined(__i386__) // TODO: Set the thread? #define PUSH(reg) "push "# reg "\n\t .cfi_adjust_cfa_offset 4\n\t" #define POP(reg) "pop "# reg "\n\t .cfi_adjust_cfa_offset -4\n\t"
__asm__ __volatile__( "movd %[hidden], %%xmm7\n\t"// This is a memory op, so do this early. If it is off of // esp, then we won't be able to access it after spilling.
// Store the inputs to the stack, but keep the referrer up top, less work.
PUSH(%[referrer]) // Align stack.
PUSH(%[referrer]) // Store referrer
PUSH(%[arg0])
PUSH(%[arg1])
PUSH(%[arg2])
PUSH(%[code]) // Now read them back into the required registers.
POP(%%edi)
POP(%%edx)
POP(%%ecx)
POP(%%eax) // Call is prepared now.
"call *%%edi\n\t"// Call the stub "addl $8, %%esp\n\t"// Pop referrer and padding. ".cfi_adjust_cfa_offset -8\n\t"
: "=a" (result) // Use the result from eax
: [arg0] "r"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code),
[referrer]"r"(referrer), [hidden]"m"(hidden) // This places code into edi, arg0 into eax, arg1 into ecx, and arg2 into edx
: "memory", "xmm7"); // clobber. #undef PUSH #undef POP #elifdefined(__arm__)
__asm__ __volatile__( "push {r1-r12, lr}\n\t"// Save state, 13*4B = 52B ".cfi_adjust_cfa_offset 52\n\t" "push {r9}\n\t" ".cfi_adjust_cfa_offset 4\n\t" "mov r9, %[referrer]\n\n" "str r9, [sp, #-8]!\n\t"// Push referrer, +8B padding so 16B aligned ".cfi_adjust_cfa_offset 8\n\t" "ldr r9, [sp, #8]\n\t"
// Push everything on the stack, so we don't rely on the order. What a mess. :-( "sub sp, sp, #24\n\t" "str %[arg0], [sp]\n\t" "str %[arg1], [sp, #4]\n\t" "str %[arg2], [sp, #8]\n\t" "str %[code], [sp, #12]\n\t" "str %[self], [sp, #16]\n\t" "str %[hidden], [sp, #20]\n\t" "ldr r0, [sp]\n\t" "ldr r1, [sp, #4]\n\t" "ldr r2, [sp, #8]\n\t" "ldr r3, [sp, #12]\n\t" "ldr r9, [sp, #16]\n\t" "ldr r12, [sp, #20]\n\t" "add sp, sp, #24\n\t"
"blx r3\n\t"// Call the stub "add sp, sp, #12\n\t"// Pop null and padding ".cfi_adjust_cfa_offset -12\n\t" "pop {r1-r12, lr}\n\t"// Restore state ".cfi_adjust_cfa_offset -52\n\t" "mov %[result], r0\n\t"// Save the result
: [result] "=r" (result) // Use the result from r0
: [arg0] "r"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code), [self] "r"(self),
[referrer] "r"(referrer), [hidden] "r"(hidden)
: "r0", "memory"); // clobber. #elifdefined(__aarch64__)
__asm__ __volatile__( // Spill x0-x7 which we say we don't clobber. May contain args. "sub sp, sp, #80\n\t" ".cfi_adjust_cfa_offset 80\n\t" "stp x0, x1, [sp]\n\t" "stp x2, x3, [sp, #16]\n\t" "stp x4, x5, [sp, #32]\n\t" "stp x6, x7, [sp, #48]\n\t" // To be extra defensive, store x20,x21. We do this because some of the stubs might make a // transition into the runtime via the blr instruction below and *not* save x20. "stp x20, x21, [sp, #64]\n\t"
// Push everything on the stack, so we don't rely on the order. What a mess. :-( "sub sp, sp, #48\n\t" ".cfi_adjust_cfa_offset 48\n\t" // All things are "r" constraints, so direct str/stp should work. "stp %[arg0], %[arg1], [sp]\n\t" "stp %[arg2], %[code], [sp, #16]\n\t" "stp %[self], %[hidden], [sp, #32]\n\t"
// End "3:\n\t"
: [result] "=r" (result) // Use the result from r0
: [arg0] "0"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code), [self] "r"(self),
[referrer] "r"(referrer), [hidden] "r"(hidden), [fpr_result] "m" (fpr_result) // X18 is a reserved register, cannot be clobbered. // Leave one register unclobbered, which is needed for compiling with // -fstack-protector-strong. According to AAPCS64 registers x9-x15 are caller-saved, // which means we should unclobber one of the callee-saved registers that are unused. // Here we use x20. // http://b/72613441, Clang 7.0 asks for one more register, so we do not reserve x21.
: "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x19", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x30", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31", "memory"); #elifdefined(__x86_64__) && !defined(__APPLE__) #define PUSH(reg) "pushq "# reg "\n\t .cfi_adjust_cfa_offset 8\n\t" #define POP(reg) "popq "# reg "\n\t .cfi_adjust_cfa_offset -8\n\t" // Note: Uses the native convention. We do a callee-save regimen by manually spilling and // restoring almost all registers. // TODO: Set the thread?
__asm__ __volatile__( // Spill almost everything (except rax, rsp). 14 registers.
PUSH(%%rbx)
PUSH(%%rcx)
PUSH(%%rdx)
PUSH(%%rsi)
PUSH(%%rdi)
PUSH(%%rbp)
PUSH(%%r8)
PUSH(%%r9)
PUSH(%%r10)
PUSH(%%r11)
PUSH(%%r12)
PUSH(%%r13)
PUSH(%%r14)
PUSH(%%r15)
: "=a" (result) // Use the result from rax
: [arg0] "r"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code),
[referrer] "r"(referrer), [hidden] "r"(hidden) // This places arg0 into rdi, arg1 into rsi, arg2 into rdx, and code into some other // register. We can't use "b" (rbx), as ASAN uses this for the frame pointer.
: "memory"); // We spill and restore (almost) all registers, so only mention memory here. #undef PUSH #undef POP #else
UNUSED(arg0, arg1, arg2, code, referrer, hidden);
LOG(WARNING) << "Was asked to invoke for an architecture I do not understand.";
result = 0; #endif
for (size_t i = 1; i < 4; ++i) {
EXPECT_NE(orig[i], trg[i]);
}
for (size_t i = 4; i < 14; ++i) {
EXPECT_EQ(orig[i], trg[i]);
}
for (size_t i = 14; i < 20; ++i) {
EXPECT_NE(orig[i], trg[i]);
}
// TODO: Test overlapping?
#else
LOG(INFO) << "Skipping memcpy as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping memcpy as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
// Force a fat lock by running identity hashcode to fill up lock word.
Handle<mirror::String> obj2(hs.NewHandle(
mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!")));
// Test done. #else
LOG(INFO) << "Skipping lock_object as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping lock_object as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
class RandGen { public: explicit RandGen(uint32_t seed) : val_(seed) {}
// NO_THREAD_SAFETY_ANALYSIS as we do not want to grab exclusive mutator lock for MonitorInfo. staticvoid TestUnlockObject(StubTest* test) NO_THREAD_SAFETY_ANALYSIS { #ifdefined(__i386__) || defined(__arm__) || defined(__aarch64__) || \
(defined(__x86_64__) && !defined(__APPLE__)) static constexpr size_t kThinLockLoops = 100;
Thread* self = Thread::Current();
const uintptr_t art_quick_lock_object = StubTest::GetEntrypoint(self, kQuickLockObject); const uintptr_t art_quick_unlock_object = StubTest::GetEntrypoint(self, kQuickUnlockObject); // Create an object
ScopedObjectAccess soa(self); // garbage is created during ClassLinker::Init static constexpr size_t kNumberOfLocks = 10; // Number of objects = lock
StackHandleScope<kNumberOfLocks + 1> hs(self);
Handle<mirror::String> obj(
hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!")));
LockWord lock = obj->GetLockWord(false);
LockWord::LockState old_state = lock.GetState();
EXPECT_EQ(LockWord::LockState::kUnlocked, old_state);
test->Invoke3(reinterpret_cast<size_t>(obj.Get()), 0U, 0U, art_quick_unlock_object, self); // This should be an illegal monitor state.
EXPECT_TRUE(self->IsExceptionPending());
self->ClearException();
// Unlock the remaining count times and then check it's unlocked. Then deallocate. // Go reverse order to correctly handle Handles. for (size_t i = 0; i < kNumberOfLocks; ++i) {
size_t index = kNumberOfLocks - 1 - i;
size_t count = counts[index]; while (count > 0) {
test->Invoke3(reinterpret_cast<size_t>(objects[index].Get()), 0U, 0U, art_quick_unlock_object,
self);
count--;
}
// Test done. #else
UNUSED(test);
LOG(INFO) << "Skipping unlock_object as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping unlock_object as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
TEST_F(StubTest, UnlockObject) { // This will lead to monitor error messages in the log.
ScopedLogSeverity sls(LogSeverity::FATAL);
#else
LOG(INFO) << "Skipping check_cast as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping check_cast as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
TEST_F(StubTest, AllocObject) { #ifdefined(__i386__) || defined(__arm__) || defined(__aarch64__) || \
(defined(__x86_64__) && !defined(__APPLE__)) // This will lead to OOM error messages in the log.
ScopedLogSeverity sls(LogSeverity::FATAL);
// TODO: Check the "Unresolved" allocation stubs
Thread* self = Thread::Current(); // Create an object
ScopedObjectAccess soa(self); // garbage is created during ClassLinker::Init
// Array helps to fill memory faster.
Handle<mirror::Class> ca(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;")));
// Use arbitrary large amount for now. staticconst size_t kMaxHandles = 1000000;
std::unique_ptr<StackHandleScope<kMaxHandles>> hsp(new StackHandleScope<kMaxHandles>(self));
std::vector<Handle<mirror::Object>> handles; // Start allocating with 128K
size_t length = 128 * KB / 4; while (length > 10) {
Handle<mirror::Object> h(hsp->NewHandle<mirror::Object>(
mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), ca.Get(), length / 4))); if (self->IsExceptionPending() || h == nullptr) {
self->ClearException();
// Try a smaller length
length = length / 8; // Use at most half the reported free space.
size_t mem = Runtime::Current()->GetHeap()->GetFreeMemory(); if (length * 8 > mem) {
length = mem / 8;
}
} else {
handles.push_back(h);
}
}
LOG(INFO) << "Used " << handles.size() << " arrays to fill space.";
// Allocate simple objects till it fails. while (!self->IsExceptionPending()) {
Handle<mirror::Object> h = hsp->NewHandle(c->AllocObject(soa.Self())); if (!self->IsExceptionPending() && h != nullptr) {
handles.push_back(h);
}
}
self->ClearException();
// Tests done. #else
LOG(INFO) << "Skipping alloc_object as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping alloc_object as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
{ // We can use null in the second argument as we do not need a method here (not used in // resolved/initialized cases)
size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()), 10U, reinterpret_cast<size_t>(nullptr),
StubTest::GetEntrypoint(self, kQuickAllocArrayResolved32),
self);
EXPECT_FALSE(self->IsExceptionPending()) << mirror::Object::PrettyTypeOf(self->GetException());
EXPECT_NE(reinterpret_cast<size_t>(nullptr), result);
ObjPtr<mirror::Object> obj = reinterpret_cast<mirror::Object*>(result);
EXPECT_TRUE(obj->IsArrayInstance());
EXPECT_TRUE(obj->IsObjectArray());
EXPECT_OBJ_PTR_EQ(c.Get(), obj->GetClass());
VerifyObject(obj);
ObjPtr<mirror::Array> array = reinterpret_cast<mirror::Array*>(result);
EXPECT_EQ(array->GetLength(), 10);
}
// Failure tests.
// Out-of-memory.
{
size_t result = Invoke3(reinterpret_cast<size_t>(c.Get()),
GB, // that should fail... reinterpret_cast<size_t>(nullptr),
StubTest::GetEntrypoint(self, kQuickAllocArrayResolved32),
self);
// Tests done. #else
LOG(INFO) << "Skipping alloc_array as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping alloc_array as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
TEST_F(StubTest, StringCompareTo) { // There is no StringCompareTo runtime entrypoint for __arm__ or __aarch64__. #ifdefined(__i386__) || (defined(__x86_64__) && !defined(__APPLE__)) // TODO: Check the "Unresolved" allocation stubs
ScopedObjectAccess soa(self); // garbage is created during ClassLinker::Init
// Create some strings // Use array so we can index into it and use a matrix for expected results // Setup: The first half is standard. The second half uses a non-zero offset. // TODO: Shared backing arrays. constchar* c[] = { "", "", "a", "aa", "ab", "aacaacaacaacaacaac", // This one's under the default limit to go to __memcmp16. "aacaacaacaacaacaacaacaacaacaacaacaac", // This one's over. "aacaacaacaacaacaacaacaacaacaacaacaaca" }; // As is this one. We need a separate one to // defeat object-equal optimizations. static constexpr size_t kStringCount = arraysize(c);
for (size_t i = 0; i < kStringCount; ++i) {
s[i] = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), c[i]));
}
// TODO: wide characters
// Matrix of expectations. First component is first parameter. Note we only check against the // sign, not the value. As we are testing random offsets, we need to compute this and need to // rely on String::CompareTo being correct.
int32_t expected[kStringCount][kStringCount]; for (size_t x = 0; x < kStringCount; ++x) { for (size_t y = 0; y < kStringCount; ++y) {
expected[x][y] = s[x]->CompareTo(s[y].Get());
}
}
// Play with it...
for (size_t x = 0; x < kStringCount; ++x) { for (size_t y = 0; y < kStringCount; ++y) { // Test string_compareto x y
size_t result = Invoke3(reinterpret_cast<size_t>(s[x].Get()), reinterpret_cast<size_t>(s[y].Get()), 0U,
art_quick_string_compareto, self);
// Tests done. #else
LOG(INFO) << "Skipping string_compareto as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping string_compareto as I don't know how to do that on " << kRuntimeISA <<
std::endl; #endif
}
for (size_t i = 0; i < num_values; ++i) {
test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), static_cast<size_t>(values[i]), 0U,
StubTest::GetEntrypoint(self, kQuickSet8Static),
self,
referrer);
size_t res = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), 0U, 0U,
StubTest::GetEntrypoint(self, kQuickGetBooleanStatic),
self,
referrer); // Boolean currently stores bools as uint8_t, be more zealous about asserting correct writes/gets.
EXPECT_EQ(values[i], static_cast<uint8_t>(res)) << "Iteration " << i;
} #else
UNUSED(f, self, referrer, test);
LOG(INFO) << "Skipping set_boolean_static as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set_boolean_static as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
} staticvoid GetSetByteStatic(ArtField* f, Thread* self, ArtMethod* referrer,
StubTest* test)
REQUIRES_SHARED(Locks::mutator_lock_) { #ifdefined(__i386__) || defined(__arm__) || defined(__aarch64__) || \
(defined(__x86_64__) && !defined(__APPLE__))
int8_t values[] = { -128, -64, 0, 64, 127 };
for (size_t i = 0; i < arraysize(values); ++i) {
test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), static_cast<size_t>(values[i]), 0U,
StubTest::GetEntrypoint(self, kQuickSet8Static),
self,
referrer);
size_t res = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), 0U, 0U,
StubTest::GetEntrypoint(self, kQuickGetByteStatic),
self,
referrer);
EXPECT_EQ(values[i], static_cast<int8_t>(res)) << "Iteration " << i;
} #else
UNUSED(f, self, referrer, test);
LOG(INFO) << "Skipping set_byte_static as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set_byte_static as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), reinterpret_cast<size_t>(obj->Get()), 0U,
StubTest::GetEntrypoint(self, kQuickGetByteInstance),
self,
referrer);
EXPECT_EQ(res, static_cast<int8_t>(res2));
} #else
UNUSED(obj, f, self, referrer, test);
LOG(INFO) << "Skipping set_byte_instance as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set_byte_instance as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
for (size_t i = 0; i < arraysize(values); ++i) {
test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), static_cast<size_t>(values[i]), 0U,
StubTest::GetEntrypoint(self, kQuickSet16Static),
self,
referrer);
size_t res = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), 0U, 0U,
StubTest::GetEntrypoint(self, kQuickGetCharStatic),
self,
referrer);
EXPECT_EQ(values[i], static_cast<uint16_t>(res)) << "Iteration " << i;
} #else
UNUSED(f, self, referrer, test);
LOG(INFO) << "Skipping set_char_static as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set_char_static as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
} staticvoid GetSetShortStatic(ArtField* f, Thread* self,
ArtMethod* referrer, StubTest* test)
REQUIRES_SHARED(Locks::mutator_lock_) { #ifdefined(__i386__) || defined(__arm__) || defined(__aarch64__) || \
(defined(__x86_64__) && !defined(__APPLE__))
int16_t values[] = { -0x7FFF, -32768, 0, 255, 32767, 0x7FFE };
for (size_t i = 0; i < arraysize(values); ++i) {
test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), static_cast<size_t>(values[i]), 0U,
StubTest::GetEntrypoint(self, kQuickSet16Static),
self,
referrer);
size_t res = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), 0U, 0U,
StubTest::GetEntrypoint(self, kQuickGetShortStatic),
self,
referrer);
EXPECT_EQ(static_cast<int16_t>(res), values[i]) << "Iteration " << i;
} #else
UNUSED(f, self, referrer, test);
LOG(INFO) << "Skipping set_short_static as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set_short_static as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), reinterpret_cast<size_t>(obj->Get()), 0U,
StubTest::GetEntrypoint(self, kQuickGetShortInstance),
self,
referrer);
EXPECT_EQ(res, static_cast<int16_t>(res2));
} #else
UNUSED(obj, f, self, referrer, test);
LOG(INFO) << "Skipping set_short_instance as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set_short_instance as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
for (size_t i = 0; i < arraysize(values); ++i) {
test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), static_cast<size_t>(values[i]), 0U,
StubTest::GetEntrypoint(self, kQuickSet32Static),
self,
referrer);
size_t res = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), 0U, 0U,
StubTest::GetEntrypoint(self, kQuickGet32Static),
self,
referrer);
EXPECT_EQ(res, values[i]) << "Iteration " << i;
} #else
UNUSED(f, self, referrer, test);
LOG(INFO) << "Skipping set32static as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set32static as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), reinterpret_cast<size_t>(obj->Get()), 0U,
StubTest::GetEntrypoint(self, kQuickGet32Instance),
self,
referrer);
EXPECT_EQ(res, static_cast<int32_t>(res2));
} #else
UNUSED(obj, f, self, referrer, test);
LOG(INFO) << "Skipping set32instance as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set32instance as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
// Allocate a string object for simplicity.
ObjPtr<mirror::String> str = mirror::String::AllocFromModifiedUtf8(self, "Test");
set_and_check_static(f->GetDexFieldIndex(), str, self, referrer, test);
set_and_check_static(f->GetDexFieldIndex(), nullptr, self, referrer, test); #else
UNUSED(f, self, referrer, test);
LOG(INFO) << "Skipping setObjstatic as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping setObjstatic as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
// Allocate a string object for simplicity.
ObjPtr<mirror::String> str = mirror::String::AllocFromModifiedUtf8(self, "Test");
set_and_check_instance(f, obj->Get(), str, self, referrer, test);
set_and_check_instance(f, obj->Get(), nullptr, self, referrer, test); #else
UNUSED(obj, f, self, referrer, test);
LOG(INFO) << "Skipping setObjinstance as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping setObjinstance as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
// TODO: Complete these tests for 32b architectures
for (size_t i = 0; i < arraysize(values); ++i) { // 64 bit FieldSet stores the set value in the second register.
test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()),
values[i], 0U,
StubTest::GetEntrypoint(self, kQuickSet64Static),
self,
referrer);
size_t res = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), 0U, 0U,
StubTest::GetEntrypoint(self, kQuickGet64Static),
self,
referrer);
EXPECT_EQ(res, values[i]) << "Iteration " << i;
} #else
UNUSED(f, self, referrer, test);
LOG(INFO) << "Skipping set64static as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set64static as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>(f->GetDexFieldIndex()), reinterpret_cast<size_t>(obj->Get()), 0U,
StubTest::GetEntrypoint(self, kQuickGet64Instance),
self,
referrer);
EXPECT_EQ(res, static_cast<int64_t>(res2));
} #else
UNUSED(obj, f, self, referrer, test);
LOG(INFO) << "Skipping set64instance as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping set64instance as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
staticvoid TestFields(Thread* self, StubTest* test, Primitive::Type test_type) { // garbage is created during ClassLinker::Init
ScopedObjectAccess soa(self);
StackHandleScope<3> hs(self);
Handle<mirror::Object> obj(hs.NewHandle(soa.Decode<mirror::Object>(o)));
Handle<mirror::Class> c(hs.NewHandle(obj->GetClass())); // Need a method as a referrer
ArtMethod* m = &c->GetMethods(kRuntimePointerSize)[0];
// Play with it...
// Static fields. for (ArtField& f : c->GetFields()) { if (!f.IsStatic()) { continue;
}
Primitive::Type type = f.GetTypeAsPrimitiveType(); if (test_type != type) { continue;
} switch (type) { case Primitive::Type::kPrimBoolean:
GetSetBooleanStatic(&f, self, m, test); break; case Primitive::Type::kPrimByte:
GetSetByteStatic(&f, self, m, test); break; case Primitive::Type::kPrimChar:
GetSetCharStatic(&f, self, m, test); break; case Primitive::Type::kPrimShort:
GetSetShortStatic(&f, self, m, test); break; case Primitive::Type::kPrimInt:
GetSet32Static(&f, self, m, test); break; case Primitive::Type::kPrimLong:
GetSet64Static(&f, self, m, test); break; case Primitive::Type::kPrimNot: // Don't try array. if (f.GetTypeDescriptor()[0] != '[') {
GetSetObjStatic(&f, self, m, test);
} break; default: break; // Skip.
}
}
// Instance fields. for (ArtField& f : c->GetFields()) { if (f.IsStatic()) { continue;
}
Primitive::Type type = f.GetTypeAsPrimitiveType(); if (test_type != type) { continue;
} switch (type) { case Primitive::Type::kPrimBoolean:
GetSetBooleanInstance(&obj, &f, self, m, test); break; case Primitive::Type::kPrimByte:
GetSetByteInstance(&obj, &f, self, m, test); break; case Primitive::Type::kPrimChar:
GetSetCharInstance(&obj, &f, self, m, test); break; case Primitive::Type::kPrimShort:
GetSetShortInstance(&obj, &f, self, m, test); break; case Primitive::Type::kPrimInt:
GetSet32Instance(&obj, &f, self, m, test); break; case Primitive::Type::kPrimLong:
GetSet64Instance(&obj, &f, self, m, test); break; case Primitive::Type::kPrimNot: // Don't try array. if (f.GetTypeDescriptor()[0] != '[') {
GetSetObjInstance(&obj, &f, self, m, test);
} break; default: break; // Skip.
}
}
// Disabled, b/27991555 . // FIXME: Hacking the entry point to point to art_quick_to_interpreter_bridge is broken. // The bridge calls through to GetCalleeSaveMethodCaller() which looks up the pre-header // and gets a bogus OatQuickMethodHeader* pointing into our assembly code just before // the bridge and uses that to check for inlined frames, crashing in the process.
TEST_F(StubTest, DISABLED_IMT) { #ifdefined(__i386__) || defined(__arm__) || defined(__aarch64__) || \
(defined(__x86_64__) && !defined(__APPLE__))
Thread* self = Thread::Current();
// We construct the ImtConflictTable ourselves, as we cannot go into the runtime stub // that will create it: the runtime stub expects to be called by compiled code.
LinearAlloc* linear_alloc = Runtime::Current()->GetLinearAlloc();
ArtMethod* conflict_method = Runtime::Current()->CreateImtConflictMethod(linear_alloc);
ImtConflictTable* empty_conflict_table =
Runtime::Current()->GetClassLinker()->CreateImtConflictTable(/*count=*/0u, linear_alloc); void* data = linear_alloc->Alloc(
self,
ImtConflictTable::ComputeSizeWithOneMoreEntry(empty_conflict_table, kRuntimePointerSize),
LinearAllocKind::kNoGCRoots);
ImtConflictTable* new_table = new (data) ImtConflictTable(
empty_conflict_table, inf_contains, contains_amethod, kRuntimePointerSize);
conflict_method->SetImtConflictTable(new_table, kRuntimePointerSize);
result = Invoke3WithReferrer( static_cast<size_t>(inf_contains->GetDexMethodIndex()), reinterpret_cast<size_t>(array_list.Get()), reinterpret_cast<size_t>(array_list.Get()),
StubTest::GetEntrypoint(self, kQuickInvokeInterfaceTrampolineWithAccessCheck), self,
contains_amethod);
ASSERT_FALSE(self->IsExceptionPending());
EXPECT_EQ(static_cast<size_t>(JNI_FALSE), result); #else
LOG(INFO) << "Skipping imt as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping imt as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
TEST_F(StubTest, StringIndexOf) { #ifdefined(__arm__) || defined(__aarch64__)
Thread* self = Thread::Current();
ScopedObjectAccess soa(self); // garbage is created during ClassLinker::Init
// Create some strings // Use array so we can index into it and use a matrix for expected results // Setup: The first half is standard. The second half uses a non-zero offset. // TODO: Shared backing arrays. constchar* c_str[] = { "", "a", "ba", "cba", "dcba", "edcba", "asdfghjkl" }; static constexpr size_t kStringCount = arraysize(c_str); constchar c_char[] = { 'a', 'b', 'c', 'd', 'e' }; static constexpr size_t kCharCount = arraysize(c_char);
for (size_t i = 0; i < kStringCount; ++i) {
s[i] = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), c_str[i]));
}
// Matrix of expectations. First component is first parameter. Note we only check against the // sign, not the value. As we are testing random offsets, we need to compute this and need to // rely on String::CompareTo being correct. static constexpr size_t kMaxLen = 9;
DCHECK_LE(strlen(c_str[kStringCount-1]), kMaxLen) << "Please fix the indexof test.";
// Last dimension: start, offset by 1.
int32_t expected[kStringCount][kCharCount][kMaxLen + 3]; for (size_t x = 0; x < kStringCount; ++x) { for (size_t y = 0; y < kCharCount; ++y) { for (size_t z = 0; z <= kMaxLen + 2; ++z) {
expected[x][y][z] = s[x]->FastIndexOf(c_char[y], static_cast<int32_t>(z) - 1);
}
}
}
// Play with it...
for (size_t x = 0; x < kStringCount; ++x) { for (size_t y = 0; y < kCharCount; ++y) { for (size_t z = 0; z <= kMaxLen + 2; ++z) {
int32_t start = static_cast<int32_t>(z) - 1;
// Test string_compareto x y
size_t result = Invoke3(reinterpret_cast<size_t>(s[x].Get()), c_char[y], start,
StubTest::GetEntrypoint(self, kQuickIndexOf), self);
EXPECT_FALSE(self->IsExceptionPending());
// The result is a 32b signed integer union {
size_t r;
int32_t i;
} conv;
conv.r = result;
// Tests done. #else
LOG(INFO) << "Skipping indexof as I don't know how to do that on " << kRuntimeISA; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping indexof as I don't know how to do that on " << kRuntimeISA << std::endl; #endif
}
// TODO: Exercise the ReadBarrierMarkRegX entry points.
GcRoot<mirror::Class> root(GetClassRoot<mirror::String>());
size_t result = Invoke3(reinterpret_cast<size_t>(&root), 0U, 0U, readBarrierForRootSlow, self);
EXPECT_FALSE(self->IsExceptionPending());
EXPECT_NE(reinterpret_cast<size_t>(nullptr), result);
mirror::Class* klass = reinterpret_cast<mirror::Class*>(result);
EXPECT_OBJ_PTR_EQ(klass, obj->GetClass()); return;
} #endif
LOG(INFO) << "Skipping read_barrier_for_root_slow"; // Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping read_barrier_for_root_slow" << std::endl;
}
} // namespace art
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.32Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.