blob: c5be347e39eee42ed136415fcecd28de1112eaff [file] [log] [blame]
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));
}
}