Support using adbconnection and openjdkjvmti without JAVA_DEBUGGABLE
We need to support using a best-effort JDWP when we have the
ENABLE_JDWP attribute without the JAVA_DEBUGGABLE attribute. This is
used on eng and userdebug builds. We do this by making the plugin try
to change the runtime to debuggable if possible and to allow getting
an ArtTiEnv which is a best-effort version of JVMTI by calling GetEnv
with (JVMTI_VERSION_1_2 | 0x4000000). This is needed since if the
runtime isn't debuggable we cannot guarantee compatibility with the
JVMTI specification in all cases due to compiler optimizations such as
inlining. By creating this special version agents are able to
positively signal that they are able to deal with this uncertainty.
We also support using openjdkjvmti without being JAVA_DEBUGGABLE. This
is done by either only allowing the best effort ArtTiEnvs or by
changing the environment to be debuggable if we are loaded early
enough.
Moving the runtime to debuggable state involves deoptimizing the boot
image and throwing out any image files associated with non-debuggable
oat-files.
Bug: 62821960
Test: ./test.py --host -j50
Test: Manual
Test: Build, Test debugging system_server and other processes.
Change-Id: I2233299fceb83c76785e5de09e51eaf18b7922e8
diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc
index a5c885a..80cfc83 100644
--- a/adbconnection/adbconnection.cc
+++ b/adbconnection/adbconnection.cc
@@ -63,9 +63,7 @@
static AdbConnectionState* gState;
static bool IsDebuggingPossible() {
- // TODO We need to do this on IsJdwpAllowed not IsDebuggable in order to support userdebug
- // workloads. For now we will only allow it when we are debuggable so that testing is easier.
- return art::Runtime::Current()->IsJavaDebuggable() && art::Dbg::IsJdwpAllowed();
+ return art::Dbg::IsJdwpAllowed();
}
// Begin running the debugger.
@@ -581,11 +579,14 @@
DCHECK(!agent_has_socket_);
if (!agent_loaded_) {
DCHECK(!agent_listening_);
+ // TODO Should we check in some other way if we are userdebug/eng?
+ CHECK(art::Dbg::IsJdwpAllowed());
// Load the agent now!
self->AssertNoPendingException();
art::Runtime::Current()->AttachAgent(/* JNIEnv* */ nullptr,
MakeAgentArg(),
- /* classloader */ nullptr);
+ /* classloader */ nullptr,
+ /*allow_non_debuggable_tooling*/ true);
if (self->IsExceptionPending()) {
LOG(ERROR) << "Failed to load agent " << agent_name_;
art::ScopedObjectAccess soa(self);
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index aae8055..027635b 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -83,6 +83,12 @@
} \
} while (false)
+// Returns whether we are able to use all jvmti features.
+static bool IsFullJvmtiAvailable() {
+ art::Runtime* runtime = art::Runtime::Current();
+ return runtime->GetInstrumentation()->IsForcedInterpretOnly() || runtime->IsJavaDebuggable();
+}
+
class JvmtiFunctions {
private:
static jvmtiError getEnvironmentError(jvmtiEnv* env) {
@@ -1092,10 +1098,64 @@
&gEventHandler);
}
+#define FOR_ALL_CAPABILITIES(FUN) \
+ FUN(can_tag_objects) \
+ FUN(can_generate_field_modification_events) \
+ FUN(can_generate_field_access_events) \
+ FUN(can_get_bytecodes) \
+ FUN(can_get_synthetic_attribute) \
+ FUN(can_get_owned_monitor_info) \
+ FUN(can_get_current_contended_monitor) \
+ FUN(can_get_monitor_info) \
+ FUN(can_pop_frame) \
+ FUN(can_redefine_classes) \
+ FUN(can_signal_thread) \
+ FUN(can_get_source_file_name) \
+ FUN(can_get_line_numbers) \
+ FUN(can_get_source_debug_extension) \
+ FUN(can_access_local_variables) \
+ FUN(can_maintain_original_method_order) \
+ FUN(can_generate_single_step_events) \
+ FUN(can_generate_exception_events) \
+ FUN(can_generate_frame_pop_events) \
+ FUN(can_generate_breakpoint_events) \
+ FUN(can_suspend) \
+ FUN(can_redefine_any_class) \
+ FUN(can_get_current_thread_cpu_time) \
+ FUN(can_get_thread_cpu_time) \
+ FUN(can_generate_method_entry_events) \
+ FUN(can_generate_method_exit_events) \
+ FUN(can_generate_all_class_hook_events) \
+ FUN(can_generate_compiled_method_load_events) \
+ FUN(can_generate_monitor_events) \
+ FUN(can_generate_vm_object_alloc_events) \
+ FUN(can_generate_native_method_bind_events) \
+ FUN(can_generate_garbage_collection_events) \
+ FUN(can_generate_object_free_events) \
+ FUN(can_force_early_return) \
+ FUN(can_get_owned_monitor_stack_depth_info) \
+ FUN(can_get_constant_pool) \
+ FUN(can_set_native_method_prefix) \
+ FUN(can_retransform_classes) \
+ FUN(can_retransform_any_class) \
+ FUN(can_generate_resource_exhaustion_heap_events) \
+ FUN(can_generate_resource_exhaustion_threads_events)
+
static jvmtiError GetPotentialCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_NON_NULL(capabilities_ptr);
*capabilities_ptr = kPotentialCapabilities;
+ if (UNLIKELY(!IsFullJvmtiAvailable())) {
+#define REMOVE_NONDEBUGGABLE_UNSUPPORTED(e) \
+ do { \
+ if (kNonDebuggableUnsupportedCapabilities.e == 1) { \
+ capabilities_ptr->e = 0; \
+ } \
+ } while (false);
+
+ FOR_ALL_CAPABILITIES(REMOVE_NONDEBUGGABLE_UNSUPPORTED);
+#undef REMOVE_NONDEBUGGABLE_UNSUPPORTED
+ }
return OK;
}
@@ -1122,49 +1182,9 @@
ret = ERR(NOT_AVAILABLE); \
} \
} \
- } while (false)
+ } while (false);
- ADD_CAPABILITY(can_tag_objects);
- ADD_CAPABILITY(can_generate_field_modification_events);
- ADD_CAPABILITY(can_generate_field_access_events);
- ADD_CAPABILITY(can_get_bytecodes);
- ADD_CAPABILITY(can_get_synthetic_attribute);
- ADD_CAPABILITY(can_get_owned_monitor_info);
- ADD_CAPABILITY(can_get_current_contended_monitor);
- ADD_CAPABILITY(can_get_monitor_info);
- ADD_CAPABILITY(can_pop_frame);
- ADD_CAPABILITY(can_redefine_classes);
- ADD_CAPABILITY(can_signal_thread);
- ADD_CAPABILITY(can_get_source_file_name);
- ADD_CAPABILITY(can_get_line_numbers);
- ADD_CAPABILITY(can_get_source_debug_extension);
- ADD_CAPABILITY(can_access_local_variables);
- ADD_CAPABILITY(can_maintain_original_method_order);
- ADD_CAPABILITY(can_generate_single_step_events);
- ADD_CAPABILITY(can_generate_exception_events);
- ADD_CAPABILITY(can_generate_frame_pop_events);
- ADD_CAPABILITY(can_generate_breakpoint_events);
- ADD_CAPABILITY(can_suspend);
- ADD_CAPABILITY(can_redefine_any_class);
- ADD_CAPABILITY(can_get_current_thread_cpu_time);
- ADD_CAPABILITY(can_get_thread_cpu_time);
- ADD_CAPABILITY(can_generate_method_entry_events);
- ADD_CAPABILITY(can_generate_method_exit_events);
- ADD_CAPABILITY(can_generate_all_class_hook_events);
- ADD_CAPABILITY(can_generate_compiled_method_load_events);
- ADD_CAPABILITY(can_generate_monitor_events);
- ADD_CAPABILITY(can_generate_vm_object_alloc_events);
- ADD_CAPABILITY(can_generate_native_method_bind_events);
- ADD_CAPABILITY(can_generate_garbage_collection_events);
- ADD_CAPABILITY(can_generate_object_free_events);
- ADD_CAPABILITY(can_force_early_return);
- ADD_CAPABILITY(can_get_owned_monitor_stack_depth_info);
- ADD_CAPABILITY(can_get_constant_pool);
- ADD_CAPABILITY(can_set_native_method_prefix);
- ADD_CAPABILITY(can_retransform_classes);
- ADD_CAPABILITY(can_retransform_any_class);
- ADD_CAPABILITY(can_generate_resource_exhaustion_heap_events);
- ADD_CAPABILITY(can_generate_resource_exhaustion_threads_events);
+ FOR_ALL_CAPABILITIES(ADD_CAPABILITY);
#undef ADD_CAPABILITY
gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env),
changed,
@@ -1186,49 +1206,9 @@
changed.e = 1; \
} \
} \
- } while (false)
+ } while (false);
- DEL_CAPABILITY(can_tag_objects);
- DEL_CAPABILITY(can_generate_field_modification_events);
- DEL_CAPABILITY(can_generate_field_access_events);
- DEL_CAPABILITY(can_get_bytecodes);
- DEL_CAPABILITY(can_get_synthetic_attribute);
- DEL_CAPABILITY(can_get_owned_monitor_info);
- DEL_CAPABILITY(can_get_current_contended_monitor);
- DEL_CAPABILITY(can_get_monitor_info);
- DEL_CAPABILITY(can_pop_frame);
- DEL_CAPABILITY(can_redefine_classes);
- DEL_CAPABILITY(can_signal_thread);
- DEL_CAPABILITY(can_get_source_file_name);
- DEL_CAPABILITY(can_get_line_numbers);
- DEL_CAPABILITY(can_get_source_debug_extension);
- DEL_CAPABILITY(can_access_local_variables);
- DEL_CAPABILITY(can_maintain_original_method_order);
- DEL_CAPABILITY(can_generate_single_step_events);
- DEL_CAPABILITY(can_generate_exception_events);
- DEL_CAPABILITY(can_generate_frame_pop_events);
- DEL_CAPABILITY(can_generate_breakpoint_events);
- DEL_CAPABILITY(can_suspend);
- DEL_CAPABILITY(can_redefine_any_class);
- DEL_CAPABILITY(can_get_current_thread_cpu_time);
- DEL_CAPABILITY(can_get_thread_cpu_time);
- DEL_CAPABILITY(can_generate_method_entry_events);
- DEL_CAPABILITY(can_generate_method_exit_events);
- DEL_CAPABILITY(can_generate_all_class_hook_events);
- DEL_CAPABILITY(can_generate_compiled_method_load_events);
- DEL_CAPABILITY(can_generate_monitor_events);
- DEL_CAPABILITY(can_generate_vm_object_alloc_events);
- DEL_CAPABILITY(can_generate_native_method_bind_events);
- DEL_CAPABILITY(can_generate_garbage_collection_events);
- DEL_CAPABILITY(can_generate_object_free_events);
- DEL_CAPABILITY(can_force_early_return);
- DEL_CAPABILITY(can_get_owned_monitor_stack_depth_info);
- DEL_CAPABILITY(can_get_constant_pool);
- DEL_CAPABILITY(can_set_native_method_prefix);
- DEL_CAPABILITY(can_retransform_classes);
- DEL_CAPABILITY(can_retransform_any_class);
- DEL_CAPABILITY(can_generate_resource_exhaustion_heap_events);
- DEL_CAPABILITY(can_generate_resource_exhaustion_threads_events);
+ FOR_ALL_CAPABILITIES(DEL_CAPABILITY);
#undef DEL_CAPABILITY
gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env),
changed,
@@ -1236,6 +1216,8 @@
return OK;
}
+#undef FOR_ALL_CAPABILITIES
+
static jvmtiError GetCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_NON_NULL(capabilities_ptr);
@@ -1341,7 +1323,7 @@
static jvmtiError GetVersionNumber(jvmtiEnv* env, jint* version_ptr) {
ENSURE_VALID_ENV(env);
- *version_ptr = JVMTI_VERSION;
+ *version_ptr = ArtJvmTiEnv::AsArtJvmTiEnv(env)->ti_version;
return OK;
}
@@ -1495,9 +1477,10 @@
extern const jvmtiInterface_1 gJvmtiInterface;
-ArtJvmTiEnv::ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler)
+ArtJvmTiEnv::ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler, jint version)
: art_vm(runtime),
local_data(nullptr),
+ ti_version(version),
capabilities(),
event_info_mutex_("jvmtiEnv_EventInfoMutex") {
object_tag_table = std::unique_ptr<ObjectTagTable>(new ObjectTagTable(event_handler, this));
@@ -1506,8 +1489,8 @@
// Creates a jvmtiEnv and returns it with the art::ti::Env that is associated with it. new_art_ti
// is a pointer to the uninitialized memory for an art::ti::Env.
-static void CreateArtJvmTiEnv(art::JavaVMExt* vm, /*out*/void** new_jvmtiEnv) {
- struct ArtJvmTiEnv* env = new ArtJvmTiEnv(vm, &gEventHandler);
+static void CreateArtJvmTiEnv(art::JavaVMExt* vm, jint version, /*out*/void** new_jvmtiEnv) {
+ struct ArtJvmTiEnv* env = new ArtJvmTiEnv(vm, &gEventHandler, version);
*new_jvmtiEnv = env;
gEventHandler.RegisterArtJvmTiEnv(env);
@@ -1520,8 +1503,14 @@
// places the return value in 'env' if this library can handle the GetEnv request. Otherwise
// returns false and does not modify the 'env' pointer.
static jint GetEnvHandler(art::JavaVMExt* vm, /*out*/void** env, jint version) {
- if (IsJvmtiVersion(version)) {
- CreateArtJvmTiEnv(vm, env);
+ // JavaDebuggable will either be set by the runtime as it is starting up or the plugin if it's
+ // loaded early enough. If this is false we cannot guarantee conformance to all JVMTI behaviors
+ // due to optimizations. We will only allow agents to get ArtTiEnvs using the kArtTiVersion.
+ if (IsFullJvmtiAvailable() && IsJvmtiVersion(version)) {
+ CreateArtJvmTiEnv(vm, JVMTI_VERSION, env);
+ return JNI_OK;
+ } else if (version == kArtTiVersion) {
+ CreateArtJvmTiEnv(vm, kArtTiVersion, env);
return JNI_OK;
} else {
printf("version 0x%x is not valid!", version);
@@ -1547,6 +1536,12 @@
SearchUtil::Register();
HeapUtil::Register();
+ {
+ // Make sure we can deopt anything we need to.
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ gDeoptManager.FinishSetup();
+ }
+
runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler);
return true;
diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
index 2a8c2e9..73cc601 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -62,10 +62,22 @@
class ObjectTagTable;
+// A special version that we use to identify special tooling interface versions which mostly matches
+// the jvmti spec but everything is best effort. This is used to implement the userdebug
+// 'debug-anything' behavior.
+//
+// This is the value 0x70010200.
+static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000;
+
// A structure that is a jvmtiEnv with additional information for the runtime.
struct ArtJvmTiEnv : public jvmtiEnv {
art::JavaVMExt* art_vm;
void* local_data;
+
+ // The ti_version we are compatible with. This is only for giving the correct value for GetVersion
+ // when running on a userdebug/eng device.
+ jint ti_version;
+
jvmtiCapabilities capabilities;
EventMasks event_masks;
@@ -90,7 +102,7 @@
// RW lock to protect access to all of the event data.
art::ReaderWriterMutex event_info_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler);
+ ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler, jint ti_version);
static ArtJvmTiEnv* AsArtJvmTiEnv(jvmtiEnv* env) {
return art::down_cast<ArtJvmTiEnv*>(env);
@@ -272,6 +284,60 @@
.can_generate_resource_exhaustion_threads_events = 0,
};
+// These are capabilities that are disabled if we were loaded without being debuggable.
+//
+// This includes the following capabilities:
+// can_retransform_any_class:
+// can_retransform_classes:
+// can_redefine_any_class:
+// can_redefine_classes:
+// We need to ensure that inlined code is either not present or can always be deoptimized. This
+// is not guaranteed for non-debuggable processes since we might have inlined bootclasspath code
+// on a threads stack.
+const jvmtiCapabilities kNonDebuggableUnsupportedCapabilities = {
+ .can_tag_objects = 0,
+ .can_generate_field_modification_events = 0,
+ .can_generate_field_access_events = 0,
+ .can_get_bytecodes = 0,
+ .can_get_synthetic_attribute = 0,
+ .can_get_owned_monitor_info = 0,
+ .can_get_current_contended_monitor = 0,
+ .can_get_monitor_info = 0,
+ .can_pop_frame = 0,
+ .can_redefine_classes = 1,
+ .can_signal_thread = 0,
+ .can_get_source_file_name = 0,
+ .can_get_line_numbers = 0,
+ .can_get_source_debug_extension = 0,
+ .can_access_local_variables = 0,
+ .can_maintain_original_method_order = 0,
+ .can_generate_single_step_events = 0,
+ .can_generate_exception_events = 0,
+ .can_generate_frame_pop_events = 0,
+ .can_generate_breakpoint_events = 0,
+ .can_suspend = 0,
+ .can_redefine_any_class = 1,
+ .can_get_current_thread_cpu_time = 0,
+ .can_get_thread_cpu_time = 0,
+ .can_generate_method_entry_events = 0,
+ .can_generate_method_exit_events = 0,
+ .can_generate_all_class_hook_events = 0,
+ .can_generate_compiled_method_load_events = 0,
+ .can_generate_monitor_events = 0,
+ .can_generate_vm_object_alloc_events = 0,
+ .can_generate_native_method_bind_events = 0,
+ .can_generate_garbage_collection_events = 0,
+ .can_generate_object_free_events = 0,
+ .can_force_early_return = 0,
+ .can_get_owned_monitor_stack_depth_info = 0,
+ .can_get_constant_pool = 0,
+ .can_set_native_method_prefix = 0,
+ .can_retransform_classes = 1,
+ .can_retransform_any_class = 1,
+ .can_generate_resource_exhaustion_heap_events = 0,
+ .can_generate_resource_exhaustion_threads_events = 0,
+};
+
} // namespace openjdkjvmti
#endif // ART_OPENJDKJVMTI_ART_JVMTI_H_
diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc
index aced769..53d8483 100644
--- a/openjdkjvmti/deopt_manager.cc
+++ b/openjdkjvmti/deopt_manager.cc
@@ -68,7 +68,9 @@
}
DeoptManager::DeoptManager()
- : deoptimization_status_lock_("JVMTI_DeoptimizationStatusLock"),
+ : deoptimization_status_lock_("JVMTI_DeoptimizationStatusLock",
+ static_cast<art::LockLevel>(
+ art::LockLevel::kClassLinkerClassesLock + 1)),
deoptimization_condition_("JVMTI_DeoptimizationCondition", deoptimization_status_lock_),
performing_deoptimization_(false),
global_deopt_count_(0),
@@ -91,6 +93,33 @@
callbacks->RemoveMethodInspectionCallback(&inspection_callback_);
}
+void DeoptManager::FinishSetup() {
+ art::Thread* self = art::Thread::Current();
+ art::MutexLock mu(self, deoptimization_status_lock_);
+
+ art::Runtime* runtime = art::Runtime::Current();
+ // See if we need to do anything.
+ if (!runtime->IsJavaDebuggable()) {
+ // See if we can enable all JVMTI functions. If this is false, only kArtTiVersion agents can be
+ // retrieved and they will all be best-effort.
+ if (PhaseUtil::GetPhaseUnchecked() == JVMTI_PHASE_ONLOAD) {
+ // We are still early enough to change the compiler options and get full JVMTI support.
+ LOG(INFO) << "Openjdkjvmti plugin loaded on a non-debuggable runtime. Changing runtime to "
+ << "debuggable state. Please pass '--debuggable' to dex2oat and "
+ << "'-Xcompiler-option --debuggable' to dalvikvm in the future.";
+ DCHECK(runtime->GetJit() == nullptr) << "Jit should not be running yet!";
+ runtime->AddCompilerOption("--debuggable");
+ runtime->SetJavaDebuggable(true);
+ } else {
+ LOG(WARNING) << "Openjdkjvmti plugin was loaded on a non-debuggable Runtime. Plugin was "
+ << "loaded too late to change runtime state to DEBUGGABLE. Only kArtTiVersion "
+ << "(0x" << std::hex << kArtTiVersion << ") environments are available. Some "
+ << "functionality might not work properly.";
+ }
+ runtime->DeoptimizeBootImage();
+ }
+}
+
bool DeoptManager::MethodHasBreakpoints(art::ArtMethod* method) {
art::MutexLock lk(art::Thread::Current(), deoptimization_status_lock_);
return MethodHasBreakpointsLocked(method);
diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h
index b265fa8..a495b68 100644
--- a/openjdkjvmti/deopt_manager.h
+++ b/openjdkjvmti/deopt_manager.h
@@ -101,6 +101,10 @@
void DeoptimizeThread(art::Thread* target) REQUIRES_SHARED(art::Locks::mutator_lock_);
void DeoptimizeAllThreads() REQUIRES_SHARED(art::Locks::mutator_lock_);
+ void FinishSetup()
+ REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
static DeoptManager* Get();
private:
@@ -141,9 +145,8 @@
REQUIRES(!art::Roles::uninterruptible_, !art::Locks::mutator_lock_);
static constexpr const char* kDeoptManagerInstrumentationKey = "JVMTI_DeoptManager";
- // static constexpr const char* kDeoptManagerThreadName = "JVMTI_DeoptManagerWorkerThread";
- art::Mutex deoptimization_status_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ art::Mutex deoptimization_status_lock_ ACQUIRED_BEFORE(art::Locks::classlinker_classes_lock_);
art::ConditionVariable deoptimization_condition_ GUARDED_BY(deoptimization_status_lock_);
bool performing_deoptimization_ GUARDED_BY(deoptimization_status_lock_);
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 29b9bfc..577f8be 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -40,6 +40,7 @@
#include "jni_internal.h"
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
+#include "oat_file.h"
#include "oat_file_assistant.h"
#include "obj_ptr-inl.h"
#include "scoped_thread_state_change-inl.h"
@@ -527,8 +528,14 @@
if (source_oat_file != nullptr) {
bool added_image_space = false;
if (source_oat_file->IsExecutable()) {
- std::unique_ptr<gc::space::ImageSpace> image_space =
- kEnableAppImage ? oat_file_assistant.OpenImageSpace(source_oat_file) : nullptr;
+ // We need to throw away the image space if we are debuggable but the oat-file source of the
+ // image is not otherwise we might get classes with inlined methods or other such things.
+ std::unique_ptr<gc::space::ImageSpace> image_space;
+ if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) {
+ image_space = oat_file_assistant.OpenImageSpace(source_oat_file);
+ } else {
+ image_space = nullptr;
+ }
if (image_space != nullptr) {
ScopedObjectAccess soa(self);
StackHandleScope<1> hs(self);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 38c2bfd..1bddb4e 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1551,6 +1551,7 @@
}
static bool EnsureJvmtiPlugin(Runtime* runtime,
+ bool allow_non_debuggable_tooling,
std::vector<Plugin>* plugins,
std::string* error_msg) {
constexpr const char* plugin_name = kIsDebugBuild ? "libopenjdkjvmtid.so" : "libopenjdkjvmti.so";
@@ -1562,9 +1563,9 @@
}
}
- // Is the process debuggable? Otherwise, do not attempt to load the plugin.
- // TODO Support a crimped jvmti for non-debuggable runtimes.
- if (!runtime->IsJavaDebuggable()) {
+ // Is the process debuggable? Otherwise, do not attempt to load the plugin unless we are
+ // specifically allowed.
+ if (!allow_non_debuggable_tooling && !runtime->IsJavaDebuggable()) {
*error_msg = "Process is not debuggable.";
return false;
}
@@ -1585,9 +1586,12 @@
// 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(JNIEnv* env, const std::string& agent_arg, jobject class_loader) {
+void Runtime::AttachAgent(JNIEnv* env,
+ const std::string& agent_arg,
+ jobject class_loader,
+ bool allow_non_debuggable_tooling) {
std::string error_msg;
- if (!EnsureJvmtiPlugin(this, &plugins_, &error_msg)) {
+ if (!EnsureJvmtiPlugin(this, allow_non_debuggable_tooling, &plugins_, &error_msg)) {
LOG(WARNING) << "Could not load plugin: " << error_msg;
ScopedObjectAccess soa(Thread::Current());
ThrowIOException("%s", error_msg.c_str());
diff --git a/runtime/runtime.h b/runtime/runtime.h
index c8edabc..3e055c3 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -661,7 +661,10 @@
void AddSystemWeakHolder(gc::AbstractSystemWeakHolder* holder);
void RemoveSystemWeakHolder(gc::AbstractSystemWeakHolder* holder);
- void AttachAgent(JNIEnv* env, const std::string& agent_arg, jobject class_loader);
+ void AttachAgent(JNIEnv* env,
+ const std::string& agent_arg,
+ jobject class_loader,
+ bool allow_non_debuggable_tooling = false);
const std::list<std::unique_ptr<ti::Agent>>& GetAgents() const {
return agents_;
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 132099a..631b14a 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -466,10 +466,9 @@
if [[ "$TEST_IS_NDEBUG" = "y" ]]; then
plugin=libopenjdkjvmti.so
fi
+ # We used to add flags here that made the runtime debuggable but that is not
+ # needed anymore since the plugin can do it for us now.
FLAGS="${FLAGS} -Xplugin:${plugin}"
- FLAGS="${FLAGS} -Xcompiler-option --debuggable"
- # Always make the compilation be debuggable.
- COMPILE_FLAGS="${COMPILE_FLAGS} --debuggable"
fi
fi