From 128ce1ee2115b54d43db1334e12410b1cc216f10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= Date: Mon, 1 Jan 2018 20:43:42 +0100 Subject: cli: extend tracer and implement reporter --- qtmips_cli/main.cpp | 83 ++++++++++++++++++++++++++++++++++++++++++++--- qtmips_cli/qtmips_cli.pro | 6 ++-- qtmips_cli/reporter.cpp | 59 +++++++++++++++++++++++++++++++++ qtmips_cli/reporter.h | 40 +++++++++++++++++++++++ qtmips_cli/tracer.cpp | 46 +++++++++++++++++++++++++- qtmips_cli/tracer.h | 10 ++++++ 6 files changed, 237 insertions(+), 7 deletions(-) create mode 100644 qtmips_cli/reporter.cpp create mode 100644 qtmips_cli/reporter.h diff --git a/qtmips_cli/main.cpp b/qtmips_cli/main.cpp index 4c4652b..2260ea5 100644 --- a/qtmips_cli/main.cpp +++ b/qtmips_cli/main.cpp @@ -1,9 +1,12 @@ #include #include +#include #include #include "tracer.h" +#include "reporter.h" using namespace machine; +using namespace std; void create_parser(QCommandLineParser &p) { p.setApplicationDescription("QtMips CLI machine simulator"); @@ -11,6 +14,17 @@ void create_parser(QCommandLineParser &p) { p.addVersionOption(); p.addPositionalArgument("FILE", "Input ELF executable file"); + + p.addOptions({ + {{"trace-fetch", "tr-fetch"}, "Trace fetched instruction."}, + {{"trace-pc", "tr-pc"}, "Print program counter register changes."}, + {{"trace-gp", "tr-gp"}, "Print general purpose register changes. You can use * for all registers.", "REG"}, + {{"trace-lo", "tr-lo"}, "Print LO register changes."}, + {{"trace-hi", "tr-hi"}, "Print HI register changes."}, + {{"dump-registers", "d-regs"}, "Dump registers state at program exit."}, + {"expect-fail", "Expect that program causes CPU trap and fail if it doesn't."}, + {"fail-match", "Program should exit with exactly this CPU TRAP. Possible values are I(unsupported Instruction), A(Unsupported ALU operation), O(Overflow/underflow) and J(Unaligned Jump). You can freely combine them. Using this implies expect-fail option.", "TRAP"}, + }); } void configure_machine(QCommandLineParser &p, MachineConfig &cc) { @@ -25,8 +39,68 @@ void configure_machine(QCommandLineParser &p, MachineConfig &cc) { } void configure_tracer(QCommandLineParser &p, Tracer &tr) { + // TODO trace fetched instruction + + if (p.isSet("trace-pc")) + tr.reg_pc(); + + QStringList gps = p.values("trace-gp"); + for (int i = 0; i < gps.size(); i++) { + if (gps[i] == "*") { + for (int y = 0; y < 32; y++) + tr.reg_gp(y); + } else { + bool res; + int num = gps[i].toInt(&res); + if (res && num <= 32) { + tr.reg_gp(num); + } else { + cout << "Unknown register number given for trace-gp: " << gps[i].toStdString() << endl; + exit(1); + } + } + } + + if (p.isSet("trace-lo")) + tr.reg_lo(); + if (p.isSet("trace-hi")) + tr.reg_hi(); + + // TODO +} + +void configure_reporter(QCommandLineParser &p, Reporter &r) { + if (p.isSet("dump-registers")) + r.regs(); + + QStringList fail = p.values("fail-match"); + for (int i = 0; i < fail.size(); i++) { + for (int y = 0; y < fail[i].length(); y++) { + enum Reporter::FailReason reason; + switch (tolower(fail[i].toStdString()[y])) { + case 'i': + reason = Reporter::FR_I; + break; + case 'a': + reason = Reporter::FR_A; + break; + case 'o': + reason = Reporter::FR_O; + break; + case 'j': + reason = Reporter::FR_J; + break; + default: + cout << "Unknown fail condition: " << fail[i].toStdString()[y] << endl; + exit(1); + } + r.expect_fail(reason); + } + } + if (p.isSet("expect-fail") && !p.isSet("fail-match")) + r.expect_fail(Reporter::FailAny); + // TODO - tr.reg_pc(); } int main(int argc, char *argv[]) { @@ -42,11 +116,12 @@ int main(int argc, char *argv[]) { configure_machine(p, cc); QtMipsMachine machine(cc); - app.connect(&machine, SIGNAL(program_exit()), &app, SLOT(quit())); - Tracer tr(&machine); configure_tracer(p, tr); - machine.play(); // Run machine + Reporter r(&app, &machine); + configure_reporter(p, r); + + machine.play(); return app.exec(); } diff --git a/qtmips_cli/qtmips_cli.pro b/qtmips_cli/qtmips_cli.pro index 088a015..d9100f8 100644 --- a/qtmips_cli/qtmips_cli.pro +++ b/qtmips_cli/qtmips_cli.pro @@ -18,7 +18,9 @@ DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ main.cpp \ - tracer.cpp + tracer.cpp \ + reporter.cpp HEADERS += \ - tracer.h + tracer.h \ + reporter.h diff --git a/qtmips_cli/reporter.cpp b/qtmips_cli/reporter.cpp new file mode 100644 index 0000000..70d1d27 --- /dev/null +++ b/qtmips_cli/reporter.cpp @@ -0,0 +1,59 @@ +#include "reporter.h" +#include +#include +#include + +using namespace machine; +using namespace std; + +Reporter::Reporter(QCoreApplication *app, QtMipsMachine *machine) : QObject() { + this->app = app; + this->machine = machine; + + connect(machine, SIGNAL(program_exit()), this, SLOT(machine_exit())); + connect(machine, SIGNAL(program_trap(machine::QtMipsException&)), this, SLOT(machine_trap(machine::QtMipsException&))); + + e_regs = false; + e_fail = (enum FailReason)0; +} + +void Reporter::regs() { + e_regs = true; +} + +void Reporter::expect_fail(enum FailReason reason) { + e_fail = (enum FailReason)(e_fail | reason); +} + +void Reporter::machine_exit() { + report(); + if (e_fail != 0) { + cout << "Machine was expected to fail but it didn't." << endl; + app->exit(1); + } else + app->exit(); +} + +void Reporter::machine_trap(QtMipsException &e) { + report(); + + bool expected = false; + auto& etype = typeid(e); + if (etype == typeid(QtMipsExceptionUnsupportedInstruction)) + expected = e_fail & FR_I; + else if (etype == typeid(QtMipsExceptionUnsupportedAluOperation)) + expected = e_fail & FR_A; + else if (etype == typeid(QtMipsExceptionOverflow)) + expected = e_fail & FR_O; + else if (etype == typeid(QtMipsExceptionUnalignedJump)) + expected = e_fail & FR_J; + + cout << "Machine trapped: " << e.what() << endl; + app->exit(expected ? 0 : 1); +} + +void Reporter::report() { + if (e_regs) { + // TODO + } +} diff --git a/qtmips_cli/reporter.h b/qtmips_cli/reporter.h new file mode 100644 index 0000000..8dcc5f1 --- /dev/null +++ b/qtmips_cli/reporter.h @@ -0,0 +1,40 @@ +#ifndef REPORTER_H +#define REPORTER_H + +#include +#include +#include "qtmipsmachine.h" + +class Reporter : QObject { + Q_OBJECT +public: + Reporter(QCoreApplication *app, machine::QtMipsMachine *machine); + + void regs(); // Report status of registers + // TODO + + enum FailReason { + FR_I = (1<<0), // Unsupported Instruction + FR_A = (1<<1), // Unsupported ALU operation + FR_O = (1<<2), // Overflow/underflow of numerical operation + FR_J = (1<<3), // Unaligned jump + }; + static const enum FailReason FailAny = (enum FailReason)(FR_I | FR_A | FR_O | FR_J); + + void expect_fail(enum FailReason reason); + +private slots: + void machine_exit(); + void machine_trap(machine::QtMipsException &e); + +private: + QCoreApplication *app; + machine::QtMipsMachine *machine; + + bool e_regs; + enum FailReason e_fail; + + void report(); +}; + +#endif // REPORTER_H diff --git a/qtmips_cli/tracer.cpp b/qtmips_cli/tracer.cpp index 2969112..55dc3e3 100644 --- a/qtmips_cli/tracer.cpp +++ b/qtmips_cli/tracer.cpp @@ -1,17 +1,61 @@ #include "tracer.h" #include +#include using namespace std; using namespace machine; Tracer::Tracer(QtMipsMachine *machine) { this->machine = machine; + for (unsigned i = 0; i < 32; i++) + gp_regs[i] = false; + r_hi = false; + r_lo = false; + + con_regs_pc = false; + con_regs_gp = false; + con_regs_hi_lo = false; } +#define CON(VAR, SIG, SLT) do { \ + if (!VAR) { \ + connect(machine->registers(), SIGNAL(SIG), this, SLOT(SLT)); \ + VAR = true;\ + }\ + } while(false) + void Tracer::reg_pc() { - connect(machine->registers(), SIGNAL(pc_update(std::uint32_t)), this, SLOT(regs_pc_update(std::uint32_t))); + CON(con_regs_pc, pc_update(std::uint32_t), regs_pc_update(std::uint32_t)); +} + +void Tracer::reg_gp(std::uint8_t i) { + SANITY_ASSERT(i <= 32, "Trying to trace invalid gp."); + CON(con_regs_gp, gp_update(std::uint8_t,std::uint32_t), regs_gp_update(std::uint8_t,std::uint32_t)); + gp_regs[i] = true; +} + +void Tracer::reg_lo() { + CON(con_regs_hi_lo, hi_lo_update(bool hi, std::uint32_t val), regs_hi_lo_update(bool hi, std::uint32_t val)); + r_lo = true; +} + +void Tracer::reg_hi() { + CON(con_regs_hi_lo, hi_lo_update(bool hi, std::uint32_t val), regs_hi_lo_update(bool hi, std::uint32_t val)); + r_hi = true; } void Tracer::regs_pc_update(std::uint32_t val) { cout << "PC:" << hex << val << endl; } + +void Tracer::regs_gp_update(std::uint8_t i, std::uint32_t val) { + if (gp_regs[i]) + cout << "GP" << dec << (unsigned)i << ":" << hex << val << endl; +} + +void Tracer::regs_hi_lo_update(bool hi, std::uint32_t val) { + if (hi && r_hi) + cout << "HI:" << hex << val << endl; + else if (!hi && r_lo) + cout << "LO:" << hex << val << endl; +} diff --git a/qtmips_cli/tracer.h b/qtmips_cli/tracer.h index d5a476b..245f418 100644 --- a/qtmips_cli/tracer.h +++ b/qtmips_cli/tracer.h @@ -11,12 +11,22 @@ public: // Trace registers void reg_pc(); + void reg_gp(std::uint8_t i); + void reg_lo(); + void reg_hi(); private slots: void regs_pc_update(std::uint32_t val); + void regs_gp_update(std::uint8_t i, std::uint32_t val); + void regs_hi_lo_update(bool hi, std::uint32_t val); private: machine::QtMipsMachine *machine; + + bool gp_regs[32]; + bool r_hi, r_lo; + + bool con_regs_pc, con_regs_gp, con_regs_hi_lo; }; #endif // TRACER_H -- cgit v1.2.3