diff options
Diffstat (limited to 'decoder/tests/snapshot_parser_lib/source/snapshot_parser.cpp')
-rw-r--r-- | decoder/tests/snapshot_parser_lib/source/snapshot_parser.cpp | 840 |
1 files changed, 840 insertions, 0 deletions
diff --git a/decoder/tests/snapshot_parser_lib/source/snapshot_parser.cpp b/decoder/tests/snapshot_parser_lib/source/snapshot_parser.cpp new file mode 100644 index 0000000000000..6e62d1e200c2d --- /dev/null +++ b/decoder/tests/snapshot_parser_lib/source/snapshot_parser.cpp @@ -0,0 +1,840 @@ +/* + * \file snapshot_parser.cpp + * \brief OpenCSD : Snapshot Parser Library + * + * \copyright Copyright (c) 2015, ARM Limited. All Rights Reserved. + */ + +/* + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. 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 HOLDER 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. + */ + +#include "snapshot_parser.h" + +#include <memory> +#include <algorithm> +#include <istream> +#include <iostream> +#include <string> +#include <utility> +using namespace std; + +#include "snapshot_parser_util.h" +#include "ini_section_names.h" +using namespace Util; +using namespace Parser; + +#include "opencsd.h" + +/************************************************************************* + * Note, this file handles the parsring of the general (device specific) + * ini file and the (much smaller) device_list file + *************************************************************************/ + +namespace ParserPrivate +{ + //! Handle CRLF terminators and '#' and ';' comments + void CleanLine(string& line) + { + string::size_type endpos = line.find_first_of("\r;#"); + if (endpos != string::npos) + { + line.erase(endpos); + } + } + + //! Split foo=bar into pair <foo, bar> + pair<string, string> SplitKeyValue(const string& kv) + { + string::size_type eq(kv.find('=')); + if (eq == string::npos) + { + throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_TEST_SNAPSHOT_PARSE, "Couldn't parse '" + kv + "' as key=value"); + } + return make_pair(Trim(kv.substr(0, eq)), Trim(kv.substr(eq + 1))); + } + + //! Whether line is just tabs and spaces + bool IsEmpty(const string& line) + { + return TrimLeft(line) == ""; + } + + /*! \brief Whether line is of form '[header]' + * \param line the line + * \param sectionName if function returns true, returns the text between the brackets + */ + bool IsSectionHeader(const string& line, string& sectionName) + { + string::size_type openBracket(line.find('[')); + if (openBracket == string::npos) + { + return false; + } + string::size_type textStart(openBracket + 1); + string::size_type closeBracket(line.find(']', textStart)); + if (closeBracket == string::npos) + { + return false; + } + sectionName.assign(Trim(line.substr(textStart, closeBracket - textStart))); + return true; + } + + template <class M, class K, class V> + void AddUniqueKey(M& m, const K& key, const V& value, const std::string &keyStr ) + { + if (!m.insert(make_pair(key, value)).second) + { + throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_TEST_SNAPSHOT_PARSE, "Duplicate key: " + keyStr); + } + } + + void PreventDupes(bool& store, const string& key, const string& section) + { + if (store) + { + throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_TEST_SNAPSHOT_PARSE, + "Duplicate " + key + " key found in " + + section + " section"); + } + store = true; + } + + + /*! \class Section + * \brief Handle an ini file section begun with a section header ([header]) + */ + class Section + { + public: + virtual ~Section() {} + + //! Notify a key=value definition + virtual void Define(const string& k, const string& v) = 0; + + //! Notify end of section - we can't handle in dtor because misparses throw. + virtual void End() = 0; + }; + + //! The initial state + class NullSection : public Section + { + public: + void Define(const string& k, const string&) + { + throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_TEST_SNAPSHOT_PARSE, "Definition of '" + k + "' has no section header"); + } + void End() {} + }; + + //! Silently ignore sections that are undefined + class IgnoredSection : public Section + { + public: + void Define(const string& , const string&) + { + } + void End() {} + }; + + //! Handle a [global] section. + class GlobalSection : public Section + { + public: + GlobalSection(Parsed& result) : m_result(result), m_got_core() + { + if (m_result.foundGlobal) + { + throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_TEST_SNAPSHOT_PARSE, string("Only one ") + GlobalSectionName + " section allowed"); + } + m_result.foundGlobal = true; + } + + void Define(const string& k, const string& v) + { + if (k == CoreKey) + { + PreventDupes(m_got_core, CoreKey, GlobalSectionName); + m_result.core.assign(v); + } + else + { + throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_TEST_SNAPSHOT_PARSE, "Unknown global option '" + k + '\''); + } + } + + void End() {} + + private: + Parsed& m_result; + bool m_got_core; + }; + + //! Handle a [dump] section + class DumpSection : public Section + { + public: + DumpSection(Parsed& result) + : m_result(result), + m_got_file(), m_got_address(), m_got_length(), m_got_offset(), m_got_space(), + m_address(), m_length(), m_offset(), m_file(), m_space() + {} + + void Define(const string& k, const string& v) + { + if (k == DumpAddressKey) + { + PreventDupes(m_got_address, DumpAddressKey, DumpFileSectionPrefix); + m_address = DecodeUnsigned<uint64_t>(v); + } + else if (k == DumpLengthKey) + { + PreventDupes(m_got_length, DumpLengthKey, DumpFileSectionPrefix); + m_length = DecodeUnsigned<size_t>(v); + } + else if (k == DumpOffsetKey) + { + PreventDupes(m_got_offset, DumpOffsetKey, DumpFileSectionPrefix); + m_offset = DecodeUnsigned<size_t>(v); + } + else if (k == DumpFileKey) + { + PreventDupes(m_got_file, DumpFileKey, DumpFileSectionPrefix); + m_file = Trim(v, "\"'"); // strip quotes + } + else if (k == DumpSpaceKey) + { + PreventDupes(m_got_space, DumpSpaceKey, DumpFileSectionPrefix); + m_space = Trim(v, "\"'"); // strip quotes + } + else + { + throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_TEST_SNAPSHOT_PARSE, "Unknown dump section key '" + k + '\''); + } + } + + void End() + { + if (!m_got_address) + { + throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_TEST_SNAPSHOT_PARSE, "Dump section is missing mandatory address definition"); + } + if (!m_got_file) + { + throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_TEST_SNAPSHOT_PARSE, "Dump section is missing mandatory file definition"); + } + + struct DumpDef add = { m_address, m_file, m_length, m_offset, m_space}; + m_result.dumpDefs.push_back(add); + } + + private: + Parsed& m_result; + bool m_got_file; + bool m_got_address; + bool m_got_length; + bool m_got_offset; + bool m_got_space; + uint64_t m_address; + size_t m_length; + size_t m_offset; + string m_file; + string m_space; + }; + + //! Handle an [extendregs] section. + class ExtendRegsSection : public Section + { + public: + ExtendRegsSection(Parsed& result) : m_result(result) + {} + + void Define(const string& k, const string& v) + { + AddUniqueKey(m_result.extendRegDefs, DecodeUnsigned<uint32_t>(k), DecodeUnsigned<uint32_t>(v),k); + } + + void End() {} + + private: + Parsed& m_result; + }; + + // Handle a [regs] section + class SymbolicRegsSection : public Section + { + public: + SymbolicRegsSection(Parsed& result) : m_result(result) + {} + + void Define(const string& k, const string& v) + { + const string value = Trim(v, "\"'"); // strip quotes + AddUniqueKey(m_result.regDefs, k, value,k); + } + + void End() {} + + private: + Parsed& m_result; + }; + + // Handle a [device] section + class DeviceSection : public Section + { + public: + DeviceSection(Parsed& result) : m_result(result), gotName(false), gotClass(false), gotType(false) + {} + + void Define(const string& k, const string& v) + { + if (k == DeviceNameKey) + { + PreventDupes(gotName, k, DeviceSectionName); + m_result.deviceName = v; + } + else if(k == DeviceClassKey) + { + PreventDupes(gotClass, k, DeviceSectionName); + m_result.deviceClass = v; + } + else if(k == DeviceTypeKey) + { + PreventDupes(gotType, k, DeviceSectionName); + m_result.deviceTypeName = v; + } + } + + void End() {} + + private: + Parsed& m_result; + bool gotName; + bool gotClass; + bool gotType; + }; + + //! Instantiate the appropriate handler for the section name + auto_ptr<Section> NewSection( const string& sectionName, Parsed& result) + { + LogInfoStr( "Start of " + sectionName + " section\n"); + + if (sectionName == GlobalSectionName) + { + return auto_ptr<Section>(new GlobalSection(result)); + } + if (sectionName.substr(0,DumpFileSectionLen) == DumpFileSectionPrefix) + { + return auto_ptr<Section>(new DumpSection(result)); + } + else if (sectionName == ExtendedRegsSectionName) + { + return auto_ptr<Section>(new ExtendRegsSection(result)); + } + else if (sectionName == SymbolicRegsSectionName) + { + return auto_ptr<Section>(new SymbolicRegsSection(result)); + } + else if (sectionName == DeviceSectionName) + { + return auto_ptr<Section>(new DeviceSection(result)); + } + else + { + LogInfoStr("Unknown section ignored: " + sectionName + "\n"); + return auto_ptr<Section>(new IgnoredSection); + } + } + + /***** Device List file parsing *********************/ + //! Handle a [device_list] section. + class DeviceListSection : public Section + { + public: + DeviceListSection(ParsedDevices& result) : m_result(result), nextId(1) + {} + + void Define(const string& , const string& v) + { + // throw away supplied key - DTSL wants them monotonically increasing from 1 + std::ostringstream id; + id << nextId++; + m_result.deviceList[id.str()] = v; + } + + void End() {} + + private: + ParsedDevices& m_result; + uint32_t nextId; + }; + + //! Instantiate the appropriate handler for the section name + auto_ptr<Section> NewDeviceList(const string& sectionName, ParsedDevices& result) + { + LogInfoStr("Start of " + sectionName + " section\n"); + + if (sectionName == DeviceListSectionName) + { + return auto_ptr<Section>(new DeviceListSection(result)); + } + else + { + // ignore unexpected sections, there may be others like [trace] + // which RDDI doesn't care about + return auto_ptr<Section>(new NullSection); + } + } + + // Handle a [snapshot] section + class SnapshotSection : public Section + { + public: + SnapshotSection(SnapshotInfo& result) : m_result(result), m_gotDescription(false), m_gotVersion(false) + {} + + void Define(const string& k, const string& v) + { + if (k == VersionKey) + { + PreventDupes(m_gotVersion, k, SnapshotSectionName); + m_result.version = v; + // the only valid contents of this are 1.0, as this is the version that introduced the "snapshot" section + if (v != "1.0" && v != "1") + throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_TEST_SNAPSHOT_PARSE, "Illegal snapshot file version: " + v); + } + else if (k == DescriptionKey) + { + PreventDupes(m_gotDescription, k, SnapshotSectionName); + m_result.description = v; + } + } + SnapshotInfo getSnapshotInfo() { return m_result; } + void End() {} + + private: + SnapshotInfo &m_result; + bool m_gotDescription; + bool m_gotVersion; + + }; + + //! Instantiate the appropriate handler for the section name + auto_ptr<Section> NewSnapshotInfo(const string& sectionName, ParsedDevices& result) + { + LogInfoStr((std::string)"Start of " + sectionName + (std::string)" section\n"); + + if (sectionName == SnapshotSectionName) + { + return auto_ptr<Section>(new SnapshotSection(result.snapshotInfo)); + } + else + { + // ignore unexpected sections, there may be others like [trace] + // which RDDI doesn't care about + return auto_ptr<Section>(new NullSection); + } + }; + + class TraceSection : public Section + { + public: + TraceSection(ParsedDevices& result) : m_result(result), gotName(false) + {} + + void Define(const string& k, const string& v) + { + if (k == MetadataKey) + { + PreventDupes(gotName, k, TraceSectionName); + m_result.traceMetaDataName = v; + } + } + + void End() {} + + private: + ParsedDevices& m_result; + bool gotName; + }; + + //! Instantiate the appropriate handler for the section name + auto_ptr<Section> NewTraceMetaData(const string& sectionName, ParsedDevices& result) + { + LogInfoStr((std::string)"Start of " + sectionName + (std::string)" section\n"); + + if (sectionName == TraceSectionName) + { + return auto_ptr<Section>(new TraceSection(result)); + } + else + { + // ignore unexpected sections, there may be others like [trace] + // which RDDI doesn't care about + return auto_ptr<Section>(new NullSection); + } + }; + + class TraceBufferListSection : public Section + { + public: + TraceBufferListSection(ParsedTrace& result) : m_result(result), gotList(false) + {} + + void Define(const string& k, const string& v) + { + if (k == BufferListKey) + { + PreventDupes(gotList, k, TraceBuffersSectionName); + std::string nameList = v; + std::string::size_type pos; + while((pos = nameList.find_first_of(',')) != std::string::npos) + { + m_result.buffer_section_names.push_back(nameList.substr(0,pos)); + nameList=nameList.substr(pos+1,std::string::npos); + } + m_result.buffer_section_names.push_back(nameList); + } + } + + void End() {} + + private: + ParsedTrace& m_result; + bool gotList; + }; + + //! Instantiate the appropriate handler for the section name + + + class TraceBufferSection : public Section + { + public: + TraceBufferSection(ParsedTrace& result, const std::string §ionName) : m_result(result), m_sectionName(sectionName), + name(""), file(""), format(""), gotName(false), gotFile(false), gotFormat(false) + {} + + void Define(const string& k, const string& v) + { + if (k == BufferNameKey) + { + PreventDupes(gotName, k, m_sectionName); + name = v; + } + else if (k == BufferFileKey) + { + PreventDupes(gotFile, k, m_sectionName); + file = v; + } + else if (k == BufferFormatKey) + { + PreventDupes(gotFormat, k, m_sectionName); + format = v; + } + } + + void End() + { + if (!gotName) + { + throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_TEST_SNAPSHOT_PARSE, "Trace Buffer section missing required buffer name"); + } + if (!gotFile) + { + throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_TEST_SNAPSHOT_PARSE, "Trace Buffer section is missing mandatory file definition"); + } + + struct TraceBufferInfo info = { name, file, format }; + m_result.trace_buffers.push_back(info); + } + + + private: + ParsedTrace& m_result; + std::string m_sectionName; + std::string name; + bool gotName; + std::string file; + bool gotFile; + std::string format; + bool gotFormat; + + }; + + class TraceSourceBuffersSection : public Section + { + public: + TraceSourceBuffersSection(ParsedTrace& result) : m_result(result) + {} + + void Define(const string& k, const string& v) + { + // k is the source name, v is the buffer name + m_result.source_buffer_assoc[k] = v; + } + + void End() {} + + private: + ParsedTrace& m_result; + }; + + class TraceCpuSourceSection : public Section + { + public: + TraceCpuSourceSection(ParsedTrace& result) : m_result(result) + {} + + void Define(const string& k, const string& v) + { + // k is the cpu name, v is the source name + m_result.cpu_source_assoc[v] = k; + } + + void End() {} + + private: + ParsedTrace& m_result; + }; + + auto_ptr<Section> NewTraceSection(const string& sectionName, ParsedTrace& result) + { + LogInfoStr((std::string)"Start of " + sectionName + (std::string)" section\n"); + + if (sectionName == TraceBuffersSectionName) + { + return auto_ptr<Section>(new TraceBufferListSection(result)); + } + else if(sectionName == SourceBuffersSectionName) + { + return auto_ptr<Section>(new TraceSourceBuffersSection(result)); + } + else if(sectionName == CoreSourcesSectionName) + { + return auto_ptr<Section>(new TraceCpuSourceSection(result)); + } + else + { + // check the list of buffer sections + std::vector<std::string>::iterator it = result.buffer_section_names.begin(); + bool matchedName = false; + while(it != result.buffer_section_names.end()) + { + if(sectionName == *it) + { + return auto_ptr<Section>(new TraceBufferSection(result, sectionName)); + } + it++; + } + // ignore unexpected sections, + return auto_ptr<Section>(new IgnoredSection); + } + }; + + +} + +using namespace ParserPrivate; + +Parser::Parsed Parser::ParseSingleDevice(istream& in) +{ + Parsed result; + + string line; + auto_ptr<Section> section(new NullSection); + + while (getline(in, line)) + { + CleanLine(line); // remove LF, comments + string sectionName; + + if (IsSectionHeader(line, sectionName)) + { + // Section ends with start of next section... + section->End(); + section = NewSection(sectionName, result); + } + else if (!IsEmpty(line)) + { + if (dynamic_cast<IgnoredSection *>(section.get()) == NULL) + { // NOT an ignored section, so process it + pair<string, string> kv(SplitKeyValue(line)); + section->Define(kv.first, kv.second); + } + } + } + // ... or end of file + section->End(); + return result; +} + +Parser::ParsedDevices Parser::ParseDeviceList(istream& in) +{ + ParsedDevices result; + result.snapshotInfo.description = ""; + // call the original format 0.0, the device_list format 0.1 and the flexible format (including version) 1.0 + result.snapshotInfo.version = "0.1"; + string line; + auto_ptr<Section> section(new NullSection); + + while (getline(in, line)) + { + CleanLine(line); // remove LF, comments + string sectionName; + + if (IsSectionHeader(line, sectionName)) + { + // Section ends with start of next section... + section->End(); + + if (sectionName == SnapshotSectionName) + section = NewSnapshotInfo(sectionName, result); + else if(sectionName == TraceSectionName) + section = NewTraceMetaData(sectionName, result); + else // else rather than elseif for closer compatibility with old tests + section = NewDeviceList(sectionName, result); + } + else if (!IsEmpty(line) && + ( dynamic_cast<DeviceListSection *>(section.get()) != NULL || + dynamic_cast<SnapshotSection *>(section.get()) != NULL || + dynamic_cast<TraceSection *>(section.get()) != NULL + ) + ) + { + pair<string, string> kv(SplitKeyValue(line)); + section->Define(kv.first, kv.second); + } + } + // ... or end of file + section->End(); + + return result; +} + + +// parse the trace metadata ini file. +ParsedTrace Parser::ParseTraceMetaData(std::istream& in) +{ + ParsedTrace result; + + string line; + auto_ptr<Section> section(new NullSection); + + while (getline(in, line)) + { + CleanLine(line); // remove LF, comments + string sectionName; + + if (IsSectionHeader(line, sectionName)) + { + // Section ends with start of next section... + section->End(); + section = NewTraceSection(sectionName, result); + } + else if (!IsEmpty(line)) + { + if (dynamic_cast<IgnoredSection *>(section.get()) == NULL) + { // NOT an ignored section, so process it + pair<string, string> kv(SplitKeyValue(line)); + section->Define(kv.first, kv.second); + } + } + } + // ... or end of file + section->End(); + return result; +} + + // build a source tree for a single buffer +bool Parser::ExtractSourceTree(const std::string &buffer_name, ParsedTrace &metadata, TraceBufferSourceTree &buffer_data) +{ + bool bFoundbuffer = false; + std::vector<TraceBufferInfo>::iterator it = metadata.trace_buffers.begin(); + + while((it != metadata.trace_buffers.end()) && !bFoundbuffer) + { + if(it->bufferName == buffer_name) + { + bFoundbuffer = true; + buffer_data.buffer_info = *it; + } + it++; + } + + if(bFoundbuffer) + { + std::map<std::string, std::string>::iterator sbit = metadata.source_buffer_assoc.begin(); + while(sbit != metadata.source_buffer_assoc.end()) + { + if(sbit->second == buffer_data.buffer_info.bufferName) + { + // found a source in this buffer... + buffer_data.source_core_assoc[sbit->first] = metadata.cpu_source_assoc[sbit->first]; + } + sbit++; + } + } + return bFoundbuffer; +} + +std::vector<std::string> Parser::GetBufferNameList(ParsedTrace &metadata) +{ + std::vector<std::string> nameList; + std::vector<TraceBufferInfo>::iterator it = metadata.trace_buffers.begin(); + while(it != metadata.trace_buffers.end()) + { + nameList.push_back(it->bufferName); + it++; + } + return nameList; +} + +void Parser::SetIErrorLogger(ITraceErrorLog *i_err_log) +{ + s_pErrorLogger = i_err_log; + if(s_pErrorLogger) + { + s_errlog_handle = s_pErrorLogger->RegisterErrorSource("snapshot_parser"); + } +} + +ITraceErrorLog *Parser::GetIErrorLogger() +{ + return s_pErrorLogger; +} + +void Parser::LogInfoStr(const std::string &logMsg) +{ + if(GetIErrorLogger() && s_verbose_logging) + GetIErrorLogger()->LogMessage(s_errlog_handle,OCSD_ERR_SEV_INFO,logMsg); +} + +void Parser::SetVerboseLogging(bool verbose) +{ + s_verbose_logging = verbose; +} +/* End of File snapshot_parser.cpp */ |