/*
* Copyright (c) 2010, 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.method;
import java.io.*;
import java.lang.ref.*;
import java.text.*;
import java.util.*;
import com.sun.max.lang.*;
import com.sun.max.tele.*;
import com.sun.max.tele.object.*;
import com.sun.max.tele.object.TeleTargetMethod.CodeCacheReferenceKind;
import com.sun.max.tele.reference.*;
import com.sun.max.tele.util.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.code.*;
import com.sun.max.vm.compiler.target.*;
import com.sun.max.vm.heap.*;
/**
* A manager for remote references to objects allocated in a {@link SemiSpaceCodeRegion}. This manager:
* <ul>
* <li>assumes that objects can be relocated and eventually collected/evicted.</li>
* <li>assumes that there can only
* be objects in the region of the kinds enumerated by {@link CodeCacheReferenceKind}, which
* are pointed to by corresponding fields in a {@link TeleMethodActor}.</li>
* <li>creates <em>canonical references</em>.</li>
* </ul>
* <p>
* This implementation depends on knowledge of the internal workings of {@link TargetMethod}.
*
* @see TargetMethod
* @see VmCodeCacheRegion
* @see TeleTargetMethod
*/
final class SemispaceCodeCacheRemoteReferenceManager extends AbstractVmHolder implements RemoteObjectReferenceManager {
/**
* The code cache region whose objects are being managed.
*/
private final VmSemiSpaceCodeCacheRegion semispaceCodeCacheRegion;
/**
* The status of the region with respect to object management.
*/
private HeapPhase heapPhase;
/**
* A two level map. For each of the possible kinds of references that can be created,
* record the ones we've created, indexed by TeleTargetMethod
* <pre>
* CodeCacheReferenceKind --> [ TeleTargetMethod --> WeakReference<TeleReference>]
* </pre>
*/
private final Map<CodeCacheReferenceKind, Map<TeleTargetMethod, WeakReference<SemispaceCodeCacheRemoteReference> > > refMaps =
new HashMap<CodeCacheReferenceKind, Map<TeleTargetMethod, WeakReference<SemispaceCodeCacheRemoteReference> > >();
/**
* Creates a manager for objects allocated in a {@link SemiSpaceCodeRegion}.
*/
public SemispaceCodeCacheRemoteReferenceManager(TeleVM vm, VmSemiSpaceCodeCacheRegion semispaceCodeCacheRegion) {
super(vm);
this.semispaceCodeCacheRegion = semispaceCodeCacheRegion;
this.heapPhase = HeapPhase.MUTATING;
// Create a separate map for references of each kind
for (CodeCacheReferenceKind kind : CodeCacheReferenceKind.values()) {
refMaps.put(kind, new HashMap<TeleTargetMethod, WeakReference<SemispaceCodeCacheRemoteReference> >());
}
}
// TODO (mlvdv) Interpret this status for the special case of objects in the code cache.
public HeapPhase phase() {
return heapPhase;
}
/**
* {@inheritDoc}
* <p>
* We don't need a heuristic for objects here; if they are present, then they are
* pointed at by one of the fields in the {@link TargetMethod}.
*/
public ObjectStatus objectStatusAt(Address origin) throws TeleError {
TeleError.check(semispaceCodeCacheRegion.memoryRegion().contains(origin), "Location is outside region");
final TeleCompilation compilation = semispaceCodeCacheRegion.findCompilation(origin);
if (compilation != null) {
final TeleTargetMethod teleTargetMethod = compilation.teleTargetMethod();
if (teleTargetMethod != null) {
// The address is contained in the code cache allocation for this target method.
for (CodeCacheReferenceKind kind : CodeCacheReferenceKind.values()) {
// Does one of the target method's references point at this location??
final Address objectOrigin = teleTargetMethod.codeCacheObjectOrigin(kind);
if (objectOrigin != null && objectOrigin.equals(origin)) {
// The specified location matches one of the target method's pointers.
// There should be an object there, but check just in case.
assert objects().isPlausibleOriginUnsafe(objectOrigin);
return ObjectStatus.LIVE;
}
}
}
}
return ObjectStatus.DEAD;
}
public boolean isForwardingAddress(Address forwardingAddress) {
return false;
}
@Override
public RemoteReference makeReference(Address origin) throws TeleError {
assert vm().lockHeldByCurrentThread();
TeleError.check(semispaceCodeCacheRegion.contains(origin));
// Locate the compilation, if any, whose code cache allocation in VM memory includes the address
final TeleCompilation compilation = semispaceCodeCacheRegion.findCompilation(origin);
if (compilation != null) {
final TeleTargetMethod teleTargetMethod = compilation.teleTargetMethod();
if (teleTargetMethod != null) {
// The address is contained in the code cache allocation for this target method.
for (CodeCacheReferenceKind kind : CodeCacheReferenceKind.values()) {
// Does one of the target method's references point at this location??
final Address objectOrigin = teleTargetMethod.codeCacheObjectOrigin(kind);
if (objectOrigin != null && origin.equals(objectOrigin)) {
// Return a canonical reference to this location
return makeCanonicalReference(teleTargetMethod, kind);
}
}
}
}
return null;
}
/**
* {@inheritDoc}
* <p>
* There are no <em>quasi</em> objects in this kind of region.
*/
public RemoteReference makeQuasiReference(Address origin) throws TeleError {
return null;
}
private int activeReferenceCount() {
int count = 0;
for (CodeCacheReferenceKind kind : CodeCacheReferenceKind.values()) {
final Map<TeleTargetMethod, WeakReference<SemispaceCodeCacheRemoteReference> > kindRefMap = refMaps.get(kind);
for (WeakReference<SemispaceCodeCacheRemoteReference> weakRef : kindRefMap.values()) {
if (weakRef != null) {
final SemispaceCodeCacheRemoteReference teleRef = weakRef.get();
if (teleRef != null) {
count++;
}
}
}
}
return count;
}
private int totalReferenceCount() {
int count = 0;
for (CodeCacheReferenceKind kind : CodeCacheReferenceKind.values()) {
count += refMaps.get(kind).size();
}
return count;
}
public void printObjectSessionStats(PrintStream printStream, int indent, boolean verbose) {
final String indentation = Strings.times(' ', indent);
printStream.println(indentation + "Object holding region: " + semispaceCodeCacheRegion.entityName());
final NumberFormat formatter = NumberFormat.getInstance();
final StringBuilder sb2 = new StringBuilder();
final int activeReferenceCount = activeReferenceCount();
final int totalReferenceCount = totalReferenceCount();
sb2.append("object refs: active=" + formatter.format(activeReferenceCount));
sb2.append(", inactive=" + formatter.format(totalReferenceCount - activeReferenceCount));
sb2.append(", mgr=" + getClass().getSimpleName());
printStream.println(indentation + sb2.toString());
}
/**
* @return a canonical reference of the specified kind for the specified target method
*/
private RemoteReference makeCanonicalReference(TeleTargetMethod teleTargetMethod, CodeCacheReferenceKind kind) {
SemispaceCodeCacheRemoteReference remoteRef = null;
final Map<TeleTargetMethod, WeakReference<SemispaceCodeCacheRemoteReference> > kindMap = refMaps.get(kind);
WeakReference<SemispaceCodeCacheRemoteReference> weakRef = kindMap.get(teleTargetMethod);
if (weakRef != null) {
remoteRef = weakRef.get();
}
if (remoteRef == null) {
// By construction, there should be an object at the location; let's just check.
assert objects().isPlausibleOriginUnsafe(teleTargetMethod.codeCacheObjectOrigin(kind));
remoteRef = new SemispaceCodeCacheRemoteReference(vm(), teleTargetMethod, kind);
kindMap.put(teleTargetMethod, new WeakReference<SemispaceCodeCacheRemoteReference>(remoteRef));
}
return remoteRef;
}
/**
* A remote object reference constrained to point only at data stored in object format in a region of code cache. In
* particular, it may point only at one of the three possible data arrays pointed at by an instance of
* {@link TargetMethod} in the VM.
* <p>
* The current code eviction algorithm marks the three fields that may contain references to data arrays when the
* method is evicted; it does so by assigning to them a sentinel reference to an empty array that lives in the boot
* heap.
*
* @see TargetMethod
* @see CodeEviction
*/
private final class SemispaceCodeCacheRemoteReference extends AbstractCodeCacheRemoteReference {
private final CodeCacheReferenceKind kind;
private Address origin = Address.zero();
private ObjectStatus status = ObjectStatus.LIVE;
public SemispaceCodeCacheRemoteReference(TeleVM vm, TeleTargetMethod teleTargetMethod, CodeCacheReferenceKind kind) {
super(vm, teleTargetMethod);
this.origin = teleTargetMethod.codeCacheObjectOrigin(kind);
this.kind = kind;
}
@Override
public ObjectStatus status() {
// References to objects in the code cache are treated for now as either
// LIVE or DEAD.
if (status.isLive() && teleTargetMethod().isCodeEvicted()) {
status = ObjectStatus.DEAD;
}
return status;
}
@Override
public ObjectStatus priorStatus() {
return status == ObjectStatus.DEAD ? ObjectStatus.LIVE : null;
}
// TODO (mlvdv) we actually need only check the origin if we can determine that there has
// been an eviction since the last time we checked (once we have a non-zero origin in the first
// place. Access to that information hasn't been arranged yet.
/**
* {@inheritDoc}
* <p>
* Return the actual address of the array in the code cache, even after it has been reassigned
* when the method's code is <em>wiped</em> during eviction.
*/
@Override
public Address origin() {
if (status().isDead()) {
return Address.zero();
}
origin = teleTargetMethod().codeCacheObjectOrigin(kind);
return origin;
}
/**
* {@inheritDoc}
* <p>
* Objects in code cache allocations may be relocated, but they are never <em>forwarded</em>
* in the usual GC sense.
*/
@Override
public Address forwardedFrom() {
return Address.zero();
}
/**
* {@inheritDoc}
* <p>
* Objects in code cache allocations may be relocated, but they are never <em>forwarded</em>
* in the usual GC sense.
*/
@Override
public Address forwardedTo() {
return Address.zero();
}
@Override
public String gcDescription() {
return "object in a semispace managed code cache region: " + kind.label();
}
}
}