diff options
Diffstat (limited to 'printer/src')
-rw-r--r-- | printer/src/mipi_syst_collateral.cpp | 331 | ||||
-rw-r--r-- | printer/src/mipi_syst_decode.cpp | 874 | ||||
-rw-r--r-- | printer/src/mipi_syst_main.cpp | 265 | ||||
-rw-r--r-- | printer/src/mipi_syst_message.cpp | 371 | ||||
-rw-r--r-- | printer/src/mipi_syst_printf.cpp | 435 |
5 files changed, 2276 insertions, 0 deletions
diff --git a/printer/src/mipi_syst_collateral.cpp b/printer/src/mipi_syst_collateral.cpp new file mode 100644 index 0000000..a421808 --- /dev/null +++ b/printer/src/mipi_syst_collateral.cpp @@ -0,0 +1,331 @@ +/* + Copyright (c) 2018, MIPI Alliance, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Contributors: + * Norbert Schulz (Intel Corporation) - Initial API and implementation + */ + +#include <iomanip> +#include <sstream> +#include <iostream> +#include <type_traits> + +#include "mipi_syst_collateral.h" +#include "mipi_syst_guid.h" + +MIPI_SYST_NAMESPACE_BEGIN + +const char tagCollateral[] = "syst:Collateral"; +const char tagClient[] = "syst:Client"; +const char tagGuids[] = "syst:Guids"; +const char tagGuid[] = "syst:Guid"; +const char tagBuilds[] = "syst:Builds"; +const char tagBuild[] = "syst:Build"; +const char tagCatalog32[] = "syst:Catalog32"; +const char tagCatalog64[] = "syst:Catalog64"; +const char tagSourceFiles[] = "syst:SourceFiles"; +const char tagFile[] = "syst:File"; +const char tagShort32[] = "syst:Short32"; +const char tagShort64[] = "syst:Short64"; +const char tagWrite[] = "syst:Write"; +const char tagProtocol[] = "syst:Protocol"; +const char tagModules[] = "syst:Modules"; +const char tagModule[] = "syst:Module"; +const char tagID[] = "ID"; +const char tagMask[] = "Mask"; +const char tagFileAttr[] = "File"; +const char tagLine[] = "Line"; +const char tagFormat[] = "syst:Format"; + + +std::vector<collateral*> collateral::parseXml(const std::string& filename) +{ + pugi::xml_document doc; + pugi::xml_parse_result scan_result(doc.load_file(filename.c_str())); + std::stringstream sstr; + + if (!scan_result) { + sstr + << "XML parser error on file " + << filename + << " : " + << scan_result.description(); + + throw std::invalid_argument(sstr.str()); + } + + std::vector<collateral*> result; + + for(auto client(doc.child(tagCollateral).child(tagClient)); + client; + client = client.next_sibling(tagClient)) + { + result.push_back(new collateral(client, filename)); + } + return result; +} + + +collateral::collateral(pugi::xml_node& node, const std::string& file) : + m_file(file), + m_name(node.attribute("Name").as_string()) +{ + // Scan id <-> item mappings + // + parseMaskedItems(node.child(tagGuids), tagGuid, m_guids); + parseMaskedItems(node.child(tagBuilds), tagBuild, m_builds); + parseMaskedItems(node.child(tagSourceFiles), tagFile, m_files); + parseMaskedItems(node.child(tagModules), tagModule, m_modules); + parseMaskedItems(node.child(tagCatalog32), tagFormat, m_msgs32); + parseMaskedItems(node.child(tagCatalog64), tagFormat, m_msgs64); + parseMaskedItems(node.child(tagShort32), tagFormat, m_shorts32); + parseMaskedItems(node.child(tagShort64), tagFormat, m_shorts64); + parseMaskedItems(node.child(tagWrite), tagProtocol, m_writeTypes); +} + +bool collateral::match(const guid& g, uint64_t build) const +{ + for (auto& it : m_guids) { + if ((g & it.mask()) == (it.key() & it.mask())) + { + if (build != 0x0) { + // find collateral with matching build number + // + if (m_builds.find(build) != m_builds.end()) + { + return true; + } + } else { + return true; + } + } + } + + return false; +} + +template<> const collateral::catalogentry<uint32_t> * collateral::getCatalogEntry<>(uint32_t id) const +{ + auto hit = m_msgs32.find(id); + return (hit != m_msgs32.end()) ? &hit->value() : nullptr; +} + +template<> const collateral::catalogentry<uint64_t> * collateral::getCatalogEntry<>(uint64_t id) const +{ + auto hit = m_msgs64.find(id); + return (hit != m_msgs64.end()) ? &hit->value() : nullptr; +} +const std::string * collateral::getSourceFile(uint32_t id) const +{ + auto hit = m_files.find(id); + return (hit != m_files.end()) ? &hit->value() : nullptr; +} + +const std::string * collateral::getWriteType(uint8_t id) const +{ + auto hit(m_writeTypes.find(id)); + return (hit != m_writeTypes.end()) ? &hit->value() : nullptr; +} + +template<> const collateral::catalogentry<uint32_t> * collateral::getShortEntry<>(uint32_t id) const +{ + auto hit(m_shorts32.find(id)); + return (hit != m_shorts32.end()) ? &hit->value() : nullptr; +} +template<> const collateral::catalogentry<uint64_t> * collateral::getShortEntry<>(uint64_t id) const +{ + auto hit(m_shorts64.find(id)); + return (hit != m_shorts64.end()) ? &hit->value() : nullptr; +} + +template<typename K> bool parse(K& dest, const std::string& s) +{ + return stringToNum<K>(s, dest); +} + +template<> bool parse<guid>(guid& g, const std::string& s) +{ + return g.parse(s); +} + +template<typename K> K nomask() { return (K)-1; } +template<> guid nomask<guid>() { return guid("{ffffffff-ffff-ffff-ffff-ffffffffffff}"); } + +template<typename T> std::ostream & operator<<(std::ostream &os, const collateral::catalogentry<T>& p) +{ + return os << p.msg; +} + +template<typename K, typename V> +void collateral::parseMaskedItems( + pugi::xml_node root, const char * tag, masked_vector<K, V>& dest) +{ + for (pugi::xml_node item(root.child(tag)); + item; + item = item.next_sibling(tag)) + { + masked_item<K, V> data; + + std::string keyStr(item.attribute(tagID).as_string()); + std::string val(item.child_value()); + + if (!parse<K>(data.key(), keyStr)) { + std::stringstream what; + what << + "Malformed " << tag << "entry ID=\"" << keyStr << + "\" value =\"" << val << "\"" << + " for catalog " << m_name; + throw std::invalid_argument(what.str()); + } + + std::string maskStr(item.attribute(tagMask).as_string()); + if (!maskStr.empty()) { + if (!parse<K>(data.mask(), maskStr)) { + std::stringstream what; + what << + "Malformed " << tag << "entry Mask=\"" << maskStr << + "\" value =\"" << val << "\"" << + " for catalog " << m_name; + throw std::invalid_argument(what.str()); + } + } else { + data.mask() = nomask<K>(); + } + + data.value() = val; + + // scan option file/line data if value supports source positions + // + if (std::is_base_of<catalogentry<K>, V>::value) { + catalogentry<K> * entry((catalogentry<K>*)&data.value()); + entry->mask = data.mask(); + + std::string file(item.attribute(tagFileAttr).as_string()); + std::string line(item.attribute(tagLine).as_string()); + + if (!file.empty()) { + if (!parse(entry->m_file, file)) { + std::stringstream what; + what << + "Malformed File attribute " << file << + "in section " << tag << + " for catalog " << m_name; + throw std::invalid_argument(what.str()); + } + } + + if (!line.empty()) { + if (!parse(entry->m_line, line)) { + std::stringstream what; + what << + "Malformed Line attribute " << line << + "in section " << tag << + " for catalog " << m_name; + throw std::invalid_argument(what.str()); + } + } + } + + auto it = dest.find(data.key()); + if (it != dest.end() && it->value() != val) { + std::cerr << + "Overwriting ID" << data.key() << " -> " << val << + " old value: " << it->value() << std::endl; + *it = data; + } + else { + dest.push_back(data); + } + } +} + +/** Helper to parse a GUID "{...}" string into the binary representation. +* @param str GUID ascii string "{xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" +* @param guid resulting guid +* @return true if guid could be parserd +*/ +bool guid::parse(const std::string& str) +{ + if ( + (str.length() != sizeof("{00000000-0000-0000-0000-000000000000}")-1) || + str[0]!= '{' ||str[37] != '}' || + str[9] != '-' || str[14] != '-' || str[19] != '-' || str[24] != '-' + ) + { + return false; + } + + // parse the byte values; + // + const char * p(str.c_str()+1); + + for (int i(0); i < 16; ++i, p += 2) { + if (i == 4 || i == 6|| i == 8 || i == 10) { + p++; // skip "-" + } + std::stringstream sstr(std::string(p, 2)); + uint16_t temp; + + if (! (sstr >> std::hex >>temp)) { + return false; + } + u.b[i] = (uint8_t)(temp); + } + + return true; +} + +std::ostream& operator<<(std::ostream& os, const guid& g) +{ + auto flags(os.flags()); + + + os << '{'; + + for (int i(0); i < 16; ++i) { + os << std::setfill('0') << std::noshowbase << std::hex << std::setw(2) + << (uint16_t)g.u.b[i]; + + if (i == 3 || i == 5|| i == 7 || i == 9) { + os << '-'; + } + } + + os << '}'; + + os.flags(flags); + return os; +} + +MIPI_SYST_NAMESPACE_END
\ No newline at end of file diff --git a/printer/src/mipi_syst_decode.cpp b/printer/src/mipi_syst_decode.cpp new file mode 100644 index 0000000..1c9b1d8 --- /dev/null +++ b/printer/src/mipi_syst_decode.cpp @@ -0,0 +1,874 @@ +/* + Copyright (c) 2018, MIPI Alliance, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Contributors: + * Norbert Schulz (Intel Corporation) - Initial API and implementation + */ +#include<sstream> +#include <iomanip> + +#include "mipi_syst_decode.h" +#include "mipi_syst_printf.h" + +MIPI_SYST_NAMESPACE_BEGIN + +/// Helper to extract string from byte array, +// +// @param dest where to write bytes to +// @param offset where to start from +// @param data byte array +// @param len number of bytes in array +// +// @return bytes consumed or < 0 if no null byte found +// +static int32_t bytes2String(std::string& dest, const uint8_t * data, uint32_t len) +{ + uint32_t i(0); + + while(i <len) + { + if (!data[i]) { + break; // null byte found + } + dest += data[i++]; + } + + if (i == len && data[i - 1]) { + return -1; + } + + return (uint32_t)dest.size(); +} + +// Helper for printf formating +// +static std::string printfFormat( + const std::string& fmt, + bool is32Bit, + const void * data, + uint32_t len) +{ + msgprintf printer(is32Bit); + std::string result; + if (len > 0) { + printer.format(fmt, data, len, result); + } + else { + printer.format(fmt, NULL, 0, result); + } + return result; +} + +void decoder::setBuildNumber(uint64_t id, const guid& g) const +{ + // cache guid <-> id association + // TODO: A production quality implementation should add a sanity check here + // and issue a warning if the ID changes compared to an earlier one. + // + if (build_by_guid.find(g) != build_by_guid.end()) { + build_by_guid[g] = id; + } + + // Remove an eventually earlier cached collateral to guid mapping. We recompute + // next time with the now known build id. + // + auto cache_hit(collateral_by_guid.find(g)); + if (cache_hit != collateral_by_guid.end()) { + collateral_by_guid.erase(cache_hit); + } +} + + +// Decode single SyS-T message +// +bool decoder::decode(message& dest, const std::vector<uint8_t>& data, const decode_context * ctx) const +{ + bool rt(false); + + try { + // header + // + if (data.size() < sizeof(uint32_t)) throw tooshort_exception(); + message::header hdr(bytes2ValLE<uint32_t>(&data[0])); + + // short message ? + // + if (message::isShort(hdr)) { + rt = decodeShortMessage(dest, &data[0], (uint32_t)data.size(), ctx); + } else { + rt = decodeNormalMessage(dest, &data[0], (uint32_t)data.size(), ctx); + } + + if (rt && dest.getHeader().field.type == message::BUILD) { + // found build message, store build number for later + // + setBuildNumber(dest.getBuild(), dest.getGuid()); + } + } catch (decode_exception& de) { + dest.setStatus(de.get_state()); + + // decode failed, show raw byte dump as payload + // + std::stringstream sstr; + sstr << std::hex << std::uppercase << std::setfill('0') << std::setw(2); + for (auto uc : data) { + sstr << (uint16_t)uc; + } + dest.setPayload(sstr.str()); + } + return rt; +} + + +template<class T> void decodeShortPayload(message & m, T v) +{ + const collateral * coll(m.getCollateral()); + const collateral::catalogentry<T> * entry( + coll ? coll->getShortEntry(v) : nullptr); + + if (entry) { + std::stringstream dest; + hostPrintf(dest, entry->msg, v & (~entry->mask), std::vector<int>()); + m.setPayload(dest.str()); + } else { + m.setPayload(toHexValue(v)); + } +} + +bool decoder::decodeShortMessage( + message& dest, + const uint8_t *data, uint32_t length, + const decode_context * ctx) const +{ + if (length < sizeof(uint32_t)) throw tooshort_exception(); + + uint32_t type(data[0] & 0x0F); // Bit 0..3 = type + uint32_t subtype(data[3] & 0x3F); // Bit 24..29 = subtype + + dest.setHeader(0); // all optional fields off + dest.setType(type); + dest.setLength(length); + dest.setCollateral(ctx ? findCollateral(ctx->getGuid()) : nullptr); + dest.setContextTS(ctx ? ctx->getTS() : 0); + + switch (type) { + case message::type::SHORT32: + decodeShortPayload(dest, bytes2ValLE<uint32_t>(data) >> 4); + break; + + case message::type::SHORT64: + if (length < sizeof(uint64_t)) throw tooshort_exception(); + decodeShortPayload(dest, bytes2ValLE<uint64_t>(data) >> 4); + break; + + case message::type::BUILD: + dest.setSubType(subtype); + switch (subtype) { + case message::subtype_build::BUILD_COMPACT32: + { + uint32_t val(bytes2ValLE<uint32_t>(data)); + val = ((val & 0x00FFFFF0) >> 4) | ((val & 0x30000000) >> 10); + dest.setBuild(val); + dest.setPayload(toHexValue(val)); + break; + } + case message::subtype_build::BUILD_COMPACT64: + { + if (length < sizeof(uint64_t)) throw tooshort_exception(); + + uint64_t val(bytes2ValLE<uint64_t>(data)); + val = ((val & 0x0000000000FFFFF0ull) >> 4) | + ((val & 0xFFFFFFFF30000000ull) >> 10); + dest.setBuild(val); + dest.setPayload(toHexValue(val)); + + break; + } + default: + throw unknown_type_exception(); + } + break; + default: + throw unknown_type_exception(); + } + + dest.setStatus(message::decode_state::OK); + + return true; +} + +// Decode "normal" SyS-T message data +// +bool decoder::decodeNormalMessage(message& dest, const uint8_t * data, uint32_t length, const decode_context * ctx) const +{ + uint32_t offset(0); + + dest.setContextTS(ctx ? ctx->getTS() : 0); + dest.setLength(length); + + // header + // + if (length < (offset + sizeof(uint32_t))) throw tooshort_exception(); + dest.setHeader(bytes2ValLE<uint32_t>(data+offset)); + offset += sizeof(uint32_t); + + // checksum at the end ? + // + if (dest.getHeader().field.chksum) { + if (length < offset + sizeof(uint32_t)) throw tooshort_exception(); + length -= sizeof(uint32_t); + dest.setCrc(bytes2ValLE<uint32_t>(data+length)); + } + + // guid + // + if (dest.getHeader().field.guid) { + if (length < (offset + sizeof(uint64_t[2]))) throw tooshort_exception(); + + dest.setGuid(guid(data+offset)); + offset += sizeof(uint64_t[2]); + } else { + dest.setGuid(generatePseudoGuid(dest.getHeaderOrigin())); + } + + // location + // + if (dest.getHeader().field.location) { + if (length < offset + sizeof(uint8_t)) throw tooshort_exception(); + uint32_t tag(data[offset++]); + if (tag & 0x1) { + // 64 bit + if (length < offset + sizeof(uint64_t)) throw tooshort_exception(); + if (tag & 0x2) { // addr + uint64_t addr(bytes2ValLE<uint64_t>(data+offset)); + dest.setLocAddr64(addr); + } else { + uint32_t file(bytes2ValLE<uint32_t>(data + offset)); + uint32_t line(bytes2ValLE<uint32_t>(data + offset + 4)); + dest.setLocFileLine(file, line); + } + offset += sizeof(uint64_t); + } else { + // 32 bit + if (length < offset + sizeof(uint32_t)) throw tooshort_exception(); + if (tag & 0x2) { // addr + uint32_t addr(bytes2ValLE<uint32_t>(data + offset)); + dest.setLocAddr32(addr); + } else { + uint16_t file(bytes2ValLE<uint16_t>(data + offset)); + uint16_t line(bytes2ValLE<uint16_t>(data + offset + 2)); + dest.setLocFileLine(file, line); + } + offset += sizeof(uint32_t); + } + } + + // length + // + uint32_t payload_len(length - offset); + + if (dest.getHeader().field.length) { + if (length < offset + sizeof(uint16_t)) throw tooshort_exception(); + payload_len = bytes2ValLE<uint16_t>(data + offset); + offset += sizeof(uint16_t); + } + + // timestamp + // + if (dest.getHeader().field.timestamp) { + if (length < offset + sizeof(uint64_t)) throw tooshort_exception(); + dest.setMessageTS(bytes2ValLE<uint64_t>(data + offset)); + offset += sizeof(uint64_t); + } + + if (dest.getHeader().field.length) { + // length given, compare with remaining payload length + // + int diff(length - offset - payload_len); + if (diff < 0) { + throw tooshort_exception(); + } else if (diff > 0) { + throw toolong_exception(); + } + } + + // check CRC32 if set + // + if (dest.getHeader().field.chksum) { + uint32_t crc = getCrc32(data, length); + if (crc != dest.getCrc()) { + throw crc_error_exception(); + } + } + + // Check for collateral information + // + const collateral* coll = findCollateral(dest.getGuid()); + dest.setCollateral(coll); + + // Set client name based on collateral (or use raw guid/origin from message) + // + if (coll != nullptr) { + dest.setClientName(coll->getName()); + } + else { + std::stringstream sstr; + if (dest.getHeader().field.guid) { + sstr << dest.getGuid(); + } else { + sstr << dest.getHeaderOrigin(); + } + dest.setClientName(sstr.str()); + } + + // Payload decode depening on type + // + (this->*payloadDecode[dest.getHeader().field.type])(dest, data+offset, length - offset); + + dest.setStatus(message::decode_state::OK); + return true; +} + +void decoder::decodeInvalidType(message& dest, const uint8_t * data, uint32_t len) const +{ + (void)dest; + (void)data; + (void)len; + + throw unknown_type_exception(); +} + +void decoder::decodeBuildPayload(message& dest, const uint8_t * data, uint32_t len) const +{ + std::stringstream sstr; + + switch (dest.getHeader().field.subtype) { + case message::subtype_build::BUILD_LONG: + if (len < sizeof(uint64_t)) throw tooshort_exception(); + + dest.setBuild(bytes2ValLE<uint64_t>(data)); + sstr << toHexValue(dest.getBuild()); + + data += sizeof(uint64_t); + len -= sizeof(uint64_t); + + if (0 != len) { // string follows .... + std::string payload; + if (bytes2String(payload, data, len) > 0) { + sstr << " " << payload; + } else { + throw tooshort_exception(); + } + } + break; + default: + throw unknown_type_exception(); + break; + } + + dest.setPayload(sstr.str()); +} + +void decoder::decodeStringPayload(message& dest, const uint8_t * data, uint32_t len) const +{ + std::string format; + int fmtlen; + + if (!len || (fmtlen = bytes2String(format, data, len)) <= 0) { + throw tooshort_exception(); + } + + std::stringstream sstr; + + switch (dest.getHeader().field.subtype) { + case message::subtype_string::STRING_PRINTF_32: + case message::subtype_string::STRING_PRINTF_64: + sstr << printfFormat( + format, + dest.getHeader().field.subtype == + message::subtype_string::STRING_PRINTF_32, + &data[fmtlen+1], + len - fmtlen - 1); + break; + + case message::subtype_string::STRING_ASSERT: + case message::subtype_string::STRING_FUNCTIONENTER: + case message::subtype_string::STRING_FUNCTIONEXIT: + case message::subtype_string::STRING_GENERIC: + case message::subtype_string::STRING_INVALIDPARAM: + sstr << format; + break; + default: + throw unknown_type_exception(); + break; + } + + dest.setPayload(sstr.str()); +} + +void decoder::decodeCatalogPayload(message& dest, const uint8_t * data, uint32_t len) const +{ + const collateral * coll(dest.getCollateral()); + if (coll == nullptr) throw missing_collateral_exception(); + + std::stringstream sstr; + bool is32Bit(false); + const collateral::sourcepos * srcpos(nullptr); + const std::string * fmt(nullptr); + + switch (dest.getHeader().field.subtype) { + case message::subtype_catalog::CATALOG_ID32_P32: + is32Bit = true; + // fall through + + case message::subtype_catalog::CATALOG_ID32_P64: + { + if (len < sizeof(uint32_t)) throw tooshort_exception(); + const collateral::catalogentry<uint32_t> * entry(nullptr); + + if ((entry = coll->getCatalogEntry(bytes2ValLE<uint32_t>(data))) == nullptr) { + sstr + << "Undefined catalog id 0x" + << toHexValue(bytes2ValLE<uint32_t>(data)); + } else { + fmt = &entry->msg; + srcpos = entry; + } + data += sizeof(uint32_t); + len -= sizeof(uint32_t); + break; + } + case message::subtype_catalog::CATALOG_ID64_P32: + is32Bit = true; + // fall through + case message::subtype_catalog::CATALOG_ID64_P64: + { + if (len < sizeof(uint64_t)) throw tooshort_exception(); + const collateral::catalogentry<uint64_t> * entry(nullptr); + + if ((entry = coll->getCatalogEntry(bytes2ValLE<uint64_t>(data))) == nullptr) { + sstr + << "Undefined catalog id 0x" + << toHexValue(bytes2ValLE<uint64_t>(data)); + } else { + fmt = &entry->msg; + srcpos = entry; + } + data += sizeof(uint64_t); + len -= sizeof(uint64_t); + break; + } + default: + throw unknown_type_exception(); + break; + } + + if (fmt != nullptr) { + sstr << printfFormat(*fmt, is32Bit, data, len); + + // if current message has no file attribute from payload, but the catalog entry + // does provides one, use the one from the catalog + // + if (0 != srcpos->m_file && 0 == (dest.getLocation().tag & message::IDANDLINE)) + { + dest.setLocFileLine(srcpos->m_file, srcpos->m_line); + } + } + + dest.setPayload(sstr.str()); +} + +void decoder::decodeRawPayload(message& dest, const uint8_t * data, uint32_t len) const +{ + std::stringstream sstr; + + while (len--) { + sstr << std::setfill('0') << std::setw(2) << std::hex << (uint16_t)*data++; + } + + dest.setPayload(sstr.str()); +} + +void decoder::loadCollateral(const std::string& filename) +{ + for (auto items : collateral::parseXml(filename)) { + collaterals.insert(collaterals.end(), items); + } +} + +guid decoder::generatePseudoGuid(uint8_t origin) +{ + // A pseudo-guid is defined by storing the origin value into + // bits [7..0] of the guid[8] byte. The highest bit is forced + // to zero, which distiquishes it from an RFC4122 compliant + // GUID. + // This example code has no additional transport data for + // identifying the message client. All other values are + // set to 0. + // + uint8_t pseudo_guid_data[16] = { + 0,0,0,0, + 0,0,0,origin, + 0,0,0,0, + 0,0,0,0 + }; + return guid(pseudo_guid_data); +} + +collateral * decoder::findCollateral(const guid& g) const +{ + // do we already know the catalog for this GUID ? + // + auto cache_hit(collateral_by_guid.find(g)); + if (cache_hit != collateral_by_guid.end()) { + return cache_hit->second; + } + + uint64_t build(0); + auto build_hit(build_by_guid.find(g)); + if (build_hit != build_by_guid.end()) { + build = build_hit->second; + } + + // search all catalogs + // + auto match = std::find_if(collaterals.begin(), collaterals.end(), + [&g,build](const collateral* v) + { + return (v->match(g, build)); + }); + + if (match != collaterals.end()) { + collateral_by_guid[g] = *match; + return *match; + } + return nullptr; +} + +// Message type dependend payload decode handlers +// +decoder::decode_payload_f decoder::payloadDecode[] = { + &decoder::decodeBuildPayload, // [0] + &decoder::decodeInvalidType, // [1] + &decoder::decodeStringPayload, // [2] + &decoder::decodeCatalogPayload, // [3] + &decoder::decodeInvalidType, // [4] + &decoder::decodeInvalidType, // [5] + &decoder::decodeRawPayload, // [6] + &decoder::decodeInvalidType, // [7] + &decoder::decodeInvalidType, // [8] + &decoder::decodeInvalidType, // [9] + &decoder::decodeInvalidType, // [10] + &decoder::decodeInvalidType, // [11] + &decoder::decodeInvalidType, // [12] + &decoder::decodeInvalidType, // [13] + &decoder::decodeInvalidType, // [14] + &decoder::decodeInvalidType // [15] +}; + +// Build CRC over a byte buffer (Castagnoli CRC-32C) +// +uint32_t decoder::getCrc32(const uint8_t * data, size_t len) +{ + uint32_t crc(0xFFFFFFFF); + + while (len--) { + crc = crc32c_table[((int)crc ^ (*data++)) & 0xFF] ^ (crc >> 8); + } + return crc ^ 0xFFFFFFFF; +} + +// Pre-computed CRC-C values for all byte values using the +// polynomial 0x1EDC6F41 (Castagnoli). +// +const uint32_t decoder::crc32c_table[256] = { + 0x00000000, + 0xF26B8303, + 0xE13B70F7, + 0x1350F3F4, + 0xC79A971F, + 0x35F1141C, + 0x26A1E7E8, + 0xD4CA64EB, + 0x8AD958CF, + 0x78B2DBCC, + 0x6BE22838, + 0x9989AB3B, + 0x4D43CFD0, + 0xBF284CD3, + 0xAC78BF27, + 0x5E133C24, + 0x105EC76F, + 0xE235446C, + 0xF165B798, + 0x030E349B, + 0xD7C45070, + 0x25AFD373, + 0x36FF2087, + 0xC494A384, + 0x9A879FA0, + 0x68EC1CA3, + 0x7BBCEF57, + 0x89D76C54, + 0x5D1D08BF, + 0xAF768BBC, + 0xBC267848, + 0x4E4DFB4B, + 0x20BD8EDE, + 0xD2D60DDD, + 0xC186FE29, + 0x33ED7D2A, + 0xE72719C1, + 0x154C9AC2, + 0x061C6936, + 0xF477EA35, + 0xAA64D611, + 0x580F5512, + 0x4B5FA6E6, + 0xB93425E5, + 0x6DFE410E, + 0x9F95C20D, + 0x8CC531F9, + 0x7EAEB2FA, + 0x30E349B1, + 0xC288CAB2, + 0xD1D83946, + 0x23B3BA45, + 0xF779DEAE, + 0x05125DAD, + 0x1642AE59, + 0xE4292D5A, + 0xBA3A117E, + 0x4851927D, + 0x5B016189, + 0xA96AE28A, + 0x7DA08661, + 0x8FCB0562, + 0x9C9BF696, + 0x6EF07595, + 0x417B1DBC, + 0xB3109EBF, + 0xA0406D4B, + 0x522BEE48, + 0x86E18AA3, + 0x748A09A0, + 0x67DAFA54, + 0x95B17957, + 0xCBA24573, + 0x39C9C670, + 0x2A993584, + 0xD8F2B687, + 0x0C38D26C, + 0xFE53516F, + 0xED03A29B, + 0x1F682198, + 0x5125DAD3, + 0xA34E59D0, + 0xB01EAA24, + 0x42752927, + 0x96BF4DCC, + 0x64D4CECF, + 0x77843D3B, + 0x85EFBE38, + 0xDBFC821C, + 0x2997011F, + 0x3AC7F2EB, + 0xC8AC71E8, + 0x1C661503, + 0xEE0D9600, + 0xFD5D65F4, + 0x0F36E6F7, + 0x61C69362, + 0x93AD1061, + 0x80FDE395, + 0x72966096, + 0xA65C047D, + 0x5437877E, + 0x4767748A, + 0xB50CF789, + 0xEB1FCBAD, + 0x197448AE, + 0x0A24BB5A, + 0xF84F3859, + 0x2C855CB2, + 0xDEEEDFB1, + 0xCDBE2C45, + 0x3FD5AF46, + 0x7198540D, + 0x83F3D70E, + 0x90A324FA, + 0x62C8A7F9, + 0xB602C312, + 0x44694011, + 0x5739B3E5, + 0xA55230E6, + 0xFB410CC2, + 0x092A8FC1, + 0x1A7A7C35, + 0xE811FF36, + 0x3CDB9BDD, + 0xCEB018DE, + 0xDDE0EB2A, + 0x2F8B6829, + 0x82F63B78, + 0x709DB87B, + 0x63CD4B8F, + 0x91A6C88C, + 0x456CAC67, + 0xB7072F64, + 0xA457DC90, + 0x563C5F93, + 0x082F63B7, + 0xFA44E0B4, + 0xE9141340, + 0x1B7F9043, + 0xCFB5F4A8, + 0x3DDE77AB, + 0x2E8E845F, + 0xDCE5075C, + 0x92A8FC17, + 0x60C37F14, + 0x73938CE0, + 0x81F80FE3, + 0x55326B08, + 0xA759E80B, + 0xB4091BFF, + 0x466298FC, + 0x1871A4D8, + 0xEA1A27DB, + 0xF94AD42F, + 0x0B21572C, + 0xDFEB33C7, + 0x2D80B0C4, + 0x3ED04330, + 0xCCBBC033, + 0xA24BB5A6, + 0x502036A5, + 0x4370C551, + 0xB11B4652, + 0x65D122B9, + 0x97BAA1BA, + 0x84EA524E, + 0x7681D14D, + 0x2892ED69, + 0xDAF96E6A, + 0xC9A99D9E, + 0x3BC21E9D, + 0xEF087A76, + 0x1D63F975, + 0x0E330A81, + 0xFC588982, + 0xB21572C9, + 0x407EF1CA, + 0x532E023E, + 0xA145813D, + 0x758FE5D6, + 0x87E466D5, + 0x94B49521, + 0x66DF1622, + 0x38CC2A06, + 0xCAA7A905, + 0xD9F75AF1, + 0x2B9CD9F2, + 0xFF56BD19, + 0x0D3D3E1A, + 0x1E6DCDEE, + 0xEC064EED, + 0xC38D26C4, + 0x31E6A5C7, + 0x22B65633, + 0xD0DDD530, + 0x0417B1DB, + 0xF67C32D8, + 0xE52CC12C, + 0x1747422F, + 0x49547E0B, + 0xBB3FFD08, + 0xA86F0EFC, + 0x5A048DFF, + 0x8ECEE914, + 0x7CA56A17, + 0x6FF599E3, + 0x9D9E1AE0, + 0xD3D3E1AB, + 0x21B862A8, + 0x32E8915C, + 0xC083125F, + 0x144976B4, + 0xE622F5B7, + 0xF5720643, + 0x07198540, + 0x590AB964, + 0xAB613A67, + 0xB831C993, + 0x4A5A4A90, + 0x9E902E7B, + 0x6CFBAD78, + 0x7FAB5E8C, + 0x8DC0DD8F, + 0xE330A81A, + 0x115B2B19, + 0x020BD8ED, + 0xF0605BEE, + 0x24AA3F05, + 0xD6C1BC06, + 0xC5914FF2, + 0x37FACCF1, + 0x69E9F0D5, + 0x9B8273D6, + 0x88D28022, + 0x7AB90321, + 0xAE7367CA, + 0x5C18E4C9, + 0x4F48173D, + 0xBD23943E, + 0xF36E6F75, + 0x0105EC76, + 0x12551F82, + 0xE03E9C81, + 0x34F4F86A, + 0xC69F7B69, + 0xD5CF889D, + 0x27A40B9E, + 0x79B737BA, + 0x8BDCB4B9, + 0x988C474D, + 0x6AE7C44E, + 0xBE2DA0A5, + 0x4C4623A6, + 0x5F16D052, + 0xAD7D5351 +}; + +MIPI_SYST_NAMESPACE_END
\ No newline at end of file diff --git a/printer/src/mipi_syst_main.cpp b/printer/src/mipi_syst_main.cpp new file mode 100644 index 0000000..32f6aeb --- /dev/null +++ b/printer/src/mipi_syst_main.cpp @@ -0,0 +1,265 @@ +/* + Copyright (c) 2018, MIPI Alliance, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Contributors: + * Norbert Schulz (Intel Corporation) - Initial API and implementation + */ + +#include <iostream> +#include <fstream> +#include <vector> +#include <string> +#include <memory> + +#include "mipi_syst_decode.h" +#include "mipi_syst_guid.h" + +// print program usage banner +// +static void usage(const std::string& details) +{ + std::cerr << "usage: systprint [-p] [-c <collateralXML>...] [-g short-message-guid] [-o output] [inputfile(s)...]" << std::endl; + std::cerr << " -p / --payload_only only print payload (default is CSV)" << std::endl; + std::cerr << " -c / --colateral filename load given SyS-T collateral XML" << std::endl; + std::cerr << " -g / --short_guid guid guid value for short messages" << std::endl; + std::cerr << " -o / --output file output file name (default stdout)" << std::endl; + std::cerr << " inputfile(s)... file(s) with example library platform output or '-' for stdin" << std::endl; + std::cerr << std::endl; + + if (!details.empty()) { + std::cerr << details << std::endl; + } + exit(1); +} + + +// ASCII char -> HEX nibble value, or 0xFF if invalid +// +static const uint8_t hexCharVal[] = +{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + + +// Convert ASCII hex string into byte array, fail if string is not a +// sequence of 0..(2*n) hex characters +// +static bool hex2bin(const std::string& hexStr, std::vector<uint8_t>& dest) +{ + size_t nibbles(hexStr.size()); + + if (nibbles % 2) { // 2 chars needed for 1 value + return false; + } + dest.clear(); + + for(auto hexChar(hexStr.c_str()); nibbles ; nibbles -= 2) + { + auto high(hexCharVal[(uint8_t)*hexChar++]); + auto low(hexCharVal[(uint8_t)*hexChar++]); + if (high == 0xFF || low == 0xFF) { + return false; + } + dest.push_back((high << 4) | low); + } + return true; +} + + +// Scan input file that is assumed to come from the SyS-T library example platform. +// Extract the raw event HEX data lines that look like this +// +// SYS-T RAW DATA: 4202000B0... +// +// Then parse the hex portion of the line and try to decode it as a SyS-T message. +// +static void readAndPrint(std::istream& is, std::ostream& os, + bool payload_only, + const mipi::syst::decode_context * ctx, + const mipi::syst::decoder& decoder) +{ + static const std::string pattern("SYS-T RAW DATA: "); + std::string line; + + while (getline(is, line)) { + if (line.compare(0, pattern.size(), pattern)) { + continue; // not a raw dump of a SyS-T message + } + + // sanitize line endings + // + while (line.size() && + (line[line.size()-1] == '\r' || + line[line.size()-1] == '\n')) + { + line.erase(line.size()-1); + } + std::vector<uint8_t> bytes; + if (!hex2bin(line.substr(pattern.size()), bytes)) { + continue; + } + mipi::syst::message msg; + + decoder.decode(msg, bytes, ctx); + if (payload_only) { + os << msg.getPayload() << std::endl; + } else { + os << msg; + } + } +} + +/// Simple GUID context wrapper +// +// This class implements a trivial decode context provider by just +// wrapping a guid set from the command line. A real implementation would +// encapsulate transport information here to build client identifying +// information. @see mipi::syst::decode_context +// +class short_guid_context : public mipi::syst::decode_context { +public: + short_guid_context(const mipi::syst::guid& g) : m_guid(g), m_fakeTS(0) {} + + const mipi::syst::guid& getGuid() const { + return m_guid; + } + + uint64_t getTS() const { + return m_fakeTS++; // replace with transport TS + } +private: + mipi::syst::guid m_guid; + mutable uint64_t m_fakeTS; +}; + + +int main(int argc, char ** argv) +{ + std::vector<std::string> inputs; + std::string output; + bool payload_only(false); + std::shared_ptr<short_guid_context> short_ctx(nullptr); // guid to use for short messages + + mipi::syst::decoder decoder; + + // parse & check arguments + // + for (auto i = 1; i < argc; ++i) { + const std::string arg(argv[i]); + if (arg == "-c" || arg == "--collateral") { + if (++i >= argc) { + usage("missing collateral file argument"); + } + try { + decoder.loadCollateral(argv[i]); + } + catch (std::exception& e) { + std::cerr << "error loading '" << argv[i] << "': " << e.what(); + exit(1); + } + } else if (arg == "-p" || arg == "--payload_only") { + payload_only = true; + } else if (arg == "-g" || arg == "--short_guid") { + if (++i >= argc) { + usage("missing guid argument for -g/--short_guid option"); + } + try { + short_ctx = std::make_shared<short_guid_context>( + mipi::syst::guid(argv[i])); + } catch (std::exception& e) { + std::cerr << e.what(); + exit(1); + } + } else if (arg == "-o" || arg == "--output") { + if (++i >= argc) { + usage("missing filename argument for -o/--output option"); + } + output = argv[i]; + } else if (arg == "-") { //stdin + inputs.push_back(arg); + } else if (arg[0] == '-') { + usage(std::string("unknown argument : ") + arg); + } else { + inputs.push_back(arg); + } + } + + if (inputs.empty()) { + usage("no input provided"); + } + + // set output destination (default stdout) + // + std::ostream * os(&std::cout); + std::ofstream ofs; + + if (!output.empty()) { + ofs.open(output.c_str(), std::ofstream::out); + if (!ofs.is_open()) { + std::cerr << "unable to open output file : " << output << std::endl; + exit(1); + } + os = &ofs; + } + + if (!payload_only) { + *os << mipi::syst::message::csvHeaderString << std::endl; + } + for (auto input : inputs) { + if (input == "-") { + readAndPrint(std::cin, *os, payload_only, short_ctx.get(), decoder); + } else { + std::ifstream ifs(input); + if (!ifs.is_open()) { + std::cerr << "unable to open input file : " << input << std::endl; + exit(1); + } + readAndPrint(ifs, *os, payload_only, short_ctx.get(), decoder); + ifs.close(); + } + } + + if (ofs.is_open()) { + ofs.close(); + } + + return 0; +}
\ No newline at end of file diff --git a/printer/src/mipi_syst_message.cpp b/printer/src/mipi_syst_message.cpp new file mode 100644 index 0000000..f67c2b6 --- /dev/null +++ b/printer/src/mipi_syst_message.cpp @@ -0,0 +1,371 @@ +/* + Copyright (c) 2018, MIPI Alliance, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Contributors: + * Norbert Schulz (Intel Corporation) - Initial API and implementation + */ + +#include<sstream> +#include <iomanip> +#include "mipi_syst_message.h" +#include "mipi_syst_collateral.h" +#include "mipi_syst_printf.h" + +MIPI_SYST_NAMESPACE_BEGIN + +// Code for storing and printing out decoded message content +// + +void message::setLocAddr32(uint32_t addr) +{ + m_loc.tag |= location_type::ADDRESS32; + m_loc.tag &= ~location_type::ADDRESS64; + m_loc.address = addr; +} + +void message::setLocAddr64(uint64_t addr) +{ + m_loc.tag |= location_type::ADDRESS64; + m_loc.tag &= ~location_type::ADDRESS32; + m_loc.address = addr; +} + +void message::setLocFileLine(uint32_t file, uint32_t line) +{ + m_loc.tag |= location_type::IDANDLINE; + m_loc.file = file; + m_loc.line = line; +} + +// Check type for short message +// +bool message::isShort(message::header hdr) { + switch (hdr.field.type) { + case message::type::SHORT32: + case message::type::SHORT64: + return true; + + case message::type::BUILD: + switch (hdr.field.subtype) { + case message::subtype_build::BUILD_COMPACT32: + case message::subtype_build::BUILD_COMPACT64: + return true; + } + } + return false; +} + + +// Pretty print decode status value +// +static const char * status2string(const message& msg) +{ + switch (msg.getState()) { + case message::decode_state::OK: + return "OK"; + case message::decode_state::UNKNOWN_TYPE: + return "UNKNOWN_TYPE"; + case message::decode_state::TOO_SHORT: + return "TOO_SHORT"; + case message::decode_state::CHECKSUM_ERROR: + return "CHECKSUM_ERROR"; + case message::decode_state::MISSING_COLLATERAL: + return "MISSING_COLLATERAL"; + default: + return "UNKNOWN_STATE"; + } +} + +// Pretty print sevrity values +// +static const char * severity2string[] = { + "MAX", + "FATAL", + "ERROR", + "WARNING", + "INFO", + "USER1", + "USER2", + "DEBUG" +}; + +// Pretty print type:subtype values +// +static const std::string type2string( + message::header hdr, + const collateral * coll) +{ + static const char * typenames[] = { + "BUILD", + "SHORT32", + "STRING", + "CATALOG", + "UNKNOWN(4)", + "UNKNOWN(5)", + "RAW", + "SHORT64", + "CLOCK", + "UNKNOWN(9)", + "UNKNOWN(10)", + "UNKNOWN(11)", + "UNKNOWN(12)", + "UNKNOWN(13)", + "UNKNOWN(14)", + "UNKNOWN(15)", + }; + + uint32_t type(hdr.field.type); + std::stringstream sstr; + + sstr << typenames[type]; + + // compute subtype string + // + std::string subtype("??"); + + switch (type) { + case message::type::SHORT32: + case message::type::SHORT64: + subtype.clear(); // no subtype + break; + + case message::type::BUILD: + switch (hdr.field.subtype) { + case message::subtype_build::BUILD_LONG: + subtype = "LONG"; + break; + case message::subtype_build::BUILD_COMPACT32: + subtype = "COMPACT32"; + break; + case message::subtype_build::BUILD_COMPACT64: + subtype = "COMPACT64"; + break; + } + break; + case message::type::STRING: + switch (hdr.field.subtype) { + case message::subtype_string::STRING_GENERIC: + subtype = "GENERIC"; + break; + case message::subtype_string::STRING_FUNCTIONENTER: + subtype = "ENTER"; + break; + case message::subtype_string::STRING_FUNCTIONEXIT: + subtype = "EXIT"; + break; + case message::subtype_string::STRING_INVALIDPARAM: + subtype = "INVPARAM"; + break; + case message::subtype_string::STRING_ASSERT: + subtype = "ASSERT"; + break; + case message::subtype_string::STRING_PRINTF_32: + subtype = "PRINTF32"; + break; + case message::subtype_string::STRING_PRINTF_64: + subtype = "PRINTF64"; + break; + } + break; + case message::type::CATALOG: + switch (hdr.field.subtype) { + case message::subtype_catalog::CATALOG_ID32_P32: + subtype = "ID32P32"; + break; + case message::subtype_catalog::CATALOG_ID64_P32: + subtype = "ID64P32"; + break; + case message::subtype_catalog::CATALOG_ID32_P64: + subtype = "ID32P64"; + break; + case message::subtype_catalog::CATALOG_ID64_P64: + subtype = "ID64P64"; + break; + } + break; + case message::type::RAW: + { + const std::string * name(nullptr); + if (coll && (name = coll->getWriteType(hdr.field.subtype)) != nullptr) + { + std::stringstream dest; + hostPrintf(dest, *name, hdr.field.subtype, std::vector<int>()); + subtype = dest.str(); + } else { + subtype = std::to_string(hdr.field.subtype); + } + } break; + + case message::type::CLOCK: + switch (hdr.field.subtype) { + case message::subtype_clock::CLOCK_SYNC: + subtype = "SYNC"; + break; + } + break; + } + + if (!subtype.empty()) { + sstr << ':' << subtype; + } + + return sstr.str(); +} + +// CSV double-quote escaping, replace " with "" and eliminate newlines +// +void csvQuoteString(std::ostream& os, const std::string& s) +{ + for (auto c : s) { + if (c == '\n') { + os << ' '; // newline not supported in CSV + } else if (c == '"') { + os << c << c; + } else { + os << c; + } + } +} + +// location information printing +// +std::string location2string( + const message::location& loc, + const collateral * collateral) +{ + bool hasAddress(0!=(loc.tag & (message::ADDRESS32 | message::ADDRESS32))); + bool hasFileLine(0 != (loc.tag & message::IDANDLINE)); + + std::stringstream sstr; + + if (hasFileLine) { + const std::string * fileName( + collateral != nullptr ? + collateral->getSourceFile(loc.file) : nullptr); + + if (fileName != nullptr) { + sstr << (*fileName) << ':' << loc.line; + } else { + sstr << loc.file << ':' << loc.line; + } + if (hasAddress) sstr << ' '; + } + + if (hasAddress) { + if (loc.tag & message::ADDRESS32) { + sstr << toHexValue((uint32_t)loc.address); + } else { + sstr << toHexValue(loc.address); + } + } + + return sstr.str(); +} +const char message::csvHeaderString[] = "Decode Status,Payload,Type,Severity,Origin,Unit,Message TimeStamp,Context TimeStamp,Location,Raw Length,Checksum,Collateral"; + +// CSV line formatting of a message +// +std::ostream& operator << (std::ostream& os, const message& msg) +{ + char delimiter(','); + const collateral * coll(msg.getCollateral()); + + // turn volatile fields like timestamp/crc off when running unit testing + // + static bool unit_testing(getenv("SYST_UNITTESTING") != NULL); + + // status + os << status2string(msg) << delimiter; + + // payload + os << '"'; + csvQuoteString(os, msg.getPayload()); + os << '"' << delimiter; + + if (msg.getState() != message::OK && msg.getState() != message::MISSING_COLLATERAL) + { + // decode failed, report other fields as empty. The data can't be trusted. + // + os << ",,,,," << std::endl; + return os; + } + + // Type/Subtype + // + os << type2string(msg.getHeader(), msg.getCollateral()) << delimiter; + + // Severity + // + os << severity2string[msg.getHeader().field.severity] << delimiter; + + // client name and unit + // + os << msg.getClientName() << delimiter; + os << msg.getUnit() << delimiter; + + // time stamps + if (msg.getHeader().field.timestamp) { + os << (unit_testing ? + "<--UNITTEST-HIDE_TS-->" : toHexValue(msg.getMessageTS())); + } + os << delimiter; + + os << (unit_testing ? + "<--UNITTEST-HIDE_TS-->" : toHexValue(msg.getContextTS())) << delimiter; + + // location + // + csvQuoteString(os, location2string(msg.getLocation(), coll)); + os << delimiter; + + // raw message length + // + os << std::to_string(msg.getLength()) << delimiter; + + // crc + // + if (msg.getHeader().field.chksum) { + os << (unit_testing ? "<--UNITTEST-HIDE_CRC-->" : toHexValue(msg.getCrc())); + } + os << delimiter; + + if (coll != nullptr) { + csvQuoteString(os, coll->getFileName()); + } + os << std::endl; + + return os; +} + +MIPI_SYST_NAMESPACE_END
\ No newline at end of file diff --git a/printer/src/mipi_syst_printf.cpp b/printer/src/mipi_syst_printf.cpp new file mode 100644 index 0000000..e0da037 --- /dev/null +++ b/printer/src/mipi_syst_printf.cpp @@ -0,0 +1,435 @@ +/* + Copyright (c) 2018, MIPI Alliance, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Contributors: + * Norbert Schulz (Intel Corporation) - Initial API and implementation + */ + +#include <sstream> +#include "mipi_syst_printf.h" + +MIPI_SYST_NAMESPACE_BEGIN + +template<> double bytes2ValLE<double>(const void * data) +{ + union { uint64_t ll; double d; } u = { bytes2ValLE<uint64_t>(data) }; + return u.d; +} + +// Parse printf style format string into a sequence of text and format fragments +// +uint32_t +fmtscanner::parse(const std::string& fmt, Args& args) +{ + uint32_t result(OK); + + const char * p(fmt.c_str()); + const char * eof(p + fmt.size()); + FmtScanState state(START); + Modifier modifier(MOD_NONE); + size_t fmtPos = 0; // curremt position in format + size_t fragmentStart = 0; // where current fragment started + size_t lastPercentPos = 0; // where the last % fmt start was seen + + args.clear(); + + while (p < eof) { + switch (state) { + case START: + fragmentStart = fmtPos; + modifier = MOD_NONE; + + state = PLAINTEXT; + ; // deliberate fall through + + case PLAINTEXT: + if (*p == '%') { + lastPercentPos = fmtPos; + state = PERCENT; + } + break; + + case PERCENT: + if (*p == '%') { + lastPercentPos = 0; // '%%' is not a format, but the % char + state = PLAINTEXT; + } else { + // arg fmt definition is starting + // + if (lastPercentPos != fragmentStart) { + args.push_back( + ArgFragment(TEXT, 0, + fmt.substr(fragmentStart, + lastPercentPos - fragmentStart))); + } + fragmentStart = lastPercentPos; + state = FLAGS; + continue; + } + break; + + case FLAGS: + switch (*p) { + case '-': + case '+': + case ' ': + case '#': + case '0': + break; + default: + state = WIDTH; + continue; + break; + } + break; + + case WIDTH: + if (*p == '*') { + args.push_back(ArgFragment(STAR, SIZEOF_INT, "")); + state = PRECISION_DOT; + } else { + state = WIDTH_NUMBER; + continue; + } + break; + + case WIDTH_NUMBER: + if (!isdigit(*p)) { + state = PRECISION_DOT; + continue; + } + break; + + case PRECISION_DOT: + if (*p == '.') { + state = PRECISION_VAL; + } else { + state = MODIFIER; + continue; + } + break; + + case PRECISION_VAL: + if (*p == '*') { + args.push_back(ArgFragment(STAR, SIZEOF_INT, std::string())); + state = MODIFIER; + } else { + state = PRECISION_NUMBER; + continue; + } + break; + + case PRECISION_NUMBER: + if (!isdigit(*p)) { + state = MODIFIER; + continue; + } + break; + + case MODIFIER: + state = SPECIFIER; + + switch (*p) { + case 'h': + modifier = MOD_H; + state = MODIFIER_HALF; + break; + case 'l': + modifier = MOD_L; + state = MODIFIER_LONG; + break; + case 'j': + modifier = MOD_J; + break; + case 'z': + modifier = MOD_Z; + break; + case 't': + modifier = MOD_T; + break; + case 'L': + modifier = MOD_LD; + break; + default: + continue; + } + break; + + case MODIFIER_HALF: + state = SPECIFIER; + if (*p == 'h') { + modifier = MOD_HH; + break; + } else { + continue; + } + break; + + + case MODIFIER_LONG: + state = SPECIFIER; + if (*p == 'l') { + modifier = MOD_LL; + break; + } else { + continue; + } + + case SPECIFIER: + { + std::string fragment = fmt.substr(fragmentStart, + fmtPos - fragmentStart + 1); + + switch (*p) { + case 'd': + case 'i': + case 'u': + case 'o': + case 'x': + case 'X': + switch (modifier) { + case MOD_L: + // convert long to longlong if client is 64bit, but host 32bit + // + if ((SIZEOF_LONG == 8) && (sizeof(long) != 8)) { + size_t pos(fragment.find('l')); + if (pos != std::string::npos) { + fragment = fragment.insert(pos, "l"); + } + } + args.push_back(ArgFragment(INTEGER, SIZEOF_LONG, fragment)); + break; + case MOD_LL: + case MOD_J: + args.push_back(ArgFragment(INTEGER, SIZEOF_LONGLONG, fragment)); + break; + case MOD_Z: + args.push_back(ArgFragment(INTEGER, SIZEOF_SIZEOF, fragment)); + break; + default: + args.push_back(ArgFragment(INTEGER, SIZEOF_INT, fragment)); + break; + } + state = START; + break; + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'a': + case 'A': + args.push_back(ArgFragment(DOUBLE, SIZEOF_LONGLONG, fragment)); + break; + case 'c': + args.push_back(ArgFragment(INTEGER, SIZEOF_INT, fragment)); + break; + case 'p': + args.push_back(ArgFragment(POINTER, SIZEOF_PTR, fragment)); + break; + case 's': + args.push_back(ArgFragment(STRING, SIZEOF_PTR, fragment)); + break; + case 'n': + args.push_back(ArgFragment(PERCENT_N, SIZEOF_PTR, fragment)); + break; + default: // unsupported format + args.push_back(ArgFragment(UNKNOWN, 0, fragment)); + result |= UNKNOWN_FORMAT; + break; + } + state = START; + } + break; + + default: + break; + } + ++p; + ++fmtPos; + } + + // check if we have a tail plain text string at the end of the format + // and add it to arg list. + // + if (state == PLAINTEXT) { + args.push_back(ArgFragment(TEXT, 0, + fmt.substr(fragmentStart, fmtPos - fragmentStart + 1))); + state = START; + } + + if (state != START) { + result |= INCOMPLETE_FORMAT; + } + + return result; +} + + +fmtscanner::ArgFragment::ArgFragment( + ArgType type, size_t size, const std::string& text) : + _type(type), _size(size), _text(text) +{} + + + +bool msgprintf::format( + const std::string& fmt, + const void * args, uint32_t size, + std::string& result) +{ + std::stringstream sstr; // where the result is build + + // Parse the format string for the number of needed parameters + // + uint32_t parseResult; + if ((parseResult = _scanner.parse(fmt, _args)) != fmtscanner::OK) { + sstr << "invalid format string '" << fmt << "'"; + result = sstr.str(); + return false; + } + + const char * bufPtr((const char *)args); //current byte from arg buffer + const void * eob((const char *)args + size); // end of buffer address + std::vector<int> starArgs; // '*' args like in "%*.*f" + bool success(true); + + // Loop over the argument fragments to format each using local printf + // and then add it to the result string. + // + for (fmtscanner::Args::const_iterator it(_args.begin()); + it != _args.end(); + ++it) + { + size_t need(it->size()); // number of bytes needed for format + fmtscanner::ArgType type(it->type()); + + switch (type) { + case fmtscanner::INTEGER: + case fmtscanner::POINTER: + if (bufPtr + need <= eob) { + if (need == sizeof(uint64_t)) { + uint64_t val(bytes2ValLE<uint64_t>(bufPtr)); + success = hostPrintf(sstr, it->text(), val, starArgs); + } else { + uint32_t val(bytes2ValLE<uint32_t>(bufPtr)); + success = hostPrintf(sstr, it->text(), val, starArgs); + } + } + break; + + case fmtscanner::DOUBLE: + if (bufPtr + need <= eob) { + double val(bytes2ValLE<double>(bufPtr)); + success = hostPrintf(sstr, it->text(), val, starArgs); + } + break; + + case fmtscanner::TEXT: + // copy text, but "%%" means "%" in printf format + // + for (const char *c = it->text().c_str(); *c; ++c) { + if ('%' == *c && '%' == *(c+1)) continue; + sstr << *c; + } + + break; + + case fmtscanner::STRING: + // String embedded in message buffer + // check if it is valid (zero terminated inside message) and + // then format it into result + // + { + const char * str(bufPtr); + + // Hunt the null byte + // + for (need = 1; str <= eob && *str; ++str, ++need) + ; + + if (str > eob) { + result = " - corrupt printf payload, missing argument string termination"; + return false; + } + + success = hostPrintf(sstr, it->text(), bufPtr, starArgs); + } + break; + case fmtscanner::PERCENT_N: + // %n is a nop . There is no usefull place to return the + // output to. + // + break; + + case fmtscanner::STAR: + // A star argument was seen in fmt, put on the *-stack (can be up to 2) + // + if (bufPtr + need <= eob) { + starArgs.push_back(bytes2ValLE<uint32_t>(bufPtr)); + } + break; + + default: + sstr << "- printf internal error, unkown argtype in format '" + << fmt << "'"; + success = false; + } + + // Check for overflow in input buffer + // + if ((bufPtr + need) > eob) { + sstr << "- insufficient argument bytes for format '" + << fmt << "'"; + success = false; + } + + if (!success) { + result = sstr.str(); + return false; + } + + if (type != fmtscanner::STAR) { + starArgs.clear(); + } + bufPtr += need; + } + + result = sstr.str(); + + return true; +} + +MIPI_SYST_NAMESPACE_END
\ No newline at end of file |