/*
* Copyright (c) 2007, 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.tele.reference;
import com.sun.max.tele.*;
import com.sun.max.tele.object.*;
import com.sun.max.tele.reference.direct.*;
import com.sun.max.tele.type.*;
import com.sun.max.tele.value.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.reference.*;
import com.sun.max.vm.value.*;
/**
* The singleton manager for instances of {@link Reference} that point (or pretend to point) at
* objects in the VM.
* <p>
* This is a work in progress; part of an evolution toward modeling correctly the
* generality of object representations in the VM.
* <p>
* A <strong>raw reference</strong> is an {@link Address} in VM memory where the object is currently
* located. However, the location may be subject to change by GC, so the raw reference may change over time.
* <p>
* References are intended to be canonical, i.e. refer to only one object. However, in the course of inspection
* duplicates may appear. These are resolved at the conclusion of each GC.
*/
public final class VmReferenceManager extends AbstractVmHolder {
private static final int TRACE_VALUE = 1;
private static VmReferenceManager vmReferenceManager;
public static VmReferenceManager make(TeleVM vm, RemoteReferenceScheme referenceScheme) {
if (vmReferenceManager == null) {
vmReferenceManager = new VmReferenceManager(vm, referenceScheme);
}
return vmReferenceManager;
}
private final RemoteReferenceScheme referenceScheme;
private VmReferenceManager(TeleVM vm, RemoteReferenceScheme referenceScheme) {
super(vm);
this.referenceScheme = referenceScheme;
referenceScheme.setContext(vm);
}
/**
* Checks that a {@link RemoteReference} points to a live heap object in the VM;
* throws an unchecked exception if not. This is a low-level method
* that uses a debugging tag or (if no tags in image) a heuristic; it does
* not require access to the {@link VmClassAccess}.
*
* @param reference memory location in the VM
* @throws InvalidReferenceException when the location does <strong>not</strong> point
* at a valid heap object.
*/
public void checkReference(RemoteReference reference) throws InvalidReferenceException {
if (!objects().objectStatusAt(reference.toOrigin()).isLive()) {
throw new InvalidReferenceException(reference);
}
}
/**
* Gets the location of an object's origin in VM memory.
*
* @param reference a remote reference to a VM object
* @return a VM memory location that is the object's origin;
* {@linkplain Address#zero()} if the reference is {@linkplain ObjectStatus#DEAD DEAD}.
*/
public Address toOrigin(RemoteReference reference) {
return referenceScheme.toOrigin(reference);
}
/**
* Gets a non-canonical instance of {@link RemoteReference} that represents the {@code null} remote object reference.
*
* @return the default instance of a zero reference
*/
public RemoteReference zeroReference() {
return (RemoteReference) referenceScheme.zero();
}
// /**
// * Returns some kind of reference associated with the given raw reference in the VM, depending
// * on what is known about the address.
// * <ol>
// * <li>If a canonical reference pointing at that location already exists, then returns it.</li>
// * <li>If the address is the valid origin of an object in a <strong>non-collected</strong> heap region, for
// * example the boot heap or an immortal heap, then return a new reference that is canonical,
// * but which is not tracked for possible GC relocation.</li>
// * <li>If the address is the valid origin of a live object in a dynamic heap region, then return
// * a new reference that is canonical and which is tracked for possible GC relocation.</li>
// * <li>If the address is the valid origin of an object in a dynamic heap region, but the object
// * is known <strong>not</strong> to be live, then return an unsafe, temporary reference that
// * wraps the address.</li>
// * <li>If the address does not point an an object origin, then return an unsafe, temporary reference that
// * wraps the address.</li>
// * </ol>
// *
// * @param address a memory location in VM memory
// * @return a special kind of {@link Reference} implementation that encapsulates a remote
// * location in VM memory, allowing the reuse of much VM code that deals with references.
// */
/**
* Creates a specialized instance of the VM's {@link Reference} class that can refer to live objects
* remotely in the VM. Each instance is specialized for the kind of object management that takes
* place in the memory region that contains the specified location, and in the case of managed
* regions, the {@link RemoteReference} tracks the object, just as in the VM itself.
*
* @param origin a location in VM Memory
* @return a reference to a live VM object, the zero {@link RemoteReference} if the specified location is {@link Address#zero()}
* or there is no live object at the location
*/
public RemoteReference makeReference(Address origin) {
if (origin.isZero()) {
return zeroReference();
}
vm().lock();
try {
final MaxEntityMemoryRegion<?> maxMemoryRegion = vm().addressSpace().find(origin);
if (maxMemoryRegion == null) {
// Not in any memory region we know about
if (vm().isAttaching() && objects().isPlausibleOriginUnsafe(origin)) {
return new ProvisionalRemoteReference(vm(), origin);
}
return referenceScheme.makeZeroReference("Null ref: address in no known memory region ", origin);
}
if (!(maxMemoryRegion.owner() instanceof VmObjectHoldingRegion<?>)) {
// In a region that isn't supposed to hold objects
return referenceScheme.makeZeroReference("Null ref: address in non-object holding region " + maxMemoryRegion.owner().entityName(), origin);
}
// In an object-holding region
final VmObjectHoldingRegion<?> objectHoldingRegion = (VmObjectHoldingRegion<?>) maxMemoryRegion.owner();
final RemoteReference liveObjectReference = objectHoldingRegion.objectReferenceManager().makeReference(origin);
if (liveObjectReference == null) {
// In an object holding region, but not an origin
return referenceScheme.makeZeroReference("Null ref: not a valid origin in " + objectHoldingRegion.entityName(), origin);
}
// A valid origin
return liveObjectReference;
} finally {
vm().unlock();
}
}
/**
* Creates a specialized instance of the VM's {@link Reference} class that can refer to <em>quasi</em> objects
* remotely in the VM. Each instance is specialized for the kind of object management that takes
* place in the memory region that contains the specified location.
*
* @param origin a location in VM Memory
* @return a reference to a <em>quasi</em> VM object, the zero {@link RemoteReference} if the specified location is {@link Address#zero()}
* or there is no quasi object at the location
*/
public RemoteReference makeQuasiReference(Address origin) {
if (origin.isZero()) {
return zeroReference();
}
vm().lock();
try {
final MaxEntityMemoryRegion<?> maxMemoryRegion = vm().addressSpace().find(origin);
if (maxMemoryRegion == null) {
// Not in any memory region we know about
if (vm().isAttaching() && objects().isPlausibleOriginUnsafe(origin)) {
return new ProvisionalRemoteReference(vm(), origin);
}
return referenceScheme.makeZeroReference("Null ref: address in no known memory region ", origin);
}
if (!(maxMemoryRegion.owner() instanceof VmObjectHoldingRegion<?>)) {
// In a region that isn't supposed to hold objects
return referenceScheme.makeZeroReference("Null ref: address in non-object holding region " + maxMemoryRegion.owner().entityName(), origin);
}
// In an object-holding region
final VmObjectHoldingRegion<?> objectHoldingRegion = (VmObjectHoldingRegion<?>) maxMemoryRegion.owner();
final RemoteReference quasiObjectReference = objectHoldingRegion.objectReferenceManager().makeQuasiReference(origin);
if (quasiObjectReference == null) {
// In an object holding region, but not an origin of a quasi object
return referenceScheme.makeZeroReference("Null ref: not a quasi object origin in " + objectHoldingRegion.entityName(), origin);
}
// Origin of a quasi object
return quasiObjectReference;
} finally {
vm().unlock();
}
}
/**
* Create a remote instance of {@link Reference} whose origin is at a given address, but without any checking that a
* valid object is at that address and without any support for possible relocation.
* <p>
* <strong>Unsafe:</strong> These are not canonical and should only be used for temporary, low level access to
* object state. They should not be retained across VM execution.
* <p>
* The object status is permanently {@link ObjectStatus#DEAD}.
*
* @param origin a constant location in VM memory about which almost nothing is guaranteed
* @return the address wrapped as a remote object reference for temporary use
*/
public RemoteReference makeTemporaryRemoteReference(Address origin) {
return new TemporaryRemoteReference(vm(), origin);
}
/**
* An unsafe {@link Reference} intended to wrap a fixed address that
* appears be the origin of a VM object, but which is not in any known
* memory region. This is intended to be used only in transient situations,
* where (for example during an attach) objects may be discovered before
* meta-information about the region that contains them has been discovered.
* <p>
* Memory status is permanently {@link ObjectStatus#LIVE}.
*
* @param origin a location in an unknown region of VM memory where an object appears to be stored
* @return the address wrapped as a reference for temporary use.
*/
public RemoteReference makeUnknownRemoteReference(Address origin) {
return new ProvisionalRemoteReference(vm(), origin);
}
/**
* Boxes the remote flavor of reference as a {@link ReferenceValue}.
*
* @param reference
* @return a boxed remote reference
*/
public ReferenceValue createReferenceValue(RemoteReference reference) {
return TeleReferenceValue.from(vm(), reference);
}
/**
* A constant reference intended for use when performing some computation on
* an {@link Address} that is implemented by re-using reference-based VM code.
*
* @see VmReferenceManager#makeTemporaryRemoteReference(Address)
*/
private static final class TemporaryRemoteReference extends ConstantRemoteReference {
TemporaryRemoteReference(TeleVM vm, Address origin) {
super(vm, origin);
}
@Override
public boolean isTemporary() {
return true;
}
@Override
public ObjectStatus status() {
return ObjectStatus.DEAD;
}
@Override
public ObjectStatus priorStatus() {
return null;
}
}
/**
* A constant reference intended to represent what appears to be a legitimate object but whose enclosing memory
* region and manger are so far unknown.
*
* @see VmReferenceManager#makeUnknownRemoteReference(Address)
*/
private static final class ProvisionalRemoteReference extends ConstantRemoteReference {
ProvisionalRemoteReference(TeleVM vm, Address origin) {
super(vm, origin);
}
@Override
public boolean isProvisional() {
return true;
}
@Override
public ObjectStatus status() {
return ObjectStatus.LIVE;
}
@Override
public ObjectStatus priorStatus() {
return null;
}
}
}