/******************************************************************************* * Copyright (c) 2000, 2009 QNX Software 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: * QNX Software Systems - Initial API and implementation * Alena Laskavaia (QNX) - Bug 221224 * Oyvind Harboe (oyvind.harboe@zylin.com) - Bug 86676 *******************************************************************************/ package org.eclipse.cdt.debug.mi.core.cdi.model; import java.util.ArrayList; import java.util.List; import org.eclipse.cdt.debug.core.cdi.CDIException; import org.eclipse.cdt.debug.core.cdi.ICDICondition; import org.eclipse.cdt.debug.core.cdi.ICDILocation; import org.eclipse.cdt.debug.core.cdi.model.ICDIBreakpoint; import org.eclipse.cdt.debug.core.cdi.model.ICDIExecuteMoveInstructionPointer; import org.eclipse.cdt.debug.core.cdi.model.ICDISignal; import org.eclipse.cdt.debug.core.cdi.model.ICDIStackFrame; import org.eclipse.cdt.debug.core.cdi.model.ICDIThread; import org.eclipse.cdt.debug.core.cdi.model.ICDIThreadStorage; import org.eclipse.cdt.debug.core.cdi.model.ICDIThreadStorageDescriptor; import org.eclipse.cdt.debug.mi.core.MIException; import org.eclipse.cdt.debug.mi.core.MISession; import org.eclipse.cdt.debug.mi.core.cdi.CdiResources; import org.eclipse.cdt.debug.mi.core.cdi.MI2CDIException; import org.eclipse.cdt.debug.mi.core.cdi.RegisterManager; import org.eclipse.cdt.debug.mi.core.cdi.Session; import org.eclipse.cdt.debug.mi.core.cdi.VariableManager; import org.eclipse.cdt.debug.mi.core.command.CommandFactory; import org.eclipse.cdt.debug.mi.core.command.MIStackInfoDepth; import org.eclipse.cdt.debug.mi.core.command.MIStackListFrames; import org.eclipse.cdt.debug.mi.core.command.MIStackSelectFrame; import org.eclipse.cdt.debug.mi.core.output.MIFrame; import org.eclipse.cdt.debug.mi.core.output.MIInfo; import org.eclipse.cdt.debug.mi.core.output.MIStackInfoDepthInfo; import org.eclipse.cdt.debug.mi.core.output.MIStackListFramesInfo; /** */ public class Thread extends CObject implements ICDIThread, ICDIExecuteMoveInstructionPointer { static ICDIStackFrame[] noStack = new ICDIStackFrame[0]; int id; String name; StackFrame currentFrame; List currentFrames; int stackdepth = 0; final public static int STACKFRAME_DEFAULT_DEPTH = 200; public Thread(Target target, int threadId) { this(target, threadId, null); } public Thread(Target target, int threadId, String threadName) { super(target); id = threadId; name = threadName; } public int getId() { return id; } public void clearState() { stackdepth = 0; currentFrame = null; currentFrames = null; } @Override public String toString() { String str = Integer.toString(id); if (name != null) { str += " " + name; //$NON-NLS-1$ } return str; } public void updateState() { try { getCurrentStackFrame(); } catch (CDIException e) { } } public StackFrame getCurrentStackFrame() throws CDIException { if (currentFrame == null) { ICDIStackFrame[] frames = getStackFrames(0, 0); if (frames.length > 0) { currentFrame = (StackFrame)frames[0]; } } return currentFrame; } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#getStackFrames() */ public ICDIStackFrame[] getStackFrames() throws CDIException { // get the frames depth int depth = getStackFrameCount(); // refresh if we have nothing or if we have just a subset get everything. if (currentFrames == null || currentFrames.size() < depth) { currentFrames = new ArrayList(); Target target = (Target)getTarget(); ICDIThread currentThread = target.getCurrentThread(); synchronized (target.getLock()) { try { target.setCurrentThread(this, false); MISession mi = target.getMISession(); CommandFactory factory = mi.getCommandFactory(); MIStackListFrames frames = factory.createMIStackListFrames(); mi.postCommand(frames); MIStackListFramesInfo info = frames.getMIStackListFramesInfo(); if (info == null) { throw new CDIException(CdiResources.getString("cdi.Common.No_answer")); //$NON-NLS-1$ } MIFrame[] miFrames = info.getMIFrames(); for (int i = 0; i < miFrames.length; i++) { currentFrames.add(new StackFrame(this, miFrames[i], depth - miFrames[i].getLevel())); } } catch (MIException e) { //throw new CDIException(e.getMessage()); //System.out.println(e); } catch (CDIException e) { //throw e; //System.out.println(e); } finally { target.setCurrentThread(currentThread, false); } } // assign the currentFrame if it was not done yet. if (currentFrame == null) { for (int i = 0; i < currentFrames.size(); i++) { ICDIStackFrame stack = (ICDIStackFrame) currentFrames.get(i); if (stack.getLevel() == depth) { currentFrame = (StackFrame)stack; } } } } return (ICDIStackFrame[]) currentFrames.toArray(noStack); } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#getStackFrames() */ public int getStackFrameCount() throws CDIException { if (stackdepth == 0) { Target target = (Target)getTarget(); ICDIThread currentThread = target.getCurrentThread(); synchronized (target.getLock()) { try { target.setCurrentThread(this, false); MISession mi = target.getMISession(); CommandFactory factory = mi.getCommandFactory(); MIStackInfoDepth depth = factory.createMIStackInfoDepth(); mi.postCommand(depth); MIStackInfoDepthInfo info = null; try { // Catch the first exception gdb can recover the second time. info = depth.getMIStackInfoDepthInfo(); if (info == null) { throw new CDIException(CdiResources.getString("cdi.Common.No_answer")); //$NON-NLS-1$ } stackdepth = info.getDepth(); } catch (MIException e) { // First try fails, retry. gdb patches up the corrupt frame // so retry should give us a frame count that is safe. depth = factory.createMIStackInfoDepth(); mi.postCommand(depth); info = depth.getMIStackInfoDepthInfo(); if (info == null) { throw new CDIException(CdiResources.getString("cdi.Common.No_answer")); //$NON-NLS-1$ } stackdepth = info.getDepth(); if (stackdepth > 0) { stackdepth--; } } } catch (MIException e) { /* GDB has a bug where it fails to evaluate the stack depth, this must, ultimately * be fixed in GDB. GNAT nr 2395 * * http://sourceware.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gdb&pr=2395 */ // Bug#86676 fix: // // 1 is safe stackdepth = 1; } finally { target.setCurrentThread(currentThread, false); } } } return stackdepth; } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#getStackFrames() */ public ICDIStackFrame[] getStackFrames(int low, int high) throws CDIException { if (currentFrames == null || currentFrames.size() < high) { currentFrames = new ArrayList(); Target target = (Target) getTarget(); ICDIThread currentThread = target.getCurrentThread(); synchronized (target.getLock()) { try { target.setCurrentThread(this, false); int depth = getStackFrameCount(); int upperBound; // try to get the largest subset. // if what the user asks is smaller then the depth // try to cache things by getting the min(depth,STACKFRAME_DEFAULT_DEPTH) // else give fetch the entire thing. if (high < depth) { upperBound = Math.min(depth, STACKFRAME_DEFAULT_DEPTH); } else { upperBound = depth; } MISession mi = target.getMISession(); CommandFactory factory = mi.getCommandFactory(); MIStackListFrames frames = factory.createMIStackListFrames(0, upperBound); mi.postCommand(frames); MIStackListFramesInfo info = frames.getMIStackListFramesInfo(); if (info == null) { throw new CDIException(CdiResources.getString("cdi.Common.No_answer")); //$NON-NLS-1$ } MIFrame[] miFrames = info.getMIFrames(); for (int i = 0; i < miFrames.length; i++) { currentFrames.add(new StackFrame(this, miFrames[i], depth - miFrames[i].getLevel())); } } catch (MIException e) { //throw new CDIException(e.getMessage()); //System.out.println(e); } catch (CDIException e) { //throw e; //System.out.println(e); } finally { target.setCurrentThread(currentThread, false); } } // take time to assign the currentFrame, if it is in the set if (currentFrame == null) { for (int i = 0; i < currentFrames.size(); i++) { StackFrame f = (StackFrame) currentFrames.get(i); if (f.getMIFrame().getLevel() == 0) { currentFrame =f; } } } } List list = ((high - low + 1) <= currentFrames.size()) ? currentFrames.subList(low, high + 1) : currentFrames; return (ICDIStackFrame[])list.toArray(noStack); } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#setCurrentStackFrame(ICDIStackFrame, boolean) */ public void setCurrentStackFrame(StackFrame stackframe, boolean doUpdate) throws CDIException { // Assert we should assert that the stackframe is one of our frames. int frameLevel = 0; if (stackframe != null) { frameLevel = stackframe.getLevel(); } // Check to see if we are already at this level if (currentFrame != null && currentFrame.getLevel() == frameLevel) { if (stackframe != null) { Thread aThread = (Thread)stackframe.getThread(); if (aThread != null && aThread.getId() == getId()) { // noop return; } } } Target target = (Target)getTarget(); MISession mi = target.getMISession(); CommandFactory factory = mi.getCommandFactory(); // Need the GDB/MI view of level which is the reverse, i.e. the highest level is 0 // See comment in StackFrame constructor. int miLevel = getStackFrameCount() - frameLevel; MIStackSelectFrame frame = factory.createMIStackSelectFrame(miLevel); // Set ourself as the current thread first. synchronized (target.getLock()) { try { target.setCurrentThread(this, doUpdate); mi.postCommand(frame); MIInfo info = frame.getMIInfo(); if (info == null) { throw new CDIException(CdiResources.getString("cdi.Common.No_answer")); //$NON-NLS-1$ } currentFrame = stackframe; // Resetting stackframe may change the value of // some variables like registers. Call an update() // To generate changeEvents. if (doUpdate) { Session session = (Session) target.getSession(); RegisterManager regMgr = session.getRegisterManager(); if (regMgr.isAutoUpdate()) { regMgr.update(target); } VariableManager varMgr = session.getVariableManager(); if (varMgr.isAutoUpdate()) { varMgr.update(target); } } } catch (MIException e) { throw new MI2CDIException(e); } } } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#stepInto() */ public void stepInto() throws CDIException { stepInto(1); } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.cdi.model.ICDIExecuteStep#stepInto(int) */ public void stepInto(int count) throws CDIException { Target target = (Target)getTarget(); synchronized(target.getLock()) { target.setCurrentThread(this); target.stepInto(count); } } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#stepIntoInstruction() */ public void stepIntoInstruction() throws CDIException { stepIntoInstruction(1); } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.cdi.model.ICDIExecuteStep#stepIntoInstruction(int) */ public void stepIntoInstruction(int count) throws CDIException { Target target = (Target)getTarget(); synchronized(target.getLock()) { target.setCurrentThread(this); target.stepIntoInstruction(count); } } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#stepOver() */ public void stepOver() throws CDIException { stepOver(1); } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.cdi.model.ICDIExecuteStep#stepOver(int) */ public void stepOver(int count) throws CDIException { Target target = (Target)getTarget(); synchronized(target.getLock()) { target.setCurrentThread(this); target.stepOver(count); } } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#stepOverInstruction() */ public void stepOverInstruction() throws CDIException { stepOverInstruction(1); } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.cdi.model.ICDIExecuteStep#stepOverInstruction(int) */ public void stepOverInstruction(int count) throws CDIException { Target target = (Target)getTarget(); synchronized(target.getLock()) { target.setCurrentThread(this); target.stepOverInstruction(count); } } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#stepReturn() */ public void stepReturn() throws CDIException { getCurrentStackFrame().stepReturn(); } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#runUntil(ICDILocation) */ public void runUntil(ICDILocation location) throws CDIException { stepUntil(location); } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.cdi.model.ICDIExecuteStep#stepUntil(org.eclipse.cdt.debug.core.cdi.ICDILocation) */ public void stepUntil(ICDILocation location) throws CDIException { Target target = (Target)getTarget(); synchronized(target.getLock()) { target.setCurrentThread(this); target.stepUntil(location); } } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#isSuspended() */ public boolean isSuspended() { return getTarget().isSuspended(); } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#suspend() */ public void suspend() throws CDIException { getTarget().suspend(); } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#resume() */ public void resume() throws CDIException { resume(false); } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.cdi.model.ICDIExecuteResume#resume(boolean) */ public void resume(boolean passSignal) throws CDIException { Target target = (Target)getTarget(); synchronized(target.getLock()) { target.setCurrentThread(this); target.resume(passSignal); } } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.cdi.model.ICDIExecuteResume#resume(org.eclipse.cdt.debug.core.cdi.ICDILocation) */ public void resume(ICDILocation location) throws CDIException { Target target = (Target)getTarget(); synchronized(target.getLock()) { target.setCurrentThread(this); target.resume(location); } } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.cdi.model.ICDIExecuteResume#resume(org.eclipse.cdt.debug.core.cdi.model.ICDISignal) */ public void resume(ICDISignal signal) throws CDIException { Target target = (Target)getTarget(); synchronized(target.getLock()) { target.setCurrentThread(this); target.resume(signal); } } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#jump(org.eclipse.cdt.debug.core.cdi.ICDILocation) */ public void jump(ICDILocation location) throws CDIException { resume(location); } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#signal() */ public void signal() throws CDIException { resume(false); } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#signal(org.eclipse.cdt.debug.core.cdi.model.ICDISignal) */ public void signal(ICDISignal signal) throws CDIException { resume(signal); } /** * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#equals(ICDIThread) */ public boolean equals(ICDIThread thread) { if (thread instanceof Thread) { Thread cthread = (Thread) thread; return id == cthread.getId(); } return super.equals(thread); } public ICDIBreakpoint[] getBreakpoints() throws CDIException { Target target = (Target)getTarget(); ICDIBreakpoint[] bps = target.getBreakpoints(); ArrayList list = new ArrayList(bps.length); for (int i = 0; i < bps.length; i++) { ICDICondition condition = bps[i].getCondition(); if (condition == null) { continue; } String[] threadIds = condition.getThreadIds(); for (int j = 0; j < threadIds.length; j++) { int tid = 0; try { tid = Integer.parseInt(threadIds[j]); } catch (NumberFormatException e) { // } if (tid == getId()) { list.add(bps[i]); } } } return (ICDIBreakpoint[]) list.toArray(new ICDIBreakpoint[list.size()]); } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#getThreadStorageDescriptors() */ public ICDIThreadStorageDescriptor[] getThreadStorageDescriptors() throws CDIException { Session session = (Session)getTarget().getSession(); VariableManager varMgr = session.getVariableManager(); return varMgr.getThreadStorageDescriptors(this); } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.cdi.model.ICDIThread#createThreadStorage(org.eclipse.cdt.debug.core.cdi.model.ICDIThreadStorageDescriptor) */ public ICDIThreadStorage createThreadStorage(ICDIThreadStorageDescriptor varDesc) throws CDIException { if (varDesc instanceof ThreadStorageDescriptor) { Session session = (Session)getTarget().getSession(); VariableManager varMgr = session.getVariableManager(); return varMgr.createThreadStorage((ThreadStorageDescriptor)varDesc); } return null; } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.cdi.model.ICDIExecuteMoveInstructionPointer#moveInstructionPointer(org.eclipse.cdt.debug.core.cdi.ICDILocation) */ /** * @since 6.0 */ public void moveInstructionPointer(ICDILocation location) throws CDIException { Target target = (Target)getTarget(); synchronized(target.getLock()) { target.setCurrentThread(this); target.moveInstructionPointer(location); } } }