/******************************************************************************* * Copyright (c) 2000, 2005 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * QNX Software Systems - Initial API and implementation *******************************************************************************/ package org.eclipse.cdt.debug.mi.core.cdi; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Hashtable; import java.util.List; import java.util.Map; import org.eclipse.cdt.debug.core.cdi.CDIException; import org.eclipse.cdt.debug.core.cdi.model.ICDIMemoryBlock; import org.eclipse.cdt.debug.mi.core.MIException; import org.eclipse.cdt.debug.mi.core.MIFormat; import org.eclipse.cdt.debug.mi.core.MISession; import org.eclipse.cdt.debug.mi.core.cdi.model.MemoryBlock; import org.eclipse.cdt.debug.mi.core.cdi.model.Target; import org.eclipse.cdt.debug.mi.core.command.CommandFactory; import org.eclipse.cdt.debug.mi.core.command.MIDataReadMemory; import org.eclipse.cdt.debug.mi.core.event.MIEvent; import org.eclipse.cdt.debug.mi.core.event.MIMemoryChangedEvent; import org.eclipse.cdt.debug.mi.core.event.MIMemoryCreatedEvent; import org.eclipse.cdt.debug.mi.core.output.MIDataReadMemoryInfo; /** */ public class MemoryManager extends Manager { ICDIMemoryBlock[] EMPTY_MEMORY_BLOCKS = {}; Map blockMap; public MemoryManager(Session session) { super(session, true); blockMap = new Hashtable(); } synchronized List getMemoryBlockList(Target target) { List blockList = (List)blockMap.get(target); if (blockList == null) { blockList = Collections.synchronizedList(new ArrayList()); blockMap.put(target, blockList); } return blockList; } /** * This method will be call by the eventManager.processSuspended() every time the * inferior comes to a Stop/Suspended. It will allow to look at the blocks that * are registered and fired any event if changed. * Note: Frozen blocks are not updated. * */ public void update(Target target) { MISession miSession = target.getMISession(); List blockList = getMemoryBlockList(target); MemoryBlock[] blocks = (MemoryBlock[]) blockList.toArray(new MemoryBlock[blockList.size()]); List eventList = new ArrayList(blocks.length); for (int i = 0; i < blocks.length; i++) { if (! blocks[i].isFrozen()) { try { update(blocks[i], eventList); } catch (CDIException e) { } } } MIEvent[] events = (MIEvent[])eventList.toArray(new MIEvent[0]); miSession.fireEvents(events); } /** * update one Block. */ public BigInteger[] update(MemoryBlock block, List aList) throws CDIException { Target target = (Target)block.getTarget(); MISession miSession = target.getMISession(); MemoryBlock newBlock = cloneBlock(block); boolean newAddress = ! newBlock.getStartAddress().equals(block.getStartAddress()); BigInteger[] array = compareBlocks(block, newBlock); // Update the block MIDataReadMemoryInfo. block.setMIDataReadMemoryInfo(newBlock.getMIDataReadMemoryInfo()); if (array.length > 0 || newAddress) { if (aList != null) { aList.add(new MIMemoryChangedEvent(miSession, array)); } else { // fire right away. miSession.fireEvent(new MIMemoryChangedEvent(miSession, array)); } } return array; } /** * Compare two blocks and return an array of all _addresses_ that are different. * This method is not smart it always assume that: * oldBlock.getStartAddress() == newBlock.getStartAddress; * oldBlock.getLength() == newBlock.getLength(); * @return Long[] array of modified addresses. */ BigInteger[] compareBlocks (MemoryBlock oldBlock, MemoryBlock newBlock) throws CDIException { byte[] oldBytes = oldBlock.getBytes(); byte[] newBytes = newBlock.getBytes(); List aList = new ArrayList(newBytes.length); BigInteger distance = newBlock.getStartAddress().subtract(oldBlock.getStartAddress()); //IPF_TODO enshure it is OK here int diff = distance.intValue(); if ( Math.abs(diff) < newBytes.length) { for (int i = 0; i < newBytes.length; i++) { if (i + diff < oldBytes.length && i + diff >= 0) { if (oldBytes[i + diff] != newBytes[i]) { aList.add(newBlock.getStartAddress().add(BigInteger.valueOf(i))); } } } } return (BigInteger[]) aList.toArray(new BigInteger[aList.size()]); } /** * Use the same expression and length of the original block * to create a new MemoryBlock. The new block is not register * with the MemoryManager. */ MemoryBlock cloneBlock(MemoryBlock block) throws CDIException { Target target = (Target)block.getTarget(); String exp = block.getExpression(); int wordSize = block.getWordSize(); boolean little = target.isLittleEndian(); MIDataReadMemoryInfo info = createMIDataReadMemoryInfo(target.getMISession(), exp, (int)block.getLength(), wordSize); return new MemoryBlock(target, exp, wordSize, little, info); } /** * Post a -data-read-memory to gdb/mi. */ MIDataReadMemoryInfo createMIDataReadMemoryInfo(MISession miSession, String exp, int units, int wordSize) throws CDIException { CommandFactory factory = miSession.getCommandFactory(); MIDataReadMemory mem = factory.createMIDataReadMemory(0, exp, MIFormat.HEXADECIMAL, wordSize, 1, units, null); try { miSession.postCommand(mem); MIDataReadMemoryInfo info = mem.getMIDataReadMemoryInfo(); if (info == null) { throw new CDIException(CdiResources.getString("cdi.Common.No_answer")); //$NON-NLS-1$ } return info; } catch (MIException e) { throw new MI2CDIException(e); } } public ICDIMemoryBlock createMemoryBlock(Target target, String address, int units, int wordSize) throws CDIException { boolean little = target.isLittleEndian(); MIDataReadMemoryInfo info = createMIDataReadMemoryInfo(target.getMISession(), address, units, wordSize); ICDIMemoryBlock block = new MemoryBlock(target, address, wordSize, little, info); List blockList = getMemoryBlockList(target); blockList.add(block); MISession miSession = target.getMISession(); miSession.fireEvent(new MIMemoryCreatedEvent(miSession, block.getStartAddress(), block.getLength())); return block; } public MemoryBlock[] getMemoryBlocks(MISession miSession) { Session session = (Session)getSession(); Target target = session.getTarget(miSession); List blockList = getMemoryBlockList(target); return (MemoryBlock[]) blockList.toArray(new MemoryBlock[blockList.size()]); } public ICDIMemoryBlock[] getMemoryBlocks(Target target) throws CDIException { List blockList = getMemoryBlockList(target); return (ICDIMemoryBlock[]) blockList.toArray(new ICDIMemoryBlock[blockList.size()]); } public void removeAllBlocks(Target target) throws CDIException { ICDIMemoryBlock[] blocks = getMemoryBlocks(target); removeBlocks(target, blocks); } public void removeBlocks(Target target, ICDIMemoryBlock[] memoryBlocks) throws CDIException { List blockList = (List)blockMap.get(target); if (blockList != null) { blockList.removeAll(Arrays.asList(memoryBlocks)); } } }