/* * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * 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. */ package com.sun.max.vm.ext.jvmti; import static com.sun.max.vm.VMConfiguration.*; import static com.sun.max.vm.ext.jvmti.JVMTICallbacks.*; import static com.sun.max.vm.ext.jvmti.JVMTIConstants.*; import static com.sun.max.vm.ext.jvmti.JVMTIUtil.*; import com.sun.max.unsafe.*; import com.sun.max.vm.*; import com.sun.max.vm.actor.holder.*; import com.sun.max.vm.heap.*; import com.sun.max.vm.layout.*; import com.sun.max.vm.object.*; import com.sun.max.vm.reference.*; import com.sun.max.vm.runtime.*; /** * All the JVMTI functions that relate to the heap. */ public class JVMTIHeapFunctions { /** * * Must be consistent with {@code struct jvmtiHeapCallbacks} in jvmti.c. */ static enum HeapCallbacks { HEAP_ITERATION(0), HEAP_REFERENCE(8), PRIMITIVE_FIELD(16), ARRAY_PRIMITIVE_VALUE(24), STRING_PRIMITIVE_VALUE(32); private final int offset; HeapCallbacks(int offset) { this.offset = offset; } Word getCallback(Pointer struct) { return struct.readWord(offset); } } /** A {@link VmOperation} that prevents any allocgtion while we walk the heap. * We don't care about the threads, just that they are blocked, so we * override the {@link VmOperation#doIt} method. */ static class IterateThroughHeapVmOperation extends VmOperation { private final CBCV cbcv; abstract class CBCV extends CallbackCellVisitor { protected final JVMTI.Env env; protected final int heapFilter; protected final Class klass; CBCV(JVMTI.Env env, int heapFilter, Class klass) { this.env = env; this.heapFilter = heapFilter; this.klass = klass; } @Override protected boolean callback(Object object) { ClassActor classActor = ObjectAccess.readClassActor(object); ClassActorProxy proxyClassActor = ClassActorProxy.asClassActorProxy(classActor); /* To avoid the tricky case where we encounter an object whose Class mirror * has not been set in the ClassActor yet, which would require allocation * we check the field in classActor directly and observe that such * an object cannot have been tagged, otherwise its class mirror would be set. */ Class objectClass = proxyClassActor.javaClass == null ? null : proxyClassActor.javaClass; if (klass != null && objectClass != klass) { return true; } if ((heapFilter & JVMTI_HEAP_FILTER_CLASS_TAGGED) != 0) { if (objectClass != null && env.tags.isTagged(objectClass)) { return true; } } if ((heapFilter & JVMTI_HEAP_FILTER_CLASS_UNTAGGED) != 0) { if (!(objectClass != null && env.tags.isTagged(objectClass))) { return true; } } if ((heapFilter & JVMTI_HEAP_FILTER_TAGGED) != 0) { if (env.tags.isTagged(object)) { return true; } } if ((heapFilter & JVMTI_HEAP_FILTER_UNTAGGED) != 0) { if (!env.tags.isTagged(object)) { return true; } } int flags = doCallback(object, objectClass); if ((flags & JVMTI_VISIT_ABORT) != 0) { return false; } return true; } protected abstract int doCallback(Object object, Class objectClass); } class CBCVNative extends CBCV { private final Pointer callbacks; private final Word userData; CBCVNative(JVMTI.Env env, int heapFilter, Class klass, Pointer callbacks, Word userData) { super(env, heapFilter, klass); this.callbacks = callbacks; this.userData = userData; } @Override protected int doCallback(Object object, Class objectClass) { Pointer tagPtr = Intrinsics.alloca(Word.size(), false); Reference objectRef = Reference.fromJava(object); Word heapIterationCallback = HeapCallbacks.HEAP_ITERATION.getCallback(callbacks); if (!heapIterationCallback.isZero()) { long tag = env.tags.getLongTag(object); tagPtr.setLong(tag); int flags = invokeHeapIterationCallback( heapIterationCallback.asPointer(), objectClass == null ? 0 : env.tags.getLongTag(objectClass), Layout.size(objectRef).toInt(), tagPtr, Layout.isArray(objectRef) ? Layout.readArrayLength(objectRef) : -1, userData); long newTag = tagPtr.getLong(); if (newTag != tag) { env.tags.setTag(object, newTag); } return flags; } return 0; } } class CBCVJava extends CBCV { protected final JJVMTI.HeapCallbacks heapCallbacks; protected final Object userData; CBCVJava(JVMTI.Env env, int heapFilter, Class klass, JJVMTI.HeapCallbacks heapCallbacks, Object userData) { super(env, heapFilter, klass); this.heapCallbacks = heapCallbacks; this.userData = userData; } @Override protected int doCallback(Object object, Class objectClass) { Reference objectRef = Reference.fromJava(object); return heapCallbacks.heapIteration(objectClass == null ? 0 : env.tags.getObjectTag(objectClass), Layout.size(objectRef).toInt(), env.tags.getObjectTag(object), Layout.isArray(objectRef) ? Layout.readArrayLength(objectRef) : -1, objectClass); } } class CBCVJavaMax extends CBCVJava { CBCVJavaMax(JVMTI.Env env, int heapFilter, Class klass, JJVMTI.HeapCallbacks heapCallbacks, Object userData) { super(env, heapFilter, klass, heapCallbacks, userData); } @Override protected int doCallback(Object object, Class objectClass) { return heapCallbacks.heapIterationMax(object, userData); } } IterateThroughHeapVmOperation(JVMTI.Env env, int heapFilter, Class klass, Pointer callbacks, Word userData) { super("JVMTI_IterateThroughHeap", null, Mode.Safepoint, false); this.cbcv = new CBCVNative(env, heapFilter, klass, callbacks, userData); } IterateThroughHeapVmOperation(JVMTI.Env env, int heapFilter, Class klass, JJVMTI.HeapCallbacks heapCallbacks, Object userData) { super("JVMTI_IterateThroughHeap", null, Mode.Safepoint, false); this.cbcv = new CBCVJava(env, heapFilter, klass, heapCallbacks, userData); } IterateThroughHeapVmOperation(JVMTI.Env env, int heapFilter, Class klass, JJVMTI.HeapCallbacks heapCallbacks, Object userData, boolean max) { super("JVMTI_IterateThroughHeapMax", null, Mode.Safepoint, false); this.cbcv = new CBCVJavaMax(env, heapFilter, klass, heapCallbacks, userData); } @Override protected void doIt() { // Ideally there should be no allocation in this path, at least in the "application" heap. // However, currently, Maxine has a unified heap and, at least for the Java JVMTI agents, // it turns out to be essentially impossible to guarantee no allocation owing to hidden // allocations in the VM itself. So we use the immortal heap for now. // TODO revisit this when Maxine addresses VM and application heap separation try { Heap.enableImmortalMemoryAllocation(); vmConfig().heapScheme().walkHeap(cbcv); } finally { Heap.disableImmortalMemoryAllocation(); } } } static int iterateThroughHeap(JVMTI.Env jvmtiEnv, int heapFilter, Class klass, Pointer callbacks, Pointer userData) { IterateThroughHeapVmOperation op = new IterateThroughHeapVmOperation(jvmtiEnv, heapFilter, klass, callbacks, userData); op.submit(); return JVMTI_ERROR_NONE; } static void iterateThroughHeap(JVMTI.Env jvmtiEnv, int heapFilter, ClassActor klass, JJVMTI.HeapCallbacks heapCallbacks, Object userData) { IterateThroughHeapVmOperation op = new IterateThroughHeapVmOperation(jvmtiEnv, heapFilter, klass == null ? null : klass.toJava(), heapCallbacks, userData); op.submit(); } static void iterateThroughHeapMax(JVMTI.Env jvmtiEnv, int heapFilter, ClassActor klass, JJVMTI.HeapCallbacks heapCallbacks, Object userData) { IterateThroughHeapVmOperation op = new IterateThroughHeapVmOperation(jvmtiEnv, heapFilter, klass == null ? null : klass.toJava(), heapCallbacks, userData, true); op.submit(); } }