tools/import-trace: import timing information + various additions
- Import timing information from traces that were recorded with timing.
- Allow restricting import to a memory map ("vertical" restriction).
- Proper fault-space right-margin handling.
- Cleanups, data-type usage, etc.
Change-Id: I7a49e8e9e49894c458e884bfc234f36b9ba8b130
This commit is contained in:
@ -28,36 +28,74 @@ bool Importer::clear_database() {
|
||||
}
|
||||
|
||||
bool Importer::copy_to_database(fail::ProtoIStream &ps) {
|
||||
unsigned row_count = 0;
|
||||
unsigned row_count = 0, row_count_fake = 0;
|
||||
// map for keeping one "open" EC for every address
|
||||
// (maps injection data address => equivalence class)
|
||||
// (maps injection data address =>
|
||||
// dyn. instruction count / time information for equivalence class left margin)
|
||||
AddrLastaccessMap open_ecs;
|
||||
|
||||
// instruction counter within trace
|
||||
unsigned instr = 0;
|
||||
// time the trace started/ended
|
||||
// For now we just use the min/max occuring timestamp; for "sparse" traces
|
||||
// (e.g., only mem accesses, only a subset of the address space) it might
|
||||
// be a good idea to store this explicitly in the trace file, though.
|
||||
simtime_t time_trace_start = 0, curtime = 0;
|
||||
|
||||
// "rightmost" instr where we did a FI experiment
|
||||
unsigned instr_rightmost = 0;
|
||||
// instruction counter within trace
|
||||
instruction_count_t instr = 0;
|
||||
|
||||
Trace_Event ev;
|
||||
|
||||
while (ps.getNext(&ev)) {
|
||||
if (ev.has_time_delta()) {
|
||||
// record trace start
|
||||
// it suffices to do this once, the events come in sorted
|
||||
if (time_trace_start == 0) {
|
||||
time_trace_start = ev.time_delta();
|
||||
LOG << "trace start time: " << time_trace_start << std::endl;
|
||||
}
|
||||
// curtime also always holds the max time, provided we only get
|
||||
// nonnegative deltas
|
||||
assert(ev.time_delta() >= 0);
|
||||
curtime += ev.time_delta();
|
||||
}
|
||||
|
||||
// instruction events just get counted
|
||||
if (!ev.has_memaddr()) {
|
||||
// new instruction
|
||||
// sanity check for overflow
|
||||
if (instr == (1LL << (sizeof(instr)*8)) - 1) {
|
||||
LOG << "error: instruction_count_t overflow, aborting at instr=" << instr << std::endl;
|
||||
return false;
|
||||
}
|
||||
instr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
address_t from = ev.memaddr(), to = ev.memaddr() + ev.width();
|
||||
// Iterate over all accessed bytes
|
||||
// FIXME Keep complete trace information (access width)?
|
||||
// advantages: may be used for pruning strategies, complete value would be visible; less DB entries
|
||||
// disadvantages: may need splitting when width varies, lots of special case handling
|
||||
// Probably implement this in a separate importer when necessary.
|
||||
for (address_t data_address = from; data_address < to; ++data_address) {
|
||||
|
||||
int instr1 = open_ecs[data_address]; // defaults to 0 if nonexistent
|
||||
int instr2 = instr; // the current instruction
|
||||
// skip events outside a possibly supplied memory map
|
||||
if (m_mm && !m_mm->isMatching(data_address)) {
|
||||
continue;
|
||||
}
|
||||
instruction_count_t instr1 =
|
||||
open_ecs[data_address].dyninstr; // defaults to 0 if nonexistent
|
||||
instruction_count_t instr2 = instr; // the current instruction
|
||||
simtime_t time1 = open_ecs[data_address].time;
|
||||
// defaulting to 0 is not such a good idea, memory reads at the
|
||||
// beginning of the trace would get an unnaturally high weight:
|
||||
if (time1 == 0) {
|
||||
time1 = time_trace_start;
|
||||
}
|
||||
simtime_t time2 = curtime;
|
||||
|
||||
// skip zero-sized intervals: these can occur when an instruction
|
||||
// accesses a memory location more than once (e.g., INC, CMPXCHG)
|
||||
// FIXME: look at timing instead?
|
||||
if (instr1 > instr2) {
|
||||
continue;
|
||||
}
|
||||
@ -67,62 +105,77 @@ bool Importer::copy_to_database(fail::ProtoIStream &ps) {
|
||||
|
||||
// we now have an interval-terminating R/W event to the memaddr
|
||||
// we're currently looking at; the EC is defined by
|
||||
// data_address [instr1, instr2] (instr_absolute)
|
||||
if (!add_trace_event(instr1, instr2, ev)) {
|
||||
// data_address, dynamic instruction start/end, the absolute PC at
|
||||
// the end, and time start/end
|
||||
if (!add_trace_event(instr1, instr2, time1, time2, ev)) {
|
||||
LOG << "add_trace_event failed" << std::endl;
|
||||
return false;
|
||||
}
|
||||
row_count ++;
|
||||
if (row_count % 1000 == 0) {
|
||||
LOG << "Imported " << row_count << " traces into the database" << std::endl;
|
||||
}
|
||||
|
||||
if (ev.accesstype() == ev.READ) {
|
||||
// FIXME this is broken: we must abort after the rightmost R
|
||||
// and must not allow Ws to find their way into the known
|
||||
// results
|
||||
instr_rightmost = instr2;
|
||||
if (row_count % 10000 == 0) {
|
||||
LOG << "Inserted " << row_count << " trace events into the database" << std::endl;
|
||||
}
|
||||
|
||||
// next interval must start at next instruction; the aforementioned
|
||||
// skipping mechanism wouldn't work otherwise
|
||||
//lastuse_it->second = instr2 + 1;
|
||||
open_ecs[data_address] = instr2 + 1;
|
||||
open_ecs[data_address].dyninstr = instr2 + 1;
|
||||
open_ecs[data_address].time = time2 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
LOG << "Inserted " << row_count << " traces into the database" << std::endl;
|
||||
// Close all open intervals (right end of the fault-space) with fake trace
|
||||
// event. This ensures we have a rectangular fault space (important for,
|
||||
// e.g., calculating the total SDC rate), and unknown memory accesses after
|
||||
// the end of the trace are properly taken into account: Either with a
|
||||
// "don't care" (a synthetic memory write at the right margin), or a "care"
|
||||
// (synthetic read), the latter resulting in more experiments to be done.
|
||||
for (AddrLastaccessMap::iterator lastuse_it = open_ecs.begin();
|
||||
lastuse_it != open_ecs.end(); ++lastuse_it) {
|
||||
|
||||
// FIXME
|
||||
// close all open intervals (right end of the fault-space) with fake trace event
|
||||
// for (AddrLastaccessMap::iterator lastuse_it = open_ecs.begin();
|
||||
// lastuse_it != open_ecs.end(); ++lastuse_it) {
|
||||
// address_t data_address = lastuse_it->first;
|
||||
// int instr1 = lastuse_it->second;
|
||||
Trace_Event fake_ev;
|
||||
fake_ev.set_memaddr(lastuse_it->first);
|
||||
fake_ev.set_width(1);
|
||||
fake_ev.set_accesstype(m_faultspace_rightmargin == 'R' ? fake_ev.READ : fake_ev.WRITE);
|
||||
|
||||
instruction_count_t instr1 = lastuse_it->second.dyninstr;
|
||||
simtime_t time1 = lastuse_it->second.time;
|
||||
|
||||
// Why -1? In most cases it does not make sense to inject before the
|
||||
// very last instruction, as we won't execute it anymore. This *only*
|
||||
// makes sense if we also inject into parts of the result vector. This
|
||||
// is not the case in this experiment, and with -1 we'll get a result
|
||||
// comparable to the non-pruned campaign.
|
||||
instruction_count_t instr2 = instr - 1;
|
||||
|
||||
simtime_t time2 = curtime; // -1?
|
||||
|
||||
// #if 0
|
||||
// // Why -1? In most cases it does not make sense to inject before the
|
||||
// // very last instruction, as we won't execute it anymore. This *only*
|
||||
// // makes sense if we also inject into parts of the result vector. This
|
||||
// // is not the case in this experiment, and with -1 we'll get a result
|
||||
// // comparable to the non-pruned campaign.
|
||||
// int instr2 = instr - 1;
|
||||
// #else
|
||||
// // EcosKernelTestCampaign only variant: fault space ends with the last FI experiment
|
||||
// int instr2 = instr_rightmost;
|
||||
// // EcosKernelTestCampaign only variant: fault space ends with the last FI experiment
|
||||
// FIXME probably implement this with cmdline parameter FAULTSPACE_CUTOFF
|
||||
// int instr2 = instr_rightmost;
|
||||
// #endif
|
||||
// // zero-sized? skip.
|
||||
// if (instr1 > instr2) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// add_trace_event(variant_id, instr1, instr2,
|
||||
// data_address, 1, 'W', 0,
|
||||
// 0, 0, 0, 0, 0, true);
|
||||
// }
|
||||
// zero-sized? skip.
|
||||
// FIXME: look at timing instead?
|
||||
if (instr1 > instr2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// fsp.fini();
|
||||
if (!add_trace_event(instr1, instr2, time1, time2, fake_ev, true)) {
|
||||
LOG << "add_trace_event failed" << std::endl;
|
||||
return false;
|
||||
}
|
||||
++row_count_fake;
|
||||
}
|
||||
|
||||
LOG << "trace duration: " << (curtime - time_trace_start) << " ticks" << std::endl;
|
||||
LOG << "Inserted " << row_count << " trace events (+" << row_count_fake
|
||||
<< " fake events) into the database" << std::endl;
|
||||
|
||||
// TODO: (configurable) sanity checks
|
||||
// PC-based fault space rectangular, covered, and non-overlapping?
|
||||
// (same for timing-based fault space?)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user