/*
* Copyright (c) 2008, 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.object;
import java.lang.management.*;
import com.sun.max.memory.*;
import com.sun.max.program.*;
import com.sun.max.tele.*;
import com.sun.max.tele.data.*;
import com.sun.max.tele.reference.*;
import com.sun.max.tele.util.*;
import com.sun.max.unsafe.*;
/**
* Canonical surrogate for a {@link MemoryRegion} object in the VM, which represents a region of VM memory.
* <p>
* Usage defaults to 100%.
*
* @see MemoryRegion
*/
public class TeleMemoryRegion extends TeleTupleObject {
private static final int TRACE_VALUE = 2;
private Address regionStartCache = Address.zero();
private long nBytesCache = 0L;
private String regionNameCache = null;
private MemoryUsage memoryUsageCache = MaxMemoryRegion.Util.NULL_MEMORY_USAGE;
private final Object localStatsPrinter = new Object() {
@Override
public String toString() {
return "region name=" + (regionNameCache == null ? "<unassigned>" : regionNameCache);
}
};
TeleMemoryRegion(TeleVM vm, RemoteReference runtimeMemoryRegionReference) {
super(vm, runtimeMemoryRegionReference);
TimedTrace timedTrace = new TimedTrace(TRACE_VALUE, tracePrefix() + "Initializing");
timedTrace.begin();
// Exception to general policy of not doing too much in constructors; some subclasses
// need to have the basic location information available at construction time.
updateRegionInfoCache();
timedTrace.end(localStatsPrinter);
}
/** {@inheritDoc}
* <p>
* Optimized for certain kinds of regions that describe non-relocatable memory
* allocations in the VM; in those cases, once location and name information
* are read, then further cache updates are skipped.
*/
@Override
protected boolean updateObjectCache(long epoch, StatsPrinter statsPrinter) {
if (!super.updateObjectCache(epoch, statsPrinter)) {
return false;
}
statsPrinter.addStat(localStatsPrinter);
if (!isRelocatable() && isAllocated() && regionNameCache != null) {
statsPrinter.addStat("allocated & not relocatable, no location refresh needed");
return true;
}
return updateRegionInfoCache();
}
@Override
public String maxineRole() {
return getRegionName();
}
/**
* Attempts to read information about the region from the {@link MemoryRegion} object in VM memory.
*/
private boolean updateRegionInfoCache() {
try {
final long nBytes = fields().MemoryRegion_size.readWord(reference()).asSize().toLong();
if (nBytes < 0L) {
TeleWarning.message("Incorrect Memory size read at " + reference());
return false;
}
final RemoteReference regionNameStringReference = fields().MemoryRegion_regionName.readRemoteReference(reference());
final TeleString teleString = (TeleString) objects().makeTeleObject(regionNameStringReference);
final String regionName = teleString == null ? "<null>" : teleString.getString();
Address regionStart = fields().MemoryRegion_start.readWord(reference()).asAddress();
if (regionStart.isZero() && regionName != null && regionName.equals(heap().bootHeapRegionName())) {
// Ugly special case: the regionStart field of the static that defines the boot heap region
// is set at zero in the boot image and only gets set to the real value when the VM starts running.
// We lie in this situation.
regionStart = vm().bootImageStart();
}
if (regionStart.isZero()) {
Trace.line(TRACE_VALUE, tracePrefix() + "zero start address read from VM for region " + this);
}
// Wait until everything read before updating (crude atomicity).
this.regionStartCache = regionStart;
this.nBytesCache = nBytes;
this.regionNameCache = regionName;
if (nBytesCache != memoryUsageCache.getUsed()) {
this.memoryUsageCache = MaxMemoryRegion.Util.defaultUsage(nBytesCache);
}
} catch (DataIOError dataIOError) {
TeleWarning.message("TeleRuntimeMemoryRegion dataIOError:", dataIOError);
dataIOError.printStackTrace();
return false;
// No update; data unreadable for some reason
// TODO (mlvdv) replace this with a more general mechanism for responding to VM unavailable
}
return true;
}
/**
* @return the descriptive name assigned to the region described by a {@link MemoryRegion} object in the VM;
*/
public final String getRegionName() {
return regionNameCache;
}
/**
* @return starting location of the region described by a {@link MemoryRegion} object in the VM;
* zero if not yet allocated.
*/
public final Address getRegionStart() {
return regionStartCache;
}
/**
* @return the size of the VM memory in bytes described by a {@link MemoryRegion} object in the VM.
*/
public long getRegionNBytes() {
return nBytesCache;
}
/**
* @return the end location of the VM memory region described by a {@link MemoryRegion} object in the VM.
*/
public final Address getRegionEnd() {
return getRegionStart().plus(getRegionNBytes());
}
/**
* Computes the usage of the the memory region described by a {@link MemoryRegion} object in the VM.
* <br>
* The default is to assume 100% utilized,
* but specific subclasses may have more refined information available.
* <br>
* Returns {@link MaxMemoryRegion.Util#NULL_MEMORY_USAGE} if no information available.
*/
public MemoryUsage getUsage() {
return memoryUsageCache;
}
/**
* Determines whether an address is in the region of VM memory described by this object.
*
* @param address a location in VM memory
* @return whether the location is in this region
*/
public final boolean contains(Address address) {
return address.greaterEqual(getRegionStart()) && address.lessThan(getRegionEnd());
}
/**
* @return the allocation mark for the region; {@code null} if not linearly allocated.
*/
public Address mark() {
return null;
}
/**
* Determines whether an address is in the allocated portion of the {@link MemoryRegion}
* described by a memory region object in the VM.
* <br>
* The default is to assume that all of the region is allocated, but
* specific subclasses may have more refined information available.
*/
public boolean containsInAllocated(Address address) {
return isAllocated() ? contains(address) : false;
}
/**
* @return whether memory has been allocated yet for the {@link MemoryRegion}
* described by a memory region object in the VM.
*/
public final boolean isAllocated() {
return getRegionStart().isNotZero() && getRegionNBytes() > 0;
}
/**
* Determines whether this memory region, once allocated, should be checked for relocation. If {@code true} then the
* location needs to be checked on every refresh cycle. If {@code false} then the check can be skipped.
* <p>
* Assume conservatively that the region is relocatable, although important subclasses (notably {@link TeleTargetMethod}}
* can exploit specific knowledge to avoid the test.
*
* @return whether this memory region, described by a {@link MemoryRegion} object in the VM, should be assume to be
* relocatable.
*/
protected boolean isRelocatable() {
return true;
}
}