/*
* #%~
* org.overture.ide.debug
* %%
* Copyright (C) 2008 - 2014 Overture
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #~%
*/
package org.overture.ide.debug.core.model.internal;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IStackFrame;
import org.overture.ide.debug.core.ExtendedDebugEventDetails;
import org.overture.ide.debug.core.VdmDebugPlugin;
import org.overture.ide.debug.core.dbgp.IDbgpNotification;
import org.overture.ide.debug.core.dbgp.IDbgpNotificationListener;
import org.overture.ide.debug.core.dbgp.IDbgpSession;
import org.overture.ide.debug.core.dbgp.IDbgpStatusInterpreterThreadState;
import org.overture.ide.debug.core.dbgp.breakpoints.IDbgpBreakpoint;
import org.overture.ide.debug.core.dbgp.commands.IDbgpExtendedCommands;
import org.overture.ide.debug.core.dbgp.exceptions.DbgpException;
import org.overture.ide.debug.core.dbgp.internal.IDbgpTerminationListener;
import org.overture.ide.debug.core.dbgp.internal.utils.Util;
import org.overture.ide.debug.core.model.DebugEventHelper;
import org.overture.ide.debug.core.model.IDebugLaunchConstants;
import org.overture.ide.debug.core.model.IVdmDebugTarget;
import org.overture.ide.debug.core.model.IVdmStackFrame;
import org.overture.ide.debug.core.model.IVdmThread;
import org.overture.ide.debug.core.model.eval.IVdmEvaluationEngine;
import org.overture.ide.debug.core.model.internal.eval.VdmEvaluationEngine;
import org.overture.ide.debug.core.model.internal.operations.DbgpDebugger;
import org.overture.ide.debug.logging.LogItem;
public class VdmThread extends VdmDebugElement implements IVdmThread,
IThreadManagement, IDbgpTerminationListener,
VdmThreadStateManager.IStateChangeHandler
{
private VdmThreadStateManager stateManager;
private final IVdmThreadManager manager;
private final VdmStack stack;
// Session
private final IDbgpSession session;
// State variables
private final IVdmDebugTarget target;
private IVdmEvaluationEngine evalEngine;
// private int currentStackLevel;
private boolean terminated = false;
private int propertyPageSize = 32;
private IDbgpStatusInterpreterThreadState interpreterThreadState;
private boolean errorState;
// VdmThreadStateManager.IStateChangeHandler
public void handleSuspend(int detail)
{
DebugEventHelper.fireExtendedEvent(this, ExtendedDebugEventDetails.BEFORE_SUSPEND);
// if (handleSmartStepInto()) {
// return;
// }
this.target.printLog(new LogItem(this.session.getInfo(), "Break", false, "Suspend"));
DebugEventHelper.fireChangeEvent(this);
DebugEventHelper.fireSuspendEvent(this, detail);
stack.update(true);
}
// private boolean handleSmartStepInto() {
// if (stateManager.isStepInto()
// && getVdmDebugTarget().isUseStepFilters()
// && stack.getFrames().length > currentStackLevel) {
// stateManager.setStepInto(false);
// IVdmDebugTarget target = this.getVdmDebugTarget();
// String[] filters = target.getFilters();
// IDLTKLanguageToolkit toolkit = this.getVdmDebugTarget()
// .getLanguageToolkit();
// if (toolkit != null) {
// ISmartStepEvaluator evaluator = SmartStepEvaluatorManager
// .getEvaluator(toolkit.getNatureId());
// if (evaluator != null) {
// if (evaluator.isFiltered(filters, this)) {
// try {
// this.stepReturn();
// return true;
// } catch (DebugException e) {
// if (DLTKCore.DEBUG) {
// e.printStackTrace();
// }
// }
// }
// }
// }
// }
// return false;
// }
public void handleResume(int detail)
{
DebugEventHelper.fireExtendedEvent(this, ExtendedDebugEventDetails.BEFORE_RESUME);
DebugEventHelper.fireResumeEvent(this, detail);
DebugEventHelper.fireChangeEvent(this);
}
public void handleTermination(DbgpException e)
{
if (e != null)
{
VdmDebugPlugin.log(e);
IVdmStreamProxy proxy = getVdmDebugTarget().getStreamProxy();
if (proxy != null)
{
proxy.writeStderr("\n" + this.getName() + " " + e.getMessage() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
stack.update(false);
IStackFrame[] frames = stack.getFrames();
if (frames.length > 0)
{
proxy.writeStderr("\nStack trace:\n"); //$NON-NLS-1$
try
{
for (int i = 0; i < frames.length; i++)
{
IVdmStackFrame frame = (IVdmStackFrame) frames[i];
String line = "\t#" + frame.getLevel() + " file:" //$NON-NLS-1$ //$NON-NLS-2$
+ frame.getSourceURI().getPath() + " [" //$NON-NLS-1$
+ frame.getLineNumber() + "]\n"; //$NON-NLS-1$
proxy.writeStderr(line);
}
} catch (DebugException e2)
{
if (VdmDebugPlugin.DEBUG)
{
e.printStackTrace();
}
}
}
}
}
manager.handleCustomTerminationCommands();
session.requestTermination();
try
{
session.waitTerminated();
} catch (InterruptedException ee)
{
ee.printStackTrace();
}
manager.terminateThread(this);
}
public VdmThread(IVdmDebugTarget target, IDbgpSession session,
IVdmThreadManager manager) throws DbgpException, CoreException
{
this.target = target;
this.manager = manager;
this.session = session;
this.session.addTerminationListener(this);
this.stateManager = new VdmThreadStateManager(this);
this.stack = new VdmStack(this);
}
public void initialize(IProgressMonitor monitor) throws DbgpException
{
monitor.beginTask(Util.EMPTY_STRING, 10);
try
{
final DbgpDebugger engine = this.stateManager.getEngine();
// if (VdmDebugPlugin.DEBUG) {
// DbgpDebugger.printEngineInfo(engine);
// }
engine.setMaxChildren(propertyPageSize);
engine.setMaxDepth(2);
engine.setMaxData(8192);
monitor.worked(2);
manager.configureThread(engine, this);
monitor.worked(6);
final boolean isDebugConsole = IDebugLaunchConstants.isDebugConsole(target.getLaunch());
if (isDebugConsole
&& engine.isFeatureSupported(IDbgpExtendedCommands.STDIN_COMMAND))
{
engine.redirectStdin();
}
engine.setNotifyOk(true);
if (true)
{
engine.redirectStdout();
engine.redirectStderr();
}
monitor.worked(2);
// HotCodeReplaceManager.getDefault().addHotCodeReplaceListener(this);
} finally
{
monitor.done();
}
final IDbgpExtendedCommands extended = session.getExtendedCommands();
session.getNotificationManager().addNotificationListener(new IDbgpNotificationListener()
{
private final BufferedReader reader = new BufferedReader(new InputStreamReader(getStreamProxy().getStdin()));
public void dbgpNotify(IDbgpNotification notification)
{
try
{
extended.sendStdin(reader.readLine() + "\n");
} catch (IOException e)
{
// TODO: log exception
e.printStackTrace();
} catch (DbgpException e)
{
// TODO: log exception
e.printStackTrace();
}
}
});
}
public boolean hasStackFrames()
{
return isSuspended() && !isTerminated() && stack.hasFrames();
}
boolean isStackInitialized()
{
return stack.isInitialized();
}
// IThread
public IStackFrame[] getStackFrames() throws DebugException
{
if (!isSuspended())
{
try
{
Thread.sleep(100);
} catch (Exception e)
{
}
if (!isSuspended())
{
return VdmStack.NO_STACK_FRAMES;
}
}
return session.getDebugOptions().filterStackLevels(stack.getFrames());
}
public int getPriority() throws DebugException
{
return 0;
}
public IStackFrame getTopStackFrame()
{
return stack.getTopFrame();
}
public String getName()
{
String name = session.getInfo().getThreadId();
if (name.length() > 0)
{
name = name.substring(0, 1).toUpperCase() + name.substring(1);
}
// TODO remove state from name
return name
+ new String(interpreterThreadState == null ? "" : " - "
+ interpreterThreadState.getState().toString());
}
public IBreakpoint[] getBreakpoints()
{
return DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(getModelIdentifier());
}
// ISuspendResume
// Suspend
public int getModificationsCount()
{
return stateManager.getModificationsCount();
}
public boolean isSuspended()
{
return stateManager.isSuspended();
}
public boolean canSuspend()
{
return stateManager.canSuspend();
}
public void suspend() throws DebugException
{
stateManager.suspend();
this.target.printLog(new LogItem(this.session.getInfo(), "REQUEST", true, "Suspend"));
}
// Resume
public boolean canResume()
{
if (errorState)
{
return false;
}
return stateManager.canResume();
}
public void resume() throws DebugException
{
this.manager.resume();
}
public void resumeInner() throws DebugException
{
this.target.printLog(new LogItem(this.session.getInfo(), "REQUEST", true, "Resume"));
stateManager.resume();
}
public void initialStepInto()
{
stateManager.setSuspended(false, DebugEvent.CLIENT_REQUEST);
stateManager.getEngine().stepInto();
this.target.printLog(new LogItem(this.session.getInfo(), "REQUEST", true, "Initial Step Into"));
}
// IStep
public boolean isStepping()
{
return stateManager.isStepping();
}
boolean isStepInto()
{
return stateManager.isStepInto();
}
// Step into
public boolean canStepInto()
{
if (errorState)
{
return false;
}
return stateManager.canStepInto();
}
public void stepInto() throws DebugException
{
this.manager.stepInto();
// currentStackLevel = this.stack.getFrames().length;
// stateManager.stepInto();
}
public void stepIntoInner() throws DebugException
{
// currentStackLevel = this.stack.getFrames().length;
stateManager.stepInto();
this.target.printLog(new LogItem(this.session.getInfo(), "REQUEST", true, "Step Into"));
}
// Step over
public boolean canStepOver()
{
if (errorState)
{
return false;
}
return stateManager.canStepOver();
}
public void stepOver() throws DebugException
{
this.manager.stepOver();
}
public void stepOverInner() throws DebugException
{
stateManager.stepOver();
this.target.printLog(new LogItem(this.session.getInfo(), "REQUEST", true, "Step Over"));
}
// Step return
public boolean canStepReturn()
{
if (errorState)
{
return false;
}
return stateManager.canStepReturn();
}
public void stepReturn() throws DebugException
{
this.manager.stepReturn();
}
public void stepReturnInner() throws DebugException
{
stateManager.stepReturn();
this.target.printLog(new LogItem(this.session.getInfo(), "REQUEST", true, "Step Return"));
}
// ITerminate
public boolean isTerminated()
{
return terminated || stateManager.isTerminated();
}
public boolean canTerminate()
{
return !isTerminated();
}
public void terminate() throws DebugException
{
target.terminate();
}
public void sendTerminationRequest() throws DebugException
{
stateManager.terminate();
}
public IDbgpSession getDbgpSession()
{
return session;
}
public IDbgpBreakpoint getDbgpBreakpoint(String id)
{
try
{
return session.getCoreCommands().getBreakpoint(id);
} catch (DbgpException e)
{
if (VdmDebugPlugin.DEBUG)
{
e.printStackTrace();
}
}
return null;
}
public IVdmStreamProxy getStreamProxy()
{
return target.getStreamProxy();
}
public IDebugTarget getDebugTarget()
{
return target.getDebugTarget();
}
public IVdmEvaluationEngine getEvaluationEngine()
{
if (evalEngine == null)
{
evalEngine = new VdmEvaluationEngine(this);
}
return evalEngine;
}
// IDbgpTerminationListener
public void objectTerminated(Object object, Exception e)
{
terminated = true;
Assert.isTrue(object == session);
// HotCodeReplaceManager.getDefault().removeHotCodeReplaceListener(this);
manager.terminateThread(this);
}
// Object
public String toString()
{
return "Thread (" + session.getInfo().getThreadId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
public void notifyModified()
{
stateManager.notifyModified();
}
// public void hotCodeReplaceFailed(IVdmDebugTarget target,
// DebugException exception) {
// if (isSuspended()) {
// stack.updateFrames();
// DebugEventHelper.fireChangeEvent(this);
// }
// }
//
// public void hotCodeReplaceSucceeded(IVdmDebugTarget target) {
// if (isSuspended()) {
// stack.updateFrames();
// DebugEventHelper.fireChangeEvent(this);
// }
// }
public int getPropertyPageSize()
{
return propertyPageSize;
}
public boolean retrieveGlobalVariables()
{
return target.retrieveGlobalVariables();
}
public boolean retrieveClassVariables()
{
return target.retrieveClassVariables();
}
public boolean retrieveLocalVariables()
{
return target.retrieveLocalVariables();
}
public void updateStackFrames()
{
stack.updateFrames();
DebugEventHelper.fireChangeEvent(VdmThread.this.getDebugTarget());
}
void updateStack()
{
stack.update(true);
}
boolean isValidStack()
{
return session.getDebugOptions().isValidStack(stack.getFrames());
}
public void resumeAfterAccept()
{
try
{
this.resumeInner();
} catch (DebugException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void setInterpreterState(
IDbgpStatusInterpreterThreadState interpreterThreadState)
{
this.interpreterThreadState = interpreterThreadState;
}
public IDbgpStatusInterpreterThreadState getInterpreterState()
{
return this.interpreterThreadState;
}
public void setErrorState()
{
errorState = true;
}
}