Design Principles Introduction This chapter describes the principles that are common across the whole API. Lists Methods use java.util.List to return multiple objects. All lists are immutable and unsynchronised. For example, an ImageProcess may contain multiple instances of ManagedRuntime. To retrieve the ManagedRuntime instances, a call would be made to: List<ManagedRuntime> ImageProcess.getRuntimes(). Lists are sometimes used in the API to access a larger number of objects than would be used in most Java applications. For this reason, use of the java.util.List.toArray() method is discouraged in situations where there would be a large number of array elements. For example: JavaObject[] heapAsArray(JavaHeap heap) { return heap.getObjects().toArray(new JavaObject[0]); } would return an array with all of the objects in the heap, perhaps numbering in the hundreds of millions. This should be considered when implementing or calling the API. Type names Type names and signatures use the same format as JNI. Please see the JavaDoc for each method for the exact formats. The class java.util.Map.entry would be formatted like so: java/util/Map$Entry An multidimensional array java.util.Map.entry[][] is formatted like: [[Ljava/util/Map$Entry; A primitive array class for int[][] is formatted like so: [[I Memory and Identification Address space Memory in the API consists of a collection of flat (i.e. not segmented) address spaces represented by the ImageAddressSpace class. Implementations do not have to report any memory as being present in the snapshot. Memory sections The ImageSection interface is used to describe arbitrary areas of memory by returning a pointer (represented by a ImagePointer instance) and a size along with a name. ImageAddressSpace instances use them to describe what memory is mapped in a snapshot. They are also used to describe the memory layout of the entities that the API interfaces represent. This is normally done with methods such as List<ImageSection> getSections(). The actual ImageSection instances returned are implementation specific and it is acceptable for there to be none. It is expected that they will be used for: determining the memory occupancy of items. For example, the heap size could be derived from JavaHeap.getSections(). accessing structures in memory. For example, JavaObject.getSections() will return all of the ImageSection instance representing the memory a JavaObject occupies. With knowledge of how an object is laid out on the heap, it would be possible to retrieve more information than is retrieved presented by the API. Addresses and Identification The API uses the ImagePointer interface to identify objects returned by the API. ImagePointer represents an address in memory, and enables programs to access memory at that address and at offsets from that address. Given that not all implementations of the API allow access to memory, the addresses returned could be entirely artificial. When an ImagePointer is used as an address of an object from the API, it is up to the implementation to decide what it is actually pointing at. It is important though that objects of the same type have unique addresses. For example, JavaObject instances much each return an ImagePointer different from all other, but there may be instances of JavaClass that share the same address. ImagePointer allows access to memory using Java types, corrected for endianness. This means that only twos-complement values can be returned, apart from the char which is unsigned in Java. There is no conversion from the native platform's floating point formats. Floating point values are assumed to be stored as Java floating point types. Package Separation While aspects of the image API are used by the runtime.java API, the reverse will never occur. The image API should, in principle, be implementable standalone. For example, the following is allowed as it refers to the image package: ImageThread JavaThread.getImageThread() But the converse is not: JavaThread ImageThread.getJavaThread() Error Handling Implementations of the API should present data as accurate and complete as reasonably possible under any circumstances. The purpose of the API includes presenting the state of a running process, a Java Virtual Machine, when it encountered abnormal conditions. The most extreme of these situations is when the native code implementing the JVM itself has crashed. As such, there will be situations where information cannot be retrieved or may be incorrect. This should be regarded as normal. Both implementors of the API and those calling the API should code anticipating errors to be normal rather than exceptional conditions. Data is retrieved from the API from two types of methods. Those returning multiple items using generisised Lists and those returning items directly. The lists that are returned are expected to return all items that they can. When implementing lists, implementors should take care to: ensure that lists have a finite number of items. For example, a corrupted linked list may be corrupt or terminated incorrectly. The API implementation should detect this and terminate the list. process as little of the items being returned as possible. Better to continue reading the collection of items rather than fail on one item that is slightly wrong. For example, if the objects are being retrieved using the list from the following method call: List<JavaObject> JavaHeap.getObjects() ... then if one object fails to identify its type properly, it is expected that the list would return the JavaObject. Calls to that JavaObject would fail appropriately, such as to JavaClass JavaObject.getJavaClass(). Errors are reported on methods returning single items (i.e. not lists) using exceptions. There are two exceptions, both subclasses of DiagnosticException. The exceptions are: MemoryAccessException. This is thrown when an attempt has been made to access memory that is not present in the snapshot. CorruptDataException. This is thrown when the data used to form a response to the method call is incorrect. Optional and missing data There are circumstances where information cannot be supplied. Methods that throw the exception DataUnavailable will do so if the information is either not presentable by the implmentation of the API, or if it is not available for that particular snapshot. There are circumstances where null is returned by a method. These circumstances will be explicitly documented in the Javadoc. DataUnavailable is thrown when the API cannot return the requested data. null is returned when the data was never there to be returned. Methods that return java.util.List instances will always do so under all circumstances. Faked objects There are many possible implementations of a JavaTM Virtual Machine each of which can have various and different optimisations. Mapping a particular implementation to this API may require the creation of synthetic objects for entities which do not actually exist in the diagnostic artifact. An example of this would be array classes. These classes are never loaded by a Javatm Virtual Machine, they are constructed as and when they are necessary. It is conceivable that there would be no actual entities that could directly correspond with JavaClass instances. In circumstances like these the API implementor would have to create a JavaClass for the array class, as that is the only means the API has for identifying that objects type. Faked objects should be implemented carefully. For instance, if a faked JavaMethod is created, then the class it declares itself as belonging to should report it, otherwise inconsistencies can arise that could cause calling programs to fail. There should be no collisions between real and faked objects. Implementations should not be misleading. If a faked object has been created, then related fack objects should be kept to a minimum. For instance a faked JavaObject should not return ImageSection instances. Object identities All implementations should override the java.lang.Object.equals(Object) method when objects are not permanently cached and may need to be recreated. All API's should use equals to test object identity. The quantity of objects held within a diagnostic artifact normally means that it is impractical to keep an in-memory instance for everything. Therefore the API does not require that repeated calls to return a specific object will in fact return the same instance. The API allows for recreation of already requested objects The behaviour of equals against objects from different snapshots is not defined.