/******************************************************************************* * Copyright (c) 2012, 2014 Ericsson 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: * Marc Khouzam (Ericsson) - initial API and implementation * Marc Dumais (Ericsson) - Bug 400231 * Marc Dumais (Ericsson) - Bug 399419 * Marc Dumais (Ericsson) - Bug 405390 * Marc Dumais (Ericsson) - Bug 396269 * Marc Dumais (Ericsson) - Bug 409512 * Marc Dumais (Ericsson) - Bug 409965 * Marc Dumais (Ericsson) - Bug 416524 * Xavier Raynaud (Kalray) - Bug 431935 *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view; import java.util.List; import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.DataModelInitializedEvent; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.IProcesses; import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext; import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMData; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.IStartedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; import org.eclipse.cdt.dsf.debug.service.IStack; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMData; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.MulticoreVisualizerUIPlugin; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerCore; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerExecutionState; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerModel; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerThread; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.DSFDebugModel; import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses.IGdbThreadDMData; import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; import org.eclipse.cdt.dsf.mi.service.IMIProcessDMContext; import org.eclipse.cdt.dsf.mi.service.IMIRunControl; import org.eclipse.cdt.dsf.mi.service.IMIRunControl.MIRunMode; import org.eclipse.cdt.dsf.mi.service.command.events.IMIDMEvent; import org.eclipse.cdt.dsf.mi.service.command.events.MISignalEvent; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfServicesTracker; /** * DSF event listener class for the Multicore Visualizer. * This class will handle different relevant DSF events * and update the Multicore Visualizer accordingly. */ public class MulticoreVisualizerEventListener { private static final String THE_THREAD_ID_DOES_NOT_CONVERT_TO_AN_INTEGER = "The thread id does not convert to an integer: "; //$NON-NLS-1$ // --- members --- /** Visualizer we're managing events for. */ protected MulticoreVisualizer fVisualizer; // --- constructors/destructors --- /** Constructor */ public MulticoreVisualizerEventListener(MulticoreVisualizer visualizer) { fVisualizer = visualizer; } // --- event handlers --- /** * Invoked when a thread or process is suspended. * Updates both state of the thread and the core it's running on */ @DsfServiceEventHandler public void handleEvent(final ISuspendedDMEvent event) { // make sure model exists final VisualizerModel model = fVisualizer.getModel(); if (model == null) { return; } IDMContext context = event.getDMContext(); // all-stop mode? If so, we take the opportunity, now that GDB has suspended // execution, to re-create the model so that we synchronize with the debug session if (context != null && isSessionAllStop(context.getSessionId()) ) { fVisualizer.update(); return; } // non-stop mode if (context instanceof IContainerDMContext) { // We don't deal with processes } else if (context instanceof IMIExecutionDMContext) { // Thread suspended final IMIExecutionDMContext execDmc = (IMIExecutionDMContext)context; IThreadDMContext threadContext = DMContexts.getAncestorOfType(execDmc, IThreadDMContext.class); final DsfServicesTracker tracker = new DsfServicesTracker(MulticoreVisualizerUIPlugin.getBundleContext(), execDmc.getSessionId()); IProcesses procService = tracker.getService(IProcesses.class); final IStack stackService = tracker.getService(IStack.class); tracker.dispose(); procService.getExecutionData(threadContext, new ImmediateDataRequestMonitor<IThreadDMData>() { @Override protected void handleSuccess() { IThreadDMData data = getData(); // Check whether we know about cores if (data instanceof IGdbThreadDMData) { String[] cores = ((IGdbThreadDMData)data).getCores(); if (cores != null) { assert cores.length == 1; // A thread belongs to a single core int coreId = Integer.parseInt(cores[0]); final VisualizerCore vCore = model.getCore(coreId); int tid; VisualizerThread threadTmp = null; try { tid = Integer.parseInt(execDmc.getThreadId()); threadTmp = model.getThread(tid); } catch (NumberFormatException e) { // unable to resolve thread assert false : THE_THREAD_ID_DOES_NOT_CONVERT_TO_AN_INTEGER + execDmc.getThreadId(); return; } if (threadTmp != null) { final VisualizerThread thread = threadTmp; assert thread.getState() == VisualizerExecutionState.RUNNING; VisualizerExecutionState _newState = VisualizerExecutionState.SUSPENDED; if (event.getReason() == StateChangeReason.SIGNAL) { if (event instanceof IMIDMEvent) { Object miEvent = ((IMIDMEvent)event).getMIEvent(); if (miEvent instanceof MISignalEvent) { String signalName = ((MISignalEvent)miEvent).getName(); if (DSFDebugModel.isCrashSignal(signalName)) { _newState = VisualizerExecutionState.CRASHED; } } } } final VisualizerExecutionState newState = _newState; if (stackService != null) { stackService.getTopFrame(execDmc, new ImmediateDataRequestMonitor<IFrameDMContext>(null) { @Override protected void handleCompleted() { IFrameDMContext targetFrameContext = null; if (isSuccess()) { targetFrameContext = getData(); } if (targetFrameContext != null) { stackService.getFrameData(targetFrameContext, new ImmediateDataRequestMonitor<IFrameDMData>(null) { @Override protected void handleCompleted() { IFrameDMData frameData = null; if (isSuccess()) { frameData = getData(); } updateThread(thread, newState, vCore, frameData); } }); } else { updateThread(thread, newState, vCore, null); } } }); } else { updateThread(thread, newState, vCore, null); } } } } } } ); } } private void updateThread(VisualizerThread thread, VisualizerExecutionState newState, VisualizerCore vCore, IFrameDMData frameData) { thread.setState(newState); thread.setCore(vCore); thread.setLocationInfo(frameData); fVisualizer.refresh(); } /** Invoked when a thread or process is resumed. */ @DsfServiceEventHandler public void handleEvent(IResumedDMEvent event) { // make sure model exists VisualizerModel model = fVisualizer.getModel(); if (model == null) { return; } IDMContext context = event.getDMContext(); // in all-stop mode... : update all threads states to "running" if (context != null && isSessionAllStop(context.getSessionId()) ) { List<VisualizerThread> tList = model.getThreads(); for(VisualizerThread t : tList) { t.setState(VisualizerExecutionState.RUNNING); t.setLocationInfo((String) null); } fVisualizer.getMulticoreVisualizerCanvas().requestUpdate(); return; } // Non-stop mode if (context instanceof IContainerDMContext) { // We don't deal with processes } else if (context instanceof IMIExecutionDMContext) { // Thread resumed int tid; VisualizerThread thread = null; String strThreadId = ((IMIExecutionDMContext) context).getThreadId(); try { tid = Integer.parseInt(strThreadId); thread = model.getThread(tid); } catch (NumberFormatException e) { // unable to resolve thread assert false : THE_THREAD_ID_DOES_NOT_CONVERT_TO_AN_INTEGER + strThreadId; return; } if (thread != null) { assert thread.getState() == VisualizerExecutionState.SUSPENDED || thread.getState() == VisualizerExecutionState.CRASHED; thread.setState(VisualizerExecutionState.RUNNING); thread.setLocationInfo((String) null); fVisualizer.getMulticoreVisualizerCanvas().requestUpdate(); } } } /** Invoked when a thread or process starts. */ @DsfServiceEventHandler public void handleEvent(IStartedDMEvent event) { // make sure model exists final VisualizerModel model = fVisualizer.getModel(); if (model == null) { return; } IDMContext context = event.getDMContext(); if (context == null) return; final String sessionId = context.getSessionId(); // all-stop mode? // If so we can't ask GDB for more info about the new thread at this moment. // So we still add it to the model, on core zero and with a OS thread id of // zero. The next time the execution is stopped, the model will be re-created // and show the correct thread ids and cores. if (isSessionAllStop(sessionId) && context instanceof IMIExecutionDMContext ) { final IMIExecutionDMContext execDmc = (IMIExecutionDMContext)context; final IMIProcessDMContext processContext = DMContexts.getAncestorOfType(execDmc, IMIProcessDMContext.class); // put it on core zero VisualizerCore vCore = model.getCore(0); if (vCore == null) return; int pid = Integer.parseInt(processContext.getProcId()); int tid; try { tid = Integer.parseInt(execDmc.getThreadId()); } catch (NumberFormatException e) { // unable to resolve thread assert false : THE_THREAD_ID_DOES_NOT_CONVERT_TO_AN_INTEGER + execDmc.getThreadId(); return; } int osTid = 0; // add thread if not already there - there is a potential race condition where a // thread can be added twice to the model: once at model creation and once more // through the listener. Checking at both places to prevent this. if (model.getThread(tid) == null ) { model.addThread(new VisualizerThread(vCore, pid, osTid, tid, VisualizerExecutionState.RUNNING)); fVisualizer.getMulticoreVisualizerCanvas().requestUpdate(); } return; } // non-stop mode if (context instanceof IContainerDMContext) { // We don't deal with processes } else if (context instanceof IMIExecutionDMContext) { // New thread added final IMIExecutionDMContext execDmc = (IMIExecutionDMContext)context; final IMIProcessDMContext processContext = DMContexts.getAncestorOfType(execDmc, IMIProcessDMContext.class); IThreadDMContext threadContext = DMContexts.getAncestorOfType(execDmc, IThreadDMContext.class); DsfServicesTracker tracker = new DsfServicesTracker(MulticoreVisualizerUIPlugin.getBundleContext(), sessionId); IProcesses procService = tracker.getService(IProcesses.class); tracker.dispose(); procService.getExecutionData(threadContext, new ImmediateDataRequestMonitor<IThreadDMData>() { @Override protected void handleSuccess() { IThreadDMData data = getData(); // Check whether we know about cores if (data instanceof IGdbThreadDMData) { String[] cores = ((IGdbThreadDMData)data).getCores(); if (cores != null) { assert cores.length == 1; // A thread belongs to a single core int coreId = Integer.parseInt(cores[0]); VisualizerCore vCore = model.getCore(coreId); // There is a race condition that sometimes happens here. We can reach // here because we were notified that a thread is started, but the model // is not yet completely constructed. If the model doesn't yet contain the // core the thread runs-on, the getCore() call above will return null. This // will later cause a problem when we try to draw this thread, if we allow // this to pass. See Bug 396269/ if (vCore == null) return; int pid = Integer.parseInt(processContext.getProcId()); int tid; try { tid = Integer.parseInt(execDmc.getThreadId()); } catch (NumberFormatException e) { // Unable to resolve thread information assert false : THE_THREAD_ID_DOES_NOT_CONVERT_TO_AN_INTEGER + execDmc.getThreadId(); return; } int osTid = 0; try { osTid = Integer.parseInt(data.getId()); } catch (NumberFormatException e) { // I've seen a case at startup where GDB is not ready to // return the osTID so we get null. // That is ok, we'll be refreshing right away at startup } // add thread if not already there - there is a potential race condition where a // thread can be added twice to the model: once at model creation and once more // through the listener. Checking at both places to prevent this. if (model.getThread(tid) == null ) { model.addThread(new VisualizerThread(vCore, pid, osTid, tid, VisualizerExecutionState.RUNNING)); fVisualizer.getMulticoreVisualizerCanvas().requestUpdate(); } } } } } ); } } /** Invoked when a thread or process exits. */ @DsfServiceEventHandler public void handleEvent(IExitedDMEvent event) { // make sure model exists final VisualizerModel model = fVisualizer.getModel(); if (model == null) { return; } IDMContext context = event.getDMContext(); final MulticoreVisualizerCanvas canvas = fVisualizer.getMulticoreVisualizerCanvas(); if (context instanceof IContainerDMContext) { // process exited // Note: this is required because we noticed that in GDB 7.6 and older, // the "thread exited" signal is not sent for the local detach case. // see bug 409512 DsfServicesTracker tracker = new DsfServicesTracker(MulticoreVisualizerUIPlugin.getBundleContext(), context.getSessionId()); IProcesses procService = tracker.getService(IProcesses.class); tracker.dispose(); // get all threads associated to this process and // mark them as exited in the model. procService.getProcessesBeingDebugged(context, new ImmediateDataRequestMonitor<IDMContext[]>() { @Override protected void handleSuccess() { assert getData() != null; IDMContext[] contexts = getData(); for (IDMContext c : contexts) { if (c instanceof IMIExecutionDMContext) { int tid; String strThreadId = ((IMIExecutionDMContext) c).getThreadId(); try { tid = Integer.parseInt(strThreadId); } catch (NumberFormatException e) { // unable to resolve the thread id assert false : THE_THREAD_ID_DOES_NOT_CONVERT_TO_AN_INTEGER + strThreadId; continue; } model.markThreadExited(tid); } } if (canvas != null) { canvas.requestUpdate(); } } @Override protected void handleFailure() { // we are overriding handleFailure() to avoid an error message // in the log, in the all-stop mode. } }); } else if (context instanceof IMIExecutionDMContext) { // Thread exited int tid; String strThreadId = ((IMIExecutionDMContext) context).getThreadId(); try { tid = Integer.parseInt(strThreadId); model.markThreadExited(tid); } catch (NumberFormatException e) { assert false : THE_THREAD_ID_DOES_NOT_CONVERT_TO_AN_INTEGER + strThreadId; } if (canvas != null) { canvas.requestUpdate(); } } } /** Invoked when the debug data model is ready */ @DsfServiceEventHandler public void handleEvent(DataModelInitializedEvent event) { // re-create the visualizer model now that CPU and core info is available fVisualizer.update(); } // helper functions /** Returns whether the session is the "all-stop" kind */ private boolean isSessionAllStop(String sessionId) { DsfServicesTracker servicesTracker = new DsfServicesTracker(MulticoreVisualizerUIPlugin.getBundleContext(), sessionId); IMIRunControl runCtrlService = servicesTracker.getService(IMIRunControl.class); servicesTracker.dispose(); if (runCtrlService != null && runCtrlService.getRunMode() == MIRunMode.ALL_STOP ) { return true; } return false; } }