blob: 06e08ebc1446d82fb8a136f5024091ef2a256c94 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2015 Wind River Systems, Inc. and others.
* All rights reserved. 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/
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.internal.debug.ui.model;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.IMemoryBlockManager;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IMemoryBlock;
import org.eclipse.debug.core.model.IMemoryBlockExtension;
import org.eclipse.debug.core.model.IMemoryBlockRetrieval;
import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension;
import org.eclipse.debug.core.model.MemoryByte;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
import org.eclipse.debug.internal.ui.viewers.provisional.AbstractModelProxy;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.memory.IMemoryRendering;
import org.eclipse.debug.ui.memory.IMemoryRenderingContainer;
import org.eclipse.debug.ui.memory.IMemoryRenderingManager;
import org.eclipse.debug.ui.memory.IMemoryRenderingSite;
import org.eclipse.debug.ui.memory.IMemoryRenderingType;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.widgets.Display;
import org.eclipse.tcf.internal.debug.model.ITCFConstants;
import org.eclipse.tcf.internal.debug.model.TCFLaunch;
import org.eclipse.tcf.internal.debug.ui.Activator;
import org.eclipse.tcf.protocol.IChannel;
import org.eclipse.tcf.protocol.IToken;
import org.eclipse.tcf.protocol.JSON;
import org.eclipse.tcf.protocol.Protocol;
import org.eclipse.tcf.services.IExpressions;
import org.eclipse.tcf.services.IMemory;
import org.eclipse.tcf.services.IMemory.MemoryError;
import org.eclipse.tcf.services.ISymbols;
import org.eclipse.tcf.util.TCFDataCache;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.helpers.DefaultHandler;
/**
* A memory block allows the user interface to request a memory block data from the debugger when needed.
* TCF memory block is based on TCF Memory service.
*/
class TCFMemoryBlock extends PlatformObject implements IMemoryBlockExtension, IModelProxyFactory {
private static class MemData {
final BigInteger addr;
final MemoryByte[] data;
final byte[] bytes;
MemData(BigInteger addr, MemoryByte[] data) {
int i = 0;
this.addr = addr;
this.data = data;
this.bytes = new byte[data.length];
for (MemoryByte b : data) bytes[i++] = b.getValue();
}
}
private static class ModelProxy extends AbstractModelProxy implements Runnable {
final TCFMemoryBlock mem_block;
final Display display;
ModelDelta delta;
public ModelProxy(TCFMemoryBlock mem_block, Display display) {
this.mem_block = mem_block;
this.display = display;
}
@Override
public void installed(Viewer viewer) {
synchronized (mem_block.model_proxies) {
if (isDisposed()) return;
setInstalled(true);
super.installed(viewer);
mem_block.model_proxies.add(this);
}
}
@Override
public void dispose() {
synchronized (mem_block.model_proxies) {
if (isDisposed()) return;
mem_block.model_proxies.remove(this);
super.dispose();
}
}
void onMemoryChanged(boolean suspended) {
assert Protocol.isDispatchThread();
int flags = IModelDelta.CONTENT;
if (suspended) flags |= IModelDelta.STATE;
if (delta != null) {
delta.setFlags(delta.getFlags() | flags);
}
else {
delta = new ModelDelta(mem_block, flags);
Protocol.invokeLater(this);
}
}
@Override
public void run() {
// Note: double posting is necessary to avoid deadlocks
assert Protocol.isDispatchThread();
final ModelDelta d = delta;
delta = null;
synchronized (Device.class) {
if (!display.isDisposed()) {
display.asyncExec(new Runnable() {
@Override
public void run() {
fireModelChanged(d);
}
});
}
}
}
}
private final TCFModel model;
private final IMemoryBlockRetrieval block_retrieval;
private final String ctx_id;
private final String expression;
private final long length;
private final Set<Object> connections = new HashSet<Object>();
private final TCFDataCache<IExpressions.Expression> remote_expression;
private final TCFDataCache<IExpressions.Value> expression_value;
private final LinkedList<ModelProxy> model_proxies = new LinkedList<ModelProxy>();
private MemData mem_data; // current memory block data
private MemData mem_prev; // previous data - before last suspend
private MemData mem_last; // last retrieved memory block data
private boolean disposed;
TCFMemoryBlock(final TCFModel model, IMemoryBlockRetrieval block_retrieval, final String ctx_id, final String expression, long length) {
assert Protocol.isDispatchThread();
this.model = model;
this.block_retrieval = block_retrieval;
this.ctx_id = ctx_id;
this.expression = expression;
this.length = length;
final TCFLaunch launch = model.getLaunch();
final IChannel channel = launch.getChannel();
remote_expression = new TCFDataCache<IExpressions.Expression>(channel) {
@Override
protected boolean startDataRetrieval() {
IExpressions exps = launch.getService(IExpressions.class);
if (exps == null) {
set(null, new Exception("Expressions service not available"), null);
return true;
}
command = exps.create(ctx_id, null, expression, new IExpressions.DoneCreate() {
@Override
public void doneCreate(IToken token, Exception error, IExpressions.Expression context) {
TCFNode node = model.getNode(ctx_id);
if (node == null) {
if (context != null) {
IExpressions exps = channel.getRemoteService(IExpressions.class);
exps.dispose(context.getID(), new IExpressions.DoneDispose() {
@Override
public void doneDispose(IToken token, Exception error) {
if (error == null) return;
if (channel.getState() != IChannel.STATE_OPEN) return;
Activator.log("Error disposing remote expression evaluator", error);
}
});
}
return;
}
set(token, error, context);
}
});
return false;
}
@Override
public void reset() {
if (isValid() && getData() != null) {
if (channel.getState() == IChannel.STATE_OPEN) {
IExpressions exps = channel.getRemoteService(IExpressions.class);
exps.dispose(remote_expression.getData().getID(), new IExpressions.DoneDispose() {
@Override
public void doneDispose(IToken token, Exception error) {
if (error == null) return;
if (channel.getState() != IChannel.STATE_OPEN) return;
Activator.log("Error disposing remote expression evaluator", error);
}
});
}
}
super.reset();
}
};
expression_value = new TCFDataCache<IExpressions.Value>(channel) {
@Override
protected boolean startDataRetrieval() {
if (!remote_expression.validate(this)) return false;
final IExpressions.Expression ctx = remote_expression.getData();
if (ctx == null) {
set(null, remote_expression.getError(), null);
return true;
}
IExpressions exps = launch.getService(IExpressions.class);
command = exps.evaluate(ctx.getID(), new IExpressions.DoneEvaluate() {
@Override
public void doneEvaluate(IToken token, Exception error, IExpressions.Value value) {
set(token, error, value);
}
});
return false;
}
};
}
@Override
public synchronized void connect(Object client) {
connections.add(client);
}
@Override
public synchronized void disconnect(Object client) {
connections.remove(client);
}
@Override
public synchronized Object[] getConnections() {
return connections.toArray(new Object[connections.size()]);
}
@Override
public void dispose() throws DebugException {
if (disposed) return;
new TCFDebugTask<Boolean>() {
@Override
public void run() {
if (!disposed) {
onContextExited(ctx_id);
expression_value.dispose();
remote_expression.dispose();
disposed = true;
}
done(Boolean.TRUE);
}
}.getD();
}
@Override
public int getAddressSize() throws DebugException {
return new TCFDebugTask<Integer>(model.getChannel()) {
@Override
public void run() {
TCFNode node = model.getNode(ctx_id);
if (node == null) {
error("Context is disposed");
}
else if (node instanceof TCFNodeExecContext) {
TCFDataCache<IMemory.MemoryContext> cache = ((TCFNodeExecContext)node).getMemoryContext();
if (!cache.validate(this)) return;
if (cache.getError() != null) {
error(cache.getError());
}
else {
IMemory.MemoryContext mem = cache.getData();
if (mem == null) {
error("Context does not provide memory access");
}
else {
done(mem.getAddressSize());
}
}
}
else {
error("Context does not provide memory access");
}
}
}.getD();
}
@Override
public int getAddressableSize() throws DebugException {
return new TCFDebugTask<Integer>(model.getChannel()) {
@Override
public void run() {
TCFNode node = model.getNode(ctx_id);
if (node == null) {
error("Context is disposed");
}
else if (node instanceof TCFNodeExecContext) {
TCFDataCache<IMemory.MemoryContext> cache = ((TCFNodeExecContext)node).getMemoryContext();
if (!cache.validate(this)) return;
if (cache.getError() != null) {
error(cache.getError());
}
else {
IMemory.MemoryContext mem = cache.getData();
if (mem == null) {
error("Context does not provide memory access");
}
else {
done(mem.getAddressableUnitSize());
}
}
}
else {
error("Context does not provide memory access");
}
}
}.getD();
}
@Override
public BigInteger getBigBaseAddress() throws DebugException {
return new TCFDebugTask<BigInteger>(model.getChannel()) {
@Override
public void run() {
if (!expression_value.validate()) {
expression_value.wait(this);
}
else if (expression_value.getError() != null) {
error(expression_value.getError());
}
else if (expression_value.getData() == null) {
error("Address expression evaluation failed");
}
else {
IExpressions.Value value = expression_value.getData();
if (value.getTypeClass() == ISymbols.TypeClass.array) {
BigInteger addr = JSON.toBigInteger(value.getAddress());
if (addr == null) {
error("Invalid expression: array without memory address");
}
else {
done(addr);
}
}
else {
byte[] data = value.getValue();
if (data == null || data.length == 0) {
error("Address expression value is empty (void)");
}
else {
boolean signed = value.getTypeClass() == ISymbols.TypeClass.integer;
done(TCFNumberFormat.toBigInteger(data, value.isBigEndian(), signed));
}
}
}
}
}.getD();
}
@Override
public MemoryByte[] getBytesFromAddress(final BigInteger address, final long units) throws DebugException {
return new TCFDebugTask<MemoryByte[]>(model.getChannel()) {
int offs = 0;
@Override
public void run() {
if (mem_data != null &&
address.compareTo(mem_data.addr) >= 0 &&
address.add(BigInteger.valueOf(units)).compareTo(
mem_data.addr.add(BigInteger.valueOf(mem_data.data.length))) <= 0) {
offs = address.subtract(mem_data.addr).intValue();
MemoryByte[] res = mem_data.data;
if (units < mem_data.data.length) {
res = new MemoryByte[(int)units];
System.arraycopy(mem_data.data, offs, res, 0, res.length);
}
setHistoryFlags();
done(res);
return;
}
TCFNode node = model.getNode(ctx_id);
if (node == null) {
error("Context is disposed");
return;
}
if (!(node instanceof TCFNodeExecContext)) {
error("Context does not provide memory access");
return;
}
TCFDataCache<IMemory.MemoryContext> cache = ((TCFNodeExecContext)node).getMemoryContext();
if (!cache.validate(this)) return;
if (cache.getError() != null) {
error(cache.getError());
return;
}
final IMemory.MemoryContext mem = cache.getData();
if (mem == null) {
error("Context does not provide memory access");
return;
}
final int size = (int)units;
final int mode = IMemory.MODE_CONTINUEONERROR | IMemory.MODE_VERIFY;
final byte[] buf = new byte[size];
final MemoryByte[] res = new MemoryByte[size];
mem.get(address, 1, buf, 0, size, mode, new IMemory.DoneMemory() {
@Override
public void doneMemory(IToken token, MemoryError error) {
int big_endian = 0;
if (mem.getProperties().get(IMemory.PROP_BIG_ENDIAN) != null) {
big_endian |= MemoryByte.ENDIANESS_KNOWN;
if (mem.isBigEndian()) big_endian |= MemoryByte.BIG_ENDIAN;
}
int cnt = 0;
while (offs < size) {
int flags = big_endian;
if (error instanceof IMemory.ErrorOffset) {
IMemory.ErrorOffset ofs = (IMemory.ErrorOffset)error;
int status = ofs.getStatus(cnt);
if (status == IMemory.ErrorOffset.BYTE_VALID) {
flags |= MemoryByte.READABLE | MemoryByte.WRITABLE;
}
else if ((status & IMemory.ErrorOffset.BYTE_UNKNOWN) != 0) {
if (cnt > 0) break;
}
}
else if (error == null) {
flags |= MemoryByte.READABLE | MemoryByte.WRITABLE;
}
res[offs] = new MemoryByte(buf[offs], (byte)flags);
offs++;
cnt++;
}
if (offs < size) {
mem.get(address.add(BigInteger.valueOf(offs)), 1, buf, offs, size - offs, mode, this);
}
else {
mem_last = mem_data = new MemData(address, res);
setHistoryFlags();
done(res);
}
}
});
}
}.getD();
}
private void setHistoryFlags() {
if (mem_data == null) return;
BigInteger addr = mem_data.addr;
BigInteger his_start = null;
BigInteger his_end = null;
if (mem_prev != null) {
his_start = mem_prev.addr;
his_end = mem_prev.addr.add(BigInteger.valueOf(mem_prev.data.length));
}
for (MemoryByte b : mem_data.data) {
int flags = b.getFlags();
if (mem_prev != null && addr.compareTo(his_start) >= 0 && addr.compareTo(his_end) < 0) {
flags |= MemoryByte.HISTORY_KNOWN;
int offs = addr.subtract(his_start).intValue();
if (b.getValue() != mem_prev.data[offs].getValue()) {
flags |= MemoryByte.CHANGED;
}
}
else {
flags &= ~(MemoryByte.HISTORY_KNOWN | MemoryByte.CHANGED);
}
b.setFlags((byte)flags);
addr = addr.add(BigInteger.valueOf(1));
}
}
@Override
public MemoryByte[] getBytesFromOffset(BigInteger offset, long units) throws DebugException {
return getBytesFromAddress(getBigBaseAddress().add(offset), units);
}
@Override
public String getExpression() {
return expression;
}
@Override
public IMemoryBlockRetrieval getMemoryBlockRetrieval() {
return block_retrieval;
}
@Override
public long getStartAddress() {
return 0; // Unbounded
}
@Override
public long getLength() {
return length;
}
@Override
public BigInteger getMemoryBlockStartAddress() throws DebugException {
return null; // Unbounded
}
@Override
public BigInteger getMemoryBlockEndAddress() throws DebugException {
return null; // Unbounded
}
@Override
public BigInteger getBigLength() throws DebugException {
return BigInteger.valueOf(length);
}
@Override
public void setBaseAddress(BigInteger address) throws DebugException {
}
@Override
public void setValue(BigInteger offset, final byte[] bytes) throws DebugException {
final BigInteger address = getBigBaseAddress().add(offset);
new TCFDebugTask<Object>(model.getChannel()) {
@Override
public void run() {
TCFNode node = model.getNode(ctx_id);
if (node == null) {
error("Context is disposed");
return;
}
if (!(node instanceof TCFNodeExecContext)) {
error("Context does not provide memory access");
return;
}
TCFDataCache<IMemory.MemoryContext> cache = ((TCFNodeExecContext)node).getMemoryContext();
if (!cache.validate(this)) return;
if (cache.getError() != null) {
error(cache.getError());
return;
}
final IMemory.MemoryContext mem = cache.getData();
if (mem == null) {
error("Context does not provide memory access");
return;
}
final int mode = IMemory.MODE_CONTINUEONERROR | IMemory.MODE_VERIFY;
mem.set(address, 1, bytes, 0, bytes.length, mode, new IMemory.DoneMemory() {
@Override
public void doneMemory(IToken token, MemoryError error) {
if (error != null) {
error(error);
}
else {
done(null);
}
}
});
}
}.getD();
}
@Override
public boolean supportBaseAddressModification() throws DebugException {
return false;
}
@Override
public boolean supportsChangeManagement() {
return true;
}
@Override
public byte[] getBytes() throws DebugException {
if (mem_data == null) return null;
return mem_data.bytes;
}
@Override
public void setValue(long offset, byte[] bytes) throws DebugException {
setValue(BigInteger.valueOf(offset), bytes);
}
@Override
public boolean supportsValueModification() {
return true;
}
@Override
public IDebugTarget getDebugTarget() {
return null;
}
@Override
public ILaunch getLaunch() {
return model.getLaunch();
}
@Override
public String getModelIdentifier() {
return ITCFConstants.ID_TCF_DEBUG_MODEL;
}
@Override
public IModelProxy createModelProxy(Object element, IPresentationContext context) {
assert element == this;
return new ModelProxy(this, context.getWindow().getShell().getDisplay());
}
@Override
@SuppressWarnings("rawtypes")
public Object getAdapter(Class adapter) {
if (adapter == IMemoryBlockRetrieval.class) return block_retrieval;
if (adapter == IMemoryBlockRetrievalExtension.class) return block_retrieval;
return super.getAdapter(adapter);
}
String getMemoryID() {
return ctx_id;
}
void flushAllCaches() {
mem_data = null;
}
void onMemoryChanged(boolean suspended) {
assert Protocol.isDispatchThread();
remote_expression.reset();
expression_value.reset();
if (suspended) mem_prev = mem_last;
mem_data = null;
synchronized (model_proxies) {
for (ModelProxy p : model_proxies) {
p.onMemoryChanged(suspended);
}
}
}
void onContextExited(String id) {
assert Protocol.isDispatchThread();
if (!id.equals(ctx_id)) return;
remote_expression.reset();
expression_value.reset();
}
/************************** Persistence ***************************************************/
private static final String
XML_NODE_MEMORY = "Memory",
XML_NODE_BLOCK = "Block",
XML_NODE_RENDERING = "Rendering",
XML_ATTR_ID = "ID",
XML_ATTR_VIEW = "View",
XML_ATTR_PANE = "Pane",
XML_ATTR_CTX = "Context",
XML_ATTR_ADDR = "Addr",
XML_ATTR_SIZE = "Size";
private static final String XML_FILE_NAME = "memview.xml";
private static final Display display = Display.getDefault();
private static final Map<String,List<Element>> blocks_memento = new HashMap<String,List<Element>>();
private static final Set<Runnable> pending_updates = new HashSet<Runnable>();
private static boolean memento_loaded;
private static void asyncExec(TCFModel model, Runnable r) {
synchronized (pending_updates) {
synchronized (Device.class) {
if (display.isDisposed()) return;
display.asyncExec(r);
}
pending_updates.add(r);
}
}
private static Node cloneXML(Document document, Node node) {
if (node instanceof Element) {
Element x = (Element)node;
Element y = document.createElement(x.getTagName());
NamedNodeMap attrs = x.getAttributes();
int l = attrs.getLength();
for (int i = 0; i < l; i++) {
Attr a = (Attr)attrs.item(i);
y.setAttribute(a.getName(), a.getValue());
}
Node c = x.getFirstChild();
while (c != null) {
Node d = cloneXML(document, c);
if (d != null) y.appendChild(d);
c = c.getNextSibling();
}
return y;
}
return null;
}
static void onModelCreated(TCFModel model) {
assert Protocol.isDispatchThread();
if (memento_loaded) return;
memento_loaded = true;
try {
synchronized (blocks_memento) {
// Load memory monitors memento from workspace
blocks_memento.clear();
IPath path = Activator.getDefault().getStateLocation();
File f = path.append(XML_FILE_NAME).toFile();
if (!f.exists()) return;
InputStream inp = new FileInputStream(f);
try {
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
parser.setErrorHandler(new DefaultHandler());
Element xml_memory = parser.parse(inp).getDocumentElement();
if (xml_memory.getTagName().equals(XML_NODE_MEMORY)) {
Node node = xml_memory.getFirstChild();
while (node != null) {
if (node instanceof Element && ((Element)node).getTagName().equals(XML_NODE_BLOCK)) {
Element xml_block = (Element)node;
String id = xml_block.getAttribute(XML_ATTR_CTX);
if (id != null) {
List<Element> list = blocks_memento.get(id);
if (list == null) {
list = new ArrayList<Element>();
blocks_memento.put(id, list);
}
list.add(xml_block);
}
}
node = node.getNextSibling();
}
}
}
finally {
inp.close();
}
}
}
catch (Exception x) {
Activator.log("Cannot read memory monitors memento", x);
}
}
static void onMemoryNodeCreated(final TCFNodeExecContext exe_ctx) {
assert Protocol.isDispatchThread();
synchronized (blocks_memento) {
// Restore memory monitors associated with the node
final List<Element> memento = blocks_memento.remove(exe_ctx.id);
if (memento == null || memento.size() == 0) return;
ArrayList<IMemoryBlock> list = new ArrayList<IMemoryBlock>();
for (Element xml_block : memento) {
String expr = xml_block.getAttribute(XML_ATTR_ADDR);
long length = Long.parseLong(xml_block.getAttribute(XML_ATTR_SIZE));
list.add(exe_ctx.model.getMemoryBlock(exe_ctx.id, expr, length));
}
final IMemoryBlock[] blks = list.toArray(new IMemoryBlock[list.size()]);
asyncExec(exe_ctx.model, new Runnable() {
@Override
public void run() {
synchronized (pending_updates) {
pending_updates.remove(this);
}
try {
int i = 0;
DebugPlugin.getDefault().getMemoryBlockManager().addMemoryBlocks(blks);
IMemoryRenderingManager rmngr = DebugUITools.getMemoryRenderingManager();
for (Element xml_block : memento) {
IMemoryBlock mb = blks[i++];
Node node = xml_block.getFirstChild();
while (node != null) {
if (node instanceof Element && ((Element)node).getTagName().equals(XML_NODE_RENDERING)) {
Element xml_rendering = (Element)node;
String view_id = xml_rendering.getAttribute(XML_ATTR_VIEW);
if (view_id != null && view_id.length() == 0) view_id = null;
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
IMemoryRenderingSite part = (IMemoryRenderingSite)page.showView(IDebugUIConstants.ID_MEMORY_VIEW,
view_id, IWorkbenchPage.VIEW_CREATE);
IMemoryRenderingType rendering_type = rmngr.getRenderingType(xml_rendering.getAttribute(XML_ATTR_ID));
IMemoryRendering rendering = rendering_type.createRendering();
IMemoryRenderingContainer container = part.getContainer(xml_rendering.getAttribute(XML_ATTR_PANE));
rendering.init(container, mb);
container.addMemoryRendering(rendering);
}
node = node.getNextSibling();
}
}
}
catch (Exception x) {
Activator.log("Cannot restore memory monitors", x);
}
}
});
}
}
static void onModelDisconnected(final TCFModel model) {
assert Protocol.isDispatchThread();
asyncExec(model, new Runnable() {
@Override
public void run() {
synchronized (pending_updates) {
pending_updates.remove(this);
}
// Dispose memory monitors associated with the model, update memento
ArrayList<IMemoryBlock> block_list = new ArrayList<IMemoryBlock>();
IMemoryBlockManager manager = DebugPlugin.getDefault().getMemoryBlockManager();
try {
Document document = DebugPlugin.newDocument();
Map<String,List<Element>> memento = new HashMap<String,List<Element>>();
Map<IMemoryBlock,Element> mb_to_xml = new HashMap<IMemoryBlock,Element>();
Element xml_memory = document.createElement(XML_NODE_MEMORY);
for (IMemoryBlock mb : manager.getMemoryBlocks()) {
if (mb instanceof TCFMemoryBlock) {
TCFMemoryBlock m = (TCFMemoryBlock)mb;
if (m.model != model) continue;
Element xml_block = document.createElement(XML_NODE_BLOCK);
xml_block.setAttribute(XML_ATTR_CTX, m.ctx_id);
xml_block.setAttribute(XML_ATTR_ADDR, m.expression);
xml_block.setAttribute(XML_ATTR_SIZE, Long.toString(m.length));
xml_memory.appendChild(xml_block);
mb_to_xml.put(m, xml_block);
List<Element> l = memento.get(m.ctx_id);
if (l == null) {
l = new ArrayList<Element>();
memento.put(m.ctx_id, l);
}
l.add(xml_block);
block_list.add(m);
}
}
for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) {
for (IWorkbenchPage page : window.getPages()) {
String[] pane_ids = {
IDebugUIConstants.ID_RENDERING_VIEW_PANE_1,
IDebugUIConstants.ID_RENDERING_VIEW_PANE_2 };
for (IViewReference ref : page.getViewReferences()) {
IViewPart part = ref.getView(false);
if (part instanceof IMemoryRenderingSite) {
IMemoryRenderingSite memory_view = (IMemoryRenderingSite)part;
for (String pane_id : pane_ids) {
IMemoryRenderingContainer container = memory_view.getContainer(pane_id);
if (container == null) continue;
IMemoryRendering[] renderings = container.getRenderings();
for (IMemoryRendering rendering : renderings) {
Element xml_block = mb_to_xml.get(rendering.getMemoryBlock());
if (xml_block == null) continue;
Element xml_rendering = document.createElement(XML_NODE_RENDERING);
xml_rendering.setAttribute(XML_ATTR_ID, rendering.getRenderingId());
if (ref.getSecondaryId() != null) {
xml_rendering.setAttribute(XML_ATTR_VIEW, ref.getSecondaryId());
}
xml_rendering.setAttribute(XML_ATTR_PANE, pane_id);
xml_block.appendChild(xml_rendering);
}
}
}
}
}
}
synchronized (blocks_memento) {
for (String id : blocks_memento.keySet()) {
if (memento.containsKey(id)) continue;
for (Element xml_block : blocks_memento.get(id)) {
xml_memory.appendChild(cloneXML(document, xml_block));
}
}
blocks_memento.clear();
Node node = xml_memory.getFirstChild();
while (node != null) {
if (node instanceof Element && ((Element)node).getTagName().equals(XML_NODE_BLOCK)) {
Element xml_block = (Element)node;
String id = xml_block.getAttribute(XML_ATTR_CTX);
if (id != null) {
List<Element> l = blocks_memento.get(id);
if (l == null) {
l = new ArrayList<Element>();
blocks_memento.put(id, l);
}
l.add(xml_block);
}
}
node = node.getNextSibling();
}
document.appendChild(xml_memory);
IPath path = Activator.getDefault().getStateLocation();
File f = path.append(XML_FILE_NAME).toFile();
FileOutputStream out = new FileOutputStream(f);
try {
BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
try {
wr.write(DebugPlugin.serializeDocument(document));
}
finally {
wr.close();
}
}
finally {
out.close();
}
}
}
catch (Exception x) {
Activator.log("Cannot save memory monitors", x);
}
if (block_list.size() != 0) manager.removeMemoryBlocks(block_list.toArray(new IMemoryBlock[block_list.size()]));
}
});
}
static void onWorkbenchShutdown() {
// Wait until all pending updates are done
display.syncExec(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (pending_updates) {
if (pending_updates.size() == 0) return;
}
if (!display.readAndDispatch()) display.sleep();
}
}
});
}
}