| /* |
| *(c) Copyright QNX Software Systems Ltd. 2002. |
| * All Rights Reserved. |
| * |
| */ |
| package org.eclipse.cdt.debug.mi.core.cdi; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import org.eclipse.cdt.debug.core.cdi.CDIException; |
| import org.eclipse.cdt.debug.core.cdi.ICDIVariableManager; |
| import org.eclipse.cdt.debug.core.cdi.model.ICDIArgument; |
| import org.eclipse.cdt.debug.core.cdi.model.ICDIArgumentObject; |
| import org.eclipse.cdt.debug.core.cdi.model.ICDIStackFrame; |
| import org.eclipse.cdt.debug.core.cdi.model.ICDITarget; |
| import org.eclipse.cdt.debug.core.cdi.model.ICDIThread; |
| import org.eclipse.cdt.debug.core.cdi.model.ICDIVariable; |
| import org.eclipse.cdt.debug.core.cdi.model.ICDIVariableObject; |
| import org.eclipse.cdt.debug.mi.core.MIException; |
| import org.eclipse.cdt.debug.mi.core.MISession; |
| import org.eclipse.cdt.debug.mi.core.cdi.model.Argument; |
| import org.eclipse.cdt.debug.mi.core.cdi.model.ArgumentObject; |
| import org.eclipse.cdt.debug.mi.core.cdi.model.Variable; |
| import org.eclipse.cdt.debug.mi.core.cdi.model.VariableObject; |
| import org.eclipse.cdt.debug.mi.core.command.CommandFactory; |
| import org.eclipse.cdt.debug.mi.core.command.MIPType; |
| import org.eclipse.cdt.debug.mi.core.command.MIStackListArguments; |
| import org.eclipse.cdt.debug.mi.core.command.MIStackListLocals; |
| import org.eclipse.cdt.debug.mi.core.command.MIVarCreate; |
| import org.eclipse.cdt.debug.mi.core.command.MIVarDelete; |
| import org.eclipse.cdt.debug.mi.core.command.MIVarUpdate; |
| import org.eclipse.cdt.debug.mi.core.event.MIEvent; |
| import org.eclipse.cdt.debug.mi.core.event.MIVarChangedEvent; |
| import org.eclipse.cdt.debug.mi.core.event.MIVarDeletedEvent; |
| import org.eclipse.cdt.debug.mi.core.output.MIArg; |
| import org.eclipse.cdt.debug.mi.core.output.MIFrame; |
| import org.eclipse.cdt.debug.mi.core.output.MIPTypeInfo; |
| import org.eclipse.cdt.debug.mi.core.output.MIStackListArgumentsInfo; |
| import org.eclipse.cdt.debug.mi.core.output.MIStackListLocalsInfo; |
| import org.eclipse.cdt.debug.mi.core.output.MIVar; |
| import org.eclipse.cdt.debug.mi.core.output.MIVarChange; |
| import org.eclipse.cdt.debug.mi.core.output.MIVarCreateInfo; |
| import org.eclipse.cdt.debug.mi.core.output.MIVarUpdateInfo; |
| |
| /** |
| */ |
| public class VariableManager extends SessionObject implements ICDIVariableManager { |
| |
| // We put a restriction on how deep we want to |
| // go when doing update of the variables. |
| // If the number is to high, gdb will just hang. |
| int MAX_STACK_DEPTH = 200; |
| List variableList; |
| boolean autoupdate; |
| MIVarChange[] noChanges = new MIVarChange[0]; |
| |
| public VariableManager(Session session) { |
| super(session); |
| variableList = Collections.synchronizedList(new ArrayList()); |
| autoupdate = true; |
| } |
| |
| /** |
| * Return the element that have the uniq varName. |
| * null is return if the element is not in the cache. |
| */ |
| public Variable getVariable(String varName) { |
| Variable[] vars = getVariables(); |
| for (int i = 0; i < vars.length; i++) { |
| if (vars[i].getMIVar().getVarName().equals(varName)) { |
| return vars[i]; |
| } |
| Variable v = vars[i].getChild(varName); |
| if (v != null) { |
| return v; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Return the Element with this stackframe, and with this name. |
| * null is return if the element is not in the cache. |
| */ |
| Variable findVariable(VariableObject v) throws CDIException { |
| ICDIStackFrame stack = v.getStackFrame(); |
| String name = v.getName(); |
| int position = v.getPosition(); |
| int depth = v.getStackDepth(); |
| Variable[] vars = getVariables(); |
| for (int i = 0; i < vars.length; i++) { |
| if (vars[i].getName().equals(name) |
| && vars[i].getCastingArrayStart() == v.getCastingArrayStart() |
| && vars[i].getCastingArrayEnd() == v.getCastingArrayEnd() |
| && ((vars[i].getCastingType() == null && v.getCastingType() == null) |
| || (vars[i].getCastingType() != null |
| && v.getCastingType() != null |
| && vars[i].getCastingType().equals(v.getCastingType())))) { |
| ICDIStackFrame frame = vars[i].getStackFrame(); |
| if (stack == null && frame == null) { |
| return vars[i]; |
| } else if (frame != null && stack != null && frame.equals(stack)) { |
| if (vars[i].getPosition() == position) { |
| if (vars[i].getStackDepth() == depth) { |
| return vars[i]; |
| } |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns all the elements that are in the cache. |
| */ |
| Variable[] getVariables() { |
| return (Variable[]) variableList.toArray(new Variable[0]); |
| } |
| |
| /** |
| * Check the type |
| */ |
| public void checkType(String type) throws CDIException { |
| if (type != null && type.length() > 0) { |
| try { |
| MISession mi = ((Session) getSession()).getMISession(); |
| CommandFactory factory = mi.getCommandFactory(); |
| MIPType ptype = factory.createMIPType(type); |
| mi.postCommand(ptype); |
| MIPTypeInfo info = ptype.getMIPtypeInfo(); |
| if (info == null) { |
| throw new CDIException("No answer"); |
| } |
| } catch (MIException e) { |
| throw new MI2CDIException(e); |
| } |
| } else { |
| throw new CDIException("Unknown type"); |
| } |
| } |
| |
| /** |
| * Tell gdb to remove the underlying var-object also. |
| */ |
| void removeMIVar(MIVar miVar) throws CDIException { |
| Session session = (Session) getSession(); |
| MISession mi = session.getMISession(); |
| CommandFactory factory = mi.getCommandFactory(); |
| MIVarDelete var = factory.createMIVarDelete(miVar.getVarName()); |
| try { |
| mi.postCommand(var); |
| var.getMIInfo(); |
| } catch (MIException e) { |
| throw new MI2CDIException(e); |
| } |
| } |
| |
| /** |
| * When element are remove from the cache, they are put on the OutOfScope list, oos, |
| * because they are still needed for the destroy events. The destroy event will |
| * call removeOutOfScope. |
| */ |
| public void removeVariable(String varName) throws CDIException { |
| Variable[] vars = getVariables(); |
| for (int i = 0; i < vars.length; i++) { |
| if (vars[i].getMIVar().getVarName().equals(varName)) { |
| variableList.remove(vars[i]); |
| removeMIVar(vars[i].getMIVar()); |
| } |
| } |
| } |
| |
| /** |
| * @see org.eclipse.cdt.debug.core.cdi.ICDIVariableManager#createArgument(ICDIArgumentObject) |
| */ |
| public ICDIArgument createArgument(ICDIArgumentObject a) throws CDIException { |
| ArgumentObject argObj = null; |
| if (a instanceof ArgumentObject) { |
| argObj = (ArgumentObject) a; |
| } |
| if (argObj != null) { |
| Variable variable = findVariable(argObj); |
| Argument argument = null; |
| if (variable != null && variable instanceof Argument) { |
| argument = (Argument) variable; |
| } |
| if (argument == null) { |
| String name = argObj.getQualifiedName(); |
| ICDIStackFrame stack = argObj.getStackFrame(); |
| Session session = (Session) getSession(); |
| ICDIThread currentThread = null; |
| ICDIStackFrame currentFrame = null; |
| if (stack != null) { |
| ICDITarget currentTarget = session.getCurrentTarget(); |
| currentThread = currentTarget.getCurrentThread(); |
| currentFrame = currentThread.getCurrentStackFrame(); |
| stack.getThread().setCurrentStackFrame(stack, false); |
| } |
| try { |
| MISession mi = session.getMISession(); |
| CommandFactory factory = mi.getCommandFactory(); |
| MIVarCreate var = factory.createMIVarCreate(name); |
| mi.postCommand(var); |
| MIVarCreateInfo info = var.getMIVarCreateInfo(); |
| if (info == null) { |
| throw new CDIException("No answer"); |
| } |
| argument = new Argument(argObj, info.getMIVar()); |
| variableList.add(argument); |
| } catch (MIException e) { |
| throw new MI2CDIException(e); |
| } finally { |
| if (currentThread != null) { |
| currentThread.setCurrentStackFrame(currentFrame, false); |
| } |
| } |
| } |
| return argument; |
| } |
| throw new CDIException("Wrong variable type"); |
| } |
| |
| /** |
| * @see org.eclipse.cdt.debug.core.cdi.ICDIVariableManager#getArgumentObjects(ICDIStackFrame) |
| */ |
| public ICDIArgumentObject[] getArgumentObjects(ICDIStackFrame frame) throws CDIException { |
| List argObjects = new ArrayList(); |
| Session session = (Session) getSession(); |
| ICDITarget currentTarget = session.getCurrentTarget(); |
| ICDIThread currentThread = currentTarget.getCurrentThread(); |
| ICDIStackFrame currentFrame = currentThread.getCurrentStackFrame(); |
| frame.getThread().setCurrentStackFrame(frame, false); |
| try { |
| MISession mi = session.getMISession(); |
| CommandFactory factory = mi.getCommandFactory(); |
| int depth = frame.getThread().getStackFrameCount(); |
| int level = frame.getLevel(); |
| // Need the GDB/MI view of leve which the reverse i.e. Highest frame is 0 |
| int miLevel = depth - level; |
| MIStackListArguments listArgs = factory.createMIStackListArguments(false, miLevel, miLevel); |
| MIArg[] args = null; |
| mi.postCommand(listArgs); |
| MIStackListArgumentsInfo info = listArgs.getMIStackListArgumentsInfo(); |
| if (info == null) { |
| throw new CDIException("No answer"); |
| } |
| MIFrame[] miFrames = info.getMIFrames(); |
| if (miFrames != null && miFrames.length == 1) { |
| args = miFrames[0].getArgs(); |
| } |
| if (args != null) { |
| ICDITarget tgt = frame.getThread().getTarget(); |
| for (int i = 0; i < args.length; i++) { |
| ArgumentObject arg = new ArgumentObject(tgt, args[i].getName(), frame, args.length - i, level); |
| argObjects.add(arg); |
| } |
| } |
| } catch (MIException e) { |
| throw new MI2CDIException(e); |
| } finally { |
| currentThread.setCurrentStackFrame(currentFrame); |
| } |
| return (ICDIArgumentObject[]) argObjects.toArray(new ICDIArgumentObject[0]); |
| } |
| |
| /** |
| * @see org.eclipse.cdt.debug.core.cdi.ICDIVariableManager#getGlobalVariableObject(String, String, String) |
| */ |
| public ICDIVariableObject getGlobalVariableObject(String filename, String function, String name) throws CDIException { |
| if (filename == null) { |
| filename = new String(); |
| } |
| if (function == null) { |
| function = new String(); |
| } |
| if (name == null) { |
| name = new String(); |
| } |
| StringBuffer buffer = new StringBuffer(); |
| if (filename.length() > 0) { |
| buffer.append('\'').append(filename).append('\'').append("::"); |
| } |
| if (function.length() > 0) { |
| buffer.append(function).append("::"); |
| } |
| buffer.append(name); |
| ICDITarget target = getSession().getCurrentTarget(); |
| return new VariableObject(target, buffer.toString(), null, 0, 0); |
| } |
| |
| /** |
| * @see org.eclipse.cdt.debug.core.cdi.ICDIVariableManager#getVariableObjectAsArray(ICDIVariableObject, int, int) |
| */ |
| public ICDIVariableObject getVariableObjectAsArray(ICDIVariableObject object, int start, int length) |
| throws CDIException { |
| VariableObject obj = null; |
| if (object instanceof VariableObject) { |
| obj = (VariableObject) object; |
| } |
| if (obj != null) { |
| VariableObject vo = |
| new VariableObject( |
| obj.getTarget(), |
| obj.getName(), |
| obj.getFullName(), |
| obj.getStackFrame(), |
| obj.getPosition(), |
| obj.getStackDepth()); |
| vo.setCastingArrayStart(obj.getCastingArrayStart() + start); |
| vo.setCastingArrayEnd(length); |
| return vo; |
| } |
| throw new CDIException("Unknown variable object"); |
| } |
| |
| /** |
| * @see org.eclipse.cdt.debug.core.cdi.ICDIVariableManager#getVariableObjectAsArray(ICDIVariableObject, String, int, int) |
| */ |
| public ICDIVariableObject getVariableObjectAsType(ICDIVariableObject object, String type) throws CDIException { |
| VariableObject obj = null; |
| if (object instanceof VariableObject) { |
| obj = (VariableObject) object; |
| } |
| if (obj != null) { |
| // throw an exception if not a good type. |
| checkType(type); |
| VariableObject vo = |
| new VariableObject( |
| obj.getTarget(), |
| obj.getName(), |
| obj.getFullName(), |
| obj.getStackFrame(), |
| obj.getPosition(), |
| obj.getStackDepth()); |
| String casting = obj.getCastingType(); |
| if (casting != null && casting.length() > 0) { |
| type = "(" + type + ")" + "(" + casting + " )"; |
| } |
| vo.setCastingType(type); |
| return vo; |
| } |
| throw new CDIException("Unknown variable object"); |
| } |
| |
| /** |
| * @see org.eclipse.cdt.debug.core.cdi.ICDIVariableManager#getVariableObjects(ICDIStackFrame) |
| */ |
| public ICDIVariableObject[] getLocalVariableObjects(ICDIStackFrame frame) throws CDIException { |
| List varObjects = new ArrayList(); |
| Session session = (Session) getSession(); |
| ICDITarget currentTarget = session.getCurrentTarget(); |
| ICDIThread currentThread = currentTarget.getCurrentThread(); |
| ICDIStackFrame currentFrame = currentThread.getCurrentStackFrame(); |
| frame.getThread().setCurrentStackFrame(frame, false); |
| try { |
| MISession mi = session.getMISession(); |
| CommandFactory factory = mi.getCommandFactory(); |
| int level = frame.getLevel(); |
| MIArg[] args = null; |
| MIStackListLocals locals = factory.createMIStackListLocals(false); |
| mi.postCommand(locals); |
| MIStackListLocalsInfo info = locals.getMIStackListLocalsInfo(); |
| if (info == null) { |
| throw new CDIException("No answer"); |
| } |
| args = info.getLocals(); |
| if (args != null) { |
| ICDITarget tgt = frame.getThread().getTarget(); |
| for (int i = 0; i < args.length; i++) { |
| VariableObject varObj = new VariableObject(tgt, args[i].getName(), frame, args.length - i, level); |
| varObjects.add(varObj); |
| } |
| } |
| } catch (MIException e) { |
| throw new MI2CDIException(e); |
| } finally { |
| currentThread.setCurrentStackFrame(currentFrame, false); |
| } |
| return (ICDIVariableObject[]) varObjects.toArray(new ICDIVariableObject[0]); |
| } |
| |
| /** |
| * @see org.eclipse.cdt.debug.core.cdi.ICDIVariableManager#getVariableObjects(ICDIStackFrame) |
| */ |
| public ICDIVariableObject[] getVariableObjects(ICDIStackFrame frame) throws CDIException { |
| ICDIVariableObject[] locals = getLocalVariableObjects(frame); |
| ICDIVariableObject[] args = getArgumentObjects(frame); |
| ICDIVariableObject[] vars = new ICDIVariableObject[locals.length + args.length]; |
| System.arraycopy(locals, 0, vars, 0, locals.length); |
| System.arraycopy(args, 0, vars, locals.length, args.length); |
| return vars; |
| } |
| |
| /** |
| * @see org.eclipse.cdt.debug.core.cdi.ICDIVariableManager#createVariable(ICDIVariableObject) |
| */ |
| public ICDIVariable createVariable(ICDIVariableObject v) throws CDIException { |
| VariableObject varObj = null; |
| if (v instanceof VariableObject) { |
| varObj = (VariableObject) v; |
| } |
| if (varObj != null) { |
| Variable variable = findVariable(varObj); |
| if (variable == null) { |
| String name = varObj.getQualifiedName(); |
| Session session = (Session) getSession(); |
| ICDIStackFrame stack = varObj.getStackFrame(); |
| ICDIThread currentThread = null; |
| ICDIStackFrame currentFrame = null; |
| if (stack != null) { |
| ICDITarget currentTarget = session.getCurrentTarget(); |
| currentThread = currentTarget.getCurrentThread(); |
| currentFrame = currentThread.getCurrentStackFrame(); |
| stack.getThread().setCurrentStackFrame(stack, false); |
| } |
| try { |
| MISession mi = session.getMISession(); |
| CommandFactory factory = mi.getCommandFactory(); |
| MIVarCreate var = factory.createMIVarCreate(name); |
| mi.postCommand(var); |
| MIVarCreateInfo info = var.getMIVarCreateInfo(); |
| if (info == null) { |
| throw new CDIException("No answer"); |
| } |
| variable = new Variable(varObj, info.getMIVar()); |
| variableList.add(variable); |
| } catch (MIException e) { |
| throw new MI2CDIException(e); |
| } finally { |
| if (currentThread != null) { |
| currentThread.setCurrentStackFrame(currentFrame, false); |
| } |
| } |
| } |
| return variable; |
| } |
| throw new CDIException("Wrong variable type"); |
| } |
| |
| /** |
| * @see org.eclipse.cdt.debug.core.cdi.ICDIVariableManager#destroyVariable(ICDIVariable) |
| */ |
| public void destroyVariable(ICDIVariable var) throws CDIException { |
| if (var instanceof Variable) { |
| // Fire a destroyEvent ? |
| Variable variable = (Variable) var; |
| MIVarDeletedEvent del = new MIVarDeletedEvent(variable.getMIVar().getVarName()); |
| Session session = (Session) getSession(); |
| MISession mi = session.getMISession(); |
| mi.fireEvent(del); |
| } |
| } |
| |
| /** |
| * @see org.eclipse.cdt.debug.core.cdi.ICDIVariableManager#isAutoUpdate() |
| */ |
| public boolean isAutoUpdate() { |
| return autoupdate; |
| } |
| |
| /** |
| * @see org.eclipse.cdt.debug.core.cdi.ICDIVariableManager#setAutoUpdate(boolean) |
| */ |
| public void setAutoUpdate(boolean update) { |
| autoupdate = update; |
| } |
| |
| /** |
| * Update the elements in the cache, from the response of the "-var-update" |
| * mi/command. Althought tempting we do not use the "-var-update *" command, since |
| * for some reason on gdb-5.2.1 it starts to misbehave until it hangs ... sigh |
| * We take the approach of updating the variables ourselfs. But we do it a smart |
| * way by only updating the variables visible in the current stackframe but not |
| * the other locals in different frames. The downside if any side effects we loose, |
| * This ok, since the IDE only a frame at a time. |
| * |
| * @see org.eclipse.cdt.debug.core.cdi.ICDIVariableManager#createArgument(ICDIArgumentObject) |
| */ |
| public void update() throws CDIException { |
| int high = 0; |
| int low = 0; |
| List eventList = new ArrayList(); |
| Session session = (Session) getSession(); |
| MISession mi = session.getMISession(); |
| CommandFactory factory = mi.getCommandFactory(); |
| Variable[] vars = getVariables(); |
| ICDITarget currentTarget = session.getCurrentTarget(); |
| ICDIStackFrame[] frames = null; |
| ICDIStackFrame currentStack = null; |
| if (currentTarget != null) { |
| ICDIThread currentThread = currentTarget.getCurrentThread(); |
| if (currentThread != null) { |
| currentStack = currentThread.getCurrentStackFrame(); |
| if (currentStack != null) { |
| high = currentStack.getLevel(); |
| } |
| if (high > 0) { |
| high--; |
| } |
| low = high - MAX_STACK_DEPTH; |
| if (low < 0) { |
| low = 0; |
| } |
| frames = currentThread.getStackFrames(low, high); |
| } |
| } |
| for (int i = 0; i < vars.length; i++) { |
| Variable variable = vars[i]; |
| if (isVariableNeedsToBeUpdate(variable, currentStack, frames, low)) { |
| String varName = variable.getMIVar().getVarName(); |
| MIVarChange[] changes = noChanges; |
| MIVarUpdate update = factory.createMIVarUpdate(varName); |
| try { |
| mi.postCommand(update); |
| MIVarUpdateInfo info = update.getMIVarUpdateInfo(); |
| if (info == null) { |
| throw new CDIException("No answer"); |
| } |
| changes = info.getMIVarChanges(); |
| } catch (MIException e) { |
| //throw new MI2CDIException(e); |
| eventList.add(new MIVarDeletedEvent(varName)); |
| } |
| for (int j = 0; j < changes.length; j++) { |
| String n = changes[j].getVarName(); |
| if (changes[j].isInScope()) { |
| eventList.add(new MIVarChangedEvent(n)); |
| } else { |
| eventList.add(new MIVarDeletedEvent(n)); |
| } |
| } |
| } |
| } |
| MIEvent[] events = (MIEvent[]) eventList.toArray(new MIEvent[0]); |
| mi.fireEvents(events); |
| } |
| |
| /** |
| * We are trying to minimize the impact of the updates, this can be very long and unncessary if we |
| * have a very deep stack and lots of local variables. We can assume here that the local variables |
| * in the other non-selected stackframe will not change and only update the selected frame variables. |
| * |
| * @param variable |
| * @param current |
| * @param frames |
| * @return |
| */ |
| boolean isVariableNeedsToBeUpdate(Variable variable, ICDIStackFrame current, ICDIStackFrame[] frames, int low) |
| throws CDIException { |
| ICDIStackFrame varStack = variable.getStackFrame(); |
| boolean inScope = false; |
| |
| // Something wrong and the program terminated bail out here. |
| if (current == null || frames == null) { |
| return false; |
| } |
| |
| // If the variable Stack is null, it means this is a global variable we should update |
| if (varStack == null) { |
| return true; |
| } else if (varStack.equals(current)) { |
| // The variable is in the current selected frame it should be updated |
| return true; |
| } else { |
| if (varStack.getLevel() >= low) { |
| // Check if the Variable is still in Scope |
| // if it is no longer in scope so update() call call "-var-delete". |
| for (int i = 0; i < frames.length; i++) { |
| if (varStack.equals(frames[i])) { |
| inScope = true; |
| } |
| } |
| } else { |
| inScope = true; |
| } |
| } |
| // return true if the variable is no longer in scope we |
| // need to call -var-delete. |
| return !inScope; |
| } |
| } |