Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 4cbb4467fc2273cc1729cf1e6c6803b174c98b19 (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
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
/*******************************************************************************
 * Copyright (c) 2008, 2015 Ericsson 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:
 *     Ericsson - Initial Implementation
 *******************************************************************************/

package org.eclipse.cdt.tests.dsf.gdb.tests;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;

import java.math.BigInteger;
import java.util.concurrent.ExecutionException;

import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Query;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext;
import org.eclipse.cdt.dsf.debug.service.IExpressions;
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
import org.eclipse.cdt.dsf.debug.service.IFormattedValues;
import org.eclipse.cdt.dsf.debug.service.IInstruction;
import org.eclipse.cdt.dsf.debug.service.IMixedInstruction;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
import org.eclipse.cdt.dsf.mi.service.MIDisassembly;
import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.tests.dsf.gdb.framework.BaseParametrizedTestCase;
import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil;
import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin;
import org.eclipse.cdt.utils.Addr64;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

/*
 * This is the Disassembly Service test suite.
 *
 * It is meant to be a regression suite to be executed automatically against
 * the DSF nightly builds.
 *
 * It is also meant to be augmented with a proper test case(s) every time a
 * feature is added or in the event (unlikely :-) that a bug is found in the
 * Disassembly Service.
 *
 * Refer to the JUnit4 documentation for an explanation of the annotations.
 */

@RunWith(Parameterized.class)
public class MIDisassemblyTest extends BaseParametrizedTestCase {
	private static final String EXEC_NAME = "MemoryTestApp.exe";
	private static final String SOURCE_NAME = "MemoryTestApp.cc";
	private static final String INVALID_SOURCE_NAME = "invalid_filename";

	protected static final String[] LINE_TAGS = { "LINE_NUMBER", };

	protected int LINE_NUMBER;

	private DsfSession fSession;
	private DsfServicesTracker fServicesTracker;
	private IDisassemblyDMContext fDisassemblyDmc;
	private MIDisassembly fDisassembly;
	private IExpressions fExpressionService;

	@Rule
	final public ExpectedException expectedException = ExpectedException.none();

	// ========================================================================
	// Housekeeping stuff
	// ========================================================================

	@Override
	public void doBeforeTest() throws Exception {
		super.doBeforeTest();

		fSession = getGDBLaunch().getSession();
		Runnable runnable = new Runnable() {
			@Override
			public void run() {
				// Get a reference to the memory service
				fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId());
				assert (fServicesTracker != null);

				fDisassembly = fServicesTracker.getService(MIDisassembly.class);
				assert (fDisassembly != null);

				fExpressionService = fServicesTracker.getService(IExpressions.class);
				assert (fExpressionService != null);
			}
		};
		fSession.getExecutor().submit(runnable).get();

		IContainerDMContext containerDmc = SyncUtil.getContainerContext();
		fDisassemblyDmc = DMContexts.getAncestorOfType(containerDmc, IDisassemblyDMContext.class);
		assert (fDisassemblyDmc != null);

		resolveLineTagLocations(SOURCE_NAME, LINE_TAGS);
		LINE_NUMBER = getLineForTag("LINE_NUMBER");
	}

	@Override
	protected void setLaunchAttributes() {
		super.setLaunchAttributes();

		// Select the binary to run the tests against
		setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, EXEC_PATH + EXEC_NAME);
	}

	@Override
	public void doAfterTest() throws Exception {
		super.doAfterTest();

		fExpressionService = null;
		fDisassembly = null;
		if (fServicesTracker != null) {
			fServicesTracker.dispose();
			fServicesTracker = null;
		}
	}

	// ========================================================================
	// Helper Functions
	// ========================================================================

	/* ------------------------------------------------------------------------
	 * evaluateExpression
	 * ------------------------------------------------------------------------
	 * Invokes the ExpressionService to evaluate an expression. In theory, we
	 * shouldn't rely on another service to test this one but we need a way to
	 * access a variable from the test application in order verify that the
	 * memory operations (read/write) are working properly.
	 * ------------------------------------------------------------------------
	 * @param expression Expression to resolve
	 * @return Resolved expression
	 * @throws InterruptedException
	 * ------------------------------------------------------------------------
	 */
	private IAddress evaluateExpression(String expression) throws Throwable {
		MIStoppedEvent stoppedEvent = getInitialStoppedEvent();
		IFrameDMContext ctx = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0);
		IExpressionDMContext expressionDMC = SyncUtil.createExpression(ctx, expression);
		return new Addr64(SyncUtil.getExpressionValue(expressionDMC, IFormattedValues.HEX_FORMAT));
	}

	/* ------------------------------------------------------------------------
	 * getInstruction
	 * ------------------------------------------------------------------------
	 * Issues a disassembly request. The result is stored in fWait.
	 * ------------------------------------------------------------------------
	 * Typical usage:
	 *  getInstruction(dmc, start, end);
	 *  fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER);
	 *  assertTrue(fWait.getMessage(), fWait.isOK());
	 * ------------------------------------------------------------------------
	 * @param dmc       the data model context
	 * @param start     the start address (null == $pc)
	 * @param end       the end address
	 * @throws InterruptedException
	 * ------------------------------------------------------------------------
	 */
	private IInstruction[] getInstruction(final IDisassemblyDMContext dmc, final BigInteger startAddress,
			final BigInteger endAddress) throws InterruptedException, ExecutionException {
		Query<IInstruction[]> query = new Query<IInstruction[]>() {

			@Override
			protected void execute(DataRequestMonitor<IInstruction[]> rm) {
				fDisassembly.getInstructions(dmc, startAddress, endAddress, rm);
			}
		};

		fDisassembly.getExecutor().submit(query);

		return query.get();
	}

	/* ------------------------------------------------------------------------
	 * getInstruction
	 * ------------------------------------------------------------------------
	 * Issues a disassembly request. The result is stored in fWait.
	 * ------------------------------------------------------------------------
	 * Typical usage:
	 *  getInstruction(dmc, start, end);
	 *  fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER);
	 *  assertTrue(fWait.getMessage(), fWait.isOK());
	 * ------------------------------------------------------------------------
	 * @param dmc       the data model context
	 * @param fucntion  the function
	 * @param linenum   the line
	 * @param count     the instruction count
	 * @throws InterruptedException
	 * ------------------------------------------------------------------------
	 */
	private IInstruction[] getInstruction(final IDisassemblyDMContext dmc, final String function, final int linenum,
			final int count) throws InterruptedException, ExecutionException {
		Query<IInstruction[]> query = new Query<IInstruction[]>() {

			@Override
			protected void execute(DataRequestMonitor<IInstruction[]> rm) {
				fDisassembly.getInstructions(dmc, function, linenum, count, rm);
			}
		};

		fDisassembly.getExecutor().submit(query);

		return query.get();
	}

	/* ------------------------------------------------------------------------
	 * getMixedInstruction
	 * ------------------------------------------------------------------------
	 * Issues a disassembly request. The result is stored in fWait.
	 * ------------------------------------------------------------------------
	 * Typical usage:
	 *  getInstruction(dmc, start, end);
	 *  fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER);
	 *  assertTrue(fWait.getMessage(), fWait.isOK());
	 * ------------------------------------------------------------------------
	 * @param dmc       the data model context
	 * @param start     the start address (null == $pc)
	 * @param end       the end address
	 * @throws InterruptedException
	 * ------------------------------------------------------------------------
	 */
	private IMixedInstruction[] getMixedInstruction(final IDisassemblyDMContext dmc, final BigInteger startAddress,
			final BigInteger endAddress) throws InterruptedException, ExecutionException {
		Query<IMixedInstruction[]> query = new Query<IMixedInstruction[]>() {

			@Override
			protected void execute(DataRequestMonitor<IMixedInstruction[]> rm) {
				fDisassembly.getMixedInstructions(dmc, startAddress, endAddress, rm);
			}
		};

		fDisassembly.getExecutor().submit(query);

		return query.get();
	}

	/* ------------------------------------------------------------------------
	 * getMixedInstruction
	 * ------------------------------------------------------------------------
	 * Issues a disassembly request. The result is stored in fWait.
	 * ------------------------------------------------------------------------
	 * Typical usage:
	 *  getInstruction(dmc, start, end);
	 *  fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER);
	 *  assertTrue(fWait.getMessage(), fWait.isOK());
	 * ------------------------------------------------------------------------
	 * @param dmc       the data model context
	 * @param start     the start address (null == $pc)
	 * @param end       the end address
	 * @throws InterruptedException
	 * ------------------------------------------------------------------------
	 */
	private IMixedInstruction[] getMixedInstruction(final IDisassemblyDMContext dmc, final String function,
			final int linenum, final int count) throws InterruptedException, ExecutionException {
		Query<IMixedInstruction[]> query = new Query<IMixedInstruction[]>() {

			@Override
			protected void execute(DataRequestMonitor<IMixedInstruction[]> rm) {
				fDisassembly.getMixedInstructions(dmc, function, linenum, count, rm);
			}
		};

		fDisassembly.getExecutor().submit(query);

		return query.get();
	}

	// ========================================================================
	// Test Cases
	// ------------------------------------------------------------------------
	// Templates:
	// ------------------------------------------------------------------------
	// @ Test
	// public void basicTest() {
	//     // First test to run
	//     assertTrue("", true);
	// }
	// ------------------------------------------------------------------------
	// @ Test(timeout=5000)
	// public void timeoutTest() {
	//     // Second test to run, which will timeout if not finished on time
	//     assertTrue("", true);
	// }
	// ------------------------------------------------------------------------
	// @ Test(expected=FileNotFoundException.class)
	// public void exceptionTest() throws FileNotFoundException {
	//     // Third test to run which expects an exception
	//     throw new FileNotFoundException("Just testing");
	// }
	// ========================================================================

	///////////////////////////////////////////////////////////////////////////
	// getMemory tests
	///////////////////////////////////////////////////////////////////////////

	// ------------------------------------------------------------------------
	// readWithNullContext
	// ------------------------------------------------------------------------
	@Test(timeout = 20000)
	public void readWithNullContext() throws Throwable {

		// Setup call parameters
		BigInteger startAddress = null;
		BigInteger endAddress = null;

		expectedException.expect(ExecutionException.class);
		expectedException.expectMessage("Unknown context type");

		// Perform the test
		getInstruction(null, startAddress, endAddress);
	}

	// ------------------------------------------------------------------------
	// readWithInvalidAddress
	// ------------------------------------------------------------------------
	@Test(timeout = 20000)
	public void readWithInvalidAddress() throws Throwable {

		// Setup call parameters
		BigInteger startAddress = BigInteger.ZERO;
		BigInteger endAddress = null;

		expectedException.expect(ExecutionException.class);
		expectedException.expectMessage("Cannot access memory at address");

		// Perform the test
		getInstruction(fDisassemblyDmc, startAddress, endAddress);
	}

	// ------------------------------------------------------------------------
	// readWithNullAddress
	// ------------------------------------------------------------------------
	@Test(timeout = 20000)
	public void readWithNullAddress() throws Throwable {

		// Setup call parameters
		BigInteger startAddress = null;
		BigInteger endAddress = null;

		// Perform the test
		IInstruction[] result = getInstruction(fDisassemblyDmc, startAddress, endAddress);

		// Verify the result
		assertThat(result.length, is(not(0)));
	}

	// ------------------------------------------------------------------------
	// readWithValidAddress
	// ------------------------------------------------------------------------
	@Test(timeout = 20000)
	public void readWithValidAddress() throws Throwable {

		// Setup call parameters
		Addr64 main = (Addr64) evaluateExpression("&main");
		BigInteger startAddress = main.getValue();
		BigInteger endAddress = null;

		// Perform the test
		IInstruction[] result = getInstruction(fDisassemblyDmc, startAddress, endAddress);

		// Verify the result
		assertThat(result.length, is(not(0)));
	}

	// ------------------------------------------------------------------------
	// readWithInvalidFilename
	// ------------------------------------------------------------------------
	@Test(timeout = 20000)
	public void readWithInvalidFilename() throws Throwable {

		// Setup call parameters
		String filename = INVALID_SOURCE_NAME;
		int linenum = 1;
		int count = -1;

		expectedException.expect(ExecutionException.class);
		expectedException.expectMessage("Invalid filename");

		// Perform the test
		getInstruction(fDisassemblyDmc, filename, linenum, count);
	}

	// ------------------------------------------------------------------------
	// readWithInvalidLineNumber
	// ------------------------------------------------------------------------
	@Test(timeout = 20000)
	public void readWithInvalidLineNumber() throws Throwable {

		// Setup call parameters
		String filename = SOURCE_NAME;
		int linenum = -1;
		int count = -1;

		expectedException.expect(ExecutionException.class);
		expectedException.expectMessage("Invalid line number");

		// Perform the test
		getInstruction(fDisassemblyDmc, filename, linenum, count);
	}

	// ------------------------------------------------------------------------
	// readWithValidFilename
	// ------------------------------------------------------------------------
	@Test(timeout = 20000)
	public void readWithValidFilename() throws Throwable {

		// Setup call parameters
		String filename = SOURCE_NAME;
		int linenum = LINE_NUMBER;
		int count = -1;

		// Perform the test
		IInstruction[] result = getInstruction(fDisassemblyDmc, filename, linenum, count);

		// Verify the result
		assertThat(result.length, is(not(0)));
	}

	// ------------------------------------------------------------------------
	// readWithLineCount
	// ------------------------------------------------------------------------
	@Test(timeout = 20000)
	public void readWithLineCount() throws Throwable {

		// Setup call parameters
		String filename = SOURCE_NAME;
		int linenum = LINE_NUMBER;
		int count = 5;

		// Perform the test
		IInstruction[] result = getInstruction(fDisassemblyDmc, filename, linenum, count);

		// Verify the result
		assertThat(result.length, is(count));
	}

	// ------------------------------------------------------------------------
	// readMixedWithValidAddress
	// ------------------------------------------------------------------------
	@Test(timeout = 20000)
	public void readMixedWithValidAddress() throws Throwable {

		// Setup call parameters
		Addr64 main = (Addr64) evaluateExpression("&main");
		BigInteger startAddress = main.getValue();
		BigInteger endAddress = null;

		// Perform the test
		IMixedInstruction[] result = getMixedInstruction(fDisassemblyDmc, startAddress, endAddress);

		// Verify the result
		assertThat(result.length, is(not(0)));
	}

	// ------------------------------------------------------------------------
	// readMixedWithLineCount
	// ------------------------------------------------------------------------
	@Test(timeout = 20000)
	public void readMixedWithLineCount() throws Throwable {

		// Setup call parameters
		String filename = SOURCE_NAME;
		int linenum = LINE_NUMBER;
		int count = 5;

		// Perform the test
		IMixedInstruction[] result = getMixedInstruction(fDisassemblyDmc, filename, linenum, count);

		// Verify the result
		int total = 0;
		for (IMixedInstruction mixed : result) {
			IInstruction[] inst = mixed.getInstructions();
			total += inst.length;
		}
		assertThat(total, is(count));
	}
}

Back to the top