/*
* 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.debug;
import static com.sun.cri.ci.CiCallingConvention.Type.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import com.sun.cri.ci.*;
import com.sun.cri.ci.CiRegister.RegisterFlag;
import com.sun.max.program.*;
import com.sun.max.tele.*;
import com.sun.max.tele.debug.BreakpointCondition.ExpressionException;
import com.sun.max.tele.method.*;
import com.sun.max.tele.method.CodeLocation.BytecodeLocation;
import com.sun.max.tele.object.*;
import com.sun.max.tele.reference.*;
import com.sun.max.tele.util.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.actor.member.MethodKey.DefaultMethodKey;
import com.sun.max.vm.compiler.target.*;
import com.sun.max.vm.layout.*;
import com.sun.max.vm.tele.*;
/**
* A breakpoint located at the beginning of a bytecode instruction
* in a method in the VM.
* <p>
* When enabled, a bytecode breakpoint creates a machine code
* breakpoint in each compilation of the specified method. This
* is true for compilations that exist when the breakpoint is created,
* as well as all subsequent compilations. When
* disabled, all related target code breakpoints are removed.
* <p>
* Conditions are supported; they are set in each target code
* breakpoint created for this breakpoint.
*/
public final class VmBytecodeBreakpoint extends VmBreakpoint {
private static final int TRACE_VALUE = 1;
// Traces each compilation completed in the VM
private static final int COMPILATION_TRACE_VALUE = 1;
private static BytecodeBreakpointManager bytecodeBreakpointManager;
public static BytecodeBreakpointManager makeManager(TeleVM vm) {
if (bytecodeBreakpointManager == null) {
bytecodeBreakpointManager = new BytecodeBreakpointManager(vm);
}
return bytecodeBreakpointManager;
}
// Cached string representations of the three parts of a method key
// for fast comparison when comparing with a method key in the VM.
private final String holderTypeDescriptorString;
private final String methodName;
private final String signatureDescriptorString;
private boolean enabled = false;
// Breakpoint is unconditional by default.
private BreakpointCondition condition = null;
// Private key used by the manager.
private MethodPositionKey methodPositionKey;
/**
* All machine code breakpoints created in compilations of the method in the VM.
* Non-null if this breakpoint is enabled; null if disabled.
*/
private List<VmTargetBreakpoint> targetBreakpoints = null;
/**
* A new bytecode breakpoint, enabled by default, at a specified location.
*
* @param vm the VM
* @param kind the kind of breakpoint to create
* @param methodPositionKey an abstract description of the location for this breakpoint, expressed in terms of the method and bytecode offset.
*/
private VmBytecodeBreakpoint(TeleVM vm, CodeLocation codeLocation, BreakpointKind kind, MethodPositionKey methodPositionKey) {
super(vm, codeLocation, kind);
this.methodPositionKey = methodPositionKey;
final MethodKey methodKey = codeLocation.methodKey();
this.holderTypeDescriptorString = methodKey.holder().string;
this.methodName = methodKey.name().string;
this.signatureDescriptorString = methodKey.signature().string;
Trace.line(TRACE_VALUE, tracePrefix() + "new=" + this);
}
/**
* Creates a machine code breakpoint in a specific compilation of this method in the VM, at a location
* corresponding to the bytecode location for which this breakpoint was created. Note that in some
* cases there may be more than one.
*
* @param teleTargetMethod a compilation in the VM of the method for which this breakpoint was created.
* @throws MaxVMBusyException
*/
private void createTargetBreakpointForMethod(TeleTargetMethod teleTargetMethod) throws MaxVMBusyException {
assert enabled;
// Delegate creation of the target breakpoints to the manager.
final List<VmTargetBreakpoint> newTargetBreakpoints = bytecodeBreakpointManager.createTargetBreakpoints(this, teleTargetMethod);
if (newTargetBreakpoints.isEmpty()) {
// This will always return true in the current implementation of method entry breakpoints, because only
// transient breakpoints are created. They go away immediately after one execution cycle.
//TeleWarning.message(tracePrefix() + "failed to create targetBreakpoint for " + this);
} else {
// TODO (mlvdv) If we support conditions, need to combine it with the trigger handler added by factory method.
for (VmTargetBreakpoint newTargetBreakpoint : newTargetBreakpoints) {
targetBreakpoints.add(newTargetBreakpoint);
Trace.line(TRACE_VALUE, tracePrefix() + "created " + newTargetBreakpoint + " for " + this);
}
}
}
/**
* Handle notification that the method for which this breakpoint was created has just been compiled, possibly
* but not necessarily the first of more than one.
*
* @param teleTargetMethod a just completed compilation in the VM of the method for which this breakpoint was created.
* @throws MaxVMBusyException
*/
private void handleNewCompilation(TeleTargetMethod teleTargetMethod) throws MaxVMBusyException {
if (enabled) {
createTargetBreakpointForMethod(teleTargetMethod);
}
}
public boolean isBytecodeBreakpoint() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public void setEnabled(boolean enabled) throws MaxVMBusyException {
if (this.enabled == enabled) {
final StringBuffer sb = new StringBuffer();
sb.append("VmBytecodeBreakpoint operation failed: attempt to ").append(enabled ? "set" : "unset");
sb.append("the ").append(this.enabled ? "set" : "unset").append(" breakpoint=").append(this);
TeleError.unexpected(sb.toString());
}
if (!vm().tryLock()) {
throw new MaxVMBusyException();
}
try {
this.enabled = enabled;
if (enabled) {
assert targetBreakpoints == null;
// Create a machine code breakpoint in every existing compilation at the location
// best corresponding to the bytecode location of this breakpoint.
targetBreakpoints = new ArrayList<VmTargetBreakpoint>();
for (TeleTargetMethod teleTargetMethod : vm().machineCode().findCompilations(codeLocation().methodKey())) {
createTargetBreakpointForMethod(teleTargetMethod);
}
} else {
assert targetBreakpoints != null;
// Remove all target code breakpoints that were created because of this breakpoint
for (VmTargetBreakpoint targetBreakpoint : targetBreakpoints) {
targetBreakpoint.remove();
}
targetBreakpoints = null;
Trace.line(TRACE_VALUE, tracePrefix() + "clearing all target breakpoints for " + this);
}
if (kind() == BreakpointKind.CLIENT) {
bytecodeBreakpointManager.fireBreakpointsChanged();
}
} finally {
vm().unlock();
}
}
@Override
public BreakpointCondition getCondition() {
return condition;
}
@Override
public void setCondition(String conditionDescriptor) throws ExpressionException, MaxVMBusyException {
if (!vm().tryLock()) {
throw new MaxVMBusyException();
}
try {
this.condition = new BreakpointCondition(vm(), conditionDescriptor);
for (VmTargetBreakpoint targetBreakpoint : targetBreakpoints) {
targetBreakpoint.setTriggerEventHandler(condition);
}
} finally {
vm().unlock();
}
}
/**
* {@inheritDoc}
* <p>
* Bytecode breakpoints don't have an owner; they only own other (target) breakpoints.
*/
public VmBreakpoint owner() {
return null;
}
@Override
public void remove() throws MaxVMBusyException {
if (!vm().tryLock()) {
throw new MaxVMBusyException();
}
try {
Trace.line(TRACE_VALUE, tracePrefix() + "removing breakpoint=" + this);
if (enabled) {
// Be sure to clear any associated machine code breakpoints.
setEnabled(false);
}
bytecodeBreakpointManager.removeBreakpoint(this);
} finally {
vm().unlock();
}
}
/**
* Receives notification that a machine code breakpoint, created in a compilation of the method
* covered by this bytecode breakpoint, has been removed because the compilation was evicted
* from the code cache.
*
* @param evictedSystemBreakpoint a target breakpoint that was created for the purpose of implementing this breakpoint in a particular compilation.
*/
public void notifyCompilationEvicted(VmTargetBreakpoint evictedSystemBreakpoint) {
Trace.line(TRACE_VALUE, tracePrefix() + " bytecode breakpoint removing target breakpoint due to code eviction;" + evictedSystemBreakpoint);
if (!targetBreakpoints.remove(evictedSystemBreakpoint)) {
// This will always return false under the current implementation of bytecode breakpoints at method entry, with bci=-1,
// since the policy is to create only a transient target breakpoint, which will have disappeared by the time this
// notification happens.
TeleWarning.message(tracePrefix() + " failed to handle removal of target breakpoint because of code eviction, breakpoint=" + this);
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Bytecodes breakpoint");
sb.append("{");
sb.append(kind().toString()).append(", ");
sb.append(codeLocation().methodKey().toString()).append(", ");
sb.append(isEnabled() ? "enabled " : "disabled ");
if (getDescription() != null) {
sb.append(", \"").append(getDescription()).append("\"");
}
sb.append("}");
return sb.toString();
}
/**
* A key for recording abstract bytecode instruction location in a method;
* defines equality to be same method descriptor, same offset.
*/
private static final class MethodPositionKey extends DefaultMethodKey {
/**
* Creates a key that uniquely identifies a method and bytecode position.
* Equality defined in terms of equivalence of the method key and position.
*
* @param codeLocation a code location that must have a method key defined.
* @return a new key
*/
public static MethodPositionKey make(CodeLocation codeLocation) {
assert codeLocation.hasMethodKey();
return new MethodPositionKey(codeLocation);
}
protected final int bci;
private MethodPositionKey(CodeLocation codeLocation) {
super(codeLocation.methodKey().holder(), codeLocation.methodKey().name(), codeLocation.methodKey().signature());
this.bci = codeLocation.bci();
}
@Override
public boolean equals(Object obj) {
if (super.equals(obj) && obj instanceof MethodPositionKey) {
final MethodPositionKey otherKey = (MethodPositionKey) obj;
return bci == otherKey.bci;
}
return false;
}
@Override
public int hashCode() {
return super.hashCode() ^ bci;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("MethodPositionKey{");
sb.append(name()).append(signature().toJavaString(false, false));
sb.append(", bci=").append(bci);
sb.append("}");
return sb.toString();
}
}
/**
* A singleton manager that creates, tracks, and removes bytecode breakpoints from the VM.
* <p>
* Bytecodes breakpoints can be created before a specified method is compiled
* or even loaded, in which case they are described by an abstract key (descriptor).
* <p>
* A bytecode breakpoint causes a target code breakpoint to be created for every
* compilation of the specified method, current and future.
*/
public static final class BytecodeBreakpointManager extends AbstractVmHolder implements TeleVMCache {
protected final class CompilationEventHandler implements VMTriggerEventHandler {
final boolean preCompilationEvent;
public CompilationEventHandler(boolean preCompilationEvent) {
this.preCompilationEvent = preCompilationEvent;
}
public boolean handleTriggerEvent(TeleNativeThread teleNativeThread) {
final TeleIntegerRegisters teleIntegerRegisters = teleNativeThread.registers().teleIntegerRegisters();
final String holderTypeDescriptorString = vm().getStringUnsafe(teleIntegerRegisters.getValue(parameter0));
final String methodName = vm().getStringUnsafe(teleIntegerRegisters.getValue(parameter1));
final String signatureDescriptorString = vm().getStringUnsafe(teleIntegerRegisters.getValue(parameter2));
if (Trace.hasLevel(COMPILATION_TRACE_VALUE)) {
String eventPrefix = preCompilationEvent ? "VM about to compile: " : "VM just compiled: ";
Trace.line(COMPILATION_TRACE_VALUE, eventPrefix + holderTypeDescriptorString + " " + methodName + " " + signatureDescriptorString);
}
for (VmBytecodeBreakpoint bytecodeBreakpoint : breakpointCache) {
// Streamlined comparison using as little Inspector machinery as possible, since we take this break at every VM compilation
if (holderTypeDescriptorString.equals(bytecodeBreakpoint.holderTypeDescriptorString) &&
methodName.equals(bytecodeBreakpoint.methodName) &&
signatureDescriptorString.equals(bytecodeBreakpoint.signatureDescriptorString)) {
if (preCompilationEvent) {
return true;
}
// Match; must set a target breakpoint on the method just compiled; is is acceptable to incur some overhead now.
final TeleObject teleObject = objects().findObjectAt(teleIntegerRegisters.getValue(parameter3));
if (teleObject != null && teleObject instanceof TeleTargetMethod) {
final TeleTargetMethod teleTargetMethod = (TeleTargetMethod) teleObject;
try {
bytecodeBreakpoint.handleNewCompilation(teleTargetMethod);
} catch (MaxVMBusyException maxVMBusyException) {
TeleError.unexpected("Unable to create target breakpoint for new compilation of " + bytecodeBreakpoint);
}
} else {
TeleWarning.message("targetMethod parameter to post-compilation trigger method was null");
continue;
}
}
}
// Handling done; now resume VM execution.
return false;
}
}
public static boolean usePrecompilationBreakpoints;
private static final List<VmBytecodeBreakpoint> EMPTY_BREAKPOINT_SEQUENCE = Collections.emptyList();
private final String tracePrefix;
// Platform-specific access to method invocation parameters in the VM.
private final CiRegister parameter0;
private final CiRegister parameter1;
private final CiRegister parameter2;
private final CiRegister parameter3;
/**
* Map: method {@link MethodPositionKey} -> existing bytecode breakpoint (whether enabled or not).
*/
private final Map<MethodPositionKey, VmBytecodeBreakpoint> breakpoints = new HashMap<MethodPositionKey, VmBytecodeBreakpoint>();
/**
* A cache of the existing breakpoints for fast traversal without allocation.
*/
private List<VmBytecodeBreakpoint> breakpointCache = EMPTY_BREAKPOINT_SEQUENCE;
/**
* A breakpoint that interrupts the compiler just as it starts compiling a method. Non-null and active
* iff there are one or more bytecode breakpoints in existence.
*/
private VmTargetBreakpoint compilationStartedBreakpoint = null;
/**
* A breakpoint that interrupts the compiler just as it finishes compiling a method. Non-null and active
* iff there are one or more bytecode breakpoints in existence.
*/
private VmTargetBreakpoint compilationCompletedBreakpoint = null;
private List<MaxBreakpointListener> breakpointListeners = new CopyOnWriteArrayList<MaxBreakpointListener>();
/**
* The number of times that the list of classes in which breakpoints are set has
* been written into the VM.
*
* @see InspectableCompilationInfo
*/
private int breakpointClassDescriptorsEpoch = 0;
private BytecodeBreakpointManager(TeleVM vm) {
super(vm);
this.tracePrefix = "[" + getClass().getSimpleName() + "] ";
Trace.begin(TRACE_VALUE, tracePrefix + "initializing");
final long startTimeMillis = System.currentTimeMillis();
// Predefine parameter accessors for reading compilation details
CiRegister[] args = MaxineVM.vm().registerConfigs.standard.getCallingConventionRegisters(JavaCall, RegisterFlag.CPU);
parameter0 = args[0];
parameter1 = args[1];
parameter2 = args[2];
parameter3 = args[3];
Trace.end(TRACE_VALUE, tracePrefix() + "initializing", startTimeMillis);
}
public void updateCache(long epoch) {
}
/**
* Adds a listener for breakpoint changes.
* <p>
* Thread-safe
*
* @param listener a breakpoint listener
*/
void addListener(MaxBreakpointListener listener) {
assert listener != null;
breakpointListeners.add(listener);
}
/**
* Removes a listener for breakpoint changes.
* <p>
* Thread-safe
*
* @param listener a breakpoint listener
*/
void removeListener(MaxBreakpointListener listener) {
assert listener != null;
breakpointListeners.remove(listener);
}
/**
* @return all client bytecode breakpoints that currently exist in the VM.
* Modification safe against breakpoint removal.
*/
List<VmBytecodeBreakpoint> clientBreakpoints() {
if (breakpointCache.isEmpty()) {
return Collections.emptyList();
}
final List<VmBytecodeBreakpoint> clientBreakpoints = new ArrayList<VmBytecodeBreakpoint>();
for (VmBytecodeBreakpoint breakpoint : breakpointCache) {
if (breakpoint.kind() == BreakpointKind.CLIENT) {
clientBreakpoints.add(breakpoint);
}
}
return clientBreakpoints;
}
/**
* @param methodCodeLocation description of a bytecode position in a method
* @return a client breakpoint set at the position, null if no client breakpoint at the position
*/
public VmBytecodeBreakpoint findClientBreakpoint(BytecodeLocation methodCodeLocation) {
final MethodPositionKey key = MethodPositionKey.make(methodCodeLocation);
VmBytecodeBreakpoint breakpoint = breakpoints.get(key);
return (breakpoint.kind() == BreakpointKind.CLIENT) ? breakpoint : null;
}
/**
* Returns a clientBreakpoint matching a method location described
* abstractly, newly created if one does not already exist for the location.
* Fails if there is a system breakpoint already at that location.
* <p>
* Thread-safe; synchronizes on VM lock
*
* @param codeLocation description of a bytecode position in a method
* @return a possibly new, enabled bytecode breakpoint,
* null if a system breakpoint is already at the location.
* @throws MaxVMBusyException
*/
public VmBreakpoint makeClientBreakpoint(CodeLocation codeLocation) throws MaxVMBusyException {
assert codeLocation.hasMethodKey();
if (!vm().tryLock()) {
throw new MaxVMBusyException();
}
VmBytecodeBreakpoint breakpoint;
try {
final MethodPositionKey key = MethodPositionKey.make(codeLocation);
breakpoint = breakpoints.get(key);
if (breakpoint == null) {
breakpoint = createBreakpoint(codeLocation, key, BreakpointKind.CLIENT);
breakpoint.setDescription("Client-specified breakpoint");
} else if (breakpoint.kind() != BreakpointKind.CLIENT) {
breakpoint = null;
}
} finally {
vm().unlock();
}
return breakpoint;
}
private void updateBreakpointCache() {
if (breakpoints.size() == 0) {
breakpointCache = EMPTY_BREAKPOINT_SEQUENCE;
} else {
breakpointCache = new ArrayList<VmBytecodeBreakpoint>(breakpoints.values());
}
}
/**
* @param codeLocation abstract description of a bytecode position in a method
* @param kind he kind of breakpoint to be created
* @return a new, enabled bytecode breakpoint
* @throws MaxVMBusyException
*/
private VmBytecodeBreakpoint createBreakpoint(CodeLocation codeLocation, MethodPositionKey key, BreakpointKind kind) throws MaxVMBusyException {
if (breakpoints.size() == 0) {
createCompilerBreakpoints();
}
final VmBytecodeBreakpoint breakpoint = new VmBytecodeBreakpoint(vm(), codeLocation, kind, key);
breakpoint.setDescription(codeLocation.description());
breakpoint.setEnabled(true);
breakpoints.put(key, breakpoint);
updateBreakpointCache();
Trace.line(TRACE_VALUE, tracePrefix + "new=" + breakpoint);
if (kind == BreakpointKind.CLIENT) {
fireBreakpointsChanged();
}
return breakpoint;
}
/**
* Removes a breakpoint at the described position, if one exists.
* <p>
* Assumes that all state related to the breakpoint has already
* been removed.
*
* @param bytecodeBreakpoint the breakpoint being removed.
*/
private void removeBreakpoint(VmBytecodeBreakpoint bytecodeBreakpoint) {
final VmBytecodeBreakpoint removedBreakpoint = breakpoints.remove(bytecodeBreakpoint.methodPositionKey);
TeleWarning.check(removedBreakpoint != null, "Failed to remove breakpoint" + bytecodeBreakpoint);
if (breakpoints.size() == 0) {
try {
removeCompilerBreakpoints();
} catch (MaxVMBusyException maxVMBusyException) {
TeleError.unexpected("Unable to remove compiler breakpont for " + bytecodeBreakpoint);
}
}
updateBreakpointCache();
Trace.line(TRACE_VALUE, tracePrefix + "removed " + bytecodeBreakpoint);
if (bytecodeBreakpoint.kind() == BreakpointKind.CLIENT) {
fireBreakpointsChanged();
}
}
/**
* Sets target code breakpoints on methods known to be called before and after of each method
* compilation in the VM. Arguments identify the method being compiled.
* <p>
* The arguments are read using low-level, type-unsafe techniques. The order and types
* of arguments processed here must match those of the compiler method where the
* breakpoint is set.
* @throws MaxVMBusyException
*
* @see InspectableCompilationInfo#notifyCompilationEvent(ClassMethodActor, TargetMethod)
*/
private void createCompilerBreakpoints() throws MaxVMBusyException {
assert compilationStartedBreakpoint == null;
assert compilationCompletedBreakpoint == null;
if (usePrecompilationBreakpoints) {
compilationStartedBreakpoint = breakpointManager().targetBreakpoints().makeSystemBreakpoint(methods().compilationStartedMethodLocation(), null);
compilationStartedBreakpoint.setDescription("System trap for compilation start");
compilationStartedBreakpoint.setTriggerEventHandler(new CompilationEventHandler(true));
Trace.line(TRACE_VALUE, tracePrefix + "creating compilation started breakpoint=" + compilationStartedBreakpoint);
}
compilationCompletedBreakpoint = breakpointManager().targetBreakpoints().makeSystemBreakpoint(methods().compilationCompletedMethodLocation(), null);
compilationCompletedBreakpoint.setDescription("System trap for compilation end");
compilationCompletedBreakpoint.setTriggerEventHandler(new CompilationEventHandler(false));
Trace.line(TRACE_VALUE, tracePrefix + "creating compilation completed breakpoint=" + compilationCompletedBreakpoint);
}
/**
* Removes the special target breakpoints set on a method called before and after each compilation in the VM.
* Don't incur the overhead of a break if there are no bytecode breakpoints enabled.
* @throws MaxVMBusyException
*/
private void removeCompilerBreakpoints() throws MaxVMBusyException {
assert compilationCompletedBreakpoint != null;
Trace.line(TRACE_VALUE, tracePrefix + "removing compilation completed breakpoint=" + compilationCompletedBreakpoint);
compilationCompletedBreakpoint.remove();
compilationCompletedBreakpoint = null;
if (compilationStartedBreakpoint != null) {
Trace.line(TRACE_VALUE, tracePrefix + "removing compilation started breakpoint=" + compilationStartedBreakpoint);
compilationStartedBreakpoint.remove();
compilationStartedBreakpoint = null;
}
}
/**
* Creates special system machine code breakpoints in a compiled method in the VM
* at location specified abstractly by a key. Normally there is exactly one such location,
* but in the special case where bytecode index is -1, which specifies the beginning
* of the compiled method's prologue, there may be more than one for different kinds
* of calls.
* <p>
* May fail when it is not possible to map the bytecode location into a target code location,
* for example in optimized code where deoptimization is not supported.
* <p>
* Trigger events are delegated to the owning bytecode breakpoint.
*
* @param owner the breakpoint on whose behalf this breakpoint is being created.
* @param teleTargetMethod a compilation in the VM of the method specified in the key
* @return machine code breakpoints at a location in the compiled method corresponding
* to the bytecode location specified in the key; null if unable to create.
* @throws MaxVMBusyException
*/
private List<VmTargetBreakpoint> createTargetBreakpoints(final VmBytecodeBreakpoint owner, TeleTargetMethod teleTargetMethod) throws MaxVMBusyException {
assert owner != null;
final List<VmTargetBreakpoint> targetBreakpoints = new LinkedList<VmTargetBreakpoint>();
final int bci = owner.methodPositionKey.bci;
Address address = Address.zero();
if (bci == -1) {
int pos = AdapterGenerator.prologueSizeForCallee(teleTargetMethod.targetMethod());
address = teleTargetMethod.getCodeStart().plus(pos);
Trace.line(TRACE_VALUE, tracePrefix + "creating transient target breakpoint at method entry in " + teleTargetMethod);
} else {
int[] bciToPosMap = teleTargetMethod.bciToPosMap();
if (bciToPosMap != null && bci < bciToPosMap.length) {
int pos = bciToPosMap[bci];
address = teleTargetMethod.getCodeStart().plus(pos);
Trace.line(TRACE_VALUE, tracePrefix + "creating target breakpoint for offset " + pos + " in " + teleTargetMethod);
} else {
TeleError.unexpected(tracePrefix + "Non-entry bytecode breakpoint unimplemented for target method=" + teleTargetMethod);
}
}
RemoteCodePointer codePointer = null;
try {
codePointer = vm().machineCode().makeCodePointer(address);
} catch (InvalidCodeAddressException e) {
TeleWarning.message(tracePrefix() + "Invalid breakpoint address " + e.getAddressString() + ": " + e.getMessage());
}
if (codePointer != null && breakpointManager().targetBreakpoints().find(codePointer) == null) {
final CodeLocation location = vm().codeLocations().createMachineCodeLocation(codePointer, "For bytecode breakpoint=" + owner.codeLocation());
final VMTriggerEventHandler vmTriggerEventHandler = new VMTriggerEventHandler() {
public boolean handleTriggerEvent(TeleNativeThread teleNativeThread) {
return owner.handleTriggerEvent(teleNativeThread);
}
};
targetBreakpoints.add(breakpointManager().targetBreakpoints().makeSystemBreakpoint(location, vmTriggerEventHandler, owner));
} else {
Trace.line(TRACE_VALUE, tracePrefix + "Target breakpoint already exists at 0x" + address.toHexString() + " in " + teleTargetMethod);
}
return targetBreakpoints;
}
private void fireBreakpointsChanged() {
// Notify registered listeners
for (final MaxBreakpointListener listener : breakpointListeners) {
listener.breakpointsChanged();
}
// Notify the VM
// Gather classes in which there is at least one bytecode breakpoint set.
final Set<String> breakpointClassDescriptors = new HashSet<String>();
for (VmBytecodeBreakpoint breakpoint : breakpointCache) {
breakpointClassDescriptors.add(breakpoint.holderTypeDescriptorString);
}
// Create string containing class descriptors for all classes in which breakpoints are set, each terminated by a space.
final StringBuilder typeDescriptorsBuilder = new StringBuilder();
for (String descriptor : breakpointClassDescriptors) {
typeDescriptorsBuilder.append(descriptor).append(" ");
}
final String breakpointClassDescriptorsString = typeDescriptorsBuilder.toString();
if (breakpointClassDescriptorsString.length() > InspectableCompilationInfo.BREAKPOINT_DESCRIPTORS_ARRAY_LENGTH) {
final StringBuilder errMsg = new StringBuilder();
errMsg.append("Implementation Restriction exceeded: list of type descriptors for classes containing ");
errMsg.append("bytecode breakpoints must not exceed ");
errMsg.append(InspectableCompilationInfo.BREAKPOINT_DESCRIPTORS_ARRAY_LENGTH).append(" characters. ");
errMsg.append("Current length=").append(breakpointClassDescriptorsString.length()).append(" characters.");
TeleError.unexpected(errMsg.toString());
}
Trace.line(TRACE_VALUE, tracePrefix + "Writing to VM type descriptors for breakpoint classes =\"" + breakpointClassDescriptorsString + "\"");
// Write the string into the designated region in the VM, along with length and incremented epoch counter
final int charsLength = breakpointClassDescriptorsString.length();
final RemoteReference charArrayReference = fields().InspectableCompilationInfo_breakpointClassDescriptorCharArray.readRemoteReference(vm());
TeleError.check(!charArrayReference.isZero(), "Can't locate inspectable code array for breakpoint classes");
for (int index = 0; index < charsLength; index++) {
Layout.setChar(charArrayReference, index, breakpointClassDescriptorsString.charAt(index));
}
fields().InspectableCompilationInfo_breakpointClassDescriptorsCharCount.writeInt(vm(), charsLength);
fields().InspectableCompilationInfo_breakpointClassDescriptorsEpoch.writeInt(vm(), ++breakpointClassDescriptorsEpoch);
}
/**
* Writes a description of every bytecode breakpoint to the stream, including those usually not shown to clients,
* with more detail than typically displayed.
* <p>
* Thread-safe
*
* @param printStream
*/
void writeSummaryToStream(PrintStream printStream) {
printStream.println("Bytecodes breakpoints :");
for (VmBytecodeBreakpoint bytecodeBreakpoint : breakpointCache) {
printStream.println(" " + bytecodeBreakpoint);
}
}
}
}