/*
* 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/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/vmSymbols.hpp"
#include "jvmtifiles/jvmtiEnv.hpp"
#include "oops/instanceMirrorKlass.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/oop.inline2.hpp"
#include "prims/jvmtiEventController.hpp"
#include "prims/jvmtiEventController.inline.hpp"
#include "prims/jvmtiExport.hpp"
#include "prims/jvmtiImpl.hpp"
#include "prims/jvmtiTagMap.hpp"
#include "runtime/biasedLocking.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/jniHandles.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/reflectionUtils.hpp"
#include "runtime/vframe.hpp"
#include "runtime/vmThread.hpp"
#include "runtime/vm_operations.hpp"
#include "services/serviceUtil.hpp"
#ifndef SERIALGC
#endif
// JvmtiTagHashmapEntry
//
// Each entry encapsulates a reference to the tagged object
// and the tag value. In addition an entry includes a next pointer which
// is used to chain entries together.
private:
friend class JvmtiTagMap;
}
// constructor
public:
// accessor methods
}
};
// JvmtiTagHashmap
//
// A hashmap is essentially a table of pointers to entries. Entries
// are hashed to a location, or position in the table, and then
// chained from that location. The "key" for hashing is address of
// the object, or oop. The "value" is the tag value.
//
// A hashmap maintains a count of the number entries in the hashmap
// and resizes if the number of entries exceeds a given threshold.
// The threshold is specified as a percentage of the size - for
// example a threshold of 0.75 will trigger the hashmap to resize
// if the number of entries is >75% of table size.
//
// A hashmap provides functions for adding, removing, and finding
// entries. It also provides a function to iterate over all entries
// in the hashmap.
private:
friend class JvmtiTagMap;
enum {
};
// private accessors
// initialize the hashmap
_entry_count = 0;
if (TraceJVMTIObjectTagging) {
} else {
_trace_threshold = -1;
}
_resizing_enabled = true;
vm_exit_out_of_memory(s, "unable to allocate initial hashtable for jvmti object tags");
}
for (int i=0; i<initial_size; i++) {
}
}
// hash a given key (oop) with the specified size
// shift right to get better distribution (as these bits will be zero
// with aligned addresses)
#ifdef _LP64
#else
#endif
}
// hash a given key (oop)
}
// resize the hashmap - allocates a large table and re-hashes
// all entries into the new table.
void resize() {
if (new_size < 0) {
// hashmap already at maximum capacity
return;
}
// allocate new table
warning("unable to allocate larger hashtable for jvmti object tags");
set_resizing_enabled(false);
return;
}
// initialize new table
int i;
for (i=0; i<new_size; i++) {
}
// rehash all entries into the new table
for (i=0; i<_size; i++) {
} else {
}
}
}
// free old table and update settings.
// compute new resize threshold
}
// internal remove function - remove an entry at a given position in the
// table.
} else {
}
_entry_count--;
}
// resizing switch
// debugging
void print_memory_usage();
void compute_next_trace_threshold();
public:
// create a JvmtiTagHashmap of a preferred size and optionally a load factor.
// The preferred size is rounded down to an actual size.
int i=0;
if (_sizes[i] < 0) {
assert(i > 0, "sanity check");
i--;
break;
}
i++;
}
// if a load factor is specified then use it, otherwise use default
if (load_factor > 0.01f) {
init(i, load_factor);
} else {
init(i);
}
}
// create a JvmtiTagHashmap with default settings
JvmtiTagHashmap() {
init();
}
// release table when JvmtiTagHashmap destroyed
~JvmtiTagHashmap() {
}
}
// accessors
// find an entry in the hashmap, returns NULL if not found.
return entry;
}
}
return NULL;
}
// add a new entry to hashmap
} else {
}
_entry_count++;
}
// if the number of entries exceed the threshold then resize
resize();
}
}
// remove an entry with the given key.
break;
}
}
}
return entry;
}
// iterate over all entries in the hashmap
};
// possible hashmap sizes - odd primes that roughly double in size.
// To avoid excessive resizing the odd primes from 4801-76831 and
// 76831-307261 have been removed. The list must be terminated by -1.
2457733, 4915219, 9830479, 19660831, 39321619, 78643219, -1 };
// A supporting class for iterating over all entries in Hashmap
class JvmtiTagHashmapEntryClosure {
public:
};
// iterate over all entries in the hashmap
for (int i=0; i<_size; i++) {
// obtain the next entry before invoking do_entry - this is
// necessary because do_entry may remove the entry from the
// hashmap.
}
}
}
// debugging
// table + entries in KB
entry_count()*sizeof(JvmtiTagHashmapEntry))/K;
}
// compute threshold for the next trace message
if (trace_threshold() < medium_trace_threshold) {
} else {
if (trace_threshold() < large_trace_threshold) {
} else {
}
}
}
// create a JvmtiTagMap
{
_hashmap = new JvmtiTagHashmap();
// finally add us to the environment
}
// destroy a JvmtiTagMap
JvmtiTagMap::~JvmtiTagMap() {
// no lock acquired as we assume the enclosing environment is
// also being destroryed.
delete entry;
}
}
// finally destroy the hashmap
delete _hashmap;
// remove any entries on the free list
delete entry;
}
}
// create a hashmap entry
// - if there's an entry on the (per-environment) free list then this
// is returned. Otherwise an new entry is allocated.
if (_free_entries == NULL) {
} else {
}
return entry;
}
// destroy an entry by returning it to the free list
// limit the size of the free list
if (_free_entries_count >= max_free_entries) {
delete entry;
} else {
}
}
// returns the tag map for the given environments. If the tag map
// doesn't exist then it is created.
}
} else {
}
return tag_map;
}
// iterate over all entries in the tag map.
}
// returns true if the hashmaps are empty
return hashmap()->entry_count() == 0;
}
// Return the tag value for an object, or 0 if the object is
// not tagged
//
return 0;
} else {
}
}
// If the object is a java.lang.Class then return the klassOop,
// otherwise return the original object
if (!java_lang_Class::is_primitive(o)) {
}
}
return o;
}
// A CallbackWrapper is a support class for querying and tagging an object
// around a callback to a profiler. The constructor does pre-callback
// work to get the tag value, klass tag value, ... and the destructor
// does the post-callback work of tagging or untagging the object.
//
// {
// CallbackWrapper wrapper(tag_map, o);
//
// (*callback)(wrapper.klass_tag(), wrapper.obj_size(), wrapper.obj_tag_p(), ...)
//
// } // wrapper goes out of scope here which results in the destructor
// checking to see if the object has been tagged, untagged, or the
// tag value has changed.
//
private:
protected:
// invoked post-callback to tag, untag, or update the tag of an object
public:
"MT unsafe or must be VM thread");
// for Classes the klassOop is tagged
// object size
// record the context
// get object tag
// get the class and the class's tag value
if (_o == o) {
} else {
// if the object represents a runtime class then use the
// tag for java.lang.Class
}
}
~CallbackWrapper() {
}
};
// callback post-callback to tag, untag, or update the tag of an object
if (obj_tag != 0) {
// callback has tagged the object
}
} else {
// object was previously tagged - the callback may have untagged
// the object or changed the tag value
if (obj_tag == 0) {
} else {
}
}
}
}
// An extended CallbackWrapper used when reporting an object reference
// to the agent.
//
// {
// TwoOopCallbackWrapper wrapper(tag_map, referrer, o);
//
// (*callback)(wrapper.klass_tag(),
// wrapper.obj_size(),
// wrapper.obj_tag_p()
// wrapper.referrer_tag_p(), ...)
//
// } // wrapper goes out of scope here which results in the destructor
// checking to see if the referrer object has been tagged, untagged,
// or the tag value has changed.
//
private:
bool _is_reference_to_self;
public:
{
// self reference needs to be handled in a special way
_is_reference_to_self = (referrer == o);
if (_is_reference_to_self) {
_referrer_tag_p = obj_tag_p();
} else {
// for Classes the klassOop is tagged
// record the context
// get object tag
// get referrer class tag.
}
}
if (!is_reference_to_self()){
}
}
// address of referrer tag
// (for a self reference this will return the same thing as obj_tag_p())
// referrer's class tag
};
// tag an object
//
// This function is performance critical. If many threads attempt to tag objects
// around the same time then it's possible that the Mutex associated with the
// tag map will be a hot lock.
// resolve the object
// for Classes we tag the klassOop
o = klassOop_if_java_lang_Class(o);
// see if the object is already tagged
// if the object is not already tagged then we tag it
if (tag != 0) {
} else {
// no-op
}
} else {
// if the object is already tagged then we either update
// the tag (if a new tag value has been provided)
// or remove the object if the new tag value is 0.
if (tag == 0) {
} else {
}
}
}
// get the tag for an object
// resolve the object
// for Classes get the tag from the klassOop
return tag_for(this, klassOop_if_java_lang_Class(o));
}
// Helper class used to describe the static or instance fields of a class.
// For each field it holds the field index (as defined by the JVMTI specification),
// the field type, and the offset.
private:
int _field_index;
int _field_offset;
char _field_type;
public:
}
};
private:
enum {
};
// list of field descriptors
// constructor
// add a field
// returns the field count for the given class
public:
~ClassFieldMap();
// access
// functions to create maps of static or instance fields
};
}
ClassFieldMap::~ClassFieldMap() {
}
delete _fields;
}
}
// Returns a heap allocated ClassFieldMap to describe the static fields
// of the given class.
//
// create the field map
FilteredFieldStream f(ikh, false, false);
int index = 0;
// ignore instance fields
continue;
}
}
return field_map;
}
// Returns a heap allocated ClassFieldMap to describe the instance fields
// of the given class. All instance fields are included (this means public
// and private fields declared in superclasses and superinterfaces too).
//
// create the field map
FilteredFieldStream f(ikh, false, false);
int index = 0;
// ignore static fields
continue;
}
}
return field_map;
}
// Helper class used to cache a ClassFileMap for the instance fields of
// a cache. A JvmtiCachedClassFieldMap can be cached by an instanceKlass during
// heap iteration and avoid creating a field map for each object in the heap
// (only need to create the map when the first instance of a class is encountered).
//
private:
enum {
};
public:
// returns the field map for a given object (returning map cached
// by instanceKlass if possible
// removes the field map from all instanceKlasses - should be
// called before VM operation completes
static void clear_cache();
// returns the number of ClassFieldMap cached by instanceKlasses
static int cached_field_map_count();
};
}
if (_field_map != NULL) {
delete _field_map;
}
}
// Marker class to ensure that the class file map cache is only used in a defined
// scope.
private:
static bool _is_active;
public:
_is_active = true;
}
_is_active = false;
}
};
// record that the given instanceKlass is caching a field map
if (_class_list == NULL) {
}
}
// returns the instance field map for the given object
// (returns field map cached by the instanceKlass if possible)
// return cached map if possible
if (cached_map != NULL) {
return cached_map->field_map();
} else {
return field_map;
}
}
// remove the fields maps cached from all instanceKlasses
if (_class_list != NULL) {
for (int i = 0; i < _class_list->length(); i++) {
delete cached_map; // deletes the encapsulated field map
}
delete _class_list;
_class_list = NULL;
}
}
// returns the number of ClassFieldMap cached by instanceKlasses
}
// helper function to indicate if an object is filtered by its tag or class tag
int heap_filter) {
// apply the heap filter
if (obj_tag != 0) {
// filter out tagged objects
if (heap_filter & JVMTI_HEAP_FILTER_TAGGED) return true;
} else {
// filter out untagged objects
if (heap_filter & JVMTI_HEAP_FILTER_UNTAGGED) return true;
}
if (klass_tag != 0) {
// filter out objects with tagged classes
if (heap_filter & JVMTI_HEAP_FILTER_CLASS_TAGGED) return true;
} else {
// filter out objects with untagged classes.
if (heap_filter & JVMTI_HEAP_FILTER_CLASS_UNTAGGED) return true;
}
return false;
}
// helper function to indicate if an object is filtered by a klass filter
if (!klass_filter.is_null()) {
return true;
}
}
return false;
}
// helper function to tell if a field is a primitive field or not
}
// helper function to copy the value from location addr to jvalue.
switch (value_type) {
default: ShouldNotReachHere();
}
}
// helper function to invoke string primitive value callback
// returns visit control flags
void* user_data)
{
// get the string value and length
// (string value may be offset from the base)
if (s_len > 0) {
} else {
}
// invoke the callback
}
// helper function to invoke string primitive value callback
// returns visit control flags
void* user_data)
{
// get base address of first element
// jvmtiPrimitiveType is defined so this mapping is always correct
}
// helper function to invoke the primitive field callback for all static fields
// of a given class
void* user_data)
{
// for static fields only the index will be set
return 0;
}
// ignore classes for object and type arrays
if (!klass->oop_is_instance()) {
return 0;
}
// ignore classes which aren't linked yet
return 0;
}
// get the field map
// invoke the callback for each static primitive field
for (int i=0; i<field_map->field_count(); i++) {
// ignore non-primitive fields
if (!is_primitive_field_type(type)) {
continue;
}
// one-to-one mapping
// get offset and field value
// field index
// invoke the callback
if (res & JVMTI_VISIT_ABORT) {
delete field_map;
return res;
}
}
delete field_map;
return 0;
}
// helper function to invoke the primitive field callback for all instance fields
// of a given object
void* user_data)
{
// for instance fields only the index will be set
// get the map of the instance fields
// invoke the callback for each instance primitive field
for (int i=0; i<fields->field_count(); i++) {
// ignore non-primitive fields
if (!is_primitive_field_type(type)) {
continue;
}
// one-to-one mapping
// get offset and field value
// field index
// invoke the callback
if (res & JVMTI_VISIT_ABORT) {
return res;
}
}
return 0;
}
// VM operation to iterate over all objects in the heap (both reachable
// and unreachable)
private:
public:
void doit() {
// allows class files maps to be cached during iteration
// make sure that heap is parsable (fills TLABs with filler objects)
// Verify heap before iteration - if the heap gets corrupted then
// JVMTI's IterateOverHeap will crash.
if (VerifyBeforeIteration) {
}
// do the iteration
// If this operation encounters a bad object when using CMS,
// consider using safe_object_iterate() which avoids perm gen
// objects that may contain bad references.
// when sharing is enabled we must iterate over the shared spaces
if (UseSharedSpaces) {
}
}
};
// An ObjectClosure used to support the deprecated IterateOverHeap and
// IterateOverInstancesOfClass functions
private:
const void* _user_data;
// accessors
// indicates if iteration has been aborted
bool _iteration_aborted;
public:
const void* user_data) :
_iteration_aborted(false)
{
}
};
// invoked for each object in the heap
// check if iteration has been halted
if (is_iteration_aborted()) return;
// ignore any objects that aren't visible to profiler
if (!ServiceUtil::visible_oop(o)) return;
// instanceof check when filtering by klass
return;
}
// prepare for the calllback
// if the object is tagged and we're only interested in untagged objects
// then don't invoke the callback. Similiarly, if the object is untagged
// and we're only interested in tagged objects we skip the callback.
if (object_filter() == JVMTI_HEAP_OBJECT_UNTAGGED) return;
} else {
if (object_filter() == JVMTI_HEAP_OBJECT_TAGGED) return;
}
// invoke the agent's callback
(void*)user_data());
if (control == JVMTI_ITERATION_ABORT) {
set_iteration_aborted(true);
}
}
// An ObjectClosure used to support the IterateThroughHeap function
private:
int _heap_filter;
const void* _user_data;
// accessor functions
// indicates if the iteration has been aborted
bool _iteration_aborted;
// used to check the visit control flags. If the abort flag is set
// then we set the iteration aborted flag so that the iteration completes
// without processing any further objects
if (is_abort) {
_iteration_aborted = true;
}
return is_abort;
}
public:
int heap_filter,
const jvmtiHeapCallbacks* heap_callbacks,
const void* user_data) :
_iteration_aborted(false)
{
}
};
// invoked for each object in the heap
// check if iteration has been halted
if (is_iteration_aborted()) return;
// ignore any objects that aren't visible to profiler
// apply class filter
// prepare for callback
// check if filtered by the heap filter
return;
}
// for arrays we need the length, otherwise -1
// invoke the object callback (if callback is provided)
(void*)user_data());
if (check_flags_for_abort(res)) return;
}
// for objects and classes we report primitive fields if callback provided
obj,
cb,
(void*)user_data());
} else {
obj,
cb,
(void*)user_data());
}
if (check_flags_for_abort(res)) return;
}
// string callback
if (!is_array &&
&wrapper,
obj,
(void*)user_data() );
if (check_flags_for_abort(res)) return;
}
// array callback
if (is_array &&
obj->is_typeArray()) {
&wrapper,
obj,
(void*)user_data() );
if (check_flags_for_abort(res)) return;
}
};
// Deprecated function to iterate over all objects in the heap
const void* user_data)
{
}
// Iterates over all objects in the heap
const jvmtiHeapCallbacks* callbacks,
const void* user_data)
{
}
// support class for get_objects_with_tags
private:
public:
}
~TagObjectCollector() {
delete _object_results;
delete _tag_results;
}
// for each tagged object check if the tag value matches
// - if it matches then we create a JNI local reference to the object
// and record the reference and tag value.
//
for (int i=0; i<_tag_count; i++) {
// the mirror is tagged
if (o->is_klass()) {
}
}
}
}
// return the results from the collection
//
// if object_result_ptr is not NULL then allocate the result and copy
// in the object references.
if (object_result_ptr != NULL) {
if (error != JVMTI_ERROR_NONE) {
return error;
}
for (int i=0; i<count; i++) {
}
}
// if tag_result_ptr is not NULL then allocate the result and copy
// in the tag values.
if (tag_result_ptr != NULL) {
if (error != JVMTI_ERROR_NONE) {
if (object_result_ptr != NULL) {
}
return error;
}
for (int i=0; i<count; i++) {
}
}
return JVMTI_ERROR_NONE;
}
};
// return the list of objects with the specified tags
{
// iterate over all tagged objects
}
}
// ObjectMarker is used to support the marking objects when walking the
// heap.
//
// This implementation uses the existing mark bits in an object for
// marking. Objects that are marked must later have their headers restored.
// As most objects are unlocked and don't have their identity hash computed
// we don't have to save their headers. Instead we save the headers that
// are "interesting". Later when the headers are restored this implementation
// restores all headers to their initial value and then restores the few
// objects that had interesting headers.
//
// Future work: This implementation currently uses growable arrays to save
// the oop and header of interesting objects. As an optimization we could
// use the same technique as the GC and make use of the unused area
// between top() and end().
//
// An ObjectClosure used to restore the mark bits of an object
public:
if (o != NULL) {
o->init_mark();
}
}
}
};
// ObjectMarker provides the mark and visited functions
private:
// saved headers
public:
static void init(); // initialize
static void done(); // clean-up
};
// initialize ObjectMarker - prepares for object marking
// prepare heap for iteration
// create stacks for interesting headers
if (UseBiasedLocking) {
}
}
// Object marking is done so restore object headers
// iterate over all objects and restore the mark bits to
// their initial value
if (needs_reset()) {
} else {
// We don't need to reset mark bits on this call, but reset the
// flag to the default for the next call.
set_needs_reset(true);
}
// When sharing is enabled we need to restore the headers of the objects
// in the readwrite space too.
if (UseSharedSpaces) {
}
// now restore the interesting headers
for (int i = 0; i < _saved_oop_stack->length(); i++) {
}
if (UseBiasedLocking) {
}
// free the stacks
delete _saved_oop_stack;
delete _saved_mark_stack;
}
// mark an object
// object's mark word
if (mark->must_be_preserved(o)) {
_saved_oop_stack->push(o);
}
// mark the object
}
// return true if object is marked
}
// Stack allocated class to help ensure that ObjectMarker is used
// correctly. Constructor initializes ObjectMarker, destructor calls
// ObjectMarker's done() function to restore object headers.
public:
ObjectMarker::init();
}
ObjectMarker::done();
}
};
// helper to map a jvmtiHeapReferenceKind to an old style jvmtiHeapRootKind
// (not performance critical as only used for roots)
switch (kind) {
case JVMTI_HEAP_REFERENCE_JNI_GLOBAL: return JVMTI_HEAP_ROOT_JNI_GLOBAL;
case JVMTI_HEAP_REFERENCE_MONITOR: return JVMTI_HEAP_ROOT_MONITOR;
case JVMTI_HEAP_REFERENCE_STACK_LOCAL: return JVMTI_HEAP_ROOT_STACK_LOCAL;
case JVMTI_HEAP_REFERENCE_JNI_LOCAL: return JVMTI_HEAP_ROOT_JNI_LOCAL;
case JVMTI_HEAP_REFERENCE_THREAD: return JVMTI_HEAP_ROOT_THREAD;
case JVMTI_HEAP_REFERENCE_OTHER: return JVMTI_HEAP_ROOT_OTHER;
default: ShouldNotReachHere(); return JVMTI_HEAP_ROOT_OTHER;
}
}
// Base class for all heap walk contexts. The base class maintains a flag
// to indicate if the context is valid or not.
private:
bool _valid;
public:
};
// A basic heap walk context for the deprecated heap walking functions.
// The context for a basic heap walk are the callbacks and fields used by
// the referrer caching scheme.
private:
// used for caching
public:
HeapWalkContext(true),
_last_referrer_tag(0) {
}
// accessors
};
// The advanced heap walk context for the FollowReferences functions.
// The context is the callbacks, and the fields used for filtering.
private:
public:
const jvmtiHeapCallbacks* heap_callbacks) :
HeapWalkContext(true),
}
// accessors
return _heap_callbacks->heap_reference_callback;
};
return _heap_callbacks->primitive_field_callback;
}
}
}
};
// The CallbackInvoker is a class with static functions that the heap walk can call
// into to invoke callbacks. It works in one of two modes. The "basic" mode is
// used for the deprecated IterateOverReachableObjects functions. The "advanced"
// mode is for the newer FollowReferences function which supports a lot of
// additional callbacks.
private:
// heap walk styles
static int _heap_walk_type;
// context for basic style heap walk
return &_basic_context;
}
// context for advanced style heap walk
return &_advanced_context;
}
// context needed for all heap walks
static const void* _user_data;
// accessors
// if the object hasn't been visited then push it onto the visit stack
// so that it will be visited later
return true;
}
// invoke basic style callbacks
static inline bool invoke_basic_heap_root_callback
static inline bool invoke_basic_stack_ref_callback
static inline bool invoke_basic_object_reference_callback
// invoke advanced style callbacks
static inline bool invoke_advanced_heap_root_callback
static inline bool invoke_advanced_stack_ref_callback
static inline bool invoke_advanced_object_reference_callback
// used to report the value of primitive fields
static inline bool report_primitive_field
public:
// initialize for basic mode
const void* user_data,
// initialize for advanced mode
const void* user_data,
// functions to report roots
// functions to report references
};
// statics
// initialize for basic heap walk (IterateOverReachableObjects et al)
const void* user_data,
}
// initialize for advanced heap walk (FollowReferences)
const void* user_data,
}
// invoke basic style heap root callback
inline bool CallbackInvoker::invoke_basic_heap_root_callback(jvmtiHeapRootKind root_kind, oop obj) {
// if we heap roots should be reported
return check_for_visit(obj);
}
(void*)user_data());
// push root to visit stack when following references
if (control == JVMTI_ITERATION_CONTINUE &&
}
return control != JVMTI_ITERATION_ABORT;
}
// invoke basic style stack ref callback
// if we stack refs should be reported
return check_for_visit(obj);
}
slot,
(void*)user_data());
// push root to visit stack when following references
if (control == JVMTI_ITERATION_CONTINUE &&
}
return control != JVMTI_ITERATION_ABORT;
}
// invoke basic style object reference callback
inline bool CallbackInvoker::invoke_basic_object_reference_callback(jvmtiObjectReferenceKind ref_kind,
// callback requires the referrer's tag. If it's the same referrer
// as the last call then we use the cached value.
} else {
}
// do the callback
(void*)user_data());
// record referrer and referrer tag. For self-references record the
// tag value from the callback as this might differ from referrer_tag.
} else {
}
if (control == JVMTI_ITERATION_CONTINUE) {
return check_for_visit(referree);
} else {
return control != JVMTI_ITERATION_ABORT;
}
}
// invoke advanced style heap root callback
// check that callback is provided
return check_for_visit(obj);
}
// apply class filter
return check_for_visit(obj);
}
// setup the callback wrapper
// apply tag filter
context->heap_filter())) {
return check_for_visit(obj);
}
// for arrays we need the length, otherwise -1
// invoke the callback
NULL, // referrer info
0, // referrer_class_tag is 0 for heap root
NULL, // referrer_tag_p
len,
(void*)user_data());
if (res & JVMTI_VISIT_ABORT) {
return false;// referrer class tag
}
if (res & JVMTI_VISIT_OBJECTS) {
}
return true;
}
// report a reference from a thread stack to an object
int depth,
// check that callback is provider
return check_for_visit(obj);
}
// apply class filter
return check_for_visit(obj);
}
// setup the callback wrapper
// apply tag filter
context->heap_filter())) {
return check_for_visit(obj);
}
// setup the referrer info
// for arrays we need the length, otherwise -1
// call into the agent
0, // referrer_class_tag is 0 for heap root (stack)
NULL, // referrer_tag is 0 for root
len,
(void*)user_data());
if (res & JVMTI_VISIT_ABORT) {
return false;
}
if (res & JVMTI_VISIT_OBJECTS) {
}
return true;
}
// This mask is used to pass reference_info to a jvmtiHeapReferenceCallback
// only for ref_kinds defined by the JVM TI spec. Otherwise, NULL is passed.
| (1 << JVMTI_HEAP_REFERENCE_STATIC_FIELD) \
| (1 << JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT) \
| (1 << JVMTI_HEAP_REFERENCE_CONSTANT_POOL) \
| (1 << JVMTI_HEAP_REFERENCE_STACK_LOCAL) \
| (1 << JVMTI_HEAP_REFERENCE_JNI_LOCAL))
// invoke the object reference callback to report a reference
inline bool CallbackInvoker::invoke_advanced_object_reference_callback(jvmtiHeapReferenceKind ref_kind,
{
// field index is only valid field in reference_info
// check that callback is provider
return check_for_visit(obj);
}
// apply class filter
return check_for_visit(obj);
}
// setup the callback wrapper
// apply tag filter
context->heap_filter())) {
return check_for_visit(obj);
}
// field index is only valid field in reference_info
// for arrays we need the length, otherwise -1
// invoke the callback
len,
(void*)user_data());
if (res & JVMTI_VISIT_ABORT) {
return false;
}
if (res & JVMTI_VISIT_OBJECTS) {
}
return true;
}
// report a "simple root"
if (is_basic_heap_walk()) {
// map to old style root kind
} else {
}
}
// invoke the primitive array values
// apply class filter
return true;
}
// apply tag filter
context->heap_filter())) {
return true;
}
// invoke the callback
&wrapper,
obj,
(void*)user_data());
return (!(res & JVMTI_VISIT_ABORT));
}
// invoke the string value callback
// apply class filter
return true;
}
// apply tag filter
context->heap_filter())) {
return true;
}
// invoke the callback
&wrapper,
str,
(void*)user_data());
return (!(res & JVMTI_VISIT_ABORT));
}
// invoke the primitive field callback
char type)
{
// for primitive fields only the index will be set
// apply class filter
return true;
}
// apply tag filter
context->heap_filter())) {
return true;
}
// the field index in the referrer
// map the type
// setup the jvalue
(void*)user_data());
return (!(res & JVMTI_VISIT_ABORT));
}
// instance field
char type) {
obj,
type);
}
// static field
char type) {
obj,
type);
}
// report a JNI local (root object) to the profiler
inline bool CallbackInvoker::report_jni_local_root(jlong thread_tag, jlong tid, jint depth, jmethodID m, oop obj) {
if (is_basic_heap_walk()) {
m,
-1,
obj);
} else {
m,
(jlocation)-1,
-1,
obj);
}
}
// report a local (stack reference, root object)
if (is_basic_heap_walk()) {
slot,
obj);
} else {
tid,
bci,
slot,
obj);
}
}
// report an object referencing a class.
if (is_basic_heap_walk()) {
} else {
return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_CLASS, referrer, referree, -1);
}
}
// report a class referencing its class loader.
if (is_basic_heap_walk()) {
return invoke_basic_object_reference_callback(JVMTI_REFERENCE_CLASS_LOADER, referrer, referree, -1);
} else {
return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_CLASS_LOADER, referrer, referree, -1);
}
}
// report a class referencing its signers.
if (is_basic_heap_walk()) {
} else {
return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_SIGNERS, referrer, referree, -1);
}
}
// report a class referencing its protection domain..
if (is_basic_heap_walk()) {
return invoke_basic_object_reference_callback(JVMTI_REFERENCE_PROTECTION_DOMAIN, referrer, referree, -1);
} else {
return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN, referrer, referree, -1);
}
}
// report a class referencing its superclass.
if (is_basic_heap_walk()) {
// Send this to be consistent with past implementation
} else {
return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_SUPERCLASS, referrer, referree, -1);
}
}
// report a class referencing one of its interfaces.
if (is_basic_heap_walk()) {
} else {
return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_INTERFACE, referrer, referree, -1);
}
}
// report a class referencing one of its static fields.
if (is_basic_heap_walk()) {
return invoke_basic_object_reference_callback(JVMTI_REFERENCE_STATIC_FIELD, referrer, referree, slot);
} else {
return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_STATIC_FIELD, referrer, referree, slot);
}
}
// report an array referencing an element object
inline bool CallbackInvoker::report_array_element_reference(oop referrer, oop referree, jint index) {
if (is_basic_heap_walk()) {
return invoke_basic_object_reference_callback(JVMTI_REFERENCE_ARRAY_ELEMENT, referrer, referree, index);
} else {
return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT, referrer, referree, index);
}
}
// report an object referencing an instance field object
if (is_basic_heap_walk()) {
} else {
return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_FIELD, referrer, referree, slot);
}
}
// report an array referencing an element object
inline bool CallbackInvoker::report_constant_pool_reference(oop referrer, oop referree, jint index) {
if (is_basic_heap_walk()) {
return invoke_basic_object_reference_callback(JVMTI_REFERENCE_CONSTANT_POOL, referrer, referree, index);
} else {
return invoke_advanced_object_reference_callback(JVMTI_HEAP_REFERENCE_CONSTANT_POOL, referrer, referree, index);
}
}
// A supporting closure used to process simple roots
private:
bool _continue;
public:
_continue = true;
}
inline bool stopped() {
return !_continue;
}
// iteration has terminated
if (stopped()) {
return;
}
// ignore null or deleted handles
return;
}
// many roots are Klasses so we use the java mirror
if (o->is_klass()) {
if (o == NULL) {
// Classes without mirrors don't correspond to real Java
// classes so just ignore them.
return;
}
} else {
// SystemDictionary::always_strong_oops_do reports the application
// class loader as a root. We want this root to be reported as
// a root kind of "OTHER" rather than "SYSTEM_CLASS".
}
}
// some objects are ignored - in the case of simple
// roots it's mostly Symbol*s that we are skipping
// here.
if (!ServiceUtil::visible_oop(o)) {
return;
}
// invoke the callback
}
};
// A supporting closure used to process JNI locals
private:
bool _continue;
public:
_continue = true;
}
inline bool stopped() {
return !_continue;
}
// iteration has terminated
if (stopped()) {
return;
}
// ignore null or deleted handles
return;
}
if (!ServiceUtil::visible_oop(o)) {
return;
}
// invoke the callback
}
};
// A VM operation to iterate over objects that are reachable from
// a set of roots or an initial object.
//
// For VM_HeapWalkOperation the set of roots used is :-
//
// - All JNI global references
// - All inflated monitors
// - All classes loaded by the boot class loader (or all classes
// in the event that class unloading is disabled)
// - All java threads
// - For each java thread then all locals and JNI local references
// on the thread's execution stack
// - All visible/explainable objects from Universes::oops_do
//
private:
enum {
};
bool _reporting_string_values;
}
// accessors
// iterate over the various object types
inline bool iterate_over_array(oop o);
inline bool iterate_over_type_array(oop o);
inline bool iterate_over_class(klassOop o);
inline bool iterate_over_object(oop o);
// root collection
inline bool collect_simple_roots();
inline bool collect_stack_roots();
// visit an object
public:
const void* user_data);
const void* user_data);
void doit();
};
const void* user_data) {
_is_advanced_heap_walk = false;
_reporting_primitive_fields = false;
_reporting_primitive_array_values = false;
_reporting_string_values = false;
}
const void* user_data) {
_is_advanced_heap_walk = true;
_following_object_refs = true;
}
if (_following_object_refs) {
delete _visit_stack;
_visit_stack = NULL;
}
}
// an array references its class and has a reference to
// each element in the array
// filtered out
return true;
}
// array reference to its class
return false;
}
// iterate over the array and report each reference to a
// non-null element
continue;
}
// report the array reference o[index] = elem
return false;
}
}
return true;
}
// a type array references its class
return false;
}
// report the array contents if required
if (is_reporting_primitive_array_values()) {
if (!CallbackInvoker::report_primitive_array_values(o)) {
return false;
}
}
return true;
}
// verify that a static oop field is in range
return true;
} else {
return false;
}
}
// a class references its super class, interfaces, class loader, ...
// and finally its static fields
int i;
if (klass->oop_is_instance()) {
// ignore the class if it's has been initialized yet
return true;
}
// get the java mirror
// super (only if something more interesting than java.lang.Object)
return false;
}
}
// class loader
return false;
}
}
// protection domain
return false;
}
}
// signers
return false;
}
}
// references from the constant pool
{
} else {
}
return false;
}
}
}
}
// interfaces
// (These will already have been reported as references from the constant pool
// but are specified by IterateOverReachableObjects and must be reported).
for (i = 0; i < interfaces->length(); i++) {
continue;
}
return false;
}
}
// iterate over the static fields
for (i=0; i<field_map->field_count(); i++) {
if (!is_primitive_field_type(type)) {
delete field_map;
return false;
}
}
} else {
if (is_reporting_primitive_fields()) {
delete field_map;
return false;
}
}
}
}
delete field_map;
return true;
}
return true;
}
// an object references a class and its instance fields
// (static fields are ignored here as we report these as
// references from the class).
// reference to the class
return false;
}
// iterate over instance fields
for (int i=0; i<field_map->field_count(); i++) {
if (!is_primitive_field_type(type)) {
// ignore any objects that aren't visible to profiler
// reflection code may have a reference to a klassOop.
// - see sun.reflect.UnsafeStaticFieldAccessorImpl and sun.misc.Unsafe
}
return false;
}
}
} else {
if (is_reporting_primitive_fields()) {
// primitive instance field
return false;
}
}
}
}
// if the object is a java.lang.String
if (is_reporting_string_values() &&
if (!CallbackInvoker::report_string_value(o)) {
return false;
}
}
return true;
}
// Collects all simple (non-stack) roots except for threads;
// threads are handled in collect_stack_roots() as an optimization.
// if there's a heap root callback provided then the callback is
// invoked for each simple root.
// if an object reference callback is provided then all simple
// roots are pushed onto the marking stack so that they can be
// processed later
//
// JNI globals
return false;
}
// Preloaded classes and loader from the system dictionary
return false;
}
// Inflated monitors
return false;
}
// threads are now handled in collect_stack_roots()
// Other kinds of roots maintained by HotSpot
// Many of these won't be visible but others (such as instances of important
// exceptions) will be visible.
// If there are any non-perm roots in the code cache, visit them.
return true;
}
// Walk the stack of a given thread and find all references (locals
// and JNI calls) and report these as stack references
{
// only need to get the thread's tag once per thread
// also need the thread id
if (java_thread->has_last_Java_frame()) {
// vframes are resource allocated
bool is_top_frame = true;
int depth = 0;
if (vf->is_java_frame()) {
// java frame (interpreted, compiled, ...)
// the jmethodID
if (o == NULL) {
continue;
}
// stack reference
return false;
}
}
}
} else {
if (is_top_frame) {
// JNI locals for the top frame.
} else {
if (last_entry_frame != NULL) {
// JNI locals for the entry frame
}
}
}
depth++;
} else {
// externalVFrame - for an entry frame then we report the JNI locals
// when we find the corresponding javaVFrame
if (fr->is_entry_frame()) {
}
}
is_top_frame = false;
}
} else {
// no last java frame but there may be JNI locals
}
return true;
}
// Collects the simple roots for all threads and collects all
// stack roots - for each thread it walks the execution
// stack to find all references and local JNI refs.
// Collect the simple root for this thread before we
// collect its stack roots
threadObj)) {
return false;
}
return false;
}
}
}
return true;
}
// visit an object
// first mark the object as visited
// second get all the outbound references from this object (in other words, all
// the objects referenced by this object).
//
// mark object as visited
ObjectMarker::mark(o);
// instance
if (o->is_instance()) {
o = klassOop_if_java_lang_Class(o);
if (o->is_klass()) {
// a java.lang.Class
return iterate_over_class(klassOop(o));
}
} else {
return iterate_over_object(o);
}
}
// object array
if (o->is_objArray()) {
return iterate_over_array(o);
}
// type array
if (o->is_typeArray()) {
return iterate_over_type_array(o);
}
return true;
}
// the heap walk starts with an initial object or the heap roots
if (initial_object().is_null()) {
// If either collect_stack_roots() or collect_simple_roots()
// returns false at this point, then there are no mark bits
// to reset.
ObjectMarker::set_needs_reset(false);
// Calling collect_stack_roots() before collect_simple_roots()
// can result in a big performance boost for an agent that is
// focused on analyzing references in the thread stacks.
if (!collect_stack_roots()) return;
if (!collect_simple_roots()) return;
// no early return so enable heap traversal to reset the mark bits
ObjectMarker::set_needs_reset(true);
} else {
}
// object references required
if (is_following_references()) {
// visit each object until all reachable objects have been
// visited or the callback asked to terminate the iteration.
while (!visit_stack()->is_empty()) {
if (!ObjectMarker::visited(o)) {
if (!visit(o)) {
break;
}
}
}
}
}
// iterate over all objects that are reachable from a set of roots
const void* user_data) {
}
// iterate over all objects that are reachable from a given object
const void* user_data) {
}
// follow references from an initial object or the GC roots
const jvmtiHeapCallbacks* callbacks,
const void* user_data)
{
}
// No locks during VM bring-up (0 threads) and no safepoints after main
// thread creation and before VMThread creation (1 thread); initial GC
// verification can happen in that window which gets to here.
"must be executed at a safepoint");
if (JvmtiEnv::environments_might_exist()) {
}
}
}
}
// does this environment have the OBJECT_FREE event enabled
// counters used for trace message
int freed = 0;
int moved = 0;
// reenable sizing (if disabled)
hashmap->set_resizing_enabled(true);
// if the hashmap is empty then we can skip it
if (hashmap->_entry_count == 0) {
return;
}
// now iterate through each entry in the table
// has object been GC'ed
// grab the tag
// remove GC'ed entry from hashmap and return the
// entry to the free list
// post the event to the profiler
if (post_object_free) {
}
++freed;
} else {
// if the object has moved then re-hash it and move its
// entry to its new location.
} else {
}
} else {
// Delay adding this entry to it's new position as we'd end up
// hitting it again during this iteration.
delayed_add = entry;
}
moved++;
} else {
// object didn't move
}
}
}
}
// Re-add all the entries which were kept aside
while (delayed_add != NULL) {
delayed_add = next;
}
// stats
if (TraceJVMTIObjectTagging) {
}
}