author | Steve Sobek <steve.sobek@nokia.com> |
Fri, 30 Oct 2009 10:27:11 -0500 | |
changeset 105 | fbceb3d6fb44 |
parent 37 | c2bce6dd59e7 |
permissions | -rw-r--r-- |
37 | 1 |
/******************************************************************************* |
2 |
* Copyright (c) 2007, 2008 Wind River Systems 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 |
* Wind River Systems - initial API and implementation |
|
10 |
* Ericsson AB - expanded from initial stub |
|
11 |
* Ericsson AB - added support for event handling |
|
12 |
* Ericsson AB - added memory cache |
|
13 |
*******************************************************************************/ |
|
14 |
package org.eclipse.cdt.dsf.mi.service; |
|
15 |
||
16 |
import java.util.HashMap; |
|
17 |
import java.util.Hashtable; |
|
18 |
import java.util.LinkedList; |
|
19 |
import java.util.ListIterator; |
|
20 |
import java.util.Map; |
|
21 |
||
22 |
import org.eclipse.cdt.core.IAddress; |
|
23 |
import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; |
|
24 |
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; |
|
25 |
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; |
|
26 |
import org.eclipse.cdt.dsf.concurrent.RequestMonitor; |
|
27 |
import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; |
|
28 |
import org.eclipse.cdt.dsf.datamodel.DMContexts; |
|
29 |
import org.eclipse.cdt.dsf.datamodel.IDMContext; |
|
30 |
import org.eclipse.cdt.dsf.debug.service.ICachingService; |
|
31 |
import org.eclipse.cdt.dsf.debug.service.IExpressions; |
|
32 |
import org.eclipse.cdt.dsf.debug.service.IMemory; |
|
33 |
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMAddress; |
|
34 |
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; |
|
35 |
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerResumedDMEvent; |
|
36 |
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent; |
|
37 |
import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; |
|
38 |
import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; |
|
39 |
import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; |
|
40 |
import org.eclipse.cdt.dsf.debug.service.command.CommandCache; |
|
41 |
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; |
|
42 |
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; |
|
43 |
import org.eclipse.cdt.dsf.mi.service.MIExpressions.ExpressionChangedEvent; |
|
44 |
import org.eclipse.cdt.dsf.mi.service.command.commands.MIDataReadMemory; |
|
45 |
import org.eclipse.cdt.dsf.mi.service.command.commands.MIDataWriteMemory; |
|
46 |
import org.eclipse.cdt.dsf.mi.service.command.output.MIDataReadMemoryInfo; |
|
47 |
import org.eclipse.cdt.dsf.mi.service.command.output.MIDataWriteMemoryInfo; |
|
48 |
import org.eclipse.cdt.dsf.service.AbstractDsfService; |
|
49 |
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; |
|
50 |
import org.eclipse.cdt.dsf.service.DsfSession; |
|
51 |
import org.eclipse.cdt.utils.Addr64; |
|
52 |
import org.eclipse.core.runtime.IStatus; |
|
53 |
import org.eclipse.core.runtime.Status; |
|
54 |
import org.eclipse.debug.core.model.MemoryByte; |
|
55 |
import org.osgi.framework.BundleContext; |
|
56 |
||
57 |
/** |
|
58 |
* Memory service implementation |
|
59 |
*/ |
|
60 |
public class MIMemory extends AbstractDsfService implements IMemory, ICachingService { |
|
61 |
||
62 |
public class MemoryChangedEvent extends AbstractDMEvent<IMemoryDMContext> |
|
63 |
implements IMemoryChangedEvent |
|
64 |
{ |
|
65 |
IAddress[] fAddresses; |
|
66 |
IDMContext fContext; |
|
67 |
||
68 |
public MemoryChangedEvent(IMemoryDMContext context, IAddress[] addresses) { |
|
69 |
super(context); |
|
70 |
fAddresses = addresses; |
|
71 |
} |
|
72 |
||
73 |
public IAddress[] getAddresses() { |
|
74 |
return fAddresses; |
|
75 |
} |
|
76 |
} |
|
77 |
||
78 |
// Back-end commands cache |
|
79 |
private CommandCache fCommandCache; |
|
80 |
// Map of memory caches |
|
81 |
private Map<IMemoryDMContext, MIMemoryCache> fMemoryCaches; |
|
82 |
||
83 |
private MIMemoryCache getMemoryCache(IMemoryDMContext memoryDMC) { |
|
84 |
MIMemoryCache cache = fMemoryCaches.get(memoryDMC); |
|
85 |
if (cache == null) { |
|
86 |
cache = new MIMemoryCache(); |
|
87 |
fMemoryCaches.put(memoryDMC, cache); |
|
88 |
} |
|
89 |
return cache; |
|
90 |
} |
|
91 |
||
92 |
/** |
|
93 |
* Constructor |
|
94 |
*/ |
|
95 |
public MIMemory(DsfSession session) { |
|
96 |
super(session); |
|
97 |
} |
|
98 |
||
99 |
/////////////////////////////////////////////////////////////////////////// |
|
100 |
// AbstractDsfService overrides |
|
101 |
/////////////////////////////////////////////////////////////////////////// |
|
102 |
||
103 |
/* (non-Javadoc) |
|
104 |
* @see org.eclipse.cdt.dsf.service.AbstractDsfService#initialize(org.eclipse.cdt.dsf.concurrent.RequestMonitor) |
|
105 |
* |
|
106 |
* This function is called during the launch sequence (where the service is |
|
107 |
* instantiated). See LaunchSequence.java. |
|
108 |
*/ |
|
109 |
@Override |
|
110 |
public void initialize(final RequestMonitor requestMonitor) { |
|
111 |
super.initialize(new RequestMonitor(getExecutor(), requestMonitor) { |
|
112 |
@Override |
|
113 |
protected void handleSuccess() { |
|
114 |
doInitialize(requestMonitor); |
|
115 |
} |
|
116 |
}); |
|
117 |
} |
|
118 |
||
119 |
/* |
|
120 |
* Initialization function: |
|
121 |
* - Register the service |
|
122 |
* - Create the command cache |
|
123 |
* - Register self to service events |
|
124 |
* |
|
125 |
* @param requestMonitor |
|
126 |
*/ |
|
127 |
private void doInitialize(final RequestMonitor requestMonitor) { |
|
128 |
// Create the command cache |
|
129 |
ICommandControlService commandControl = getServicesTracker().getService(ICommandControlService.class); |
|
130 |
fCommandCache = new CommandCache(getSession(), commandControl); |
|
131 |
fCommandCache.setContextAvailable(commandControl.getContext(), true); |
|
132 |
||
133 |
// Register this service |
|
134 |
register(new String[] { MIMemory.class.getName(), IMemory.class.getName() }, new Hashtable<String, String>()); |
|
135 |
||
136 |
// Create the memory requests cache |
|
137 |
fMemoryCaches = new HashMap<IMemoryDMContext, MIMemoryCache>(); |
|
138 |
||
139 |
// Register as service event listener |
|
140 |
getSession().addServiceEventListener(this, null); |
|
141 |
||
142 |
// Done |
|
143 |
requestMonitor.done(); |
|
144 |
} |
|
145 |
||
146 |
/* (non-Javadoc) |
|
147 |
* @see org.eclipse.cdt.dsf.service.AbstractDsfService#shutdown(org.eclipse.cdt.dsf.concurrent.RequestMonitor) |
|
148 |
*/ |
|
149 |
@Override |
|
150 |
public void shutdown(final RequestMonitor requestMonitor) { |
|
151 |
||
152 |
// Unregister this service |
|
153 |
unregister(); |
|
154 |
||
155 |
// Remove event listener |
|
156 |
getSession().removeServiceEventListener(this); |
|
157 |
||
158 |
// Complete the shutdown |
|
159 |
super.shutdown(requestMonitor); |
|
160 |
} |
|
161 |
||
162 |
/* (non-Javadoc) |
|
163 |
* @see org.eclipse.cdt.dsf.service.AbstractDsfService#getBundleContext() |
|
164 |
*/ |
|
165 |
@Override |
|
166 |
protected BundleContext getBundleContext() { |
|
167 |
return GdbPlugin.getBundleContext(); |
|
168 |
} |
|
169 |
||
170 |
/////////////////////////////////////////////////////////////////////////// |
|
171 |
// IMemory |
|
172 |
/////////////////////////////////////////////////////////////////////////// |
|
173 |
||
174 |
/* (non-Javadoc) |
|
175 |
* @see org.eclipse.cdt.dsf.debug.service.IMemory#getMemory(org.eclipse.cdt.dsf.datamodel.IDMContext, org.eclipse.cdt.core.IAddress, long, int, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) |
|
176 |
*/ |
|
177 |
public void getMemory(IMemoryDMContext memoryDMC, IAddress address, long offset, |
|
178 |
int word_size, int count, DataRequestMonitor<MemoryByte[]> drm) |
|
179 |
{ |
|
180 |
// Validate the context |
|
181 |
if (memoryDMC == null) { |
|
182 |
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Unknown context type", null)); //$NON-NLS-1$); |
|
183 |
drm.done(); |
|
184 |
return; |
|
185 |
} |
|
186 |
||
187 |
// Validate the word size |
|
188 |
// NOTE: We only accept 1 byte words for this implementation |
|
189 |
if (word_size != 1) { |
|
190 |
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Word size not supported (!= 1)", null)); //$NON-NLS-1$ |
|
191 |
drm.done(); |
|
192 |
return; |
|
193 |
} |
|
194 |
||
195 |
// Validate the byte count |
|
196 |
if (count < 0) { |
|
197 |
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Invalid word count (< 0)", null)); //$NON-NLS-1$ |
|
198 |
drm.done(); |
|
199 |
return; |
|
200 |
} |
|
201 |
||
202 |
// All is clear: go for it |
|
203 |
getMemoryCache(memoryDMC).getMemory(memoryDMC, address.add(offset), word_size, count, drm); |
|
204 |
} |
|
205 |
||
206 |
/* (non-Javadoc) |
|
207 |
* @see org.eclipse.cdt.dsf.debug.service.IMemory#setMemory(org.eclipse.cdt.dsf.datamodel.IDMContext, org.eclipse.cdt.core.IAddress, long, int, byte[], org.eclipse.cdt.dsf.concurrent.RequestMonitor) |
|
208 |
*/ |
|
209 |
public void setMemory(IMemoryDMContext memoryDMC, IAddress address, long offset, |
|
210 |
int word_size, int count, byte[] buffer, RequestMonitor rm) |
|
211 |
{ |
|
212 |
// Validate the context |
|
213 |
if (memoryDMC == null) { |
|
214 |
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Unknown context type", null)); //$NON-NLS-1$); |
|
215 |
rm.done(); |
|
216 |
return; |
|
217 |
} |
|
218 |
||
219 |
// Validate the word size |
|
220 |
// NOTE: We only accept 1 byte words for this implementation |
|
221 |
if (word_size != 1) { |
|
222 |
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Word size not supported (!= 1)", null)); //$NON-NLS-1$ |
|
223 |
rm.done(); |
|
224 |
return; |
|
225 |
} |
|
226 |
||
227 |
// Validate the byte count |
|
228 |
if (count < 0) { |
|
229 |
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Invalid word count (< 0)", null)); //$NON-NLS-1$ |
|
230 |
rm.done(); |
|
231 |
return; |
|
232 |
} |
|
233 |
||
234 |
// Validate the buffer size |
|
235 |
if (buffer.length < count) { |
|
236 |
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Buffer too short", null)); //$NON-NLS-1$ |
|
237 |
rm.done(); |
|
238 |
return; |
|
239 |
} |
|
240 |
||
241 |
// All is clear: go for it |
|
242 |
getMemoryCache(memoryDMC).setMemory(memoryDMC, address, offset, word_size, count, buffer, rm); |
|
243 |
} |
|
244 |
||
245 |
/* (non-Javadoc) |
|
246 |
* @see org.eclipse.cdt.dsf.debug.service.IMemory#fillMemory(org.eclipse.cdt.dsf.datamodel.IDMContext, org.eclipse.cdt.core.IAddress, long, int, byte[], org.eclipse.cdt.dsf.concurrent.RequestMonitor) |
|
247 |
*/ |
|
248 |
public void fillMemory(IMemoryDMContext memoryDMC, IAddress address, long offset, |
|
249 |
int word_size, int count, byte[] pattern, RequestMonitor rm) |
|
250 |
{ |
|
251 |
// Validate the context |
|
252 |
if (memoryDMC == null) { |
|
253 |
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Unknown context type", null)); //$NON-NLS-1$); |
|
254 |
rm.done(); |
|
255 |
return; |
|
256 |
} |
|
257 |
||
258 |
// Validate the word size |
|
259 |
// NOTE: We only accept 1 byte words for this implementation |
|
260 |
if (word_size != 1) { |
|
261 |
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Word size not supported (!= 1)", null)); //$NON-NLS-1$ |
|
262 |
rm.done(); |
|
263 |
return; |
|
264 |
} |
|
265 |
||
266 |
// Validate the repeat count |
|
267 |
if (count < 0) { |
|
268 |
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Invalid repeat count (< 0)", null)); //$NON-NLS-1$ |
|
269 |
rm.done(); |
|
270 |
return; |
|
271 |
} |
|
272 |
||
273 |
// Validate the pattern |
|
274 |
if (pattern.length < 1) { |
|
275 |
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Empty pattern", null)); //$NON-NLS-1$ |
|
276 |
rm.done(); |
|
277 |
return; |
|
278 |
} |
|
279 |
||
280 |
// Create an aggregate buffer so we can write in 1 shot |
|
281 |
int length = pattern.length; |
|
282 |
byte[] buffer = new byte[count * length]; |
|
283 |
for (int i = 0; i < count; i++) { |
|
284 |
System.arraycopy(pattern, 0, buffer, i * length, length); |
|
285 |
} |
|
286 |
||
287 |
// All is clear: go for it |
|
288 |
getMemoryCache(memoryDMC).setMemory(memoryDMC, address, offset, word_size, count * length, buffer, rm); |
|
289 |
} |
|
290 |
||
291 |
/////////////////////////////////////////////////////////////////////// |
|
292 |
// Back-end functions |
|
293 |
/////////////////////////////////////////////////////////////////////// |
|
294 |
||
295 |
/** |
|
296 |
* @param memoryDMC |
|
297 |
* @param address |
|
298 |
* @param offset |
|
299 |
* @param word_size |
|
300 |
* @param count |
|
301 |
* @param drm |
|
302 |
* |
|
303 |
* @since 1.1 |
|
304 |
*/ |
|
305 |
protected void readMemoryBlock(IDMContext dmc, IAddress address, final long offset, |
|
306 |
final int word_size, final int count, final DataRequestMonitor<MemoryByte[]> drm) |
|
307 |
{ |
|
308 |
/* To simplify the parsing of the MI result, we request the output to |
|
309 |
* be on 1 row of [count] columns, no char interpretation. |
|
310 |
*/ |
|
311 |
int mode = MIFormat.HEXADECIMAL; |
|
312 |
int nb_rows = 1; |
|
313 |
int nb_cols = count; |
|
314 |
Character asChar = null; |
|
315 |
||
316 |
fCommandCache.execute( |
|
317 |
new MIDataReadMemory(dmc, offset, address.toString(), mode, word_size, nb_rows, nb_cols, asChar), |
|
318 |
new DataRequestMonitor<MIDataReadMemoryInfo>(getExecutor(), drm) { |
|
319 |
@Override |
|
320 |
protected void handleSuccess() { |
|
321 |
// Retrieve the memory block |
|
322 |
drm.setData(getData().getMIMemoryBlock()); |
|
323 |
drm.done(); |
|
324 |
} |
|
325 |
@Override |
|
326 |
protected void handleFailure() { |
|
327 |
// Bug234289: If memory read fails, return a block marked as invalid |
|
328 |
MemoryByte[] block = new MemoryByte[word_size * count]; |
|
329 |
for (int i = 0; i < block.length; i++) |
|
330 |
block[i] = new MemoryByte((byte) 0, (byte) 0); |
|
331 |
drm.setData(block); |
|
332 |
drm.done(); |
|
333 |
} |
|
334 |
} |
|
335 |
); |
|
336 |
} |
|
337 |
||
338 |
/** |
|
339 |
* @param memoryDMC |
|
340 |
* @param address |
|
341 |
* @param offset |
|
342 |
* @param word_size |
|
343 |
* @param count |
|
344 |
* @param buffer |
|
345 |
* @param rm |
|
346 |
* |
|
347 |
* @since 1.1 |
|
348 |
*/ |
|
349 |
protected void writeMemoryBlock(final IDMContext dmc, final IAddress address, final long offset, |
|
350 |
final int word_size, final int count, final byte[] buffer, final RequestMonitor rm) |
|
351 |
{ |
|
352 |
// Each byte is written individually (GDB power...) |
|
353 |
// so we need to keep track of the count |
|
354 |
final CountingRequestMonitor countingRM = new CountingRequestMonitor(getExecutor(), rm); |
|
355 |
countingRM.setDoneCount(count); |
|
356 |
||
357 |
// We will format the individual bytes in decimal |
|
358 |
int format = MIFormat.DECIMAL; |
|
359 |
String baseAddress = address.toString(); |
|
360 |
||
361 |
// Issue an MI request for each byte to write |
|
362 |
for (int i = 0; i < count; i++) { |
|
363 |
String value = new Byte(buffer[i]).toString(); |
|
364 |
fCommandCache.execute( |
|
365 |
new MIDataWriteMemory(dmc, offset + i, baseAddress, format, word_size, value), |
|
366 |
new DataRequestMonitor<MIDataWriteMemoryInfo>(getExecutor(), countingRM) |
|
367 |
); |
|
368 |
} |
|
369 |
} |
|
370 |
||
371 |
////////////////////////////////////////////////////////////////////////// |
|
372 |
// Event handlers |
|
373 |
////////////////////////////////////////////////////////////////////////// |
|
374 |
||
375 |
/** |
|
376 |
* @nooverride This method is not intended to be re-implemented or extended by clients. |
|
377 |
* @noreference This method is not intended to be referenced by clients. |
|
378 |
*/ |
|
379 |
@DsfServiceEventHandler |
|
380 |
public void eventDispatched(IResumedDMEvent e) { |
|
381 |
if (e instanceof IContainerResumedDMEvent) { |
|
382 |
fCommandCache.setContextAvailable(e.getDMContext(), false); |
|
383 |
} |
|
384 |
||
385 |
if (e.getReason() != StateChangeReason.STEP) { |
|
386 |
fCommandCache.reset(e.getDMContext()); |
|
387 |
IMemoryDMContext memoryDMC = DMContexts.getAncestorOfType(e.getDMContext(), IMemoryDMContext.class); |
|
388 |
if (fMemoryCaches.containsKey(memoryDMC)) { |
|
389 |
// We do not want to use the call to getMemoryCache() here. |
|
390 |
// This is because: |
|
391 |
// 1- if there is not an entry already , we do not want to automatically |
|
392 |
// create one, just to call reset() on it. |
|
393 |
// 2- if memoryDMC == null, we do not want to create a cache |
|
394 |
// entry for which the key is 'null' |
|
395 |
fMemoryCaches.get(memoryDMC).reset(); |
|
396 |
} |
|
397 |
} |
|
398 |
} |
|
399 |
||
400 |
/** |
|
401 |
* @nooverride This method is not intended to be re-implemented or extended by clients. |
|
402 |
* @noreference This method is not intended to be referenced by clients. |
|
403 |
*/ |
|
404 |
@DsfServiceEventHandler |
|
405 |
public void eventDispatched(ISuspendedDMEvent e) { |
|
406 |
if (e instanceof IContainerSuspendedDMEvent) { |
|
407 |
fCommandCache.setContextAvailable(e.getDMContext(), true); |
|
408 |
} |
|
409 |
||
410 |
fCommandCache.reset(e.getDMContext()); |
|
411 |
IMemoryDMContext memoryDMC = DMContexts.getAncestorOfType(e.getDMContext(), IMemoryDMContext.class); |
|
412 |
if (fMemoryCaches.containsKey(memoryDMC)) { |
|
413 |
// We do not want to use the call to getMemoryCache() here. |
|
414 |
// This is because: |
|
415 |
// 1- if there is not an entry already , we do not want to automatically |
|
416 |
// create one, just to call reset() on it. |
|
417 |
// 2- if memoryDMC == null, we do not want to create a cache |
|
418 |
// entry for which the key is 'null' |
|
419 |
fMemoryCaches.get(memoryDMC).reset(); |
|
420 |
} |
|
421 |
} |
|
422 |
||
423 |
/** |
|
424 |
* @nooverride This method is not intended to be re-implemented or extended by clients. |
|
425 |
* @noreference This method is not intended to be referenced by clients. |
|
426 |
*/ |
|
427 |
@DsfServiceEventHandler |
|
428 |
public void eventDispatched(ExpressionChangedEvent e) { |
|
429 |
||
430 |
// Get the context and expression service handle |
|
431 |
final IExpressionDMContext context = e.getDMContext(); |
|
432 |
IExpressions expressionService = getServicesTracker().getService(IExpressions.class); |
|
433 |
||
434 |
// Get the variable information and update the corresponding memory locations |
|
435 |
if (expressionService != null) { |
|
436 |
expressionService.getExpressionAddressData(context, |
|
437 |
new DataRequestMonitor<IExpressionDMAddress>(getExecutor(), null) { |
|
438 |
@Override |
|
439 |
protected void handleSuccess() { |
|
440 |
// Figure out which memory area was modified |
|
441 |
IExpressionDMAddress expression = getData(); |
|
442 |
final int count = expression.getSize(); |
|
105
fbceb3d6fb44
let expression getAddress() return a string, such as register name or invalid access message
Steve Sobek <steve.sobek@nokia.com>
parents:
37
diff
changeset
|
443 |
Object expAddress = expression.getAddress(); |
37 | 444 |
final Addr64 address; |
445 |
if (expAddress instanceof Addr64) |
|
446 |
address = (Addr64) expAddress; |
|
105
fbceb3d6fb44
let expression getAddress() return a string, such as register name or invalid access message
Steve Sobek <steve.sobek@nokia.com>
parents:
37
diff
changeset
|
447 |
else if (expAddress instanceof IAddress) |
fbceb3d6fb44
let expression getAddress() return a string, such as register name or invalid access message
Steve Sobek <steve.sobek@nokia.com>
parents:
37
diff
changeset
|
448 |
address = new Addr64(((IAddress)expAddress).getValue()); |
37 | 449 |
else |
105
fbceb3d6fb44
let expression getAddress() return a string, such as register name or invalid access message
Steve Sobek <steve.sobek@nokia.com>
parents:
37
diff
changeset
|
450 |
return; // not a valid memory address |
37 | 451 |
|
452 |
final IMemoryDMContext memoryDMC = DMContexts.getAncestorOfType(context, IMemoryDMContext.class); |
|
453 |
getMemoryCache(memoryDMC).refreshMemory(memoryDMC, address, 0, 1, count, |
|
454 |
new RequestMonitor(getExecutor(), null)); |
|
455 |
} |
|
456 |
}); |
|
457 |
} |
|
458 |
} |
|
459 |
||
460 |
/////////////////////////////////////////////////////////////////////////// |
|
461 |
// SortedLinkedlist |
|
462 |
/////////////////////////////////////////////////////////////////////////// |
|
463 |
||
464 |
// This class is really the equivalent of a C struct (old habits die hard...) |
|
465 |
// For simplicity, everything is public. |
|
466 |
private class MemoryBlock { |
|
467 |
public IAddress fAddress; |
|
468 |
public long fLength; |
|
469 |
public MemoryByte[] fBlock; |
|
470 |
public MemoryBlock(IAddress address, long length, MemoryByte[] block) { |
|
471 |
fAddress = address; |
|
472 |
fLength = length; |
|
473 |
fBlock = block; |
|
474 |
} |
|
475 |
} |
|
476 |
||
477 |
// Address-ordered data structure to cache the memory blocks. |
|
478 |
// Contiguous blocks are merged if possible. |
|
479 |
@SuppressWarnings("serial") |
|
480 |
private class SortedMemoryBlockList extends LinkedList<MemoryBlock> { |
|
481 |
||
482 |
public SortedMemoryBlockList() { |
|
483 |
super(); |
|
484 |
} |
|
485 |
||
486 |
// Insert the block in the sorted linked list and merge contiguous |
|
487 |
// blocks if necessary |
|
488 |
@Override |
|
489 |
public boolean add(MemoryBlock block) { |
|
490 |
||
491 |
// If the list is empty, just store the block |
|
492 |
if (isEmpty()) { |
|
493 |
addFirst(block); |
|
494 |
return true; |
|
495 |
} |
|
496 |
||
497 |
// Insert the block at the correct location and then |
|
498 |
// merge the blocks if possible |
|
499 |
ListIterator<MemoryBlock> it = listIterator(); |
|
500 |
while (it.hasNext()) { |
|
501 |
int index = it.nextIndex(); |
|
502 |
MemoryBlock item = it.next(); |
|
503 |
if (block.fAddress.compareTo(item.fAddress) < 0) { |
|
504 |
add(index, block); |
|
505 |
compact(index); |
|
506 |
return true; |
|
507 |
} |
|
508 |
} |
|
509 |
||
510 |
// Put at the end of the list and merge if necessary |
|
511 |
addLast(block); |
|
512 |
compact(size() - 1); |
|
513 |
return true; |
|
514 |
} |
|
515 |
||
516 |
// Merge this block with its contiguous neighbors (if any) |
|
517 |
// Note: Merge is not performed if resulting block size would exceed MAXINT |
|
518 |
private void compact(int index) { |
|
519 |
||
520 |
MemoryBlock newBlock = get(index); |
|
521 |
||
522 |
// Case where the block is to be merged with the previous block |
|
523 |
if (index > 0) { |
|
524 |
MemoryBlock prevBlock = get(index - 1); |
|
525 |
IAddress endOfPreviousBlock = prevBlock.fAddress.add(prevBlock.fLength); |
|
526 |
if (endOfPreviousBlock.distanceTo(newBlock.fAddress).longValue() == 0) { |
|
527 |
long newLength = prevBlock.fLength + newBlock.fLength; |
|
528 |
if (newLength <= Integer.MAX_VALUE) { |
|
529 |
MemoryByte[] block = new MemoryByte[(int) newLength] ; |
|
530 |
System.arraycopy(prevBlock.fBlock, 0, block, 0, (int) prevBlock.fLength); |
|
531 |
System.arraycopy(newBlock.fBlock, 0, block, (int) prevBlock.fLength, (int) newBlock.fLength); |
|
532 |
newBlock = new MemoryBlock(prevBlock.fAddress, newLength, block); |
|
533 |
remove(index); |
|
534 |
index -= 1; |
|
535 |
set(index, newBlock); |
|
536 |
} |
|
537 |
} |
|
538 |
} |
|
539 |
||
540 |
// Case where the block is to be merged with the following block |
|
541 |
int lastIndex = size() - 1; |
|
542 |
if (index < lastIndex) { |
|
543 |
MemoryBlock nextBlock = get(index + 1); |
|
544 |
IAddress endOfNewBlock = newBlock.fAddress.add(newBlock.fLength); |
|
545 |
if (endOfNewBlock.distanceTo(nextBlock.fAddress).longValue() == 0) { |
|
546 |
long newLength = newBlock.fLength + nextBlock.fLength; |
|
547 |
if (newLength <= Integer.MAX_VALUE) { |
|
548 |
MemoryByte[] block = new MemoryByte[(int) newLength] ; |
|
549 |
System.arraycopy(newBlock.fBlock, 0, block, 0, (int) newBlock.fLength); |
|
550 |
System.arraycopy(nextBlock.fBlock, 0, block, (int) newBlock.fLength, (int) nextBlock.fLength); |
|
551 |
newBlock = new MemoryBlock(newBlock.fAddress, newLength, block); |
|
552 |
set(index, newBlock); |
|
553 |
remove(index + 1); |
|
554 |
} |
|
555 |
} |
|
556 |
} |
|
557 |
} |
|
558 |
} |
|
559 |
||
560 |
/////////////////////////////////////////////////////////////////////////// |
|
561 |
// MIMemoryCache |
|
562 |
/////////////////////////////////////////////////////////////////////////// |
|
563 |
||
564 |
private class MIMemoryCache { |
|
565 |
// The memory cache data structure |
|
566 |
private SortedMemoryBlockList fMemoryBlockList; |
|
567 |
||
568 |
public MIMemoryCache() { |
|
569 |
// Create the memory block cache |
|
570 |
fMemoryBlockList = new SortedMemoryBlockList(); |
|
571 |
} |
|
572 |
||
573 |
public void reset() { |
|
574 |
// Clear the memory cache |
|
575 |
fMemoryBlockList.clear(); |
|
576 |
} |
|
577 |
||
578 |
/** |
|
579 |
* This function walks the address-sorted memory block list to identify |
|
580 |
* the 'missing' blocks (i.e. the holes) that need to be fetched on the target. |
|
581 |
* |
|
582 |
* The idea is fairly simple but an illustration could perhaps help. |
|
583 |
* Assume the cache holds a number of cached memory blocks with gaps i.e. |
|
584 |
* there is un-cached memory areas between blocks A, B and C: |
|
585 |
* |
|
586 |
* +---------+ +---------+ +---------+ |
|
587 |
* + A + + B + + C + |
|
588 |
* +---------+ +---------+ +---------+ |
|
589 |
* : : : : : : |
|
590 |
* [a] : : [b] : : [c] : : [d] |
|
591 |
* : : : : : : |
|
592 |
* [e---+--] : [f--+---------+--] : : |
|
593 |
* [g---+---------+------+---------+------+---------+----] |
|
594 |
* : : : : : : |
|
595 |
* : [h] : : [i----+--] : : |
|
596 |
* |
|
597 |
* |
|
598 |
* We have the following cases to consider.The requested block [a-i] either: |
|
599 |
* |
|
600 |
* [1] Fits entirely before A, in one of the gaps, or after C |
|
601 |
* with no overlap and no contiguousness (e.g. [a], [b], [c] and [d]) |
|
602 |
* -> Add the requested block to the list of blocks to fetch |
|
603 |
* |
|
604 |
* [2] Starts before an existing block but overlaps part of it, possibly |
|
605 |
* spilling in the gap following the cached block (e.g. [e], [f] and [g]) |
|
606 |
* -> Determine the length of the missing part (< count) |
|
607 |
* -> Add a request to fill the gap before the existing block |
|
608 |
* -> Update the requested block for the next iteration: |
|
609 |
* - Start address to point just after the end of the cached block |
|
610 |
* - Count reduced by cached block length (possibly becoming negative, e.g. [e]) |
|
611 |
* At this point, the updated requested block starts just beyond the cached block |
|
612 |
* for the next iteration. |
|
613 |
* |
|
614 |
* [3] Starts at or into an existing block and overlaps part of it ([h] and [i]) |
|
615 |
* -> Update the requested block for the next iteration: |
|
616 |
* - Start address to point just after the end of the cached block |
|
617 |
* - Count reduced by length to end of cached block (possibly becoming negative, e.g. [h]) |
|
618 |
* At this point, the updated requested block starts just beyond the cached block |
|
619 |
* for the next iteration. |
|
620 |
* |
|
621 |
* We iterate over the cached blocks list until there is no entry left or until |
|
622 |
* the remaining requested block count is <= 0, meaning the result list contains |
|
623 |
* only the sub-blocks needed to fill the gap(s), if any. |
|
624 |
* |
|
625 |
* (As is often the case, it takes much more typing to explain it than to just do it :-) |
|
626 |
* |
|
627 |
* What is missing is a parameter that indicates the minimal block size that is worth fetching. |
|
628 |
* This is target-specific and straight in the realm of the coalescing function... |
|
629 |
* |
|
630 |
* @param reqBlockStart The address of the requested block |
|
631 |
* @param count Its length |
|
632 |
* @return A list of the sub-blocks to fetch in order to fill enough gaps in the memory cache |
|
633 |
* to service the request |
|
634 |
*/ |
|
635 |
private LinkedList<MemoryBlock> getListOfMissingBlocks(IAddress reqBlockStart, int count) { |
|
636 |
||
637 |
LinkedList<MemoryBlock> list = new LinkedList<MemoryBlock>(); |
|
638 |
ListIterator<MemoryBlock> it = fMemoryBlockList.listIterator(); |
|
639 |
||
640 |
// Look for holes in the list of memory blocks |
|
641 |
while (it.hasNext() && count > 0) { |
|
642 |
MemoryBlock cachedBlock = it.next(); |
|
643 |
IAddress cachedBlockStart = cachedBlock.fAddress; |
|
644 |
IAddress cachedBlockEnd = cachedBlock.fAddress.add(cachedBlock.fLength); |
|
645 |
||
646 |
// Case where we miss a block before the cached block |
|
647 |
if (reqBlockStart.distanceTo(cachedBlockStart).longValue() >= 0) { |
|
648 |
int length = (int) Math.min(reqBlockStart.distanceTo(cachedBlockStart).longValue(), count); |
|
649 |
// If both blocks start at the same location, no need to create a new cached block |
|
650 |
if (length > 0) { |
|
651 |
MemoryBlock newBlock = new MemoryBlock(reqBlockStart, length, new MemoryByte[0]); |
|
652 |
list.add(newBlock); |
|
653 |
} |
|
654 |
// Adjust request block start and length for the next iteration |
|
655 |
reqBlockStart = cachedBlockEnd; |
|
656 |
count -= length + cachedBlock.fLength; |
|
657 |
} |
|
658 |
||
659 |
// Case where the requested block starts somewhere in the cached block |
|
660 |
else if (cachedBlockStart.distanceTo(reqBlockStart).longValue() > 0 |
|
661 |
&& reqBlockStart.distanceTo(cachedBlockEnd).longValue() >= 0) |
|
662 |
{ |
|
663 |
// Start of the requested block already in cache |
|
664 |
// Adjust request block start and length for the next iteration |
|
665 |
count -= reqBlockStart.distanceTo(cachedBlockEnd).longValue(); |
|
666 |
reqBlockStart = cachedBlockEnd; |
|
667 |
} |
|
668 |
} |
|
669 |
||
670 |
// Case where we miss a block at the end of the cache |
|
671 |
if (count > 0) { |
|
672 |
MemoryBlock newBlock = new MemoryBlock(reqBlockStart, count, new MemoryByte[0]); |
|
673 |
list.add(newBlock); |
|
674 |
} |
|
675 |
||
676 |
return list; |
|
677 |
} |
|
678 |
||
679 |
/** |
|
680 |
* This function walks the address-sorted memory block list to get the |
|
681 |
* cached memory bytes (possibly from multiple contiguous blocks). |
|
682 |
* This function is called *after* the missing blocks have been read from |
|
683 |
* the back end i.e. the requested memory is all cached. |
|
684 |
* |
|
685 |
* Again, this is fairly simple. As we loop over the address-ordered list, |
|
686 |
* There are really only 2 cases: |
|
687 |
* |
|
688 |
* [1] The requested block fits entirely in the cached block ([a] or [b]) |
|
689 |
* [2] The requested block starts in a cached block and ends in the |
|
690 |
* following (contiguous) one ([c]) in which case it is treated |
|
691 |
* as 2 contiguous requests ([c'] and [c"]) |
|
692 |
* |
|
693 |
* +--------------+--------------+ |
|
694 |
* + A + B + |
|
695 |
* +--------------+--------------+ |
|
696 |
* : [a----] : [b-----] : |
|
697 |
* : : : |
|
698 |
* : [c-----+------] : |
|
699 |
* : [c'---]+[c"---] : |
|
700 |
* |
|
701 |
* @param reqBlockStart The address of the requested block |
|
702 |
* @param count Its length |
|
703 |
* @return The cached memory content |
|
704 |
*/ |
|
705 |
private MemoryByte[] getMemoryBlockFromCache(IAddress reqBlockStart, int count) { |
|
706 |
||
707 |
IAddress reqBlockEnd = reqBlockStart.add(count); |
|
708 |
MemoryByte[] resultBlock = new MemoryByte[count]; |
|
709 |
ListIterator<MemoryBlock> iter = fMemoryBlockList.listIterator(); |
|
710 |
||
711 |
while (iter.hasNext()) { |
|
712 |
MemoryBlock cachedBlock = iter.next(); |
|
713 |
IAddress cachedBlockStart = cachedBlock.fAddress; |
|
714 |
IAddress cachedBlockEnd = cachedBlock.fAddress.add(cachedBlock.fLength); |
|
715 |
||
716 |
// Case where the cached block overlaps completely the requested memory block |
|
717 |
if (cachedBlockStart.distanceTo(reqBlockStart).longValue() >= 0 |
|
718 |
&& reqBlockEnd.distanceTo(cachedBlockEnd).longValue() >= 0) |
|
719 |
{ |
|
720 |
int pos = (int) cachedBlockStart.distanceTo(reqBlockStart).longValue(); |
|
721 |
System.arraycopy(cachedBlock.fBlock, pos, resultBlock, 0, count); |
|
722 |
} |
|
723 |
||
724 |
// Case where the beginning of the cached block is within the requested memory block |
|
725 |
else if (reqBlockStart.distanceTo(cachedBlockStart).longValue() >= 0 |
|
726 |
&& cachedBlockStart.distanceTo(reqBlockEnd).longValue() > 0) |
|
727 |
{ |
|
728 |
int pos = (int) reqBlockStart.distanceTo(cachedBlockStart).longValue(); |
|
729 |
int length = (int) Math.min(cachedBlock.fLength, count - pos); |
|
730 |
System.arraycopy(cachedBlock.fBlock, 0, resultBlock, pos, length); |
|
731 |
} |
|
732 |
||
733 |
// Case where the end of the cached block is within the requested memory block |
|
734 |
else if (cachedBlockStart.distanceTo(reqBlockStart).longValue() >= 0 |
|
735 |
&& reqBlockStart.distanceTo(cachedBlockEnd).longValue() > 0) |
|
736 |
{ |
|
737 |
int pos = (int) cachedBlockStart.distanceTo(reqBlockStart).longValue(); |
|
738 |
int length = (int) Math.min(cachedBlock.fLength - pos, count); |
|
739 |
System.arraycopy(cachedBlock.fBlock, pos, resultBlock, 0, length); |
|
740 |
} |
|
741 |
} |
|
742 |
return resultBlock; |
|
743 |
} |
|
744 |
||
745 |
/** |
|
746 |
* This function walks the address-sorted memory block list and updates |
|
747 |
* the content with the actual memory just read from the target. |
|
748 |
* |
|
749 |
* @param modBlockStart |
|
750 |
* @param count |
|
751 |
* @param modBlock |
|
752 |
*/ |
|
753 |
private void updateMemoryCache(IAddress modBlockStart, int count, MemoryByte[] modBlock) { |
|
754 |
||
755 |
IAddress modBlockEnd = modBlockStart.add(count); |
|
756 |
ListIterator<MemoryBlock> iter = fMemoryBlockList.listIterator(); |
|
757 |
||
758 |
while (iter.hasNext()) { |
|
759 |
MemoryBlock cachedBlock = iter.next(); |
|
760 |
IAddress cachedBlockStart = cachedBlock.fAddress; |
|
761 |
IAddress cachedBlockEnd = cachedBlock.fAddress.add(cachedBlock.fLength); |
|
762 |
||
763 |
// For now, we only bother to update bytes already cached. |
|
764 |
// Note: In a better implementation (v1.1), we would augment |
|
765 |
// the cache with the missing memory blocks since we went |
|
766 |
// through the pains of reading them in the first place. |
|
767 |
// (this is left as an exercise to the reader :-) |
|
768 |
||
769 |
// Case where the modified block is completely included in the cached block |
|
770 |
if (cachedBlockStart.distanceTo(modBlockStart).longValue() >= 0 |
|
771 |
&& modBlockEnd.distanceTo(cachedBlockEnd).longValue() >= 0) |
|
772 |
{ |
|
773 |
int pos = (int) cachedBlockStart.distanceTo(modBlockStart).longValue(); |
|
774 |
System.arraycopy(modBlock, 0, cachedBlock.fBlock, pos, count); |
|
775 |
} |
|
776 |
||
777 |
// Case where the beginning of the modified block is within the cached block |
|
778 |
else if (cachedBlockStart.distanceTo(modBlockStart).longValue() >= 0 |
|
779 |
&& modBlockStart.distanceTo(cachedBlockEnd).longValue() > 0) |
|
780 |
{ |
|
781 |
int pos = (int) cachedBlockStart.distanceTo(modBlockStart).longValue(); |
|
782 |
int length = (int) cachedBlockStart.distanceTo(modBlockEnd).longValue(); |
|
783 |
System.arraycopy(modBlock, 0, cachedBlock.fBlock, pos, length); |
|
784 |
} |
|
785 |
||
786 |
// Case where the end of the modified block is within the cached block |
|
787 |
else if (cachedBlockStart.distanceTo(modBlockEnd).longValue() > 0 |
|
788 |
&& modBlockEnd.distanceTo(cachedBlockEnd).longValue() >= 0) |
|
789 |
{ |
|
790 |
int pos = (int) modBlockStart.distanceTo(cachedBlockStart).longValue(); |
|
791 |
int length = (int) cachedBlockStart.distanceTo(modBlockEnd).longValue(); |
|
792 |
System.arraycopy(modBlock, pos, cachedBlock.fBlock, 0, length); |
|
793 |
} |
|
794 |
} |
|
795 |
return; |
|
796 |
} |
|
797 |
||
798 |
/** |
|
799 |
* @param memoryDMC |
|
800 |
* @param address the memory block address (on the target) |
|
801 |
* @param word_size the size, in bytes, of an addressable item |
|
802 |
* @param count the number of bytes to read |
|
803 |
* @param drm the asynchronous data request monitor |
|
804 |
*/ |
|
805 |
public void getMemory(IMemoryDMContext memoryDMC, final IAddress address, final int word_size, |
|
806 |
final int count, final DataRequestMonitor<MemoryByte[]> drm) |
|
807 |
{ |
|
808 |
// Determine the number of read requests to issue |
|
809 |
LinkedList<MemoryBlock> missingBlocks = getListOfMissingBlocks(address, count); |
|
810 |
int numberOfRequests = missingBlocks.size(); |
|
811 |
||
812 |
// A read request will be issued for each block needed |
|
813 |
// so we need to keep track of the count |
|
814 |
final CountingRequestMonitor countingRM = |
|
815 |
new CountingRequestMonitor(getExecutor(), drm) { |
|
816 |
@Override |
|
817 |
protected void handleSuccess() { |
|
818 |
// We received everything so read the result from the memory cache |
|
819 |
drm.setData(getMemoryBlockFromCache(address, count)); |
|
820 |
drm.done(); |
|
821 |
} |
|
822 |
}; |
|
823 |
countingRM.setDoneCount(numberOfRequests); |
|
824 |
||
825 |
// Issue the read requests |
|
826 |
for (int i = 0; i < numberOfRequests; i++) { |
|
827 |
MemoryBlock block = missingBlocks.get(i); |
|
828 |
final IAddress startAddress = block.fAddress; |
|
829 |
final int length = (int) block.fLength; |
|
830 |
readMemoryBlock(memoryDMC, startAddress, 0, word_size, length, |
|
831 |
new DataRequestMonitor<MemoryByte[]>(getSession().getExecutor(), drm) { |
|
832 |
@Override |
|
833 |
protected void handleSuccess() { |
|
834 |
MemoryByte[] block = new MemoryByte[count]; |
|
835 |
block = getData(); |
|
836 |
MemoryBlock memoryBlock = new MemoryBlock(startAddress, length, block); |
|
837 |
fMemoryBlockList.add(memoryBlock); |
|
838 |
countingRM.done(); |
|
839 |
} |
|
840 |
}); |
|
841 |
} |
|
842 |
} |
|
843 |
||
844 |
/** |
|
845 |
* @param memoryDMC |
|
846 |
* @param address the memory block address (on the target) |
|
847 |
* @param offset the offset from the start address |
|
848 |
* @param word_size the size, in bytes, of an addressable item |
|
849 |
* @param count the number of bytes to write |
|
850 |
* @param buffer the source buffer |
|
851 |
* @param rm the asynchronous request monitor |
|
852 |
*/ |
|
853 |
public void setMemory(final IMemoryDMContext memoryDMC, final IAddress address, |
|
854 |
final long offset, final int word_size, final int count, final byte[] buffer, |
|
855 |
final RequestMonitor rm) |
|
856 |
{ |
|
857 |
writeMemoryBlock( |
|
858 |
memoryDMC, address, offset, word_size, count, buffer, |
|
859 |
new RequestMonitor(getSession().getExecutor(), rm) { |
|
860 |
@Override |
|
861 |
protected void handleSuccess() { |
|
862 |
// Clear the command cache (otherwise we can't guarantee |
|
863 |
// that the subsequent memory read will be correct) |
|
864 |
fCommandCache.reset(); |
|
865 |
||
866 |
// Re-read the modified memory block to asynchronously update of the memory cache |
|
867 |
readMemoryBlock(memoryDMC, address, offset, word_size, count, |
|
868 |
new DataRequestMonitor<MemoryByte[]>(getExecutor(), rm) { |
|
869 |
@Override |
|
870 |
protected void handleSuccess() { |
|
871 |
updateMemoryCache(address.add(offset), count, getData()); |
|
872 |
// Send the MemoryChangedEvent |
|
873 |
IAddress[] addresses = new IAddress[count]; |
|
874 |
for (int i = 0; i < count; i++) { |
|
875 |
addresses[i] = address.add(offset + i); |
|
876 |
} |
|
877 |
getSession().dispatchEvent(new MemoryChangedEvent(memoryDMC, addresses), getProperties()); |
|
878 |
// Finally... |
|
879 |
rm.done(); |
|
880 |
} |
|
881 |
}); |
|
882 |
} |
|
883 |
}); |
|
884 |
} |
|
885 |
||
886 |
/** |
|
887 |
* @param memoryDMC |
|
888 |
* @param address |
|
889 |
* @param offset |
|
890 |
* @param word_size |
|
891 |
* @param count |
|
892 |
* @param rm |
|
893 |
*/ |
|
894 |
public void refreshMemory(final IMemoryDMContext memoryDMC, final IAddress address, |
|
895 |
final long offset, final int word_size, final int count, final RequestMonitor rm) |
|
896 |
{ |
|
897 |
// Check if we already cache part of this memory area (which means it |
|
898 |
// is used by a memory service client that will have to be updated) |
|
899 |
LinkedList<MemoryBlock> list = getListOfMissingBlocks(address, count); |
|
900 |
int sizeToRead = 0; |
|
901 |
for (MemoryBlock block : list) { |
|
902 |
sizeToRead += block.fLength; |
|
903 |
} |
|
904 |
||
905 |
// If none of the requested memory is in cache, just get out |
|
906 |
if (sizeToRead == count) { |
|
907 |
rm.done(); |
|
908 |
return; |
|
909 |
} |
|
910 |
||
911 |
// Prepare the data for the MemoryChangedEvent |
|
912 |
final IAddress[] addresses = new IAddress[count]; |
|
913 |
for (int i = 0; i < count; i++) { |
|
914 |
addresses[i] = address.add(i); |
|
915 |
} |
|
916 |
||
917 |
// Read the corresponding memory block |
|
918 |
fCommandCache.reset(); |
|
919 |
readMemoryBlock(memoryDMC, address, 0, 1, count, |
|
920 |
new DataRequestMonitor<MemoryByte[]>(getExecutor(), rm) { |
|
921 |
@Override |
|
922 |
protected void handleSuccess() { |
|
923 |
MemoryByte[] oldBlock = getMemoryBlockFromCache(address, count); |
|
924 |
MemoryByte[] newBlock = getData(); |
|
925 |
boolean blocksDiffer = false; |
|
926 |
for (int i = 0; i < oldBlock.length; i++) { |
|
927 |
if (oldBlock[i].getValue() != newBlock[i].getValue()) { |
|
928 |
blocksDiffer = true; |
|
929 |
break; |
|
930 |
} |
|
931 |
} |
|
932 |
if (blocksDiffer) { |
|
933 |
updateMemoryCache(address, count, newBlock); |
|
934 |
getSession().dispatchEvent(new MemoryChangedEvent(memoryDMC, addresses), getProperties()); |
|
935 |
} |
|
936 |
rm.done(); |
|
937 |
} |
|
938 |
}); |
|
939 |
} |
|
940 |
} |
|
941 |
||
942 |
/** |
|
943 |
* {@inheritDoc} |
|
944 |
* @since 1.1 |
|
945 |
*/ |
|
946 |
public void flushCache(IDMContext context) { |
|
947 |
fCommandCache.reset(context); |
|
948 |
||
949 |
IMemoryDMContext memoryDMC = DMContexts.getAncestorOfType(context, IMemoryDMContext.class); |
|
950 |
if (fMemoryCaches.containsKey(memoryDMC)) { |
|
951 |
// We do not want to use the call to getMemoryCache() here. |
|
952 |
// This is because: |
|
953 |
// 1- if there is not an entry already , we do not want to automatically |
|
954 |
// create one, just to call reset() on it. |
|
955 |
// 2- if memoryDMC == null, we do not want to create a cache |
|
956 |
// entry for which the key is 'null' |
|
957 |
fMemoryCaches.get(memoryDMC).reset(); |
|
958 |
} |
|
959 |
} |
|
960 |
} |