|
Added
Link Here
|
| 1 |
/******************************************************************************* |
| 2 |
* Copyright (c) 2010 Monta Vista and others. |
| 3 |
* All rights reserved. This program and the accompanying materials |
| 4 |
* are made available under the terms of the Eclipse Public License v1.0 |
| 5 |
* which accompanies this distribution, and is available at |
| 6 |
* http://www.eclipse.org/legal/epl-v10.html |
| 7 |
* |
| 8 |
* Contributors: |
| 9 |
* Monta Vista - initial API and implementation |
| 10 |
* Ericsson - Modified for handling of multiple execution contexts |
| 11 |
* Ericsson - Major updates for GDB/MI implementation |
| 12 |
* Ericsson - Major re-factoring to deal with children |
| 13 |
* Ericsson - Created the MacOS version |
| 14 |
*******************************************************************************/ |
| 15 |
package org.eclipse.cdt.dsf.gdb.service.macos; |
| 16 |
|
| 17 |
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; |
| 18 |
import org.eclipse.cdt.dsf.mi.service.MIVariableManager; |
| 19 |
import org.eclipse.cdt.dsf.mi.service.command.commands.macos.MacOSMIVarUpdate; |
| 20 |
import org.eclipse.cdt.dsf.mi.service.command.output.MIVarChange; |
| 21 |
import org.eclipse.cdt.dsf.mi.service.command.output.macos.MacOSMIVarUpdateInfo; |
| 22 |
import org.eclipse.cdt.dsf.service.DsfServicesTracker; |
| 23 |
import org.eclipse.cdt.dsf.service.DsfSession; |
| 24 |
|
| 25 |
/** |
| 26 |
* Manages a list of variable objects as created through GDB/MI commands. |
| 27 |
* |
| 28 |
* This class is passed expression-meta-commands which have their own cache. |
| 29 |
* Therefore, we don't use the standard MICommandCache in this class. |
| 30 |
* In fact, we can't even use it, because many variableObject MI commands, |
| 31 |
* should not be cached as they alter the state of the back-end. |
| 32 |
* |
| 33 |
* Design details: |
| 34 |
* ============== |
| 35 |
* |
| 36 |
* GDB variable object information |
| 37 |
* ------------------------------- |
| 38 |
* o Variable objects are recursively hierarchical, where children can be created through |
| 39 |
* the parent. |
| 40 |
* o A varObject created with -var-create is a ROOT |
| 41 |
* o A varObject created with -var-list-children, is not a root |
| 42 |
* o Only varObject with no children or varObjects that are pointers can change values |
| 43 |
* and therefore |
| 44 |
* those objects can be used with -var-assign |
| 45 |
* o After a program stops, a varObject must be 'updated' before used |
| 46 |
* o Only root varObject can be updated with -var-update, which will trigger all |
| 47 |
* of the root's descendants to be updated. |
| 48 |
* o Once updated, a varObject need not be updated until the program resumes again; |
| 49 |
* this is true even after -var-assign is used (because it does an implicit -var-update) |
| 50 |
* o -var-update will return the list of all modifiable descendants of the udpated root which |
| 51 |
* have changed |
| 52 |
* o -var-update will indicate if a root is out-of-scope (which implies that all |
| 53 |
* its descendants are out-of-scope) |
| 54 |
* o if a varObject is out-of-scope, another varObject may be valid for the same |
| 55 |
* expression as the out-of-scope varObject |
| 56 |
* o deleting a varObject will delete all its descendants, therefore, it is only |
| 57 |
* necessary to delete roots |
| 58 |
* |
| 59 |
* |
| 60 |
* Class details |
| 61 |
* ------------- |
| 62 |
* - We have an MIVariableObject class which represents a variable object in GDB |
| 63 |
* |
| 64 |
* - MIVariableObject includes a buffered value for each allowed format. |
| 65 |
* |
| 66 |
* - We have an MIRootVariableObject class inheriting from MIVariableObject to describe |
| 67 |
* root varObjects created with -var-create. Objects created with -var-list-children |
| 68 |
* are MIVariableObjects only. The root class will keep track of if the root object |
| 69 |
* needs to be updated, if the root object is out-of-scope, and of a list of all |
| 70 |
* modifiable descendants of this root. The list of modifiable descendants is |
| 71 |
* accessed using the gdb-given name to allow quick updates from the -var-update |
| 72 |
* result (see below.) |
| 73 |
* |
| 74 |
* - we do not use -var-list-children for arrays, but create them manually |
| 75 |
* |
| 76 |
* - when the program stops, we should mark all roots as needing to be updated. |
| 77 |
* To achieve this efficiently, we have a dedicated list of roots that are updated. |
| 78 |
* When the program stops, we go through this list, remove each element and mark it |
| 79 |
* as needing to be updated. |
| 80 |
* |
| 81 |
* - when a varObject is accessed, if its root must be updated, the var-update |
| 82 |
* command shall be used. The result of that command will indicate all |
| 83 |
* modifiable descendants that have changed. We also use --all-values with -var-update |
| 84 |
* to get the new value (in the current format) for each modified descendant. Using the list of modifiable |
| 85 |
* descendants of the root, we can quickly update the changed ones to invalidate their buffered |
| 86 |
* values and store the new current format value. |
| 87 |
* |
| 88 |
* - all values of non-modifiable varObjects (except arrays) will be set to {...} |
| 89 |
* without going to the back-end |
| 90 |
* |
| 91 |
* - requesting the value of an array varObject will trigger the creation of a new |
| 92 |
* varObject for the array's address. Note that we must still use a variable |
| 93 |
* object and not the command -data-evaluate-expression, because we still need to get |
| 94 |
* the array address in multiple formats. |
| 95 |
* |
| 96 |
* - we keep an LRU (Least Recently Used) structure of all variable objects. This LRU |
| 97 |
* will be bounded to a maximum allowed number of variable objects. Whenever we get an |
| 98 |
* object from the LRU cleanup will be done if the maximum size has been reached. |
| 99 |
* The LRU will not delete a parent varObject until all its children are deleted; this is |
| 100 |
* achieved by touching each of the parents of an object whenever that object is put or get |
| 101 |
* |
| 102 |
* - It may happen that when accessing a varObject we find its root to be |
| 103 |
* out-of-scope. The expression for which we are trying to access a varObject |
| 104 |
* could still be valid, and therefore we should try to create a new varObject for |
| 105 |
* that expression. This can happen for example if two methods use the same name |
| 106 |
* for a variable. In the case when we find that a varObject is out-of-scope (when |
| 107 |
* its root is out-of-scope) the following should be done: |
| 108 |
* - replace the varObject in the LRU with a newly created one in GDB |
| 109 |
* - if the old object was a root, delete it in GDB. |
| 110 |
* |
| 111 |
* - In GDB, -var-update will only report a change if -var-evaluate-expression has |
| 112 |
* changed -- in the current format--. This means that situations like |
| 113 |
* double z = 1.2; |
| 114 |
* z = 1.4; |
| 115 |
* Will not report a change if the format is anything else than natural. |
| 116 |
* This is because 1.2 and 1.4 are both printed as 1, 0x1, etc |
| 117 |
* Since we cache the values of every format, we must know if the value has |
| 118 |
* change in -any- format, not just the current one. |
| 119 |
* To solve this, we always keep the display format of variable objects (and their |
| 120 |
* children) to the natural format; we believe that if the value changes in any |
| 121 |
* format, it guarantees that it will change in the natural format. |
| 122 |
* The simplest way to do this is that whenever we change the format |
| 123 |
* of a variable object, we immediately set it back to natural with a second |
| 124 |
* var-set-format command. |
| 125 |
* Note that versions of GDB after 6.7 will allows to issue -var-evaluate-expression |
| 126 |
* with a specified format, therefore allowing us to never use -var-set-format, and |
| 127 |
* consequently, to easily keep the display format of all variable objects to natural. |
| 128 |
* |
| 129 |
* @since 2.1 |
| 130 |
*/ |
| 131 |
public class MacOSGDBVariableManager extends MIVariableManager { |
| 132 |
|
| 133 |
public MacOSGDBVariableManager(DsfSession session, DsfServicesTracker tracker) { |
| 134 |
super(session, tracker); |
| 135 |
} |
| 136 |
|
| 137 |
private class MacOSGDBRootVariableObject extends MIRootVariableObject { |
| 138 |
|
| 139 |
public MacOSGDBRootVariableObject(VariableObjectId id) { |
| 140 |
super(id); |
| 141 |
} |
| 142 |
|
| 143 |
@Override |
| 144 |
public void update(final DataRequestMonitor<Boolean> rm) { |
| 145 |
|
| 146 |
if (isOutOfScope()) { |
| 147 |
rm.setData(false); |
| 148 |
rm.done(); |
| 149 |
} else if (currentState != STATE_READY) { |
| 150 |
// Object is not fully created or is being updated |
| 151 |
// so add RequestMonitor to pending queue |
| 152 |
updatesPending.add(rm); |
| 153 |
} else if (isOutOfDate() == false) { |
| 154 |
rm.setData(false); |
| 155 |
rm.done(); |
| 156 |
} else { |
| 157 |
// Object needs to be updated in the back-end |
| 158 |
currentState = STATE_UPDATING; |
| 159 |
|
| 160 |
// In GDB, var-update will only report a change if -var-evaluate-expression has |
| 161 |
// changed -- in the current format--. This means that situations like |
| 162 |
// double z = 1.2; |
| 163 |
// z = 1.4; |
| 164 |
// Will not report a change if the format is anything else than natural. |
| 165 |
// This is because 1.2 and 1.4 are both printed as 1, 0x1, etc |
| 166 |
// Since we cache the values of every format, we must know if -any- format has |
| 167 |
// changed, not just the current one. |
| 168 |
// To solve this, we always do an update in the natural format; I am not aware |
| 169 |
// of any case where the natural format would stay the same, but another format |
| 170 |
// would change. However, since a var-update update all children as well, |
| 171 |
// we must make sure these children are also in the natural format |
| 172 |
// The simplest way to do this is that whenever we change the format |
| 173 |
// of a variable object, we immediately set it back to natural with a second |
| 174 |
// var-set-format command. This is done in the getValue() method |
| 175 |
getCommandControl().queueCommand( |
| 176 |
new MacOSMIVarUpdate(getRootToUpdate().getControlDMContext(), getGdbName()), |
| 177 |
new DataRequestMonitor<MacOSMIVarUpdateInfo>(getSession().getExecutor(), rm) { |
| 178 |
@Override |
| 179 |
protected void handleCompleted() { |
| 180 |
currentState = STATE_READY; |
| 181 |
|
| 182 |
if (isSuccess()) { |
| 183 |
markAsOutOfDate(false); |
| 184 |
|
| 185 |
MIVarChange[] changes = getData().getMIVarChanges(); |
| 186 |
if (changes.length > 0 && changes[0].isInScope() == false) { |
| 187 |
// Object is out-of-scope |
| 188 |
outOfScope = true; |
| 189 |
|
| 190 |
// We can delete this root in GDB right away. This is safe, even |
| 191 |
// if the root has children, because they are also out-of-scope. |
| 192 |
// We -must- also remove this entry from our LRU. If we don't |
| 193 |
// we can end-up with a race condition that create this object |
| 194 |
// twice, or have an infinite loop while never re-creating the object. |
| 195 |
// The can happen if we update a child first then we request |
| 196 |
// the root later, |
| 197 |
removeVariable(getInternalId()); |
| 198 |
|
| 199 |
rm.setData(true); |
| 200 |
rm.done(); |
| 201 |
} else { |
| 202 |
// The root object is now up-to-date, we must parse the changes, if any. |
| 203 |
processChanges(changes); |
| 204 |
|
| 205 |
// We only mark this root as updated in our list if it is in-scope. |
| 206 |
// For out-of-scope object, we don't ever need to re-update them so |
| 207 |
// we don't need to add them to this list. |
| 208 |
rootVariableUpdated(MacOSGDBRootVariableObject.this); |
| 209 |
|
| 210 |
rm.setData(false); |
| 211 |
rm.done(); |
| 212 |
} |
| 213 |
|
| 214 |
while (updatesPending.size() > 0) { |
| 215 |
DataRequestMonitor<Boolean> pendingRm = updatesPending.poll(); |
| 216 |
pendingRm.setData(false); |
| 217 |
pendingRm.done(); |
| 218 |
} |
| 219 |
} else { |
| 220 |
// We were not able to update for some reason |
| 221 |
rm.setData(false); |
| 222 |
rm.done(); |
| 223 |
|
| 224 |
while (updatesPending.size() > 0) { |
| 225 |
DataRequestMonitor<Boolean> pendingRm = updatesPending.poll(); |
| 226 |
pendingRm.setStatus(getStatus()); |
| 227 |
pendingRm.done(); |
| 228 |
} |
| 229 |
} |
| 230 |
} |
| 231 |
}); |
| 232 |
} |
| 233 |
} |
| 234 |
} |
| 235 |
|
| 236 |
} |