Implemented two instantiations of Fault-Space Regions (FSR) as a program-structure-guided approximation of fault spaces based on the precise Def/Use-Pruning using basic blocks or function calls. Further reading: Program-Structure-Guided Approximation of Large Fault Spaces Oskar Pusz and Daniel Kiechle and Christian Dietrich and Daniel Lohmann In: 24th Pacific Rim International Symposium on Dependable Computing (PRDC'19) IEEE Computer Science Press, 2019 Some configurations for bochs and generic-experiment
343 lines
12 KiB
C++
343 lines
12 KiB
C++
#include "BasicBlockPruner.hpp"
|
|
#include "util/Logger.hpp"
|
|
#include <sstream>
|
|
|
|
|
|
|
|
using std::endl;
|
|
|
|
bool BasicBlockPruner::create_database() {
|
|
Pruner::create_database();
|
|
bool ret;
|
|
|
|
// We add an index to trace to not die of old age
|
|
std::string add_index = "ALTER TABLE `trace` ADD INDEX IF NOT EXISTS `time1` (`time1`);";
|
|
ret = (bool)db->query(add_index.c_str());
|
|
if (!ret) return false;
|
|
|
|
|
|
std::string create_statement =
|
|
"CREATE TABLE IF NOT EXISTS fspregion ("
|
|
" fspmethod_id int(11) NOT NULL,"
|
|
" variant_id int(11) NOT NULL,"
|
|
" id int(11) NOT NULL," // static_instr_t
|
|
" tag int(10) unsigned NOT NULL," // dynamic_instr_t
|
|
" instr1 int(10) unsigned NOT NULL," // dynamic_instr_t
|
|
" instr2 int(10) unsigned NOT NULL," // dynamic_time_t
|
|
" time1 bigint(10) NOT NULL," // dynamic_time_t
|
|
" time2 bigint(10) NOT NULL,"
|
|
" weight_inner int(11) NOT NULL,"
|
|
" weight_outer int(11) NOT NULL,"
|
|
" count_inner int(11) NOT NULL,"
|
|
" count_outer int(11) NOT NULL,"
|
|
" PRIMARY KEY (fspmethod_id, variant_id, id)"
|
|
") engine=MyISAM";
|
|
ret = (bool) db->query(create_statement.c_str());
|
|
if(!ret) return false;
|
|
|
|
create_statement = "DROP VIEW IF EXISTS fspregion_pilot";
|
|
ret = (bool)db->query(create_statement.c_str());
|
|
if(!ret) return false;
|
|
|
|
create_statement =
|
|
"CREATE VIEW fspregion_pilot AS SELECT"
|
|
" R.variant_id as variant_id,"
|
|
" R.fspmethod_id as region_fspmethod_id,"
|
|
" R.id as region_id,"
|
|
" R.tag as region_tag,"
|
|
" p.id as pilot_id,"
|
|
" t.time2 > R.time2 as is_outer,"
|
|
" (t.time2 - t.time1 + 1) as weight_regular,"
|
|
" IFNULL((t.time2 > R.time2) * (R.weight_inner + R.weight_outer) / R.count_outer, 0) as weight_mean,"
|
|
" IFNULL(((t.time2 > R.time2) * ((t.time2 - t.time1 + 1) / R.weight_outer) * (R.weight_inner + R.weight_outer)), 0) as weight_wmean"
|
|
" FROM fspregion R"
|
|
" STRAIGHT_JOIN trace t USE INDEX(time1) ON t.time1 between R.time1 and R.time2"
|
|
" JOIN fsppilot p ON p.fspmethod_id = (select id from fspmethod where method = 'basic')"
|
|
" AND t.variant_id = p.variant_id AND t.instr2 = p.instr2 AND t.data_address = p.data_address"
|
|
" WHERE t.variant_id = R.variant_id AND t.accesstype = 'R'";
|
|
|
|
ret = (bool)db->query(create_statement.c_str());
|
|
if (!ret) return false;
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
bool BasicBlockPruner::clear_database() {
|
|
// Clear fsppilot and fspgroup
|
|
Pruner::clear_database();
|
|
|
|
std::stringstream ss;
|
|
ss << "DELETE FROM fspregion WHERE variant_id IN (" << m_variants_sql
|
|
<< ") AND fspmethod_id = " << m_method_id;
|
|
bool ret = (bool) db->query(ss.str().c_str());
|
|
LOG << "deleted " << db->affected_rows() << " rows from fspregions table" << std::endl;
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, const BasicBlockPruner::trace_event_t &e) {
|
|
return os << "("
|
|
<< std::hex << e.static_instr << ", "
|
|
<< std::dec << e.dynamic_instr << ","
|
|
<< std::dec << e.dynamic_time << ")";
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, const BasicBlockPruner::region &R) {
|
|
os << "Region ID: " << R.id << std::endl;
|
|
os << "Region instr count: " << R.instruction_count << std::endl;
|
|
os << "Region begin: (addr, instr, time): " << R.begin << std::endl;
|
|
os << "Region end: (addr, instr, time): " << R.end << std::endl;
|
|
return os;
|
|
}
|
|
|
|
|
|
bool BasicBlockPruner::prune_all(){
|
|
for (const variant_t & variant : m_variants) {
|
|
instructions.clear();
|
|
leaders.clear();
|
|
regions.clear();
|
|
|
|
LOG << "Pruning for variant: " << variant.variant << "/" << variant.benchmark << endl;
|
|
|
|
// Import Objdump events
|
|
if(!this->importObjdump(variant)) return false;
|
|
|
|
// Find basic blocks..
|
|
// -> Find beginnings of basic bloks
|
|
if(!this->findRegionLeaders()) return false;
|
|
|
|
// -> Find ends of basic bloks
|
|
if(!this->findRegions(variant)) return false;
|
|
|
|
|
|
// -> Find ends of basic bloks
|
|
if(!this->exportRegions(variant)) return false;
|
|
|
|
LOG << "End of pruning for variant: " << variant.variant << "/" << variant.benchmark << endl << endl;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
class trace_stream {
|
|
fail::Logger LOG;;
|
|
|
|
using trace_event_t = BasicBlockPruner::trace_event_t;
|
|
std::ifstream normal_stream;
|
|
igzstream gz_stream;
|
|
BasicBlockPruner::dynamic_instr_t instr;
|
|
fail::simtime_t curtime;
|
|
fail::ProtoIStream ps;
|
|
|
|
std::istream &openStream(const char *input_file, std::ifstream& normal_stream, igzstream& gz_stream) {
|
|
normal_stream.open(input_file);
|
|
if (!normal_stream) {
|
|
LOG << "couldn't open " << input_file << endl;
|
|
exit(-1);
|
|
}
|
|
unsigned char b1, b2;
|
|
normal_stream >> b1 >> b2;
|
|
|
|
if (b1 == 0x1f && b2 == 0x8b) {
|
|
normal_stream.close();
|
|
gz_stream.open(input_file);
|
|
if (!gz_stream) {
|
|
LOG << "couldn't open " << input_file << endl;
|
|
exit(-1);
|
|
}
|
|
LOG << "opened file " << input_file << " in GZip mode" << endl;
|
|
return gz_stream;
|
|
}
|
|
|
|
normal_stream.seekg(0);
|
|
|
|
LOG << "opened file " << input_file << " in normal mode" << endl;
|
|
return normal_stream;
|
|
}
|
|
|
|
public:
|
|
trace_stream(std::string file) :
|
|
LOG("trace_stream"), normal_stream(), gz_stream(), instr(0), curtime(0),
|
|
ps(&openStream(file.c_str(), normal_stream, gz_stream))
|
|
{}
|
|
|
|
bool next(trace_event_t &event) {
|
|
Trace_Event ev;
|
|
|
|
while (ps.getNext(&ev)) {
|
|
if (ev.has_time_delta()) {
|
|
// curtime also always holds the max time, provided we only get
|
|
// nonnegative deltas
|
|
assert(ev.time_delta() >= 0);
|
|
curtime += ev.time_delta();
|
|
}
|
|
|
|
// is instruction event
|
|
if (!ev.has_memaddr()) {
|
|
event.static_instr = ev.ip();
|
|
event.dynamic_instr = instr;
|
|
event.dynamic_time = curtime;
|
|
instr++;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
};
|
|
|
|
bool BasicBlockPruner::importObjdump(const variant_t &variant){
|
|
std::stringstream ss;
|
|
ss << "SELECT instr_address, (char_length(opcode) DIV 2) FROM objdump WHERE variant_id = "
|
|
<< variant.id
|
|
<< " ORDER BY instr_address";
|
|
|
|
MYSQL_RES *res = db->query_stream(ss.str().c_str());
|
|
if (!res) {
|
|
LOG << "ERROR: sql read to objdump table failed" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
MYSQL_ROW row;
|
|
while ((row = mysql_fetch_row(res))) {
|
|
static_instr_t pc = std::strtoul(row[0], 0, 10);
|
|
instr_width_t width = std::strtoul(row[1], 0, 10);
|
|
this->instructions[pc] = width;
|
|
}
|
|
LOG << "objdump: " << this->instructions.size() << " instructions" << endl;
|
|
if (this->instructions.size() == 0) {
|
|
LOG << "ERROR: No objdump found" << std::endl;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool BasicBlockPruner::findRegionLeaders(){
|
|
trace_stream stream(trace_file);
|
|
trace_event_t previous, next;
|
|
bool valid = stream.next(next);
|
|
leaders.insert(next.static_instr);
|
|
assert(valid && "There must be at least one valid instruction"); (void) valid;
|
|
unsigned count = 1;
|
|
|
|
while (previous = next, stream.next(next)) {
|
|
count ++;
|
|
// Sometime, for unknown reasons, traces contain events that
|
|
// are contained within other instructions.
|
|
static_instr_t l = previous.static_instr;
|
|
static_instr_t w = this->instructions.at(l);
|
|
if ( l < next.static_instr && next.static_instr < (l+w)) {
|
|
LOG << "WARNING: Instruction within previous instruction: 0x" << next.static_instr << endl;
|
|
if(!stream.next(next)) break;
|
|
continue;
|
|
}
|
|
|
|
if (!inSameRegion(previous.static_instr, next.static_instr)) {
|
|
leaders.insert(next.static_instr);
|
|
instr_width_t width = this->instructions.at(previous.static_instr);
|
|
leaders.insert(previous.static_instr + width);
|
|
}
|
|
}
|
|
LOG << "Crawled " << count << " dynamic instructions" << std::endl;
|
|
LOG << "Found " << leaders.size() << " regions leaders addresses" << std::endl;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool BasicBlockPruner::findRegions(const variant_t& variant){
|
|
trace_stream stream(trace_file);
|
|
// Loop trace
|
|
trace_event_t event;
|
|
bool valid = stream.next(event);
|
|
unsigned dynamic_regions = 0;
|
|
std::set<static_instr_t> static_regions;
|
|
|
|
std::string create_table =
|
|
"CREATE TABLE fspregion_helper ("
|
|
" id int(11),"
|
|
" tag int(10) unsigned,"
|
|
" instr1 int(10),"
|
|
" instr2 int(10),"
|
|
" time1 bigint(10),"
|
|
" time2 bigint(10)"
|
|
")";
|
|
if(!db->query(create_table.c_str())) return false;
|
|
|
|
std::string insert_sql = "INSERT INTO fspregion_helper (id, tag, instr1, instr2, time1, time2) VALUES ";
|
|
std::stringstream value_sql;
|
|
|
|
while (valid) {
|
|
// Generate new dynamic region object
|
|
region region(dynamic_regions, event);
|
|
dynamic_regions++;
|
|
static_regions.insert(event.static_instr);
|
|
|
|
// Add all instructions of region to dynamic region object
|
|
do {
|
|
region.appendInstruction(event);
|
|
valid = stream.next(event);
|
|
} while(valid && leaders.count(event.static_instr) == 0);
|
|
|
|
|
|
// Insert range into temporary table
|
|
value_sql << "("
|
|
<< region.id << ","
|
|
<< region.begin.static_instr << ","
|
|
<< region.begin.dynamic_instr << ","
|
|
<< region.end.dynamic_instr << ","
|
|
<< region.begin.dynamic_time << ","
|
|
<< region.end.dynamic_time
|
|
<< ")";
|
|
std::string value_sql_str = value_sql.str();
|
|
value_sql.str("");
|
|
if (!db->insert_multiple(insert_sql.c_str(), value_sql_str.c_str())) {
|
|
LOG << "Database::insert_multiple() failed" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
|
|
}
|
|
// Flush insert multiple cache
|
|
db->insert_multiple();
|
|
|
|
LOG << "Found " << dynamic_regions << " dynamic regions " << endl;
|
|
LOG << "Found " << static_regions.size() << " static regions " << endl;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool BasicBlockPruner::exportRegions(const variant_t& variant){
|
|
bool ok = true;
|
|
|
|
std::stringstream ss;
|
|
ss << "INSERT INTO fspregion (fspmethod_id, variant_id, id, tag, instr1, instr2, time1, time2, "
|
|
<< " weight_inner, weight_outer, count_inner, count_outer) "
|
|
<< "SELECT " << m_method_id << "," << variant.id <<","
|
|
<< " R.id, R.tag, R.instr1, R.instr2, R.time1, R.time2,"
|
|
<< " IFNULL(weight_inner,0), IFNULL(weight_outer,0), IFNULL(count_inner,0), IFNULL(count_outer,0)"
|
|
<< " FROM fspregion_helper R"
|
|
<< " LEFT JOIN (SELECT RR.id as region_id, "
|
|
<< " sum((t.time2 - t.time1 + 1) * (t.time2 <= RR.time2)) as weight_inner,"
|
|
<< " sum((t.time2 - t.time1 + 1) * (t.time2 > RR.time2)) as weight_outer,"
|
|
<< " sum((t.time2 <= RR.time2)) as count_inner,"
|
|
<< " sum((t.time2 > RR.time2)) as count_outer"
|
|
<< " FROM fspregion_helper RR"
|
|
<< " JOIN trace t USE INDEX(time1) ON t.time1 between RR.time1 and RR.time2"
|
|
<< " WHERE t.accesstype='R' AND t.variant_id = " << variant.id
|
|
<< " GROUP BY RR.id) AS RJ"
|
|
<< " ON RJ.region_id = R.id";
|
|
std::string sql = ss.str();
|
|
ok = (bool)db->query(sql.c_str());
|
|
if(!ok) return false;
|
|
|
|
LOG << "created " << db->affected_rows() << " fspregion entries" << std::endl;
|
|
|
|
// Remove the temporary table
|
|
std::string drop_table = "DROP TABLE fspregion_helper";
|
|
if(!db->query(drop_table.c_str())) return false;
|
|
|
|
return ok;
|
|
}
|