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