mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-08-04 14:55:41 +02:00
Bug 397715 - [memory][expressions] Make use of GDB 7.6 new
=memory-changed MI event Change-Id: I98c3cd273e185dfb1782527d7abd553351a163d9 Reviewed-on: https://git.eclipse.org/r/9662 Reviewed-by: Marc Khouzam <marc.khouzam@ericsson.com> IP-Clean: Marc Khouzam <marc.khouzam@ericsson.com> Tested-by: Marc Khouzam <marc.khouzam@ericsson.com>
This commit is contained in:
parent
7f1f78e77a
commit
0248aa4474
7 changed files with 551 additions and 19 deletions
|
@ -0,0 +1,165 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2013 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:
|
||||||
|
* Marc Khouzam (Ericsson) - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.cdt.dsf.gdb.service;
|
||||||
|
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.core.IAddress;
|
||||||
|
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
|
||||||
|
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
|
||||||
|
import org.eclipse.cdt.dsf.datamodel.DMContexts;
|
||||||
|
import org.eclipse.cdt.dsf.debug.service.IMemory;
|
||||||
|
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
|
||||||
|
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
|
||||||
|
import org.eclipse.cdt.dsf.debug.service.command.IEventListener;
|
||||||
|
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||||
|
import org.eclipse.cdt.dsf.mi.service.IMIProcesses;
|
||||||
|
import org.eclipse.cdt.dsf.mi.service.MIMemory;
|
||||||
|
import org.eclipse.cdt.dsf.mi.service.command.output.MIConst;
|
||||||
|
import org.eclipse.cdt.dsf.mi.service.command.output.MINotifyAsyncOutput;
|
||||||
|
import org.eclipse.cdt.dsf.mi.service.command.output.MIOOBRecord;
|
||||||
|
import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput;
|
||||||
|
import org.eclipse.cdt.dsf.mi.service.command.output.MIResult;
|
||||||
|
import org.eclipse.cdt.dsf.mi.service.command.output.MIValue;
|
||||||
|
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||||
|
import org.eclipse.cdt.utils.Addr64;
|
||||||
|
import org.eclipse.core.runtime.IStatus;
|
||||||
|
import org.eclipse.core.runtime.Status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Memory service that uses the enhancements from GDB 7.6:
|
||||||
|
* =memory-changed MI event
|
||||||
|
*
|
||||||
|
* @since 4.2
|
||||||
|
*/
|
||||||
|
public class GDBMemory_7_6 extends GDBMemory_7_0 implements IEventListener {
|
||||||
|
|
||||||
|
private ICommandControlService fConnection;
|
||||||
|
|
||||||
|
public GDBMemory_7_6(DsfSession session) {
|
||||||
|
super(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(final RequestMonitor requestMonitor) {
|
||||||
|
super.initialize(
|
||||||
|
new ImmediateRequestMonitor(requestMonitor) {
|
||||||
|
@Override
|
||||||
|
public void handleSuccess() {
|
||||||
|
doInitialize(requestMonitor);
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doInitialize(final RequestMonitor requestMonitor) {
|
||||||
|
register(new String[] { MIMemory.class.getName(),
|
||||||
|
IMemory.class.getName(),
|
||||||
|
GDBMemory_7_0.class.getName(),
|
||||||
|
GDBMemory_7_6.class.getName()},
|
||||||
|
new Hashtable<String, String>());
|
||||||
|
|
||||||
|
fConnection = getServicesTracker().getService(ICommandControlService.class);
|
||||||
|
if (fConnection == null) {
|
||||||
|
requestMonitor.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, "CommandControl Service is not available")); //$NON-NLS-1$
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fConnection.addEventListener(this);
|
||||||
|
|
||||||
|
requestMonitor.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown(final RequestMonitor requestMonitor) {
|
||||||
|
fConnection.removeEventListener(this);
|
||||||
|
unregister();
|
||||||
|
super.shutdown(requestMonitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eventReceived(Object output) {
|
||||||
|
if (output instanceof MIOutput) {
|
||||||
|
MIOOBRecord[] records = ((MIOutput)output).getMIOOBRecords();
|
||||||
|
for (MIOOBRecord r : records) {
|
||||||
|
if (r instanceof MINotifyAsyncOutput) {
|
||||||
|
MINotifyAsyncOutput notifyOutput = (MINotifyAsyncOutput)r;
|
||||||
|
String asyncClass = notifyOutput.getAsyncClass();
|
||||||
|
// These events have been added with GDB 7.6
|
||||||
|
if ("memory-changed".equals(asyncClass)) { //$NON-NLS-1$
|
||||||
|
String groupId = null;
|
||||||
|
String addr = null;
|
||||||
|
int length = 0;
|
||||||
|
|
||||||
|
MIResult[] results = notifyOutput.getMIResults();
|
||||||
|
for (int i = 0; i < results.length; i++) {
|
||||||
|
String var = results[i].getVariable();
|
||||||
|
MIValue val = results[i].getMIValue();
|
||||||
|
if (var.equals("thread-group")) { //$NON-NLS-1$
|
||||||
|
if (val instanceof MIConst) {
|
||||||
|
groupId = ((MIConst)val).getString();
|
||||||
|
}
|
||||||
|
} else if (var.equals("addr")) { //$NON-NLS-1$
|
||||||
|
if (val instanceof MIConst) {
|
||||||
|
addr = ((MIConst)val).getString();
|
||||||
|
}
|
||||||
|
} else if (var.equals("len")) { //$NON-NLS-1$
|
||||||
|
if (val instanceof MIConst) {
|
||||||
|
try {
|
||||||
|
String lenStr = ((MIConst)val).getString().trim();
|
||||||
|
if (lenStr.startsWith("0x")) { //$NON-NLS-1$
|
||||||
|
length = Integer.parseInt(lenStr.substring(2), 16);
|
||||||
|
} else {
|
||||||
|
length = Integer.parseInt(lenStr);
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
assert false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (var.equals("type")) { //$NON-NLS-1$
|
||||||
|
if (val instanceof MIConst) {
|
||||||
|
if ("code".equals(((MIConst)val).getString())) { //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IMIProcesses procService = getServicesTracker().getService(IMIProcesses.class);
|
||||||
|
if (procService != null && groupId != null && addr != null && length > 0) {
|
||||||
|
IContainerDMContext containerDmc =
|
||||||
|
procService.createContainerContextFromGroupId(fConnection.getContext(), groupId);
|
||||||
|
|
||||||
|
// Now refresh our memory cache, it case it contained this address. Don't have
|
||||||
|
// it send the potential IMemoryChangedEvent as we will send it ourselves (see below).
|
||||||
|
final IMemoryDMContext memoryDMC = DMContexts.getAncestorOfType(containerDmc, IMemoryDMContext.class);
|
||||||
|
final IAddress address = new Addr64(addr);
|
||||||
|
// The length returned by GDB is in bytes, while the memory cache expects
|
||||||
|
// a count of number of addresses of 8 bytes.
|
||||||
|
int count = length/8 + 1;
|
||||||
|
getMemoryCache(memoryDMC).refreshMemory(memoryDMC, address, 0, 1, count, false,
|
||||||
|
new RequestMonitor(getExecutor(), null) {
|
||||||
|
@Override
|
||||||
|
protected void handleCompleted() {
|
||||||
|
// Only once the memory cache is updated, we send the IMemoryChangedEvent. If we were to do it
|
||||||
|
// earlier, the memory view may not show the updated value.
|
||||||
|
//
|
||||||
|
// We must always send this event when GDB reports a memory change because it can mean that
|
||||||
|
// an expression or register has changed, and therefore we must notify the different views
|
||||||
|
// and services of it. We cannot rely on this event to be sent by the memory cache after being
|
||||||
|
// refreshed, because if the memory cache does not contain this address, it will not send
|
||||||
|
// the event.
|
||||||
|
getSession().dispatchEvent(new MemoryChangedEvent(memoryDMC, new IAddress[] { address }), getProperties());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (c) 2008, 2012 Ericsson and others.
|
* Copyright (c) 2008, 2013 Ericsson and others.
|
||||||
* All rights reserved. This program and the accompanying materials
|
* All rights reserved. This program and the accompanying materials
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
* which accompanies this distribution, and is available at
|
* which accompanies this distribution, and is available at
|
||||||
|
@ -12,6 +12,7 @@
|
||||||
* Marc Khouzam (Ericsson) - Support for GDB 7.4 (Bug 367788)
|
* Marc Khouzam (Ericsson) - Support for GDB 7.4 (Bug 367788)
|
||||||
* Marc Khouzam (Ericsson) - Include IGDBHardware service for the multicore visualizer (Bug 335027)
|
* Marc Khouzam (Ericsson) - Include IGDBHardware service for the multicore visualizer (Bug 335027)
|
||||||
* Vladimir Prus (Mentor Graphics) - Support for OS resources.
|
* Vladimir Prus (Mentor Graphics) - Support for OS resources.
|
||||||
|
* Marc Khouzam (Ericsson) - Support for GDB 7.6 memory service
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.dsf.gdb.service;
|
package org.eclipse.cdt.dsf.gdb.service;
|
||||||
|
|
||||||
|
@ -67,6 +68,8 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory {
|
||||||
public static final String GDB_7_4_VERSION = "7.4"; //$NON-NLS-1$
|
public static final String GDB_7_4_VERSION = "7.4"; //$NON-NLS-1$
|
||||||
/** @since 4.2*/
|
/** @since 4.2*/
|
||||||
public static final String GDB_7_5_VERSION = "7.5"; //$NON-NLS-1$
|
public static final String GDB_7_5_VERSION = "7.5"; //$NON-NLS-1$
|
||||||
|
/** @since 4.2*/
|
||||||
|
public static final String GDB_7_6_VERSION = "7.5.50"; //$NON-NLS-1$
|
||||||
|
|
||||||
private final String fVersion;
|
private final String fVersion;
|
||||||
|
|
||||||
|
@ -172,6 +175,10 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected IMemory createMemoryService(DsfSession session) {
|
protected IMemory createMemoryService(DsfSession session) {
|
||||||
|
if (GDB_7_6_VERSION.compareTo(fVersion) <= 0) {
|
||||||
|
return new GDBMemory_7_6(session);
|
||||||
|
}
|
||||||
|
|
||||||
if (GDB_7_0_VERSION.compareTo(fVersion) <= 0) {
|
if (GDB_7_0_VERSION.compareTo(fVersion) <= 0) {
|
||||||
return new GDBMemory_7_0(session);
|
return new GDBMemory_7_0(session);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.eclipse.cdt.dsf.datamodel.DMContexts;
|
||||||
import org.eclipse.cdt.dsf.datamodel.IDMContext;
|
import org.eclipse.cdt.dsf.datamodel.IDMContext;
|
||||||
import org.eclipse.cdt.dsf.debug.service.ICachingService;
|
import org.eclipse.cdt.dsf.debug.service.ICachingService;
|
||||||
import org.eclipse.cdt.dsf.debug.service.IExpressions;
|
import org.eclipse.cdt.dsf.debug.service.IExpressions;
|
||||||
|
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionChangedDMEvent;
|
||||||
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMAddress;
|
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMAddress;
|
||||||
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
|
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
|
||||||
import org.eclipse.cdt.dsf.debug.service.IMemory;
|
import org.eclipse.cdt.dsf.debug.service.IMemory;
|
||||||
|
@ -92,7 +93,8 @@ public class MIMemory extends AbstractDsfService implements IMemory, ICachingSer
|
||||||
// Map of memory caches
|
// Map of memory caches
|
||||||
private Map<IMemoryDMContext, MIMemoryCache> fMemoryCaches;
|
private Map<IMemoryDMContext, MIMemoryCache> fMemoryCaches;
|
||||||
|
|
||||||
private MIMemoryCache getMemoryCache(IMemoryDMContext memoryDMC) {
|
/** @since 4.2 */
|
||||||
|
protected MIMemoryCache getMemoryCache(IMemoryDMContext memoryDMC) {
|
||||||
MIMemoryCache cache = fMemoryCaches.get(memoryDMC);
|
MIMemoryCache cache = fMemoryCaches.get(memoryDMC);
|
||||||
if (cache == null) {
|
if (cache == null) {
|
||||||
cache = new MIMemoryCache();
|
cache = new MIMemoryCache();
|
||||||
|
@ -491,11 +493,18 @@ public class MIMemory extends AbstractDsfService implements IMemory, ICachingSer
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @nooverride This method is not intended to be re-implemented or extended by clients.
|
* @deprecated Replaced by the generic {@link #eventDispatched(IExpressionChangedDMEvent)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void eventDispatched(ExpressionChangedEvent e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* @noreference This method is not intended to be referenced by clients.
|
* @noreference This method is not intended to be referenced by clients.
|
||||||
|
* @since 4.2
|
||||||
*/
|
*/
|
||||||
@DsfServiceEventHandler
|
@DsfServiceEventHandler
|
||||||
public void eventDispatched(ExpressionChangedEvent e) {
|
public void eventDispatched(IExpressionChangedDMEvent e) {
|
||||||
|
|
||||||
// Get the context and expression service handle
|
// Get the context and expression service handle
|
||||||
final IExpressionDMContext context = e.getDMContext();
|
final IExpressionDMContext context = e.getDMContext();
|
||||||
|
@ -518,7 +527,7 @@ public class MIMemory extends AbstractDsfService implements IMemory, ICachingSer
|
||||||
address = new Addr64(expAddress.getValue());
|
address = new Addr64(expAddress.getValue());
|
||||||
|
|
||||||
final IMemoryDMContext memoryDMC = DMContexts.getAncestorOfType(context, IMemoryDMContext.class);
|
final IMemoryDMContext memoryDMC = DMContexts.getAncestorOfType(context, IMemoryDMContext.class);
|
||||||
getMemoryCache(memoryDMC).refreshMemory(memoryDMC, address, 0, 1, count,
|
getMemoryCache(memoryDMC).refreshMemory(memoryDMC, address, 0, 1, count, true,
|
||||||
new RequestMonitor(getExecutor(), null));
|
new RequestMonitor(getExecutor(), null));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -629,7 +638,8 @@ public class MIMemory extends AbstractDsfService implements IMemory, ICachingSer
|
||||||
// MIMemoryCache
|
// MIMemoryCache
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private class MIMemoryCache {
|
/** @since 4.2 */
|
||||||
|
protected class MIMemoryCache {
|
||||||
// The memory cache data structure
|
// The memory cache data structure
|
||||||
private SortedMemoryBlockList fMemoryBlockList;
|
private SortedMemoryBlockList fMemoryBlockList;
|
||||||
|
|
||||||
|
@ -965,10 +975,12 @@ public class MIMemory extends AbstractDsfService implements IMemory, ICachingSer
|
||||||
* @param offset
|
* @param offset
|
||||||
* @param word_size
|
* @param word_size
|
||||||
* @param count
|
* @param count
|
||||||
|
* @param sendMemoryEvent Indicates if a IMemoryChangedEvent should be sent if the memory cache has changed.
|
||||||
* @param rm
|
* @param rm
|
||||||
*/
|
*/
|
||||||
public void refreshMemory(final IMemoryDMContext memoryDMC, final IAddress address,
|
public void refreshMemory(final IMemoryDMContext memoryDMC, final IAddress address,
|
||||||
final long offset, final int word_size, final int count, final RequestMonitor rm)
|
final long offset, final int word_size, final int count, final boolean sendMemoryEvent,
|
||||||
|
final RequestMonitor rm)
|
||||||
{
|
{
|
||||||
// Check if we already cache part of this memory area (which means it
|
// Check if we already cache part of this memory area (which means it
|
||||||
// is used by a memory service client that will have to be updated)
|
// is used by a memory service client that will have to be updated)
|
||||||
|
@ -984,15 +996,9 @@ public class MIMemory extends AbstractDsfService implements IMemory, ICachingSer
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the data for the MemoryChangedEvent
|
|
||||||
final IAddress[] addresses = new IAddress[count];
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
addresses[i] = address.add(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the corresponding memory block
|
// Read the corresponding memory block
|
||||||
fCommandCache.reset();
|
fCommandCache.reset();
|
||||||
readMemoryBlock(memoryDMC, address, 0, 1, count,
|
readMemoryBlock(memoryDMC, address, offset, word_size, count,
|
||||||
new DataRequestMonitor<MemoryByte[]>(getExecutor(), rm) {
|
new DataRequestMonitor<MemoryByte[]>(getExecutor(), rm) {
|
||||||
@Override
|
@Override
|
||||||
protected void handleSuccess() {
|
protected void handleSuccess() {
|
||||||
|
@ -1006,9 +1012,16 @@ public class MIMemory extends AbstractDsfService implements IMemory, ICachingSer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (blocksDiffer) {
|
if (blocksDiffer) {
|
||||||
updateMemoryCache(address, count, newBlock);
|
updateMemoryCache(address.add(offset), count, newBlock);
|
||||||
|
if (sendMemoryEvent) {
|
||||||
|
// Send the MemoryChangedEvent
|
||||||
|
final IAddress[] addresses = new IAddress[count];
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
addresses[i] = address.add(offset + i);
|
||||||
|
}
|
||||||
getSession().dispatchEvent(new MemoryChangedEvent(memoryDMC, addresses), getProperties());
|
getSession().dispatchEvent(new MemoryChangedEvent(memoryDMC, addresses), getProperties());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
rm.done();
|
rm.done();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int testMemoryChanges() {
|
||||||
|
int i = 8;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
printf("Running ConsoleSyncTestApp\n");
|
||||||
|
|
||||||
|
testMemoryChanges();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,330 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2013 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:
|
||||||
|
* Marc Khouzam (Ericsson) - Initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
package org.eclipse.cdt.tests.dsf.gdb.tests.tests_7_6;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
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.datamodel.IDMContext;
|
||||||
|
import org.eclipse.cdt.dsf.datamodel.IDMEvent;
|
||||||
|
import org.eclipse.cdt.dsf.debug.service.IExpressions;
|
||||||
|
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMAddress;
|
||||||
|
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
|
||||||
|
import org.eclipse.cdt.dsf.debug.service.IMemory;
|
||||||
|
import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent;
|
||||||
|
import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext;
|
||||||
|
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
|
||||||
|
import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl;
|
||||||
|
import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent;
|
||||||
|
import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo;
|
||||||
|
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
|
||||||
|
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
|
||||||
|
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||||
|
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.cdt.tests.dsf.gdb.tests.ITestConstants;
|
||||||
|
import org.eclipse.debug.core.model.MemoryByte;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test case verifies that different commands issued from the
|
||||||
|
* GDB console cause proper updating within the CDT views.
|
||||||
|
*/
|
||||||
|
@RunWith(BackgroundRunner.class)
|
||||||
|
public class GDBConsoleSynchronizingTest_7_6 extends BaseTestCase {
|
||||||
|
|
||||||
|
final static private int DEFAULT_TIMEOUT = 1000;
|
||||||
|
final static private TimeUnit DEFAULT_TIME_UNIT = TimeUnit.MILLISECONDS;
|
||||||
|
|
||||||
|
private DsfSession fSession;
|
||||||
|
private DsfServicesTracker fServicesTracker;
|
||||||
|
private IGDBControl fCommandControl;
|
||||||
|
private IMemory fMemoryService;
|
||||||
|
private IExpressions fExprService;
|
||||||
|
|
||||||
|
private List<IDMEvent<? extends IDMContext>> fEventsReceived = new ArrayList<IDMEvent<? extends IDMContext>>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setGdbVersion() {
|
||||||
|
setGdbProgramNamesLaunchAttributes(ITestConstants.SUFFIX_GDB_7_6);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setLaunchAttributes() {
|
||||||
|
super.setLaunchAttributes();
|
||||||
|
|
||||||
|
setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, "data/launch/bin/ConsoleSyncTestApp.exe");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doBeforeTest() throws Exception {
|
||||||
|
super.doBeforeTest();
|
||||||
|
|
||||||
|
fSession = getGDBLaunch().getSession();
|
||||||
|
Runnable runnable = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId());
|
||||||
|
Assert.assertTrue(fServicesTracker != null);
|
||||||
|
|
||||||
|
fCommandControl = fServicesTracker.getService(IGDBControl.class);
|
||||||
|
Assert.assertTrue(fCommandControl != null);
|
||||||
|
|
||||||
|
fMemoryService = fServicesTracker.getService(IMemory.class);
|
||||||
|
Assert.assertTrue(fMemoryService != null);
|
||||||
|
|
||||||
|
fExprService = fServicesTracker.getService(IExpressions.class);
|
||||||
|
Assert.assertTrue(fExprService != null);
|
||||||
|
|
||||||
|
// Register to breakpoint events
|
||||||
|
fSession.addServiceEventListener(GDBConsoleSynchronizingTest_7_6.this, null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fSession.getExecutor().submit(runnable).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAfterTest() throws Exception {
|
||||||
|
Runnable runnable = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
fSession.removeServiceEventListener(GDBConsoleSynchronizingTest_7_6.this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fSession.getExecutor().submit(runnable).get();
|
||||||
|
fEventsReceived.clear();
|
||||||
|
fServicesTracker.dispose();
|
||||||
|
super.doAfterTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Start of tests
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test verifies that setting a variable from the console
|
||||||
|
* using the set command will properly trigger a DSF event to
|
||||||
|
* indicate the change. This test makes sure the value that
|
||||||
|
* changes is in the memory cache also.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSettingVariableWithSetWithMemory() throws Throwable {
|
||||||
|
MIStoppedEvent stoppedEvent = SyncUtil.runToLocation("testMemoryChanges");
|
||||||
|
|
||||||
|
final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0);
|
||||||
|
final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, "i");
|
||||||
|
|
||||||
|
// Read the memory that will change first, or else there will be no event for it
|
||||||
|
Query<IExpressionDMAddress> query = new Query<IExpressionDMAddress>() {
|
||||||
|
@Override
|
||||||
|
protected void execute(final DataRequestMonitor<IExpressionDMAddress> rm) {
|
||||||
|
fExprService.getExpressionAddressData(exprDmc, rm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fSession.getExecutor().execute(query);
|
||||||
|
IExpressionDMAddress data = query.get();
|
||||||
|
|
||||||
|
IMemoryDMContext memoryDmc = DMContexts.getAncestorOfType(frameDmc, IMemoryDMContext.class);
|
||||||
|
readMemory(memoryDmc, data.getAddress(), data.getSize());
|
||||||
|
|
||||||
|
fEventsReceived.clear();
|
||||||
|
queueConsoleCommand("set variable i=100");
|
||||||
|
|
||||||
|
IMemoryChangedEvent memoryEvent = waitForEvent(IMemoryChangedEvent.class);
|
||||||
|
assertEquals(1, memoryEvent.getAddresses().length);
|
||||||
|
assertEquals(data.getAddress(), memoryEvent.getAddresses()[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test verifies that setting a variable from the console
|
||||||
|
* using the set command will properly trigger a DSF event to
|
||||||
|
* indicate the change, when the address is not in the memory cache.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSettingVariableWithSet() throws Throwable {
|
||||||
|
MIStoppedEvent stoppedEvent = SyncUtil.runToLocation("testMemoryChanges");
|
||||||
|
|
||||||
|
final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0);
|
||||||
|
final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, "i");
|
||||||
|
|
||||||
|
// Read the memory that will change first, or else there will be no event for it
|
||||||
|
Query<IExpressionDMAddress> query = new Query<IExpressionDMAddress>() {
|
||||||
|
@Override
|
||||||
|
protected void execute(final DataRequestMonitor<IExpressionDMAddress> rm) {
|
||||||
|
fExprService.getExpressionAddressData(exprDmc, rm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fSession.getExecutor().execute(query);
|
||||||
|
IExpressionDMAddress data = query.get();
|
||||||
|
|
||||||
|
fEventsReceived.clear();
|
||||||
|
queueConsoleCommand("set variable i=100");
|
||||||
|
|
||||||
|
IMemoryChangedEvent memoryEvent = waitForEvent(IMemoryChangedEvent.class);
|
||||||
|
assertEquals(1, memoryEvent.getAddresses().length);
|
||||||
|
assertEquals(data.getAddress(), memoryEvent.getAddresses()[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test verifies that setting a variable from the console
|
||||||
|
* using the print command will properly trigger a DSF event
|
||||||
|
* to indicate the change.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSettingVariableWithPrint() throws Throwable {
|
||||||
|
MIStoppedEvent stoppedEvent = SyncUtil.runToLocation("testMemoryChanges");
|
||||||
|
|
||||||
|
final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0);
|
||||||
|
final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, "i");
|
||||||
|
|
||||||
|
// Read the memory that will change first, or else there will be no event for it
|
||||||
|
Query<IExpressionDMAddress> query = new Query<IExpressionDMAddress>() {
|
||||||
|
@Override
|
||||||
|
protected void execute(final DataRequestMonitor<IExpressionDMAddress> rm) {
|
||||||
|
fExprService.getExpressionAddressData(exprDmc, rm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fSession.getExecutor().execute(query);
|
||||||
|
IExpressionDMAddress data = query.get();
|
||||||
|
|
||||||
|
fEventsReceived.clear();
|
||||||
|
queueConsoleCommand("print i=100");
|
||||||
|
|
||||||
|
IMemoryChangedEvent memoryEvent = waitForEvent(IMemoryChangedEvent.class);
|
||||||
|
assertEquals(1, memoryEvent.getAddresses().length);
|
||||||
|
assertEquals(data.getAddress(), memoryEvent.getAddresses()[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test verifies that setting a memory location from the
|
||||||
|
* console will properly trigger a DSF event to indicate the change.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSettingMemory() throws Throwable {
|
||||||
|
MIStoppedEvent stoppedEvent = SyncUtil.runToLocation("testMemoryChanges");
|
||||||
|
|
||||||
|
final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0);
|
||||||
|
final IExpressionDMContext exprDmc = SyncUtil.createExpression(frameDmc, "i");
|
||||||
|
|
||||||
|
// Read the memory that will change first, or else there will be no event for it
|
||||||
|
Query<IExpressionDMAddress> query = new Query<IExpressionDMAddress>() {
|
||||||
|
@Override
|
||||||
|
protected void execute(final DataRequestMonitor<IExpressionDMAddress> rm) {
|
||||||
|
fExprService.getExpressionAddressData(exprDmc, rm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fSession.getExecutor().execute(query);
|
||||||
|
IExpressionDMAddress data = query.get();
|
||||||
|
|
||||||
|
fEventsReceived.clear();
|
||||||
|
queueConsoleCommand("set {int}&i=100");
|
||||||
|
|
||||||
|
IMemoryChangedEvent memoryEvent = waitForEvent(IMemoryChangedEvent.class);
|
||||||
|
assertEquals(1, memoryEvent.getAddresses().length);
|
||||||
|
assertEquals(data.getAddress(), memoryEvent.getAddresses()[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// End of tests
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@DsfServiceEventHandler
|
||||||
|
public void eventDispatched(IDMEvent<? extends IDMContext> e) {
|
||||||
|
synchronized(this) {
|
||||||
|
fEventsReceived.add(e);
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MemoryByte[] readMemory(final IMemoryDMContext dmc, final IAddress address, final int count)
|
||||||
|
throws Throwable
|
||||||
|
{
|
||||||
|
Query<MemoryByte[]> query = new Query<MemoryByte[]>() {
|
||||||
|
@Override
|
||||||
|
protected void execute(DataRequestMonitor<MemoryByte[]> rm) {
|
||||||
|
fMemoryService.getMemory(dmc, address, 0, 1, count, rm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fSession.getExecutor().execute(query);
|
||||||
|
return query.get(DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void queueConsoleCommand(String command) throws Throwable {
|
||||||
|
queueConsoleCommand(command, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void queueConsoleCommand(final String command, int timeout, TimeUnit unit) throws Throwable {
|
||||||
|
Query<MIInfo> query = new Query<MIInfo>() {
|
||||||
|
@Override
|
||||||
|
protected void execute(DataRequestMonitor<MIInfo> rm) {
|
||||||
|
fCommandControl.queueCommand(
|
||||||
|
fCommandControl.getCommandFactory().createMIInterpreterExecConsole(
|
||||||
|
fCommandControl.getContext(),
|
||||||
|
command),
|
||||||
|
rm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fSession.getExecutor().execute(query);
|
||||||
|
query.get(timeout, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <V extends IDMEvent<? extends IDMContext>> V waitForEvent(Class<V> eventType) throws Exception {
|
||||||
|
return waitForEvent(eventType, DEFAULT_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <V extends IDMEvent<? extends IDMContext>> V waitForEvent(Class<V> eventType, int timeout) throws Exception {
|
||||||
|
IDMEvent<?> event = getEvent(eventType);
|
||||||
|
if (event == null) {
|
||||||
|
synchronized(this) {
|
||||||
|
try {
|
||||||
|
wait(timeout);
|
||||||
|
}
|
||||||
|
catch (InterruptedException ex) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
event = getEvent(eventType);
|
||||||
|
if (event == null) {
|
||||||
|
throw new Exception(String.format("Timed out waiting for '%s' to occur.", eventType.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (V)event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private synchronized <V extends IDMEvent<? extends IDMContext>> V getEvent(Class<V> eventType) {
|
||||||
|
for (IDMEvent<?> e : fEventsReceived) {
|
||||||
|
if (eventType.isAssignableFrom(e.getClass())) {
|
||||||
|
fEventsReceived.remove(e);
|
||||||
|
return (V)e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -48,6 +48,7 @@ import org.junit.runners.Suite;
|
||||||
GDBMultiNonStopRunControlTest_7_6.class,
|
GDBMultiNonStopRunControlTest_7_6.class,
|
||||||
Suite_Sessionless_Tests.class,
|
Suite_Sessionless_Tests.class,
|
||||||
GDBConsoleBreakpointsTest_7_6.class,
|
GDBConsoleBreakpointsTest_7_6.class,
|
||||||
|
GDBConsoleSynchronizingTest_7_6.class,
|
||||||
/* Add your test class here */
|
/* Add your test class here */
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ import org.junit.runners.Suite;
|
||||||
Suite_Sessionless_Tests.class,
|
Suite_Sessionless_Tests.class,
|
||||||
GDBConsoleBreakpointsTest_7_6.class,
|
GDBConsoleBreakpointsTest_7_6.class,
|
||||||
TraceFileTest_7_6.class,
|
TraceFileTest_7_6.class,
|
||||||
|
GDBConsoleSynchronizingTest_7_6.class,
|
||||||
/* Add your test class here */
|
/* Add your test class here */
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue