Skip to main content
summaryrefslogtreecommitdiffstats
blob: ce5e9d2baca68cadd995da7c648014ab4b69c079 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
/*******************************************************************************
 * Copyright (c) 2010, 2015 Texas Instruments, Inc. 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:
 *     Dobrin Alexiev (Texas Instruments) - initial API and implementation (bug 240208)
********************************************************************************/
package org.eclipse.cdt.dsf.debug.ui.viewmodel.launch;

import java.util.ArrayList;

import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.datamodel.IDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerResumedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController.SteppingTimedOutEvent;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext;
import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;

/**
 * This class is a base class of AbstractThreadVMNode and AbstractContainerVMNode.
 * It contains common functionality between these classes.
 * 
 * The main reason this class is introduced is to allow the debug view to 
 * show multiple levels of execution containers and properly handle the delta generation.  
 * 
 * In the longer term we would like to merge the classes AbstractContainerVMNode and 
 * AbstractThreadVMNode. That will make the implementation of both classes 
 * more generic and robust in the case of recursive containers.
 *  
 * Having this class as a base for both AbstractContainerVMNode and 
 * AbstractThreadVMNode enables us to merge them in the future.
 * 
 * Originally DefaultVMModelProxyStrategy didn't accept recursive containers for 
 * generating deltas, even though they are accepted and supported by
 * AbstractDMVMProvider for viewing. 
 * The approach I took to support recursive containers for delta generation is to have 
 * the VMNodes generate their deltas level by level, instead of one whole delta at once. 
 * That required changes in identifying which is the correct context for each of the events. 
 *
 * See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=240208
 * 
 * @since 2.2
 * @experimental
 */
public abstract class AbstractExecutionContextVMNode extends AbstractDMVMNode
{
	/**
	 * List that keeps track of which events are considered leaf events for 
	 * delta creation. 
	 */
    protected ArrayList<Class<?>> leafEventTypes = new ArrayList<>();
    
	/**
	 * List that keeps track of which events are considered container events for 
	 * delta creation. 
	 */
    protected ArrayList<Class<?>> containerEventTypes = new ArrayList<>();
    

    public AbstractExecutionContextVMNode(AbstractDMVMProvider provider,
		DsfSession session, Class<? extends IDMContext> dmcClassType) {
		super(provider, session, dmcClassType);
	}

	/**
	 * Adds the events that common DSF classes rely on.
	 */
	protected void addCommonEventTypes() {

		// non container events. 
    	addEventType(ISuspendedDMEvent.class,false);
    	addEventType(IResumedDMEvent.class, false); 
    	addEventType(FullStackRefreshEvent.class, false);
    	addEventType(SteppingTimedOutEvent.class, false);
    	addEventType(ExpandStackEvent.class, false);
		
    	// container events. 
    	addEventType(IContainerSuspendedDMEvent.class,true);
    	addEventType(IContainerResumedDMEvent.class, true);
	}

	/**
	 * When DSF debuggers define custom events for which the container and thread
	 * nodes need to be updated, they need to register these events using this 
	 * function, so the proper recursive deltas are created. 
	 *  
	 * @param eventClass The event class to keep track of
	 * @param containerEvent Is the event a container event or now
	 */
	protected void addEventType(Class<? extends IDMEvent<?>> eventClass, boolean containerEvent) {
		if (containerEvent) { 
			containerEventTypes.add(eventClass);
		} else { 
			leafEventTypes.add(eventClass);
		}
	}

	/**
	 * If DSF debuggers override the behavior of AbstractThreadVMNode 
	 * or AbstractContainerVMNode, some events may no longer be needed
	 * and the derived VMNode can call this method to remove such events. 
	 *  
	 * @param eventClass The event class to remove
	 * @param containerEvent Is the event a container event or now
	 */
	protected void removeEventType(Class<?> eventClass, boolean containerEvent) {
		if (containerEvent) {
			containerEventTypes.remove(eventClass);
		} else {
			leafEventTypes.remove(eventClass);
		}
	}
	
	
	/**
	 * When we support recursive containers we want to make sure the immediate parent is returned only.
	 *
	 * @return true if the context is set by the method. 
	 */
    protected boolean getContextsForRecursiveVMNode(VMDelta parentDelta, Object e, DataRequestMonitor<IVMContext[]> rm) {
    	
    	IExecutionDMContext leafContext = null;
    	if (isExecutionContainerEvent(e)) {
    		leafContext = getLeafContextForContainerEvent(e);
    	}
    	else if (isExecutionLeafEvent(e)) {
    		leafContext = getLeafContextForLeafEvent(e);
    	}
		if (leafContext != null) {
			setImmediateParentAsContexts(leafContext, parentDelta, rm);
			return true;
    	}
		return false;
    }
    
    /**
     * Make sure we build the delta for the recursive containers one level at a time.
     * 
     * @param e - the event 
	 * @return true if the delta is built by this method. 
     */
	protected boolean buildDeltaForRecursiveVMNode(Object e, final VMDelta parentDelta, int nodeOffset, RequestMonitor rm) {

    	IExecutionDMContext leafContext = null;
    	if (isExecutionContainerEvent(e)) {
    		leafContext = getLeafContextForContainerEvent(e);
    	}
    	else if (isExecutionLeafEvent(e)) {
    		leafContext = getLeafContextForLeafEvent(e);
    	}
		if (leafContext != null) {
			addOneLevelToDelta(leafContext, parentDelta, rm);
			return true;
    	}
		return false;
	}
    
    /**
     * When the deltas are generated one level at a time we need to distinguish  
     * between container and regular events to return the proper context for the event.    
     */
	protected IExecutionDMContext getLeafContextForContainerEvent(Object event) {
    	
    	IExecutionDMContext leafEC = null;
    	IExecutionDMContext[] triggeringContext = null;

    	if (isExecutionContainerEvent(event)) {
			if (event instanceof IContainerSuspendedDMEvent) {
				IContainerSuspendedDMEvent typedEvent = (IContainerSuspendedDMEvent)event;   
				triggeringContext = typedEvent.getTriggeringContexts();
			}
			if (event instanceof IContainerResumedDMEvent) {
				IContainerResumedDMEvent typedEvent = (IContainerResumedDMEvent)event;   
				triggeringContext = typedEvent.getTriggeringContexts();
			}
    	}
		
		if (triggeringContext != null && triggeringContext.length > 0){
			leafEC = triggeringContext[0];
		}
		
		return leafEC;
    }

    /**
     * When the deltas are generated one level at a time we need to distinguish  
     * between container and regular events to return the proper context for the event.    
     */
	protected IExecutionDMContext getLeafContextForLeafEvent(Object event) {
    	
    	IExecutionDMContext leafEC = null;
    	
    	if (event instanceof IDMEvent<?>) { 
    		if (isExecutionLeafEvent(event)) {
	    		IDMEvent<?> typedEvent = (IDMEvent<?>)event;
	    		IDMContext dmContext = typedEvent.getDMContext();
	    		if (dmContext instanceof IExecutionDMContext) {
	    			leafEC = (IExecutionDMContext)dmContext;
	    		}
    		}
    	}
    	
		return leafEC;
    }
    
	/**
	 * Considers the parent delta when we construct the next level. 
	 */
    protected void addOneLevelToDelta(IExecutionDMContext leafContext, VMDelta parentDelta, RequestMonitor requestMonitor) {
    	assert leafContext != null;
		if (parentDelta.getElement() instanceof ILaunch) {
			IContainerDMContext topContainer = 
				DMContexts.getTopMostAncestorOfType(leafContext, IContainerDMContext.class);
			
			// It is possible for a thread node to be an immediate child of a launch node
			// with no container node in between.  
			if (topContainer != null) {
				parentDelta.addNode(createVMContext(topContainer), 0, IModelDelta.NO_CHANGE);
			}
		}
		else if (parentDelta.getElement() instanceof IDMVMContext) {
			IDMVMContext vmContext = (IDMVMContext)parentDelta.getElement();
			IDMContext dmContext = vmContext.getDMContext(); 
			IExecutionDMContext current = DMContexts.getParentOfType(leafContext, IContainerDMContext.class);
			while (current != null) {
				IContainerDMContext parent = DMContexts.getParentOfType(current, IContainerDMContext.class);
				if (dmContext.equals(parent)) {
					parentDelta.addNode(createVMContext(current), 0, IModelDelta.NO_CHANGE);
					break;
				}
				current = parent;
			}
		}
		requestMonitor.done();
    }

    /**
     * Based on the event (container or not), set the proper context that is the immediate 
     * parent one level at a time. 
     */
    protected void setImmediateParentAsContexts(IExecutionDMContext leafContext, 
    	VMDelta parentDelta, DataRequestMonitor<IVMContext[]> rm){

    	assert leafContext != null;
    	IVMContext[] all = null;
		if (parentDelta.getElement() instanceof ILaunch) {
			IContainerDMContext topContainer = 
				DMContexts.getTopMostAncestorOfType(leafContext, IContainerDMContext.class);
			if (topContainer != null) {
				all = new IVMContext[] { createVMContext(topContainer) };
			}
			else {
				// the thread is directly a child node of the launch node (no container in the middle).
				all = new IVMContext[] { createVMContext(leafContext) };
			}
		}
		else if (parentDelta.getElement() instanceof IDMVMContext) {
			IDMVMContext vmContext = (IDMVMContext)parentDelta.getElement();
			IDMContext dmContext = vmContext.getDMContext(); 
			IExecutionDMContext current = leafContext;
			while (current != null) {
				IContainerDMContext parent = DMContexts.getParentOfType(current, IContainerDMContext.class);
				if (dmContext.equals(parent)) {
					all = new IVMContext[] { createVMContext(current)};
					break;
				}
				current = parent;
			}
		}
		if (all == null) {
			all = new IVMContext[0];
		}
		rm.setData(all);
		rm.done();
	}
	
    /**
     * Returns whether the event should be considered a container event or not. 
     */
    protected boolean isExecutionContainerEvent(Object event) {
    	if (event != null) {
    		for (Class<?> clazz : containerEventTypes)
    			if (clazz.isAssignableFrom(event.getClass())) {
    				return true;
    			}
    	}
	    return false;
	}

    /**
     * Returns whether the event should be use to generate deltas for each of the levels.  
     */
    protected boolean isExecutionLeafEvent(Object event) {
    	if (event != null) {
	    	for (Class<?> clazz : leafEventTypes) {
	    		if (clazz.isAssignableFrom(event.getClass())) {
	    			return true;
	    		}
	    	}
    	}
	    return false;
	}
}

Back to the top