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
This commit is contained in:
Horst Schirmeier
2018-07-27 11:37:03 +02:00
parent 226545de58
commit d370ded9b9
5 changed files with 84 additions and 74 deletions

View File

@ -42,21 +42,18 @@ google::protobuf::Message* GenericExperiment::cb_new_result(ExperimentData* data
return result;
}
std::vector<char> loadFile(std::string filename)
std::string loadFile(std::string filename)
{
std::vector<char> 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<std::string, CommandLine::option_handle> 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();
}
}

View File

@ -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<char> 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;

View File

@ -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<address>, 0x<address>:0x<address>, 0x<address>:<length>)");
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();

View File

@ -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;