Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 7227c39c6a688c16aebab1d3c6525bad327ec4db (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
/*******************************************************************************
 * Copyright (c) 2011, 2016 Mentor Graphics and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Vladimir Prus (Mentor Graphics) - initial API and implementation
 *     Teodor Madan (Freescale Semiconductor) - Bug 486521: attaching to selected process
 *******************************************************************************/

package org.eclipse.cdt.dsf.gdb.internal.ui.osview;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.datamodel.DataModelInitializedEvent;
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.IResumedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext;
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS2;
import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS2.IResourceClass;
import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS2.IResourcesInformation;
import org.eclipse.cdt.dsf.mi.service.IMIRunControl;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.statushandlers.StatusManager;
import org.osgi.framework.BundleContext;

/** Responsible for fetching and storing OS awareness data for a
 * specific DSF session.
 *
 * @since 2.4
 */
public class SessionOSData {

	private DsfSession fSession;
	private DsfServicesTracker fTracker;
	private IGDBHardwareAndOS2 fHardwareOs;
	private ICommandControlDMContext fContext;

	private IResourceClass[] fResourceClasses = new IResourceClass[0];
	private Map<String, OSData> fExistingData = new HashMap<>();
	private Map<String, Date> fTimestamp = new HashMap<>();

	private Listener fUIListener;
	private Control fUIControl;

	private boolean fWaitingForSession = true;
	private boolean fSupported = true;
	private boolean fAcceptingCommands = false;
	private boolean fFetchingClasses = false;
	private boolean fFetchingContent = false;

	public SessionOSData(DsfSession session, final ICommandControlDMContext executionContext) {
		fSession = session;
		BundleContext c = GdbUIPlugin.getDefault().getBundle().getBundleContext();
		fTracker = new DsfServicesTracker(c, fSession.getId());
		fContext = executionContext;

		final DsfExecutor executor = fSession.getExecutor();
		executor.submit(new DsfRunnable() {

			@Override
			public void run() {

				IMIRunControl runControl = fTracker.getService(IMIRunControl.class);
				fAcceptingCommands = runControl.isTargetAcceptingCommands();

				fSession.addServiceEventListener(SessionOSData.this, null);

				fHardwareOs = fTracker.getService(IGDBHardwareAndOS2.class);

				if (fHardwareOs == null) {
					fSupported = false;
					notifyUI();
					return;
				}

				if (fHardwareOs.isAvailable()) {
					fetchClasses();
				}
			}
		});
	}

	@ConfinedToDsfExecutor("")
	private void fetchClasses() {
		fWaitingForSession = false;
		fFetchingClasses = true;
		fHardwareOs.getResourceClasses(fContext,
				new DataRequestMonitor<IResourceClass[]>(fSession.getExecutor(), null) {
					@Override
					@ConfinedToDsfExecutor("fExecutor")
					protected void handleCompleted() {

						if (isSuccess()) {
							fResourceClasses = getData();
							if (fResourceClasses.length == 0)
								fSupported = false;
						} else {
							fSupported = false;
						}
						fFetchingClasses = false;
						notifyUI();
					}
				});
	}

	@DsfServiceEventHandler
	public void eventDispatched(DataModelInitializedEvent e) {
		// If we see this event, it necessary means that by the time we've set event listener,
		// isAvailable() was returning false, so we need to fetch classes now.
		if (fHardwareOs != null)
			fetchClasses();
	}

	public boolean waitingForSessionInitialization() {
		return fWaitingForSession;
	}

	public boolean osResourcesSupported() {
		return fSupported;
	}

	public void dispose() {
		fSession.removeServiceEventListener(SessionOSData.this);
		fTracker.dispose();
	}

	public IResourceClass[] getResourceClasses() {
		return fResourceClasses;
	}

	/** Returns OS awareness data for given resource class that
	 * was previously fetched, or null if none was ever fetched.
	 */
	public OSData existingData(String resourceClass) {
		return fExistingData.get(resourceClass);
	}

	/** Returns the timestamp at which data for 'resourceClass' have
	 * been obtained.
	 * @pre existingData(resourceClass) != null
	 */
	public Date timestamp(String resourceClass) {
		return fTimestamp.get(resourceClass);
	}

	/** Returns true if fresh data can be fetched at this time.
	 * Generally, it's possible if we're not fetching data already,
	 * and if GDB is accepting commands right now.
	 *
	 */
	public boolean canFetchData() {
		return fAcceptingCommands && !fFetchingContent;
	}

	public boolean fetchingClasses() {
		return fFetchingClasses;
	}

	/** Returns true if we're presently fetching data. This can
	 * be used to provide some feedback to the user.
	 */
	public boolean fetchingContent() {
		return fFetchingContent;
	}

	/** Fetches up-to-date data for resourceClass.  Listeners will be
	 * informed when the new data is available.  */
	public void fetchData(final String resourceClass) {
		fFetchingContent = true;
		notifyUI();

		final DsfExecutor executor = fSession.getExecutor();
		executor.submit(new DsfRunnable() {

			@Override
			public void run() {
				fHardwareOs.getResourcesInformation(fContext, resourceClass,
						new DataRequestMonitor<IResourcesInformation>(executor, null) {

							@Override
							@ConfinedToDsfExecutor("fExecutor")
							protected void handleCompleted() {

								fFetchingContent = false;

								if (isSuccess()) {
									OSData data = new OSData(resourceClass, getData());
									fExistingData.put(resourceClass, data);
									fTimestamp.put(resourceClass, new Date());
								} else {
									StatusManager.getManager().handle(getStatus(), StatusManager.SHOW);
								}
								notifyUI();
							}
						});
			}
		});

	}

	public interface Listener {
		void update();
	}

	/** Setup the listener that will be notified whenever externally
	 * visible state changes. The listener will always be invoked
	 * in the UI thread. 'control' is the control associated with
	 * the listener. The listener will not be called if the control
	 * is disposed.
	 */
	public void setUIListener(Listener listener, Control control) {
		fUIListener = listener;
		fUIControl = control;
	}

	private void notifyUI() {

		final Control c = fUIControl;
		if (c != null && !c.isDisposed())
			// There be dragons: if you try to use c.getDisplay() below, then this Runnable will not
			// run until resource view is actually visible. And it will also block other interesting
			//  async/job runnables, like perspective switch runnable using during debug launch,
			//   causing launch to be stuck at random point.
			//
			Display.getDefault().asyncExec(new Runnable() {

				@Override
				public void run() {

					if (!c.isDisposed())
						fUIListener.update();
				}
			});

	}

	@DsfServiceEventHandler
	public void eventDispatched(IResumedDMEvent e) {
		if (e instanceof IContainerResumedDMEvent) {
			// This event is raised only in all-stop. It does not
			// seem to be possible to issue -info-os in all-stop,
			// regardless of whether target-async is in effect, and
			// according to DSF folks, all-stop+target-async will
			// not work anyway. So, we assume that no commands
			// can be issued right now.
			fAcceptingCommands = false;
			notifyUI();
		}
	}

	@DsfServiceEventHandler
	public void eventDispatched(ISuspendedDMEvent e) {
		if (e instanceof IContainerSuspendedDMEvent) {
			fAcceptingCommands = true;
			notifyUI();
		}
	}

	/**
	 * @return the fContext
	 */
	public ICommandControlDMContext getContext() {
		return fContext;
	}
}

Back to the top