/*
* Copyright (c) 2007, 2011, 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.jdwp.handlers;
import java.io.IOException;
import java.util.List;
import java.util.logging.Logger;
import com.sun.max.jdwp.constants.StepDepth;
import com.sun.max.jdwp.constants.SuspendPolicy;
import com.sun.max.jdwp.data.JDWPException;
import com.sun.max.jdwp.data.JDWPLocation;
import com.sun.max.jdwp.data.JDWPNotImplementedException;
import com.sun.max.jdwp.data.JDWPSender;
import com.sun.max.jdwp.protocol.EventCommands.Composite;
import com.sun.max.jdwp.protocol.EventRequestCommands;
import com.sun.max.jdwp.vm.data.LineTableEntry;
import com.sun.max.jdwp.vm.proxy.JdwpCodeLocation;
import com.sun.max.jdwp.vm.proxy.ThreadProvider;
import com.sun.max.jdwp.vm.proxy.VMListener;
public abstract class JDWPEventRequest<EventsCommon_Type extends Composite.Events.EventsCommon> {
private static final Logger LOGGER = Logger.getLogger(JDWPEventRequest.class.getName());
private List<JDWPEventModifier> modifiers;
private JDWPSession session;
private EventRequestCommands.Set.IncomingRequest incomingRequest;
private int id;
private JDWPSender sender;
private static int idCounter = 0;
private JDWPEventRequest(EventRequestCommands.Set.IncomingRequest incomingRequest, JDWPSender sender, JDWPSession session) throws JDWPException {
this.incomingRequest = incomingRequest;
this.session = session;
this.modifiers = JDWPEventModifier.Static.createList(session, incomingRequest.modifiers);
this.sender = sender;
assignId();
}
private void assignId() {
idCounter++;
id = idCounter;
}
public int getId() {
return id;
}
public byte eventKind() {
return incomingRequest.eventKind;
}
public byte suspendPolicy() {
return incomingRequest.suspendPolicy;
}
public JDWPSession session() {
return session;
}
public List<JDWPEventModifier> modifiers() {
return modifiers;
}
public abstract void install();
public abstract void uninstall();
private void removeModifier(JDWPEventModifier modifier) {
modifiers.remove(modifier);
}
@SuppressWarnings("unchecked")
private <JDWPEventModifier_Type extends JDWPEventModifier> JDWPEventModifier_Type lookupMandatoryModifier(Class<JDWPEventModifier_Type> klass) throws JDWPException {
for (JDWPEventModifier m : modifiers) {
if (m.getClass().equals(klass)) {
return (JDWPEventModifier_Type) m;
}
}
throw new JDWPNotImplementedException();
}
public void eventOccurred(JDWPEventContext context, byte suspendPolicy, EventsCommon_Type eventData) {
for (JDWPEventModifier m : modifiers) {
if (!m.isAccepted(context)) {
return;
}
}
LOGGER.info("Event occurred (suspended: " + suspendPolicy + "): " + this);
final Composite.Reply r = new Composite.Reply();
r.suspendPolicy = suspendPolicy;
r.events = new Composite.Events[1];
r.events[0] = new Composite.Events();
r.events[0].eventKind = this.eventKind();
r.events[0].aEventsCommon = eventData;
try {
sender.sendCommand(r);
} catch (IOException e) {
LOGGER.severe("Could not send event, because of exception: " + e);
}
}
public static class ClassPrepare extends JDWPEventRequest<Composite.Events.ClassUnload> {
public ClassPrepare(EventRequestCommands.Set.IncomingRequest incomingRequest, JDWPSender sender, JDWPSession session) throws JDWPException {
super(incomingRequest, sender, session);
}
@Override
public void install() {
}
@Override
public void uninstall() {
}
}
public static class SingleStep extends JDWPEventRequest<Composite.Events.SingleStep> {
private ThreadProvider thread;
private int depth;
public SingleStep(EventRequestCommands.Set.IncomingRequest incomingRequest, JDWPSender sender, JDWPSession session) throws JDWPException {
super(incomingRequest, sender, session);
final JDWPEventModifier.Step step = super.lookupMandatoryModifier(JDWPEventModifier.Step.class);
this.thread = step.thread();
this.depth = step.depth();
}
private final VMListener listener = new VMAdapter() {
@Override
public void singleStepMade(ThreadProvider thr, JdwpCodeLocation location) {
Logger.getLogger(SingleStep.class.getName()).info("SingleStep was made by thread " + thr + " onto location " + location);
Logger.getLogger(SingleStep.class.getName()).info("Method name: " + location.method().getName() + ", Signature: " + location.method().getSignature());
for (LineTableEntry entry : location.method().getLineTable()) {
Logger.getLogger(SingleStep.class.getName()).info("Line table entry: " + entry.getCodeIndex() + " line number : " + entry.getLineNumber());
}
final JDWPLocation locationHit = session().fromCodeLocation(location);
if (thread.equals(thr)) {
final JDWPEventContext context = new JDWPEventContext(thread, null, locationHit);
eventOccurred(context, (byte) SuspendPolicy.ALL,
new Composite.Events.SingleStep(getId(), session().toID(thread), locationHit/*session().fromCodeLocation(_thread.getFrames()[0].getLocation())*/));
}
}
};
@Override
public void install() {
session().vm().addListener(listener);
if (this.depth == StepDepth.OUT) {
thread.doStepOut();
} else {
thread.doSingleStep();
}
}
@Override
public void uninstall() {
session().vm().removeListener(listener);
}
}
public static class Exception extends JDWPEventRequest<Composite.Events.ClassUnload> {
public Exception(EventRequestCommands.Set.IncomingRequest incomingRequest, JDWPSender sender, JDWPSession session) throws JDWPException {
super(incomingRequest, sender, session);
}
@Override
public void install() {
}
@Override
public void uninstall() {
}
}
public static class ClassUnload extends JDWPEventRequest<Composite.Events.ClassUnload> {
public ClassUnload(EventRequestCommands.Set.IncomingRequest incomingRequest, JDWPSender sender, JDWPSession session) throws JDWPException {
super(incomingRequest, sender, session);
}
@Override
public void install() {
}
@Override
public void uninstall() {
}
}
public static class ThreadDeath extends JDWPEventRequest<Composite.Events.ThreadDeath> {
public ThreadDeath(EventRequestCommands.Set.IncomingRequest incomingRequest, JDWPSender sender, JDWPSession session) throws JDWPException {
super(incomingRequest, sender, session);
}
private final VMListener listener = new VMAdapter() {
@Override
public void threadDied(ThreadProvider thread) {
final JDWPEventContext context = new JDWPEventContext(thread, null, null);
eventOccurred(context, (byte) SuspendPolicy.NONE, new Composite.Events.ThreadDeath(getId(), session().toID(thread)));
}
};
@Override
public void install() {
session().vm().addListener(listener);
}
@Override
public void uninstall() {
session().vm().removeListener(listener);
}
}
public static class ThreadStart extends JDWPEventRequest<Composite.Events.ThreadStart> {
public ThreadStart(EventRequestCommands.Set.IncomingRequest incomingRequest, JDWPSender sender, JDWPSession session) throws JDWPException {
super(incomingRequest, sender, session);
}
private final VMListener listener = new VMAdapter() {
@Override
public void threadStarted(ThreadProvider thread) {
final JDWPEventContext context = new JDWPEventContext(thread, null, null);
eventOccurred(context, (byte) SuspendPolicy.NONE, new Composite.Events.ThreadStart(getId(), session().toID(thread)));
}
};
@Override
public void install() {
session().vm().addListener(listener);
}
@Override
public void uninstall() {
session().vm().removeListener(listener);
}
}
public static class Breakpoint extends JDWPEventRequest<Composite.Events.Breakpoint> {
private JDWPLocation location;
private JdwpCodeLocation codeLocation;
public Breakpoint(EventRequestCommands.Set.IncomingRequest incomingRequest, JDWPSender sender, JDWPSession session) throws JDWPException {
super(incomingRequest, sender, session);
final JDWPEventModifier.LocationOnly locationOnly = super.lookupMandatoryModifier(JDWPEventModifier.LocationOnly.class);
this.location = locationOnly.location();
super.removeModifier(locationOnly);
this.codeLocation = session().toCodeLocation(location);
}
private final VMListener listener = new VMAdapter() {
@Override
public void breakpointHit(ThreadProvider thread, JdwpCodeLocation loc) {
Logger.getLogger(Breakpoint.class.getName()).info("Breakpoint was hit by thread " + thread + " on location " + loc);
final JDWPLocation locationHit = session().fromCodeLocation(loc);
if (locationHit.equals(location)) {
final JDWPEventContext context = new JDWPEventContext(thread, session().methodToReferenceType(loc.method()), locationHit);
eventOccurred(context, (byte) SuspendPolicy.ALL, new Composite.Events.Breakpoint(getId(), session().toID(thread), locationHit));
}
}
};
@Override
public void install() {
session().vm().addListener(listener);
session().vm().addBreakpoint(codeLocation, this.suspendPolicy() == SuspendPolicy.ALL);
}
@Override
public void uninstall() {
session().vm().removeListener(listener);
}
}
}