/******************************************************************************* * Copyright (c) 2011, 2012 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 * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.debug.test.services; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import org.eclipse.tcf.debug.test.services.ResetMap.IResettable; import org.eclipse.tcf.debug.test.util.DataCallback; import org.eclipse.tcf.debug.test.util.ICache; import org.eclipse.tcf.debug.test.util.RangeCache; import org.eclipse.tcf.debug.test.util.TokenCache; import org.eclipse.tcf.debug.test.util.Transaction; import org.eclipse.tcf.debug.test.util.TransactionCache; import org.eclipse.tcf.protocol.IChannel; import org.eclipse.tcf.protocol.IToken; import org.eclipse.tcf.services.IMemory; import org.eclipse.tcf.services.IMemory.MemoryContext; import org.eclipse.tcf.services.IMemoryMap; import org.eclipse.tcf.services.IRunControl; import org.eclipse.tcf.services.IRunControl.RunControlContext; import org.eclipse.tcf.services.IStackTrace; import org.eclipse.tcf.services.IStackTrace.StackTraceContext; /** * */ public class StackTraceCM extends AbstractCacheManager { private IStackTrace fService; private RunControlCM fRunControlCM; private IMemory fMemory; private IMemoryMap fMemoryMap; private final ResetMap fRunControlStateResetMap = new ResetMap(); private final ResetMap fMemoryResetMap = new ResetMap(); /** * Listener public for testing purposes only. */ public final IRunControl.RunControlListener fRunControlListener = new IRunControl.RunControlListener() { @Override public void contextAdded(RunControlContext[] contexts) { } @Override public void contextChanged(RunControlContext[] contexts) { for (RunControlContext context : contexts) fRunControlStateResetMap.reset(context.getID()); } @Override public void contextRemoved(String[] context_ids) { for (String id : context_ids) fRunControlStateResetMap.reset(id); } @Override public void contextSuspended(String context, String pc, String reason, Map<String, Object> params) { fRunControlStateResetMap.reset(context); } @Override public void contextResumed(String context) { fRunControlStateResetMap.reset(context); } @Override public void containerSuspended(String context, String pc, String reason, Map<String, Object> params, String[] suspended_ids) { for (String id : suspended_ids) fRunControlStateResetMap.reset(id); } @Override public void containerResumed(String[] context_ids) { for (String id : context_ids) fRunControlStateResetMap.reset(id); } @Override public void contextException(String context, String msg) { fRunControlStateResetMap.reset(context); } }; public final IMemory.MemoryListener fMemoryListener = new IMemory.MemoryListener() { @Override public void contextAdded(MemoryContext[] contexts) { } @Override public void contextChanged(MemoryContext[] contexts) { for (MemoryContext context : contexts) fMemoryResetMap.reset(context.getID()); } public void contextRemoved(String[] context_ids) { for (String context_id : context_ids) fMemoryResetMap.reset(context_id); }; @Override public void memoryChanged(String context_id, Number[] addr, long[] size) { fMemoryResetMap.reset(context_id); } }; public final IMemoryMap.MemoryMapListener fMemoryMapListener = new IMemoryMap.MemoryMapListener() { @Override public void changed(String context_id) { // Memory Map changed fMemoryResetMap.reset(context_id); } }; public StackTraceCM(IChannel channel, IStackTrace service, RunControlCM runControlCM, IMemory memory, IMemoryMap memoryMap) { super(channel); fService = service; fRunControlCM = runControlCM; fRunControlCM.getService().addListener(fRunControlListener); fMemory = memory; fMemory.addListener(fMemoryListener); fMemoryMap = memoryMap; fMemoryMap.addListener(fMemoryMapListener); } @Override public void dispose() { fRunControlCM.getService().removeListener(fRunControlListener); fMemory.removeListener(fMemoryListener); fMemoryMap.removeListener(fMemoryMapListener); super.dispose(); } public ICache<String[]> getChildren(final String id) { class MyCache extends TransactionCache<String[]> { class InnerCache extends TokenCache<String[]> implements IStackTrace.DoneGetChildren { InnerCache() { super(fChannel); } @Override protected IToken retrieveToken() { return fService.getChildren(id, this); } @Override public void doneGetChildren(IToken token, Exception error, String[] context_ids) { set(token, context_ids, error); } }; private final InnerCache fInner = new InnerCache(); @Override protected String[] process() throws InvalidCacheException, ExecutionException { RunControlContext rcContext = validate(fRunControlCM.getContext(id)); validate(fInner); fRunControlStateResetMap.addValid(id, fInner); fMemoryResetMap.addValid(rcContext.getProcessID(), fInner); return fInner.getData(); } }; return mapCache(new IdKey<MyCache>(MyCache.class, id) { @Override MyCache createCache() { return new MyCache(); } }); } public RangeCache<StackTraceContext> getContextRange(final String parentId) { class MyCache extends RangeCache<StackTraceContext> implements IResettable { boolean fIsValid = false; @Override protected void retrieve(final long offset, final int count, DataCallback<List<StackTraceContext>> rm) { new Transaction<List<StackTraceContext>>() { @Override protected List<StackTraceContext> process() throws InvalidCacheException, ExecutionException { String[] ids = validate(getChildren(parentId)); int adjustedCount = Math.min(count, ids.length + (int)offset); String[] subIds = new String[adjustedCount]; System.arraycopy(ids, (int)offset, subIds, 0, adjustedCount); StackTraceContext[] contexts = validate(getContexts(subIds)); RunControlContext rcContext = validate(fRunControlCM.getContext(parentId)); if (!fIsValid) { fRunControlStateResetMap.addValid(parentId, MyCache.this); fMemoryResetMap.addValid(rcContext.getProcessID(), MyCache.this); } return Arrays.asList(contexts); } }.request(rm); } public void reset() { fIsValid = false; @SuppressWarnings("unchecked") List<StackTraceContext> emptyData = (List<StackTraceContext>)Collections.EMPTY_LIST; set(0, 0, emptyData, new Throwable("Cache invalid") ); } }; return mapCache(new IdKey<MyCache>(MyCache.class, parentId) { @Override MyCache createCache() { return new MyCache(); } }); } public ICache<StackTraceContext[]> getContexts(final String[] ids) { assert ids.length != 0; class MyCache extends TransactionCache<StackTraceContext[]> { class InnerCache extends TokenCache<StackTraceContext[]> implements IStackTrace.DoneGetContext { InnerCache() { super(fChannel); } @Override protected IToken retrieveToken() { return fService.getContext(ids, this); } public void doneGetContext(IToken token, Exception error, StackTraceContext[] contexts) { set(token, contexts, error); } } private InnerCache fInner = new InnerCache(); @Override protected StackTraceContext[] process() throws InvalidCacheException, ExecutionException { StackTraceContext[] contexts = validate(fInner); String threadId = contexts[0].getParentID(); fMemoryResetMap.addPending(fInner); RunControlContext threadContext = validate(fRunControlCM.getContext(threadId)); fRunControlStateResetMap.addValid(threadId, fInner); fMemoryResetMap.addValid(threadContext.getProcessID(), fInner); return contexts; } } return mapCache(new IdKey<MyCache>(MyCache.class, Arrays.toString(ids)) { @Override MyCache createCache() { return new MyCache(); } }); } }