| package xdc.runtime; |
| |
| import xdc.rov.*; |
| import xdc.rta.*; |
| |
| import java.util.Arrays; |
| import java.util.Comparator; |
| import java.util.Vector; |
| import java.util.HashMap; |
| |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| /* |
| * ======== LoggerBufDecoder ======== |
| * Decodes whole LoggerBuf buffers and returns an array of HostEvent objects. |
| * |
| * This decoder is used to optimize the performance of the |
| * xdc.runtime.LoggerBuf 'Records' ROV view, and is also used by stop-mode |
| * RTA. |
| */ |
| 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 on all targets */ |
| private static final int entrySize = 32; |
| |
| /* map of the memory reads to perform for each logger instance. */ |
| private HashMap<Integer, LoggerRead> loggerReads = new HashMap<Integer, LoggerRead>(); |
| |
| /* memory reader used by 'readBuffer' for StopMode RTA. */ |
| private IMemoryImage memReader; |
| |
| /* |
| * ======== LoggerRead ======== |
| * Contains the necessary information to read a logger's buffer. |
| */ |
| private class LoggerRead { |
| public long address; |
| public int numBytes; |
| } |
| |
| /* |
| * ======== ROV Constructor ======== |
| * ROV constructs an object to implement IEventMetaData which references |
| * the application configuration through the ROV recap file. |
| */ |
| public LoggerBufDecoder(TargetType.Endianess endianess, int bitsPerChar, |
| IEventMetaData meta) throws Exception |
| { |
| this.targDec = new TargetDecoder(endianess, bitsPerChar); |
| this.meta = meta; |
| } |
| |
| /* |
| * ======== RTA Constructor ======== |
| * With RTA, the IEventMetaData interface is implemented by the |
| * xdc.rta.MetaData class. |
| */ |
| public LoggerBufDecoder(MetaData meta) |
| { |
| this.targDec = new TargetDecoder(meta.getEndianess(), |
| meta.getBitsPerChar()); |
| this.meta = meta; |
| } |
| |
| /* |
| * ======== initStopMode ======== |
| * Initialize the LoggerBufDecoder to perform memory reads for stop mode |
| * RTA. |
| */ |
| public void initStopMode(ISymbolTable symTab, IMemoryImage memReader, MetaData metaData) |
| { |
| this.memReader = memReader; |
| |
| int numLoggers = metaData.getLoggerNames().length; |
| |
| /* For each logger instance... */ |
| for (int i = 0; i < numLoggers; i++) { |
| /* Only retrieve LoggerBuf instances. */ |
| if (metaData.getLogger(i).type.equals("xdc.runtime.LoggerBuf")) { |
| Node metaArgs = meta.getLoggerMetaArgs(i); |
| |
| NodeList loggerProps = metaArgs.getChildNodes(); |
| |
| LoggerRead read = new LoggerRead(); |
| |
| /* Retrieve all of the logger's properties */ |
| for (int j = 0; j < loggerProps.getLength(); j++) { |
| Node n = loggerProps.item(j); |
| String nodeName = n.getNodeName(); |
| /* Get the symbol at which the buffer can be found. */ |
| if (nodeName.equals("bufferSymbol")) { |
| String bufferSymbol = n.getTextContent(); |
| read.address = symTab.getSymbolValue(bufferSymbol); |
| } |
| /* Get the size of the buffer in MAUs, convert to bytes. */ |
| else if (nodeName.equals("bufferSize")) { |
| read.numBytes = Integer.parseInt(n.getTextContent()) * |
| (metaData.getBitsPerChar() / 8); |
| } |
| } |
| |
| /* Map the logger id to the read info */ |
| loggerReads.put(i, read); |
| } |
| } |
| } |
| |
| /* |
| * ======== readBuffer ======== |
| * Read the buffer for the specified logger. |
| */ |
| public byte[] readBuffer(int loggerId) throws Exception |
| { |
| 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. |
| */ |
| public HostEvent[] decodeBuffer(byte[] buffer, int offset, int length) |
| { |
| Vector<HostEvent> events = new Vector<HostEvent>(); |
| |
| int numRecs = length / entrySize; |
| |
| /* All fields are 4 bytes */ |
| int size = 4; |
| |
| /* |
| * 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 * size); |
| evt.args[j] = (int) targDec.decodeBytes(buffer, argOff, size, |
| 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, |
| size, 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)); |
| } |
| } |