package org.erlide.backend.debug; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugException; import org.erlide.backend.BackendUtils; import org.erlide.backend.api.IBackend; import org.erlide.backend.internal.BackendActivator; import org.erlide.runtime.rpc.IOtpRpc; import org.erlide.runtime.rpc.RpcException; import org.erlide.runtime.rpc.RpcTimeoutException; import org.erlide.util.ErlLogger; import org.erlide.util.Util; 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.OtpErlangList; import com.ericsson.otp.erlang.OtpErlangObject; import com.ericsson.otp.erlang.OtpErlangPid; import com.ericsson.otp.erlang.OtpErlangTuple; public class ErlideDebug { private static final String ERLIDE_DEBUG = "erlide_debug"; private static final OtpErlangAtom PARENT_ATOM = new OtpErlangAtom("parent"); @SuppressWarnings("boxing") public static OtpErlangList getProcesses(final IOtpRpc backend, final boolean showSystemProcesses, final boolean showErlideProcesses) { OtpErlangList procs = null; try { procs = (OtpErlangList) BackendUtils.ok(backend.call(ERLIDE_DEBUG, "processes", "oo", showSystemProcesses, showErlideProcesses)); } catch (final RpcException e) { ErlLogger.warn(e); } return procs; } @SuppressWarnings("boxing") public static OtpErlangPid startDebug(final IOtpRpc backend, final int debugFlags, OtpErlangPid parentPid) throws DebugException { OtpErlangObject res = null; try { res = backend.call(ERLIDE_DEBUG, "start_debug", "i", debugFlags); } catch (final RpcException e) { ErlLogger.warn(e); } if (res instanceof OtpErlangTuple) { final OtpErlangTuple t = (OtpErlangTuple) res; final OtpErlangObject o = t.elementAt(1); final IStatus s = new Status(IStatus.ERROR, BackendActivator.PLUGIN_ID, DebugException.REQUEST_FAILED, o.toString(), null); throw new DebugException(s); } final OtpErlangPid pid = (OtpErlangPid) res; // TODO bake in this in start_debug backend.send(pid, OtpErlang.mkTuple(PARENT_ATOM, parentPid)); return pid; } /** * Sent upon attach of process (should we move this to erlang, erlide_debug? * maybe we should have an erlang process subscribing, and even filtering * events to us) * * @param backend * backend * @param pid * pid of attached process * @param jpid * java pseudo-pid * @return pid of meta process */ public static OtpErlangPid attached(final IOtpRpc backend, final OtpErlangPid pid, final OtpErlangPid jpid) { OtpErlangObject res = null; try { res = backend.call(ERLIDE_DEBUG, "attached", "xx", pid, jpid); if (res instanceof OtpErlangTuple) { final OtpErlangTuple t = (OtpErlangTuple) res; final OtpErlangPid meta = (OtpErlangPid) t.elementAt(1); return meta; } return null; } catch (final RpcException e) { ErlLogger.warn(e); } return null; } public static OtpErlangObject getProcessInfo(final IOtpRpc backend, final OtpErlangPid pid, final String item) { try { final OtpErlangObject res = backend.call(ERLIDE_DEBUG, "process_info", "pa", pid, item); if (res instanceof OtpErlangTuple) { final OtpErlangTuple t = (OtpErlangTuple) res; return t.elementAt(1); } } catch (final RpcException e) { ErlLogger.warn(e); } return null; } public static boolean isErlideProcess(final IOtpRpc backend, final OtpErlangPid pid) { boolean res = false; try { final OtpErlangAtom eres = (OtpErlangAtom) backend.call(ERLIDE_DEBUG, "is_erlide_process", "p", pid); res = Boolean.parseBoolean(eres.atomValue()); } catch (final Exception e) { } return res; } @SuppressWarnings("boxing") public static boolean interpret(final IOtpRpc backend, final String moduleName, final OtpErlangList options, final boolean distributed, final boolean interpret) { try { final OtpErlangObject res = backend.call(ERLIDE_DEBUG, "interpret", "alxoo", moduleName, options, distributed, interpret); if (res instanceof OtpErlangTuple) { final OtpErlangTuple t = (OtpErlangTuple) res; final OtpErlangObject o = t.elementAt(0); if (o instanceof OtpErlangAtom) { final OtpErlangAtom moduleAtom = (OtpErlangAtom) o; return moduleAtom.atomValue().equals("module"); } } return Util.isOk(res); } catch (final RpcException e) { ErlLogger.warn(e); } return false; } @SuppressWarnings("boxing") public static void addDeleteLineBreakpoint(final IBackend backend, final String module, final int line, final int action) { try { final String a = action == ErlDebugConstants.REQUEST_INSTALL ? "add" : "delete"; backend.getOtpRpc().call(ERLIDE_DEBUG, "line_breakpoint", "sia", module, line, a); } catch (final RpcTimeoutException e) { if (backend.isRunning()) { ErlLogger.warn(e); } } catch (final RpcException e) { ErlLogger.warn(e); } } public static void sendStarted(final IBackend backend, final OtpErlangPid meta) { try { backend.getOtpRpc().call(ERLIDE_DEBUG, "send_started", "x", meta); } catch (final RpcTimeoutException e) { if (backend.isRunning()) { ErlLogger.warn(e); } } catch (final RpcException e) { ErlLogger.warn(e); } } public static void resume(final IBackend backend, final OtpErlangPid meta) { try { backend.getOtpRpc().call(ERLIDE_DEBUG, "resume", "x", meta); } catch (final RpcTimeoutException e) { if (backend.isRunning()) { ErlLogger.warn(e); } } catch (final RpcException e) { ErlLogger.warn(e); } } public static void suspend(final IBackend backend, final OtpErlangPid meta) { try { backend.getOtpRpc().call(ERLIDE_DEBUG, "suspend", "x", meta); } catch (final RpcTimeoutException e) { if (backend.isRunning()) { ErlLogger.warn(e); } } catch (final RpcException e) { ErlLogger.warn(e); } } public static OtpErlangList getBindings(final IBackend backend, final OtpErlangPid meta) { try { final OtpErlangObject res = backend.getOtpRpc().call(ERLIDE_DEBUG, "bindings", "x", meta); return (OtpErlangList) res; } catch (final RpcTimeoutException e) { if (backend.isRunning()) { ErlLogger.warn(e); } } catch (final RpcException e) { ErlLogger.warn(e); } return null; } public static void stepOver(final IBackend backend, final OtpErlangPid meta) { try { backend.getOtpRpc().call(ERLIDE_DEBUG, "step_over", "x", meta); } catch (final RpcTimeoutException e) { if (backend.isRunning()) { ErlLogger.warn(e); } } catch (final RpcException e) { ErlLogger.warn(e); } } public static void stepReturn(final IBackend backend, final OtpErlangPid meta) { try { backend.getOtpRpc().call(ERLIDE_DEBUG, "step_return", "x", meta); } catch (final RpcTimeoutException e) { if (backend.isRunning()) { ErlLogger.warn(e); } } catch (final RpcException e) { ErlLogger.warn(e); } } public static void stepInto(final IBackend backend, final OtpErlangPid meta) { try { backend.getOtpRpc().call(ERLIDE_DEBUG, "step_into", "x", meta); } catch (final RpcTimeoutException e) { if (backend.isRunning()) { ErlLogger.warn(e); } } catch (final RpcException e) { ErlLogger.warn(e); } } public static OtpErlangTuple getAllStackframes(final IOtpRpc backend, final OtpErlangPid meta) { try { final OtpErlangObject res = backend.call(ERLIDE_DEBUG, "all_stack_frames", "x", meta); if (res instanceof OtpErlangTuple) { return (OtpErlangTuple) res; } } catch (final RpcException e) { ErlLogger.warn(e); } return null; } public static List<String> getAllModulesOnStack(final IOtpRpc backend, final OtpErlangPid meta) { try { final OtpErlangObject res = backend.call(ERLIDE_DEBUG, "all_modules_on_stack", "x", meta); if (res instanceof OtpErlangList) { final OtpErlangList modules = (OtpErlangList) res; final List<String> result = new ArrayList<>(modules.arity()); for (final OtpErlangObject module : modules) { final OtpErlangAtom moduleA = (OtpErlangAtom) module; result.add(moduleA.atomValue()); } return result; } } catch (final RpcException e) { ErlLogger.warn(e); ErlLogger.error(e); } return null; } @SuppressWarnings("boxing") public static OtpErlangTuple tracing(final IOtpRpc backend, final boolean trace, final OtpErlangPid meta) { try { final OtpErlangObject res = backend.call(ERLIDE_DEBUG, "tracing", "ox", trace, meta); if (res instanceof OtpErlangTuple) { return (OtpErlangTuple) res; } } catch (final RpcException e) { ErlLogger.warn(e); } return null; } public static OtpErlangObject eval(final IOtpRpc backend, final String expression, final OtpErlangPid meta) { try { final OtpErlangObject res = backend.call(ERLIDE_DEBUG, "eval", "sx", expression, meta); if (res instanceof OtpErlangTuple) { final OtpErlangTuple t = (OtpErlangTuple) res; if (Util.isOk(t)) { return t.elementAt(1); } } } catch (final RpcException e) { ErlLogger.warn(e); } return null; } public static final OtpErlangAtom OK = new OtpErlangAtom("ok"); @SuppressWarnings("boxing") public static String setVariableValue(final IOtpRpc backend, final String name, final String value, final int stackFrameNo, final OtpErlangPid meta) { try { final OtpErlangObject res = backend.call(ERLIDE_DEBUG, "set_variable_value", "ssix", name, value, stackFrameNo + 1, meta); try { final OtpBindings bind = OtpErlang.match("{eval_rsp, {'EXIT', Val}}", res); if (bind == null) { return null; } final String err = bind.getAsString("Val"); return err; } catch (final OtpParserException e1) { } return null; } catch (final RpcException e) { ErlLogger.warn(e); } return "error"; } public static OtpErlangObject distributeDebuggerCode(final IOtpRpc backend, final List<OtpErlangTuple> modules) { try { final OtpErlangObject o = backend.call(ERLIDE_DEBUG, "distribute_debugger_code", "lx", modules); return o; } catch (final RpcException e) { ErlLogger.warn(e); } return null; } public static void unloadDebuggerCode(final IOtpRpc backend, final List<String> modules) { if (backend == null) { return; } try { backend.cast(ERLIDE_DEBUG, "unload_debugger_code", "la", modules); } catch (final RpcException e) { // ignore, we're already closing } return; } public static OtpErlangList nodes(final IOtpRpc backend) { try { final OtpErlangObject o = backend.call(ERLIDE_DEBUG, "nodes", ""); if (o instanceof OtpErlangList) { return (OtpErlangList) o; } } catch (final RpcException e) { ErlLogger.error(e); } return null; } public static boolean dropToFrame(final IOtpRpc backend, final OtpErlangPid metaPid, final int stackFrameNo) { try { final OtpErlangObject o = backend.call(ERLIDE_DEBUG, "drop_to_frame", "xi", metaPid, stackFrameNo); return Util.isOk(o); } catch (final RpcException e) { } return false; } public static boolean isRunning(final IOtpRpc backend) { try { final OtpErlangObject o = backend.call(ERLIDE_DEBUG, "is_running", ""); if (o instanceof OtpErlangAtom) { final OtpErlangAtom atom = (OtpErlangAtom) o; return atom.booleanValue(); } } catch (final RpcException e) { ErlLogger.error(e); } return false; } public static boolean isSystemProcess(final IOtpRpc OtpRpc, final OtpErlangPid fPid) { return false; } }