blob: f2182bdd57e44c27495ff363663d08d44a1a2283 [file] [log] [blame]
/* --COPYRIGHT--,EPL
* Copyright (c) 2008-2020 Texas Instruments Incorporated
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Texas Instruments - initial implementation
*
* --/COPYRIGHT--
*/
/*
* ======== StructureDecoder.xs ========
* The StructureDecoder is organized into 'fetch' and 'decode' APIs. The
* 'fetch' APIs are intended as the real interface to the StructureDecoder,
* and they in turn call down to the appropriate decode API.
*
* Of the 'decode' APIs, there are two categories, those that take a type
* object (an object from the XDC object model):
* decodeStruct
* decodeStructArray
* and those that take a type string (a specially formatted type string):
* _decodeField
* _decodeScalar
* _decodeArray
*
* The StructureDecoder only decodes structures and arrays of structures. To
* handle scalars, the xdc.rov.support.ScalarStructs module defines structure
* types for each of the scalar types.
*
* The 'fetch' APIs call down to either decodeStruct or decodeStructArray.
* These APIs decode the structures by retrieving the list of fields from the
* type object. Each field has a specially formatted type string. These
* strings are then passed down to the other decode APIs to do the actual
* decoding.
*
* The decode APIs are made slightly more complicated by some optimizations
* for decoding arrays. Rather than call a single 'decode' function on each
* element in the array, the type information is only interpereted once and
* stored in a StructureDecoder.FieldType object, then this object is used
* to decode all of the array elements.
*/
/*
* ======== instance$meta$init ========
* Initialize the StructureDecoder instance.
*/
function instance$meta$init(mem, targ)
{
this.$private.memReader = mem;
this.$private.targConfig = targ;
/* Create a TargetDecoder for decoding the raw bytes. */
xdc.loadPackage('xdc.rov');
/* Get the target endianess and convert from string to enum. */
var endianStr = this.$private.targConfig.model.endian;
var endian = Packages.xdc.rov.TargetType.strToEndianess(endianStr);
/* Create the TargetDecoder. */
var bitsPerChar = this.$private.targConfig.bitsPerChar;
this.$private.targDec = new Packages.xdc.rov.TargetDecoder(endian,
bitsPerChar);
}
/*
* ===============================
* Type-Object Decode APIs
* ===============================
*/
/*
* ======== decodeStruct ========
* Decodes an entire structure by decoding each of the structures fields.
*
* structType - Structure type. Not just a string, an actual object with the
* target layout of the structure.
* buffer - Buffer object containing buffer and current offset.
* inStr - Optional instance of structType to be filled in.
*/
function decodeStruct(structType, buffer, inStr)
{
decoderPrint("Decoding structure of type " + (new structType).$type);
/*
* The caller can optionally pass a structure to fill in. Otherwise, create
* one to be filled in.
*/
var str;
if (inStr != null) {
str = inStr;
}
else {
str = _createObjFromType(structType, buffer.addr + buffer.off);
}
/* Store the initial offset. */
var base = buffer.off;
/*
* From package schema:
*
* var so = xdc.om['ti.sysbios.knl.Swi.HookSet'];
* so.$$bind('$$sizes', [
* ['registerFxn', 'UFxn'],
* ['createFxn', 'UFxn'],
* ...,
* ]);
*
* 'sizes' is a 2D array; each entry represents a field and is a pair
* including the field name and the field type.
*/
var sizes = structType.$$sizes;
/* For each field in the structure... */
for (var i = 0; i < sizes.length; i++) {
var field = sizes[i];
var fieldName = field[0];
var fieldType = field[1];
decoderPrint(" Decoding field " + fieldName + " (" + fieldType + ")");
/* Update offset for field */
buffer.off = base + structType.$offsetof(fieldName);
/* Decode the field */
str[fieldName] = this._decodeField(fieldType, buffer);
}
return (str);
}
/*
* ======== _createObjFromType ========
* In order to have better control over the contents and display of structs,
* ROV creates untyped JavaScript objects to represent structs, and copies
* over any fields from the typed struct that it needs.
* This helper function copies over the type name and any $fetchDesc
* properties of the struct.
*/
function _createObjFromType(structType, addr)
{
var obj = {};
obj.$addr = $addr(addr);
/*
* Create a temporary instance of the type used to copy over some of
* the fields from the typed object into the untyped one.
*/
var temp = new structType;
/* Copy over the type */
obj.$type = temp.$type;
/* Copy over any $fetchDesc fields. */
for (var fld in temp) {
/* Check if there is a $fetchDesc for this field, and if so, copy it */
var fetchDesc = fld + '$fetchDesc';
if (fetchDesc in temp) {
obj[fetchDesc] = temp[fetchDesc];
}
}
return (obj);
}
/*
* ======== decodeStructArray ========
* Decodes an array of structures given the structure's XDC OM type object.
*
* This API exists to perform some optimizations when decoding an array
* of a single type of object. Rather than call _createObjFromType for each
* object in the array, it creates a single object to use as a model, then
* clones this object to be used for each array element.
*/
function decodeStructArray(strType, buffer, length)
{
var arr = new Array();
/*
* Create a JavaScript object representation of the structure, then use
* this object as a model and clone it rather than calling
* _createObjFromType for each element.
*/
var strModel = _createObjFromType(strType, buffer.addr + buffer.off);
/* Store the initial offset. */
var base = buffer.off;
for (var i = 0; i < length; i++) {
/* Update the offset. */
buffer.off = base + (i * strType.$sizeof());
/* Clone the object and update the address. */
var str = _cloneObject(strModel);
str.$addr = buffer.addr + buffer.off;
/* Decode the structure, pass in in the structure to fill. */
this.decodeStruct(strType, buffer, str);
arr[i] = str;
}
return (arr);
}
/*
* ======== _cloneObject ========
* Does a shallow copy of 'obj' to create a new object. The object returned
* has all of the same fields as the original, but this is not a deep copy of
* all of the object's values.
*/
function _cloneObject(obj)
{
var clone = new Object();
for (var p in obj) {
clone[p] = obj[p];
}
return (clone);
}
/*
* ========================
* Type String APIs
* ========================
*/
/*
* ======== _parseTypeString ========
* This function interperets a field's type string. The type string comes from
* the $$sizes property of the structure's type object (from the XDC object
* model).
*
* The string is parsed and the interperetation is stored in a
* StructureDecoder.FieldType object. This allows this parsing code to be
* shared between different APIs. It is also used as an optimization for
* decoding arrays, allowing the StructureDecoder to parse the string once and
* apply it to all of the elements in the array.
*
* Only the fields in the FieldType object relevant to the type are filled in.
*
* The type string begins with a letter indicating whether the field is an
* embedded array ('A'), an embedded structure ('S'), or one of the scalar
* types ('N', 'T', 'E', or 'U').
*
* The definition of a structure's type strings can be found in the package's
* schema file, 'package/<package_name>.java'
*/
function _parseTypeString(fieldType)
{
var StructureDecoder = xdc.useModule('xdc.rov.StructureDecoder');
var type = new StructureDecoder.FieldType;
switch (fieldType[0]) {
/*
* 'A': an embedded array, followed by the array length and type.
* Example:
* 'A16;UInt32'
*/
case 'A': {
type.isArrType = true;
var sep = fieldType.indexOf(';');
/* Get the fixed length of the embedded array */
type.len = Number(fieldType.substring(1, sep));
/* Get the type of the array elements. */
type.elemType = fieldType.substring(sep + 1);
decoderPrint(" Field is an embedded array; len: " + type.len +
" type: " + type.elemType);
return (type);
}
/*
* 'S': Structure
* Example:
* 'Sti.sysbios.misc.Queue;Elem'
*/
case 'S': {
type.isStrType = true;
/* Get the structure's definition from the object model */
var structNames = fieldType.substring(1).split(';');
var modName = structNames[0];
var structName = structNames[1];
decoderPrint(" Field is a structure of type " + modName + "." +
structName);
type.strType = xdc.om[modName][structName];
return (type);
}
/*
* Scalar values
* 'N': Enum
* 'T': Signed
* 'E': Encoded
* 'U': Unsigned
*
* Examples:
* 'UPtr'
* 'TInt'
* 'UInt'
* 'Nti.sysbios.knl.Task.Mode'
* 'UIArg'
*/
case 'N':
case 'T':
case 'E':
case 'U': {
type.isScalarType = true;
type.signed = fieldType[0] != 'U';
type.isEnum = fieldType[0] == 'N';
type.isEncoded = fieldType[0] == 'E';
/* Determine the size and alignment of the type. */
/*
* The type of an enum is not always an int. A type can be
* specified by the user. Or, on some targets, the compiler may
* 'pack' the enum and choose the smallest possible type to hold
* the values.
*/
if (type.isEnum) {
var stdType = $$getEnumType(fieldType,
this.$private.targConfig);
type.size = stdType.size;
type.align = stdType.align;
}
/* TODO - An encoded type is not necessarily always an int. */
else if (type.isEncoded) {
var tname = 't_Int';
type.size = this.$private.targConfig.stdTypes[tname].size;
type.align = this.$private.targConfig.stdTypes[tname].align;
}
/* For all other scalars, the type is specified by the string. */
else {
var tname = 't_' + fieldType.substring(1);
type.size = this.$private.targConfig.stdTypes[tname].size;
type.align = this.$private.targConfig.stdTypes[tname].align;
}
/* Pointer types are all presented as addresses */
type.isAddr = (tname == 't_Ptr' || tname == 't_Fxn' ||
tname == 't_IArg');
type.fldType = fieldType;
decoderPrint(" Field is a scalar (" + fieldType + "), size: "
+ type.size
+ " align: " + type.align);
return (type);
}
default: {
throw (new Error("Unknown type code '" + fieldType +
"'; can't decode value"));
}
}
}
/*
* ======== _decodeField ========
* Decodes a single field within a structure and returns its value.
*
* This function will increment the offset field in the 'buffer'
* argument. The decodeStruct API will reset the offset for each field, but
* for decoding arrays,
*
* fieldType - Type string
* buffer - Buffer object containing buffer and current offset.
*/
function _decodeField(fieldType, buffer)
{
var type = this._parseTypeString(fieldType);
/* Embedded array */
if (type.isArrType) {
return (this._decodeArray(type.elemType, buffer, type.len));
}
/* Structure */
else if (type.isStrType) {
return (this.decodeStruct(type.strType, buffer));
}
/* Scalar */
else if (type.isScalarType) {
return (this._decodeScalar(type, buffer));
}
/* Should never reach here... */
return (undefined);
}
/*
* ======== _decodeArray ========
* This API decodes an array of any type of elements given the elements' type
* string.
*
* decodeStructArray takes an XDC OM type object, while _decodeArray takes a
* type string. _decodeArray calls down to decodeStructArray if the element
* type is a structure.
*/
function _decodeArray(fieldType, buffer, length)
{
var arr = new Array();
/* Parse the type string */
var type = this._parseTypeString(fieldType);
/* Array of embedded arrays. */
if (type.isArrType) {
/* Decode each of the arrays. */
for (var i = 0; i < length; i++) {
arr[i] = this._decodeArray(arrType.elemType, buffer, arrType.len);
}
return (arr);
}
/* Array of structures. */
else if (type.isStrType) {
return (this.decodeStructArray(type.strType, buffer, length));
}
/* Array of scalars. */
else if (type.isScalarType) {
/* Decode each of the scalar values. */
for (var i = 0; i < length; i++) {
arr[i] = this._decodeScalar(type, buffer);
buffer.off += type.size;
}
return (arr);
}
/* Should never reach here... */
return (undefined);
}
/*
* ======== _decodeScalar ========
* Decodes a scalar value of 'type' at the offset specified in 'buffer.off'.
*/
function _decodeScalar(type, buffer)
{
/*
* Adjust the offset of the field within the structure based on the
* target's required alignment of the field type.
*/
var d = buffer.off % type.align;
if (d > 0) {
buffer.off += type.align - d;
}
/* Decode the value */
var val = this.$private.targDec.decodeMAUs(buffer.buffer, buffer.off,
type.size, type.signed);
decoderPrint(" Value " + val + " at offset: " + buffer.off +
", size: " + type.size);
/*
* Use address type for pointers and functions.
* Also use it for UArgs so that these are displayed in hex by default
* (since they are often pointers).
*/
if (type.isAddr) {
val = $addr(Number(val));
}
/* Can't catch Java exception, so check for too large Integer */
if (type.isEnum && val > java.lang.Integer.MAX_VALUE) {
throw (new Error("Value " + val
+ " is too large to convert to Java Integer"));
}
/* For Enums, return an Enum object. */
if (type.isEnum) {
/* First, parse the type string for the name of the enum. */
var index = type.fldType.indexOf(';');
var enumName;
if (index == -1) {
enumName = type.fldType.substring(1);
}
else {
enumName = type.fldType.substring(1, index);
}
return ($$Enum(xdc.om[enumName], null, val));
}
if (type.fldType == "TFloat") {
val = java.lang.Float.intBitsToFloat(val & 0xFFFFFFFF);
}
else if (type.fldType == "TDouble") {
val = java.lang.Double.longBitsToDouble(val);
}
return (val);
}
/*
* ==================
* Fetch APIs
* ==================
*/
/*
* ======== fetchStruct ========
* Reads and decodes a structure from the target, given its type and address.
*/
function fetchStruct(structType, addr, addrCheck)
{
var StructureDecoder = xdc.useModule('xdc.rov.StructureDecoder');
/* Read the structure's raw bytes from the target. */
var buf = this.$private.memReader.readMaus(Number(addr),
structType.$sizeof(),
addrCheck);
/* Wrap the data in a Buffer object. */
var buffer = new StructureDecoder.Buffer;
buffer.buffer = buf;
buffer.addr = addr;
buffer.off = 0;
/* Decode the structure. */
var str = this.decodeStruct(structType, buffer);
return (str);
}
/*
* ======== fetchArray ========
*/
function fetchArray(structType, addr, len, isScalar, addrCheck)
{
var StructureDecoder = xdc.useModule('xdc.rov.StructureDecoder');
/* If the array has zero length throw an exception. */
if (len == 0) {
throw (new Error("fetchArray called with length 0."));
}
/* Read the array's raw bytes from memory. */
var buf = this.$private.memReader.readMaus(Number(addr),
structType.$sizeof() * len,
addrCheck);
/* Create a Buffer object around the target data. */
var buffer = new StructureDecoder.Buffer;
buffer.buffer = buf;
buffer.addr = addr;
buffer.off = 0;
if (isScalar) {
/* The scalar structs only have one field. */
var fieldType = structType.$$sizes[0][1];
return (this._decodeArray(fieldType, buffer, len));
}
else {
var array = this.decodeStructArray(structType, buffer, len);
}
return (array);
}
/*
* ======== decoderPrint ========
* Print statements used for debugging issues with the StructureDecoder.
*/
function decoderPrint(msg)
{
//print(msg);
}