blob: 4570700dd4c8fd2799f2d0546e8114056b5bf3c0 [file] [log] [blame]
/*
* \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"
static ITraceErrorLog *s_pErrorLogger = 0;
static ocsd_hndl_err_log_t s_errlog_handle = 0;
static bool s_verbose_logging = true;
/*************************************************************************
* 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 &sectionName) : 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 */