diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/TracepointTestApp.cc b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/TracepointTestApp.cc index 9aac1214a98..e21193bb9f2 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/TracepointTestApp.cc +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/TracepointTestApp.cc @@ -2,6 +2,23 @@ * This program should be compiled with -g3 -O0 to try to always obtain * the proper instruction sizes at the expected location. * The instruction sizes matter for tests that set tracepoints + * + * There is a test, GDBRemoteTracepointsTest.checkInstructionsAreExpectedLength(), which + * attempts to make sure the instructions are the size the comments say they are. Compiler + * changes, whether version or platform, can affect these results. + * + * To experiment at setting the instructions to the correct length, a command like this + * can be used: + g++ -g3 -c -O0 -pthread -o TracepointTestApp.o TracepointTestApp.cc && gdb -batch -ex 'file TracepointTestApp.o' -ex 'disassemble /rs foo' + * On the lines following the N_BYTE comment, the instruction should be N bytes long. + * for example, this is the output I had when writing (with the comment line munged as to not affect resolveLineTagLocations): + +17 if (x != a) { // *3*_BYTE + 0x0000000000000022 <+23>: 8b 45 fc mov -0x4(%rbp),%eax + 0x0000000000000025 <+26>: 3b 45 ec cmp -0x14(%rbp),%eax + 0x0000000000000028 <+29>: 74 0b je 0x35 + + * As you can see, the "mov" instruction above is 3-bytes (8b 45 fc) long. */ int bar() { @@ -12,20 +29,22 @@ int foo(int a) { int x = a; - while(x < 5) { // 2-byte on both 32bit and 64bit + while(x < 5) { - if (x != a) { // 3-byte on both 32bit and 64bit + if (x != a) { // 3_BYTE // IF_X_NE_A - ++x; // 4-byte on both 32bit and 64bit + ++x; // 4_BYTE - bar(); // 5-byte on both 32bit and 64bit + bar(); // 5_BYTE + goto end; // 2_BYTE } - x++; + x++; // INCR_X } +end: return 0; -} // 1-byte on both 32bit and 64bit +} // 1_BYTE int gIntVar = 0; bool gBoolVar = true; diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/GDBRemoteTracepointsTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/GDBRemoteTracepointsTest.java index f68ace839c7..f7828e09fea 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/GDBRemoteTracepointsTest.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/GDBRemoteTracepointsTest.java @@ -14,8 +14,11 @@ package org.eclipse.cdt.tests.dsf.gdb.tests; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -36,8 +39,13 @@ import org.eclipse.cdt.dsf.gdb.internal.tracepointactions.CollectAction; import org.eclipse.cdt.dsf.gdb.internal.tracepointactions.EvaluateAction; import org.eclipse.cdt.dsf.gdb.internal.tracepointactions.TracepointActionManager; import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils; +import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; import org.eclipse.cdt.dsf.mi.service.MIBreakpointDMData; import org.eclipse.cdt.dsf.mi.service.MIBreakpoints; +import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOOBRecord; +import org.eclipse.cdt.dsf.mi.service.command.output.MIStreamRecord; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; @@ -59,16 +67,23 @@ public class GDBRemoteTracepointsTest extends BaseParametrizedTestCase { // private ITraceControl fTraceService; protected IBreakpointsTargetDMContext fBreakpointsDmc; // private ITraceTargetDMContext fTraceTargetDmc; + protected IGDBControl fGdbControl; + protected CommandFactory fCommandFactory; // private int fTotalTracingBufferSize = 0; protected static final String EXEC_NAME = "TracepointTestApp.exe"; protected static final String SOURCE_NAME = "TracepointTestApp.cc"; - protected static final int LINE_NUMBER_1_BYTE_INSTR = 28; - protected static final int LINE_NUMBER_2_BYTE_INSTR = 15; - protected static final int LINE_NUMBER_3_BYTE_INSTR = 17; - protected static final int LINE_NUMBER_4_BYTE_INSTR = 19; - protected static final int LINE_NUMBER_5_BYTE_INSTR = 21; + + // Breakpoint tags in TracepointTestApp.cc + public static final String[] LINE_TAGS = new String[] { + "1_BYTE", + "2_BYTE", + "3_BYTE", + "4_BYTE", + "5_BYTE" + }; + protected static final String NO_CONDITION = ""; protected static final String NO_COMMANDS = ""; // private static final int LAST_LINE_NUMBER = 94; @@ -149,8 +164,8 @@ public class GDBRemoteTracepointsTest extends BaseParametrizedTestCase { @Override public void doBeforeTest() throws Exception { - assumeRemoteSession(); super.doBeforeTest(); + resolveLineTagLocations(SOURCE_NAME, LINE_TAGS); fSession = getGDBLaunch().getSession(); Runnable runnable = new Runnable() { @@ -160,6 +175,9 @@ public class GDBRemoteTracepointsTest extends BaseParametrizedTestCase { fBreakpointService = fServicesTracker.getService(IBreakpoints.class); + fGdbControl = fServicesTracker.getService(IGDBControl.class); + fCommandFactory = fGdbControl.getCommandFactory(); + // fTraceService = fServicesTracker.getService(ITraceControl.class); fSession.addServiceEventListener(GDBRemoteTracepointsTest.this, null); @@ -561,9 +579,6 @@ public class GDBRemoteTracepointsTest extends BaseParametrizedTestCase { // checkTraceStatus(supported, active, frames, null, null); // } - // GDB 7.0 does not support fast tracepoints, but GDB 7.2 will - protected boolean fastTracepointsSupported() { return false; } - protected class TracepointData { String sourceFile; int lineNumber; @@ -584,11 +599,8 @@ public class GDBRemoteTracepointsTest extends BaseParametrizedTestCase { passcount = pass; enabled = isEnabled; commands = cmds; - if (fastTracepointsSupported()) { - isFastTp = fast; - } else { - isFastTp = false; - } + isFastTp = fast; + assertTrue(!fast || fastTracepointsSupported()); } } @@ -597,44 +609,44 @@ public class GDBRemoteTracepointsTest extends BaseParametrizedTestCase { // Fetch the tp list from the backend IBreakpointDMContext[] tracepoints = getBreakpoints(fBreakpointsDmc); - assertTrue("expected " + numTracepoints + " breakpoint(s), received " - + tracepoints.length, tracepoints.length == numTracepoints); + assertEquals("expected " + numTracepoints + " breakpoint(s), received " + + tracepoints.length, numTracepoints, tracepoints.length); for (int i=0; i attributes = null; int[] lineNumbers = { - LINE_NUMBER_1_BYTE_INSTR, - LINE_NUMBER_2_BYTE_INSTR, - LINE_NUMBER_3_BYTE_INSTR, - LINE_NUMBER_4_BYTE_INSTR, - LINE_NUMBER_5_BYTE_INSTR }; + getLineForTag("1_BYTE"), + getLineForTag("2_BYTE"), + getLineForTag("3_BYTE"), + getLineForTag("4_BYTE"), + getLineForTag("5_BYTE") }; for (int i = 0; i < lineNumbers.length; i++) { attributes = new HashMap(); @@ -685,6 +697,53 @@ public class GDBRemoteTracepointsTest extends BaseParametrizedTestCase { // checkTraceStatus(true, false, 0); } + /** + * This test relies on knowing that instructions in the compiled + * code are of the expected number of bytes long. See the {@value #SOURCE_NAME} + * for details of how to resolve this test failing. + */ + @Test + public void checkInstructionsAreExpectedLength() throws Throwable { + // GDB helpfully returns an error if you try to insert a fast tracepoint + // on an instruction < 4 bytes long. We can use that to verify how big + // each instruction is. + + IContainerDMContext containerDmc = SyncUtil.getContainerContext(); + Query query = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + fGdbControl.queueCommand( + fCommandFactory.createMIInterpreterExecConsole(containerDmc, "disassemble /rs foo"), rm); + } + }; + fGdbControl.getExecutor().execute(query); + MIInfo miInfo = query.get(TestsPlugin.massageTimeout(500), TimeUnit.MILLISECONDS); + try { + MIOOBRecord[] mioobRecords = miInfo.getMIOutput().getMIOOBRecords(); + Set passed = new HashSet<>(); + for (int i = 0; i < mioobRecords.length; i++) { + String sourceLineWithComment = ((MIStreamRecord) mioobRecords[i]).getString(); + int index; + if ((index = sourceLineWithComment.indexOf("_BYTE")) >= 0) { + int byteCount = Integer.parseInt(sourceLineWithComment.substring(index - 1, index)); + String disassembledInstruction = ((MIStreamRecord) mioobRecords[i + 1]).getString(); + String[] split = disassembledInstruction.split("\\t", 3); + @SuppressWarnings("unused") + String addr = split[0]; + String bytes = split[1]; + @SuppressWarnings("unused") + String mnemonic = split[2]; + String[] bytes2 = bytes.split(" "); + assertEquals(byteCount, bytes2.length); + passed.add(byteCount); + } + } + assertEquals("Some byte length were not seen", new HashSet(Arrays.asList(1, 2, 3, 4, 5)), passed); + } catch (AssertionError | Exception e) { + throw new AssertionError("Failed to verify instruction lengths. Output from GDB's disassemble:\n" + miInfo.toString(), e); + } + } + /** * This test sets different tracepoints in the program: * - using a method address @@ -874,11 +933,11 @@ public class GDBRemoteTracepointsTest extends BaseParametrizedTestCase { Map attributes = null; int[] lineNumbers = { - LINE_NUMBER_1_BYTE_INSTR, - LINE_NUMBER_2_BYTE_INSTR, - LINE_NUMBER_3_BYTE_INSTR, - LINE_NUMBER_4_BYTE_INSTR, - LINE_NUMBER_5_BYTE_INSTR }; + getLineForTag("1_BYTE"), + getLineForTag("2_BYTE"), + getLineForTag("3_BYTE"), + getLineForTag("4_BYTE"), + getLineForTag("5_BYTE") }; for (int i = 0; i < lineNumbers.length; i++) { attributes = new HashMap(); @@ -896,11 +955,11 @@ public class GDBRemoteTracepointsTest extends BaseParametrizedTestCase { } TracepointData[] dataArray = new TracepointData[] { - new TracepointData(LINE_NUMBER_1_BYTE_INSTR, NO_CONDITION, 0, true, cmdResults[0], false), - new TracepointData(LINE_NUMBER_2_BYTE_INSTR, NO_CONDITION, 0, true, cmdResults[1], false), - new TracepointData(LINE_NUMBER_3_BYTE_INSTR, NO_CONDITION, 0, true, cmdResults[2], false), - new TracepointData(LINE_NUMBER_4_BYTE_INSTR, NO_CONDITION, 0, true, cmdResults[3], acceptsFastTpOnFourBytes()), - new TracepointData(LINE_NUMBER_5_BYTE_INSTR, NO_CONDITION, 0, true, cmdResults[4], true), + new TracepointData(getLineForTag("1_BYTE"), NO_CONDITION, 0, true, cmdResults[0], false), + new TracepointData(getLineForTag("2_BYTE"), NO_CONDITION, 0, true, cmdResults[1], false), + new TracepointData(getLineForTag("3_BYTE"), NO_CONDITION, 0, true, cmdResults[2], false), + new TracepointData(getLineForTag("4_BYTE"), NO_CONDITION, 0, true, cmdResults[3], acceptsFastTpOnFourBytes()), + new TracepointData(getLineForTag("5_BYTE"), NO_CONDITION, 0, true, cmdResults[4], fastTracepointsSupported()), }; checkTracepoints(dataArray); @@ -1143,6 +1202,10 @@ public class GDBRemoteTracepointsTest extends BaseParametrizedTestCase { return false; } + protected boolean fastTracepointsSupported() { + return LaunchUtils.compareVersions(getGdbVersion(), ITestConstants.SUFFIX_GDB_7_2) >= 0; + } + /** * This test sets the different types of tracepoints and then sets some string collection actions */ @@ -1177,11 +1240,11 @@ public class GDBRemoteTracepointsTest extends BaseParametrizedTestCase { updateBreakpoint(fTracepoints[4], delta); TracepointData[] dataArray = new TracepointData[] { - new TracepointData(LINE_NUMBER_1_BYTE_INSTR, NO_CONDITION, 0, true, action1.toString(), false), - new TracepointData(LINE_NUMBER_2_BYTE_INSTR, NO_CONDITION, 0, true, action2.toString(), false), - new TracepointData(LINE_NUMBER_3_BYTE_INSTR, NO_CONDITION, 0, true, action1.toString(), false), - new TracepointData(LINE_NUMBER_4_BYTE_INSTR, NO_CONDITION, 0, true, action1.toString(), acceptsFastTpOnFourBytes()), - new TracepointData(LINE_NUMBER_5_BYTE_INSTR, NO_CONDITION, 0, true, action2.toString(), true), + new TracepointData(getLineForTag("1_BYTE"), NO_CONDITION, 0, true, action1.toString(), false), + new TracepointData(getLineForTag("2_BYTE"), NO_CONDITION, 0, true, action2.toString(), false), + new TracepointData(getLineForTag("3_BYTE"), NO_CONDITION, 0, true, action1.toString(), false), + new TracepointData(getLineForTag("4_BYTE"), NO_CONDITION, 0, true, action1.toString(), acceptsFastTpOnFourBytes()), + new TracepointData(getLineForTag("5_BYTE"), NO_CONDITION, 0, true, action2.toString(), fastTracepointsSupported()), }; checkTracepoints(dataArray); diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/TraceFileTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/TraceFileTest.java index 6c6cb8c8fdf..bfda80c1fcd 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/TraceFileTest.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/TraceFileTest.java @@ -69,8 +69,11 @@ public class TraceFileTest extends BaseParametrizedTestCase { private final static String EXEC_NAME = "TracepointTestApp.exe"; private final static String TRACE_NAME = "trace"; private final static String TRACE_FILE_PATH = EXEC_PATH + TRACE_NAME; - private final static int LINE_NUMBER_1 = 17; - private final static int LINE_NUMBER_2 = 24; + // Breakpoint tags in TracepointTestApp.cc + public static final String[] LINE_TAGS = new String[] { + "IF_X_NE_A", + "INCR_X", + }; private final static String END_FUNCTION = "lastCall"; private final static String TEVAL_STRING = "a"; private final static String COLLECT_STRING1 = "x"; @@ -87,6 +90,7 @@ public class TraceFileTest extends BaseParametrizedTestCase { @Override public void doBeforeTest() throws Exception { + resolveLineTagLocations(SOURCE_NAME, LINE_TAGS); assumeGdbVersionAtLeast(ITestConstants.SUFFIX_GDB_7_4); removeTeminatedLaunchesBeforeTest(); // Suppress settings of the launch attributes and launching. @@ -231,23 +235,17 @@ public class TraceFileTest extends BaseParametrizedTestCase { // This test requires the presence of tracepoints created by another test. // To allow our current test to be independent, we explicitly call // the required test ourselves. - try { - testTraceFile(); - suppressRemoveAllPlatformBreakpoints = true; - try { - // Cleanup the interim launch that we just caused - doAfterTest(); - // Setup for the upcoming launch - doBeforeTest(); - } finally { - suppressRemoveAllPlatformBreakpoints = false; - } - } catch (Throwable t) { - // If we cannot setup properly, ignore the test using the - // assume check below. The reason for the failure could be a missing - // gdbserver, and we don't want to fail a local test due to that - Assume.assumeTrue("Cannot properly setup test", false); - } + testTraceFile(); + suppressRemoveAllPlatformBreakpoints = true; + try { + // Cleanup the interim launch that we just caused + doAfterTest(); + // Setup for the upcoming launch + clearLineTags(); + doBeforeTest(); + } finally { + suppressRemoveAllPlatformBreakpoints = false; + } // Verify that actions and tracepoints required for this test are in place. checkActionsAndTracepoints(); @@ -323,7 +321,7 @@ public class TraceFileTest extends BaseParametrizedTestCase { private void checkTracepoint(ICTracepoint tracepoint) throws Throwable { TracepointActionManager tam = TracepointActionManager.getInstance(); assertTrue(SOURCE_NAME.equals(new Path(tracepoint.getFileName()).lastSegment())); - assertTrue(LINE_NUMBER_1 == tracepoint.getLineNumber() || LINE_NUMBER_2 == tracepoint.getLineNumber()); + assertTrue(getLineForTag("IF_X_NE_A") == tracepoint.getLineNumber() || getLineForTag("INCR_X") == tracepoint.getLineNumber()); String[] actionNames = ((String)tracepoint.getMarker().getAttribute(BreakpointActionManager.BREAKPOINT_ACTION_ATTRIBUTE)).split(TracepointActionManager.TRACEPOINT_ACTION_DELIMITER); for (String name : actionNames) { @@ -456,11 +454,11 @@ public class TraceFileTest extends BaseParametrizedTestCase { Map attributes = new HashMap(); attributes.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.TRACEPOINT); attributes.put(MIBreakpoints.FILE_NAME, SOURCE_NAME); - attributes.put(MIBreakpoints.LINE_NUMBER, LINE_NUMBER_1); + attributes.put(MIBreakpoints.LINE_NUMBER, getLineForTag("IF_X_NE_A")); attributes.put(MIBreakpoints.COMMANDS, evalAction.getName()); insertBreakpoint(fBreakpointsDmc, attributes); - attributes.put(MIBreakpoints.LINE_NUMBER, LINE_NUMBER_2); + attributes.put(MIBreakpoints.LINE_NUMBER, getLineForTag("INCR_X")); attributes.put(MIBreakpoints.COMMANDS, String.format("%s%s%s", collectAction1.getName(), TracepointActionManager.TRACEPOINT_ACTION_DELIMITER, collectAction2.getName()));