You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
990 lines
44 KiB
990 lines
44 KiB
/*
|
|
* Copyright (C) 2015 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 "instrumentation.h"
|
|
|
|
#include "android-base/macros.h"
|
|
#include "art_method-inl.h"
|
|
#include "base/enums.h"
|
|
#include "class_linker-inl.h"
|
|
#include "common_runtime_test.h"
|
|
#include "common_throws.h"
|
|
#include "dex/dex_file.h"
|
|
#include "gc/scoped_gc_critical_section.h"
|
|
#include "handle_scope-inl.h"
|
|
#include "jni/jni_internal.h"
|
|
#include "jvalue.h"
|
|
#include "runtime.h"
|
|
#include "scoped_thread_state_change-inl.h"
|
|
#include "interpreter/shadow_frame.h"
|
|
#include "thread-inl.h"
|
|
#include "thread_list.h"
|
|
#include "well_known_classes.h"
|
|
|
|
namespace art {
|
|
namespace instrumentation {
|
|
|
|
class TestInstrumentationListener final : public instrumentation::InstrumentationListener {
|
|
public:
|
|
TestInstrumentationListener()
|
|
: received_method_enter_event(false),
|
|
received_method_exit_event(false),
|
|
received_method_exit_object_event(false),
|
|
received_method_unwind_event(false),
|
|
received_dex_pc_moved_event(false),
|
|
received_field_read_event(false),
|
|
received_field_written_event(false),
|
|
received_field_written_object_event(false),
|
|
received_exception_thrown_event(false),
|
|
received_exception_handled_event(false),
|
|
received_branch_event(false),
|
|
received_watched_frame_pop(false) {}
|
|
|
|
virtual ~TestInstrumentationListener() {}
|
|
|
|
void MethodEntered(Thread* thread ATTRIBUTE_UNUSED,
|
|
Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
|
|
ArtMethod* method ATTRIBUTE_UNUSED,
|
|
uint32_t dex_pc ATTRIBUTE_UNUSED)
|
|
override REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
received_method_enter_event = true;
|
|
}
|
|
|
|
void MethodExited(Thread* thread ATTRIBUTE_UNUSED,
|
|
Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
|
|
ArtMethod* method ATTRIBUTE_UNUSED,
|
|
uint32_t dex_pc ATTRIBUTE_UNUSED,
|
|
instrumentation::OptionalFrame frame ATTRIBUTE_UNUSED,
|
|
MutableHandle<mirror::Object>& return_value ATTRIBUTE_UNUSED)
|
|
override REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
received_method_exit_object_event = true;
|
|
}
|
|
|
|
void MethodExited(Thread* thread ATTRIBUTE_UNUSED,
|
|
Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
|
|
ArtMethod* method ATTRIBUTE_UNUSED,
|
|
uint32_t dex_pc ATTRIBUTE_UNUSED,
|
|
instrumentation::OptionalFrame frame ATTRIBUTE_UNUSED,
|
|
JValue& return_value ATTRIBUTE_UNUSED)
|
|
override REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
received_method_exit_event = true;
|
|
}
|
|
|
|
void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED,
|
|
Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
|
|
ArtMethod* method ATTRIBUTE_UNUSED,
|
|
uint32_t dex_pc ATTRIBUTE_UNUSED)
|
|
override REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
received_method_unwind_event = true;
|
|
}
|
|
|
|
void DexPcMoved(Thread* thread ATTRIBUTE_UNUSED,
|
|
Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
|
|
ArtMethod* method ATTRIBUTE_UNUSED,
|
|
uint32_t new_dex_pc ATTRIBUTE_UNUSED)
|
|
override REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
received_dex_pc_moved_event = true;
|
|
}
|
|
|
|
void FieldRead(Thread* thread ATTRIBUTE_UNUSED,
|
|
Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
|
|
ArtMethod* method ATTRIBUTE_UNUSED,
|
|
uint32_t dex_pc ATTRIBUTE_UNUSED,
|
|
ArtField* field ATTRIBUTE_UNUSED)
|
|
override REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
received_field_read_event = true;
|
|
}
|
|
|
|
void FieldWritten(Thread* thread ATTRIBUTE_UNUSED,
|
|
Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
|
|
ArtMethod* method ATTRIBUTE_UNUSED,
|
|
uint32_t dex_pc ATTRIBUTE_UNUSED,
|
|
ArtField* field ATTRIBUTE_UNUSED,
|
|
Handle<mirror::Object> field_value ATTRIBUTE_UNUSED)
|
|
override REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
received_field_written_object_event = true;
|
|
}
|
|
|
|
void FieldWritten(Thread* thread ATTRIBUTE_UNUSED,
|
|
Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
|
|
ArtMethod* method ATTRIBUTE_UNUSED,
|
|
uint32_t dex_pc ATTRIBUTE_UNUSED,
|
|
ArtField* field ATTRIBUTE_UNUSED,
|
|
const JValue& field_value ATTRIBUTE_UNUSED)
|
|
override REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
received_field_written_event = true;
|
|
}
|
|
|
|
void ExceptionThrown(Thread* thread ATTRIBUTE_UNUSED,
|
|
Handle<mirror::Throwable> exception_object ATTRIBUTE_UNUSED)
|
|
override REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
received_exception_thrown_event = true;
|
|
}
|
|
|
|
void ExceptionHandled(Thread* self ATTRIBUTE_UNUSED,
|
|
Handle<mirror::Throwable> throwable ATTRIBUTE_UNUSED)
|
|
override REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
received_exception_handled_event = true;
|
|
}
|
|
|
|
void Branch(Thread* thread ATTRIBUTE_UNUSED,
|
|
ArtMethod* method ATTRIBUTE_UNUSED,
|
|
uint32_t dex_pc ATTRIBUTE_UNUSED,
|
|
int32_t dex_pc_offset ATTRIBUTE_UNUSED)
|
|
override REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
received_branch_event = true;
|
|
}
|
|
|
|
void WatchedFramePop(Thread* thread ATTRIBUTE_UNUSED, const ShadowFrame& frame ATTRIBUTE_UNUSED)
|
|
override REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
received_watched_frame_pop = true;
|
|
}
|
|
|
|
void Reset() {
|
|
received_method_enter_event = false;
|
|
received_method_exit_event = false;
|
|
received_method_exit_object_event = false;
|
|
received_method_unwind_event = false;
|
|
received_dex_pc_moved_event = false;
|
|
received_field_read_event = false;
|
|
received_field_written_event = false;
|
|
received_field_written_object_event = false;
|
|
received_exception_thrown_event = false;
|
|
received_exception_handled_event = false;
|
|
received_branch_event = false;
|
|
received_watched_frame_pop = false;
|
|
}
|
|
|
|
bool received_method_enter_event;
|
|
bool received_method_exit_event;
|
|
bool received_method_exit_object_event;
|
|
bool received_method_unwind_event;
|
|
bool received_dex_pc_moved_event;
|
|
bool received_field_read_event;
|
|
bool received_field_written_event;
|
|
bool received_field_written_object_event;
|
|
bool received_exception_thrown_event;
|
|
bool received_exception_handled_event;
|
|
bool received_branch_event;
|
|
bool received_watched_frame_pop;
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(TestInstrumentationListener);
|
|
};
|
|
|
|
class InstrumentationTest : public CommonRuntimeTest {
|
|
public:
|
|
// Unique keys used to test Instrumentation::ConfigureStubs.
|
|
static constexpr const char* kClientOneKey = "TestClient1";
|
|
static constexpr const char* kClientTwoKey = "TestClient2";
|
|
|
|
void CheckConfigureStubs(const char* key, Instrumentation::InstrumentationLevel level) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
|
|
ScopedThreadSuspension sts(soa.Self(), kSuspended);
|
|
gc::ScopedGCCriticalSection gcs(soa.Self(),
|
|
gc::kGcCauseInstrumentation,
|
|
gc::kCollectorTypeInstrumentation);
|
|
ScopedSuspendAll ssa("Instrumentation::ConfigureStubs");
|
|
instr->ConfigureStubs(key, level);
|
|
}
|
|
|
|
Instrumentation::InstrumentationLevel GetCurrentInstrumentationLevel() {
|
|
return Runtime::Current()->GetInstrumentation()->GetCurrentInstrumentationLevel();
|
|
}
|
|
|
|
size_t GetInstrumentationUserCount() {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
return Runtime::Current()->GetInstrumentation()->requested_instrumentation_levels_.size();
|
|
}
|
|
|
|
void TestEvent(uint32_t instrumentation_event) {
|
|
TestEvent(instrumentation_event, nullptr, nullptr, false);
|
|
}
|
|
|
|
void TestEvent(uint32_t instrumentation_event,
|
|
ArtMethod* event_method,
|
|
ArtField* event_field,
|
|
bool with_object) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
|
|
TestInstrumentationListener listener;
|
|
{
|
|
ScopedThreadSuspension sts(soa.Self(), kSuspended);
|
|
ScopedSuspendAll ssa("Add instrumentation listener");
|
|
instr->AddListener(&listener, instrumentation_event);
|
|
}
|
|
|
|
mirror::Object* const event_obj = nullptr;
|
|
const uint32_t event_dex_pc = 0;
|
|
ShadowFrameAllocaUniquePtr test_frame = CREATE_SHADOW_FRAME(0, nullptr, event_method, 0);
|
|
|
|
// Check the listener is registered and is notified of the event.
|
|
EXPECT_TRUE(HasEventListener(instr, instrumentation_event));
|
|
EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event, with_object));
|
|
ReportEvent(instr,
|
|
instrumentation_event,
|
|
soa.Self(),
|
|
event_method,
|
|
event_obj,
|
|
event_field,
|
|
event_dex_pc,
|
|
*test_frame);
|
|
EXPECT_TRUE(DidListenerReceiveEvent(listener, instrumentation_event, with_object));
|
|
|
|
listener.Reset();
|
|
{
|
|
ScopedThreadSuspension sts(soa.Self(), kSuspended);
|
|
ScopedSuspendAll ssa("Remove instrumentation listener");
|
|
instr->RemoveListener(&listener, instrumentation_event);
|
|
}
|
|
|
|
// Check the listener is not registered and is not notified of the event.
|
|
EXPECT_FALSE(HasEventListener(instr, instrumentation_event));
|
|
EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event, with_object));
|
|
ReportEvent(instr,
|
|
instrumentation_event,
|
|
soa.Self(),
|
|
event_method,
|
|
event_obj,
|
|
event_field,
|
|
event_dex_pc,
|
|
*test_frame);
|
|
EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event, with_object));
|
|
}
|
|
|
|
void DeoptimizeMethod(Thread* self, ArtMethod* method, bool enable_deoptimization)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
Runtime* runtime = Runtime::Current();
|
|
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
|
|
ScopedThreadSuspension sts(self, kSuspended);
|
|
gc::ScopedGCCriticalSection gcs(self,
|
|
gc::kGcCauseInstrumentation,
|
|
gc::kCollectorTypeInstrumentation);
|
|
ScopedSuspendAll ssa("Single method deoptimization");
|
|
if (enable_deoptimization) {
|
|
instrumentation->EnableDeoptimization();
|
|
}
|
|
instrumentation->Deoptimize(method);
|
|
}
|
|
|
|
void UndeoptimizeMethod(Thread* self, ArtMethod* method,
|
|
const char* key, bool disable_deoptimization)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
Runtime* runtime = Runtime::Current();
|
|
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
|
|
ScopedThreadSuspension sts(self, kSuspended);
|
|
gc::ScopedGCCriticalSection gcs(self,
|
|
gc::kGcCauseInstrumentation,
|
|
gc::kCollectorTypeInstrumentation);
|
|
ScopedSuspendAll ssa("Single method undeoptimization");
|
|
instrumentation->Undeoptimize(method);
|
|
if (disable_deoptimization) {
|
|
instrumentation->DisableDeoptimization(key);
|
|
}
|
|
}
|
|
|
|
void DeoptimizeEverything(Thread* self, const char* key, bool enable_deoptimization)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
Runtime* runtime = Runtime::Current();
|
|
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
|
|
ScopedThreadSuspension sts(self, kSuspended);
|
|
gc::ScopedGCCriticalSection gcs(self,
|
|
gc::kGcCauseInstrumentation,
|
|
gc::kCollectorTypeInstrumentation);
|
|
ScopedSuspendAll ssa("Full deoptimization");
|
|
if (enable_deoptimization) {
|
|
instrumentation->EnableDeoptimization();
|
|
}
|
|
instrumentation->DeoptimizeEverything(key);
|
|
}
|
|
|
|
void UndeoptimizeEverything(Thread* self, const char* key, bool disable_deoptimization)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
Runtime* runtime = Runtime::Current();
|
|
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
|
|
ScopedThreadSuspension sts(self, kSuspended);
|
|
gc::ScopedGCCriticalSection gcs(self,
|
|
gc::kGcCauseInstrumentation,
|
|
gc::kCollectorTypeInstrumentation);
|
|
ScopedSuspendAll ssa("Full undeoptimization");
|
|
instrumentation->UndeoptimizeEverything(key);
|
|
if (disable_deoptimization) {
|
|
instrumentation->DisableDeoptimization(key);
|
|
}
|
|
}
|
|
|
|
void EnableMethodTracing(Thread* self, const char* key, bool needs_interpreter)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
Runtime* runtime = Runtime::Current();
|
|
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
|
|
ScopedThreadSuspension sts(self, kSuspended);
|
|
gc::ScopedGCCriticalSection gcs(self,
|
|
gc::kGcCauseInstrumentation,
|
|
gc::kCollectorTypeInstrumentation);
|
|
ScopedSuspendAll ssa("EnableMethodTracing");
|
|
instrumentation->EnableMethodTracing(key, needs_interpreter);
|
|
}
|
|
|
|
void DisableMethodTracing(Thread* self, const char* key)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
Runtime* runtime = Runtime::Current();
|
|
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
|
|
ScopedThreadSuspension sts(self, kSuspended);
|
|
gc::ScopedGCCriticalSection gcs(self,
|
|
gc::kGcCauseInstrumentation,
|
|
gc::kCollectorTypeInstrumentation);
|
|
ScopedSuspendAll ssa("EnableMethodTracing");
|
|
instrumentation->DisableMethodTracing(key);
|
|
}
|
|
|
|
private:
|
|
static bool HasEventListener(const instrumentation::Instrumentation* instr, uint32_t event_type)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
switch (event_type) {
|
|
case instrumentation::Instrumentation::kMethodEntered:
|
|
return instr->HasMethodEntryListeners();
|
|
case instrumentation::Instrumentation::kMethodExited:
|
|
return instr->HasMethodExitListeners();
|
|
case instrumentation::Instrumentation::kMethodUnwind:
|
|
return instr->HasMethodUnwindListeners();
|
|
case instrumentation::Instrumentation::kDexPcMoved:
|
|
return instr->HasDexPcListeners();
|
|
case instrumentation::Instrumentation::kFieldRead:
|
|
return instr->HasFieldReadListeners();
|
|
case instrumentation::Instrumentation::kFieldWritten:
|
|
return instr->HasFieldWriteListeners();
|
|
case instrumentation::Instrumentation::kExceptionThrown:
|
|
return instr->HasExceptionThrownListeners();
|
|
case instrumentation::Instrumentation::kExceptionHandled:
|
|
return instr->HasExceptionHandledListeners();
|
|
case instrumentation::Instrumentation::kBranch:
|
|
return instr->HasBranchListeners();
|
|
case instrumentation::Instrumentation::kWatchedFramePop:
|
|
return instr->HasWatchedFramePopListeners();
|
|
default:
|
|
LOG(FATAL) << "Unknown instrumentation event " << event_type;
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
static void ReportEvent(const instrumentation::Instrumentation* instr,
|
|
uint32_t event_type,
|
|
Thread* self,
|
|
ArtMethod* method,
|
|
mirror::Object* obj,
|
|
ArtField* field,
|
|
uint32_t dex_pc,
|
|
const ShadowFrame& frame)
|
|
REQUIRES_SHARED(Locks::mutator_lock_) {
|
|
switch (event_type) {
|
|
case instrumentation::Instrumentation::kMethodEntered:
|
|
instr->MethodEnterEvent(self, obj, method, dex_pc);
|
|
break;
|
|
case instrumentation::Instrumentation::kMethodExited: {
|
|
JValue value;
|
|
instr->MethodExitEvent(self, obj, method, dex_pc, {}, value);
|
|
break;
|
|
}
|
|
case instrumentation::Instrumentation::kMethodUnwind:
|
|
instr->MethodUnwindEvent(self, obj, method, dex_pc);
|
|
break;
|
|
case instrumentation::Instrumentation::kDexPcMoved:
|
|
instr->DexPcMovedEvent(self, obj, method, dex_pc);
|
|
break;
|
|
case instrumentation::Instrumentation::kFieldRead:
|
|
instr->FieldReadEvent(self, obj, method, dex_pc, field);
|
|
break;
|
|
case instrumentation::Instrumentation::kFieldWritten: {
|
|
JValue value;
|
|
instr->FieldWriteEvent(self, obj, method, dex_pc, field, value);
|
|
break;
|
|
}
|
|
case instrumentation::Instrumentation::kExceptionThrown: {
|
|
ThrowArithmeticExceptionDivideByZero();
|
|
mirror::Throwable* event_exception = self->GetException();
|
|
instr->ExceptionThrownEvent(self, event_exception);
|
|
self->ClearException();
|
|
break;
|
|
}
|
|
case instrumentation::Instrumentation::kBranch:
|
|
instr->Branch(self, method, dex_pc, -1);
|
|
break;
|
|
case instrumentation::Instrumentation::kWatchedFramePop:
|
|
instr->WatchedFramePopped(self, frame);
|
|
break;
|
|
case instrumentation::Instrumentation::kExceptionHandled: {
|
|
ThrowArithmeticExceptionDivideByZero();
|
|
mirror::Throwable* event_exception = self->GetException();
|
|
self->ClearException();
|
|
instr->ExceptionHandledEvent(self, event_exception);
|
|
break;
|
|
}
|
|
default:
|
|
LOG(FATAL) << "Unknown instrumentation event " << event_type;
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
static bool DidListenerReceiveEvent(const TestInstrumentationListener& listener,
|
|
uint32_t event_type,
|
|
bool with_object) {
|
|
switch (event_type) {
|
|
case instrumentation::Instrumentation::kMethodEntered:
|
|
return listener.received_method_enter_event;
|
|
case instrumentation::Instrumentation::kMethodExited:
|
|
return (!with_object && listener.received_method_exit_event) ||
|
|
(with_object && listener.received_method_exit_object_event);
|
|
case instrumentation::Instrumentation::kMethodUnwind:
|
|
return listener.received_method_unwind_event;
|
|
case instrumentation::Instrumentation::kDexPcMoved:
|
|
return listener.received_dex_pc_moved_event;
|
|
case instrumentation::Instrumentation::kFieldRead:
|
|
return listener.received_field_read_event;
|
|
case instrumentation::Instrumentation::kFieldWritten:
|
|
return (!with_object && listener.received_field_written_event) ||
|
|
(with_object && listener.received_field_written_object_event);
|
|
case instrumentation::Instrumentation::kExceptionThrown:
|
|
return listener.received_exception_thrown_event;
|
|
case instrumentation::Instrumentation::kExceptionHandled:
|
|
return listener.received_exception_handled_event;
|
|
case instrumentation::Instrumentation::kBranch:
|
|
return listener.received_branch_event;
|
|
case instrumentation::Instrumentation::kWatchedFramePop:
|
|
return listener.received_watched_frame_pop;
|
|
default:
|
|
LOG(FATAL) << "Unknown instrumentation event " << event_type;
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
};
|
|
|
|
TEST_F(InstrumentationTest, NoInstrumentation) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
|
|
ASSERT_NE(instr, nullptr);
|
|
|
|
EXPECT_FALSE(instr->AreExitStubsInstalled());
|
|
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
|
|
EXPECT_FALSE(instr->IsActive());
|
|
EXPECT_FALSE(instr->ShouldNotifyMethodEnterExitEvents());
|
|
|
|
// Test interpreter table is the default one.
|
|
EXPECT_EQ(instrumentation::kMainHandlerTable, instr->GetInterpreterHandlerTable());
|
|
|
|
// Check there is no registered listener.
|
|
EXPECT_FALSE(instr->HasDexPcListeners());
|
|
EXPECT_FALSE(instr->HasExceptionThrownListeners());
|
|
EXPECT_FALSE(instr->HasExceptionHandledListeners());
|
|
EXPECT_FALSE(instr->HasFieldReadListeners());
|
|
EXPECT_FALSE(instr->HasFieldWriteListeners());
|
|
EXPECT_FALSE(instr->HasMethodEntryListeners());
|
|
EXPECT_FALSE(instr->HasMethodExitListeners());
|
|
EXPECT_FALSE(instr->IsActive());
|
|
}
|
|
|
|
// Test instrumentation listeners for each event.
|
|
TEST_F(InstrumentationTest, MethodEntryEvent) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
jobject class_loader = LoadDex("Instrumentation");
|
|
Runtime* const runtime = Runtime::Current();
|
|
ClassLinker* class_linker = runtime->GetClassLinker();
|
|
StackHandleScope<1> hs(soa.Self());
|
|
Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
|
|
ObjPtr<mirror::Class> klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
|
|
ASSERT_TRUE(klass != nullptr);
|
|
ArtMethod* method =
|
|
klass->FindClassMethod("returnReference", "()Ljava/lang/Object;", kRuntimePointerSize);
|
|
ASSERT_TRUE(method != nullptr);
|
|
ASSERT_TRUE(method->IsDirect());
|
|
ASSERT_TRUE(method->GetDeclaringClass() == klass);
|
|
TestEvent(instrumentation::Instrumentation::kMethodEntered,
|
|
/*event_method=*/ method,
|
|
/*event_field=*/ nullptr,
|
|
/*with_object=*/ true);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, MethodExitObjectEvent) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
jobject class_loader = LoadDex("Instrumentation");
|
|
Runtime* const runtime = Runtime::Current();
|
|
ClassLinker* class_linker = runtime->GetClassLinker();
|
|
StackHandleScope<1> hs(soa.Self());
|
|
MutableHandle<mirror::ClassLoader> loader(
|
|
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
|
|
ObjPtr<mirror::Class> klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
|
|
ASSERT_TRUE(klass != nullptr);
|
|
ArtMethod* method =
|
|
klass->FindClassMethod("returnReference", "()Ljava/lang/Object;", kRuntimePointerSize);
|
|
ASSERT_TRUE(method != nullptr);
|
|
ASSERT_TRUE(method->IsDirect());
|
|
ASSERT_TRUE(method->GetDeclaringClass() == klass);
|
|
TestEvent(instrumentation::Instrumentation::kMethodExited,
|
|
/*event_method=*/ method,
|
|
/*event_field=*/ nullptr,
|
|
/*with_object=*/ true);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, MethodExitPrimEvent) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
jobject class_loader = LoadDex("Instrumentation");
|
|
Runtime* const runtime = Runtime::Current();
|
|
ClassLinker* class_linker = runtime->GetClassLinker();
|
|
StackHandleScope<1> hs(soa.Self());
|
|
Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
|
|
ObjPtr<mirror::Class> klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
|
|
ASSERT_TRUE(klass != nullptr);
|
|
ArtMethod* method = klass->FindClassMethod("returnPrimitive", "()I", kRuntimePointerSize);
|
|
ASSERT_TRUE(method != nullptr);
|
|
ASSERT_TRUE(method->IsDirect());
|
|
ASSERT_TRUE(method->GetDeclaringClass() == klass);
|
|
TestEvent(instrumentation::Instrumentation::kMethodExited,
|
|
/*event_method=*/ method,
|
|
/*event_field=*/ nullptr,
|
|
/*with_object=*/ false);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, MethodUnwindEvent) {
|
|
TestEvent(instrumentation::Instrumentation::kMethodUnwind);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, DexPcMovedEvent) {
|
|
TestEvent(instrumentation::Instrumentation::kDexPcMoved);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, FieldReadEvent) {
|
|
TestEvent(instrumentation::Instrumentation::kFieldRead);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, WatchedFramePop) {
|
|
TestEvent(instrumentation::Instrumentation::kWatchedFramePop);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, FieldWriteObjectEvent) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
jobject class_loader = LoadDex("Instrumentation");
|
|
Runtime* const runtime = Runtime::Current();
|
|
ClassLinker* class_linker = runtime->GetClassLinker();
|
|
StackHandleScope<1> hs(soa.Self());
|
|
Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
|
|
ObjPtr<mirror::Class> klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
|
|
ASSERT_TRUE(klass != nullptr);
|
|
ArtField* field = klass->FindDeclaredStaticField("referenceField", "Ljava/lang/Object;");
|
|
ASSERT_TRUE(field != nullptr);
|
|
|
|
TestEvent(instrumentation::Instrumentation::kFieldWritten,
|
|
/*event_method=*/ nullptr,
|
|
/*event_field=*/ field,
|
|
/*with_object=*/ true);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, FieldWritePrimEvent) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
jobject class_loader = LoadDex("Instrumentation");
|
|
Runtime* const runtime = Runtime::Current();
|
|
ClassLinker* class_linker = runtime->GetClassLinker();
|
|
StackHandleScope<1> hs(soa.Self());
|
|
Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
|
|
ObjPtr<mirror::Class> klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
|
|
ASSERT_TRUE(klass != nullptr);
|
|
ArtField* field = klass->FindDeclaredStaticField("primitiveField", "I");
|
|
ASSERT_TRUE(field != nullptr);
|
|
|
|
TestEvent(instrumentation::Instrumentation::kFieldWritten,
|
|
/*event_method=*/ nullptr,
|
|
/*event_field=*/ field,
|
|
/*with_object=*/ false);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, ExceptionHandledEvent) {
|
|
TestEvent(instrumentation::Instrumentation::kExceptionHandled);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, ExceptionThrownEvent) {
|
|
TestEvent(instrumentation::Instrumentation::kExceptionThrown);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, BranchEvent) {
|
|
TestEvent(instrumentation::Instrumentation::kBranch);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, DeoptimizeDirectMethod) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
jobject class_loader = LoadDex("Instrumentation");
|
|
Runtime* const runtime = Runtime::Current();
|
|
instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
|
|
ClassLinker* class_linker = runtime->GetClassLinker();
|
|
StackHandleScope<1> hs(soa.Self());
|
|
Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
|
|
ObjPtr<mirror::Class> klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
|
|
ASSERT_TRUE(klass != nullptr);
|
|
ArtMethod* method_to_deoptimize =
|
|
klass->FindClassMethod("instanceMethod", "()V", kRuntimePointerSize);
|
|
ASSERT_TRUE(method_to_deoptimize != nullptr);
|
|
ASSERT_TRUE(method_to_deoptimize->IsDirect());
|
|
ASSERT_TRUE(method_to_deoptimize->GetDeclaringClass() == klass);
|
|
|
|
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
|
|
EXPECT_FALSE(instr->IsDeoptimized(method_to_deoptimize));
|
|
|
|
DeoptimizeMethod(soa.Self(), method_to_deoptimize, true);
|
|
|
|
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
|
|
EXPECT_TRUE(instr->AreExitStubsInstalled());
|
|
EXPECT_TRUE(instr->IsDeoptimized(method_to_deoptimize));
|
|
|
|
constexpr const char* instrumentation_key = "DeoptimizeDirectMethod";
|
|
UndeoptimizeMethod(soa.Self(), method_to_deoptimize, instrumentation_key, true);
|
|
|
|
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
|
|
EXPECT_FALSE(instr->IsDeoptimized(method_to_deoptimize));
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, FullDeoptimization) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
Runtime* const runtime = Runtime::Current();
|
|
instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
|
|
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
|
|
|
|
constexpr const char* instrumentation_key = "FullDeoptimization";
|
|
DeoptimizeEverything(soa.Self(), instrumentation_key, true);
|
|
|
|
EXPECT_TRUE(instr->AreAllMethodsDeoptimized());
|
|
EXPECT_TRUE(instr->AreExitStubsInstalled());
|
|
|
|
UndeoptimizeEverything(soa.Self(), instrumentation_key, true);
|
|
|
|
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, MixedDeoptimization) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
jobject class_loader = LoadDex("Instrumentation");
|
|
Runtime* const runtime = Runtime::Current();
|
|
instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
|
|
ClassLinker* class_linker = runtime->GetClassLinker();
|
|
StackHandleScope<1> hs(soa.Self());
|
|
Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
|
|
ObjPtr<mirror::Class> klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
|
|
ASSERT_TRUE(klass != nullptr);
|
|
ArtMethod* method_to_deoptimize =
|
|
klass->FindClassMethod("instanceMethod", "()V", kRuntimePointerSize);
|
|
ASSERT_TRUE(method_to_deoptimize != nullptr);
|
|
ASSERT_TRUE(method_to_deoptimize->IsDirect());
|
|
ASSERT_TRUE(method_to_deoptimize->GetDeclaringClass() == klass);
|
|
|
|
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
|
|
EXPECT_FALSE(instr->IsDeoptimized(method_to_deoptimize));
|
|
|
|
DeoptimizeMethod(soa.Self(), method_to_deoptimize, true);
|
|
// Deoptimizing a method does not change instrumentation level.
|
|
EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing,
|
|
GetCurrentInstrumentationLevel());
|
|
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
|
|
EXPECT_TRUE(instr->AreExitStubsInstalled());
|
|
EXPECT_TRUE(instr->IsDeoptimized(method_to_deoptimize));
|
|
|
|
constexpr const char* instrumentation_key = "MixedDeoptimization";
|
|
DeoptimizeEverything(soa.Self(), instrumentation_key, false);
|
|
EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter,
|
|
GetCurrentInstrumentationLevel());
|
|
EXPECT_TRUE(instr->AreAllMethodsDeoptimized());
|
|
EXPECT_TRUE(instr->AreExitStubsInstalled());
|
|
EXPECT_TRUE(instr->IsDeoptimized(method_to_deoptimize));
|
|
|
|
UndeoptimizeEverything(soa.Self(), instrumentation_key, false);
|
|
EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing,
|
|
GetCurrentInstrumentationLevel());
|
|
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
|
|
EXPECT_TRUE(instr->AreExitStubsInstalled());
|
|
EXPECT_TRUE(instr->IsDeoptimized(method_to_deoptimize));
|
|
|
|
UndeoptimizeMethod(soa.Self(), method_to_deoptimize, instrumentation_key, true);
|
|
EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing,
|
|
GetCurrentInstrumentationLevel());
|
|
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
|
|
EXPECT_FALSE(instr->IsDeoptimized(method_to_deoptimize));
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, MethodTracing_Interpreter) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
Runtime* const runtime = Runtime::Current();
|
|
instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
|
|
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
|
|
|
|
constexpr const char* instrumentation_key = "MethodTracing";
|
|
EnableMethodTracing(soa.Self(), instrumentation_key, true);
|
|
EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter,
|
|
GetCurrentInstrumentationLevel());
|
|
EXPECT_TRUE(instr->AreAllMethodsDeoptimized());
|
|
EXPECT_TRUE(instr->AreExitStubsInstalled());
|
|
|
|
DisableMethodTracing(soa.Self(), instrumentation_key);
|
|
EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing,
|
|
GetCurrentInstrumentationLevel());
|
|
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, MethodTracing_InstrumentationEntryExitStubs) {
|
|
ScopedObjectAccess soa(Thread::Current());
|
|
Runtime* const runtime = Runtime::Current();
|
|
instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
|
|
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
|
|
|
|
constexpr const char* instrumentation_key = "MethodTracing";
|
|
EnableMethodTracing(soa.Self(), instrumentation_key, false);
|
|
EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
|
|
GetCurrentInstrumentationLevel());
|
|
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
|
|
EXPECT_TRUE(instr->AreExitStubsInstalled());
|
|
|
|
DisableMethodTracing(soa.Self(), instrumentation_key);
|
|
EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing,
|
|
GetCurrentInstrumentationLevel());
|
|
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
|
|
}
|
|
|
|
// We use a macro to print the line number where the test is failing.
|
|
#define CHECK_INSTRUMENTATION(_level, _user_count) \
|
|
do { \
|
|
Instrumentation* const instr = Runtime::Current()->GetInstrumentation(); \
|
|
bool interpreter = \
|
|
((_level) == Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter); \
|
|
EXPECT_EQ(_level, GetCurrentInstrumentationLevel()); \
|
|
EXPECT_EQ(_user_count, GetInstrumentationUserCount()); \
|
|
if (instr->IsForcedInterpretOnly()) { \
|
|
EXPECT_TRUE(instr->InterpretOnly()); \
|
|
} else if (interpreter) { \
|
|
EXPECT_TRUE(instr->InterpretOnly()); \
|
|
} else { \
|
|
EXPECT_FALSE(instr->InterpretOnly()); \
|
|
} \
|
|
if (interpreter) { \
|
|
EXPECT_TRUE(instr->AreAllMethodsDeoptimized()); \
|
|
} else { \
|
|
EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); \
|
|
} \
|
|
} while (false)
|
|
|
|
TEST_F(InstrumentationTest, ConfigureStubs_Nothing) {
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
|
|
// Check no-op.
|
|
CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, ConfigureStubs_InstrumentationStubs) {
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
|
|
// Check we can switch to instrumentation stubs
|
|
CheckConfigureStubs(kClientOneKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
|
|
1U);
|
|
|
|
// Check we can disable instrumentation.
|
|
CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, ConfigureStubs_Interpreter) {
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
|
|
// Check we can switch to interpreter
|
|
CheckConfigureStubs(kClientOneKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U);
|
|
|
|
// Check we can disable instrumentation.
|
|
CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, ConfigureStubs_InstrumentationStubsToInterpreter) {
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
|
|
// Configure stubs with instrumentation stubs.
|
|
CheckConfigureStubs(kClientOneKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
|
|
1U);
|
|
|
|
// Configure stubs with interpreter.
|
|
CheckConfigureStubs(kClientOneKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U);
|
|
|
|
// Check we can disable instrumentation.
|
|
CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, ConfigureStubs_InterpreterToInstrumentationStubs) {
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
|
|
// Configure stubs with interpreter.
|
|
CheckConfigureStubs(kClientOneKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U);
|
|
|
|
// Configure stubs with instrumentation stubs.
|
|
CheckConfigureStubs(kClientOneKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
|
|
// Make sure we are still interpreter since going from interpreter->instrumentation is dangerous.
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter,
|
|
1U);
|
|
|
|
// Check we can disable instrumentation.
|
|
CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest,
|
|
ConfigureStubs_InstrumentationStubsToInterpreterToInstrumentationStubs) {
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
|
|
// Configure stubs with instrumentation stubs.
|
|
CheckConfigureStubs(kClientOneKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
|
|
1U);
|
|
|
|
// Configure stubs with interpreter.
|
|
CheckConfigureStubs(kClientOneKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U);
|
|
|
|
// Configure stubs with instrumentation stubs again.
|
|
CheckConfigureStubs(kClientOneKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter,
|
|
1U);
|
|
|
|
// Check we can disable instrumentation.
|
|
CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, MultiConfigureStubs_Nothing) {
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
|
|
// Check kInstrumentNothing with two clients.
|
|
CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
|
|
CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, MultiConfigureStubs_InstrumentationStubs) {
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
|
|
// Configure stubs with instrumentation stubs for 1st client.
|
|
CheckConfigureStubs(kClientOneKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
|
|
1U);
|
|
|
|
// Configure stubs with instrumentation stubs for 2nd client.
|
|
CheckConfigureStubs(kClientTwoKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
|
|
2U);
|
|
|
|
// 1st client requests instrumentation deactivation but 2nd client still needs
|
|
// instrumentation stubs.
|
|
CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
|
|
1U);
|
|
|
|
// 2nd client requests instrumentation deactivation
|
|
CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, MultiConfigureStubs_Interpreter) {
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
|
|
// Configure stubs with interpreter for 1st client.
|
|
CheckConfigureStubs(kClientOneKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U);
|
|
|
|
// Configure stubs with interpreter for 2nd client.
|
|
CheckConfigureStubs(kClientTwoKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 2U);
|
|
|
|
// 1st client requests instrumentation deactivation but 2nd client still needs interpreter.
|
|
CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U);
|
|
|
|
// 2nd client requests instrumentation deactivation
|
|
CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, MultiConfigureStubs_InstrumentationStubsThenInterpreter) {
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
|
|
// Configure stubs with instrumentation stubs for 1st client.
|
|
CheckConfigureStubs(kClientOneKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs,
|
|
1U);
|
|
|
|
// Configure stubs with interpreter for 2nd client.
|
|
CheckConfigureStubs(kClientTwoKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 2U);
|
|
|
|
// 1st client requests instrumentation deactivation but 2nd client still needs interpreter.
|
|
CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U);
|
|
|
|
// 2nd client requests instrumentation deactivation
|
|
CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
}
|
|
|
|
TEST_F(InstrumentationTest, MultiConfigureStubs_InterpreterThenInstrumentationStubs) {
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
|
|
// Configure stubs with interpreter for 1st client.
|
|
CheckConfigureStubs(kClientOneKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U);
|
|
|
|
// Configure stubs with instrumentation stubs for 2nd client.
|
|
CheckConfigureStubs(kClientTwoKey,
|
|
Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 2U);
|
|
|
|
// 1st client requests instrumentation deactivation but 2nd client still needs
|
|
// instrumentation stubs. Since we already got interpreter stubs we need to stay there.
|
|
CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter,
|
|
1U);
|
|
|
|
// 2nd client requests instrumentation deactivation
|
|
CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing);
|
|
CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U);
|
|
}
|
|
|
|
} // namespace instrumentation
|
|
} // namespace art
|