diff options
author | Alvaro Sanchez-Leon | 2014-10-24 15:42:04 +0000 |
---|---|---|
committer | Alvaro Sanchez-Leon | 2014-12-09 15:37:26 +0000 |
commit | 3ab1678bc370c56cb41f6830a872e806555ef61a (patch) | |
tree | 18157c35fbde11d23a71f0947d8dd8ced5337caf /dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse | |
parent | a830f285fe92a58fb9689020c3ddc605b0d36867 (diff) | |
download | org.eclipse.cdt-3ab1678bc370c56cb41f6830a872e806555ef61a.tar.gz org.eclipse.cdt-3ab1678bc370c56cb41f6830a872e806555ef61a.tar.xz org.eclipse.cdt-3ab1678bc370c56cb41f6830a872e806555ef61a.zip |
Bug 235747: [registers] Allow user to edit the register groups.
Bug 235747: Move register group actions to the command framework.
Change-Id: Ife5aefc1a1609309724db01d92a35750e25def24
Signed-off-by: Alvaro Sanchez-Leon <alvsan09@gmail.com>
Signed-off-by: Marc Khouzam <marc.khouzam@ericsson.com>
Reviewed-on: https://git.eclipse.org/r/13980
Tested-by: Hudson CI
Diffstat (limited to 'dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse')
8 files changed, 1294 insertions, 109 deletions
diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBPatternMatchingExpressions.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBPatternMatchingExpressions.java index e83df89332d..74fe922ae22 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBPatternMatchingExpressions.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBPatternMatchingExpressions.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Ericsson and others. + * Copyright (c) 2012, 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 @@ -9,6 +9,7 @@ * Marc Khouzam (Ericsson) - initial API and implementation * Grzegorz Kuligowski - Cannot cast to type that contain commas (bug 393474) * Marc Khouzam (Ericsson) - Support for glob-expressions for local variables (bug 394408) + * Alvaro Sanchez-Leon (Ericsson AB) - Allow user to edit register groups (Bug 235747) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; @@ -37,9 +38,9 @@ import org.eclipse.cdt.dsf.debug.service.IExpressions; import org.eclipse.cdt.dsf.debug.service.IExpressions2; import org.eclipse.cdt.dsf.debug.service.IExpressions3; import org.eclipse.cdt.dsf.debug.service.IFormattedValues; -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.IRegisterGroupDMContext; +import org.eclipse.cdt.dsf.debug.service.IRegisters2; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; import org.eclipse.cdt.dsf.debug.service.IStack; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.debug.service.IStack.IVariableDMContext; @@ -781,41 +782,38 @@ public class GDBPatternMatchingExpressions extends AbstractDsfService implements * @param rm RequestMonitor that will contain the unsorted matches. */ protected void matchRegisters(final IExpressionGroupDMContext globDmc, final DataRequestMonitor<List<IExpressionDMContext>> rm) { - final IRegisters registerService = getServicesTracker().getService(IRegisters.class); + final IRegisters2 registerService = getServicesTracker().getService(IRegisters2.class); if (registerService == null) { rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Register service unavailable", null)); //$NON-NLS-1$ return; } - registerService.getRegisterGroups(globDmc, new ImmediateDataRequestMonitor<IRegisterGroupDMContext[]>(rm) { - @Override - protected void handleSuccess() { - registerService.getRegisters( - new CompositeDMContext(new IDMContext[] { getData()[0], globDmc } ), - new ImmediateDataRequestMonitor<IRegisterDMContext[]>(rm) { - @Override - protected void handleSuccess() { - assert getData() instanceof MIRegisterDMC[]; - ArrayList<IExpressionDMContext> matches = new ArrayList<IExpressionDMContext>(); - - String fullExpr = globDmc.getExpression().trim(); - if (fullExpr.startsWith(GLOB_EXPRESSION_PREFIX)) { - // Strip the leading '=' and any extra spaces - fullExpr = fullExpr.substring(1).trim(); - } - - for (MIRegisterDMC register : (MIRegisterDMC[])getData()) { - String potentialMatch = REGISTER_PREFIX + register.getName(); - if (globMatches(fullExpr, potentialMatch)) { - matches.add(createExpression(globDmc, potentialMatch)); - } - } - - rm.done(matches); + final IContainerDMContext contDmc = DMContexts.getAncestorOfType(globDmc, IContainerDMContext.class); + + registerService.getRegisters( + new CompositeDMContext(new IDMContext[] { contDmc, globDmc } ), + new ImmediateDataRequestMonitor<IRegisterDMContext[]>(rm) { + @Override + protected void handleSuccess() { + assert getData() instanceof MIRegisterDMC[]; + ArrayList<IExpressionDMContext> matches = new ArrayList<IExpressionDMContext>(); + + String fullExpr = globDmc.getExpression().trim(); + if (fullExpr.startsWith(GLOB_EXPRESSION_PREFIX)) { + // Strip the leading '=' and any extra spaces + fullExpr = fullExpr.substring(1).trim(); + } + + for (MIRegisterDMC register : (MIRegisterDMC[])getData()) { + String potentialMatch = REGISTER_PREFIX + register.getName(); + if (globMatches(fullExpr, potentialMatch)) { + matches.add(createExpression(globDmc, potentialMatch)); } - }); - } - }); + } + + rm.done(matches); + } + }); } /** diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRegisters.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRegisters.java new file mode 100644 index 00000000000..d9bda88cb36 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRegisters.java @@ -0,0 +1,1137 @@ +/******************************************************************************* + * Copyright (c) 2014 Ericsson AB 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: + * Alvaro Sanchez-Leon (Ericsson) - First Implementation and API (Bug 235747) + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; + +import org.eclipse.cdt.debug.core.model.IRegisterDescriptor; +import org.eclipse.cdt.debug.internal.core.RegisterGroupsPersistance; +import org.eclipse.cdt.debug.internal.core.model.IRegisterGroupDescriptor; +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor; +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.IRegisters; +import org.eclipse.cdt.dsf.debug.service.IRegisters2; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.MIRegisters; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.osgi.util.NLS; + +/** + * <p>An extension of MIRegisters to support management of Register Groups as per the IRegisters2 interface.</p> + * <p>The managed registered groups are user-defined subsets of the complete list of Registers reported by GDB for a specific Target</p> + * <p>This class also triggers the read/write (persistence) of the user-defined Register Groups during the start/shutdown process of a session respectively</p> + * @since 4.6 + */ +public class GDBRegisters extends MIRegisters implements IRegisters2 { + + /** + * Unique temporary id for a group. 0 is reserved for the root group + */ + private static int fGroupBookingCount = 1; + + /** + * References to all groups related to a given context. Different programs may use different sets of registers e.g. + * 32/64 bits + */ + private final ContextToGroupsMap<IContainerDMContext, MIRegisterGroupDMC[]> fContextToGroupsMap = new ContextToGroupsMap<IContainerDMContext, MIRegisterGroupDMC[]>(); + + /** + * Used to save base list of Registers associated to a group, these registers can not be used as is for + * "getRegisters" since the execution context may change e.g. The current selection points to a process or a running + * thread or a different frame, all information besides the execution context is valid. + */ + private final GroupRegistersMap<MIRegisterGroupDMC, MIRegisterDMC[]> fGroupToRegistersMap = new GroupRegistersMap<MIRegisterGroupDMC, MIRegisterDMC[]>(); + + /** + * Saves the Group number to RegisterGroupDescriptor created from the serialized memento, The group number is used across contexts as the + * key:Integer uses a booking number incremented across container contexts + */ + private final Map<Integer, IRegisterGroupDescriptor> fGroupMementoDescriptorIndex = new HashMap<Integer, IRegisterGroupDescriptor>(); + + public GDBRegisters(DsfSession session) { + super(session); + } + + private class ContextToGroupsMap<K, V> extends HashMap<IContainerDMContext, MIRegisterGroupDMC[]> { + private static final long serialVersionUID = 1L; + private final Map<IContainerDMContext, Map<String, MIRegisterGroupDMC>> fNameToGroupMap = new HashMap<IContainerDMContext, Map<String, MIRegisterGroupDMC>>(); + + @Override + public MIRegisterGroupDMC[] put(IContainerDMContext key, MIRegisterGroupDMC[] value) { + if (key == null || value == null) { + return null; + } + + // Contents are updated for the given context, reset this context + // cache + // So it can be rebuilt on the next get + fNameToGroupMap.remove(key); + return super.put(key, value); + } + + @Override + public void clear() { + fNameToGroupMap.clear(); + fGroupMementoDescriptorIndex.clear(); + fGroupToRegistersMap.clear(); + super.clear(); + } + + @Override + public MIRegisterGroupDMC[] remove(Object key) { + fNameToGroupMap.remove(key); + return super.remove(key); + } + + public Map<String, MIRegisterGroupDMC> getGroupNameMap(IContainerDMContext key) { + // validate input + if (key == null) { + return null; + } + + Map<String, MIRegisterGroupDMC> nameMap = fNameToGroupMap.get(key); + if (nameMap == null) { + // cache not resolved, rebuild + nameMap = new HashMap<String, MIRegisterGroupDMC>(); + MIRegisterGroupDMC[] groupsArr = super.get(key); + // If the container context exist, build the name map + if (groupsArr != null) { + for (MIRegisterGroupDMC group : groupsArr) { + nameMap.put(group.getName(), group); + } + + // cache it ! + fNameToGroupMap.put(key, nameMap); + } + } + + return nameMap; + } + + /** + * Needed when group name(s) change but the associated group objects remain the same + */ + public void resetGroupNameMap(IContainerDMContext key) { + fNameToGroupMap.remove(key); + } + + /** + * The result will reflect the reverse order of creation, i.e. last created first + */ + public MIRegisterGroupDMC[] getReversed(IDMContext key) { + MIRegisterGroupDMC[] groups = get(key); + MIRegisterGroupDMC[] reversedGroups = new MIRegisterGroupDMC[groups.length]; + int size = groups.length; + for (int i = 0; i < size; i++) { + reversedGroups[size - 1 - i] = groups[i]; + } + + return reversedGroups; + } + } + + /** + * Used to associate two dependent maps, Group to ordered Register[] and Group to indexed registers (Map<String, + * Register>) + */ + private class GroupRegistersMap<K, V> extends HashMap<MIRegisterGroupDMC, MIRegisterDMC[]> { + private static final long serialVersionUID = 1L; + private final Map<MIRegisterGroupDMC, Map<String, MIRegisterDMC>> fNameToRegisterMap = new HashMap<MIRegisterGroupDMC, Map<String, MIRegisterDMC>>(); + + @Override + public MIRegisterDMC[] put(MIRegisterGroupDMC key, MIRegisterDMC[] value) { + // Make sure a previous entry of the key does not keep an out of + // date cache + fNameToRegisterMap.remove(key); + return super.put(key, value); + } + + public Map<String, MIRegisterDMC> getIndexedRegisters(MIRegisterGroupDMC key) { + Map<String, MIRegisterDMC> nameToRegisterMap = fNameToRegisterMap.get(key); + if (nameToRegisterMap == null && get(key) != null) { + // Needs indexing + nameToRegisterMap = indexRegisters(key); + if (nameToRegisterMap != null) { + fNameToRegisterMap.put(key, nameToRegisterMap); + } + } + + return nameToRegisterMap; + } + + @Override + public void clear() { + fNameToRegisterMap.clear(); + super.clear(); + } + + @Override + public MIRegisterDMC[] remove(Object key) { + fNameToRegisterMap.remove(key); + return super.remove(key); + } + + private Map<String, MIRegisterDMC> indexRegisters(MIRegisterGroupDMC registerGroup) { + MIRegisterDMC[] registers = super.get(registerGroup); + if (registers == null || registers.length < 1) { + return null; + } + + Map<String, MIRegisterDMC> registerNameMap = new HashMap<String, MIRegisterDMC>(); + for (IRegisterDMContext register : registers) { + assert(register instanceof MIRegisterDMC); + MIRegisterDMC registerDmc = (MIRegisterDMC) register; + registerNameMap.put(registerDmc.getName(), registerDmc); + } + + return registerNameMap; + } + } + + private class RegisterGroupDescriptor implements IRegisterGroupDescriptor { + private final boolean fEnabled; + private final MIRegisterGroupDMC fgroup; + + public RegisterGroupDescriptor(MIRegisterGroupDMC group, boolean enabled) { + fgroup = group; + fEnabled = enabled; + } + + @Override + public String getName() { + return fgroup.getName(); + } + + @Override + public boolean isEnabled() { + return fEnabled; + } + + @Override + public IRegisterDescriptor[] getChildren() throws CoreException { + IRegisterDescriptor[] regDescriptors = null; + // Get a snap shot of the current registers + MIRegisterDMC[] registers = fGroupToRegistersMap.get(fgroup); + if (registers != null && registers.length > 0) { + regDescriptors = new IRegisterDescriptor[registers.length]; + for (int i = 0; i < registers.length; i++) { + regDescriptors[i] = new RegisterDescriptor(registers[i]); + } + } else { + // The registers were probably never fetched, obtain the + // original definitions from deserialized groups + IRegisterGroupDescriptor groupMementoDescriptor = fGroupMementoDescriptorIndex.get(fgroup.getGroupNo()); + if (groupMementoDescriptor != null) { + regDescriptors = groupMementoDescriptor.getChildren(); + } + } + + return regDescriptors; + } + + } + + private class RegisterDescriptor implements IRegisterDescriptor { + private final MIRegisterDMC fRegister; + private final static String ORIGINAL_GROUP_NAME = "Main"; //$NON-NLS-1$ + + public RegisterDescriptor(MIRegisterDMC register) { + fRegister = register; + } + + @Override + public String getName() { + return fRegister.getName(); + } + + @Override + public String getGroupName() { + // Hard coded to keep compatibility with CDI's format + return ORIGINAL_GROUP_NAME; + } + } + + @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[]{IRegisters.class.getName(), + IRegisters2.class.getName(), + MIRegisters.class.getName(), + GDBRegisters.class.getName()}, new Hashtable<String,String>()); + requestMonitor.done(); + } + + @Override + public void getRegisterGroups(final IDMContext ctx, final DataRequestMonitor<IRegisterGroupDMContext[]> rm) { + final IContainerDMContext contDmc = DMContexts.getAncestorOfType(ctx, IContainerDMContext.class); + if (contDmc == null) { + IStatus status = new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, + "Container context not provided, unable to get Register Groups", null); //$NON-NLS-1$ + rm.setStatus(status); + rm.done(); + return; + } + + if (fContextToGroupsMap.containsKey(contDmc)) { + // The groups information is already available and can be returned + rm.setData(fContextToGroupsMap.getReversed(contDmc)); + rm.done(); + return; + } + + // The register groups information needs to be built from GDB and user-defined groups i.e. de-serialized + // from the launch configuration. + super.getRegisterGroups(ctx, new ImmediateDataRequestMonitor<IRegisterGroupDMContext[]>(rm) { + @Override + @ConfinedToDsfExecutor("fExecutor") + protected void handleSuccess() { + final IRegisterGroupDMContext[] regGroups = getData(); + // only one group from MI is expected at the moment + assert (regGroups.length == 1); + assert (regGroups[0] instanceof MIRegisterGroupDMC); + + final MIRegisterGroupDMC miGroup = (MIRegisterGroupDMC) regGroups[0]; + + // read serialized groups + MIRegisterGroupDMC[] mementoGroups = readGroupsFromMemento(contDmc); + + // Track the groups associated to this context + // The root group (mi) is placed and expected at index 0 followed + // by the user groups read from the memento + MIRegisterGroupDMC[] regGroupsCtx = concatenateArr(new MIRegisterGroupDMC[] { miGroup }, mementoGroups); + + // Have the information ready for subsequent request or group operations. + fContextToGroupsMap.put(contDmc, regGroupsCtx); + + // Reverse the order i.e. latest on top and get back to parent monitor + rm.setData(fContextToGroupsMap.getReversed(contDmc)); + rm.done(); + } + }); + } + + @Override + public void getRegisterGroupData(final IRegisterGroupDMContext regGroupDmc, + final DataRequestMonitor<IRegisterGroupDMData> rm) { + + assert (regGroupDmc instanceof MIRegisterGroupDMC); + + if (regGroupDmc instanceof MIRegisterGroupDMC) { + MIRegisterGroupDMC groupDmc = (MIRegisterGroupDMC) regGroupDmc; + rm.setData(createRegisterGroupData(groupDmc)); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, + "Unable to resolve Group Data, Invalid Register Group provided", null)); //$NON-NLS-1$ + } + + rm.done(); + } + + private IRegisterGroupDMData createRegisterGroupData(final MIRegisterGroupDMC groupDmc) { + + IRegisterGroupDMData groupData = new IRegisterGroupDMData() { + @Override + public String getName() { + return groupDmc.getName(); + } + + @Override + public String getDescription() { + if (groupDmc.getName().equals(ROOT_GROUP_NAME)) { + return ROOT_GROUP_DESCRIPTION; + } + + return BLANK_STRING; + } + + }; + + // Make sure this group is available in the groups to registers map, + // as this map provides the input to save /serialize the groups + // The associated registers will be resolved upon request. + if (fGroupToRegistersMap.get(groupDmc) == null) { + fGroupToRegistersMap.put(groupDmc, new MIRegisterDMC[0]); + } + + return groupData; + } + + @Override + public void getRegisters(final IDMContext aCtx, final DataRequestMonitor<IRegisterDMContext[]> rm) { + findRegisterGroup(aCtx, ROOT_GROUP_NAME, new ImmediateDataRequestMonitor<IRegisterGroupDMContext>() { + @Override + protected void handleSuccess() { + //Get the root group, needed as a possible default group and to resolve target registers + IRegisterGroupDMContext rootGroup = getData(); + assert (rootGroup instanceof MIRegisterGroupDMC); + final MIRegisterGroupDMC rootGroupContext = (MIRegisterGroupDMC) rootGroup; + + //if the received context does not contain a register group i.e.is null, the default group to resolve registers is the root group + MIRegisterGroupDMC tGroupDmc = DMContexts.getAncestorOfType(aCtx, MIRegisterGroupDMC.class); + + IDMContext tCtx = aCtx; + if (tGroupDmc == null) { + tGroupDmc = rootGroupContext; + //We need a register group as part of the context to resolve registers + tCtx = new CompositeDMContext(new IDMContext[] {aCtx, tGroupDmc}); + } + + final IDMContext ctx = tCtx; + + final MIRegisterGroupDMC groupDmc = tGroupDmc; + // check if base registers have been loaded already + MIRegisterDMC[] baseRegisters = fGroupToRegistersMap.get(groupDmc); + if (baseRegisters != null && baseRegisters.length > 0) { + // use baseRegisters to build registers associated to the given context + buildGroupRegisters(ctx, baseRegisters, rm); + return; + } + + IContainerDMContext rootGroupContainer = DMContexts.getAncestorOfType(rootGroupContext, + IContainerDMContext.class); + MIRegisterDMC[] registerBase = fGroupToRegistersMap.get(rootGroupContainer); + if (registerBase == null || registerBase.length < 1) { + // Prepare to fetch the register information from GDB (root group) + // Include the frame/execution context whenever available + IDMContext miExecDmc = DMContexts.getAncestorOfType(ctx, IFrameDMContext.class); + if (miExecDmc == null) { + miExecDmc = DMContexts.getAncestorOfType(ctx, IMIExecutionDMContext.class); + } + + // if Execution context is not available return shallow registers i.e. no execution context + final CompositeDMContext compCtx; + if (miExecDmc != null) { + compCtx = new CompositeDMContext(new IDMContext[] { rootGroupContext, miExecDmc }); + } else { + compCtx = new CompositeDMContext(new IDMContext[] { rootGroupContext }); + } + + // Fetch the register base from GDB + GDBRegisters.super.getRegisters(compCtx, new DataRequestMonitor<IRegisterDMContext[]>(getExecutor(), rm) { + @Override + @ConfinedToDsfExecutor("fExecutor") + protected void handleSuccess() { + IRegisterDMContext[] iregisters = getData(); + MIRegisterDMC[] registers = Arrays.copyOf(iregisters, iregisters.length, MIRegisterDMC[].class); + + // associate group to bare registers i.e. not associated to a specific execution context + fGroupToRegistersMap.put(rootGroupContext, toBareRegisters(registers)); + if (groupDmc.getName().equals(ROOT_GROUP_NAME)) { + buildGroupRegisters(ctx, registers, rm); + return; + } + + // Now proceed to resolve the requested user group registers + getUserGroupRegisters(ctx, rm); + } + }); + } else { + if (groupDmc.getName().equals(ROOT_GROUP_NAME)) { + buildGroupRegisters(ctx, registerBase, rm); + } else { + // resolve user group registers + getUserGroupRegisters(ctx, rm); + } + } + + } + }); + } + + @Override + public void canAddRegisterGroup(IDMContext selectionContext, DataRequestMonitor<Boolean> rm) { + // Not relevant checks at this point + rm.setData(true); + rm.done(); + } + + @Override + public void addRegisterGroup(final IDMContext containerContext, final String groupName, final IRegisterDMContext[] registers, RequestMonitor rm) { + if (registers == null || registers.length < 1) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, + Messages.RegisterGroup_invalid_number_of_registers, null)); + rm.done(); + return; + } + + if (groupName.trim().toLowerCase().equals(ROOT_GROUP_NAME.toLowerCase())) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, + NLS.bind(Messages.RegisterGroup_name_reserved, ROOT_GROUP_NAME), null)); + rm.done(); + return; + } + + if (!(registers[0] instanceof MIRegisterDMC)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, + "Unexpected IRegisterDMContext input instance type", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + IContainerDMContext contDmc = DMContexts.getAncestorOfType(registers[0], IContainerDMContext.class); + if (contDmc == null) { + contDmc = DMContexts.getAncestorOfType(containerContext, IContainerDMContext.class); + if (contDmc == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, + "Unable to add Register group, Invalid Container", null)); //$NON-NLS-1$ + rm.done(); + return; + } + } + + // must be a child of an existing container, at least the root group must be present + assert (fContextToGroupsMap.containsKey(contDmc)); + + // Make sure the name is not currently in use + if (fContextToGroupsMap.getGroupNameMap(contDmc).get(groupName) != null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, NLS.bind( + Messages.RegisterGroup_name_used, groupName), null)); + rm.done(); + return; + } + + //create the new group + MIRegisterGroupDMC group = new MIRegisterGroupDMC(this, contDmc, fGroupBookingCount, groupName); + fGroupBookingCount++; + + // Update the context to groups map including the new group + fContextToGroupsMap.put(contDmc, + concatenateArr(fContextToGroupsMap.get(contDmc), new MIRegisterGroupDMC[] { group })); + + //type adjustment + MIRegisterDMC[] miRegisters = Arrays.copyOf(registers, registers.length, MIRegisterDMC[].class); + + // associate group to bare registers i.e. not associated to a specific execution context + MIRegisterDMC[] bareRegisters = toBareRegisters(miRegisters); + fGroupToRegistersMap.put(group, bareRegisters); + + // Create event notification, to trigger the UI refresh + getSession().dispatchEvent(new GroupsChangedDMEvent(contDmc), null); + rm.done(); + } + + @Override + public void canEditRegisterGroup(IRegisterGroupDMContext group, DataRequestMonitor<Boolean> rm) { + rm.setData(canEditRegisterGroup(group)); + rm.done(); + } + + @Override + public void editRegisterGroup(IRegisterGroupDMContext group, String newGroupName, IRegisterDMContext[] iRegisters, + RequestMonitor rm) { + + if (iRegisters != null && iRegisters.length == 0) { + rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, + Messages.RegisterGroup_invalid_number_of_registers, null)); + return; + } + + if (!(group instanceof MIRegisterGroupDMC)) { + rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Unknown DMC type", null)); //$NON-NLS-1$ + return; + } + + IContainerDMContext contDmc = DMContexts.getAncestorOfType(group, IContainerDMContext.class); + if (contDmc == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, + "Unable to edit Register group, Invalid Container", null)); //$NON-NLS-1$ + rm.done(); + } + + MIRegisterGroupDMC miGroup = ((MIRegisterGroupDMC) group); + + if (!canEditRegisterGroup(group)) { + // Should not happen as canEdit is expected to be called before edit + rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Cannot currently edit register groups", null)); //$NON-NLS-1$ + return; + } + + if (newGroupName != null && !newGroupName.isEmpty()) { + // Make sure the new group name is not the reserved root group name + if (newGroupName.trim().toLowerCase().equals(ROOT_GROUP_NAME.toLowerCase())) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, + NLS.bind(Messages.RegisterGroup_name_reserved, ROOT_GROUP_NAME), null)); + rm.done(); + return; + } + + // Make sure the name is not currently in use + if (!miGroup.getName().equals(newGroupName)) { + // we are updating the name, lets make sure this new name is not in use + if (fContextToGroupsMap.getGroupNameMap(contDmc).get(newGroupName) != null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, + NLS.bind(Messages.RegisterGroup_name_used, newGroupName), null)); + rm.done(); + return; + } + } + + miGroup.setName(newGroupName); + + //make sure we update the group name cache + fContextToGroupsMap.resetGroupNameMap(contDmc); + + generateRegisterGroupChangedEvent(miGroup); + } else { + // Request to keep name the same + } + + if (iRegisters != null) { + assert (iRegisters.length > 0); + + // transform to MIRegistersDMC[] + MIRegisterDMC[] registers = arrangeRegisters(iRegisters); + + // preserve registers in a general format not associated to a specific frame + registers = toBareRegisters(registers); + fGroupToRegistersMap.put(miGroup, registers); + // Notify of Registers changed + generateRegistersChangedEvent(miGroup); + } else { + // Request to keep register list the same + } + + rm.done(); + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.cdt.dsf.debug.service.IRegisters2#removeRegisterGroups(org.eclipse.cdt.dsf.debug.service.IRegisters + * .IRegisterGroupDMContext[], org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + @Override + public void removeRegisterGroups(IRegisterGroupDMContext[] groups, RequestMonitor rm) { + removeRegisterGroups(groups, false, rm); + } + + @Override + public void restoreDefaultGroups(final IDMContext selectionContext, final RequestMonitor rm) { + for (IDMContext context : fContextToGroupsMap.keySet()) { + removeRegisterGroups(context); + } + + // clean the serialized registers group information + save(); + + // Clear all global references to the contexts and groups + fContextToGroupsMap.clear(); + rm.done(); + } + + /** + * Reset this class i.e. does not impact saved groups within launch configuration + * + * @param rm + */ + public void reset(final RequestMonitor rm) { + for (IDMContext context : fContextToGroupsMap.keySet()) { + removeRegisterGroups(context); + } + + // Clear all global references to the contexts and groups + fContextToGroupsMap.clear(); + rm.done(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.cdt.dsf.mi.service.MIRegisters#findRegisterGroup(org.eclipse.cdt.dsf.datamodel.IDMContext, + * java.lang.String, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + @Override + public void findRegisterGroup(final IDMContext ctx, final String name, final DataRequestMonitor<IRegisterGroupDMContext> rm) { + final IContainerDMContext contDmc = DMContexts.getAncestorOfType(ctx, IContainerDMContext.class); + if (contDmc == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, + "Container context not found", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + if (fContextToGroupsMap.get(ctx) == null) { + // Need to build the list of register groups including the one from target + getRegisterGroups(contDmc, new DataRequestMonitor<IRegisterGroupDMContext[]>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Using the list of groups indirectly to find the one with the given name from it + findRegisterGroup(contDmc, name, rm); + } + }); + } else { + // The context to groups map has been initialized and can be used + findRegisterGroup(contDmc, name, rm); + } + } + + /** + * Call it only after getRegisterGroups has been called at least once, so the context to groups map is not empty + */ + private void findRegisterGroup(IContainerDMContext contDmc, String name, DataRequestMonitor<IRegisterGroupDMContext> rm) { + Map<String, MIRegisterGroupDMC> nameToGroup = fContextToGroupsMap.getGroupNameMap(contDmc); + if (nameToGroup != null) { + rm.setData(nameToGroup.get(name)); + } else { + rm.setData(null); + } + + rm.done(); + } + + @Override + public void shutdown(RequestMonitor rm) { + save(); + super.shutdown(rm); + } + + public void save() { + IRegisterGroupDescriptor[] groups = buildDescriptors(); + ILaunchConfiguration launchConfig = getLaunchConfig(); + if (launchConfig != null) { + RegisterGroupsPersistance serializer = new RegisterGroupsPersistance(launchConfig); + try { + serializer.saveGroups(groups); + } catch (CoreException e1) { + e1.printStackTrace(); + } + } + } + + /** + * Cast to MI and sort them ascending order by register index + */ + private MIRegisterDMC[] arrangeRegisters(IRegisterDMContext[] iRegisters) { + TreeMap<Integer, MIRegisterDMC> sortedRegisters = new TreeMap<Integer, MIRegisterDMC>(); + for (int i = 0; i < iRegisters.length; i++) { + assert(iRegisters[i] instanceof MIRegisterDMC); + MIRegisterDMC register = (MIRegisterDMC) iRegisters[i]; + sortedRegisters.put(register.getRegNo(), register); + } + + return sortedRegisters.values().toArray(new MIRegisterDMC[sortedRegisters.size()]); + } + + /** + * @param groups + * - The groups to be removed + * @param removeRoot + * - indicates if the root group needs to be removed e.g. during restore to defaults + * @param rm + */ + private void removeRegisterGroups(IRegisterGroupDMContext[] groups, boolean removeRoot, RequestMonitor rm) { + if (groups != null) { + // Save a list of updated containers to only send an update event for each of them + final Set<IContainerDMContext> updatedContainers = new HashSet<IContainerDMContext>(); + for (IRegisterGroupDMContext group : groups) { + + if (!removeRoot) { + // Prevent removal of the Root Group + if (!(group instanceof MIRegisterGroupDMC)) { + // All groups are expected to be instances of MIREgisterGroupDMC + assert (false); + continue; + } + + if (((MIRegisterGroupDMC) group).getName().equals(ROOT_GROUP_NAME)) { + // Skip removal of a root group, except when restoring to default groups + continue; + } + } + + final IContainerDMContext containerDmc = DMContexts.getAncestorOfType(group, IContainerDMContext.class); + + // All given groups are expected to be part of the same Container, however it's safer to create a new list + // per context to cover the unsual case + // This could be revisited in case there is performance concerns which does not seem an issue at this + // point. + MIRegisterGroupDMC[] groupsCtx = fContextToGroupsMap.get(containerDmc); + assert(groupsCtx != null); + + if (groupsCtx != null) { + List<MIRegisterGroupDMC> groupsList = new ArrayList<MIRegisterGroupDMC>(Arrays.asList(groupsCtx)); + + // Removing a single group + groupsList.remove(group); + + // Back to context map without the given group + fContextToGroupsMap.put(containerDmc, groupsList.toArray(new MIRegisterGroupDMC[groupsList.size()])); + // Now remove the group from the groups to registers map + if (fGroupToRegistersMap.remove(group) != null) { + updatedContainers.add(containerDmc); + } + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, + "Unable to remove Register group, Invalid Container", null)); //$NON-NLS-1$ + rm.done(); + return; + } + } + + // Sending only one update per container + for (IContainerDMContext container : updatedContainers) { + getSession().dispatchEvent(new GroupsChangedDMEvent(container), null); + } + } + + rm.done(); + } + + private void removeRegisterGroups(IDMContext containerDmc) { + MIRegisterGroupDMC[] groups = fContextToGroupsMap.get(containerDmc); + if (groups != null) { + removeRegisterGroups(groups, true, new RequestMonitor(getExecutor(), null) { + }); + } + } + + private boolean canEditRegisterGroup(IRegisterGroupDMContext group) { + if (group instanceof MIRegisterGroupDMC) { + MIRegisterGroupDMC miGroup = ((MIRegisterGroupDMC) group); + // Prevent changes to the root group + if (miGroup.getName().trim().toLowerCase().equals(ROOT_GROUP_NAME.toLowerCase())) { + return false; + } + + // Expected to be on the existing groups map + if (fGroupToRegistersMap.containsKey(group)) { + return true; + } + } + + return false; + } + + private ILaunchConfiguration getLaunchConfig() { + ILaunch launch = (ILaunch) getSession().getModelAdapter(ILaunch.class); + if (launch == null) { + // The launch is no longer active + return null; + } + + ILaunchConfiguration config = launch.getLaunchConfiguration(); + return config; + } + + private IRegisterGroupDescriptor[] buildDescriptors() { + // use a tree map to sort the entries by group number + TreeMap<Integer, MIRegisterGroupDMC> sortedGroups = new TreeMap<Integer, MIRegisterGroupDMC>(); + + for (MIRegisterGroupDMC group : fGroupToRegistersMap.keySet()) { + sortedGroups.put(Integer.valueOf(group.getGroupNo()), group); + } + + // Not serializing the root group which is dynamically created from GDB + sortedGroups.remove(Integer.valueOf(0)); + + Set<Entry<Integer, MIRegisterGroupDMC>> groupSet = sortedGroups.entrySet(); + IRegisterGroupDescriptor[] descriptors = new IRegisterGroupDescriptor[groupSet.size()]; + + // load group descriptors sorted in ascending order to their group + // number into the result array + int i = 0; + for (Iterator<Entry<Integer, MIRegisterGroupDMC>> iterator = groupSet.iterator(); iterator.hasNext();) { + Entry<Integer, MIRegisterGroupDMC> entry = iterator.next(); + descriptors[i] = new RegisterGroupDescriptor(entry.getValue(), true); + i++; + } + + return descriptors; + } + + private <T> T[] concatenateArr(T[] origArr, T[] deltaArr) { + if (origArr == null) { + return deltaArr; + } + + if (deltaArr == null) { + return origArr; + } + + T[] newArr = Arrays.copyOf(origArr, origArr.length + deltaArr.length); + System.arraycopy(deltaArr, 0, newArr, origArr.length, deltaArr.length); + return newArr; + } + + private MIRegisterGroupDMC[] readGroupsFromMemento(final IContainerDMContext contDmc) { + RegisterGroupsPersistance deserializer = new RegisterGroupsPersistance(getLaunchConfig()); + IRegisterGroupDescriptor[] groupDescriptions = deserializer.parseGroups(); + + List<MIRegisterGroupDMC> groups = new ArrayList<MIRegisterGroupDMC>(); + for (IRegisterGroupDescriptor group : groupDescriptions) { + fGroupMementoDescriptorIndex.put(fGroupBookingCount, group); + groups.add(new MIRegisterGroupDMC(this, contDmc, fGroupBookingCount, group.getName())); + fGroupBookingCount++; + } + + return groups.toArray(new MIRegisterGroupDMC[groups.size()]); + } + + private void getUserGroupRegisters(IDMContext ctx, final DataRequestMonitor<IRegisterDMContext[]> rm) { + final MIRegisterGroupDMC groupDmc = DMContexts.getAncestorOfType(ctx, MIRegisterGroupDMC.class); + + // Need to build the corresponding register[] from the memento descriptors + IRegisterGroupDescriptor grpDescriptor = fGroupMementoDescriptorIndex.get(groupDmc.getGroupNo()); + + if (grpDescriptor == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, + "The Register Group Descriptor does not exist for group: " + groupDmc.getName(), null)); //$NON-NLS-1$ + rm.done(); + return; + } + + MIRegisterDMC[] registers; + try { + // Resolve bare registers from the memento descriptors + registers = resolveRegisters(grpDescriptor, ctx); + } catch (CoreException e) { + rm.setStatus(e.getStatus()); + rm.done(); + return; + } + + // update internal data + fGroupToRegistersMap.put(groupDmc, registers); + + // now resolve to context specific registers + buildGroupRegisters(ctx, registers, rm); + } + + /** + * Resolve register dmcs from de-serialized memento descriptors + */ + private MIRegisterDMC[] resolveRegisters(IRegisterGroupDescriptor grpDescriptor, IDMContext ctx) + throws CoreException { + final List<MIRegisterDMC> registerContexts = new ArrayList<MIRegisterDMC>(); + final IContainerDMContext containerDmc = DMContexts.getAncestorOfType(ctx, IContainerDMContext.class); + final MIRegisterGroupDMC groupDmc = DMContexts.getAncestorOfType(ctx, MIRegisterGroupDMC.class); + + IRegisterDescriptor[] registerDescriptions = grpDescriptor.getChildren(); + MIRegisterGroupDMC[] groupContexts = fContextToGroupsMap.get(containerDmc); + if (groupContexts != null && groupContexts.length > 0) { + // Get the General Group (base) at index 0, + // Registers map indexed by name + Map<String, MIRegisterDMC> indexedRegisterBase = fGroupToRegistersMap.getIndexedRegisters(groupContexts[0]); + + // For each descriptors find its corresponding MIRegisterDMC + for (IRegisterDescriptor registerDescription : registerDescriptions) { + MIRegisterDMC registerDmc = indexedRegisterBase.get(registerDescription.getName()); + if (registerDmc == null) { + // The Register is not present from the base received from GDB + // Create a register DMC with no execution dmc and invalid + // register number e.g. not mapped to a gdb register. + registerDmc = new MIRegisterDMC(this, groupDmc, -1, registerDescription.getName()); + } + + registerContexts.add(registerDmc); + } + } + + return registerContexts.toArray(new MIRegisterDMC[registerContexts.size()]); + } + + @Override + protected void generateRegisterChangedEvent(final IRegisterDMContext dmc) { + // notify the register value change + getSession().dispatchEvent(new RegisterChangedDMEvent(dmc), getProperties()); + + + // Propagate notification to all groups. + // A change of a single register needs to be propagated to all groups within the same Container/Process + // I.e. Some registers are dependent on the value of others and these dependent registers could be + // associated to different groups. + IContainerDMContext containerDmc = DMContexts.getAncestorOfType(dmc, IContainerDMContext.class); + generateRegistersChangedEvent(containerDmc); + } + + private void generateRegistersChangedEvent(IContainerDMContext containerDmc) { + //resolve the groups for the current container (process) context + final MIRegisterGroupDMC[] groups = fContextToGroupsMap.get(containerDmc); + + //trigger notification to all groups in the container + for (int i = 0; i < groups.length; i++) { + //We need final locals variables from the loop. Use a method call for this + generateRegistersChangedEvent(groups[i]); + } + } + + private void generateRegistersChangedEvent(final MIRegisterGroupDMC groupDmc) { + IRegistersChangedDMEvent event = new IRegistersChangedDMEvent() { + @Override + public IRegisterGroupDMContext getDMContext() { + return groupDmc; + } + }; + + getSession().dispatchEvent(event, getProperties()); + } + + private void generateRegisterGroupChangedEvent(final MIRegisterGroupDMC groupDmc) { + IGroupChangedDMEvent event = new IGroupChangedDMEvent() { + @Override + public IRegisterGroupDMContext getDMContext() { + return groupDmc; + } + }; + + getSession().dispatchEvent(event, getProperties()); + } + + + + /** + * Create Registers from specific execution context to a generic register context, e.g. not associated to a specific + * execution context. + */ + private MIRegisterDMC[] toBareRegisters(MIRegisterDMC[] registers) { + + MIRegisterDMC[] bareRegisters = new MIRegisterDMC[registers.length]; + for (int i = 0; i < registers.length; i++) { + // only one parent i.e. group context + MIRegisterGroupDMC groupDmc = DMContexts.getAncestorOfType(registers[i], MIRegisterGroupDMC.class); + assert (groupDmc != null); + bareRegisters[i] = new MIRegisterDMC(this, groupDmc, registers[i].getRegNo(), registers[i].getName()); + } + + return bareRegisters; + } + + private void buildGroupRegisters(final IDMContext ctx, final MIRegisterDMC[] baseRegisters, + final DataRequestMonitor<IRegisterDMContext[]> rm) { + final MIRegisterGroupDMC groupDmc = DMContexts.getAncestorOfType(ctx, MIRegisterGroupDMC.class); + + assert (groupDmc != null); + + final IFrameDMContext frameDmc = DMContexts.getAncestorOfType(ctx, IFrameDMContext.class); + if (frameDmc == null) { + // The selection does not provide a specific frame, then resolve the top frame on the current thread + // if the execution frame is not available proceed with no frame context i.e. will not be able to resolve + // values. + IMIExecutionDMContext execDmc = DMContexts.getAncestorOfType(ctx, IMIExecutionDMContext.class); + if (execDmc != null) { + IStack stackService = getServicesTracker().getService(IStack.class); + if (stackService != null) { + stackService.getTopFrame(execDmc, new ImmediateDataRequestMonitor<IStack.IFrameDMContext>(rm) { + @Override + protected void handleSuccess() { + cloneRegistersToContext(groupDmc, getData(), baseRegisters, rm); + } + + @Override + protected void handleFailure() { + // Unable to resolve top frame on current thread. + // The thread could e.g. be in running state, + // we return register instances with no associated execution context + // i.e. unable to resolve its associated value. + cloneRegistersToContext(groupDmc, null, baseRegisters, rm); + } + }); + + return; + } + } + } + + cloneRegistersToContext(groupDmc, frameDmc, baseRegisters, rm); + } + + /** + * Create a new array of register instances with the given context + */ + private void cloneRegistersToContext(MIRegisterGroupDMC groupDmc, IFrameDMContext frameDmc, + MIRegisterDMC[] baseRegisters, DataRequestMonitor<IRegisterDMContext[]> rm) { + MIRegisterDMC[] registers = new MIRegisterDMC[baseRegisters.length]; + if (frameDmc != null) { + // build to valid stack frame context + for (int i = 0; i < registers.length; i++) { + registers[i] = new MIRegisterDMC(this, groupDmc, frameDmc, baseRegisters[i].getRegNo(), + baseRegisters[i].getName()); + } + } else { + // build with no execution context, normal case if a selection is pointing to + // e.g. a running thread, a process.. i.e. not able to associate register values. + for (int i = 0; i < registers.length; i++) { + registers[i] = new MIRegisterDMC(this, groupDmc, baseRegisters[i].getRegNo(), + baseRegisters[i].getName()); + } + } + + // return the registers + rm.setData(registers); + rm.done(); + } + + @Override + public void canRemoveRegisterGroups(IRegisterGroupDMContext[] groups, DataRequestMonitor<Boolean> rm) { + if (groups == null || groups.length < 1) { + rm.setData(false); + rm.done(); + return; + } + + for(IRegisterGroupDMContext group : groups) { + assert(group instanceof MIRegisterGroupDMC); + MIRegisterGroupDMC miGroup = (MIRegisterGroupDMC) group; + if (miGroup.getName().equals(ROOT_GROUP_NAME)) { + // Not allowed to remove the root group + rm.setData(false); + rm.done(); + return; + } + } + + rm.setData(true); + rm.done(); + + } + + @Override + public void canRestoreDefaultGroups(IDMContext selectionContext, DataRequestMonitor<Boolean> rm) { + // Not relevant checks at this point + rm.setData(true); + rm.done(); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java index 6e62fd373bb..924f4cf973c 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java @@ -17,6 +17,7 @@ * William Riley (Renesas) - Support for GDB 7.3 disassembly service (Bug 357270) * Marc Khouzam (Ericsson) - Support for GDB 7.4 processes service (Bug 389945) * Marc Khouzam (Ericsson) - Support dynamic printf in bp service 7.5 (Bug 400628) + * Alvaro Sanchez-Leon (Ericsson) - Allow user to edit the register groups (Bug 235747) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; @@ -48,7 +49,6 @@ import org.eclipse.cdt.dsf.mi.service.MIBreakpointsSynchronizer; import org.eclipse.cdt.dsf.mi.service.MIDisassembly; import org.eclipse.cdt.dsf.mi.service.MIExpressions; import org.eclipse.cdt.dsf.mi.service.MIModules; -import org.eclipse.cdt.dsf.mi.service.MIRegisters; import org.eclipse.cdt.dsf.mi.service.MIStack; import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; import org.eclipse.cdt.dsf.service.DsfSession; @@ -231,7 +231,7 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory { @Override protected IRegisters createRegistersService(DsfSession session) { - return new MIRegisters(session); + return new GDBRegisters(session); } @Override diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/Messages.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/Messages.java index 13f6e681016..cc3da86b098 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/Messages.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/Messages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012, 2013 Ericsson and others. + * Copyright (c) 2012, 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 @@ -7,6 +7,7 @@ * * Contributors: * Marc Khouzam (Ericsson) - initial API and implementation + * Alvaro Sanchez-Leon (Ericsson) - Bug 235747 *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; @@ -25,6 +26,9 @@ class Messages extends NLS { public static String UniqueMatch; public static String UniqueMatches; public static String ErrorNotSupported; + public static String RegisterGroup_name_reserved; + public static String RegisterGroup_name_used; + public static String RegisterGroup_invalid_number_of_registers; static { // initialize resource bundle diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/Messages.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/Messages.properties index 8a6245c7a19..96bdbce7173 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/Messages.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/Messages.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2012 Ericsson and others. +# Copyright (c) 2012, 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 @@ -7,6 +7,7 @@ # # Contributors: # Marc Khouzam (Ericsson) - initial API and implementation +# Alvaro Sanchez-Leon (Ericsson) - Support Register Groups (Bug 235747) ############################################################################### Tracing_not_supported_error=Tracing not supported @@ -17,3 +18,6 @@ NoMatches=No matches UniqueMatch={0} unique match UniqueMatches={0} unique matches ErrorNotSupported=Operation not supported with this GDB version +RegisterGroup_name_reserved=The group name "{0}" is reserved +RegisterGroup_name_used=The group name "{0}" is already in use +RegisterGroup_invalid_number_of_registers=A minimum of one register is needed for a Register Group diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRegisters.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRegisters.java index e0239b89425..cf7de936f0f 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRegisters.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRegisters.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2010 Wind River Systems and others. + * Copyright (c) 2006, 2014 Wind River Systems 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 @@ -11,6 +11,7 @@ * Roland Grunberg (RedHat) - Refresh all registers once one is changed (Bug 400840) * Alvaro Sanchez-Leon (Ericsson) - Make Registers View specific to a frame (Bug 323552) * Alvaro Sanchez-Leon (Ericsson) - Register view does not refresh register names per process (Bug 418176) + * Alvaro Sanchez-Leon (Ericsson) - Allow user to edit the register groups (Bug 235747) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service; @@ -66,7 +67,18 @@ import org.osgi.framework.BundleContext; */ public class MIRegisters extends AbstractDsfService implements IRegisters, ICachingService { - private static final String BLANK_STRING = ""; //$NON-NLS-1$ + /** + * @since 4.6 + */ + protected static final String BLANK_STRING = ""; //$NON-NLS-1$ + /** + * @since 4.6 + */ + protected static final String ROOT_GROUP_NAME = Messages.MIRegisters_General_Registers; + /** + * @since 4.6 + */ + protected static final String ROOT_GROUP_DESCRIPTION = Messages.MIRegisters_General_Registers_description; /* * Support class used to construct Register Group DMCs. */ @@ -83,6 +95,12 @@ public class MIRegisters extends AbstractDsfService implements IRegisters, ICach public int getGroupNo() { return fGroupNo; } public String getName() { return fGroupName; } + /** + * @since 4.6 + */ + public void setName(String groupName) { + fGroupName = groupName; + } @Override public boolean equals(Object other) { @@ -93,7 +111,7 @@ public class MIRegisters extends AbstractDsfService implements IRegisters, ICach @Override public int hashCode() { return super.baseHashCode() ^ fGroupNo; } @Override - public String toString() { return baseToString() + ".group[" + fGroupNo + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + public String toString() { return baseToString() + ".group[" + fGroupNo + "," + fGroupName + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } /* @@ -125,20 +143,20 @@ public class MIRegisters extends AbstractDsfService implements IRegisters, ICach } /** - * This Register context is associated to two parent contexts. A stack frame context (IFrameDMContext), and a register group - * context (MIRegisterGroupDMC). When the scenario requires to build a register contexts from the selection of a thread, then the top - * frame shall be resolved and be provided in this constructor. + * This Register context is associated to two parent contexts. A stack frame context (IFrameDMContext), and a + * register group context (MIRegisterGroupDMC). When the scenario requires to build a register contexts from the + * selection of a thread, then the top frame shall be resolved and be provided in this constructor. * * The frame context is necessary to resolve the register's data e.g. value * * @since 4.3 */ - public MIRegisterDMC(MIRegisters service, MIRegisterGroupDMC group, IFrameDMContext frameDmc, int regNo, String regName) { - super(service.getSession().getId(), - new IDMContext[] { frameDmc, group }); - fRegNo = regNo; - fRegName = regName; - } + public MIRegisterDMC(MIRegisters service, MIRegisterGroupDMC group, IFrameDMContext frameDmc, int regNo, + String regName) { + super(service.getSession().getId(), new IDMContext[] { frameDmc, group }); + fRegNo = regNo; + fRegName = regName; + } public int getRegNo() { return fRegNo; } public String getName() { return fRegName; } @@ -162,7 +180,10 @@ public class MIRegisters extends AbstractDsfService implements IRegisters, ICach private final IRegisterDMContext fRegisterDmc; - RegisterChangedDMEvent(IRegisterDMContext registerDMC) { + /** + * @since 4.6 + */ + public RegisterChangedDMEvent(IRegisterDMContext registerDMC) { fRegisterDmc = registerDMC; } @@ -172,14 +193,33 @@ public class MIRegisters extends AbstractDsfService implements IRegisters, ICach } } + /** + * Indicates a change in the list of Register groups e.g. after addition and removal + * @since 4.6 + */ + public static class GroupsChangedDMEvent implements IRegisters.IGroupsChangedDMEvent { + + private final IContainerDMContext fGroupContainerDmc; + + public GroupsChangedDMEvent(IContainerDMContext containerDMC) { + fGroupContainerDmc = containerDMC; + } + + @Override + public IContainerDMContext getDMContext() { + return fGroupContainerDmc; + } + } + /* * Internal control variables. */ private CommandFactory fCommandFactory; - //One Group per container process + //One Group per container process private final Map<IContainerDMContext, MIRegisterGroupDMC> fContainerToGroupMap = new HashMap<IContainerDMContext, MIRegisterGroupDMC>(); + private CommandCache fRegisterNameCache; // Cache for holding the Register Names in the single Group private CommandCache fRegisterValueCache; // Cache for holding the Register Values @@ -267,13 +307,13 @@ public class MIRegisters extends AbstractDsfService implements IRegisters, ICach public void getRegisterGroupData(IRegisterGroupDMContext regGroupDmc, DataRequestMonitor<IRegisterGroupDMData> rm) { /** * For the GDB GDBMI implementation there is only on group. The GPR and FPU registers are grouped into - * one set. We are going to hard wire this set as the "General Registers". + * one set. We are going to hard wire this set as the value of ROOT_GROUP_NAME. */ class RegisterGroupData implements IRegisterGroupDMData { @Override - public String getName() { return "General Registers"; } //$NON-NLS-1$ + public String getName() { return ROOT_GROUP_NAME; } @Override - public String getDescription() { return "General Purpose and FPU Register Group"; } //$NON-NLS-1$ + public String getDescription() { return ROOT_GROUP_DESCRIPTION; } } rm.setData( new RegisterGroupData() ) ; @@ -387,7 +427,10 @@ public class MIRegisters extends AbstractDsfService implements IRegisters, ICach }); } - static class RegisterData implements IRegisterDMData { + /** + * @since 4.6 + */ + protected static class RegisterData implements IRegisterDMData { final private String fRegName; final private String fRegDesc; @@ -487,14 +530,18 @@ public class MIRegisters extends AbstractDsfService implements IRegisters, ICach fRegisterValueCache.reset(); } - private void generateRegisterChangedEvent(final IRegisterDMContext dmc ) { + /** + * @since 4.6 + */ + protected void generateRegisterChangedEvent(IRegisterDMContext dmc ) { getSession().dispatchEvent(new RegisterChangedDMEvent(dmc), getProperties()); - // Temporary fix for Bug 400840 + // Fix for Bug 400840 // When one register is modified, it could affect other registers. // To properly reflect that, we send a change for all registers. - // We cheat since we know there is currently only one group. Once we handle - // more groups, this may not work properly. + // We cheat by pretending the group has changed, since we know there is + // only one group. + // This method can be extended by group managers to propagate the event as needed final IRegisterGroupDMContext groupDmc = DMContexts.getAncestorOfType(dmc, IRegisterGroupDMContext.class); if (groupDmc != null) { IRegistersChangedDMEvent event = new IRegistersChangedDMEvent() { @@ -504,8 +551,7 @@ public class MIRegisters extends AbstractDsfService implements IRegisters, ICach } }; getSession().dispatchEvent(event, getProperties()); - } - + } } /* @@ -524,15 +570,16 @@ public class MIRegisters extends AbstractDsfService implements IRegisters, ICach } //Bug 418176 - //Only one group per Process (container) is supported for the time being + //Only one group per Process (container) is supported in the implementation of this class MIRegisterGroupDMC registerGroup = fContainerToGroupMap.get(contDmc); - if (registerGroup == null) { - registerGroup = new MIRegisterGroupDMC( this , contDmc, 0 , "General Registers" ) ; //$NON-NLS-1$ - fContainerToGroupMap.put(contDmc, registerGroup); - } + if (registerGroup == null) { + registerGroup = new MIRegisterGroupDMC(this, contDmc, 0, ROOT_GROUP_NAME); + fContainerToGroupMap.put(contDmc, registerGroup); + } MIRegisterGroupDMC[] groupDMCs = new MIRegisterGroupDMC[] { registerGroup }; + rm.setData(groupDMCs) ; rm.done() ; } @@ -642,48 +689,35 @@ public class MIRegisters extends AbstractDsfService implements IRegisters, ICach * @see org.eclipse.cdt.dsf.debug.service.IRegisters#writeRegister(org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext, java.lang.String, java.lang.String, org.eclipse.cdt.dsf.concurrent.RequestMonitor) */ @Override - public void writeRegister(IRegisterDMContext regCtx, final String regValue, final String formatId, final RequestMonitor rm) { - MIRegisterGroupDMC grpDmc = DMContexts.getAncestorOfType(regCtx, MIRegisterGroupDMC.class); - if ( grpDmc == null ) { - rm.setStatus( new Status( IStatus.ERROR , GdbPlugin.PLUGIN_ID , INVALID_HANDLE , "RegisterGroup context not found" , null ) ) ; //$NON-NLS-1$ - rm.done(); - return; - } - - final MIRegisterDMC regDmc = (MIRegisterDMC)regCtx; - // There is only one group and its number must be 0. - if ( grpDmc.getGroupNo() == 0 ) { - final IExpressions exprService = getServicesTracker().getService(IExpressions.class); - String regName = regDmc.getName(); - final IExpressionDMContext exprCtxt = exprService.createExpression(regCtx, "$" + regName); //$NON-NLS-1$ - - final FormattedValueDMContext valueDmc = exprService.getFormattedValueContext(exprCtxt, formatId); - exprService.getFormattedExpressionValue( - valueDmc, - new DataRequestMonitor<FormattedValueDMData>(getExecutor(), rm) { - @Override - protected void handleSuccess() { - if(! regValue.equals(getData().getFormattedValue()) || ! valueDmc.getFormatID().equals(formatId)){ - exprService.writeExpression(exprCtxt, regValue, formatId, new DataRequestMonitor<MIInfo>(getExecutor(), rm) { - @Override - protected void handleSuccess() { - generateRegisterChangedEvent(regDmc); - rm.done(); - } - }); - }//if - else { - rm.done(); - } - }//handleSuccess - } - ); - } - else { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Invalid group = " + grpDmc, null)); //$NON-NLS-1$ - rm.done(); - } - } + public void writeRegister(IRegisterDMContext regCtx, final String regValue, final String formatId, + final RequestMonitor rm) { + + assert(regCtx instanceof MIRegisterDMC); + final MIRegisterDMC regDmc = (MIRegisterDMC) regCtx; + + final IExpressions exprService = getServicesTracker().getService(IExpressions.class); + String regName = regDmc.getName(); + final IExpressionDMContext exprCtxt = exprService.createExpression(regCtx, "$" + regName); //$NON-NLS-1$ + + final FormattedValueDMContext valueDmc = exprService.getFormattedValueContext(exprCtxt, formatId); + exprService.getFormattedExpressionValue(valueDmc, new DataRequestMonitor<FormattedValueDMData>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + if (!regValue.equals(getData().getFormattedValue()) || !valueDmc.getFormatID().equals(formatId)) { + exprService.writeExpression(exprCtxt, regValue, formatId, new DataRequestMonitor<MIInfo>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + generateRegisterChangedEvent(regDmc); + rm.done(); + } + }); + }// if + else { + rm.done(); + } + }// handleSuccess + }); + } /* * (non-Javadoc) diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.java index 2eed408eb77..d7435c8063e 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2010 Ericsson and others. + * Copyright (c) 2010, 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 @@ -8,6 +8,7 @@ * Contributors: * Ericsson - initial API and implementation * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) + * Alvaro Sanchez-Leon (Ericsson) - Support Register Groups (Bug 235747) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service; @@ -23,6 +24,8 @@ class Messages extends NLS { public static String Breakpoint_installation_failed; public static String MIExpressions_NotAvailableBecauseChildOfDynamicVarobj; public static String MIExpressions_ReturnValueAlias; + public static String MIRegisters_General_Registers; + public static String MIRegisters_General_Registers_description; static { // initialize resource bundle diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.properties index 62306b7f606..ddee09bb77f 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2010 Ericsson and others. +# Copyright (c) 2010, 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 @@ -8,6 +8,7 @@ # Contributors: # Ericsson - initial API and implementation # Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) +# Alvaro Sanchez-Leon (Ericsson) - Support Register Groups (Bug 235747) ############################################################################### Breakpoint_attribute_detailed_problem=Breakpoint installation failed: {0} @@ -16,3 +17,7 @@ Breakpoint_installation_failed=installation failed MIExpressions_NotAvailableBecauseChildOfDynamicVarobj=N/A (child of pretty-printed object) MIExpressions_ReturnValueAlias=%s returned + +MIRegisters_General_Registers=General Registers + +MIRegisters_General_Registers_description=General Purpose and FPU Register Group |