diff --git a/cmake/FindLLVM.cmake b/cmake/FindLLVM.cmake index e13cda05..3379368e 100644 --- a/cmake/FindLLVM.cmake +++ b/cmake/FindLLVM.cmake @@ -1,4 +1,4 @@ -find_program(LLVMCONFIG NAMES llvm-config llvm-config-3.4 llvm-config-3.3 llvm-config-3.2 llvm-config-3.1) +find_program(LLVMCONFIG NAMES llvm-config-3.4 llvm-config-3.3 llvm-config-3.2 llvm-config-3.1 llvm-config) if( NOT LLVMCONFIG ) message(FATAL_ERROR "llvm-config not found, try installing llvm-dev llvm") diff --git a/doc/how-to-build.txt b/doc/how-to-build.txt index f7af63d7..9604c1d1 100644 --- a/doc/how-to-build.txt +++ b/doc/how-to-build.txt @@ -33,6 +33,7 @@ Required for Fail*: * a MySQL 5.0+ or MariaDB 5.1+ (MariaDB 5.5 recommended) server * doxygen * cmake-curses-gui + * python-numpy and python-matplotlib for the faultspaceplot tool diff --git a/src/core/comm/DatabaseCampaignMessage.proto.in b/src/core/comm/DatabaseCampaignMessage.proto.in index 9e4c4dbc..3389b223 100644 --- a/src/core/comm/DatabaseCampaignMessage.proto.in +++ b/src/core/comm/DatabaseCampaignMessage.proto.in @@ -19,9 +19,12 @@ message DatabaseCampaignMessage { required string benchmark = 9 [(sql_ignore) = true]; required InjectionPointMessage injection_point = 10 [(sql_ignore) = true]; + + required bool inject_bursts = 11 [default = false]; } message DatabaseExperimentMessage { - required uint32 bitoffset = 1 [(sql_primary_key) = true]; - required uint32 original_value = 2; + required uint32 bitoffset = 1 [(sql_primary_key) = true]; + required uint32 original_value = 2; + required uint32 injection_width = 3; } diff --git a/src/core/cpn/DatabaseCampaign.cc b/src/core/cpn/DatabaseCampaign.cc index 1d445df1..c093e70d 100644 --- a/src/core/cpn/DatabaseCampaign.cc +++ b/src/core/cpn/DatabaseCampaign.cc @@ -46,6 +46,10 @@ bool DatabaseCampaign::run() { cmd.addOption("p", "prune-method", Arg::Required, "-p/--prune-method \tWhich import method(s) to use (default: \"%\"; use % and _ as wildcard characters)"); + CommandLine::option_handle BURST = + cmd.addOption("","inject-bursts", Arg::None, + "--inject-bursts \tinject burst faults (default: single bitflips)"); + if (!cmd.parse()) { log_send << "Error parsing arguments." << std::endl; exit(-1); @@ -91,6 +95,14 @@ bool DatabaseCampaign::run() { benchmarks.push_back("%"); } + if (cmd[BURST]) { + m_inject_bursts = true; + log_send << "fault model: burst" << std::endl; + } else { + m_inject_bursts = false; + log_send << "fault model: single-bit flip" << std::endl; + } + if (cmd[PRUNER]) { m_fspmethod = std::string(cmd[PRUNER].first()->arg); } else { @@ -225,7 +237,7 @@ bool DatabaseCampaign::run_variant(Database::Variant variant) { } pilot.set_data_address(data_address); pilot.set_data_width(data_width); - + pilot.set_inject_bursts(m_inject_bursts); this->cb_send_pilot(pilot); diff --git a/src/core/cpn/DatabaseCampaign.hpp b/src/core/cpn/DatabaseCampaign.hpp index 97bafc30..6546d722 100644 --- a/src/core/cpn/DatabaseCampaign.hpp +++ b/src/core/cpn/DatabaseCampaign.hpp @@ -39,6 +39,8 @@ class DatabaseCampaign : public Campaign { id_map completed_pilots; // !< map: Pilot IDs -> result count #endif + bool m_inject_bursts; // !< inject burst faults? + public: DatabaseCampaign() {}; @@ -61,7 +63,9 @@ public: * there are less result rows, the pilot will be again sent to the clients * @return \c exptected number of results */ - virtual int expected_number_of_results(std::string variant, std::string benchmark) { return 8;} + virtual int expected_number_of_results(std::string variant, std::string benchmark) { + return (m_inject_bursts ? 1 : 8); + } /** * Callback function that can be used to add command line options diff --git a/src/core/efw/DatabaseExperiment.cc b/src/core/efw/DatabaseExperiment.cc index 7cc67d30..9342708e 100644 --- a/src/core/efw/DatabaseExperiment.cc +++ b/src/core/efw/DatabaseExperiment.cc @@ -13,6 +13,7 @@ #include #include +//#define LOCAL using namespace std; using namespace fail; @@ -27,6 +28,18 @@ DatabaseExperiment::~DatabaseExperiment() { delete this->m_jc; } +unsigned DatabaseExperiment::injectBurst(address_t data_address) { + unsigned value, burst_value; + value = m_mm.getByte(data_address); + burst_value = value ^ 0xff; + m_mm.setByte(data_address, burst_value); + + m_log << "INJECTED BURST at: 0x" << hex<< setw(2) << setfill('0') << data_address + << " value: 0x" << setw(2) << setfill('0') << value << " -> 0x" + << setw(2) << setfill('0') << burst_value << endl; + return value; +} + unsigned DatabaseExperiment::injectBitFlip(address_t data_address, unsigned bitpos){ unsigned int value, injectedval; @@ -79,21 +92,32 @@ bool DatabaseExperiment::run() while (executed_jobs < 25 || m_jc->getNumberOfUndoneJobs() > 0) { m_log << "asking jobserver for parameters" << endl; ExperimentData * param = this->cb_allocate_experiment_data(); +#ifndef LOCAL if (!m_jc->getParam(*param)){ m_log << "Dying." << endl; // We were told to die. simulator.terminate(1); } +#endif m_current_param = param; DatabaseCampaignMessage * fsppilot = protobufFindSubmessageByTypename(¶m->getMessage(), "DatabaseCampaignMessage"); assert (fsppilot != 0); +#ifdef LOCAL + fsppilot->set_injection_instr(0); + fsppilot->set_injection_instr_absolute(1048677); + fsppilot->set_data_address(2101240); + fsppilot->set_data_width(1); + fsppilot->set_inject_bursts(true); +#endif + unsigned injection_instr = fsppilot->injection_instr(); address_t data_address = fsppilot->data_address(); unsigned width = fsppilot->data_width(); + unsigned injection_width = fsppilot->inject_bursts() ? 8 : 1; - for (unsigned bit_offset = 0; bit_offset < width * 8; ++bit_offset) { + for (unsigned bit_offset = 0; bit_offset < width * 8; bit_offset += injection_width) { // 8 results in one job Message *outer_result = cb_new_result(param); m_current_result = outer_result; @@ -107,7 +131,7 @@ bool DatabaseExperiment::run() m_log << "Trying to inject @ instr #" << dec << injection_instr << endl; - simulator.clearListeners(); + simulator.clearListeners(this); if (!this->cb_before_fast_forward()) { continue; @@ -155,10 +179,16 @@ bool DatabaseExperiment::run() simulator.terminate(1); } - simulator.clearListeners(); + simulator.clearListeners(this); - /// INJECT BITFLIP: - result->set_original_value(injectBitFlip(data_address, bit_offset)); + if (fsppilot->inject_bursts()) { + /// INJECT BURST: + result->set_original_value(injectBurst((data_address+bit_offset/8))); + } else { + /// INJECT BITFLIP: + result->set_original_value(injectBitFlip(data_address, bit_offset)); + } + result->set_injection_width(injection_width); if (!this->cb_before_resume()) { continue; // Continue to next experiment @@ -175,9 +205,13 @@ bool DatabaseExperiment::run() m_log << "Resume done" << std::endl; this->cb_after_resume(listener); - simulator.clearListeners(); + simulator.clearListeners(this); } +#ifndef LOCAL m_jc->sendResult(*param); +#else + break; +#endif this->cb_free_experiment_data(param); } // Explicitly terminate, or the simulator will continue to run. diff --git a/src/core/efw/DatabaseExperiment.hpp b/src/core/efw/DatabaseExperiment.hpp index f60ddf9a..71fae098 100644 --- a/src/core/efw/DatabaseExperiment.hpp +++ b/src/core/efw/DatabaseExperiment.hpp @@ -15,6 +15,7 @@ class DatabaseExperiment : public fail::ExperimentFlow { fail::JobClient *m_jc; unsigned injectBitFlip(fail::address_t data_address, unsigned bitpos); + unsigned injectBurst(fail::address_t data_address); /** The current experiment data as returned by the job client. This diff --git a/src/experiments/generic-experiment/experiment.cc b/src/experiments/generic-experiment/experiment.cc index c11cb690..4183dd66 100644 --- a/src/experiments/generic-experiment/experiment.cc +++ b/src/experiments/generic-experiment/experiment.cc @@ -42,6 +42,23 @@ google::protobuf::Message* GenericExperiment::cb_new_result(ExperimentData* data return result; } +std::vector loadFile(std::string filename) +{ + std::vector data; + FILE *f = fopen(filename.c_str(), "rb"); + if (!f) { + 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); + return data; +} void handleEvent(GenericExperimentMessage_Result& result, GenericExperimentMessage_Result_ResultType restype, @@ -56,7 +73,7 @@ void GenericExperiment::parseSymbols(const std::string &args, std::setgetSymbol(item)); if (!symbol->isValid()) { m_log << "ELF Symbol not found: " << item << endl; simulator.terminate(1); @@ -76,6 +93,9 @@ bool GenericExperiment::cb_start_experiment() { cmd.addOption("", "", Arg::None, "USAGE: fail-client -Wf,[option] -Wf,[option] ... \n\n"); 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)"); + CommandLine::option_handle STATE_DIR = cmd.addOption("", "state-dir", Arg::Required, "--state-dir \t Path to the state directory"); @@ -92,6 +112,9 @@ bool GenericExperiment::cb_start_experiment() { CommandLine::option_handle TIMEOUT = cmd.addOption("", "timeout", Arg::Required, "--timeout \t Experiment Timeout in uS"); + CommandLine::option_handle E9_FILE = cmd.addOption("", "e9-file", Arg::Required, + "--e9-file FILE \t data logged via port 0xE9 in golden run"); + std::map option_handles; for (std::map::iterator it = end_marker_groups.begin(); @@ -104,7 +127,7 @@ bool GenericExperiment::cb_start_experiment() { if (!cmd.parse()) { cerr << "Error parsing arguments." << endl; - exit(-1); + exit(1); } if (cmd[HELP]) { @@ -112,13 +135,32 @@ bool GenericExperiment::cb_start_experiment() { exit(0); } + if (cmd[ELF_FILE]) { + elf_file = cmd[ELF_FILE].first()->arg; + m_elf = new ElfReader(elf_file.c_str()); + m_log << "ELF file specified: " << elf_file << std::endl; + } else { + char *elfpath = getenv("FAIL_ELF_PATH"); + if (elfpath == NULL) { + m_elf = NULL; + } else { + elf_file = elfpath; + m_elf = new ElfReader(elf_file.c_str()); + m_log << "ELF file via environment variable: " << elf_file << std::endl; + } + } + if (m_elf == NULL) { + m_log << "ERROR: no FAIL_ELF_PATH set or --elf-file given. exiting!" << std::endl; + exit(1); + } + address_t minimal_ip = INT_MAX; // Every address is lower address_t maximal_ip = 0; address_t minimal_data = 0x100000; // 1 Mbyte address_t maximal_data = 0; - for (ElfReader::section_iterator it = m_elf.sec_begin(); - it != m_elf.sec_end(); ++it) { + for (ElfReader::section_iterator it = m_elf->sec_begin(); + it != m_elf->sec_end(); ++it) { const ElfSymbol &symbol = *it; std::string prefix(".text"); if (symbol.getName().compare(0, prefix.size(), prefix) == 0) { @@ -130,6 +172,20 @@ bool GenericExperiment::cb_start_experiment() { } } + 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); + + // 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); + } + if (cmd[WRITE_MEM_TEXT]) { m_log << "Catch writes to text segment from " << hex << minimal_ip << " to " << maximal_ip << std::endl; enabled_mem_text = true; @@ -145,7 +201,7 @@ bool GenericExperiment::cb_start_experiment() { l_mem_outerspace.setWatchAddress(maximal_data); l_mem_outerspace.setTriggerAccessType(MemAccessEvent::MEM_WRITE); - l_mem_outerspace.setWatchWidth(0xfffffff0); + l_mem_outerspace.setWatchWidth(0xfffffff0 - maximal_data); } if (cmd[TRAP]) { @@ -187,6 +243,15 @@ bool GenericExperiment::cb_start_experiment() { } +bool GenericExperiment::cb_before_fast_forward() +{ + if (enabled_e9_sol) { + // output may already appear *before* FI + simulator.addFlow(&e9_sol); + } + return true; +} + bool GenericExperiment::cb_before_resume() { if (enabled_trap) simulator.addListener(&l_trap); @@ -216,7 +281,6 @@ void GenericExperiment::cb_after_resume(fail::BaseListener *event) { // Record the crash time result->set_crash_time(simulator.getTimerTicks()); - if (event == &l_timeout) { handleEvent(*result, result->TIMEOUT, m_Timeout); } else if (event == &l_trap) { @@ -236,6 +300,16 @@ void GenericExperiment::cb_after_resume(fail::BaseListener *event) { const ElfSymbol *symbol = listener_to_symbol[event]; handleEvent(*result, result->OK_MARKER, symbol->getAddress()); + // check experiment's data for SDC + if (enabled_e9_sol) { + // 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())) ) { + handleEvent(*result, result->SDC, 0); + } + } + } else if (FAIL_marker.find(event) != FAIL_marker.end()) { const ElfSymbol *symbol = listener_to_symbol[event]; handleEvent(*result, result->FAIL_MARKER, symbol->getAddress()); @@ -263,4 +337,10 @@ void GenericExperiment::cb_after_resume(fail::BaseListener *event) { } else { 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(); + } } diff --git a/src/experiments/generic-experiment/experiment.hpp b/src/experiments/generic-experiment/experiment.hpp index 9d63f338..46f28187 100644 --- a/src/experiments/generic-experiment/experiment.hpp +++ b/src/experiments/generic-experiment/experiment.hpp @@ -7,6 +7,7 @@ #include "efw/JobClient.hpp" #include "util/Logger.hpp" #include "util/ElfReader.hpp" +#include "../plugins/serialoutput/SerialOutputLogger.hpp" #include #include #include @@ -14,10 +15,16 @@ class GenericExperiment : public fail::DatabaseExperiment { - fail::ElfReader m_elf; + fail::ElfReader *m_elf; + std::string elf_file; std::string m_state_dir; + bool enabled_e9_sol; + std::string e9_file; + SerialOutputLogger e9_sol; + std::vector e9_goldenrun; + bool enabled_mem_text; fail::MemAccessListener l_mem_text; @@ -51,6 +58,7 @@ class GenericExperiment : public fail::DatabaseExperiment { public: GenericExperiment() : DatabaseExperiment("GenericExperiment"), m_state_dir("state"), + e9_sol(0xE9), l_trap(fail::ANY_TRAP), l_timeout(0) { enabled_mem_text = false; enabled_mem_outerspace = false; @@ -90,6 +98,14 @@ public: */ virtual bool cb_start_experiment(); + /** + * Callback that is called before the fast forward is done. This + * can be used to add additional event listeners during the fast + * forward phase. If returning false, the experiment is canceled. + * @return \c true on success, \c false otherwise + */ + virtual bool cb_before_fast_forward(); + /** * Callback that is called before the resuming till crash has * started. This is called after the fault was injected. Here the diff --git a/src/experiments/generic-experiment/generic-experiment.proto b/src/experiments/generic-experiment/generic-experiment.proto index 09b25fc6..9baaf4a4 100644 --- a/src/experiments/generic-experiment/generic-experiment.proto +++ b/src/experiments/generic-experiment/generic-experiment.proto @@ -13,7 +13,7 @@ message GenericExperimentMessage { OK_MARKER = 1; FAIL_MARKER = 2; DETECTED_MARKER = 3; - + GROUP1_MARKER = 4; GROUP2_MARKER = 5; GROUP3_MARKER = 6; @@ -24,6 +24,8 @@ message GenericExperimentMessage { WRITE_TEXTSEGMENT = 10; WRITE_OUTERSPACE = 11; + SDC = 12; + UNKNOWN = 100; } // result type, see above diff --git a/src/experiments/generic-tracing/experiment.cc b/src/experiments/generic-tracing/experiment.cc index 7ce02d6f..233a885f 100644 --- a/src/experiments/generic-tracing/experiment.cc +++ b/src/experiments/generic-tracing/experiment.cc @@ -62,11 +62,13 @@ void GenericTracing::parseOptions() { CommandLine::option_handle START_ADDRESS = cmd.addOption("B", "start-address", Arg::Required, "-B,--start-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"); + "-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)"); CommandLine::option_handle SERIAL_FILE = cmd.addOption("", "serial-file", Arg::Required, "--serial-file \tSave the serial output to file"); + CommandLine::option_handle E9_FILE = cmd.addOption("", "e9-file", Arg::Required, + "--e9-file FILE \tData logged via port 0xE9 is stored in this file"); if (!cmd.parse()) { cerr << "Error parsing arguments." << endl; @@ -228,6 +230,14 @@ void GenericTracing::parseOptions() { 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 = ""; + } + + if (m_elf != NULL) { m_log << "start/save symbol: " << start_symbol << " 0x" << std::hex << start_address << std::endl; m_log << "stop symbol: " << stop_symbol << " 0x" << std::hex << stop_address << std::endl; @@ -287,6 +297,12 @@ 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); @@ -314,6 +330,17 @@ 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 e605cf0b..4b2364e0 100644 --- a/src/experiments/generic-tracing/experiment.hpp +++ b/src/experiments/generic-tracing/experiment.hpp @@ -28,6 +28,8 @@ 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 19e4f6f1..a2d85c35 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(); + /** + * Sets the character limit. Does not truncate the stored data if it + * already exceeds the new limit. 0 = unlimited. + */ + void setLimit(unsigned char_limit) { m_limit = char_limit; } }; #endif // __SERIAL_OUTPUT_LOGGER_HPP__ diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 269dc4b9..3a4198cc 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -6,6 +6,9 @@ option(BUILD_CONVERT_TRACE "Build the trace converter tool?" OFF) option(BUILD_COMPUTE_HOPS "Build the compute hops tool?" OFF) option(BUILD_DUMP_HOPS "Build the hops dump tool?" OFF) +option(BUILD_FAULTSPACEPLOT "Build the faultspace plotting tool?" OFF) +option(BUILD_DATA_AGGREGATOR "Build the data aggregation tools?" OFF) + ### Setup search paths for headers ## include_directories(${CMAKE_CURRENT_BINARY_DIR}/../src/core) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src/core) @@ -33,3 +36,11 @@ endif(BUILD_COMPUTE_HOPS) if(BUILD_DUMP_HOPS) add_subdirectory(dump-hops) endif(BUILD_DUMP_HOPS) + +if(BUILD_FAULTSPACEPLOT) + add_subdirectory(analysis/faultspaceplot) +endif(BUILD_FAULTSPACEPLOT) + +if(BUILD_DATA_AGGREGATOR) + add_subdirectory(analysis/data-aggregator) +endif(BUILD_DATA_AGGREGATOR) diff --git a/tools/analysis/data-aggregator/CMakeLists.txt b/tools/analysis/data-aggregator/CMakeLists.txt new file mode 100644 index 00000000..2b91f10b --- /dev/null +++ b/tools/analysis/data-aggregator/CMakeLists.txt @@ -0,0 +1 @@ +install(PROGRAMS function-occurrences.sh resulttype-occurrences.sh resulttype-occurrences_coverage.sh symbol-occurrences.sh symbol-occurrences_coverage.sh translation-unit-occurrences.sh variant-durations.sh DESTINATION bin) diff --git a/tools/analysis/data-aggregator/function-occurrences.sh b/tools/analysis/data-aggregator/function-occurrences.sh new file mode 100755 index 00000000..65e2367b --- /dev/null +++ b/tools/analysis/data-aggregator/function-occurrences.sh @@ -0,0 +1,38 @@ +#!/bin/bash +set -e + +if [ "$1" = -t ]; then + FORMAT=-t + shift +else + FORMAT=-B +fi + +if [ ! $# -eq 3 ]; then + echo "usage: $0 [ -t ] DATABASE VARIANT BENCHMARK" >&2 + echo " -t Display output in table format (tab-separated CSV otherwise)" >&2 + exit 1 +fi + +DATABASE=$1 +VARIANT=$2 +BENCHMARK=$3 +MYSQL="mysql $FORMAT $DATABASE" + +$MYSQL << EOT +SELECT s.name, s.address, r.resulttype, SUM(t.time2-t.time1+1) AS occurrences +FROM variant v +JOIN symbol s + ON s.variant_id = v.id +JOIN trace t + ON t.variant_id = s.variant_id AND t.instr2_absolute BETWEEN s.address AND s.address + s.size - 1 +JOIN fspgroup g + ON g.variant_id = t.variant_id AND g.data_address = t.data_address AND g.instr2 = t.instr2 +JOIN result_GenericExperimentMessage r + ON r.pilot_id = g.pilot_id +WHERE v.variant="$VARIANT" + AND v.benchmark="$BENCHMARK" +GROUP BY s.variant_id, s.address, r.resulttype +ORDER BY s.address, r.resulttype +; +EOT diff --git a/tools/analysis/data-aggregator/resulttype-occurrences.sh b/tools/analysis/data-aggregator/resulttype-occurrences.sh new file mode 100755 index 00000000..1c93900c --- /dev/null +++ b/tools/analysis/data-aggregator/resulttype-occurrences.sh @@ -0,0 +1,50 @@ +#!/bin/bash +set -e + +if [ "$1" = -t ]; then + FORMAT=-t + shift +else + FORMAT=-B +fi + +if [ $# -ne 3 -a $# -ne 1 ]; then + echo "usage: $0 [ -t ] DATABASE [ VARIANT BENCHMARK ]" >&2 + echo " -t Display output in table format (tab-separated CSV otherwise)" >&2 + exit 1 +fi + +DATABASE=$1 +VARIANT=$2 +BENCHMARK=$3 +MYSQL="mysql $FORMAT $DATABASE" + +if [ -z "$VARIANT" ]; then +$MYSQL << EOT + SET sql_mode = 'NO_UNSIGNED_SUBTRACTION'; + SELECT v.benchmark, v.variant, r.resulttype, SUM((t.time2-t.time1+1) * t.width) AS occurrences + FROM result_GenericExperimentMessage r + INNER JOIN fspgroup g ON g.pilot_id=r.pilot_id + INNER JOIN trace t ON g.instr2=t.instr2 + AND g.data_address=t.data_address + AND g.variant_id=t.variant_id + INNER JOIN variant v ON t.variant_id=v.id + GROUP BY v.id, r.resulttype + ORDER BY v.benchmark, v.variant, r.resulttype; +EOT +else +$MYSQL << EOT + SET sql_mode = 'NO_UNSIGNED_SUBTRACTION'; + SELECT r.resulttype, SUM((t.time2-t.time1+1) * t.width) AS occurrences + FROM result_GenericExperimentMessage r + INNER JOIN fspgroup g ON g.pilot_id=r.pilot_id + INNER JOIN trace t ON g.instr2=t.instr2 + AND g.data_address=t.data_address + AND g.variant_id=t.variant_id + INNER JOIN variant v ON t.variant_id=v.id + WHERE v.variant="$VARIANT" + AND v.benchmark="$BENCHMARK" + GROUP BY r.resulttype + ORDER BY r.resulttype ASC; +EOT +fi diff --git a/tools/analysis/data-aggregator/resulttype-occurrences_coverage.sh b/tools/analysis/data-aggregator/resulttype-occurrences_coverage.sh new file mode 100755 index 00000000..5fa7c2c3 --- /dev/null +++ b/tools/analysis/data-aggregator/resulttype-occurrences_coverage.sh @@ -0,0 +1,70 @@ +#!/bin/bash +set -e + +if [ "$1" = -t ]; then + FORMAT=-t + shift +else + FORMAT=-B +fi + +if [ $# -ne 3 -a $# -ne 1 ]; then + echo "usage: $0 [ -t ] DATABASE [ VARIANT BENCHMARK ]" >&2 + echo " -t Display output in table format (tab-separated CSV otherwise)" >&2 + exit 1 +fi + +DATABASE=$1 +VARIANT=$2 +BENCHMARK=$3 +MYSQL="mysql $FORMAT $DATABASE" + +if [ -z "$VARIANT" ]; then +$MYSQL << EOT + SET sql_mode = 'NO_UNSIGNED_SUBTRACTION'; + SELECT v.benchmark, v.variant, r.resulttype, + SUM((t.time2-t.time1+1) * t.width) + / + (SELECT SUM(t.time2-t.time1+1)*t.width + FROM result_GenericExperimentMessage r + INNER JOIN fspgroup g ON g.pilot_id=r.pilot_id + INNER JOIN trace t ON g.instr2=t.instr2 + AND g.data_address=t.data_address + AND g.variant_id=t.variant_id + WHERE t.variant_id = v.id -- refers to parent query + ) AS coverage + FROM result_GenericExperimentMessage r + INNER JOIN fspgroup g ON g.pilot_id=r.pilot_id + INNER JOIN trace t ON g.instr2=t.instr2 + AND g.data_address=t.data_address + AND g.variant_id=t.variant_id + INNER JOIN variant v ON t.variant_id=v.id + GROUP BY v.id, r.resulttype + ORDER BY v.benchmark, v.variant, r.resulttype; +EOT +else +$MYSQL << EOT + SET sql_mode = 'NO_UNSIGNED_SUBTRACTION'; + SELECT r.resulttype, + SUM((t.time2-t.time1+1) * t.width) + / + (SELECT SUM(t.time2-t.time1+1)*t.width + FROM result_GenericExperimentMessage r + INNER JOIN fspgroup g ON g.pilot_id=r.pilot_id + INNER JOIN trace t ON g.instr2=t.instr2 + AND g.data_address=t.data_address + AND g.variant_id=t.variant_id + WHERE t.variant_id = v.id -- refers to parent query + ) AS coverage + FROM result_GenericExperimentMessage r + INNER JOIN fspgroup g ON g.pilot_id=r.pilot_id + INNER JOIN trace t ON g.instr2=t.instr2 + AND g.data_address=t.data_address + AND g.variant_id=t.variant_id + INNER JOIN variant v ON t.variant_id=v.id + WHERE v.variant="$VARIANT" + AND v.benchmark="$BENCHMARK" + GROUP BY r.resulttype + ORDER BY r.resulttype; +EOT +fi diff --git a/tools/analysis/data-aggregator/symbol-occurrences.sh b/tools/analysis/data-aggregator/symbol-occurrences.sh new file mode 100755 index 00000000..f430fd0d --- /dev/null +++ b/tools/analysis/data-aggregator/symbol-occurrences.sh @@ -0,0 +1,33 @@ +#!/bin/bash +set -e + +if [ "$1" = -t ]; then + FORMAT=-t + shift +else + FORMAT=-B +fi + +if [ ! $# -eq 3 ]; then + echo "usage: $0 [ -t ] DATABASE VARIANT BENCHMARK" >&2 + echo " -t Display output in table format (tab-separated CSV otherwise)" >&2 + exit 1 +fi + +DATABASE=$1 +VARIANT=$2 +BENCHMARK=$3 +MYSQL="mysql $FORMAT $DATABASE" + +$MYSQL << EOT + SELECT v.benchmark, v.variant, s.name, s.size, r.resulttype, SUM(t.time2-t.time1+1) AS occurrences + FROM variant v + INNER JOIN symbol s ON v.id = s.variant_id + INNER JOIN trace t ON t.variant_id = v.id AND t.data_address BETWEEN s.address AND s.address + s.size - 1 + INNER JOIN fspgroup g ON t.variant_id = g.variant_id AND t.data_address = g.data_address AND t.instr2 = g.instr2 + INNER JOIN result_GenericExperimentMessage r ON r.pilot_id = g.pilot_id + WHERE v.variant="$VARIANT" + AND v.benchmark="$BENCHMARK" + GROUP BY v.benchmark, v.variant, s.name, r.resulttype + ORDER BY v.benchmark, v.variant, s.name, r.resulttype; +EOT diff --git a/tools/analysis/data-aggregator/symbol-occurrences_coverage.sh b/tools/analysis/data-aggregator/symbol-occurrences_coverage.sh new file mode 100755 index 00000000..8335482c --- /dev/null +++ b/tools/analysis/data-aggregator/symbol-occurrences_coverage.sh @@ -0,0 +1,44 @@ +#!/bin/bash +set -e + +if [ "$1" = -t ]; then + FORMAT=-t + shift +else + FORMAT=-B +fi + +if [ ! $# -eq 3 ]; then + echo "usage: $0 [ -t ] DATABASE VARIANT BENCHMARK" >&2 + echo " -t Display output in table format (tab-separated CSV otherwise)" >&2 + exit 1 +fi + +DATABASE=$1 +VARIANT=$2 +BENCHMARK=$3 +MYSQL="mysql $FORMAT $DATABASE" + +$MYSQL << EOT + SELECT v.benchmark, v.variant, s.name, s.size, r.resulttype, + SUM(t.time2-t.time1+1) + / + (SELECT SUM(t.time2-t.time1+1) + FROM variant v + INNER JOIN symbol s ON v.id = s.variant_id + INNER JOIN trace t ON t.variant_id = v.id AND t.data_address BETWEEN s.address AND s.address + s.size - 1 + INNER JOIN fspgroup g ON t.variant_id = g.variant_id AND t.data_address = g.data_address AND t.instr2 = g.instr2 + INNER JOIN result_GenericExperimentMessage r ON r.pilot_id = g.pilot_id + WHERE v.variant="$VARIANT" + AND v.benchmark="$BENCHMARK" + ) AS coverage + FROM variant v + INNER JOIN symbol s ON v.id = s.variant_id + INNER JOIN trace t ON t.variant_id = v.id AND t.data_address BETWEEN s.address AND s.address + s.size - 1 + INNER JOIN fspgroup g ON t.variant_id = g.variant_id AND t.data_address = g.data_address AND t.instr2 = g.instr2 + INNER JOIN result_GenericExperimentMessage r ON r.pilot_id = g.pilot_id + WHERE v.variant="$VARIANT" + AND v.benchmark="$BENCHMARK" + GROUP BY v.benchmark, v.variant, s.name, r.resulttype + ORDER BY v.benchmark, v.variant, s.name, r.resulttype; +EOT diff --git a/tools/analysis/data-aggregator/translation-unit-occurrences.sh b/tools/analysis/data-aggregator/translation-unit-occurrences.sh new file mode 100755 index 00000000..2427706b --- /dev/null +++ b/tools/analysis/data-aggregator/translation-unit-occurrences.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -e + +if [ "$1" = -t ]; then + FORMAT=-t + shift +else + FORMAT=-B +fi + +if [ ! $# -eq 3 ]; then + echo "usage: $0 [ -t ] DATABASE VARIANT BENCHMARK" >&2 + echo " -t Display output in table format (tab-separated CSV otherwise)" >&2 + exit 1 +fi + +DATABASE=$1 +VARIANT=$2 +BENCHMARK=$3 +MYSQL="mysql $FORMAT $DATABASE" + +$MYSQL << EOT +SELECT v.benchmark, v.variant, f.path, r.resulttype, SUM(t.time2-t.time1+1) AS occurrences +FROM variant v +JOIN dbg_filename f + ON f.variant_id = v.id +JOIN dbg_mapping m + ON f.variant_id = m.variant_id AND f.file_id = m.file_id +JOIN trace t + ON t.variant_id = m.variant_id AND t.instr2_absolute BETWEEN m.instr_absolute AND m.instr_absolute + m.line_range_size - 1 +JOIN fspgroup g + ON g.variant_id = t.variant_id AND g.data_address = t.data_address AND g.instr2 = t.instr2 +JOIN result_GenericExperimentMessage r + ON r.pilot_id = g.pilot_id +WHERE v.variant="$VARIANT" + AND v.benchmark="$BENCHMARK" +GROUP BY v.id, f.file_id, r.resulttype +ORDER BY v.benchmark, v.variant, f.path, r.resulttype +; +EOT diff --git a/tools/analysis/data-aggregator/variant-durations.sh b/tools/analysis/data-aggregator/variant-durations.sh new file mode 100755 index 00000000..818c7516 --- /dev/null +++ b/tools/analysis/data-aggregator/variant-durations.sh @@ -0,0 +1,44 @@ +#!/bin/bash +set -e + +if [ "$1" = -t ]; then + FORMAT=-t + shift +else + FORMAT=-B +fi + +if [ $# -ne 3 -a $# -ne 1 ]; then + echo "usage: $0 [ -t ] DATABASE [ VARIANT BENCHMARK ]" >&2 + echo " -t Display output in table format (tab-separated CSV otherwise)" >&2 + exit 1 +fi + +DATABASE=$1 +VARIANT=$2 +BENCHMARK=$3 +MYSQL="mysql $FORMAT $DATABASE" + +if [ -z "$VARIANT" ]; then +$MYSQL << EOT + SET sql_mode = 'NO_UNSIGNED_SUBTRACTION'; + SELECT v.benchmark, v.variant, + MAX(t.time2)-MIN(t.time1)+1 AS duration, + MAX(t.instr2)-MIN(t.instr1)+1 AS dyn_instr + FROM trace t + JOIN variant v ON t.variant_id = v.id + GROUP BY v.id + ORDER BY v.benchmark, v.variant; +EOT +else +$MYSQL << EOT + SET sql_mode = 'NO_UNSIGNED_SUBTRACTION'; + SELECT + MAX(t.time2)-MIN(t.time1)+1 AS duration, + MAX(t.instr2)-MIN(t.instr1)+1 AS dyn_instr + FROM trace t + JOIN variant v ON t.variant_id = v.id + WHERE v.variant = "$VARIANT" + AND v.benchmark = "$BENCHMARK" +EOT +fi diff --git a/tools/analysis/faultspaceplot/CMakeLists.txt b/tools/analysis/faultspaceplot/CMakeLists.txt new file mode 100644 index 00000000..d3c83219 --- /dev/null +++ b/tools/analysis/faultspaceplot/CMakeLists.txt @@ -0,0 +1 @@ +install(PROGRAMS faultspaceplot.sh fsp.compact-horizontal.sh fsp.compact.sh fsp.compact-vertical.sh fsp.plot.py DESTINATION bin) diff --git a/tools/analysis/faultspaceplot/faultspaceplot.sh b/tools/analysis/faultspaceplot/faultspaceplot.sh new file mode 100755 index 00000000..b7366cbc --- /dev/null +++ b/tools/analysis/faultspaceplot/faultspaceplot.sh @@ -0,0 +1,101 @@ +#!/bin/bash +set -e + +if [ "$1" = -k ]; then + KEEPCSV=yes + shift +else + KEEPCSV=no +fi + +if [ ! $# -eq 3 ]; then + echo "usage: $0 [ -k ] DATABASE VARIANT BENCHMARK" >&2 + echo " -k Keep compacted plot (and symbols) CSV" >&2 + exit 1 +fi + +DATABASE=$1 +VARIANT=$2 +BENCHMARK=$3 +MYSQL="mysql -B --quick $DATABASE" + +MYDIR=$(dirname $0) + +function table_exists() +{ + N=$(echo "SHOW TABLES LIKE '$1'" | $MYSQL $DATABASE | wc -l) + [ $N -gt 0 ] + return +} + +# get data +RAWCSV=$(mktemp) +echo "getting faultspace data.." +$MYSQL < $RAWCSV + SELECT t.time1 - (SELECT MIN(t2.time1) FROM trace t2 WHERE t.variant_id = t2.variant_id) AS time1, + t.time2 - (SELECT MIN(t2.time1) FROM trace t2 WHERE t.variant_id = t2.variant_id) AS time2, + t.data_address, r.bitoffset, r.injection_width, + CASE + WHEN r.resulttype = 'OK_MARKER' THEN '#FFFFFF' + WHEN r.resulttype = 'FAIL_MARKER' THEN '#EE0000' + WHEN r.resulttype = 'DETECTED_MARKER' THEN '#00FF00' + WHEN r.resulttype = 'GROUP0_MARKER' THEN '#EAEAEA' + WHEN r.resulttype = 'GROUP1_MARKER' THEN '#EBEBEB' + WHEN r.resulttype = 'GROUP2_MARKER' THEN '#ECECEC' + WHEN r.resulttype = 'GROUP3_MARKER' THEN '#EDEDED' + WHEN r.resulttype = 'TIMEOUT' THEN '#CCCC00' + WHEN r.resulttype = 'TRAP' THEN '#00CCCC' + WHEN r.resulttype = 'WRITE_TEXTSEGMENT' THEN '#0000AA' + WHEN r.resulttype = 'WRITE_OUTERSPACE' THEN '#0000BB' + WHEN r.resulttype = 'SDC' THEN '#FF0000' + WHEN r.resulttype = 'UNKNOWN' THEN '#000000' + END AS color + FROM trace t + JOIN fspgroup g ON t.variant_id = g.variant_id AND t.data_address = g.data_address AND t.instr2 = g.instr2 + JOIN variant v ON v.id = t.variant_id + JOIN result_GenericExperimentMessage r ON r.pilot_id=g.pilot_id + WHERE + v.variant = '$VARIANT' AND v.benchmark = '$BENCHMARK' + AND r.resulttype != 'OK_MARKER' + AND t.accesstype = 'R'; +EOT + +# compact data +echo "compacting data.." +COMPACTCSV=$(mktemp) +"$MYDIR"/fsp.compact.sh $RAWCSV $COMPACTCSV +rm $RAWCSV + +# fetch symbols if available +SYMBOLCSV= +if table_exists symbol; then + SYMBOLCSV=$(mktemp) + echo "getting symbol information ..." + $MYSQL < $SYMBOLCSV + SELECT s.address, s.size, s.name + FROM variant v + JOIN symbol s ON s.variant_id = v.id + WHERE v.variant = '$VARIANT' AND v.benchmark = '$BENCHMARK' + ORDER BY s.address +EOT +fi + +# plot data +echo "plotting.." +if [ $KEEPCSV = yes ]; then + KEPT="$VARIANT"_"$BENCHMARK"-plot.csv + mv "$COMPACTCSV" "$KEPT" + COMPACTCSV=$KEPT +fi +if [ ! -z "$SYMBOLCSV" -a $(wc -l < $SYMBOLCSV) -gt 1 ]; then + if [ $KEEPCSV = yes ]; then + KEPT="$VARIANT"_"$BENCHMARK"-symbols.csv + mv "$SYMBOLCSV" "$KEPT" + SYMBOLCSV=$KEPT + fi + "$MYDIR"/fsp.plot.py "$COMPACTCSV" "$SYMBOLCSV" + [ $KEEPCSV = no ] && rm "$SYMBOLCSV" +else + "$MYDIR"/fsp.plot.py "$COMPACTCSV" +fi +[ $KEEPCSV = no ] && rm "$COMPACTCSV" diff --git a/tools/analysis/faultspaceplot/fsp.compact-horizontal.sh b/tools/analysis/faultspaceplot/fsp.compact-horizontal.sh new file mode 100755 index 00000000..82710868 --- /dev/null +++ b/tools/analysis/faultspaceplot/fsp.compact-horizontal.sh @@ -0,0 +1,52 @@ +#!/bin/bash +set -e + +TMP=$(mktemp) + +read HEADER +echo "$HEADER" + +BASE='' +LAST='' +sort -snk 1 | sort -snk 4 | sort -snk 3 > $TMP + +while read CUR +do + a=($CUR) + C_I1=${a[0]} + C_I2=${a[1]} + C_ADDR=${a[2]} + C_BIT=${a[3]} + C_BW=${a[4]} + C_COL=${a[5]} + + if [ -z "$BASE" ] + then + BASE=$CUR + LAST=$CUR + elif (($C_I1 != $L_I2 + 1 || $L_ADDR != $C_ADDR || $L_BIT != $C_BIT || $L_BW != $C_BW)) || \ + [ "$C_COL" != "$L_COL" ] + then + a=($BASE) + B_I1=${a[0]} + + echo -e "$B_I1\t$L_I2\t$L_ADDR\t$L_BIT\t$L_BW\t$L_COL" + + BASE=$CUR + fi + + LAST="$CUR" + L_I1=$C_I1 + L_I2=$C_I2 + L_ADDR=$C_ADDR + L_BIT=$C_BIT + L_BW=$C_BW + L_COL=$C_COL +done < $TMP + +a=($BASE) +B_I1=${a[0]} + +echo -e "$B_I1\t$L_I2\t$L_ADDR\t$L_BIT\t$L_BW\t$L_COL" + +rm $TMP diff --git a/tools/analysis/faultspaceplot/fsp.compact-vertical.sh b/tools/analysis/faultspaceplot/fsp.compact-vertical.sh new file mode 100755 index 00000000..f819a643 --- /dev/null +++ b/tools/analysis/faultspaceplot/fsp.compact-vertical.sh @@ -0,0 +1,63 @@ +#!/bin/bash +set -e + +TMP=$(mktemp) + +read HEADER +echo "$HEADER" + +BASE='' +LAST='' +sort -snk 4 | sort -snk 3 | sort -snk 1 > $TMP + +while read CUR +do + a=($CUR) + C_I1=${a[0]} + C_I2=${a[1]} + C_ADDR=${a[2]} + C_BIT=${a[3]} + C_BW=${a[4]} + C_COL=${a[5]} + C_N=$(($C_ADDR*8 + $C_BIT)) + + if [ -z "$BASE" ] + then + BASE=$CUR + LAST=$CUR + elif (($C_I1 != $L_I1 || $C_I2 != $L_I2 || \ + $L_N + $L_BW != $C_N)) || \ + [ "$C_COL" != "$L_COL" ] + then + a=($BASE) + B_ADDR=${a[2]} + B_BIT=${a[3]} + B_N=$(($B_ADDR*8 + $B_BIT)) + + BW=$(($L_N + $L_BW - $B_N)) + + echo -e "$L_I1\t$L_I2\t$B_ADDR\t$B_BIT\t$BW\t$L_COL" + + BASE=$CUR + fi + + LAST="$CUR" + L_I1=$C_I1 + L_I2=$C_I2 + L_ADDR=$C_ADDR + L_BIT=$C_BIT + L_BW=$C_BW + L_COL=$C_COL + L_N=$C_N +done < $TMP + +a=($BASE) +B_ADDR=${a[2]} +B_BIT=${a[3]} +B_N=$(($B_ADDR*8 + $B_BIT)) + +BW=$(($L_N + $L_BW - $B_N)) + +echo -e "$L_I1\t$L_I2\t$B_ADDR\t$B_BIT\t$BW\t$L_COL" + +rm $TMP diff --git a/tools/analysis/faultspaceplot/fsp.compact.sh b/tools/analysis/faultspaceplot/fsp.compact.sh new file mode 100755 index 00000000..bd291339 --- /dev/null +++ b/tools/analysis/faultspaceplot/fsp.compact.sh @@ -0,0 +1,43 @@ +#!/bin/bash +set -e + +FIRST=vertical + +[ $# -lt 2 -o $# -gt 3 ] && echo "usage: $0 input.csv output.csv [vertical|horizontal]" >&2 && exit 1 + +if [ -z $3 ]; then + FIRST=vertical +else + FIRST=$3 +fi + +TMP1=$(mktemp) +TMP2=$(mktemp) + +cp $1 $TMP1 +COUNT=$(wc -l <$TMP1) + +NEXT=$FIRST + +while true +do + echo "at $COUNT, $NEXT ..." + fsp.compact-$NEXT.sh < $TMP1 > $TMP2 + + PREVCOUNT=$COUNT + COUNT=$(wc -l <$TMP2) + + if (($COUNT >= $PREVCOUNT)) + then + echo "no improvement (now $COUNT), stop." + cp $TMP1 $2 + break + fi + + mv $TMP2 $TMP1 + if [ $NEXT = vertical ]; then + NEXT=horizontal + else + NEXT=vertical + fi +done diff --git a/tools/analysis/faultspaceplot/fsp.plot.py b/tools/analysis/faultspaceplot/fsp.plot.py new file mode 100755 index 00000000..25d9f8a3 --- /dev/null +++ b/tools/analysis/faultspaceplot/fsp.plot.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python + +# ------------------------------------------------------------------------- +# Import modules +import csv, sys +import matplotlib.pyplot as plt +import numpy as np +import matplotlib.patches as mpatches +import matplotlib.pylab as pylab + +ENTRY_COUNT_PER_LINE = 6 +RESULT_RECT_HEIGHT = 1.0/8 + +# ------------------------------------------------------------------------- +# Basic drawing class +class Rectangle: + def __init__(self, x, y, w, h, color): + # w + self.xpos = x # |-------------------| + self.ypos = y # | | h + self.width = w # | | + self.height = h # (x,y)---------------| + self.color = color # fill color + self.alpha = None # transparency level [0,1] + def draw(self): + """ Draw the rectangle. """ + if self.color == (1,1,1): + return + tmp = self.xpos, self.ypos, + p = mpatches.Rectangle(tmp, self.width, self.height, color=self.color, \ + alpha=self.alpha) + plt.gca().add_patch(p) + +# ------------------------------------------------------------------------- +# Check provided arguments: +if len(sys.argv) <= 1: + print "ERROR: Not enough arguments provided! See -h for more infos." + exit(1) +if sys.argv[1] == '-h': + print "Displays experiment results for the weather-monitor-experiment." + print " CALL-SYNTAX: fsp.plot.py DATA_FILE [USER_TAG_FILE]" + print "DATA_FILE is a CSV-file, storing the tab-separated values" + print "retrieved by the experiment run. USER_TAG_FILE is an optional" + print "CSV-file which can be used to add user-specific marks to the" + print "plot." # TODO: be more precise here + exit(0) + +print "Opening and processing \"" + sys.argv[1] + "\"..." +file = open(sys.argv[1], "r") +dialect = csv.Sniffer().sniff(file.read(1024)) +file.seek(0) +reader = csv.reader(file, dialect) +reader.next() # Move down a line to skip the header + +if len(sys.argv) >= 3: + print "Opening and processing \"" + sys.argv[2] + "\"..." + symbolfile = open(sys.argv[2], "r") + dialect = csv.Sniffer().sniff(symbolfile.read(1024)) + symbolfile.seek(0) + symbolreader = csv.reader(symbolfile, dialect) + symbolreader.next() # Move down a line to skip the header + have_symbols = True +else: + have_symbols = False + +fig = plt.figure() + +xmin = 99999999 +xmax = 0 +ymin = 99999999 +ymax = 0 + +line_counter = 1 +for row in reader: + line_counter += 1 + # Check if there are at least ENTRY_COUNT_PER_LINE entries per line: + if len(row) != ENTRY_COUNT_PER_LINE: + print "ERROR: Line " + str(line_counter) + " is invalid (" +\ + str(ENTRY_COUNT_PER_LINE) + " entries expected)" + sys.exit(1) + + # Some constants to access the row-entries much easier: + IDX_INSTR1 = 0; IDX_INSTR2 = 1; IDX_DATA_ADDRESS = 2; + IDX_BITNR = 3; IDX_BITWIDTH = 4; IDX_COLOR = 5; + + # Update xmin/xmax/ymin/ymax + x1 = int(row[IDX_INSTR1]) + x2 = int(row[IDX_INSTR2]) + 1 # inclusive + width = x2 - x1 + if xmin > x1: + xmin = x1 + if xmax < x2: + xmax = x2 + + y1 = float(row[IDX_DATA_ADDRESS]) + float(row[IDX_BITNR]) / 8.0 + height = float(row[IDX_BITWIDTH]) / 8.0 + y2 = y1 + height + + if ymin > y1: + ymin = y1 + if ymax < y2: + ymax = y2 + + Rectangle(x1, y1, width, height, row[IDX_COLOR]).draw() + if line_counter == 50000: # debug stuff + pass + #break + +# round down to nearest 1000 +#ymin = int(ymin / 1000) * 1000 + +file.close() +plt.xlim(xmin, xmax) +plt.ylim(ymin, ymax) + +plt.ylabel('Data Memory (RAM)') +plt.xlabel('Time (Cycles)') + +# show symbols +if have_symbols: + # Adding 2nd y-axis for object names + ax1 = fig.add_subplot(111) + ax2 = ax1.twinx() + ax2.set_ylabel('Symbols') + + # necessary for proper symbol placement + plt.ylim(ymin, ymax) + + ticks = [] + symbols = [] + + IDX_SYMBOL_ADDRESS = 0; IDX_SYMBOL_SIZE = 1; IDX_SYMBOL_NAME = 2; + + for row in symbolreader: + # TODO: somehow plot size as well + address = int(row[IDX_SYMBOL_ADDRESS]) + size = int(row[IDX_SYMBOL_SIZE]) + if (address >= ymin) and (address <= ymax): + ticks.append(address) + symbols.append(row[IDX_SYMBOL_NAME]) + #print "symbol: " + str(address) + " " + str(size) + " " + row[IDX_SYMBOL_NAME] + elif (address < ymin) and (address + size >= ymin): + ticks.append(ymin) + symbols.append("(" + row[IDX_SYMBOL_NAME] + ")") + #print "partial symbol: " + str(address) + " " + str(size) + " " + row[IDX_SYMBOL_NAME] + else: + #print "skipped symbol: " + str(address) + " " + str(size) + " " + row[IDX_SYMBOL_NAME] + pass + + # list of interesting addresses + ax2.yaxis.set_ticks(ticks) + ax2.yaxis.grid(b=True, color='gray') + + # create minor ticks centered between major tick marks + centered_ticks = [] + major_locator = ax2.yaxis.get_major_locator() + major_locs = major_locator() + for i in range(1,len(major_locs)): + y_last,y = major_locs[i-1],major_locs[i] + centered_ticks.append(0.5*(y+y_last)) + ax2.yaxis.set_ticks(centered_ticks, minor=True) + + # list of corresponding symbol names + ax2.set_yticklabels(symbols, minor=False, rotation=0, rotation_mode='anchor') + +plt.show() +#pylab.savefig('baseline.pdf', bbox_inches='tight') diff --git a/tools/import-trace/CMakeLists.txt b/tools/import-trace/CMakeLists.txt index b8a04e49..9e9d3a30 100644 --- a/tools/import-trace/CMakeLists.txt +++ b/tools/import-trace/CMakeLists.txt @@ -42,3 +42,4 @@ if (BUILD_LLVM_DISASSEMBLER) endif (BUILD_LLVM_DISASSEMBLER) install(TARGETS import-trace RUNTIME DESTINATION bin) +install(PROGRAMS import-symbols.sh DESTINATION bin) diff --git a/tools/import-trace/import-symbols.sh b/tools/import-trace/import-symbols.sh new file mode 100755 index 00000000..4cdb27ec --- /dev/null +++ b/tools/import-trace/import-symbols.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# TODO: make this part of import-trace +set -e + +# handle command line arguments +if [ ! $# -eq 4 ]; then + echo "usage: $0 DATABASE VARIANT BENCHMARK ELF" >&2 + exit 1 +fi + +DATABASE=$1 +VARIANT=$2 +BENCHMARK=$3 +ELF=$4 +TABLE=symbol +MYSQL="mysql -B --quick $DATABASE" + + +# check if ELF exists +if [ ! -e $ELF ]; then + echo "$ELF not found" >&2 + exit 1 +fi +echo "importing $VARIANT/$BENCHMARK symbols from $ELF ..." + + +# get fail*'s variant_id +ID=$(echo "SELECT id FROM variant WHERE variant='$VARIANT' AND benchmark='$BENCHMARK'" | $MYSQL -N) +if [ -z $ID ]; then + echo "no such variant/benchmark!" >&2 + exit 1 +fi + + +# generate huge SQL statement and execute +$MYSQL <