| <html> |
| |
| <head> |
| <meta http-equiv="Content-Type" |
| content="text/html; charset=windows-1252"> |
| <title>Inside the Memory View: A Guide for Debug Providers</title> |
| <link href="../article.css" type="text/css" rel="stylesheet"> |
| </head> |
| |
| <body> |
| |
| <h1>Inside the Memory View: A Guide for Debug Providers</h1> |
| <div class="summary"> |
| <h2>Summary</h2> |
| <p>There are many programming languages that allow access and |
| discrete control of system memory. If you are a debug provider for one |
| of these languages, then you probably have a requirement to provide |
| support for debug-time memory inspection and manipulation. The Eclipse |
| debug framework provides a Memory View, along with an extensible |
| framework to simplify and standardize this task. This article introduces |
| the Memory View and describes how to add your own customized memory |
| support.</p> |
| |
| <div class="author">By Samantha Chan, IBM Toronto Lab</div> |
| <div class="copyright">Copyright © 2007 International |
| Business Machines Corp.</div> |
| <div class="date">September 7, 2007</div> |
| </div> |
| <div class="content"> |
| <h2>Introduction</h2> |
| <p>The Memory View is provided by the debug framework and allows |
| users to examine memory from their application. It is a flexible view |
| that allows debug implementers to present memory monitors in ways that |
| make sense to their users. You can display a memory monitor using any |
| type of SWT widget.</p> |
| <p>When the Memory View was first developed, one of the goals was to |
| support a diverse audience with very different needs and debug models. |
| It needed to be able to support everyone from developers who work on |
| huge mainframe systems to developers working on small embedded devices. |
| The needs of this vast range of developers are quite different. While it |
| may have made sense to present memory in a certain way for one |
| development environment, it may be completely confusing for developers |
| working on a different platform.</p> |
| <p>The solution was to provide a view and a framework that allows |
| debug implementers to define how their memory monitors are to be |
| presented in the user interface. This article will discuss this |
| framework and will show how one can add memory view support in a |
| debugger.</p> |
| <img src="images/memoryView.gif" align="center" |
| title="Figure 1 - Memory View" alt=""> |
| <p>Figure 1 - Memory View</p> |
| <p>Figure 1 shows the Memory View. The Memory View consists of three |
| panes. The pane on the left is called the <i>Monitors</i> pane and lists |
| all of the memory monitors that are monitored by the current debug |
| session. The two panes on the right are called the <i>Renderings</i> |
| panes. They show renderings of the selected memory monitor from the |
| Monitors pane. A memory rendering is a user interface representation of |
| a memory monitor. It shows the content of a memory monitor in a display |
| format. In Figure 1, the Memory View is currently displaying two |
| renderings for the memory monitor "&str". The <i>Hex</i> rendering |
| displays the memory monitor in the traditional table format. The <i>Hex |
| Tree</i> rendering displays the same memory monitor in a tree.</p> |
| <p>How can you, as a debug provider, use the Memory View? You can |
| add support for the Memory View in one of the following two ways:</p> |
| <dl> |
| <dt><b>Reuse the memory renderings provided by the debug |
| platform</b></dt> |
| <dd>The debug platform provides a set of table renderings for you |
| to reuse. There are four table renderings: Hex, ASCII, Signed Integer |
| and Unsigned Integer. You can simply add memory monitor support in your |
| debugger and reuse these renderings. These renderings can be customized |
| as needed. The <i>Hex</i> rendering from Figure 1 is an example of a |
| table rendering provided by the debug platform.</dd> |
| <dt><b>Create customized memory renderings</b></dt> |
| <dd>This method of adding Memory View support involves more work, |
| but allows you to control how memory monitors are presented to users. |
| In addition to providing memory monitor support in your debugger, you |
| can define new types of memory rendering. You can then display your |
| memory monitors using customized rendering types. The <i>Hex Tree</i> |
| rendering from Figure 1 is an example of a customized rendering.</dd> |
| </dl> |
| <p>This article will show you how to add memory monitor support in a |
| debugger. It will also explain how to reuse the table renderings |
| provided by the debug platform. In addition, this article will discuss |
| some of the customization points available in the table renderings.</p> |
| <h2>Running the Example</h2> |
| <p>This article comes with a sample project that will help you learn |
| how to add memory monitor support to a debugger. This sample consists of |
| a launch configuration, a set of debug model objects, and a debug engine |
| that simulates a system that is capable of monitoring memory.</p> |
| <p>To run this sample:</p> |
| <ol> |
| <li>Extract <a href="/articles/Article-MemoryView/example/memoryExample.zip">memoryExample.zip</a> to your plugins directory |
| (eclipse/plugins/)</li> |
| <li>Restart the workbench and create a "Debug Nothing" launch |
| configuration.</li> |
| <li>You do not need to enter anything in the launch configuration. |
| Simply click "Debug".</li> |
| </ol> |
| <p>Once launched, the Memory View will open automatically and the <b>Add |
| Memory Monitor</b> button (the button with the "+" from the Monitors Pane) |
| is enabled. Click that button and type any expression in the field. The |
| sample debugger will simulate evaluating this expression to an address |
| and allow you to add memory renderings to the view.</p> |
| <h2>The Memory View Framework</h2> |
| <p>In order to add memory monitor support in your debugger, you must |
| first have a basic understanding of the Memory View framework. Figure 2 |
| shows the Memory View framework.</p> |
| <img src="images/framework.gif" align="CENTER" |
| title="Figure 2 - Memory View Framework" alt=""> |
| <p>Figure 2 - Memory View Framework</p> |
| <dl> |
| <dt><i>Memory Block</i></dt> |
| <dd>A memory block represents a block of memory allocated in your |
| debugger. It is a debug model object that implements IMemoryBlock or |
| IMemoryBlockExtension. We will talk about the differences between |
| IMemoryBlock and IMemoryBlockExtension in the following sections.</dd> |
| |
| <dt><i>Memory Block Manager</i></dt> |
| <dd>The memory block manager is responsible for managing all of |
| the memory blocks in the workbench. You can use the memory block |
| manager to query the memory blocks that are currently present. In |
| addition, when a new memory block is added, the memory block manager |
| will broadcast a memory block added event. When a memory block is |
| removed, the manager will broadcast a memory block removed event. You |
| may register a listener to the memory block manager and receive |
| notifications for these events.</dd> |
| |
| <dt><i>Memory View</i></dt> |
| <dt></dt> |
| <dd>The Memory View is a memory rendering site. A memory rendering |
| site hosts the memory renderings contributed to the workbench. It also |
| provides a synchronization service that allows renderings from the same |
| rendering site to communicate with each other.</dd> |
| |
| <dt><i>Memory Rendering Type</i></dt> |
| <dd>A memory rendering type represents a kind of memory rendering. |
| It is contributed via plugin.xml. A memory rendering type specifies a |
| rendering type delegate, which is responsible for creating a memory |
| rendering in the Memory View. Memory rendering types are not tied to |
| any debug model - rather, they are defined globally in the workbench. |
| To reuse an existing rendering type, a model must create a memory |
| rendering binding that binds a rendering type to the model.</dd> |
| |
| <dt><i>Memory Rendering Binding</i></dt> |
| <dd>A memory rendering binding defines the types of renderings |
| that are valid for a debug model. You can contribute a memory rendering |
| binding via plugin.xml.</dd> |
| |
| <dt><i>Memory Rendering Manager</i></dt> |
| <dd>The memory rendering manager manages all rendering types and |
| rendering bindings in the workbench.</dd> |
| </dl> |
| <p>In order to support the Memory View, your debugger must be able |
| to create and return a memory block. The memory block is responsible for |
| getting memory from your debug engine/system. In addition, your debugger |
| must define the necessary memory rendering bindings. The following |
| sections will explain how to add memory block support in your debugger. |
| We will also talk about defining memory rendering bindings and reuse the |
| memory renderings provided by the debug platform.</p> |
| <h2>Adding Memory Monitor Support in a Debugger</h2> |
| <p>To add memory monitor support, your debug model needs to |
| implement two key interfaces:</p> |
| <ol> |
| <li>Your debug target must implement <b>IMemoryBlockRetrieval</b> |
| or <b>IMemoryBlockRetrievalExtension</b>. IMemoryBlockRetrieval and its |
| extension, allow the user interface to ask your debug target for a |
| memory block when needed.</li> |
| <li>Your debug model must implement <b>IMemoryBlock</b> or its |
| extension <b>IMemoryBlockExtension</b>. IMemoryBlock and its extension |
| represent a memory monitor in your debugger. The user interface |
| communicates with your debug model through this interface when it needs |
| to retrieve memory to populate the view.</li> |
| </ol> |
| |
| <h3>Implementing IMemoryBlockRetrieval</h3> |
| <p>A memory block retrieval allows the user interface to request a |
| memory block from your debugger when needed. It is usually implemented |
| by a debug target. IMemoryBlockRetrieval requires that you implement |
| these two methods:</p> |
| <pre>
|
| IMemoryBlockRetrieval:
|
| <img src="images/tag_1.gif" height="13" width="24" align="CENTER" alt=""> public boolean supportsStorageRetrieval();
|
| <img src="images/tag_2.gif" height="13" width="24" align="CENTER" alt=""> IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException;
|
| </pre> |
| <p>The supportsStorageRetrieval() method <img src="images/tag_1.gif" |
| height="13" width="24" align="CENTER" alt=""> allows the user |
| interface to query if your debug target supports memory block retrieval. |
| If it returns true, then the "Add Memory Monitor" actions in the Memory |
| View will be enabled. Otherwise, the actions will be permanently |
| disabled.</p> |
| <p>The getMemoryBlock() method <img src="images/tag_2.gif" |
| height="13" width="24" align="CENTER" alt=""> allows the user |
| interface to request a memory block from your debug target. When called, |
| your debug target must create and return a memory block based on the |
| given start address and length. If there is any problem creating the |
| memory block, your debug target must report the error by throwing a |
| debug exception.</p> |
| <p>IMemoryBlock and IMemoryBlockRetrieval allow your users to create |
| a simple memory monitor with very basic functionality. For example, by |
| implementing IMemoryBlock, you are limiting your user to looking at |
| memory within a certain range. Your users cannot examine memory that is |
| beyond the range defined by the start address and the length of the |
| memory block.</p> |
| <p>To provide more advanced functionality to your users and to |
| represent a more complicated system, your debug model must implement the |
| IMemoryBlockRetrievalExtension and its counterpart, |
| IMemoryBlockExtension. The IMemoryBlockRetrievalExtension interface |
| requires your debug model to implement an extra method:</p> |
| <pre>
|
| IMemoryBlockRetrievalExtension:
|
| <img src="images/tag_3.gif" height="13" width="24" align="CENTER" alt=""> public IMemoryBlockExtension getExtendedMemoryBlock(String expression, Object context) throws DebugException;
|
| </pre> |
| <p>Action enablements in the Memory View will still be determined by |
| the supportsStorageRetrieval() <img src="images/tag_1.gif" height="13" |
| width="24" align="CENTER" alt=""> method from |
| IMemoryBlockRetrieval. However, when a memory block is required, the |
| user interface will, through the debug target, request |
| IMemoryBlockExtenson via the IMemoryBlockRetrievalExtension interface. |
| When called, the getExtendedMemoryBlock() method <img |
| src="images/tag_3.gif" height="13" width="24" align="CENTER" alt=""> |
| must convert the given expression to an address and return an |
| IMemoryBlockExtension. If there are any problems creating the requested |
| memory block, the debug target must throw a debug exception to report |
| this error to the Memory View.</p> |
| <h4>Example:</h4> |
| <p>SampleDebugTarget from the example plug-in implements |
| IMemoryBlockRetrievalExtension. This is how it implements the |
| getExtendedMemoryBlock() method:</p> |
| <pre>
|
| public IMemoryBlockExtension getExtendedMemoryBlock(String expression, Object context) throws DebugException {
|
|
|
| // ask debug engine for an address
|
| <img src="images/tag_4.gif" height="13" width="24" align="CENTER" alt=""> BigInteger address = getEngine().evaluateExpression(expression, context);
|
|
|
| // if address can be evaluated to an address, create memory block
|
| if (address != null)
|
| {
|
| <img src="images/tag_5.gif" height="13" width="24" align="CENTER" alt=""> IMemoryBlockExtension memoryBlock = new SampleMemoryBlock(this, expression, address);
|
| fMemoryBlocks.add(memoryBlock);
|
|
|
| <img src="images/tag_6.gif" height="13" width="24" align="CENTER" alt=""> return memoryBlock;
|
| }
|
| // otherwise throw debug exception
|
| IStatus status = new Status(IStatus.ERROR, "example.debug.memoryview", 0, "Expression cannot be evaluated to an address", null);
|
| DebugException exception = new DebugException(status);
|
| <img src="images/tag_7.gif" height="13" width="24" align="CENTER" alt=""> throw exception;
|
| }
|
| </pre> |
| <p>When there is a request to create a memory block, the debug |
| target <img src="images/tag_4.gif" height="13" width="24" align="CENTER" |
| alt=""> passes the request to the engine to evaluate the given |
| expression to a valid address. If the address can be evaluated |
| successfully, the target <img src="images/tag_5.gif" height="13" |
| width="24" align="CENTER" alt="">creates and returns a <i>SampleMemoryBlock</i>, |
| which implements IMemoryBlockExtension. If the expression cannot be |
| evaluated successfully, the debug target throws a debug exception. <img |
| src="images/tag_7.gif" height="13" width="24" align="CENTER" alt=""></p> |
| |
| <h3>So, what should I implement? <br> |
| IMemoryBlockRetrieval vs. IMemoryBlockRetrievalExtension</h3> |
| <p>IMemoryBlockRetrieval and IMemoryBlock interfaces were created in |
| Eclipse 2.0. They are a set of simple APIs that allows a debug target to |
| create a simple memory block based on a given address and length. The |
| resulting memory block is static, and only allows its caller to retrieve |
| memory from the memory block as a whole. As a result, when an |
| IMemoryBlock is displayed in a rendering, users will not be able to |
| examine memory beyond the range defined by the memory block. Since these |
| interfaces require the implementation of a small number of simple |
| methods, using them is the easier option. However, this option poses |
| some limitations on your users.</p> |
| <p>IMemoryBlockRetrievalExtension and IMemoryBlockExtension were |
| introduced in Eclipse 3.1. They provide a richer set of methods to allow |
| the user interface to obtain more information about the target system. |
| Because IMemoryBlockRetrievalExtension is capable of creating a memory |
| block from an expression and a debug context, users are not forced to |
| enter an address when they create a memory monitor. Instead, the user |
| can simply type in an expression (for example a variable name or a |
| register name) to create a memory monitor. In addition, the resulting |
| memory block (an IMemoryBlockExtension) is more dynamic and allow its |
| callers to request memory from any address. This allows for dynamic |
| scrolling in the table rendering and the user can examine memory at any |
| valid location in a system.</p> |
| <p>The following table summarizes the differences between |
| IMemoryBlockRetrieval and IMemoryBlockRetrievalExtension.</p> |
| <table width="100%" border="1"> |
| <tr> |
| <th align="left">IMemoryBlockRetrieval</th> |
| <th align="left">IMemoryBlockRetrievalExtension</th> |
| </tr> |
| <tr> |
| <td>Creates IMemoryBlock – a basic static memory block. Does not |
| offer as much functionality, but easier to implement.</td> |
| <td>Creates IMemoryBlockExtension – provides much more |
| functionality. More difficult to implement.</td> |
| </tr> |
| <tr> |
| <td>User must enter an address and length to create a memory |
| monitor.</td> |
| <td>User can enter an arbitrary expression when creating a memory |
| monitor (e.g. variable name).</td> |
| </tr> |
| <tr> |
| <td>Content of the resulting memory monitor must be retrieved as |
| a whole and dynamic scrolling is not allowed.</td> |
| <td>Resulting memory monitor can retrieve memory from any valid |
| location, which enables dynamic scrolling in table renderings.</td> |
| </tr> |
| </table> |
| <p>The next section discusses the differences between IMemoryBlock |
| and IMemoryBlockExtension in more detail.</p> |
| |
| <h3>Implementing IMemoryBlock / IMemoryBlockExtension</h3> |
| <p>A memory block represents a memory monitor in a debug model. It |
| allows the user interface to request a block of memory from the debug |
| model for populating the view. IMemoryBlock is an easier interface to |
| implement as it has fewer methods. However, this interface was designed |
| with a few assumptions about the target system. IMemoryBlock does not |
| provide as much flexibility or information for the user interface to |
| present.</p> |
| |
| <p>IMemoryBlockExtension is a more complicated interface to |
| implement as it requires your debug implementation to be more flexible |
| and provide more information about the target system. However, it allows |
| the user interface to provide a richer set of features for memory |
| inspection.</p> |
| <h4>IMemoryBlock</h4> |
| <p>IMemoryBlock requires the following methods to be implemented:</p> |
| <pre>
|
| <img src="images/tag_1.gif" alt=""> public long getStartAddress();
|
| public long getLength();
|
| <img src="images/tag_2.gif" alt=""> public byte[] getBytes() throws DebugException;
|
| public void setValue(long offset, byte[] bytes) throws DebugException;
|
| </pre> |
| <p><i>Fixed Length Memory Block</i><br> |
| IMemoryBlock is a fixed-length memory monitor. The range of memory to |
| monitor is defined by a start address and a length. If you look at the <img |
| src="images/tag_2.gif" alt=""> getBytes() method, callers to this |
| interface can only retrieve memory from this memory block as a whole. |
| Callers are not able to ask for memory from an arbitrary location. As a |
| result, when this type of memory block is displayed in a rendering, it |
| does not allow its users to scroll beyond the range defined by the start |
| address and its length.</p> |
| |
| <p><i>32-bit-Only Addressing Support</i><br> |
| IMemoryBlock only supports systems with address sizes of 32-bit or less. |
| <img src="images/tag_1.gif" alt=""> The address in a memory block |
| is represented by the <i>long</i> data type. As a result, an address can |
| only be represented by the largest value that a long can represent. If |
| your target system supports addresses larger than 32-bit, (for example, |
| 64-bit address), then you cannot use IMemoryBlock to represent the |
| target system.</p> |
| |
| <p><i>Byte-Size-Only Addressable Unit Support</i><br> |
| IMemoryBlock assumes that the target system has byte-size addressable |
| units. Since the getBytes() method returns an array of bytes and the |
| length of the memory block is defined as the number of bytes in the |
| memory block, IMemoryBlock can only represent a target system whose |
| minimum addressable size is 8-bit. On some systems, the minimum |
| addressable unit size is larger than 8-bit. Some systems have a 16-bit |
| addressable size, while others can have a 32-bit addressable size. |
| IMemoryBlock is not capable of representing these systems.</p> |
| |
| <p><i>Does Not Support Endianness</i><br> |
| IMemoryBlock does not provide methods for callers to query the |
| endianness of the target system. As a result, it makes rendering memory |
| to numbers difficult.</p> |
| <h4>IMemoryBlockExtension</h4> |
| <p>IMemoryBlockExtension requires that these methods are |
| implemented:</p> |
| <pre>
|
| <img src="images/tag_1.gif" alt=""> public BigInteger getBigBaseAddress() throws DebugException;
|
| <img src="images/tag_1.gif" alt=""> public int getAddressSize() throws DebugException;
|
| <img src="images/tag_2.gif" alt=""> public BigInteger getMemoryBlockStartAddress() throws DebugException;
|
| <img src="images/tag_2.gif" alt=""> public BigInteger getMemoryBlockEndAddress() throws DebugException;
|
| <img src="images/tag_3.gif" alt=""> public int getAddressableSize() throws DebugException;
|
| <img src="images/tag_4.gif" alt=""> public MemoryByte[] getBytesFromAddress(BigInteger address, long units) throws DebugException;
|
| <img src="images/tag_4.gif" alt=""> public MemoryByte[] getBytesFromOffset(BigInteger unitOffset, long addressableUnits) throws DebugException
|
| </pre> |
| <p><i>Support Any Address Size</i><br> |
| IMemoryBlockExtension can handle platforms that have an address size |
| larger than 32-bit. Since all addresses are represented by a BigInteger |
| <img src="images/tag_1.gif" alt="">, there is no limit on address |
| size.</p> |
| <p><i>Boundary on the Memory Block</i><br> |
| IMemoryBlockExtension allows you to specify a boundary on a memory |
| block. <img src="images/tag_2.gif" alt=""> Once a boundary is |
| specified, users cannot examine memory that is beyond the boundary. This |
| allows you to prevent your users from going into memory regions that |
| they are not supposed to access.</p> |
| <p><i>Support for Multi-Byte Addressable Unit</i><br> |
| On some systems, the size of a minimum addressable unit is larger than |
| one byte. <img src="images/tag_3.gif" alt=""> |
| IMemoryBlockExtension allows debug providers to specify the size of |
| addressable units. A memory rendering can make use of this information |
| to present the content of its memory block correctly.</p> |
| <p><i>Allow Caller to Retrieve Memory Dynamically</i><br> |
| <img src="images/tag_4.gif" alt=""> IMemoryBlockExtension allows |
| its callers to retrieve any amount of memory from an arbitrary address |
| location. This means that users can examine memory from any valid |
| location on the system. In addition, this results in performance |
| improvements since the view does not have to retrieve the entire content |
| of the memory block as a whole. It can request memory from the memory |
| block on demand.</p> |
| <p><i>Allows Models to Provide Additional Information</i><br> |
| When asked to retrieve memory, IMemoryBlockExtension returns an array of |
| MemoryByte instead of an array of byte. <img src="images/tag_4.gif" |
| alt=""> A MemoryByte is a wrapper class for a byte. It consists |
| of a value and an attribute. The attribute field allows you to provide |
| additional information about the system at a particular byte. For |
| example, you can indicate if a byte of memory is writable by enabling |
| the <i>writable</i> attribute in a MemoryByte.</p> |
| <p>When deciding which set of interfaces to implement, you have to |
| determine if your users require the additional features provided by |
| IMemoryBlockExtension. The major benefits of implementing |
| IMemoryBlockExtension is in its ability to retrieve memory dynamically, |
| thereby improving better performance during memory inspection. It is |
| more flexible and allows your debug model to provide much more |
| information about the application being debugged.</p> |
| <h4>Example:</h4> |
| <p>SampleMemoryBlock from the example plug-in implements |
| IMemoryBlockExtension. The base address of the memory block is used in |
| various places. It is one of the most important properties of a memory |
| block. It is used for generating the labels of the rendeirngs and to |
| calculate where a rendering should begin asking for memory. The |
| getBigBaseAddress() method is implemented in SampleMemoryBlock as:</p> |
| <pre>
|
| public BigInteger getBigBaseAddress() throws DebugException {
|
| <img src="images/tag_1.gif" alt=""> fBaseAddress = fDebugTarget.getEngine().evaluateExpression(fExpression, null);
|
| return fBaseAddress;
|
| }
|
| </pre> |
| <p>When it is asked to return the base address of the memory block, |
| <img src="images/tag_1.gif" alt=""> SampleMemoryBlock re-evaluates |
| its expression and returns the new base address. If there is an error |
| with the evaluation, the memory block throws a debug exception. To |
| optimize this code and avoid interactions with your debug engine, it is |
| desirable to cache the value of the base address and only update its |
| value when necessary.</p> |
| <p>The table renderings provided by the debug platform request |
| content from the memory block using the getBytesFromAddress() method. |
| SampleMemoryBlock implements this method as:</p> |
| <pre>
|
| public MemoryByte[] getBytesFromAddress(BigInteger address, long length) throws DebugException {
|
| try {
|
| <img src="images/tag_1.gif" alt=""> MemoryByte[] bytes = new MemoryByte[(int)length * fDebugTarget.getEngine().getAddressableSize()];
|
| BigInteger addressCnt = address;
|
| int lengthCnt = (int)length;
|
| int i=0;
|
|
|
| // asks engine to get bytes from address
|
| <img src="images/tag_2.gif" alt=""> MemoryByte[] engineBytes = fDebugTarget.getEngine().getBytesFromAddress(addressCnt, lengthCnt);
|
| System.arraycopy(engineBytes, 0, bytes, i, engineBytes.length);
|
|
|
| // pad with dummy memory if engine did not return enough memory
|
| :
|
| :
|
|
|
| <img src="images/tag_3.gif" alt=""> return bytes;
|
| } catch (RuntimeException e) {
|
| <img src="images/tag_4.gif" alt=""> throw e;
|
| }
|
| }
|
| </pre> |
| <p>When the request for memory is made, <img src="images/tag_1.gif" |
| alt="">the memory block first creates a buffer of MemoryByte to |
| hold the memory returned by the engine. <img src="images/tag_2.gif" |
| alt="">Then the memory block asks the engine for the required |
| amount of memory. The memory block copies the memory to the buffer to |
| avoid others from modifying the memory from the engine accidentally. <img |
| src="images/tag_3.gif" alt="">The memory block then returns the |
| array of MemoryByte to its caller. <img src="images/tag_4.gif" alt="">If |
| there is an error retrieving the memory, the engine throws an exception |
| which gets re-thrown by the memory block.</p> |
| <p>If you want users to be able to modify the memory block, you must |
| implement the supportsValueModification() method.</p> |
| <pre>
|
| public boolean supportsValueModification() {
|
| return fDebugTarget.getEngine().supportsValueModification(this);
|
| }
|
| </pre> |
| <p>If memory modification is supported by your platform, this method |
| should return true. Otherwise, this method should return false. If the |
| supportsValueModification() method returns false, the edit action in the |
| renderings will be disabled.</p> |
| <p>When the user has finished modifying memory from the table |
| rendering, the rendering will ask its memory block to modify memory |
| using the setValues() method. Your memory block should then ask your |
| debug engine to modify memory at the given location. If there is an |
| error, a debug exception must be thrown. SampleMemoryBlock implements |
| the setValues() method as:</p> |
| <pre>
|
| public void setValue(BigInteger offset, byte[] bytes) throws DebugException {
|
| try {
|
| // ask the engine to modify memory at specified address
|
| <img src="images/tag_1.gif" alt=""> fDebugTarget.getEngine().setValue(fBaseAddress.add(offset), bytes);
|
| <img src="images/tag_2.gif" alt=""> fireContentChangeEvent();
|
| } catch (RuntimeException e) {
|
| IStatus status = new Status(IStatus.ERROR, "example.debug.memoryview", 0,
|
| "Failed to edit.", e);
|
| DebugException exception = new DebugException(status);
|
| <img src="images/tag_3.gif" alt=""> throw exception;
|
| }
|
| }
|
| </pre> |
| <p>When requested to change memory, <img src="images/tag_1.gif" |
| alt="">the memory block asks the debug engine to change the value |
| at the specified offset. <img src="images/tag_2.gif" alt="">After |
| the values are changed, the memory block must issue a content change |
| debug event. The table renderings listen for change events from the |
| memory block and will update accordingly. <img src="images/tag_3.gif" |
| alt="">If there are problems when modifying the memory block, it |
| catches the error from the engine and throws a debug exception.</p> |
| <p>When a user removes a memory block, the Memory View framework |
| calls the IMemoryBlockExtension.dispose() method. This is a chance for |
| your debugger to perform the necessary clean-up when a memory block is |
| removed. For example, if your memory block is caching data, you may want |
| to clean the cache when the dispose() method is called. If your engine |
| is monitoring memory at a certain location, you may want to notify it |
| that the memory monitor has been removed and that it should stop |
| monitoring. In our example, memory monitors are tracked by the debug |
| target. The dispose() method gives us a chance to remove the memory |
| block from our debug target. SampleMemoryBlock implements the dispose() |
| method as:</p> |
| <pre>
|
| public void dispose() throws DebugException {
|
| // remove this memory block from debug target
|
| fDebugTarget.removeMemoryBlock(this);
|
| }
|
| </pre> |
| <h2>Reusing Table Renderings</h2> |
| <p>Now that you have implemented memory monitor support in your |
| debug model, you are ready to display the memory blocks using the table |
| renderings provided by the debug platform. The debug platform provides |
| four rendering types:</p> |
| <table width="100%" border="1"> |
| <tr> |
| <th align="left">Name</th> |
| <th align="left">Rendering Type Id</th> |
| <th align="left">Description</th> |
| </tr> |
| <tr valign="top"> |
| <td width="15%">Hex</td> |
| <td>org.eclipse.debug.ui.rendering.raw_memory</td> |
| <td>Renders memory as raw memory. It does not take endianness of |
| the platform into account. Content of the memory block is shown in |
| hexadecimal values.</td> |
| </tr> |
| <tr valign="top"> |
| <td width="15%">ASCII</td> |
| <td>org.eclipse.debug.ui.rendering.ascii</td> |
| <td>Renders memory into ASCII strings. The codepage used to |
| convert memory into ASCII strings is defined by a code page preference |
| that can be found in the Memory View. The default code page is CP1252.</td> |
| </tr> |
| <tr valign="top"> |
| <td width="15%">Signed Integer</td> |
| <td>org.eclipse.debug.ui.rendering.signedint</td> |
| <td>Renders memory into signed integers. If the endianness of the |
| memory block is known, the rendering renders memory according to the |
| endianness of the memory block. Otherwise, the rendering defaults to |
| render memory as big endian. Users can switch between big and little |
| endian when working with this rendering.</td> |
| </tr> |
| <tr valign="top"> |
| <td width="15%">Unsigned Integer</td> |
| <td>org.eclipse.debug.ui.rendering.unsignedint</td> |
| <td>Renders memory into unsigned integers. If the endianness of |
| the memory block is known, the rendering renders memory according to |
| the endianness of the memory block. Otherwise, the rendering defaults |
| to render memory as big endian. Users can switch between big and |
| little endian when working with this rendering.</td> |
| </tr> |
| </table> |
| <p>To reuse these renderings, you must create a memory rendering |
| binding in your plugin.xml file. A memory rendering binding defines a |
| set of rendering types that are valid for your memory block. It lists |
| the rendering types that you want your users to see when working with |
| your memory blocks. In our example, SampleMemoryBlock is bound to all |
| four rendering types that are provided by the debug platform. Its memory |
| rendering binding is defined as:</p> |
| <pre>
|
| <extension point="org.eclipse.debug.ui.memoryRenderings">
|
| <renderingBindings
|
| <img src="images/tag_2.gif" alt=""> defaultIds="org.eclipse.debug.ui.rendering.raw_memory,org.eclipse.debug.ui.rendering.signedint"
|
| <img src="images/tag_3.gif" alt=""> primaryId="org.eclipse.debug.ui.rendering.raw_memory"
|
| <img src="images/tag_1.gif" alt=""> renderingIds="org.eclipse.debug.ui.rendering.raw_memory,
|
| org.eclipse.debug.ui.rendering.ascii,
|
| org.eclipse.debug.ui.rendering.signedint,
|
| org.eclipse.debug.ui.rendering.unsignedint">
|
| <enablement>
|
| <img src="images/tag_4.gif" alt=""> <instanceof value="example.debug.memoryview.internal.core.SampleMemoryBlock"/>
|
| </enablement>
|
| </renderingBindings>
|
| </extension>
|
| </pre> |
| <p>A rendering binding consists of three attributes:</p> |
| <p><i>renderingIds</i> - defines a list of rendering types that are |
| valid for your memory block. <img src="images/tag_1.gif" alt=""> |
| In this example, the rendering binding specifies that Hex, ASCII, Signed |
| Integer, and Unsigned Integer are all applicable for SampleMemoryBlock.</p> |
| <p><i>defaultIds</i> - defines a list of renderings that should be |
| created by default whenever the specified memory block is added to the |
| workbench. <img src="images/tag_2.gif" alt=""> For example, if you |
| want the the Hex and Signed Integer rendering to be created by default |
| whenever your users add a memory monitor, you should specify their |
| rendering type identifiers in the <i>defaultIds</i> attribute.</p> |
| <p><i>primaryId</i> - defines which one of the default renderings |
| should be hosted in the primary rendering pane.</p> |
| <p>The Memory View consists of two rendering panes for displaying |
| memory renderings. The pane on the left is considered the primary |
| rendering pane. The pane on the right is considered the secondary |
| rendering pane. The primary rendering pane is opened by default when the |
| Memory View is opened. User has to open the secondary pane manually when |
| needed.</p> |
| <p>When multiple renderings are specified in the <i>defaultIds</i> |
| attribute, it is not clear which one should be opened in the primary |
| rendering pane. As shown in our example, <img src="images/tag_2.gif" |
| alt=""> the sample lists both the <i>Hex</i> and <i>Signed |
| Integer</i> rendering as its default renderings. It is unclear if both |
| renderings should be created in the primary rendering pane or if each |
| rendering pane should host one of the default renderings.</p> |
| <p>To solve this problem, a memory rendering binding defines a <img |
| src="images/tag_3.gif" alt=""> <i>primaryId</i> attribute. There |
| can only be one primary rendering type in a memory rendering binding |
| extension. The rendering listed in <i>primaryId</i> will be created in |
| the primary rendering pane by default. The rest of the renderings listed |
| in <i>defaultIds</i> will be created in the secondary rendering pane. In |
| this example, the <i>Hex</i> rendering type is listed as the primary |
| rendering type. As a result, when a SampleMemoryBlock is created, the <i>Hex</i> |
| rendering will always be displayed in the rendering pane on the left. |
| The <i>Signed Integer</i> rendering will be displayed in the rendering |
| pane on the right.</p> |
| <p>A memory rendering binding is associated with a type of memory |
| block using expression enablement. <img src="images/tag_4.gif" alt=""> |
| As shown in the example, the memory rendering binding is tied to |
| SampleMemoryBlock. When SampleMemoryBlock is added to the workbench, |
| this memory rendering binding will be used to determine which renderings |
| the user can create. It will also be used to determine what renderings |
| are to be created by default.</p> |
| <div class="note"><img src="images/tryit.gif" width="61" height="13" alt=""> |
| To see how a memory rendering binding works, remove one of the rendering |
| identifiers from the "renderingIds" attribute in the example plugin. |
| When a rendering is not listed in "renderingIds", the rendering will |
| become unavailable to users.</div> |
| <h2>Customizing Table Renderings</h2> |
| <p>Almost everything within a table rendering can be customized. You |
| can customize the decoration of the label, add different icons in each |
| cell, render the content with a different color, or even change the |
| column or row headings in the rendering. The following table is a |
| summary of all customization points available for table renderings. It |
| also describes what adapters you would need to provide in order to |
| customize the table renderings.</p> |
| <table width="100%" border="1"> |
| <tr> |
| <th align="left">Attribute</th> |
| <th align="left">Description</th> |
| <th align="left">Adapter</th> |
| </tr> |
| <tr> |
| <td>Text in a cell</td> |
| <td>The text displayed in each cell.</td> |
| <td>ILabelProvider</td> |
| </tr> |
| <tr> |
| <td>Icon in a cell</td> |
| <td>The icon displayed in each cell.</td> |
| <td>ILabelProvider</td> |
| </tr> |
| <tr> |
| <td>Color</td> |
| <td>The foreground and background color in each cell.</td> |
| <td>IColorProvider</td> |
| </tr> |
| <tr> |
| <td>Column Heading</td> |
| <td>The column headings displayed in the table rendering.</td> |
| <td>IMemoryBlockTablePresentation</td> |
| </tr> |
| <tr> |
| <td>Row Heading</td> |
| <td>The text displayed in the address column.</td> |
| <td>IMemoryBlockTablePresentation</td> |
| </tr> |
| <tr> |
| <td>Label or Icon of the rendering</td> |
| <td>The label displayed in the tab item.</td> |
| <td>ILabelDecorator</td> |
| </tr> |
| </table> |
| <p>Customization points of the table rendering are exposed using the |
| IAdaptable interface. To customize an attribute in a table rendering, |
| your memory block must provide the corresponding adapter and return it |
| when the getAdapter() method is called. For example, to customize the |
| text in each table rendering cell, your memory block will have to |
| provide an ILabelProvider adapter and return the adapter from the |
| getAdapter() method. Before text can be rendered in a table rendering, |
| the rendering requests an ILabelProvider from its memory block. If an |
| adapter is returned, the rendering relies on the adapter to provide the |
| text for each of the cells.</p> |
| |
| <p>Your adapter will be given an object of type |
| MemoryRenderingElement when called. MemoryRenderingElement describes the |
| memory that is currently being rendered by the table rendering. It |
| provides the following information:</p> |
| <ul> |
| <li>The rendering that is trying to render this |
| MemoryRenderingElement.</li> |
| <li>An array of MemoryByte to be rendered.</li> |
| <li>The address where the memory is located.</li> |
| </ul> |
| <p>Your adapter can make use of the provided information and render |
| memory differently.</p> |
| <h4>Example</h4> |
| <p>Our sample adapter renders memory differently if memory is |
| read-only. Our sample adapter displays read-only memory in blue. If the |
| memory is writable, then we use the default coloring schemes provided by |
| the table rendering. To customize the colors in the table rendering, we |
| need to provide an IColorProvider adapter from SampleMemoryBlock:</p> |
| <pre>
|
| public Object getAdapter(Class adapter) {
|
| if (adapter.equals(IMemoryBlockRetrievalExtension.class))
|
| return getDebugTarget();
|
|
|
| if (adapter == IColorProvider.class)
|
| {
|
| <img src="images/tag_1.gif" alt=""> return SampleModelPresentation.getSampleModelPresentation();
|
| }
|
| return super.getAdapter(adapter);
|
| }
|
| </pre> |
| <p>When the sample memory block is asked to return an adapter to |
| IColoProvider adapter, <img src="images/tag_1.gif" alt=""> the |
| memory block returns its model presentation. The model presentation |
| implements the IColorProvider interface.</p> |
| <p>SampleModelPresesntation.getForeground() is implemented as:</p> |
| <pre>
|
| public Color getForeground(Object element) {
|
|
|
| <img src="images/tag_1.gif" alt=""> if (element instanceof MemoryRenderingElement)
|
| {
|
| MemoryRenderingElement elm = (MemoryRenderingElement) element;
|
| MemoryByte[] bytes = elm.getBytes();
|
| <img src="images/tag_2.gif" alt=""> if (!bytes[0].isWritable())
|
| {
|
| <img src="images/tag_3.gif" alt=""> return blue;
|
| }
|
| }
|
| <img src="images/tag_4.gif" alt=""> return null;
|
| }
|
| </pre> |
| <p>When the getForeground() method is called, <img |
| src="images/tag_1.gif" alt=""> the model presentation checks to |
| see if the caller is asking for the foreground color of a |
| MemoryRenderingElement. <img src="images/tag_2.gif" alt=""> If it |
| is, the model presentation checks if the memory being rendered is |
| writable. This example assumes that if the first byte is writable, then |
| the entire cell is writable. If the memory is not writable, <img |
| src="images/tag_3.gif" alt=""> then the model presentation |
| returns <i>blue</i> as the foreground color. <img src="images/tag_4.gif" |
| alt=""> Otherwise, the model presentation returns null, which |
| implies that default foreground color should be used.</p> |
| <div class="note"><img src="images/tryit.gif" width="61" height="13" alt=""> |
| The sample debug engine produces read-only memory from 0xAB123456 to |
| 0xAB123556. To see how the adapter customizes the foreground color of |
| read-only memory, go to 0xAB123456 and you should see that that region |
| is rendered in blue.</div> |
| <h2>Wrapping Up</h2> |
| <p>In this article, you have learned how you can support monitoring |
| memory using the Memory View framework. To add memory monitor support, |
| your debug adapter must implement the IMemoryBlockRetrieval and |
| IMemoryBlock interfaces. If you would like to optimize performance and |
| provide more advanced features in your memory renderings, your debug |
| adapter must implement the IMemoryBlockRetreivalExtension and |
| IMemoryBlockExtension interfaces. You must then determine which |
| renderings you would like to provide. To show memory blocks in a |
| rendering, you must bind your memory blocks to a rendering type. |
| Finally, the table renderings are highly flexible - so you can customize |
| the table renderings by providing the appropriate adapters.</p> |
| <p>Showing memory monitors in a table format is just one way of |
| looking at memory. If table renderings are not sufficient for your |
| users, you can always be creative and define a customized rendering |
| type. The possibilities are endless.</p> |
| </div> |
| </body> |
| </html> |