/******************************************************************************* * Copyright (c) 2013, 2014 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 * Alvaro Sanchez-Leon (Ericsson AB) - [Memory] Support 16 bit addressable size (Bug 426730) *******************************************************************************/ 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(), IGDBMemory.class.getName(), IGDBMemory2.class.getName(), GDBMemory.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 count = 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(); // count is expected in addressable units if (lenStr.startsWith("0x")) { //$NON-NLS-1$ count = Integer.parseInt(lenStr.substring(2), 16); } else { count = 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 && count > 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); getMemoryCache(memoryDMC).refreshMemory(memoryDMC, address, 0, getAddressableSize(memoryDMC), 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()); } }); } } } } } } }