| /******************************************************************************* |
| * Copyright (c) 2013, 2014 Mentor Graphics 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: |
| * Mentor Graphics - Initial API and implementation |
| * John Dallaway - Add methods to get the endianness and address size (Bug 225609) |
| * Philippe Gil (AdaCore) - Switch to c language when getting sizeof(void *) when required (Bug 421541) |
| * Alvaro Sanchez-Leon (Ericsson AB) - [Memory] Support 16 bit addressable size (Bug 426730) |
| *******************************************************************************/ |
| package org.eclipse.cdt.dsf.gdb.service; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.Map; |
| |
| import org.eclipse.cdt.core.IAddress; |
| import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; |
| import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor; |
| import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; |
| import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor; |
| import org.eclipse.cdt.dsf.concurrent.RequestMonitor; |
| import org.eclipse.cdt.dsf.concurrent.Sequence; |
| import org.eclipse.cdt.dsf.datamodel.DMContexts; |
| import org.eclipse.cdt.dsf.datamodel.IDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IExpressions; |
| import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IMemory; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; |
| import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; |
| import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; |
| import org.eclipse.cdt.dsf.mi.service.MIMemory; |
| import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; |
| import org.eclipse.cdt.dsf.mi.service.command.output.CLIAddressableSizeInfo; |
| import org.eclipse.cdt.dsf.mi.service.command.output.CLIShowEndianInfo; |
| import org.eclipse.cdt.dsf.mi.service.command.output.MIDataEvaluateExpressionInfo; |
| import org.eclipse.cdt.dsf.mi.service.command.output.MIGDBShowLanguageInfo; |
| import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; |
| import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; |
| import org.eclipse.cdt.dsf.service.DsfSession; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.debug.core.model.MemoryByte; |
| |
| /** |
| * @since 4.2 |
| */ |
| public class GDBMemory extends MIMemory implements IGDBMemory2 { |
| |
| private IGDBControl fCommandControl; |
| |
| /** |
| * Cache of the address sizes for each memory context. |
| */ |
| private Map<IMemoryDMContext, Integer> fAddressSizes = new HashMap<>(); |
| |
| /** |
| * Cache of the addressable sizes for each memory context. |
| */ |
| private Map<IMemoryDMContext, Integer> fAddressableSizes = new HashMap<>(); |
| |
| /** |
| * Cache of the endianness for each memory context. |
| */ |
| private Map<IMemoryDMContext, Boolean> fIsBigEndian = new HashMap<>(); |
| |
| public GDBMemory(DsfSession session) { |
| super(session); |
| } |
| |
| @Override |
| public void initialize(final RequestMonitor requestMonitor) { |
| super.initialize(new ImmediateRequestMonitor(requestMonitor) { |
| @Override |
| protected void handleSuccess() { |
| doInitialize(requestMonitor); |
| } |
| }); |
| } |
| |
| private void doInitialize(final RequestMonitor requestMonitor) { |
| fCommandControl = getServicesTracker().getService(IGDBControl.class); |
| getSession().addServiceEventListener(this, null); |
| register(new String[] { IMemory.class.getName(), MIMemory.class.getName(), IGDBMemory.class.getName(), |
| IGDBMemory2.class.getName(), GDBMemory.class.getName(), }, new Hashtable<String, String>()); |
| requestMonitor.done(); |
| } |
| |
| @Override |
| public void shutdown(RequestMonitor requestMonitor) { |
| unregister(); |
| getSession().removeServiceEventListener(this); |
| fAddressableSizes.clear(); |
| fAddressSizes.clear(); |
| super.shutdown(requestMonitor); |
| } |
| |
| @Override |
| protected void readMemoryBlock(final IDMContext dmc, IAddress address, long offset, int word_size, int word_count, |
| final DataRequestMonitor<MemoryByte[]> drm) { |
| super.readMemoryBlock(dmc, address, offset, word_size, word_count, |
| new DataRequestMonitor<MemoryByte[]>(ImmediateExecutor.getInstance(), drm) { |
| @Override |
| protected void handleSuccess() { |
| IMemoryDMContext memDmc = DMContexts.getAncestorOfType(dmc, IMemoryDMContext.class); |
| if (memDmc != null) { |
| boolean bigEndian = isBigEndian(memDmc); |
| for (MemoryByte b : getData()) { |
| b.setBigEndian(bigEndian); |
| b.setEndianessKnown(true); |
| } |
| } |
| drm.setData(getData()); |
| drm.done(); |
| } |
| }); |
| } |
| |
| @Override |
| public void initializeMemoryData(final IMemoryDMContext memContext, RequestMonitor rm) { |
| |
| ImmediateExecutor.getInstance().execute(new Sequence(getExecutor(), rm) { |
| |
| private String originalLanguage = MIGDBShowLanguageInfo.AUTO; |
| private boolean abortLanguageSteps = false; |
| |
| // Need a global here as getSteps() can be called more than once. |
| private Step[] steps = null; |
| |
| private void determineSteps() { |
| ArrayList<Step> stepsList = new ArrayList<>(); |
| |
| if (fAddressSizes.get(memContext) == null) { |
| stepsList.add(new Step() { |
| // store original language |
| @Override |
| public void execute(final RequestMonitor requestMonitor) { |
| fCommandControl.queueCommand( |
| fCommandControl.getCommandFactory().createMIGDBShowLanguage(memContext), |
| new ImmediateDataRequestMonitor<MIGDBShowLanguageInfo>(requestMonitor) { |
| @Override |
| protected void handleCompleted() { |
| if (isSuccess()) { |
| originalLanguage = getData().getLanguage(); |
| } else { |
| abortLanguageSteps = true; |
| } |
| requestMonitor.done(); |
| } |
| }); |
| } |
| }); |
| stepsList.add(new Step() { |
| // switch to c language |
| @Override |
| public void execute(final RequestMonitor requestMonitor) { |
| if (abortLanguageSteps) { |
| requestMonitor.done(); |
| return; |
| } |
| |
| fCommandControl.queueCommand( |
| fCommandControl.getCommandFactory().createMIGDBSetLanguage(memContext, |
| MIGDBShowLanguageInfo.C), |
| new ImmediateDataRequestMonitor<MIInfo>(requestMonitor) { |
| @Override |
| protected void handleCompleted() { |
| if (!isSuccess()) { |
| abortLanguageSteps = true; |
| } |
| // Accept failure |
| requestMonitor.done(); |
| } |
| }); |
| } |
| }); |
| |
| stepsList.add(new Step() { |
| // Run this step even if the language commands where aborted, but accept failures. |
| // Resolve Addressable and Address size |
| @Override |
| public void execute(final RequestMonitor requestMonitor) { |
| //Read Minimum addressable memory size and actual address size |
| readAddressableSize(memContext, new ImmediateDataRequestMonitor<Integer>(requestMonitor) { |
| @Override |
| protected void handleCompleted() { |
| if (isSuccess()) { |
| final Integer minAddressableInOctets = getData(); |
| //Preserve the addressable size per context |
| fAddressableSizes.put(memContext, minAddressableInOctets); |
| } |
| |
| readAddressSize(memContext, |
| new ImmediateDataRequestMonitor<Integer>(requestMonitor) { |
| @Override |
| protected void handleCompleted() { |
| if (isSuccess()) { |
| //Preserve the address size per context |
| fAddressSizes.put(memContext, getData()); |
| } |
| |
| // Accept failures |
| requestMonitor.done(); |
| } |
| }); |
| } |
| }); |
| } |
| }); |
| |
| stepsList.add(new Step() { |
| // restore original language |
| @Override |
| public void execute(final RequestMonitor requestMonitor) { |
| if (abortLanguageSteps) { |
| requestMonitor.done(); |
| return; |
| } |
| |
| fCommandControl |
| .queueCommand( |
| fCommandControl.getCommandFactory().createMIGDBSetLanguage(memContext, |
| originalLanguage), |
| new ImmediateDataRequestMonitor<MIInfo>(requestMonitor) { |
| @Override |
| protected void handleCompleted() { |
| if (!isSuccess()) { |
| // If we are unable to set the original language back things could be bad. |
| // Let's try setting it to "auto" as a fall back. Log the situation as info. |
| GdbPlugin.log(getStatus()); |
| |
| fCommandControl.queueCommand( |
| fCommandControl.getCommandFactory() |
| .createMIGDBSetLanguage(memContext, |
| MIGDBShowLanguageInfo.AUTO), |
| new ImmediateDataRequestMonitor<MIInfo>( |
| requestMonitor) { |
| @Override |
| protected void handleCompleted() { |
| if (!isSuccess()) { |
| // This error could be bad because we've changed the language to C |
| // but are unable to switch it back. Log the error. |
| // If the language happens to be C anyway, everything will |
| // continue to work, which is why we don't abort the sequence |
| // (which would cause the entire session to fail). |
| GdbPlugin.log(getStatus()); |
| } |
| // Accept failure |
| requestMonitor.done(); |
| } |
| }); |
| } else { |
| requestMonitor.done(); |
| } |
| } |
| }); |
| } |
| }); |
| |
| } |
| |
| if (fIsBigEndian.get(memContext) == null) { |
| stepsList.add(new Step() { |
| // read endianness |
| @Override |
| public void execute(final RequestMonitor requestMonitor) { |
| readEndianness(memContext, new ImmediateDataRequestMonitor<Boolean>(requestMonitor) { |
| @Override |
| protected void handleCompleted() { |
| if (isSuccess()) { |
| fIsBigEndian.put(memContext, getData()); |
| } |
| // Accept failure |
| requestMonitor.done(); |
| } |
| }); |
| } |
| }); |
| } |
| |
| steps = stepsList.toArray(new Step[stepsList.size()]); |
| } |
| |
| @Override |
| public Step[] getSteps() { |
| if (steps == null) { |
| determineSteps(); |
| } |
| |
| return steps; |
| } |
| }); |
| } |
| |
| @DsfServiceEventHandler |
| public void eventDispatched(IExitedDMEvent event) { |
| if (event.getDMContext() instanceof IContainerDMContext) { |
| IMemoryDMContext context = DMContexts.getAncestorOfType(event.getDMContext(), IMemoryDMContext.class); |
| if (context != null) { |
| fAddressSizes.remove(context); |
| fAddressableSizes.remove(context); |
| } |
| } |
| } |
| |
| @Override |
| public int getAddressSize(IMemoryDMContext context) { |
| Integer addressSize = fAddressSizes.get(context); |
| return (addressSize != null) ? addressSize.intValue() : 8; |
| } |
| |
| /** |
| * @since 4.4 |
| */ |
| @Override |
| public int getAddressableSize(IMemoryDMContext context) { |
| Integer addressableSize = fAddressableSizes.get(context); |
| return (addressableSize != null) ? addressableSize.intValue() : 1; |
| } |
| |
| @Override |
| public boolean isBigEndian(IMemoryDMContext context) { |
| Boolean isBigEndian = fIsBigEndian.get(context); |
| assert isBigEndian != null; |
| if (isBigEndian == null) { |
| GdbPlugin.log( |
| new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, "Endianness was never initialized for " + context)); //$NON-NLS-1$ |
| return false; |
| } |
| return isBigEndian.booleanValue(); |
| } |
| |
| /** |
| * Address size is determined by space, in octets, used to store an address value (e.g. a pointer) on a target system. |
| * |
| * <p>NOTE: Implementation requires addressable memory size to be known</p> |
| * @param memContext |
| * @param drm |
| * |
| * @see IGDBMemory#getAddressSize(org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext) |
| * @see GDBMemory#readAddressableSize(org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext, DataRequestMonitor) |
| */ |
| protected void readAddressSize(final IMemoryDMContext memContext, final DataRequestMonitor<Integer> drm) { |
| IExpressions exprService = getServicesTracker().getService(IExpressions.class); |
| IExpressionDMContext exprContext = exprService.createExpression(memContext, "sizeof (void*)"); //$NON-NLS-1$ |
| CommandFactory commandFactory = fCommandControl.getCommandFactory(); |
| fCommandControl.queueCommand(commandFactory.createMIDataEvaluateExpression(exprContext), |
| new DataRequestMonitor<MIDataEvaluateExpressionInfo>(ImmediateExecutor.getInstance(), drm) { |
| @Override |
| protected void handleSuccess() { |
| try { |
| // 'sizeof' returns number of bytes (aka 'chars'). |
| // Multiply with byte size in octets to get storage required to hold a pointer. |
| Integer ptrBytes = Integer.decode(getData().getValue()); |
| drm.setData(ptrBytes * getAddressableSize(memContext)); |
| } catch (NumberFormatException e) { |
| drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, |
| String.format("Invalid address size: %s", getData().getValue()))); //$NON-NLS-1$ |
| } |
| drm.done(); |
| } |
| }); |
| } |
| |
| /** |
| * The minimum addressable size is determined by the space used to store a "char" on a target system |
| * This is then resolved by retrieving a hex representation of -1 casted to the size of a "char" |
| * e.g. from GDB command line |
| * > p/x (char)-1 |
| * > $7 = 0xffff |
| * |
| * Since two hex characters are representing one octet, for the above example this method should return 2 |
| * @since 4.4 |
| * |
| */ |
| protected void readAddressableSize(IMemoryDMContext memContext, final DataRequestMonitor<Integer> drm) { |
| //We use a CLI command here instead of the expression services, since the target may not be available |
| //e.g. when using a remote launch. |
| // Using MI directly is a possibility although there is no way to specify the required output format to hex. |
| CommandFactory commandFactory = fCommandControl.getCommandFactory(); |
| fCommandControl.queueCommand(commandFactory.createCLIAddressableSize(memContext), |
| new DataRequestMonitor<CLIAddressableSizeInfo>(ImmediateExecutor.getInstance(), drm) { |
| @Override |
| protected void handleSuccess() { |
| drm.setData(Integer.valueOf(getData().getAddressableSize())); |
| drm.done(); |
| } |
| }); |
| } |
| |
| protected void readEndianness(IMemoryDMContext memContext, final DataRequestMonitor<Boolean> drm) { |
| CommandFactory commandFactory = fCommandControl.getCommandFactory(); |
| fCommandControl.queueCommand(commandFactory.createCLIShowEndian(memContext), |
| new DataRequestMonitor<CLIShowEndianInfo>(ImmediateExecutor.getInstance(), drm) { |
| @Override |
| protected void handleSuccess() { |
| drm.setData(Boolean.valueOf(getData().isBigEndian())); |
| drm.done(); |
| } |
| }); |
| } |
| } |