/******************************************************************************* * Copyright (c) 2004 Vlad Dumitrescu and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Vlad Dumitrescu *******************************************************************************/ package org.erlide.backend.debug.model; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IBreakpointManager; import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IStackFrame; import org.eclipse.debug.core.model.IThread; import org.erlide.backend.api.IBackend; import org.erlide.backend.debug.ErlangLineBreakpoint; import org.erlide.backend.debug.ErlideDebug; import org.erlide.backend.internal.BackendActivator; import org.erlide.engine.model.erlang.ErlangFunction; import org.erlide.util.ErlLogger; import org.erlide.util.ErlangFunctionCall; import org.erlide.util.erlang.OtpBindings; import org.erlide.util.erlang.OtpErlang; import org.erlide.util.erlang.OtpParserException; import com.ericsson.otp.erlang.OtpErlangAtom; import com.ericsson.otp.erlang.OtpErlangException; import com.ericsson.otp.erlang.OtpErlangList; import com.ericsson.otp.erlang.OtpErlangLong; import com.ericsson.otp.erlang.OtpErlangObject; import com.ericsson.otp.erlang.OtpErlangPid; import com.ericsson.otp.erlang.OtpErlangRangeException; import com.ericsson.otp.erlang.OtpErlangTuple; public class ErlangProcess extends ErlangDebugElement implements IThread { public static final String STATUS_WAITING = "waiting"; public static final String STATUS_RUNNING = "running"; public static final String STATUS_RUNNABLE = "runnable"; public static final String STATUS_SUSPENDED = "suspended"; public static final String STATUS_TERMINATED = "exit"; public static final String STATUS_UNKNOWN = "unknown"; public static final String STATUS_BREAK = "break"; public static final String STATUS_IDLE = "idle"; private final OtpErlangPid fPid; private OtpErlangPid fCachedMetaPid = null; private final IBackend fBackend; private String fStatus; private String fExitStatus; private List<IStackFrame> stackFrames; private boolean stepping; private ErlangFunctionCall fInitialCall; private boolean fTracing; public ErlangProcess(final IDebugTarget target, final IBackend backend, final OtpErlangPid pid) { super(target); fPid = pid; fBackend = backend; fStatus = STATUS_UNKNOWN; fExitStatus = null; stackFrames = new ArrayList<>(); stepping = false; fTracing = false; } public String getRegisteredName() { final OtpErlangObject res = ErlideDebug.getProcessInfo(fBackend.getOtpRpc(), fPid, "registered_name"); if (res != null) { return res.toString(); } return null; } public OtpErlangTuple getCurrentFunction() { final OtpErlangObject res = ErlideDebug.getProcessInfo(fBackend.getOtpRpc(), fPid, "current_function"); return (OtpErlangTuple) res; } public OtpErlangPid getMeta() { if (fCachedMetaPid == null) { fCachedMetaPid = getErlangDebugTarget().getMetaFromPid(fPid); } return fCachedMetaPid; } public long getReductions() { final OtpErlangObject res = ErlideDebug.getProcessInfo(fBackend.getOtpRpc(), fPid, "reductions"); if (res != null) { return ((OtpErlangLong) res).longValue(); } return -1; } public OtpErlangObject getDictionary() { final OtpErlangObject res = ErlideDebug.getProcessInfo(fBackend.getOtpRpc(), fPid, "dictionary"); return res; } public OtpErlangObject getErrorHandler() { final OtpErlangObject res = ErlideDebug.getProcessInfo(fBackend.getOtpRpc(), fPid, "error_handler"); return res; } public OtpErlangObject getGroupLeader() { final OtpErlangObject res = ErlideDebug.getProcessInfo(fBackend.getOtpRpc(), fPid, "group_leader"); return res; } public OtpErlangObject getHeapSize() { final OtpErlangObject res = ErlideDebug.getProcessInfo(fBackend.getOtpRpc(), fPid, "heap_size"); return res; } public ErlangFunctionCall getInitialCall() { return fInitialCall; } public void setInitialCall(final ErlangFunctionCall initialCall) { fInitialCall = initialCall; } public OtpErlangObject getLinks() { final OtpErlangObject res = ErlideDebug.getProcessInfo(fBackend.getOtpRpc(), fPid, "links"); return res; } public OtpErlangObject getMessageQueueLen() { final OtpErlangObject res = ErlideDebug.getProcessInfo(fBackend.getOtpRpc(), fPid, "message_queue_len"); return res; } public OtpErlangObject getMessages() { final OtpErlangObject res = ErlideDebug.getProcessInfo(fBackend.getOtpRpc(), fPid, "messages"); return res; } public OtpErlangObject getErlPriority() { final OtpErlangObject res = ErlideDebug.getProcessInfo(fBackend.getOtpRpc(), fPid, "priority"); return res; } public OtpErlangObject getStackSize() { final OtpErlangObject res = ErlideDebug.getProcessInfo(fBackend.getOtpRpc(), fPid, "stack_size"); return res; } // public String getStatus() { // final OtpErlangAtom res = (OtpErlangAtom) ErlideDebug.getProcessInfo( // runtime, fPid, "status"); // if (res != null) { // return res.atomValue(); // } // return STATUS_TERMINATED; // } public String getStatus() { return fStatus; } public void setStatus(final String status) { fStatus = status; } public boolean getTrapExit() { final OtpErlangAtom res = (OtpErlangAtom) ErlideDebug .getProcessInfo(fBackend.getOtpRpc(), fPid, "trap_exit"); return Boolean.parseBoolean(res.atomValue()); } public void getStackAndBindings(final String module, final int line) { final OtpErlangTuple stackAndBindings = ErlideDebug .getAllStackframes(fBackend.getOtpRpc(), getMeta()); if (stackAndBindings == null) { ErlLogger.warn("could not retrieve stack -" + "- are there more than one debug sessions started?"); return; } OtpErlangObject savedStackTrace = null; // OtpBindings b = OtpErlang.match("{ST, F:l, }", stackAndBindings); // System.out.println("STACK=" + stackAndBindings); OtpErlangObject el0 = stackAndBindings.elementAt(0); if (el0 instanceof OtpErlangTuple) { final OtpErlangTuple t = (OtpErlangTuple) el0; savedStackTrace = t.elementAt(1); el0 = t.elementAt(0); } final OtpErlangList erlStackFrames = (OtpErlangList) el0; final OtpErlangList bs = (OtpErlangList) stackAndBindings.elementAt(1); setStackFrames(module, line, erlStackFrames, bs); if (savedStackTrace instanceof OtpErlangTuple) { addStackTrace((OtpErlangTuple) savedStackTrace); } } private void addStackTrace(final OtpErlangTuple savedStackTrace) { try { final OtpBindings bind = OtpErlang.match("{saved_stacktrace, _,STrace}", savedStackTrace); if (bind != null) { final Collection<OtpErlangObject> trace = bind.getList("STrace"); for (final OtpErlangObject oframe : trace) { final OtpErlangTuple frame = (OtpErlangTuple) oframe; final OtpErlangAtom m = (OtpErlangAtom) frame.elementAt(0); final OtpErlangAtom f = (OtpErlangAtom) frame.elementAt(1); final OtpErlangLong a = (OtpErlangLong) frame.elementAt(2); try { stackFrames.add(new ErlangUninterpretedStackFrame(m.atomValue(), new ErlangFunction(f.atomValue(), a.intValue()), this, getDebugTarget())); } catch (final OtpErlangRangeException e) { ErlLogger.error(e); } } } } catch (final OtpParserException e1) { // ignore ErlLogger.error(e1); } catch (final OtpErlangException e1) { ErlLogger.error(e1); } } public void removeStackFrames() { stackFrames = new ArrayList<>(); } public void setStackFrames(final String module, final int line, final OtpErlangList erlStackFrames, final OtpErlangList bs) { stackFrames = new ArrayList<>(); final IDebugTarget target = getDebugTarget(); stackFrames.add(new ErlangStackFrame(module, this, target, line, null, bs, erlStackFrames.arity() + 2)); for (final OtpErlangObject o : erlStackFrames) { final OtpErlangTuple t = (OtpErlangTuple) o; final OtpErlangTuple mfa; final OtpErlangObject el0 = t.elementAt(0); final OtpErlangObject el1 = t.elementAt(1); if (el0 instanceof OtpErlangTuple) { mfa = (OtpErlangTuple) el0; } else if (el1 instanceof OtpErlangTuple) { mfa = (OtpErlangTuple) el1; } else { mfa = t; } int stackFrameNo; final OtpErlangObject mfa0 = mfa.elementAt(0); if (!(mfa0 instanceof OtpErlangAtom)) { ErlLogger.debug("%s", mfa0); } final OtpErlangAtom m = (OtpErlangAtom) mfa0; final OtpErlangLong l = (OtpErlangLong) t.elementAt(1); final OtpErlangList bindings = (OtpErlangList) t.elementAt(2); final OtpErlangLong n = (OtpErlangLong) t.elementAt(3); int lin; try { lin = l.intValue(); } catch (final OtpErlangRangeException e) { lin = -1; } final String mod = m.atomValue(); try { stackFrameNo = n.intValue(); } catch (final OtpErlangRangeException e) { stackFrameNo = -1; } stackFrames.add(new ErlangStackFrame(mod, this, target, lin, null, bindings, stackFrameNo)); } } public OtpErlangObject getLastCalls() { final OtpErlangObject res = ErlideDebug.getProcessInfo(fBackend.getOtpRpc(), fPid, "last_calls"); return res; } public OtpErlangObject getMemory() { final OtpErlangObject res = ErlideDebug.getProcessInfo(fBackend.getOtpRpc(), fPid, "memory"); return res; } public OtpErlangObject getMonitoredBy() { final OtpErlangObject res = ErlideDebug.getProcessInfo(fBackend.getOtpRpc(), fPid, "monitored_by"); return res; } public OtpErlangObject getMonitors() { final OtpErlangObject res = ErlideDebug.getProcessInfo(fBackend.getOtpRpc(), fPid, "monitors"); return res; } @Override public IStackFrame[] getStackFrames() throws DebugException { return stackFrames.toArray(new IStackFrame[stackFrames.size()]); } @Override public boolean hasStackFrames() throws DebugException { return !stackFrames.isEmpty(); } @Override public int getPriority() throws DebugException { return 0; } @Override public IStackFrame getTopStackFrame() throws DebugException { if (stackFrames.isEmpty()) { return null; } return stackFrames.get(0); } @Override public String getName() throws DebugException { return toLocalPid(fPid); } public static String toLocalPid(final OtpErlangPid pid) { final int a1 = pid.id(); final int a2 = pid.serial(); return "<0." + a1 + "." + a2 + ">"; } @Override public IBreakpoint[] getBreakpoints() { IStackFrame top = null; try { top = getTopStackFrame(); } catch (final DebugException e1) { // can never happen } if (top instanceof ErlangStackFrame) { final ErlangStackFrame topFrame = (ErlangStackFrame) top; final IBreakpointManager breakpointManager = DebugPlugin.getDefault() .getBreakpointManager(); final IBreakpoint[] breakpoints = breakpointManager.getBreakpoints(); for (final IBreakpoint breakpoint : breakpoints) { if (breakpoint instanceof ErlangLineBreakpoint) { final ErlangLineBreakpoint lineBreakpoint = (ErlangLineBreakpoint) breakpoint; try { if (lineBreakpoint.getModule().equals(topFrame.getModule()) && lineBreakpoint.getLineNumber() == topFrame .getLineNumber()) { return new IBreakpoint[] { lineBreakpoint }; } } catch (final DebugException e) { ErlLogger.warn(e); } catch (final CoreException e) { ErlLogger.warn(e); } } } } return new IBreakpoint[0]; } @Override public boolean isSuspended() { // return getStatus().equals(STATUS_SUSPENDED) // || getStatus().equals(STATUS_BREAK) // || getStatus().equals(STATUS_IDLE); return getStatus().equals(STATUS_SUSPENDED) || getStatus().equals(STATUS_BREAK); } @Override public boolean isTerminated() { return getStatus().equals(STATUS_TERMINATED); } @Override public void resume() throws DebugException { stepping = false; ErlideDebug.resume(fBackend, getMeta()); } @Override public void suspend() throws DebugException { stepping = false; ErlideDebug.suspend(fBackend, getMeta()); } @Override public boolean canResume() { return isSuspended(); } @Override public boolean canSuspend() { return !isSuspended(); } @Override public boolean canStepInto() { return isSuspended(); } @Override public boolean canStepOver() { return isSuspended(); } @Override public boolean canStepReturn() { return isSuspended(); } @Override public boolean isStepping() { return stepping; } public void setNotStepping() { stepping = false; } @Override public void stepInto() throws DebugException { stepping = true; ErlideDebug.stepInto(fBackend, getMeta()); } @Override public void stepOver() throws DebugException { stepping = true; ErlideDebug.stepOver(fBackend, getMeta()); } @Override public void stepReturn() throws DebugException { stepping = true; ErlideDebug.stepReturn(fBackend, getMeta()); } @Override public boolean canTerminate() { return false; } @Override public void terminate() throws DebugException { } public boolean isSystemProcess() { return ErlideDebug.isSystemProcess(fBackend.getOtpRpc(), fPid); } public boolean isErlideProcess() { return ErlideDebug.isErlideProcess(fBackend.getOtpRpc(), fPid); } /** * @return the fPid */ public OtpErlangPid getPid() { return fPid; } public void setExitStatus(final String fExitStatus) { this.fExitStatus = fExitStatus; } public String getExitStatus() { return fExitStatus; } public void setTracing(final boolean tracing) { if (fTracing != tracing) { fTracing = tracing; ErlideDebug.tracing(fBackend.getOtpRpc(), tracing, getMeta()); } } public boolean getTracing() { return fTracing; } public void dropToFrame(final int stackFrameNo) throws DebugException { if (!ErlideDebug.dropToFrame(fBackend.getOtpRpc(), getMeta(), stackFrameNo)) { final IStatus s = new Status(IStatus.ERROR, BackendActivator.PLUGIN_ID, DebugException.REQUEST_FAILED, "frame not found", null); throw new DebugException(s); } } }