/******************************************************************************* * 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 v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * 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); 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(); } } 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); 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(); BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f), "UTF-8")); wr.write(DebugPlugin.serializeDocument(document)); wr.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(); } } }); } }