/******************************************************************************* * Copyright (c) 2000, 2009 QNX Software Systems 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: * QNX Software Systems - Initial API and implementation *******************************************************************************/ package org.eclipse.cdt.debug.mi.core.cdi; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Observable; import java.util.Observer; import org.eclipse.cdt.debug.core.cdi.CDIException; import org.eclipse.cdt.debug.core.cdi.ICDIEventManager; import org.eclipse.cdt.debug.core.cdi.event.ICDIEvent; import org.eclipse.cdt.debug.core.cdi.event.ICDIEventListener; import org.eclipse.cdt.debug.core.cdi.model.ICDIStackFrame; import org.eclipse.cdt.debug.mi.core.MIException; import org.eclipse.cdt.debug.mi.core.MIPlugin; import org.eclipse.cdt.debug.mi.core.MISession; import org.eclipse.cdt.debug.mi.core.cdi.event.ChangedEvent; import org.eclipse.cdt.debug.mi.core.cdi.event.CreatedEvent; import org.eclipse.cdt.debug.mi.core.cdi.event.DestroyedEvent; import org.eclipse.cdt.debug.mi.core.cdi.event.DisconnectedEvent; import org.eclipse.cdt.debug.mi.core.cdi.event.ExitedEvent; import org.eclipse.cdt.debug.mi.core.cdi.event.MemoryChangedEvent; import org.eclipse.cdt.debug.mi.core.cdi.event.ResumedEvent; import org.eclipse.cdt.debug.mi.core.cdi.event.SuspendedEvent; import org.eclipse.cdt.debug.mi.core.cdi.model.MemoryBlock; import org.eclipse.cdt.debug.mi.core.cdi.model.Target; import org.eclipse.cdt.debug.mi.core.cdi.model.Thread; import org.eclipse.cdt.debug.mi.core.command.Command; import org.eclipse.cdt.debug.mi.core.command.CommandFactory; import org.eclipse.cdt.debug.mi.core.command.MIExecContinue; import org.eclipse.cdt.debug.mi.core.command.MIExecFinish; import org.eclipse.cdt.debug.mi.core.command.MIStackInfoDepth; import org.eclipse.cdt.debug.mi.core.command.MIStackSelectFrame; import org.eclipse.cdt.debug.mi.core.command.MIThreadSelect; import org.eclipse.cdt.debug.mi.core.event.MIBreakpointChangedEvent; import org.eclipse.cdt.debug.mi.core.event.MIBreakpointCreatedEvent; import org.eclipse.cdt.debug.mi.core.event.MIBreakpointDeletedEvent; import org.eclipse.cdt.debug.mi.core.event.MIChangedEvent; import org.eclipse.cdt.debug.mi.core.event.MICreatedEvent; import org.eclipse.cdt.debug.mi.core.event.MIDestroyedEvent; import org.eclipse.cdt.debug.mi.core.event.MIDetachedEvent; import org.eclipse.cdt.debug.mi.core.event.MIEvent; import org.eclipse.cdt.debug.mi.core.event.MIGDBExitEvent; import org.eclipse.cdt.debug.mi.core.event.MIInferiorCreatedEvent; import org.eclipse.cdt.debug.mi.core.event.MIInferiorExitEvent; import org.eclipse.cdt.debug.mi.core.event.MIInferiorSignalExitEvent; import org.eclipse.cdt.debug.mi.core.event.MIMemoryChangedEvent; import org.eclipse.cdt.debug.mi.core.event.MIMemoryCreatedEvent; import org.eclipse.cdt.debug.mi.core.event.MIRegisterChangedEvent; import org.eclipse.cdt.debug.mi.core.event.MIRegisterCreatedEvent; import org.eclipse.cdt.debug.mi.core.event.MIRunningEvent; import org.eclipse.cdt.debug.mi.core.event.MISharedLibChangedEvent; import org.eclipse.cdt.debug.mi.core.event.MISharedLibCreatedEvent; import org.eclipse.cdt.debug.mi.core.event.MISharedLibEvent; import org.eclipse.cdt.debug.mi.core.event.MISharedLibUnloadedEvent; import org.eclipse.cdt.debug.mi.core.event.MISignalChangedEvent; import org.eclipse.cdt.debug.mi.core.event.MIStoppedEvent; import org.eclipse.cdt.debug.mi.core.event.MIThreadCreatedEvent; import org.eclipse.cdt.debug.mi.core.event.MIThreadExitEvent; import org.eclipse.cdt.debug.mi.core.event.MIVarChangedEvent; import org.eclipse.cdt.debug.mi.core.event.MIVarCreatedEvent; import org.eclipse.cdt.debug.mi.core.event.MIVarDeletedEvent; import org.eclipse.cdt.debug.mi.core.output.MIInfo; import org.eclipse.cdt.debug.mi.core.output.MIStackInfoDepthInfo; /** */ public class EventManager extends SessionObject implements ICDIEventManager, Observer { List list = Collections.synchronizedList(new ArrayList(1)); MIRunningEvent lastRunningEvent; Command lastUserCommand = null; boolean fAllowProcessingEvents = true; /** * Process the event from MI, do any state work on the CDI, * and fire the corresponding CDI event. */ public void update(Observable o, Object arg) { MIEvent miEvent = (MIEvent)arg; Session session = (Session)getSession(); Target currentTarget = session.getTarget(miEvent.getMISession()); if (currentTarget == null) { return; // bailout; this no concern to us. But we should Assert. } List cdiList = new ArrayList(1); if (miEvent instanceof MIStoppedEvent) { if (processSuspendedEvent((MIStoppedEvent)miEvent)) { cdiList.add(new SuspendedEvent(session, miEvent)); } } else if (miEvent instanceof MIRunningEvent) { if (processRunningEvent((MIRunningEvent)miEvent)) cdiList.add(new ResumedEvent(session, (MIRunningEvent)miEvent)); } else if (miEvent instanceof MIChangedEvent) { if (miEvent instanceof MIVarChangedEvent) { cdiList.add(new ChangedEvent(session, (MIVarChangedEvent)miEvent)); } else if (miEvent instanceof MIRegisterChangedEvent) { cdiList.add(new ChangedEvent(session, (MIRegisterChangedEvent)miEvent)); } else if (miEvent instanceof MIMemoryChangedEvent) { // We need to fire an event for all the register blocks // that may contain the modified addresses. MemoryManager mgr = session.getMemoryManager(); MemoryBlock[] blocks = mgr.getMemoryBlocks(miEvent.getMISession()); MIMemoryChangedEvent miMem = (MIMemoryChangedEvent)miEvent; BigInteger[] addresses = miMem.getAddresses(); for (int i = 0; i < blocks.length; i++) { if (blocks[i].contains(addresses) && (! blocks[i].isFrozen() || blocks[i].isDirty())) { cdiList.add(new MemoryChangedEvent(session, blocks[i], miMem)); blocks[i].setDirty(false); } } } else if (miEvent instanceof MIBreakpointChangedEvent) { MIBreakpointChangedEvent bpoint = (MIBreakpointChangedEvent)miEvent; if (bpoint.getNumber() > 0) { cdiList.add(new ChangedEvent(session, bpoint)); } else { try { // Pass the event to access to the event's hint // https://bugs.eclipse.org/bugs/show_bug.cgi?id=135250 session.getBreakpointManager().update(currentTarget, miEvent); } catch (CDIException e) { } } } else if (miEvent instanceof MISharedLibChangedEvent) { cdiList.add(new ChangedEvent(session, (MISharedLibChangedEvent)miEvent)); } else if (miEvent instanceof MISignalChangedEvent) { MISignalChangedEvent sig = (MISignalChangedEvent)miEvent; String name = sig.getName(); if (name == null || name.length() == 0) { // Something change we do not know what // Let the signal manager handle it with an update(). try { SignalManager sMgr = session.getSignalManager(); sMgr.update(currentTarget); } catch (CDIException e) { } } else { cdiList.add(new ChangedEvent(session, sig)); } } } else if (miEvent instanceof MIDestroyedEvent) { if (miEvent instanceof MIThreadExitEvent) { cdiList.add(new DestroyedEvent(session,(MIThreadExitEvent)miEvent)); } else if (miEvent instanceof MIInferiorSignalExitEvent) { cdiList.add(new ExitedEvent(session, (MIInferiorSignalExitEvent)miEvent)); } else if (miEvent instanceof MIInferiorExitEvent) { cdiList.add(new ExitedEvent(session, (MIInferiorExitEvent)miEvent)); } else if (miEvent instanceof MIGDBExitEvent) { // Remove the target from the list. Target target = session.getTarget(miEvent.getMISession()); if (target != null) { session.removeTargets(new Target[] { target }); } cdiList.add(new ExitedEvent(session, (MIGDBExitEvent)miEvent)); } else if (miEvent instanceof MIDetachedEvent) { cdiList.add(new DisconnectedEvent(session, (MIDetachedEvent)miEvent)); } else if (miEvent instanceof MIBreakpointDeletedEvent) { MIBreakpointDeletedEvent bpoint = (MIBreakpointDeletedEvent)miEvent; if (bpoint.getNumber() > 0) { cdiList.add(new DestroyedEvent(session, bpoint)); } else { // Something was deleted we do not know what // Let the breakpoint manager handle it with an update(). try { session.getBreakpointManager().update(currentTarget); } catch (CDIException e) { } } } else if (miEvent instanceof MISharedLibUnloadedEvent) { processSharedLibUnloadedEvent((MISharedLibUnloadedEvent)miEvent); cdiList.add(new DestroyedEvent(session, (MISharedLibUnloadedEvent)miEvent)); } else if (miEvent instanceof MIVarDeletedEvent) { cdiList.add(new DestroyedEvent(session, (MIVarDeletedEvent)miEvent)); } } else if (miEvent instanceof MICreatedEvent) { if (miEvent instanceof MIBreakpointCreatedEvent) { MIBreakpointCreatedEvent bpoint = (MIBreakpointCreatedEvent)miEvent; if (bpoint.getNumber() > 0) { cdiList.add(new CreatedEvent(session, bpoint)); } else { // Something created we do not know what // Let the breakpoint manager handle it with an update(). try { session.getBreakpointManager().update(currentTarget); } catch (CDIException e) { } } } else if (miEvent instanceof MIVarCreatedEvent) { cdiList.add(new CreatedEvent(session, (MIVarCreatedEvent)miEvent)); } else if (miEvent instanceof MIRegisterCreatedEvent) { cdiList.add(new CreatedEvent(session, (MIRegisterCreatedEvent)miEvent)); } else if (miEvent instanceof MIThreadCreatedEvent) { cdiList.add(new CreatedEvent(session, (MIThreadCreatedEvent)miEvent)); } else if (miEvent instanceof MIMemoryCreatedEvent) { cdiList.add(new CreatedEvent(session, (MIMemoryCreatedEvent)miEvent)); } else if (miEvent instanceof MISharedLibCreatedEvent) { cdiList.add(new CreatedEvent(session, (MISharedLibCreatedEvent)miEvent)); } else if (miEvent instanceof MIInferiorCreatedEvent) { cdiList.add(new CreatedEvent(session, (MIInferiorCreatedEvent)miEvent)); } } // Fire the event; ICDIEvent[] cdiEvents = (ICDIEvent[])cdiList.toArray(new ICDIEvent[0]); fireEvents(cdiEvents); } public EventManager(Session session) { super(session); } /** * @see org.eclipse.cdt.debug.core.cdi.ICDIEventManager#addEventListener(ICDIEventListener) */ public void addEventListener(ICDIEventListener listener) { list.add(listener); } /** * @see org.eclipse.cdt.debug.core.cdi.ICDIEventManager#removeEventListener(ICDIEventListener) */ public void removeEventListener(ICDIEventListener listener) { list.remove(listener); } public void removeEventListeners() { list.clear(); } /** * Send ICDIEvent to the listeners. */ public void fireEvents(ICDIEvent[] cdiEvents) { if (cdiEvents != null && cdiEvents.length > 0) { ICDIEventListener[] listeners = (ICDIEventListener[])list.toArray(new ICDIEventListener[0]); for (int i = 0; i < listeners.length; i++) { listeners[i].handleDebugEvents(cdiEvents); } } } /** * When suspended arrives, reset managers and target. * Alse the variable and the memory needs to be updated and events * fired for changes. */ boolean processSuspendedEvent(MIStoppedEvent stopped) { Session session = (Session)getSession(); MISession miSession = stopped.getMISession(); Target currentTarget = session.getTarget(miSession); currentTarget.setSupended(true); // Bailout early if we do not want to process any events. if (!isAllowingProcessingEvents()) { return false; } if (processSharedLibEvent(stopped)) { // Event was consumed by the shared lib processing bailout return false; } if (processBreakpointHitEvent(stopped)) { // Event was consumed, i.e. it was not the right exception. return false; } int threadId = stopped.getThreadId(); currentTarget.updateState(threadId); try { Thread cthread = (Thread)currentTarget.getCurrentThread(); if (cthread != null) { cthread.getCurrentStackFrame(); } else { return true; } } catch (CDIException e1) { //e1.printStackTrace(); return true; } // Update the managers. // For the Variable/Expression Managers call only the updateManager. VariableManager varMgr = session.getVariableManager(); ExpressionManager expMgr = session.getExpressionManager(); RegisterManager regMgr = session.getRegisterManager(); MemoryManager memMgr = session.getMemoryManager(); BreakpointManager bpMgr = session.getBreakpointManager(); SignalManager sigMgr = session.getSignalManager(); SourceManager srcMgr = session.getSourceManager(); SharedLibraryManager libMgr = session.getSharedLibraryManager(); try { if (varMgr.isAutoUpdate()) { varMgr.update(currentTarget); } if (expMgr.isAutoUpdate()) { expMgr.update(currentTarget); } if (regMgr.isAutoUpdate()) { regMgr.update(currentTarget); } if (memMgr.isAutoUpdate()) { memMgr.update(currentTarget); } if (bpMgr.isAutoUpdate()) { bpMgr.update(currentTarget); } if (sigMgr.isAutoUpdate()) { sigMgr.update(currentTarget); } if (libMgr.isAutoUpdate()) { libMgr.update(currentTarget); } if (srcMgr.isAutoUpdate()) { srcMgr.update(currentTarget); } } catch (CDIException e) { // Something went wrong => preventing updates to the model, this is serious MIPlugin.log(e); } return true; } /** * When a shared library is unloading we could possibly have stale libraries. * GDB does no react well to this: see PR * https://bugs.eclipse.org/bugs/show_bug.cgi?id=74496 * @param unLoaded * @return */ boolean processSharedLibUnloadedEvent(MISharedLibUnloadedEvent unLoaded) { Session session = (Session)getSession(); MISession miSession = unLoaded.getMISession(); Target target = session.getTarget(miSession); // We do not need to do fancy checking we can just delete all // the expression variable and let UI recreate them by reevaluating. ExpressionManager expMgr = session.getExpressionManager(); try { expMgr.deleteAllVariables(target); } catch (CDIException e) { } return false; } /** * If the deferredBreakpoint processing is set * catch the shared-lib-event go to the last known * stackframe and try to finish. * Save the last user command and issue it again. * @param stopped * @return */ boolean processSharedLibEvent(MIStoppedEvent stopped) { Session session = (Session)getSession(); MISession miSession = stopped.getMISession(); Target currentTarget = session.getTarget(miSession); SharedLibraryManager mgr = session.getSharedLibraryManager(); if (mgr.isDeferredBreakpoint(currentTarget)) { if (stopped instanceof MISharedLibEvent) { // Check if we have a new library loaded try { mgr.update(currentTarget); } catch (CDIException e3) { } CommandFactory factory = miSession.getCommandFactory(); int type = (lastRunningEvent == null) ? MIRunningEvent.CONTINUE : lastRunningEvent.getType(); if (lastUserCommand == null) { switch (type) { case MIRunningEvent.NEXT: lastUserCommand = factory.createMIExecNext(1); break; case MIRunningEvent.NEXTI: lastUserCommand = factory.createMIExecNextInstruction(1); break; case MIRunningEvent.STEP: lastUserCommand = factory.createMIExecStep(1); break; case MIRunningEvent.STEPI: lastUserCommand = factory.createMIExecStepInstruction(1); break; case MIRunningEvent.FINISH: lastUserCommand = factory.createMIExecFinish(); break; case MIRunningEvent.RETURN: lastUserCommand = factory.createMIExecReturn(); break; case MIRunningEvent.CONTINUE: { MIExecContinue cont = factory.createMIExecContinue(); cont.setQuiet(true); try { miSession.postCommand(cont); MIInfo info = cont.getMIInfo(); if (info == null) { // throw new CDIException("Target is not responding"); } } catch (MIException e) { // throw new MI2CDIException(e); } return true; // for the continue bailout early no need to the stuff below } } } int miLevel = 0; int tid = 0; Thread currentThread = null; try { currentThread = (Thread)currentTarget.getCurrentThread(); } catch (CDIException e1) { } tid = currentThread.getId(); // Select the old thread now. if (tid > 0) { MIThreadSelect selectThread = factory.createMIThreadSelect(tid); try { miSession.postCommand(selectThread); } catch (MIException e) { // ignore } } ICDIStackFrame frame = null; try { frame = currentThread.getCurrentStackFrame(); } catch (CDIException e2) { } int count = 0; try { MIStackInfoDepth depth = factory.createMIStackInfoDepth(); miSession.postCommand(depth); MIStackInfoDepthInfo info = depth.getMIStackInfoDepthInfo(); if (info == null) { //throw new CDIException("No answer"); } count = info.getDepth(); } catch (MIException e) { //throw new MI2CDIException(e); //System.out.println(e); } if (frame != null) { // Fortunately the ICDIStackFrame store the level // in ascending level the higher the stack the higher the level // GDB does the opposite the highest stack is 0. // This allow us to do some calculation, in figure out the // level of the old stack. The -1 is because gdb level is zero-based miLevel = count - frame.getLevel() - 1; } if (miLevel >= 0) { MIStackSelectFrame selectFrame = factory.createMIStackSelectFrame(miLevel); MIExecFinish finish = factory.createMIExecFinish(); finish.setQuiet(true); try { miSession.postCommand(selectFrame); miSession.postCommand(finish); } catch (MIException e) { // ignore } } else { // if we are still at the same level in the backtrace // for example the StopEventLib was on a different thread // redo the last command. Command cmd = lastUserCommand; cmd.setQuiet(true); lastUserCommand = null; try { miSession.postCommand(cmd); } catch (MIException e) { // ignore } } return true; } else if (lastUserCommand != null) { Command cmd = lastUserCommand; cmd.setQuiet(true); lastUserCommand = null; try { miSession.postCommand(cmd); } catch (MIException e) { } return true; } } return false; } boolean processBreakpointHitEvent(MIStoppedEvent stopped) { // Session session = (Session)getSession(); // if (stopped instanceof MIBreakpointHitEvent) { // MIBreakpointHitEvent bpEvent = (MIBreakpointHitEvent)stopped; // BreakpointManager bpMgr = session.getBreakpointManager(); // int bpNo = bpEvent.getNumber(); // } return false; } /** * Do any processing of before a running event. */ boolean processRunningEvent(MIRunningEvent running) { lastRunningEvent = running; Session session = (Session)getSession(); MISession miSession = running.getMISession(); Target currentTarget = session.getTarget(miSession); currentTarget.setSupended(false); // Bailout early if we do not want to process any events. if (!isAllowingProcessingEvents() || !running.propagate()) { return false; } return true; } public boolean isAllowingProcessingEvents() { return fAllowProcessingEvents; } public void allowProcessingEvents(boolean allowed) { fAllowProcessingEvents = allowed; } }