/******************************************************************************* * Copyright (c) 2009, 2010 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: * Ericsson - initial API and implementation *******************************************************************************/ 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.LinkedList; import java.util.List; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; import org.eclipse.cdt.dsf.concurrent.Query; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.datamodel.CompositeDMContext; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.IFormattedValues; import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; import org.eclipse.cdt.dsf.debug.service.IRegisters; import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext; import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData; import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMContext; import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMData; import org.eclipse.cdt.dsf.debug.service.IRunControl; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; import org.eclipse.cdt.dsf.mi.service.IMIProcesses; import org.eclipse.cdt.dsf.mi.service.MIProcesses; 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.AsyncCompletionWaitor; import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; import org.eclipse.cdt.tests.dsf.gdb.framework.BaseTestCase; import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil; import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; import org.eclipse.core.runtime.Platform; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(BackgroundRunner.class) public class MIRegistersTest extends BaseTestCase { protected List<String> get_X86_REGS() { List<String> list = new LinkedList<String>(Arrays.asList("eax","ecx","edx","ebx","esp","ebp","esi","edi","eip","eflags", "cs","ss","ds","es","fs","gs","st0","st1","st2","st3", "st4","st5","st6","st7","fctrl","fstat","ftag","fiseg","fioff","foseg", "fooff","fop","xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7", "mxcsr","orig_eax","mm0","mm1","mm2","mm3","mm4","mm5","mm6","mm7")); // On Windows, gdb doesn't report "orig_eax" as a register. Apparently it does on Linux if (Platform.getOS().equals(Platform.OS_WIN32)) { list.remove("orig_eax"); } return list; } /* * Path to executable */ private static final String EXEC_PATH = "data/launch/bin/"; /* * Name of the executable */ private static final String EXEC_NAME = "MultiThread.exe"; private static final String SRC_NAME = "MultiThread.cc"; // Will be used to wait for asynchronous call to complete //private final AsyncCompletionWaitor fWait = new AsyncCompletionWaitor(); private DsfSession fSession; private DsfServicesTracker fServicesTracker; private IContainerDMContext fContainerDmc; private IRegisters fRegService; private IRunControl fRunControl; @Before public void init() throws Exception { fSession = getGDBLaunch().getSession(); Runnable runnable = new Runnable() { public void run() { // We obtain the services we need after the new // launch has been performed fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId()); ICommandControlService commandControl = fServicesTracker.getService(ICommandControlService.class); IMIProcesses procService = fServicesTracker.getService(IMIProcesses.class); IProcessDMContext procDmc = procService.createProcessContext(commandControl.getContext(), MIProcesses.UNIQUE_GROUP_ID); fContainerDmc = procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID); fRegService = fServicesTracker.getService(IRegisters.class); fRunControl = fServicesTracker.getService(IRunControl.class); } }; fSession.getExecutor().submit(runnable).get(); } @BeforeClass public static void beforeClassMethod() { setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, EXEC_PATH + EXEC_NAME); } @After public void tearDown() { fServicesTracker.dispose(); fRegService = null; } /* * This is a common support method which gets the Register Group Information * and verifies it. */ private IRegisterGroupDMContext getRegisterGroup() throws Throwable { final AsyncCompletionWaitor fWait = new AsyncCompletionWaitor(); final DataRequestMonitor<IRegisterGroupDMContext[]> regGroupDone = new DataRequestMonitor<IRegisterGroupDMContext[]>(fRegService.getExecutor(), null) { @Override protected void handleCompleted() { if (isSuccess()) { fWait.setReturnInfo(getData()); } fWait.waitFinished(getStatus()); } }; fRegService.getExecutor().submit(new Runnable() { public void run() { fRegService.getRegisterGroups(fContainerDmc, regGroupDone); } }); fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(fWait.getMessage(), fWait.isOK()); IRegisterGroupDMContext[] regGroupsDMCs = (IRegisterGroupDMContext[])fWait.getReturnInfo(); assertTrue("There was more than one register group (" + regGroupsDMCs.length + ")", //$NON-NLS-1$ regGroupsDMCs.length == 1 ); fWait.waitReset(); return(regGroupsDMCs[0]); } /* * This is a common support method which gets the Registers names. */ private IRegisterDMContext[] getRegisters(final IFrameDMContext frameDmc) throws Throwable { final AsyncCompletionWaitor fWait = new AsyncCompletionWaitor(); final IRegisterGroupDMContext regGroupsDMC = getRegisterGroup(); fRegService.getExecutor().submit(new Runnable() { public void run() { fRegService.getRegisters( new CompositeDMContext(new IDMContext[] { regGroupsDMC, frameDmc} ), new DataRequestMonitor<IRegisterDMContext[]>(fRegService.getExecutor(), null) { @Override protected void handleCompleted() { if (isSuccess()) { fWait.setReturnInfo(getData()); } fWait.waitFinished(getStatus()); } }); } }); fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(fWait.getMessage(), fWait.isOK()); IRegisterDMContext[] regContexts = (IRegisterDMContext[]) fWait.getReturnInfo(); fWait.waitReset(); assertEquals("Wrong number of registers", get_X86_REGS().size(), regContexts.length); return(regContexts); } /************************************************************************* * * The tests for the register service. * *************************************************************************/ @Test public void getRegisterGroups() throws Throwable { final IRegisterGroupDMContext regGroupsDMC = getRegisterGroup(); Query<IRegisterGroupDMData> query = new Query<IRegisterGroupDMData>() { @Override protected void execute(DataRequestMonitor<IRegisterGroupDMData> rm) { fRegService.getRegisterGroupData(regGroupsDMC, rm); } }; fSession.getExecutor().execute(query); IRegisterGroupDMData data = query.get(); assertTrue("The name of the main group should be: General Registers instead of: " + data.getName(), data.getName().equals("General Registers")); } @Test public void getRegistersLength() throws Throwable { MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final IRegisterDMContext[] regDMCs = getRegisters(frameDmc); assertEquals("Wrong number of registers", get_X86_REGS().size(), regDMCs.length); } @Test public void getRegisters() throws Throwable { MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); final IRegisterDMContext[] regDMCs = getRegisters(frameDmc); List<String> regNames = get_X86_REGS(); Query<IRegisterDMData[]> query = new Query<IRegisterDMData[]>() { @Override protected void execute(DataRequestMonitor<IRegisterDMData[]> rm) { final IRegisterDMData[] datas = new IRegisterDMData[regDMCs.length]; rm.setData(datas); final CountingRequestMonitor countingRm = new CountingRequestMonitor(ImmediateExecutor.getInstance(), rm); countingRm.setDoneCount(regDMCs.length); for (int i = 0; i < regDMCs.length; i++) { final int index = i; fRegService.getRegisterData( regDMCs[index], new DataRequestMonitor<IRegisterDMData>(ImmediateExecutor.getInstance(), countingRm) { @Override protected void handleSuccess() { datas[index] = getData(); countingRm.done(); } }); } } }; fSession.getExecutor().execute(query); IRegisterDMData[] datas = query.get(); for(IRegisterDMData data: datas){ String regName = data.getName(); Assert.assertFalse("GDB does not support register name: " + regName, !regNames.contains(regName)); } } private String getModelDataForRegisterDataValue(IFrameDMContext frameDmc, String format, int regNo) throws Throwable { final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); final IRegisterDMContext[] regDMCs = getRegisters(frameDmc); final FormattedValueDMContext valueDmc = fRegService.getFormattedValueContext(regDMCs[regNo], format); final DataRequestMonitor<FormattedValueDMData> regRm = new DataRequestMonitor<FormattedValueDMData>(fRegService.getExecutor(), null) { @Override protected void handleCompleted() { if (isSuccess()) { wait.setReturnInfo(getData()); } wait.waitFinished(getStatus()); } }; fRegService.getExecutor().submit(new Runnable() { public void run() { fRegService.getFormattedExpressionValue(valueDmc, regRm); } }); wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); assertTrue(wait.getMessage(), wait.isOK()); FormattedValueDMData data = (FormattedValueDMData)wait.getReturnInfo(); return data.getFormattedValue(); } private static String REGISTER_VALUE = ""; @Test public void getModelDataForRegisterDataValueInDifferentNumberFormats() throws Throwable { MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); String val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.NATURAL_FORMAT, 0); REGISTER_VALUE = val; assertTrue("Register Value is not in NATURAL format " , Integer.parseInt(val)== Integer.parseInt(REGISTER_VALUE)); val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.HEX_FORMAT, 0); assertTrue("Register Value is not in HEX_FORMAT " ,val.startsWith("0x")); val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.BINARY_FORMAT, 0); Assert.assertEquals("Register Value is not in BINARY_FORMAT ", Integer.toBinaryString(Integer.parseInt(REGISTER_VALUE)), val); val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.DECIMAL_FORMAT , 0); Assert.assertEquals("Register Value is not in DECIMAL_FORMAT", Integer.parseInt(REGISTER_VALUE), Integer.parseInt(val)); val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.OCTAL_FORMAT, 0); assertTrue("Register Value is not in OCTAL_FORMAT " ,val.startsWith("0")); } @Test public void compareRegisterForMultipleExecutionContexts() throws Throwable { // Run past the line that creates a thread and past the sleep that // follows it. This is a bit tricky because the code that creates the // thread is conditional depending on environment. Run to the printf // before it (which is common), then do step operations over the // non-common code (but same number of lines) SyncUtil.runToLine(SRC_NAME, Integer.toString(MIRunControlTest.LINE_MAIN_PRINTF)); SyncUtil.step(StepType.STEP_OVER); // over the printf SyncUtil.step(StepType.STEP_OVER); // over the create-thread call MIStoppedEvent stoppedEvent = SyncUtil.step(StepType.STEP_OVER, TestsPlugin.massageTimeout(2000)); // over the one second sleep // Get the thread IDs final IContainerDMContext containerDmc = DMContexts.getAncestorOfType(stoppedEvent.getDMContext(), IContainerDMContext.class); final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); final DataRequestMonitor<IExecutionDMContext[]> drm = new DataRequestMonitor<IExecutionDMContext[]>(fRegService.getExecutor(), null) { @Override protected void handleCompleted() { if (isSuccess()) { wait.setReturnInfo(getData()); } wait.waitFinished(getStatus()); } }; fRegService.getExecutor().submit(new Runnable() { public void run() { fRunControl.getExecutionContexts(containerDmc, drm); } }); wait.waitUntilDone(TestsPlugin.massageTimeout(5000)); Assert.assertTrue(wait.getMessage(), wait.isOK()); IExecutionDMContext[] ctxts = (IExecutionDMContext[])wait.getReturnInfo(); wait.waitReset(); Assert.assertNotNull(ctxts); Assert.assertTrue(ctxts.length > 1); int tid1 = ((IMIExecutionDMContext)ctxts[0]).getThreadId(); int tid2 = ((IMIExecutionDMContext)ctxts[1]).getThreadId(); // Get execution context to thread 2 IExecutionDMContext execDmc = SyncUtil.createExecutionContext(containerDmc, tid2); IFrameDMContext frameDmc2 = SyncUtil.getStackFrame(execDmc, 0); String thread2RegVal0 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 0); String thread2RegVal1 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 1); String thread2RegVal2 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 2); String thread2RegVal3 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 3); String thread2RegVal4 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 4); String thread2RegVal5 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 5); // Get execution context to thread 1 execDmc = SyncUtil.createExecutionContext(containerDmc, tid1); IFrameDMContext frameDmc1 = SyncUtil.getStackFrame(execDmc, 0); getModelDataForRegisterDataValue(frameDmc1, IFormattedValues.NATURAL_FORMAT, 0); // Re-set the execution context to 2 and Fetch from the Cache String dupliThread2RegVal0 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 0); String dupliThread2RegVal1 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 1); String dupliThread2RegVal2 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 2); String dupliThread2RegVal3 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 3); String dupliThread2RegVal4 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 4); String dupliThread2RegVal5= getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 5); // If Values not equal , then context haven't been re-set properly assertTrue("Multiple context not working. Execution Context is not reset to 2", thread2RegVal0.equals(dupliThread2RegVal0)); assertTrue("Multiple context not working. Execution Context is not reset to 2", thread2RegVal1.equals(dupliThread2RegVal1)); assertTrue("Multiple context not working. Execution Context is not reset to 2", thread2RegVal2.equals(dupliThread2RegVal2)); assertTrue("Multiple context not working. Execution Context is not reset to 2", thread2RegVal3.equals(dupliThread2RegVal3)); assertTrue("Multiple context not working. Execution Context is not reset to 2", thread2RegVal4.equals(dupliThread2RegVal4)); assertTrue("Multiple context not working. Execution Context is not reset to 2", thread2RegVal5.equals(dupliThread2RegVal5)); } private void writeRegister(IFrameDMContext frameDmc, final int regIndex, final String regValue, final String formatId) throws Throwable { final AsyncCompletionWaitor fWait = new AsyncCompletionWaitor(); final IRegisterDMContext[] regDMCs = getRegisters(frameDmc); fRegService.getExecutor().submit(new Runnable() { public void run() { fRegService.writeRegister( regDMCs[regIndex], regValue, formatId, new RequestMonitor(fRegService.getExecutor(), null) { @Override protected void handleCompleted() { fWait.waitFinished(getStatus()); } }); } }); fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); fWait.waitReset(); } @Test public void writeRegisterNaturalFormat() throws Throwable{ MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); String regValue = "10"; int regIndex = 3; writeRegister(frameDmc, 3, regValue, IFormattedValues.NATURAL_FORMAT); String val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.NATURAL_FORMAT, regIndex); assertTrue("Failed writing register. New value should have been " + regValue, regValue.equals(val)); } @Test public void writeRegisterHEXFormat() throws Throwable{ MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); String regValue = "0x10"; int regIndex = 3; writeRegister(frameDmc, 3, regValue, IFormattedValues.HEX_FORMAT); String val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.HEX_FORMAT, regIndex); assertTrue("Failed writing register. New value should have been " + regValue, regValue.equals(val)); } @Test @Ignore public void writeRegisterBinaryFormat() throws Throwable{ MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); //String regValue = "0100101001"; String regValue = "10"; int regIndex = 3; writeRegister(frameDmc, 3, regValue, IFormattedValues.BINARY_FORMAT); String val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.BINARY_FORMAT, regIndex); assertTrue("Failed writing register. New value should have been " + regValue + " instead of " + val, regValue.equals(val)); } @Test public void writeRegisterOctalFormat() throws Throwable{ MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); //String regValue = "10"; String regValue = "012"; int regIndex = 3; writeRegister(frameDmc, 3, regValue, IFormattedValues.OCTAL_FORMAT); String val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.OCTAL_FORMAT, regIndex); assertTrue("Failed writing register. New value should have been " + regValue + "instead of " + val, regValue.equals(val)); } }