#include #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 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::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 branch 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((signed)BRANCH_WINDOW_SIZE, (signed)branches_taken.size() - (signed)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(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, (unsigned) 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; }