Add option for JSON formatted telemetry output
MODIFY
src/cmd/cmdoptions.cpp
src/cmd/cmdoptions.cpp
namespace Ripes {
-template <typename T>
-QString enumToString(T value) {
- int castValue = static_cast<int>(value);
- return QMetaEnum::fromType<T>().valueToKey(castValue);
-}
-
void addCmdOptions(QCommandLineParser &parser, Ripes::CmdModeOptions &options) {
-
- QCommandLineOption srcOption("src", "Source file", "src");
- parser.addOption(srcOption);
-
- QCommandLineOption srcTypeOption("t", "Source type. Options: [c, asm, bin]",
- "type", "asm");
- srcTypeOption.setDefaultValue("asm");
- parser.addOption(srcTypeOption);
+ parser.addOption(QCommandLineOption("src", "Source file", "src"));
+ parser.addOption(QCommandLineOption(
+ "t", "Source type. Options: [c, asm, bin]", "type", "asm"));
// Processor models. Generate information from processor registry.
QStringList processorOptions;
@@ -29,25 +18,20 @@ void addCmdOptions(QCommandLineParser &parser, Ripes::CmdModeOptions &options) {
enumToString<ProcessorID>(static_cast<ProcessorID>(i)));
QString desc =
"Processor model. Options: [" + processorOptions.join(", ") + "]";
- QCommandLineOption processorOption("proc", desc, "proc");
- parser.addOption(processorOption);
-
- // ISA extensions.
- QCommandLineOption isaExtensionsOptions("isaexts",
+ parser.addOption(QCommandLineOption("proc", desc, "proc"));
+ parser.addOption(QCommandLineOption("isaexts",
"ISA extensions to enable (comma "
"separated)",
- "isaexts", "");
- parser.addOption(isaExtensionsOptions);
-
- QCommandLineOption verboseOption("v", "Verbose output");
- parser.addOption(verboseOption);
-
- // Output to file
- QCommandLineOption outputOption(
+ "isaexts", ""));
+ parser.addOption(QCommandLineOption("v", "Verbose output"));
+ parser.addOption(QCommandLineOption(
"output",
"Telemetry output file. If not set, telemtry is printed to stdout.",
- "output", "");
- parser.addOption(outputOption);
+ "output"));
+ parser.addOption(
+ QCommandLineOption("json", "Report JSON-formatted telemetry."));
+
+ parser.addOption(QCommandLineOption("all", "Enable all telemetry options."));
// Telemtry reporting
options.telemetry.push_back(std::make_shared<CyclesTelemetry>());
@@ -56,6 +40,7 @@ void addCmdOptions(QCommandLineParser &parser, Ripes::CmdModeOptions &options) {
options.telemetry.push_back(std::make_shared<IPCTelemetry>());
options.telemetry.push_back(std::make_shared<PipelineTelemetry>());
options.telemetry.push_back(std::make_shared<RegisterTelemetry>());
+ options.telemetry.push_back(std::make_shared<RunInfoTelemetry>(&parser));
for (auto &telemetry : options.telemetry) {
QString desc = "Report " + telemetry->description();
@@ -106,6 +91,8 @@ bool parseCmdOptions(QCommandLineParser &parser, QString &errorMessage,
}
options.proc = static_cast<ProcessorID>(procID);
+ options.jsonOutput = parser.isSet("json");
+
if (parser.isSet("isaexts")) {
options.isaExtensions = parser.value("isaexts").split(",");
@@ -130,7 +117,7 @@ bool parseCmdOptions(QCommandLineParser &parser, QString &errorMessage,
// Enable selected telemetry options.
for (auto &telemetry : options.telemetry)
- if (parser.isSet(telemetry->key()))
+ if (parser.isSet("all") || parser.isSet(telemetry->key()))
telemetry->enable();
return true;

MODIFY
src/cmd/cmdoptions.h
src/cmd/cmdoptions.h
@@ -15,6 +15,7 @@ struct CmdModeOptions {
QStringList isaExtensions;
bool verbose;
QString outputFile;
+ bool jsonOutput;
// A list of enabled telemetry options.
std::vector<std::shared_ptr<Telemetry>> telemetry;

MODIFY
src/cmd/cmdrunner.cpp
src/cmd/cmdrunner.cpp
#include "programutilities.h"
#include "syscall/systemio.h"
+#include <QJsonDocument>
+#include <QJsonObject>
+
namespace Ripes {
+// An extended QVariant-to-string convertion method which handles a few special
+// cases.
+static QString qVariantToString(QVariant &v) {
+ QString def = v.toString();
+ if (!def.isEmpty())
+ return def;
+
+ if (v.canConvert<QVariantMap>()) {
+ QVariantMap map = qvariant_cast<QVariantMap>(v);
+ QString out;
+ for (auto it : map.toStdMap())
+ out += it.first + ": " + qVariantToString(it.second) + "\n";
+ return out;
+ } else if (v.canConvert<QStringList>()) {
+ QStringList list = qvariant_cast<QStringList>(v);
+ return list.join(", ");
+ }
+
+ // Fallback will always be to return an empty string (this also applies to
+ // cases where the QVariant actually was an empty string!).
+ return def;
+}
+
CmdRunner::CmdRunner(const CmdModeOptions &options)
: QObject(), m_options(options) {
info("Ripes command-line mode", false, true);
@@ -91,7 +117,7 @@ int CmdRunner::runModel() {
int CmdRunner::postRun() {
info("Post-run", false, true);
- // Open telemetry stream
+ // Open output stream
std::unique_ptr<QTextStream> stream;
std::unique_ptr<QFile> outputFile;
if (m_options.outputFile.isEmpty()) {
@@ -106,10 +132,22 @@ int CmdRunner::postRun() {
stream = std::make_unique<QTextStream>(outputFile.get());
}
+ if (m_options.jsonOutput) {
+ // Telemetry output
+ QJsonObject jsonOutput;
+ for (auto &telemetry : m_options.telemetry)
+ if (telemetry->isEnabled())
+ jsonOutput.insert(telemetry->prettyKey(),
+ QJsonValue::fromVariant(telemetry->report()));
+ *stream << QJsonDocument(jsonOutput).toJson(QJsonDocument::Indented);
+ } else {
+ // Telemetry output
for (auto &telemetry : m_options.telemetry)
if (telemetry->isEnabled()) {
*stream << "===== " << telemetry->description() << "\n";
- telemetry->report(*stream);
+ QVariant reportedValue = telemetry->report();
+ *stream << qVariantToString(reportedValue) << "\n";
+ }
}
// Close output file if necessary

MODIFY
src/cmd/telemetry.h
src/cmd/telemetry.h
@@ -16,12 +16,15 @@ public:
Telemetry() {}
virtual ~Telemetry(){};
- // Report this telemetry to the provided output stream.
- virtual void report(QTextStream &out) = 0;
+ // Report this telemetry as a QVariant.
+ virtual QVariant report() = 0;
// Returns the name of this telemetry.
virtual QString key() const = 0;
+ // Returns a pretty version of the primary key.
+ virtual QString prettyKey() const { return key(); }
+
// Returns the description of this telemetry.
virtual QString description() const = 0;
@@ -35,51 +38,51 @@ private:
class CPITelemetry : public Telemetry {
QString key() const override { return "cpi"; }
+ QString prettyKey() const override { return "CPI"; }
QString description() const override {
return "cycles per instruction (CPI)";
}
- void report(QTextStream &out) override {
+ QVariant report() override {
const auto cycleCount = ProcessorHandler::getProcessor()->getCycleCount();
const auto instrsRetired =
ProcessorHandler::getProcessor()->getInstructionsRetired();
const double cpi =
static_cast<double>(cycleCount) / static_cast<double>(instrsRetired);
- out << QString::number(cpi) << "\n";
+ return cpi;
}
};
class IPCTelemetry : public Telemetry {
QString key() const override { return "ipc"; }
+ QString prettyKey() const override { return "IPC"; }
QString description() const override {
return "instructions per cycle (IPC)";
}
- void report(QTextStream &out) override {
+ QVariant report() override {
const auto cycleCount = ProcessorHandler::getProcessor()->getCycleCount();
const auto instrsRetired =
ProcessorHandler::getProcessor()->getInstructionsRetired();
const double cpi =
static_cast<double>(cycleCount) / static_cast<double>(instrsRetired);
const double ipc = 1 / cpi;
- out << QString::number(ipc) << "\n";
+ return ipc;
}
};
class CyclesTelemetry : public Telemetry {
QString key() const override { return "cycles"; }
QString description() const override { return "cycles"; }
- void report(QTextStream &out) override {
- const auto cycleCount = ProcessorHandler::getProcessor()->getCycleCount();
- out << QString::number(cycleCount) << "\n";
+ QVariant report() override {
+ return ProcessorHandler::getProcessor()->getCycleCount();
}
};
class InstrsRetiredTelemetry : public Telemetry {
QString key() const override { return "iret"; }
+ QString prettyKey() const override { return "# instructions retired"; }
QString description() const override { return "instructions retired"; }
- void report(QTextStream &out) override {
- const auto instrsRetired =
- ProcessorHandler::getProcessor()->getInstructionsRetired();
- out << QString::number(instrsRetired) << "\n";
+ QVariant report() override {
+ return ProcessorHandler::getProcessor()->getInstructionsRetired();
}
};
@@ -95,9 +98,9 @@ public:
QString key() const override { return "pipeline"; }
QString description() const override { return "pipeline state"; }
- void report(QTextStream &out) override {
+ QVariant report() override {
// Simply grab the current state of the pipeline diagram model and print it.
- out << m_pipelineDiagramModel->toString() << "\n";
+ return m_pipelineDiagramModel->toString();
}
private:
@@ -106,10 +109,12 @@ private:
class RegisterTelemetry : public Telemetry {
public:
- RegisterTelemetry() {}
QString key() const override { return "regs"; }
+ QString prettyKey() const override { return "registers"; }
QString description() const override { return "register values"; }
- void report(QTextStream &out) override {
+ QVariant report() override {
+ QString v;
+ QTextStream out(&v);
auto *isa = ProcessorHandler::currentISA();
for (unsigned i = 0; i < isa->regCnt(); i++) {
auto v = ProcessorHandler::getRegisterValue(RegisterFileType::GPR, i);
@@ -117,7 +122,32 @@ public:
<< encodeRadixValue(v, Radix::Signed, isa->bytes()) << "\t";
out << "(" << encodeRadixValue(v, Radix::Hex, isa->bytes()) << ")\n";
}
+ return v;
}
};
+class RunInfoTelemetry : public Telemetry {
+public:
+ RunInfoTelemetry(QCommandLineParser *parser) {
+ // Store a handle to the parser so we can look up the input file name upon
+ // reporting.
+ m_parser = parser;
+ }
+ QString key() const override { return "runinfo"; }
+ QString description() const override {
+ return "simulation information in output (processor "
+ "configuration, input file, ...)";
+ }
+ QVariant report() override {
+ QVariantMap m;
+ m["processor"] = enumToString<ProcessorID>(ProcessorHandler::getID());
+ m["ISA extensions"] = ProcessorHandler::currentISA()->enabledExtensions();
+ m["source file"] = m_parser->value("src");
+ return m;
+ }
+
+private:
+ QCommandLineParser *m_parser = nullptr;
+};
+
} // namespace Ripes

MODIFY
src/processorregistry.h
src/processorregistry.h
#pragma once
+#include <QMetaEnum>
#include <QMetaType>
#include <QPointF>
#include <map>
namespace Ripes {
Q_NAMESPACE
+template <typename T>
+QString enumToString(T value) {
+ int castValue = static_cast<int>(value);
+ return QMetaEnum::fromType<T>().valueToKey(castValue);
+}
+
// =============================== Processors =================================
// The order of the ProcessorID enum defines the order of which the processors
// will appear in the processor selection dialog.

Add option for JSON formatted telemetry output