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:
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user