/*
* 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 "interpreter/interpreter.hpp"
#include "jvmtifiles/jvmtiEnv.hpp"
#include "memory/resourceArea.hpp"
#include "oops/instanceKlass.hpp"
#include "prims/jvmtiAgentThread.hpp"
#include "prims/jvmtiEventController.inline.hpp"
#include "prims/jvmtiImpl.hpp"
#include "prims/jvmtiRedefineClasses.hpp"
#include "runtime/atomic.hpp"
#include "runtime/deoptimization.hpp"
#include "runtime/handles.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/interfaceSupport.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/serviceThread.hpp"
#include "runtime/signature.hpp"
#include "runtime/vframe.hpp"
#include "runtime/vframe_hp.hpp"
#include "runtime/vm_operations.hpp"
#include "utilities/exceptions.hpp"
#ifdef TARGET_OS_FAMILY_linux
# include "thread_linux.inline.hpp"
#endif
#ifdef TARGET_OS_FAMILY_solaris
# include "thread_solaris.inline.hpp"
#endif
#ifdef TARGET_OS_FAMILY_windows
# include "thread_windows.inline.hpp"
#endif
#ifdef TARGET_OS_FAMILY_bsd
# include "thread_bsd.inline.hpp"
#endif
//
// class JvmtiAgentThread
//
// JavaThread used to wrap a thread started by an agent
// using the JVMTI method RunAgentThread.
//
JvmtiAgentThread::JvmtiAgentThread(JvmtiEnv* env, jvmtiStartFunction start_fn, const void *start_arg)
}
void
// It is expected that any Agent threads will be created as
// Java Threads. If this is the case, notification of the creation
// of the thread is given in JavaThread::thread_main().
}
void
ThreadToNativeFromVM transition(this);
}
//
// class GrowableCache - private methods
//
for (int i=0; i<len; i++) {
//
// The cache entry has gone bad. Without a valid frame pointer
// value, the entry is useless so we simply delete it in product
// mode. The call to remove() will rebuild the cache again
// without the bad entry.
//
assert(false, "cannot recache NULL elements");
remove(i);
return;
}
}
}
}
//
// class GrowableCache - public methods
//
}
GrowableCache::~GrowableCache() {
clear();
delete _elements;
}
recache();
}
// number of elements in the collection
}
// get the value of the index element in the collection
return e;
}
}
// append a copy of the element to the end of the collection
recache();
}
// insert a copy of the element using lessthan()
int n = length()-2;
for (int i=n; i>=0; i--) {
}
}
recache();
}
// remove the element at index
delete e;
recache();
}
// clear out all elements, release all heap space and
// let our listener know that things have changed.
for (int i=0; i<len; i++) {
}
recache();
}
for (int i=0; i<len; i++) {
e->oops_do(f);
}
}
for (int i=0; i<len; i++) {
}
}
//
// class JvmtiBreakpoint
//
_bci = 0;
#ifdef CHECK_UNHANDLED_OOPS
// This one is always allocated with new, but check it just in case.
}
#endif // CHECK_UNHANDLED_OOPS
}
#ifdef CHECK_UNHANDLED_OOPS
// Could be allocated with new and wouldn't be on the unhandled oop list.
}
#endif // CHECK_UNHANDLED_OOPS
}
}
return false;
}
}
_bci >= 0;
}
}
// are EMCP. Directly or transitively obsolete methods are
// not saved in the PreviousVersionInfo.
{
// PreviousVersionInfo objects returned via PreviousVersionWalker
// contain a GrowableArray of handles. We have to clean up the
// GrowableArray _after_ the PreviousVersionWalker destructor
// has destroyed the handles.
{
// search previous versions if they exist
// We have run into a PreviousVersion generation where
// all methods were made obsolete during that generation's
// RedefineClasses() operation. At the time of that
// operation, all EMCP methods were flushed so we don't
// have to go back any further.
//
// A NULL methods array is different than an empty methods
// array. We cannot infer any optimizations about older
// generations from an empty methods array for the current
// generation.
break;
}
break;
}
}
}
} // pvw is cleaned up
} // rm is cleaned up
}
}
}
#ifndef PRODUCT
#endif
}
//
// class VM_ChangeBreakpoints
//
// Modify the Breakpoints data structure at a safepoint
//
switch (_operation) {
case SET_BREAKPOINT:
break;
case CLEAR_BREAKPOINT:
break;
default:
assert(false, "Unknown operation");
}
}
// The JvmtiBreakpoints in _breakpoints will be visited via
// JvmtiExport::oops_do.
}
}
//
// class JvmtiBreakpoints
//
// a JVMTI internal collection of JvmtiBreakpoint
//
}
JvmtiBreakpoints:: ~JvmtiBreakpoints() {}
}
_bps.gc_epilogue();
}
#ifndef PRODUCT
for (int i=0; i<n; i++) {
}
#endif
}
if (i == -1) {
}
}
if (i != -1) {
}
}
return JVMTI_ERROR_DUPLICATE;
}
return JVMTI_ERROR_NONE;
}
return JVMTI_ERROR_NOT_FOUND;
}
return JVMTI_ERROR_NONE;
}
bool changed = true;
// We are going to run thru the list of bkpts
// and delete some. This deletion probably alters
// the list in some implementation defined way such
// that when we delete entry i, the next entry might
// no longer be at i+1. To be safe, each time we delete
// an entry, we'll just start again from the beginning.
// We'll stop when we make a pass thru the whole list without
// deleting anything.
while (changed) {
changed = false;
for (int i = 0; i < len; i++) {
// This changed 'i' so we have to start over.
changed = true;
break;
}
}
}
}
//
// class JvmtiCurrentBreakpoints
//
return (*_jvmti_breakpoints);
}
}
if (_jvmti_breakpoints != NULL) {
}
}
if (_jvmti_breakpoints != NULL) {
}
}
///////////////////////////////////////////////////////////////
//
// class VM_GetOrSetLocal
//
// Constructor for non-object getter
, _set(false)
{
}
// Constructor for object or non-object setter
VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, jint depth, int index, BasicType type, jvalue value)
, _set(true)
{
}
// Constructor for object getter
VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, JavaThread* calling_thread, jint depth, int index)
, _set(false)
{
}
if (!_thread->has_last_Java_frame()) {
return NULL;
}
int d = 0;
d++;
}
return vf;
}
return NULL;
}
if (!vf->is_java_frame()) {
return NULL;
}
return jvf;
}
// Check that the klass is assignable to a type with the given signature.
// Another solution could be to use the function Klass::is_subtype_of(type).
// But the type class can be forced to load/initialize eagerly in such a case.
// This may cause unexpected consequences like CFLH or class-init JVMTI events.
// It is better to avoid such a behavior.
ty_sign++;
len -= 2;
}
return true;
}
// Compare primary supers
int idx;
return true;
}
}
// Compare secondary supers
return true;
}
}
return false;
}
// Checks error conditions:
// JVMTI_ERROR_INVALID_SLOT
// JVMTI_ERROR_TYPE_MISMATCH
// Returns: 'true' - everything is Ok, 'false' - error code
if (!method_oop->has_localvariable_table()) {
// Just to check index boundaries
return false;
}
return true;
}
if (num_entries == 0) {
return false; // There are no slots
}
for (int i = 0; i < num_entries; i++) {
// Here we assume that locations of LVT entries
// with the same slot number cannot be overlapped
break;
}
}
if (signature_idx == -1) {
return false; // Incorrect slot index
}
switch (slot_type) {
case T_BYTE:
case T_SHORT:
case T_CHAR:
case T_BOOLEAN:
break;
case T_ARRAY:
break;
};
return false;
}
// Check that the jobject class matches the return type signature.
return false;
}
}
return true;
}
}
_jvf = get_java_vframe();
NULL_CHECK(_jvf, false);
return true;
} else {
return false;
}
}
if (!check_slot_type(_jvf)) {
return false;
}
return true;
}
if (_set) {
// Force deoptimization of frame if compiled because it's
// possible the compiler emitted some locals as constant values,
// meaning they are not mutable.
if (can_be_deoptimized(_jvf)) {
// Schedule deoptimization so that eventually the local
// update will be written to an interpreter frame.
// Now store a new value for the local which will be applied
// once deoptimization occurs. Note however that while this
// write is deferred until deoptimization actually happens
// can vframe created after this point will have its locals
// reflecting this update so as far as anyone can see the
// write has already taken place.
// If we are updating an oop then get the oop from the handle
// since the handle will be long gone by the time the deopt
// happens. The oop stored in the deferred local will be
// gc'd on its own.
}
// Re-read the vframe so we can see that it is deoptimized
// [ Only need because of assert in update_local() ]
_jvf = get_java_vframe();
return;
}
switch (_type) {
case T_OBJECT: {
break;
}
default: ShouldNotReachHere();
}
} else {
} else {
return;
}
switch (_type) {
case T_OBJECT: {
// Wrap the oop to be returned in a local JNI handle since
// oops_do() no longer applies after doit() is finished.
break;
}
default: ShouldNotReachHere();
}
}
}
}
return true; // May need to deoptimize
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// class JvmtiSuspendControl - see comments in jvmtiImpl.hpp
//
// external suspend should have caught suspending a thread twice
// Immediate suspension required for JPDA back-end so JVMTI agent threads do
// not deadlock due to later suspension on transitions while holding
// raw monitors. Passing true causes the immediate suspension.
// java_suspend() will catch threads in the process of exiting
// and will ignore them.
// It would be nice to have the following assertion in all the time,
// but it is possible for a racing resume request to have resumed
// this thread right after we suspended it. Temporarily enable this
// assertion if you are chasing a different kind of bug.
//
// assert(java_lang_Thread::thread(java_thread->threadObj()) == NULL ||
// java_thread->is_being_ext_suspended(), "thread is not suspended");
// check again because we can get delayed in java_suspend():
// the thread is in process of exiting.
return false;
}
return true;
}
// external suspend should have caught resuming a thread twice
// resume thread
{
// must always grab Threads_lock, see JVM_SuspendThread
}
return true;
}
#ifndef PRODUCT
#if JVMTI_TRACE
#else
const char *name = "";
#endif /*JVMTI_TRACE */
if (!thread->has_last_Java_frame()) {
}
}
#endif
}
// Keep the nmethod alive until the ServiceThread can process
// this deferred event.
return event;
}
// Keep the nmethod alive until the ServiceThread can process
// this deferred event. This will keep the memory for the
// generated code from being reused too early. We pass
// zombie_ok == true here so that our nmethod that was just
// made into a zombie can be locked.
return event;
}
// Need to make a copy of the name since we don't know how long
// the event poster will keep it around after we enqueue the
// deferred event and return. strdup() failure is handled in
// the post() routine below.
return event;
}
"Service thread must post enqueued events");
switch(_type) {
case TYPE_COMPILED_METHOD_LOAD: {
// done with the deferred event so unlock the nmethod
break;
}
case TYPE_COMPILED_METHOD_UNLOAD: {
// done with the deferred event so unlock the nmethod
break;
}
case TYPE_DYNAMIC_CODE_GENERATED: {
// if strdup failed give the event a default name
// release our copy
}
break;
}
default:
}
}
volatile JvmtiDeferredEventQueue::QueueNode*
}
// Events get added to the end of the queue (and are pulled off the front).
if (_queue_tail == NULL) {
} else {
_queue_tail = node;
}
"Inconsistent queue markers");
}
if (_queue_head == NULL) {
// Just in case this happens in product; it shouldn't but let's not crash
return JvmtiDeferredEvent();
}
if (_queue_head == NULL) {
_queue_tail = NULL;
}
"Inconsistent queue markers");
delete node;
return event;
}
const JvmtiDeferredEvent& event) {
bool success = false;
do {
}
// This method transfers any events that were added by someone NOT holding
// the lock into the mainline queue.
if (_pending_list != NULL) {
"Inconsistent queue markers");
// Since we've treated the pending list as a stack (with newer
// events at the beginning), we need to join the bottom of the stack
// with the 'tail' of the queue in order to get the events in the
// right order. We do this by reversing the pending list and appending
// it to the queue.
// This reverses the list
}
// Now append the new list to the queue
if (_queue_tail != NULL) {
} else { // _queue_head == NULL
}
}
}
}