From d370ded9b9f275cd962ab012e6ad39de91aee2ef Mon Sep 17 00:00:00 2001 From: Horst Schirmeier Date: Fri, 27 Jul 2018 11:37:03 +0200 Subject: [PATCH] generic-experiment: generalize serial-output monitoring The generic-experiment now learned to record and compare output on an arbitrary serial port. Using Bochs' port 0xe9 hack (parameter --e9-file) is kept for compatibility reasons. Change-Id: I5b1aa02d244e8b474919e1bdf043e523ea0e4f45 --- .../generic-experiment/experiment.cc | 82 ++++++++++++------- .../generic-experiment/experiment.hpp | 10 +-- src/experiments/generic-tracing/experiment.cc | 59 +++++-------- .../generic-tracing/experiment.hpp | 2 - .../serialoutput/SerialOutputLogger.hpp | 5 ++ 5 files changed, 84 insertions(+), 74 deletions(-) diff --git a/src/experiments/generic-experiment/experiment.cc b/src/experiments/generic-experiment/experiment.cc index 4183dd66..494f2fe9 100644 --- a/src/experiments/generic-experiment/experiment.cc +++ b/src/experiments/generic-experiment/experiment.cc @@ -42,21 +42,18 @@ google::protobuf::Message* GenericExperiment::cb_new_result(ExperimentData* data return result; } -std::vector loadFile(std::string filename) +std::string loadFile(std::string filename) { - std::vector data; - FILE *f = fopen(filename.c_str(), "rb"); - if (!f) { + std::string data; + std::ifstream in(filename, std::ios::in | std::ios::binary); + if (!in) { return data; } - fseek(f, 0, SEEK_END); - long len = ftell(f); - fseek(f, 0, SEEK_SET); - if (len > 0) { - data.resize(len); - fread(&data[0], len, 1, f); - } - fclose(f); + in.seekg(0, std::ios::end); + data.resize(in.tellg()); + in.seekg(0, std::ios::beg); + in.read(&data[0], data.size()); + in.close(); return data; } @@ -112,8 +109,12 @@ bool GenericExperiment::cb_start_experiment() { CommandLine::option_handle TIMEOUT = cmd.addOption("", "timeout", Arg::Required, "--timeout \t Experiment Timeout in uS"); + CommandLine::option_handle SERIAL_FILE = cmd.addOption("", "serial-file", Arg::Required, + "--serial-file FILE \tGolden-run serial output recording to check against"); + CommandLine::option_handle SERIAL_PORT = cmd.addOption("", "serial-port", Arg::Required, + "--serial-port PORT \tI/O port to expect output on (default: 0x3f8, i.e. x86's serial COM1)"); CommandLine::option_handle E9_FILE = cmd.addOption("", "e9-file", Arg::Required, - "--e9-file FILE \t data logged via port 0xE9 in golden run"); + "--e9-file FILE \tShorthand for --serial-file FILE --serial-port 0xe9"); std::map option_handles; @@ -172,18 +173,43 @@ bool GenericExperiment::cb_start_experiment() { } } + if (cmd[SERIAL_PORT]) { + option::Option *opt = cmd[SERIAL_PORT].first(); + char *endptr; + serial_port = strtoul(opt->arg, &endptr, 16); + if (endptr == opt->arg) { + m_log << "Couldn't parse " << opt->arg << std::endl; + exit(-1); + } + m_log << "serial port: 0x" << std::hex << serial_port << std::endl; + } else { + serial_port = 0x3f8; + } + + std::string serial_file; + if (cmd[SERIAL_FILE]) { + serial_file = std::string(cmd[SERIAL_FILE].first()->arg); + serial_enabled = true; + m_log << "serial file: " << serial_file << std::endl; + } + if (cmd[E9_FILE]) { - m_log << "enabled logging on port E9 for SDC-detection" << std::endl; - enabled_e9_sol = true; - e9_file = std::string(cmd[E9_FILE].first()->arg); - e9_goldenrun = loadFile(e9_file); + serial_file = std::string(cmd[E9_FILE].first()->arg); + serial_port = 0xe9; + serial_enabled = true; + m_log << "port E9 output is monitored and compared to: " << serial_file << std::endl; + } + + if (serial_enabled) { + serial_goldenrun = loadFile(serial_file); + sol.setPort(serial_port); // Limit the serial-output logger buffer to prevent overly large memory // consumption in case the target system ends up, e.g., in an endless // loop. "+ 1" to be able to detect the case when the target system // makes a correct output but faultily adds extra characters // afterwards. - e9_sol.setLimit(e9_goldenrun.size() + 1); + sol.setLimit(serial_goldenrun.size() + 1); } if (cmd[WRITE_MEM_TEXT]) { @@ -245,9 +271,9 @@ bool GenericExperiment::cb_start_experiment() { bool GenericExperiment::cb_before_fast_forward() { - if (enabled_e9_sol) { + if (serial_enabled) { // output may already appear *before* FI - simulator.addFlow(&e9_sol); + simulator.addFlow(&sol); } return true; } @@ -301,11 +327,11 @@ void GenericExperiment::cb_after_resume(fail::BaseListener *event) { handleEvent(*result, result->OK_MARKER, symbol->getAddress()); // check experiment's data for SDC - if (enabled_e9_sol) { + if (serial_enabled) { // compare golden run to experiment - std::string e9_experiment = e9_sol.getOutput(); - if ( ! (e9_experiment.size() == e9_goldenrun.size() - && equal(e9_experiment.begin(), e9_experiment.end(), e9_goldenrun.begin())) ) { + std::string serial_experiment = sol.getOutput(); + if ( ! (serial_experiment.size() == serial_goldenrun.size() + && equal(serial_experiment.begin(), serial_experiment.end(), serial_goldenrun.begin())) ) { handleEvent(*result, result->SDC, 0); } } @@ -338,9 +364,9 @@ void GenericExperiment::cb_after_resume(fail::BaseListener *event) { handleEvent(*result, result->UNKNOWN, 0); } - // remove and reset 0xE9 logger even if this run was not "OK" - if (enabled_e9_sol) { - simulator.removeFlow(&e9_sol); - e9_sol.resetOutput(); + // remove and reset serial logger even if this run was not "OK" + if (serial_enabled) { + simulator.removeFlow(&sol); + sol.resetOutput(); } } diff --git a/src/experiments/generic-experiment/experiment.hpp b/src/experiments/generic-experiment/experiment.hpp index 46f28187..c2883c8d 100644 --- a/src/experiments/generic-experiment/experiment.hpp +++ b/src/experiments/generic-experiment/experiment.hpp @@ -20,10 +20,10 @@ class GenericExperiment : public fail::DatabaseExperiment { std::string m_state_dir; - bool enabled_e9_sol; - std::string e9_file; - SerialOutputLogger e9_sol; - std::vector e9_goldenrun; + fail::guest_address_t serial_port; + SerialOutputLogger sol; + bool serial_enabled; + std::string serial_goldenrun; bool enabled_mem_text; fail::MemAccessListener l_mem_text; @@ -58,7 +58,7 @@ class GenericExperiment : public fail::DatabaseExperiment { public: GenericExperiment() : DatabaseExperiment("GenericExperiment"), m_state_dir("state"), - e9_sol(0xE9), + sol(0), l_trap(fail::ANY_TRAP), l_timeout(0) { enabled_mem_text = false; enabled_mem_outerspace = false; diff --git a/src/experiments/generic-tracing/experiment.cc b/src/experiments/generic-tracing/experiment.cc index 105ce926..09a0ceaf 100644 --- a/src/experiments/generic-tracing/experiment.cc +++ b/src/experiments/generic-tracing/experiment.cc @@ -33,20 +33,20 @@ void GenericTracing::parseOptions() { CommandLine::option_handle HELP = cmd.addOption("h", "help", Arg::None, "-h,--help \tPrint usage and exit"); CommandLine::option_handle ELF_FILE = cmd.addOption("", "elf-file", Arg::Required, - "--elf-file \tELF Binary File (default: $FAIL_ELF_PATH)"); + "--elf-file FILE \tELF binary file (default: $FAIL_ELF_PATH)"); CommandLine::option_handle START_SYMBOL = cmd.addOption("s", "start-symbol", Arg::Required, - "-s,--start-symbol \tELF symbol to start tracing (default: main)"); + "-s,--start-symbol SYMBOL \tELF symbol to start tracing (default: main)"); CommandLine::option_handle STOP_SYMBOL = cmd.addOption("e", "end-symbol", Arg::Required, - "-e,--end-symbol \tELF symbol to end tracing"); + "-e,--end-symbol SYMBOL \tELF symbol to end tracing"); // only there for backwards compatibility; remove at some point CommandLine::option_handle SAVE_SYMBOL = cmd.addOption("S", "save-symbol", Arg::Required, - "-S,--save-symbol \tELF symbol to save the state of the machine " + "-S,--save-symbol SYMBOL \tELF symbol to save the state of the machine " "(exists for backward compatibility, must be identical to --start-symbol if used)\n"); CommandLine::option_handle STATE_FILE = cmd.addOption("f", "state-file", Arg::Required, - "-f,--state-file \tFile/dir to save the state to (default: state). " + "-f,--state-file FILE \tFile/dir to save the state to (default: state). " "Use /dev/null if no state is required"); CommandLine::option_handle TRACE_FILE = cmd.addOption("t", "trace-file", Arg::Required, - "-t,--trace-file \tFile to save the execution trace to (default: trace.pb)\n"); + "-t,--trace-file FILE \tFile to save the execution trace to (default: trace.pb)\n"); CommandLine::option_handle RESTORE = cmd.addOption("", "restore", Arg::None, "--restore \tRestore to the saved state of the machine immediately after saving (default: off). " @@ -55,20 +55,21 @@ void GenericTracing::parseOptions() { CommandLine::option_handle FULL_TRACE = cmd.addOption("", "full-trace", Arg::None, "--full-trace \tDo a full trace (more data, default: off)"); CommandLine::option_handle MEM_SYMBOL = cmd.addOption("m", "memory-symbol", Arg::Required, - "-m,--memory-symbol \tELF symbol(s) to trace accesses (default: all mem read/writes are traced)"); + "-m,--memory-symbol SYMBOL \tELF symbol to trace accesses to " + "(default: all mem read/writes are traced; may be used more than once)"); CommandLine::option_handle MEM_REGION = cmd.addOption("M", "memory-region", Arg::Required, - "-M,--memory-region \trestrict memory region which is traced" + "-M,--memory-region R \trestrict memory region which is traced" " (Possible formats: 0x
, 0x
:0x
, 0x
:)"); CommandLine::option_handle START_ADDRESS = cmd.addOption("B", "start-address", Arg::Required, - "-B,--start-address \tStart Address to start tracing"); + "-B,--start-address ADDRESS \tStart Address to start tracing"); CommandLine::option_handle STOP_ADDRESS = cmd.addOption("E", "end-address",Arg::Required, - "-E,--end-address \tEnd Address to end tracing"); - CommandLine::option_handle SERIAL_PORT = cmd.addOption("", "serial-port", Arg::Required, - "--serial-port \tListen to a given I/O address (default: 0x3F8)"); + "-E,--end-address ADDRESS \tEnd Address to end tracing"); CommandLine::option_handle SERIAL_FILE = cmd.addOption("", "serial-file", Arg::Required, - "--serial-file \tSave the serial output to file"); + "--serial-file FILE \tSave the serial output to file"); + CommandLine::option_handle SERIAL_PORT = cmd.addOption("", "serial-port", Arg::Required, + "--serial-port PORT \tI/O port to expect output on (default: 0x3f8, i.e. x86's serial COM1)"); CommandLine::option_handle E9_FILE = cmd.addOption("", "e9-file", Arg::Required, - "--e9-file FILE \tData logged via port 0xE9 is stored in this file"); + "--e9-file FILE \tShorthand for --serial-file FILE --serial-port 0xe9"); if (!cmd.parse()) { cerr << "Error parsing arguments." << endl; @@ -233,23 +234,20 @@ void GenericTracing::parseOptions() { m_log << "Couldn't parse " << opt->arg << std::endl; exit(-1); } - m_log << "serial port: " << serial_port << std::endl; + m_log << "serial port: 0x" << std::hex << serial_port << std::endl; } else { - serial_port = 0x3F8; + serial_port = 0x3f8; } if (cmd[SERIAL_FILE]) { serial_file = std::string(cmd[SERIAL_FILE].first()->arg); m_log << "serial file: " << serial_file << std::endl; - } else { - serial_file = ""; } if (cmd[E9_FILE]) { - e9_file = std::string(cmd[E9_FILE].first()->arg); - m_log << "port E9 output is written to: " << e9_file << std::endl; - } else { - e9_file = ""; + serial_file = std::string(cmd[E9_FILE].first()->arg); + serial_port = 0xe9; + m_log << "port E9 output is written to: " << serial_file << std::endl; } @@ -312,12 +310,6 @@ bool GenericTracing::run() simulator.addFlow(&sol); } - SerialOutputLogger e9_sol(0xE9); - if (e9_file != "") { - simulator.addFlow(&e9_sol); - } - - //////////////////////////////////////////////////////////////// // Step 2: Continue to the stop point simulator.addListener(&l_stop_symbol); @@ -345,17 +337,6 @@ bool GenericTracing::run() of_serial.close(); } - if (e9_file != "") { - simulator.removeFlow(&e9_sol); - ofstream of_e9(e9_file.c_str(), ios::out|ios::binary); - if (!of_e9.fail()) { - of_e9 << e9_sol.getOutput(); - } else { - m_log << "failed to write " << e9_file << endl; - } - of_e9.close(); - } - simulator.clearListeners(); simulator.terminate(); diff --git a/src/experiments/generic-tracing/experiment.hpp b/src/experiments/generic-tracing/experiment.hpp index 4b2364e0..e605cf0b 100644 --- a/src/experiments/generic-tracing/experiment.hpp +++ b/src/experiments/generic-tracing/experiment.hpp @@ -28,8 +28,6 @@ class GenericTracing : public fail::ExperimentFlow { fail::guest_address_t serial_port; std::string serial_file; - std::string e9_file; - fail::Logger m_log; fail::ElfReader *m_elf; diff --git a/src/plugins/serialoutput/SerialOutputLogger.hpp b/src/plugins/serialoutput/SerialOutputLogger.hpp index a2d85c35..a0d82941 100644 --- a/src/plugins/serialoutput/SerialOutputLogger.hpp +++ b/src/plugins/serialoutput/SerialOutputLogger.hpp @@ -40,6 +40,11 @@ public: * Returns the output variable. */ std::string getOutput(); + /** + * Re-sets the port. Will not work properly if the plugin is already + * running. + */ + void setPort(unsigned port) { m_port = port; } /** * Sets the character limit. Does not truncate the stored data if it * already exceeds the new limit. 0 = unlimited.