/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "precompiled.hpp"
#include "classfile/systemDictionary.hpp"
#include "code/nmethod.hpp"
#include "code/pcDesc.hpp"
#include "code/scopeDesc.hpp"
#include "interpreter/interpreter.hpp"
#include "jvmtifiles/jvmtiEnv.hpp"
#include "memory/resourceArea.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/objArrayOop.hpp"
#include "prims/jvmtiCodeBlobEvents.hpp"
#include "prims/jvmtiEventController.hpp"
#include "prims/jvmtiEventController.inline.hpp"
#include "prims/jvmtiExport.hpp"
#include "prims/jvmtiImpl.hpp"
#include "prims/jvmtiManageCapabilities.hpp"
#include "prims/jvmtiRawMonitor.hpp"
#include "prims/jvmtiTagMap.hpp"
#include "prims/jvmtiThreadState.inline.hpp"
#include "runtime/arguments.hpp"
#include "runtime/handles.hpp"
#include "runtime/interfaceSupport.hpp"
#include "runtime/objectMonitor.hpp"
#include "runtime/objectMonitor.inline.hpp"
#include "runtime/thread.hpp"
#include "runtime/vframe.hpp"
#include "services/attachListener.hpp"
#include "services/serviceUtil.hpp"
#ifndef SERIALGC
#include "gc_implementation/parallelScavenge/psMarkSweep.hpp"
#endif
#ifdef JVMTI_TRACE
#define EVT_TRACE(evt,out) if ((JvmtiTrace::event_trace_flags(evt) & JvmtiTrace::SHOW_EVENT_SENT) != 0) { SafeResourceMark rm; tty->print_cr out; }
#define EVT_TRIG_TRACE(evt,out) if ((JvmtiTrace::event_trace_flags(evt) & JvmtiTrace::SHOW_EVENT_TRIGGER) != 0) { SafeResourceMark rm; tty->print_cr out; }
#else
#endif
///////////////////////////////////////////////////////////////
//
// JvmtiEventTransition
//
// TO DO --
// more handle purging
// Use this for JavaThreads and state is _thread_in_vm.
private:
public:
_rm(),
};
// For JavaThreads which are not in _thread_in_vm state
// and other system threads use this.
private:
public:
if (thread->is_Java_thread()) {
if (_saved_state == _thread_in_Java) {
} else {
}
} else {
}
}
}
};
///////////////////////////////////////////////////////////////
//
// JvmtiEventMark
//
private:
bool _exception_detected;
bool _exception_caught;
#if 0
#endif
public:
#if 0
#else
// we want to use the code above - but that needs the JNIHandle changes - later...
// for now, steal JNI push local frame code
// we are before an event.
// Save current jvmti thread exception state.
} else {
_exception_detected = false;
_exception_caught = false;
}
#endif
};
~JvmtiEventMark() {
#if 0
#else
// we want to use the code above - but that needs the JNIHandle changes - later...
// for now, steal JNI pop local frame code
// Note that we set the pop_frame_link to NULL explicitly, otherwise
// the release_block call will release the blocks.
#endif
// we are continuing after an event.
// Restore the jvmti thread exception state.
if (_exception_detected) {
}
if (_exception_caught) {
}
}
}
#if 0
#else
// we want to use the code above - but that needs the JNIHandle changes - later...
// for now, use regular make_local
#endif
jclass to_jclass(klassOop klass) { return (klass == NULL ? NULL : (jclass)to_jobject(Klass::cast(klass)->java_mirror())); }
};
private:
public:
};
};
private:
public:
};
};
private:
public:
};
private:
public:
};
private:
public:
JvmtiExceptionEventMark(JavaThread *thread, methodHandle method, address location, Handle exception) :
};
private:
const char *_class_name;
public:
Handle class_loader, Handle prot_domain, KlassHandle *class_being_redefined) : JvmtiThreadEventMark(thread) {
if (class_being_redefined == NULL) {
} else {
}
};
const char *class_name() {
return _class_name;
}
return _jloader;
}
return _protection_domain;
}
return _class_being_redefined;
}
};
//////////////////////////////////////////////////////////////////////////////
//
// field access management
//
// interpreter generator needs the address of the counter
// We don't grab a lock because we don't want to
// serialize field access between all threads. This means that a
// thread on another processor can see the wrong count value and
// may either miss making a needed call into post_field_access()
// or will make an unneeded call into post_field_access(). We pay
// this price to avoid slowing down the VM when we aren't watching
// field accesses.
return (address)(&_field_access_count);
}
//
// field modification management
//
// interpreter generator needs the address of the counter
// We don't grab a lock because we don't
// want to serialize field modification between all threads. This
// means that a thread on another processor can see the wrong
// count value and may either miss making a needed call into
// post_field_modification() or will make an unneeded call into
// post_field_modification(). We pay this price to avoid slowing
// down the VM when we aren't watching field modifications.
return (address)(&_field_modification_count);
}
///////////////////////////////////////////////////////////////
// Functions needed by java.lang.instrument for starting up javaagent.
///////////////////////////////////////////////////////////////
// The JVMTI_VERSION_INTERFACE_JVMTI part of the version number
// has already been validated in JNI GetEnv().
// micro version doesn't matter here (yet?)
switch (major) {
case 1:
switch (minor) {
case 0: // version 1.0.<micro> is recognized
case 1: // version 1.1.<micro> is recognized
case 2: // version 1.2.<micro> is recognized
break;
default:
return JNI_EVERSION; // unsupported minor version number
}
break;
default:
return JNI_EVERSION; // unsupported major version number
}
// transition code: native to VM
*penv = jvmti_env->jvmti_external(); // actual type is jvmtiEnv* -- not to be confused with JvmtiEnv*
return JNI_OK;
// not live, no thread to transition
*penv = jvmti_env->jvmti_external(); // actual type is jvmtiEnv* -- not to be confused with JvmtiEnv*
return JNI_OK;
} else {
// Called at the wrong time
return JNI_EDETACHED;
}
}
void
int * micro) {
}
}
}
}
}
//
// JVMTI events that the VM posts to the debugger and also startup agent
// and call the agent's premain() for java.lang.instrument.
//
// can now enable some events
}
}
}
}
// can now enable events
}
}
}
}
}
}
}
}
char**
// Have to grab JVMTI thread state lock to be sure environment doesn't
// go away while we iterate them. No locks during VM bring-up.
} else {
}
}
private:
unsigned char ** _data_ptr;
unsigned char ** _end_ptr;
unsigned char * _curr_data;
unsigned char ** _cached_data_ptr;
public:
unsigned char **cached_data_ptr,
_curr_data = *data_ptr;
// Clear class_being_redefined flag here. The action
// from agent handler could generate a new class file load
// hook event and if it is not cleared the new event generated
// from regular class file load could have this stale redefined
// class handle info.
} else {
// redefine and retransform will always set the thread state
}
}
void post() {
// EVT_TRIG_TRACE(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
// ("JVMTI [%s] class file load hook event triggered",
// JvmtiTrace::safe_get_thread_name(_thread)));
}
private:
void post_all_envs() {
if (_load_kind != jvmti_class_load_kind_retransform) {
// for class load and redefine,
// call the non-retransformable agents
// non-retransformable agents cannot retransform back,
// so no need to cache the original class file bytes
post_to_env(env, false);
}
}
}
// retransformable agents get all events
// retransformable agents need to cache the original class file
// bytes if changes are made via the ClassFileLoadHook
post_to_env(env, true);
}
}
}
// EVT_TRACE(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
// ("JVMTI [%s] class file load hook event sent %s data_ptr = %d, data_len = %d",
// JvmtiTrace::safe_get_thread_name(_thread),
// _h_name == NULL ? "NULL" : _h_name->as_utf8(),
// _curr_data, _curr_len ));
}
// this agent has modified class data.
// data has been changed by the new retransformable agent
// and it hasn't already been cached, cache it
}
if (_curr_data != *_data_ptr) {
// curr_data is previous agent modified class data.
// And this has been changed by the new agent so
// we can delete it now.
}
// Class file data has changed by the current agent.
// Save the current agent env we need this to deallocate the
// memory allocated by this agent.
}
}
void copy_modified_data() {
// if one of the agent has modified class file data.
// Copy modified class data to new resources array.
if (_curr_data != *_data_ptr) {
}
}
};
// this entry is for class file load hook on class load, redefine and retransform
unsigned char **data_ptr,
unsigned char **end_ptr,
unsigned char **cached_data_ptr,
}
// if the object is a java.lang.Class then return the java mirror
if (k == SystemDictionary::Class_klass()) {
}
}
return k;
}
private:
public:
JvmtiVMObjectAllocEventMark(JavaThread *thread, oop obj) : JvmtiClassEventMark(thread, oop_to_klassOop(obj)) {
};
};
private:
const void *_code_data;
const void *_compile_info;
public:
_compile_info = compile_info_ptr; // Set void pointer of compiledMethodLoad Event. Default value is NULL.
}
}
};
private:
public:
}
};
///////////////////////////////////////////////////////////////
//
// pending CompiledMethodUnload support
//
("JVMTI [%s] method compile unload event triggered",
// post the event for each environment that has this event enabled.
("JVMTI [%s] class compile method unload event sent jmethodID " PTR_FORMAT,
}
}
}
}
///////////////////////////////////////////////////////////////
//
// JvmtiExport
//
return;
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
//
// JVMTI single step management
//
void JvmtiExport::at_single_stepping_point(JavaThread *thread, methodOop method, address location) {
// update information about current location and post a step event
return;
}
if (!state->hide_single_stepping()) {
if (state->is_pending_step_for_popframe()) {
}
if (state->is_pending_step_for_earlyret()) {
}
}
}
}
}
return true;
} else {
return false;
}
}
return;
}
}
}
}
}
return;
}
}
}
}
}
// get JavaThread for whom we are proxy
// do everything manually, since this is a proxy - needs special care
// Before we call the JVMTI agent, we have to set the state in the
// thread for which we are proxying.
}
"JavaThread should be in native");
}
}
}
}
// do JVMTI thread initialization (if needed)
// Do not post thread start event for hidden java thread.
}
}
}
}
}
return;
}
// Do not post thread end event for hidden java thread.
}
}
}
}
}
}
}
}
}
}
}
// for any thread that actually wants method entry, interp_only_mode is set
return;
}
}
}
}
}
}
// for any thread that actually wants method exit, interp_only_mode is set
return;
}
// return a flag when a method terminates by throwing an exception
// i.e. if an exception is thrown and it's not caught by the current method
value.j = 0L;
// if the method hasn't been popped because of an exception then we populate
// the return_value parameter for the callback. At this point we only have
// the address of a "raw result" and we just call into the interpreter to
// convert this into a jvalue.
if (!exception_exit) {
}
}
}
}
}
}
}
// we have a NotifyFramePop entry for this frame.
// we also need to issue a frame pop event for this frame
}
}
// remove the frame's entry
}
}
}
}
// Todo: inline this for optimization
return;
}
}
}
}
}
void JvmtiExport::post_exception_throw(JavaThread *thread, methodOop method, address location, oop exception) {
return;
}
if (!state->is_exception_detected()) {
("JVMTI [%s] Evt Exception thrown sent %s.%s @ %d",
// It's okay to clear these exceptions here because we duplicate
// this lookup in InterpreterRuntime::exception_handler_for_exception.
bool should_repeat;
do {
do {
should_repeat = false;
if (HAS_PENDING_EXCEPTION) {
should_repeat = true;
}
if (current_bci < 0) {
catch_jmethodID = 0;
current_bci = 0;
} else {
}
}
}
}
}
// frames may get popped because of this throw, be safe - invalidate cached depth
}
void JvmtiExport::notice_unwind_due_to_exception(JavaThread *thread, methodOop method, address location, oop exception, bool in_handler_frame) {
return;
}
("JVMTI [%s] Trg unwind_due_to_exception triggered %s.%s @ %s%d - %s",
if (state->is_exception_detected()) {
if (!in_handler_frame) {
// Not in exception handler.
if(state->is_interp_only_mode()) {
// method exit and frame pop events are posted only in interp mode.
// When these events are enabled code should be in running in interp mode.
// The cached cur_stack_depth might have changed from the
// operations of frame pop or method exit. We are not 100% sure
// the cached cur_stack_depth is still valid depth so invalidate
// it.
}
} else {
// In exception handler frame. Report exception catch.
// Update cur_stack_depth - the frames above the current frame
// have been unwound due to this exception:
("JVMTI [%s] Evt ExceptionCatch sent %s.%s @ %d",
}
}
}
}
}
}
// At least one field access watch is set so we have more work
// to do. This wrapper is used by entry points that allow us
// to create handles in post_field_access_by_jni().
// event posting can block so refetch oop if we were passed a jobj
}
return obj;
}
// At least one field access watch is set so we have more work
// to do. This wrapper is used by "quick" entry points that don't
// allow us to create handles in post_field_access_by_jni(). We
// override that with a ResetNoHandleMark.
// event posting can block so refetch oop if we were passed a jobj
}
return obj;
}
// We must be called with a Java context in order to provide reasonable
// values for the klazz, method, and location fields. The callers of this
// function don't make the call unless there is a Java context.
// if get_field_descriptor finds fieldID to be invalid, then we just bail
if (!valid_fieldID) return;
// field accesses are not watched so bail
if (!fd.is_field_access_watched()) return;
if (!is_static) {
// non-static field accessors have an object, but we need a handle
}
}
return;
}
}
}
}
}
// At least one field modification watch is set so we have more work
// to do. This wrapper is used by entry points that allow us
// to create handles in post_field_modification_by_jni().
// event posting can block so refetch oop if we were passed a jobj
}
return obj;
}
// At least one field modification watch is set so we have more work
// to do. This wrapper is used by "quick" entry points that don't
// allow us to create handles in post_field_modification_by_jni(). We
// override that with a ResetNoHandleMark.
// event posting can block so refetch oop if we were passed a jobj
}
return obj;
}
// We must be called with a Java context in order to provide reasonable
// values for the klazz, method, and location fields. The callers of this
// function don't make the call unless there is a Java context.
// if get_field_descriptor finds fieldID to be invalid, then we just bail
if (!valid_fieldID) return;
// field modifications are not watched so bail
if (!fd.is_field_modification_watched()) return;
if (!is_static) {
// non-static field accessors have an object, but we need a handle
}
}
// 'I' instructions are used for byte, char, short and int.
// determine which it really is, and convert
// should be found (if not, leave as is)
if (found) {
// convert value from int to appropriate type
switch (fd.field_type()) {
case T_BOOLEAN:
sig_type = 'Z';
value->i = 0; // clear it
break;
case T_BYTE:
sig_type = 'B';
value->i = 0; // clear it
break;
case T_CHAR:
sig_type = 'C';
value->i = 0; // clear it
break;
case T_SHORT:
sig_type = 'S';
value->i = 0; // clear it
break;
case T_INT:
// nothing to do
break;
default:
// this is an integer instruction, should be one of above
break;
}
}
}
// convert oop to JNI handle.
}
// Destroy the JNI handle allocated above.
if (sig_type == 'L') {
}
}
return;
}
("JVMTI [%s] Trg Field Modification event triggered",
("JVMTI [%s] Evt Field Modification event sent %s.%s @ %d",
}
}
}
}
EVT_TRIG_TRACE(JVMTI_EVENT_NATIVE_METHOD_BIND, ("JVMTI [%s] Trg Native Method Bind event triggered",
}
}
}
}
}
// Returns a record containing inlining information for the given nmethod
jvmtiCompiledMethodLoadInlineRecord* record = (jvmtiCompiledMethodLoadInlineRecord*)NEW_RESOURCE_OBJ(jvmtiCompiledMethodLoadInlineRecord);
}
int scope = 0;
}
int stackframe = 0;
// sd->method() can be NULL for stubs but not for nmethods. To be completely robust, include an assert that we should never see a null sd->method()
stackframe++;
}
scope++;
}
return record;
}
("JVMTI [%s] method compile load event triggered",
("JVMTI [%s] class compile method load event sent %s.%s ",
// Add inlining information
// Pass inlining information through the void pointer
}
}
}
}
// post a COMPILED_METHOD_LOAD event for a given environment
void JvmtiExport::post_compiled_method_load(JvmtiEnv* env, const jmethodID method, const jint length,
const jvmtiAddrLocationMap* map)
{
("JVMTI [%s] method compile load event triggered (by GenerateEvents)",
("JVMTI [%s] class compile method load event sent (by GenerateEvents), jmethodID=" PTR_FORMAT,
}
}
}
void JvmtiExport::post_dynamic_code_generated_internal(const char *name, const void *code_begin, const void *code_end) {
// In theory everyone coming thru here is in_vm but we need to be certain
// because a callee will do a vm->native transition
("JVMTI [%s] method dynamic code generated event triggered",
("JVMTI [%s] dynamic code generated event sent for %s",
}
}
}
}
void JvmtiExport::post_dynamic_code_generated(const char *name, const void *code_begin, const void *code_end) {
} else {
// It may not be safe to post the event from this thread. Defer all
// postings to the service thread so that it can perform them in a safe
// context and in-order.
}
}
// post a DYNAMIC_CODE_GENERATED event for a given environment
// used by GenerateEvents
const void *code_begin, const void *code_end)
{
("JVMTI [%s] dynamic code generated event triggered (by GenerateEvents)",
("JVMTI [%s] dynamic code generated event sent for %s",
}
}
}
// post a DynamicCodeGenerated event while holding locks in the VM.
{
// register the stub with the current dynamic code event collector
// state can only be NULL if the current thread is exiting which
// should not happen since we're trying to post an event
}
// Collect all the vm internally allocated objects which are visible to java world
// Can not take safepoint here.
// Can not take safepoint here so can not use state_for to get
// jvmti thread state.
// state is non NULL when VMObjectAllocEventCollector is enabled.
// Don't record classes as these will be notified via the ClassLoad
// event.
}
}
}
}
}
("JVMTI [%s] garbage collection finish event triggered",
("JVMTI [%s] garbage collection finish event sent ",
// JNIEnv is NULL here because this event is posted from VM Thread
}
}
}
}
("JVMTI [%s] garbage collection start event triggered",
("JVMTI [%s] garbage collection start event sent ",
// JNIEnv is NULL here because this event is posted from VM Thread
}
}
}
}
("JVMTI [%s] data dump request event triggered",
("JVMTI [%s] data dump request event sent ",
// JNIEnv is NULL here because this event is posted from VM Thread
}
}
}
}
// Ignore monitor contended enter for vm internal object.
return;
}
return;
}
("JVMTI [%s] montior contended enter event triggered",
("JVMTI [%s] monitor contended enter event sent",
}
}
}
}
// Ignore monitor contended entered for vm internal object.
return;
}
return;
}
("JVMTI [%s] montior contended entered event triggered",
("JVMTI [%s] monitor contended enter event sent",
}
}
}
}
return;
}
("JVMTI [%s] montior wait event triggered",
("JVMTI [%s] monitor wait event sent ",
}
}
}
}
void JvmtiExport::post_monitor_waited(JavaThread *thread, ObjectMonitor *obj_mntr, jboolean timed_out) {
// Ignore monitor waited for vm internal object.
return;
}
return;
}
("JVMTI [%s] montior waited event triggered",
("JVMTI [%s] monitor waited event sent ",
}
}
}
}
return;
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
// This has to happen after the thread state is removed, which is
// why it is not in post_thread_end_event like its complement
// Maybe both these functions should be rolled into the posts?
}
}
}
}
}
// Onload raw monitor transition.
}
////////////////////////////////////////////////////////////////////////////////////////////////
// type for the Agent_OnAttach entry point
extern "C" {
}
void* library;
// get agent name and options
// The abs paramter should be "true" or "false"
// If the path is absolute we attempt to load the library. Otherwise we try to
// load it from the standard dll directory.
if (is_absolute_path) {
} else {
// Try to load the agent from the standard dll directory
// not found - try local path
}
}
// If the library was loaded then we attempt to invoke the Agent_OnAttach
// function
// Lookup the Agent_OnAttach function
if (on_attach_entry != NULL) break;
}
if (on_attach_entry == NULL) {
// Agent_OnAttach missing - unload library
} else {
// Invoke the Agent_OnAttach function
{
}
// Agent_OnAttach may have used JNI
if (HAS_PENDING_EXCEPTION) {
}
// If OnAttach returns JNI_OK then we add it to the list of
// agent libraries so that we can call Agent_OnUnload later.
}
// Agent_OnAttach executed so completion status is JNI_OK
}
}
return result;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Setup current current thread for event collection.
// set this event collector to be the current one.
// state can only be NULL if the current thread is exiting which
// should not happen since we're trying to configure for event collection
if (is_vm_object_alloc_event()) {
} else if (is_dynamic_code_event()) {
}
}
// Unset current event collection in this thread and reset it with previous
// collector.
// restore the previous event collector (if any)
if (is_vm_object_alloc_event()) {
if (state->get_vm_object_alloc_event_collector() == this) {
} else {
// this thread's jvmti state was created during the scope of
// the event collector.
}
} else {
if (is_dynamic_code_event()) {
if (state->get_dynamic_code_event_collector() == this) {
} else {
// this thread's jvmti state was created during the scope of
// the event collector.
}
}
}
}
}
// create the dynamic code event collector
}
}
// iterate over any code blob descriptors collected and post a
// DYNAMIC_CODE_GENERATED event to the profiler.
// iterate over any code blob descriptors that we collected
if (_code_blobs != NULL) {
for (int i=0; i<_code_blobs->length(); i++) {
}
delete _code_blobs;
}
}
// register a stub
if (_code_blobs == NULL) {
}
}
// Setup current thread to record vm allocated objects.
if (JvmtiExport::should_post_vm_object_alloc()) {
_enable = true;
} else {
_enable = false;
}
}
// Post vm_object_alloc event for vm allocated objects visible to java
// world.
if (_allocated != NULL) {
set_enabled(false);
for (int i = 0; i < _allocated->length(); i++) {
}
}
delete _allocated;
}
}
if (_allocated == NULL) {
}
}
// GC support.
if (_allocated != NULL) {
}
}
}
}
// no-op if jvmti not enabled
if (!JvmtiEnv::environments_might_exist()) {
return;
}
// Runs at safepoint. So no need to acquire Threads_lock.
}
}
}
}
// Disable collection of VMObjectAlloc events
// a no-op if VMObjectAlloc event is not enabled
if (!JvmtiExport::should_post_vm_object_alloc()) {
return;
}
_collector->set_enabled(false);
}
}
}
}
// Re-Enable collection of VMObjectAlloc events (if previously enabled)
if (was_enabled()) {
_collector->set_enabled(true);
}
};
// if there aren't any JVMTI environments then nothing to do
if (!JvmtiEnv::environments_might_exist()) {
return;
}
}
if (SafepointSynchronize::is_at_safepoint()) {
// Do clean up tasks that need to be done at a safepoint
}
}
JvmtiGCMarker::~JvmtiGCMarker() {
// if there aren't any JVMTI environments then nothing to do
if (!JvmtiEnv::environments_might_exist()) {
return;
}
// JVMTI notify gc finish
}
}