Files
fail/tools/import-trace/AdvancedMemoryImporter.cc
Horst Schirmeier 64b3af299e import-trace: sliding-window AdvancedMemoryImporter
Initially this was implemented by directly passing through trace
events to the MemoryImporter, keeping a record of conditional jumps
and opcodes, and UPDATEing all inserted rows in a second pass when the
MemoryImporter is finished.

Unfortunately, UPDATE is very slow, and keeping all information in
memory till the end doesn't scale indefinitely.  Therefore the
implementation now delays passing memory access events upwards to the
MemoryImporter only until enough branch history is aggregated, and
taps into Importer's database operations with a set of new virtual
functions that are called downwards.

Change-Id: I159b2533932087087fb3049f4ff07a5f17a25a00
2013-09-10 17:37:26 +02:00

172 lines
5.9 KiB
C++

#include <algorithm>
#include "AdvancedMemoryImporter.hpp"
using namespace llvm;
using namespace llvm::object;
using namespace fail;
static fail::Logger LOG("AdvancedMemoryImporter");
std::string AdvancedMemoryImporter::database_additional_columns()
{
return MemoryImporter::database_additional_columns() +
"opcode INT UNSIGNED NULL, "
"duration BIGINT UNSIGNED AS (time2 - time1 + 1) PERSISTENT, "
"jumphistory INT UNSIGNED NULL, ";
}
void AdvancedMemoryImporter::database_insert_columns(std::string& sql, unsigned& num_columns)
{
// FIXME upcall?
sql = ", opcode, jumphistory";
num_columns = 2;
}
//#include <google/protobuf/text_format.h>
bool AdvancedMemoryImporter::database_insert_data(Trace_Event &ev, MYSQL_BIND *bind, unsigned num_columns, bool is_fake)
{
static my_bool null = true;
// FIXME upcall?
assert(num_columns == 2);
#if 0
// sanity check
if (!is_fake && delayed_entries.size() > 0 && ev.ip() != delayed_entries.front().ev.ip()) {
std::string out;
google::protobuf::TextFormat::PrintToString(ev, &out);
std::cout << "ev: " << out << std::endl;
google::protobuf::TextFormat::PrintToString(delayed_entries.front().ev, &out);
std::cout << "delayed_entries.front.ev: " << out << std::endl;
}
#endif
assert(is_fake || delayed_entries.size() == 0 || ev.ip() == delayed_entries.front().ev.ip());
bind[0].buffer_type = MYSQL_TYPE_LONG;
bind[0].is_unsigned = 1;
bind[0].buffer = &delayed_entries.front().opcode;
bind[1].buffer_type = MYSQL_TYPE_LONG;
bind[1].is_unsigned = 1;
bind[1].buffer = &m_cur_branchmask;
if (is_fake) {
bind[0].is_null = &null;
bind[1].is_null = &null;
}
return true;
}
void AdvancedMemoryImporter::insert_delayed_entries(bool finalizing)
{
unsigned branchmask;
unsigned last_branches_before = UINT_MAX;
// If we don't know enough future, and there's a chance we'll learn more,
// delay further.
for (std::deque<DelayedTraceEntry>::iterator it = delayed_entries.begin();
it != delayed_entries.end() &&
(it->branches_before + BRANCH_WINDOW_SIZE <= branches_taken.size() ||
finalizing);
it = delayed_entries.erase(it)) {
// determine branche decisions before / after this mem event
if (it->branches_before != last_branches_before) {
branchmask = 0;
int pos = std::max(-(signed)BRANCH_WINDOW_SIZE, - (signed) it->branches_before);
int maxpos = std::min(BRANCH_WINDOW_SIZE, branches_taken.size() - it->branches_before);
for (; pos < maxpos; ++pos) {
branchmask |=
((unsigned) branches_taken[it->branches_before + pos])
<< (16 - pos - 1);
}
m_cur_branchmask = branchmask;
}
//LOG << "AdvancedMemoryImporter::insert_delayed_entries instr = " << it->instr << " data_address = " << it->ev.memaddr() << std::endl;
// trigger INSERT
// (will call back via database_insert_data() and ask for additional data)
MemoryImporter::handle_mem_event(it->curtime, it->instr, it->ev);
}
// FIXME branches_taken could be shrunk here to stay within a bounded
// memory footprint
}
bool AdvancedMemoryImporter::handle_ip_event(fail::simtime_t curtime, instruction_count_t instr,
Trace_Event &ev)
{
// Previous instruction was a branch, check and remember whether it was taken
if (m_last_was_conditional_branch) {
m_last_was_conditional_branch = false;
branches_taken.push_back(ev.ip() != m_ip_jump_not_taken);
}
// Check whether we know enough branch-taken future to INSERT a few more
// (delayed) trace entries
insert_delayed_entries(false);
if (!binary) {
/* Disassemble the binary if necessary */
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllDisassemblers();
if (error_code ec = createBinary(m_elf->getFilename(), binary)) {
LOG << m_elf->getFilename() << "': " << ec.message() << ".\n";
return false;
}
// necessary due to an AspectC++ bug triggered by LLVM 3.3's dyn_cast()
#ifndef __puma
ObjectFile *obj = dyn_cast<ObjectFile>(binary.get());
disas.reset(new LLVMDisassembler(obj));
#endif
disas->disassemble();
LLVMDisassembler::InstrMap &instr_map = disas->getInstrMap();
LOG << "instructions disassembled: " << std::dec << instr_map.size() << " Triple: " << disas->GetTriple() << std::endl;
#if 0
for (LLVMDisassembler::InstrMap::const_iterator it = instr_map.begin();
it != instr_map.end(); ++it) {
LOG << "DIS " << std::hex << it->second.address << " " << (int) it->second.length << std::endl;
}
#endif
}
const LLVMDisassembler::InstrMap &instr_map = disas->getInstrMap();
const LLVMDisassembler::InstrMap::const_iterator it = instr_map.find(ev.ip());
if (it == instr_map.end()) {
LOG << "WARNING: LLVMDisassembler hasn't disassembled instruction at 0x"
<< ev.ip() << " -- are you using LLVM < 3.3?" << std::endl;
return true; // probably weird things will happen now
}
const LLVMDisassembler::Instr &opcode = it->second;
/* Now we've got the opcode and know whether it's a conditional branch. If
* it is, the next IP event will tell us whether it was taken or not. */
if (opcode.conditional_branch) {
m_last_was_conditional_branch = true;
m_ip_jump_not_taken = opcode.address + opcode.length;
}
// IP events may need to be delayed, too, if the parent Importer draws any
// information from them. MemoryImporter does not, though.
//return MemoryImporter::handle_ip_event(curtime, instr, ev);
return true;
}
bool AdvancedMemoryImporter::handle_mem_event(fail::simtime_t curtime, instruction_count_t instr,
Trace_Event &ev)
{
const LLVMDisassembler::InstrMap &instr_map = disas->getInstrMap();
const LLVMDisassembler::Instr &opcode = instr_map.at(ev.ip());
DelayedTraceEntry entry = { curtime, instr, ev, opcode.opcode, branches_taken.size() };
delayed_entries.push_back(entry);
// delay upcall to handle_mem_event until we know enough future branch decisions
return true;
}
bool AdvancedMemoryImporter::trace_end_reached()
{
LOG << "inserting remaining trace events ..." << std::endl;
// INSERT the remaining entries (with incomplete branch future)
insert_delayed_entries(true);
return true;
}