blob: 2c51b5e9ca566b01bba4d47a19d8738bf615f374 [file] [log] [blame]
/*
*(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;
}
}