| package xdc.runtime; |
| |
| import xdc.rov.MemoryImage; |
| import xdc.rov.ISymbolTable; |
| import xdc.rov.TargetDecoder; |
| import xdc.rov.TargetType; |
| |
| import xdc.rta.HostEvent; |
| import xdc.rta.MetaData; |
| import xdc.rta.IEventMetaData; |
| |
| import java.util.Arrays; |
| import java.util.Vector; |
| import java.util.HashMap; |
| |
| import org.w3c.dom.Element; |
| |
| /* |
| * ======== LoggerBufDecoder ======== |
| * Decodes whole LoggerBuf buffers and returns an array of HostEvent objects. |
| * |
| * This decoder is shared between ROV and RTA. In ROV, it is used to optimize |
| * the performance of the xdc.runtime.LoggerBuf 'Records' ROV view. In RTA, |
| * it's used to support the stop mode collection of records from LoggerBuf |
| * instances. |
| * |
| * This decoder contains some APIs and fields which are used by StopMode RTA |
| * but not by ROV. This is because in ROV the LoggerBuf 'Records' view |
| * performs the memory reads and passes the buffers into the decoder, but in |
| * RTA the LoggerBufDecoder is responsible for reading the buffers. |
| * |
| * ROV has easy access to all of the LoggerBuf instance state structures and |
| * uses the 'entryArr' and 'numEntries' fields to locate and read in the |
| * target buffers. The LoggerBuf 'Records' ROV view performs the memory read |
| * and passes the buffer into the LoggerBufDecoder to decode. |
| * |
| * In RTA, the LoggerBufDecoder is responsible for locating and reading the |
| * memory buffers. The 'initStopMode' API is called to initialize the |
| * LoggerBufDecoder to locate all of the buffers. It does this using meta |
| * information stored in the RTA XML file. The 'readBuffer' API is then |
| * called to retrieve the buffer for each LoggerBuf instance. |
| */ |
| public class LoggerBufDecoder { |
| |
| /* |
| * Byte offsets of the fields in a LoggerBuf record. |
| * |
| * A LoggerBuf record has the following definition: |
| * struct Entry { |
| * Types.Timestamp64 tstamp; |
| * Bits32 serial; |
| * Types.Event evt; |
| * IArg arg1; |
| * IArg arg2; |
| * IArg arg3; |
| * IArg arg4; |
| * }; |
| */ |
| private static final int TSH = 0; |
| private static final int TSL = 4; |
| private static final int SERIAL = 8; |
| private static final int EVENT = 12; |
| private static final int ARGS = 16; |
| |
| /* TargetDecoder for decoding raw bytes */ |
| private TargetDecoder targDec = null; |
| |
| /* MetaData object for retrieving event names, messages, etc */ |
| private IEventMetaData meta; |
| |
| /* Size of a LoggerBuf_Entry in bytes */ |
| private int entrySize; |
| |
| /* |
| * Map of the memory reads to perform for each LoggerBuf instance in |
| * StopMode RTA |
| */ |
| private HashMap<Integer, LoggerRead> loggerReads; |
| |
| /* Memory reader used by 'readBuffer' for StopMode RTA */ |
| private MemoryImage memReader; |
| |
| /* |
| * ======== LoggerRead ======== |
| * Contains the necessary information to read a LoggerBuf's buffer. |
| * Used for StopMode RTA. |
| */ |
| private class LoggerRead { |
| public long address; |
| public int numBytes; |
| } |
| |
| /* |
| * ======== Constructors ======== |
| * The LoggerBufDecoder requires: |
| * - Target endianess |
| * - Target MAU size |
| * - IEventMetaData implementation |
| * |
| * This data comes from different sources in ROV and StopMode RTA. |
| */ |
| |
| /* |
| * ======== RTA Constructor ======== |
| * In RTA, the LoggerBufDecoder can get everything it needs from an |
| * xdc.rta.MetaData instance, which also implements the IEventMetaData |
| * interface. |
| */ |
| public LoggerBufDecoder(MetaData meta) |
| { |
| this(meta.getEndianess(), meta.getBitsPerChar(), meta); |
| } |
| |
| /* |
| * ======== ROV Constructor ======== |
| * ROV retrieves the target endianess and MAU size from the ROV recap |
| * file. The ROV recap file also contains all of the information needed |
| * to implement the IEventMetaData interface. ROV constructs an object |
| * which references the ROV recap file through xdc.runtime.Log APIs, and |
| * wraps it to create an IEventMetaData implementation. |
| */ |
| public LoggerBufDecoder(TargetType.Endianess endianess, int bitsPerChar, |
| IEventMetaData meta) |
| { |
| this.targDec = new TargetDecoder(endianess, bitsPerChar); |
| this.meta = meta; |
| |
| /* |
| * Initialize the size of a LoggerBuf entry. It is dependent on the |
| * target size of the IArg type. |
| */ |
| this.entrySize = 16 + (4 * meta.getTargetArgSize()); |
| } |
| |
| /* |
| * ======== initStopMode ======== |
| * Initialize the LoggerBufDecoder to perform memory reads for stop mode |
| * RTA. |
| * |
| * When the RTA XML file is generated, each LoggerBuf instance provides |
| * the information needed to retrieve its buffer: a symbol which points to |
| * the address of the buffer, and the number of entries the buffer holds. |
| * This information is retrieved here from the xdc.rta.MetaData instance. |
| * |
| * The memory reader is not needed to locate the buffers, but is stored |
| * away to use later to perform the actual memory reads. |
| */ |
| public void initStopMode(ISymbolTable symTab, MemoryImage memReader, |
| MetaData metaData) |
| { |
| /* |
| * Create a map of the data needed to perform the memory read for each |
| * LoggerBuf instance. |
| */ |
| this.loggerReads = new HashMap<Integer, LoggerRead>(); |
| |
| /* Store off the memory reader to use later in the 'readBuffer' API */ |
| this.memReader = memReader; |
| |
| int numLoggers = metaData.getLoggerNames().length; |
| |
| /* For each logger instance... */ |
| for (int i = 0; i < numLoggers; i++) { |
| /* Only deal with LoggerBuf instances */ |
| if (metaData.getLogger(i).type.equals("xdc.runtime.LoggerBuf")) { |
| /* |
| * Retrieve the meta data which each LoggerBuf instance |
| * provided during the generation of the RTA XML file. |
| */ |
| Element metaArgs = (Element) metaData.getLoggerMetaArgs(i); |
| |
| LoggerRead read = new LoggerRead(); |
| |
| /* Get the symbol at which the buffer can be found */ |
| String bufferSymbol = metaData.getProperty(metaArgs, |
| "bufferSymbol"); |
| |
| /* Look up the buffer address */ |
| read.address = symTab.getSymbolValue(bufferSymbol); |
| |
| /* Get the size of the buffer in MAUs */ |
| String bufferSize = metaData.getProperty(metaArgs, |
| "bufferSize"); |
| |
| /* Convert the size to bytes */ |
| read.numBytes = Integer.parseInt(bufferSize) * |
| (metaData.getBitsPerChar() / 8); |
| |
| /* Map the logger id to the read info */ |
| loggerReads.put(i, read); |
| } |
| } |
| } |
| |
| /* |
| * ======== readBuffer ======== |
| * Read the buffer for the specified logger. |
| * |
| * This is only used by StopMode RTA. 'initStopMode' must be called to |
| * initialize the memory reads before calling this API. |
| */ |
| public byte[] readBuffer(int loggerId) throws Exception |
| { |
| /* Retrieve the memory read for the specified logger */ |
| LoggerRead read = loggerReads.get(loggerId); |
| |
| /* Perform the memory read */ |
| byte[] buffer = memReader.readBytes(read.address, |
| read.numBytes, true); |
| |
| return (buffer); |
| } |
| |
| /* |
| * ======== decodeBuffer ======== |
| * Decode an entire buffer of LoggerBuf.Entry structs from the target. |
| * |
| * LoggerBuf records can not be decoded one at a time, the entire buffer |
| * must be considered. This is because a single LoggerBuf entry only holds |
| * four arguments; LoggerBuf makes use of 'continuation' records to |
| * support records with more than four arguments. |
| * |
| * All records have an odd serial number, and continuation records are |
| * marked with an even serial number. |
| * |
| * If the buffer supports wrapping, it is possible that the entry at the |
| * beginning of the buffer will be the continuation to the last record |
| * in the buffer. |
| */ |
| public HostEvent[] decodeBuffer(byte[] buffer, int offset, int length) |
| { |
| Vector<HostEvent> events = new Vector<HostEvent>(); |
| |
| int numRecs = length / entrySize; |
| |
| /* |
| * The sequence number, event code, and timestamp low and high values |
| * are always 4-bytes. |
| */ |
| int size = 4; |
| |
| /* The size of the arguments can vary between targets */ |
| int argSize = meta.getTargetArgSize(); |
| |
| /* |
| * If the first record is actually an extension of the last record, |
| * hold onto it until we get to the end. |
| */ |
| HostEvent lastRecExt = null; |
| |
| /* For each record in the buffer */ |
| for (int i = 0; i < numRecs; i++) { |
| /* Calculate the offset of the 'i'th record */ |
| int off = offset + (i * entrySize); |
| |
| /* |
| * Decode the sequence number and event field first so that |
| * we know whether to continue. |
| */ |
| long seqNum = targDec.decodeBytes(buffer, off + SERIAL, |
| size, false); |
| long code = targDec.decodeBytes(buffer, off + EVENT, |
| size, false); |
| |
| /* |
| * If both the sequence number and event id are 0, this is an |
| * empty record. Move on. |
| * |
| * We need to be able to distinguish between an incomplete |
| * "extension" record that has a serial number of 0 (if it's |
| * base record was 0xffffffff) and an empty record, and we do this |
| * by checking the evt field, which gets set to ~0 for extension |
| * records. |
| */ |
| if ((seqNum == 0) && (code == 0)) { |
| continue; |
| } |
| |
| HostEvent evt = new HostEvent(this.meta); |
| |
| /* |
| * Decode all four arguments, whether the record is a normal one |
| * or a continuation record. Decode the arguments as signed. |
| */ |
| for (int j = 0; j < 4; j++) { |
| int argOff = off + ARGS + (j * argSize); |
| evt.args[j] = (int)targDec.decodeBytes(buffer, argOff, argSize, |
| true); |
| } |
| |
| /* |
| * If the sequence number is odd, this is a normal record. |
| * Decode all of the normal fields. |
| */ |
| if ((seqNum & 1) != 0) { |
| evt.sequenceNum = (seqNum + 1) / 2; |
| |
| /* Extract the event id and module id from the event field */ |
| evt.eventId = (int) code >> 16; |
| evt.moduleId = (int) code & 0x0000FFFF; |
| |
| /* Decode the 64-bit timestamp */ |
| long hi = targDec.decodeBytes(buffer, off + TSH, size, |
| false); |
| long lo = targDec.decodeBytes(buffer, off + TSL, size, |
| false); |
| evt.timestamp = |
| hi < 0 ? -1L : (long) (hi * Math.pow(2, 32) + lo); |
| |
| /* If this is a Log_print, decode first argument as unsigned */ |
| if (evt.eventId == 0) { |
| /* The first argument is the format string */ |
| evt.formatAddr = targDec.decodeBytes(buffer, off + ARGS, |
| argSize, false); |
| } |
| |
| events.add(evt); |
| } |
| /* |
| * Otherwise, this is a continuation record. |
| * |
| * If this is the first record in the buffer, then the |
| * arguments in this record belong to the last record in the |
| * buffer. These args should be saved off until we read the |
| * last record. |
| */ |
| else if (events.size() == 0) { |
| lastRecExt = evt; |
| } |
| /* Otherwise just add the arguments to the previous record. */ |
| else { |
| /* Add the arguments to the previous record */ |
| HostEvent prevEvt = events.lastElement(); |
| for (int j = 0; j < 4; j++) { |
| prevEvt.args[4 + j] = evt.args[j]; |
| } |
| } |
| } |
| |
| /* |
| * If the first record in the buffer was a continuation of the last |
| * record, copy over the arguments. |
| */ |
| if (lastRecExt != null) { |
| HostEvent evt = events.lastElement(); |
| for (int i = 0; i < 4; i++) { |
| evt.args[i + 4] = lastRecExt.args[i]; |
| } |
| } |
| |
| /* Convert the Vector to an array */ |
| HostEvent[] evtArr = events.toArray(new HostEvent[0]); |
| |
| /* Sort the records by sequence number */ |
| if (evtArr.length != 0) { |
| Arrays.sort(evtArr, evtArr[0].new CompareRecs()); |
| } |
| |
| /* Return the array of processed records */ |
| return (evtArr); |
| } |
| |
| /* |
| * ======== decodeBytes ======== |
| * Exposes the TargetDecoder's 'decodeBytes' API. |
| * This is a utility function for testing. |
| */ |
| public long decodeBytes(byte buffer[], int offset, int size, boolean signed) |
| { |
| return (targDec.decodeBytes(buffer, offset, size, signed)); |
| } |
| } |