mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-29 19:45:01 +02:00
Bug 309576 - Memory view doesn't work with 64 bit executables
Change-Id: Ie23f1c28b312a8a1b5705dddf0a20664d48b1a2f Reviewed-on: https://git.eclipse.org/r/11529 Reviewed-by: Marc Khouzam <marc.khouzam@ericsson.com> IP-Clean: Marc Khouzam <marc.khouzam@ericsson.com> Tested-by: Marc Khouzam <marc.khouzam@ericsson.com> Reviewed-by: Mikhail Khodjaiants <mikhailkhod@googlemail.com> IP-Clean: Mikhail Khodjaiants <mikhailkhod@googlemail.com> Tested-by: Mikhail Khodjaiants <mikhailkhod@googlemail.com>
This commit is contained in:
parent
54990da8f6
commit
68dae1a7ec
9 changed files with 386 additions and 5 deletions
|
@ -27,6 +27,7 @@ import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext;
|
|||
import org.eclipse.cdt.dsf.debug.service.IMemorySpaces;
|
||||
import org.eclipse.cdt.dsf.debug.service.IMemorySpaces.IMemorySpaceDMContext;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||
import org.eclipse.cdt.dsf.gdb.service.IGDBMemory;
|
||||
import org.eclipse.cdt.utils.Addr64;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
|
@ -266,4 +267,25 @@ public class GdbMemoryBlock extends DsfMemoryBlock implements IMemorySpaceAwareM
|
|||
}
|
||||
return super.getExpression();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAddressSize() throws DebugException {
|
||||
GdbMemoryBlockRetrieval retrieval = (GdbMemoryBlockRetrieval)getMemoryBlockRetrieval();
|
||||
IMemoryDMContext context = null;
|
||||
if (fMemorySpaceID != null) {
|
||||
IMemorySpaces memorySpacesService = (IMemorySpaces) retrieval.getMemorySpaceServiceTracker().getService();
|
||||
if (memorySpacesService != null) {
|
||||
context = new MemorySpaceDMContext(memorySpacesService.getSession().getId(), fMemorySpaceID, getContext());
|
||||
}
|
||||
}
|
||||
else {
|
||||
context = getContext();
|
||||
}
|
||||
IGDBMemory memoryService = (IGDBMemory)retrieval.getServiceTracker().getService();
|
||||
if (memoryService != null) {
|
||||
return memoryService.getAddressSize(context);
|
||||
}
|
||||
|
||||
throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, Messages.Err_MemoryServiceNotAvailable, null));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,223 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2013 Mentor Graphics 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:
|
||||
* Mentor Graphics - Initial API and implementation
|
||||
* John Dallaway - Add methods to get the endianness and address size (Bug 225609)
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.gdb.service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.cdt.core.IAddress;
|
||||
import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
|
||||
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
||||
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.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.debug.service.IRunControl.IStartedDMEvent;
|
||||
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.CLIShowEndianInfo;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIDataEvaluateExpressionInfo;
|
||||
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 IGDBMemory {
|
||||
|
||||
private IGDBControl fCommandControl;
|
||||
|
||||
/**
|
||||
* Cache of the address sizes for each memory context.
|
||||
*/
|
||||
private Map<IMemoryDMContext, Integer> fAddressSizes = new HashMap<IMemoryDMContext, Integer>();
|
||||
|
||||
/**
|
||||
* We assume the endianness is the same for all processes because GDB supports only one target.
|
||||
*/
|
||||
private boolean fIsBigEndian = false;
|
||||
|
||||
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(),
|
||||
GDBMemory.class.getName(),
|
||||
},
|
||||
new Hashtable<String, String>());
|
||||
requestMonitor.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown(RequestMonitor requestMonitor) {
|
||||
unregister();
|
||||
getSession().removeServiceEventListener(this);
|
||||
fAddressSizes.clear();
|
||||
super.shutdown(requestMonitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readMemoryBlock(final IDMContext dmc, IAddress address,
|
||||
long offset, int word_size, int count, final DataRequestMonitor<MemoryByte[]> drm) {
|
||||
super.readMemoryBlock(
|
||||
dmc,
|
||||
address,
|
||||
offset,
|
||||
word_size,
|
||||
count,
|
||||
new DataRequestMonitor<MemoryByte[]>(ImmediateExecutor.getInstance(), drm) {
|
||||
@Override
|
||||
@ConfinedToDsfExecutor("fExecutor")
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@DsfServiceEventHandler
|
||||
public void eventDispatched(IStartedDMEvent event) {
|
||||
if (event.getDMContext() instanceof IContainerDMContext) {
|
||||
IMemoryDMContext memContext = DMContexts.getAncestorOfType(event.getDMContext(), IMemoryDMContext.class);
|
||||
if (memContext != null) {
|
||||
readAddressSize(memContext);
|
||||
readEndianness(memContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAddressSize(IMemoryDMContext context) {
|
||||
Integer addressSize = fAddressSizes.get(context);
|
||||
return (addressSize != null) ? addressSize.intValue() : 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBigEndian(IMemoryDMContext context) {
|
||||
return fIsBigEndian;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the address size for given memory and execution contexts.
|
||||
*/
|
||||
private void readAddressSize(final IMemoryDMContext memContext) {
|
||||
Integer addrSize = fAddressSizes.get(memContext);
|
||||
if (addrSize == null) {
|
||||
doReadAddressSize(
|
||||
memContext,
|
||||
new DataRequestMonitor<Integer>(ImmediateExecutor.getInstance(), null) {
|
||||
@Override
|
||||
@ConfinedToDsfExecutor("fExecutor")
|
||||
protected void handleSuccess() {
|
||||
fAddressSizes.put(memContext, getData());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the endianness for given memory and execution contexts.
|
||||
*/
|
||||
private void readEndianness(IMemoryDMContext memContext) {
|
||||
doReadEndianness(
|
||||
memContext,
|
||||
new DataRequestMonitor<Boolean>(ImmediateExecutor.getInstance(), null) {
|
||||
@Override
|
||||
@ConfinedToDsfExecutor("fExecutor")
|
||||
protected void handleSuccess() {
|
||||
fIsBigEndian = getData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void doReadAddressSize(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
|
||||
@ConfinedToDsfExecutor("fExecutor")
|
||||
protected void handleSuccess() {
|
||||
try {
|
||||
drm.setData(Integer.decode(getData().getValue()));
|
||||
}
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void doReadEndianness(IMemoryDMContext memContext, final DataRequestMonitor<Boolean> drm) {
|
||||
CommandFactory commandFactory = fCommandControl.getCommandFactory();
|
||||
fCommandControl.queueCommand(
|
||||
commandFactory.createCLIShowEndian(memContext),
|
||||
new DataRequestMonitor<CLIShowEndianInfo>(ImmediateExecutor.getInstance(), drm) {
|
||||
@Override
|
||||
@ConfinedToDsfExecutor("fExecutor")
|
||||
protected void handleSuccess() {
|
||||
drm.setData(Boolean.valueOf(getData().isBigEndian()));
|
||||
drm.done();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ import org.eclipse.cdt.dsf.mi.service.MIMemory;
|
|||
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||
import org.eclipse.debug.core.model.MemoryByte;
|
||||
|
||||
public class GDBMemory_7_0 extends MIMemory {
|
||||
public class GDBMemory_7_0 extends GDBMemory {
|
||||
|
||||
public GDBMemory_7_0(DsfSession session) {
|
||||
super(session);
|
||||
|
@ -45,7 +45,14 @@ public class GDBMemory_7_0 extends MIMemory {
|
|||
}
|
||||
|
||||
private void doInitialize(final RequestMonitor requestMonitor) {
|
||||
register(new String[] { MIMemory.class.getName(), IMemory.class.getName(), GDBMemory_7_0.class.getName()},
|
||||
register(
|
||||
new String[] {
|
||||
MIMemory.class.getName(),
|
||||
IMemory.class.getName(),
|
||||
IGDBMemory.class.getName(),
|
||||
GDBMemory.class.getName(),
|
||||
GDBMemory_7_0.class.getName()
|
||||
},
|
||||
new Hashtable<String, String>());
|
||||
|
||||
requestMonitor.done();
|
||||
|
|
|
@ -61,6 +61,8 @@ public class GDBMemory_7_6 extends GDBMemory_7_0 implements IEventListener {
|
|||
private void doInitialize(final RequestMonitor requestMonitor) {
|
||||
register(new String[] { MIMemory.class.getName(),
|
||||
IMemory.class.getName(),
|
||||
IGDBMemory.class.getName(),
|
||||
GDBMemory.class.getName(),
|
||||
GDBMemory_7_0.class.getName(),
|
||||
GDBMemory_7_6.class.getName()},
|
||||
new Hashtable<String, String>());
|
||||
|
|
|
@ -42,7 +42,6 @@ import org.eclipse.cdt.dsf.mi.service.MIBreakpointsManager;
|
|||
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.MIMemory;
|
||||
import org.eclipse.cdt.dsf.mi.service.MIModules;
|
||||
import org.eclipse.cdt.dsf.mi.service.MIRegisters;
|
||||
import org.eclipse.cdt.dsf.mi.service.MIStack;
|
||||
|
@ -186,7 +185,7 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory {
|
|||
return new GDBMemory_7_0(session);
|
||||
}
|
||||
|
||||
return new MIMemory(session);
|
||||
return new GDBMemory(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2013 Mentor Graphics 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:
|
||||
* Mentor Graphics - Initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.gdb.service;
|
||||
|
||||
import org.eclipse.cdt.dsf.debug.service.IMemory;
|
||||
|
||||
/**
|
||||
* @since 4.2
|
||||
*/
|
||||
public interface IGDBMemory extends IMemory {
|
||||
|
||||
/**
|
||||
* Returns the address size (in bytes) of the memory specified by the given context.
|
||||
*/
|
||||
public int getAddressSize(IMemoryDMContext context);
|
||||
|
||||
/**
|
||||
* Returns whether the memory specified by the given context is big endian.
|
||||
*/
|
||||
public boolean isBigEndian(IMemoryDMContext context);
|
||||
}
|
|
@ -28,6 +28,7 @@ import org.eclipse.cdt.dsf.datamodel.IDMContext;
|
|||
import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IModules.IModuleDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
|
||||
|
@ -52,6 +53,7 @@ import org.eclipse.cdt.dsf.mi.service.command.commands.CLIMaintenance;
|
|||
import org.eclipse.cdt.dsf.mi.service.command.commands.CLIPasscount;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.CLIRecord;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.CLIRemoteGet;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.CLIShowEndian;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.CLISource;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.CLIThread;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.CLITrace;
|
||||
|
@ -172,6 +174,7 @@ import org.eclipse.cdt.dsf.mi.service.command.output.CLIInfoBreakInfo;
|
|||
import org.eclipse.cdt.dsf.mi.service.command.output.CLIInfoProgramInfo;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.CLIInfoSharedLibraryInfo;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.CLIInfoThreadsInfo;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.CLIShowEndianInfo;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.CLIThreadInfo;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.CLITraceDumpInfo;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.CLITraceInfo;
|
||||
|
@ -291,6 +294,13 @@ public class CommandFactory {
|
|||
return new CLIRemoteGet(ctx, remoteFile, localFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.2
|
||||
*/
|
||||
public ICommand<CLIShowEndianInfo> createCLIShowEndian(IMemoryDMContext ctx) {
|
||||
return new CLIShowEndian(ctx);
|
||||
}
|
||||
|
||||
public ICommand<MIInfo> createCLISource(ICommandControlDMContext ctx, String file) {
|
||||
return new CLISource(ctx, file);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2013 Mentor Graphics 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:
|
||||
* Mentor Graphics - Initial API and implementation
|
||||
* John Dallaway - Add methods to get the endianness and address size (Bug 225609)
|
||||
*******************************************************************************/
|
||||
|
||||
package org.eclipse.cdt.dsf.mi.service.command.commands;
|
||||
|
||||
import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.CLIShowEndianInfo;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput;
|
||||
|
||||
/**
|
||||
* Returns the endianness of the current GDB target.
|
||||
*
|
||||
* @since 4.2
|
||||
*/
|
||||
public class CLIShowEndian extends MIInterpreterExecConsole<CLIShowEndianInfo> {
|
||||
|
||||
private static final String SHOW_ENDIAN = "show endian"; //$NON-NLS-1$
|
||||
|
||||
public CLIShowEndian(IMemoryDMContext ctx) {
|
||||
super(ctx, SHOW_ENDIAN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CLIShowEndianInfo getResult(MIOutput miResult) {
|
||||
return new CLIShowEndianInfo(miResult);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2013 Mentor Graphics 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:
|
||||
* Mentor Graphics - Initial API and implementation
|
||||
*******************************************************************************/
|
||||
|
||||
package org.eclipse.cdt.dsf.mi.service.command.output;
|
||||
|
||||
/**
|
||||
* 'show endian' returns the endianness of the current target.
|
||||
*
|
||||
* sample output:
|
||||
*
|
||||
* (gdb) show endian
|
||||
* The target endianness is set automatically (currently little endian)
|
||||
*
|
||||
* @since 4.2
|
||||
*/
|
||||
public class CLIShowEndianInfo extends MIInfo {
|
||||
|
||||
final private static String BIG_ENDIAN = "big endian"; //$NON-NLS-1$
|
||||
|
||||
private boolean fIsBigEndian = false;
|
||||
|
||||
public CLIShowEndianInfo(MIOutput record) {
|
||||
super(record);
|
||||
parse();
|
||||
}
|
||||
|
||||
protected void parse() {
|
||||
if (isDone()) {
|
||||
MIOutput out = getMIOutput();
|
||||
for (MIOOBRecord oob : out.getMIOOBRecords()) {
|
||||
if (oob instanceof MIConsoleStreamOutput) {
|
||||
String line = ((MIConsoleStreamOutput)oob).getString().trim();
|
||||
if (line.indexOf(BIG_ENDIAN) >= 0 ) {
|
||||
fIsBigEndian = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBigEndian() {
|
||||
return fIsBigEndian;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue