Compare commits
	
		
			11 Commits
		
	
	
		
			03bec27376
			...
			Trace_enha
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 1720bd4aaa | |||
| df16378605 | |||
| 1438f0f373 | |||
| 766f3ba9ee | |||
| 5da4e6b424 | |||
| e382217e04 | |||
| 9db4e3fd87 | |||
| bb658be3b4 | |||
| 6579780dc9 | |||
| e56bc12788 | |||
| e88f309ea2 | 
| @@ -83,6 +83,10 @@ elseif(TARGET elfio::elfio) | ||||
| else() | ||||
|     message(FATAL_ERROR "No elfio library found, maybe a find_package() call is missing") | ||||
| endif() | ||||
| if(TARGET lz4::lz4) | ||||
|     target_compile_definitions(${PROJECT_NAME} PUBLIC WITH_LZ4) | ||||
|     target_link_libraries(${PROJECT_NAME} PUBLIC lz4::lz4) | ||||
| endif() | ||||
| if(TARGET RapidJSON) | ||||
|     target_link_libraries(${PROJECT_NAME} PUBLIC RapidJSON) | ||||
| endif() | ||||
|   | ||||
| @@ -163,13 +163,13 @@ struct ${coreDef.name.toLowerCase()}: public arch_if { | ||||
|         uint${byteSize(reg.size)}_t ${reg.name} = 0;<% | ||||
|         }}%> | ||||
|     } reg; | ||||
| #pragma pack(pop) | ||||
|     uint32_t trap_state = 0, pending_trap = 0; | ||||
|     uint64_t icount = 0; | ||||
|     uint64_t cycle = 0; | ||||
|     uint64_t instret = 0; | ||||
|     uint32_t instruction = 0; | ||||
|     uint32_t last_branch = 0; | ||||
| #pragma pack(pop) | ||||
|     std::array<address_type, 4> addr_mode; | ||||
|      | ||||
|     uint64_t interrupt_sim=0; | ||||
|   | ||||
| @@ -314,11 +314,9 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | ||||
| 		        <%}}%>// calculate next pc value | ||||
| 		        *NEXT_PC = *PC + ${instr.length/8}; | ||||
| 		        // execute instruction | ||||
| 		        try { | ||||
| 		        <%instr.behavior.eachLine{%>${it} | ||||
| 		        <%}%>} catch(...){} | ||||
| 	    	} | ||||
| 	    	break;<%}%> | ||||
| 		        <%}%>TRAP_${instr.name}:break; | ||||
| 	    	}// @suppress("No break at end of case")<%}%> | ||||
|             default: { | ||||
|                 *NEXT_PC = *PC + ((instr & 3) == 3 ? 4 : 2); | ||||
|                 raise(0,  2); | ||||
|   | ||||
| @@ -567,22 +567,22 @@ iss::status riscv_hart_m_p<BASE, FEAT>::read(const address_type type, const acce | ||||
|     try { | ||||
|         switch (space) { | ||||
|         case traits<BASE>::MEM: { | ||||
|             if (unlikely((access == iss::access_type::FETCH || access == iss::access_type::DEBUG_FETCH) && (addr & 0x1) == 1)) { | ||||
|             auto alignment = is_fetch(access)? (traits<BASE>::MISA_VAL&0x100? 2 : 4) : length; | ||||
|             if (unlikely(is_fetch(access) && (addr&(alignment-1)))) { | ||||
|                 fault_data = addr; | ||||
|                 if (access && iss::access_type::DEBUG) throw trap_access(0, addr); | ||||
|                 if (is_debug(access)) throw trap_access(0, addr); | ||||
|                 this->trap_state = (1 << 31); // issue trap 0 | ||||
|                 return iss::Err; | ||||
|             } | ||||
|             try { | ||||
|                 auto alignment = access == iss::access_type::FETCH? (traits<BASE>::MISA_VAL&0x100? 2 : 4) : length; | ||||
|                 if(alignment>1 && (addr&(alignment-1))){ | ||||
|                 if(!is_debug(access) && (addr&(alignment-1))){ | ||||
|                     this->trap_state = 1<<31 | 4<<16; | ||||
|                     fault_data=addr; | ||||
|                     return iss::Err; | ||||
|                 } | ||||
|                 auto phys_addr = type==iss::address_type::PHYSICAL?phys_addr_t{access, space, addr}:BASE::v2p(iss::addr_t{access, type, space, addr}); | ||||
|                 auto res = iss::Err; | ||||
|                 if(access != access_type::FETCH && memfn_range.size()){ | ||||
|                 if(!is_fetch(access) && memfn_range.size()){ | ||||
|                     auto it = std::find_if(std::begin(memfn_range), std::end(memfn_range), [phys_addr](std::tuple<uint64_t, uint64_t> const& a){ | ||||
|                         return std::get<0>(a)<=phys_addr.val && (std::get<0>(a)+std::get<1>(a))>phys_addr.val; | ||||
|                     }); | ||||
|   | ||||
| @@ -611,13 +611,19 @@ iss::status riscv_hart_msu_vp<BASE>::read(const address_type type, const access_ | ||||
|     try { | ||||
|         switch (space) { | ||||
|         case traits<BASE>::MEM: { | ||||
|             if (unlikely((access == iss::access_type::FETCH || access == iss::access_type::DEBUG_FETCH) && (addr & 0x1) == 1)) { | ||||
|             auto alignment = is_fetch(access)? (traits<BASE>::MISA_VAL&0x100? 2 : 4) : length; | ||||
|             if (unlikely(is_fetch(access) && (addr&(alignment-1)))) { | ||||
|                 fault_data = addr; | ||||
|                 if (access && iss::access_type::DEBUG) throw trap_access(0, addr); | ||||
|                 this->trap_state = (1 << 31); // issue trap 0 | ||||
|                 return iss::Err; | ||||
|             } | ||||
|             try { | ||||
|                 if(!is_debug(access)  && (addr&(alignment-1))){ | ||||
|                     this->trap_state = 1<<31 | 4<<16; | ||||
|                     fault_data=addr; | ||||
|                     return iss::Err; | ||||
|                 } | ||||
|                 if (unlikely((addr & ~PGMASK) != ((addr + length - 1) & ~PGMASK))) { // we may cross a page boundary | ||||
|                     vm_info vm = hart_state_type::decode_vm_info(this->reg.PRIV, state.satp); | ||||
|                     if (vm.levels != 0) { // VM is active | ||||
|   | ||||
| @@ -705,22 +705,22 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::read(const address_type type, const acc | ||||
|         switch (space) { | ||||
|         case traits<BASE>::MEM: { | ||||
|             if(FEAT & FEAT_PMP){ | ||||
|                 if(!pmp_check(access, addr, length) && (access&access_type::DEBUG) != access_type::DEBUG) { | ||||
|                 if(!pmp_check(access, addr, length) && !is_debug(access)) { | ||||
|                     fault_data = addr; | ||||
|                     if (access && iss::access_type::DEBUG) throw trap_access(0, addr); | ||||
|                     if (is_debug(access)) throw trap_access(0, addr); | ||||
|                     this->trap_state = (1 << 31) | ((access==access_type::FETCH?1:5) << 16); // issue trap 1 | ||||
|                     return iss::Err; | ||||
|                 } | ||||
|             } | ||||
|             if (unlikely((access == iss::access_type::FETCH || access == iss::access_type::DEBUG_FETCH) && (addr & 0x1) == 1)) { | ||||
|             auto alignment = is_fetch(access)? (traits<BASE>::MISA_VAL&0x100? 2 : 4) : length; | ||||
|             if (unlikely(is_fetch(access) && (addr&(alignment-1)))) { | ||||
|                 fault_data = addr; | ||||
|                 if (access && iss::access_type::DEBUG) throw trap_access(0, addr); | ||||
|                 if (is_debug(access)) throw trap_access(0, addr); | ||||
|                 this->trap_state = (1 << 31); // issue trap 0 | ||||
|                 return iss::Err; | ||||
|             } | ||||
|             try { | ||||
|                 auto alignment = access == iss::access_type::FETCH? (traits<BASE>::MISA_VAL&0x100? 2 : 4) : length; | ||||
|                 if(alignment>1 && (addr&(alignment-1))){ | ||||
|                 if(!is_debug(access)  && (addr&(alignment-1))){ | ||||
|                     this->trap_state = 1<<31 | 4<<16; | ||||
|                     fault_data=addr; | ||||
|                     return iss::Err; | ||||
|   | ||||
| @@ -252,13 +252,13 @@ struct tgc_c: public arch_if { | ||||
|         uint8_t PRIV = 0;  | ||||
|         uint32_t DPC = 0; | ||||
|     } reg; | ||||
| #pragma pack(pop) | ||||
|     uint32_t trap_state = 0, pending_trap = 0; | ||||
|     uint64_t icount = 0; | ||||
|     uint64_t cycle = 0; | ||||
|     uint64_t instret = 0; | ||||
|     uint32_t instruction = 0; | ||||
|     uint32_t last_branch = 0; | ||||
| #pragma pack(pop) | ||||
|     std::array<address_type, 4> addr_mode; | ||||
|      | ||||
|     uint64_t interrupt_sim=0; | ||||
|   | ||||
| @@ -81,7 +81,7 @@ public: | ||||
|     void callback(instr_info_t instr_info, exec_info const&) override; | ||||
|  | ||||
| private: | ||||
|     iss::instrumentation_if *arch_instr; | ||||
|     iss::instrumentation_if *instr_if; | ||||
|     std::vector<instr_desc> delays; | ||||
|     struct pair_hash { | ||||
|         size_t operator()(const std::pair<uint64_t, uint64_t> &p) const { | ||||
|   | ||||
| @@ -44,7 +44,7 @@ | ||||
|  | ||||
| namespace iss { | ||||
| namespace plugin { | ||||
|  | ||||
| class lz4compress_steambuf; | ||||
| class cov : public iss::vm_plugin { | ||||
|     struct instr_delay { | ||||
|         std::string instr_name; | ||||
| @@ -88,10 +88,13 @@ public: | ||||
| private: | ||||
|     iss::instrumentation_if *instr_if  {nullptr}; | ||||
|     std::ofstream output; | ||||
| #ifdef WITH_LZ4 | ||||
|     std::unique_ptr<lz4compress_steambuf> strbuf; | ||||
|     std::ostream ostr; | ||||
| #endif | ||||
|     std::string filename; | ||||
|     std::vector<instr_desc> delays; | ||||
|     bool jumped, first; | ||||
|  | ||||
|     bool jumped{false}, first{true}; | ||||
| }; | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -60,13 +60,13 @@ int main(int argc, char *argv[]) { | ||||
|     desc.add_options() | ||||
|         ("help,h", "Print help message") | ||||
|         ("verbose,v", po::value<int>()->implicit_value(0), "Sets logging verbosity") | ||||
|         ("logfile,f", po::value<std::string>(), "Sets default log file.") | ||||
|         ("logfile,l", po::value<std::string>(), "Sets default log file.") | ||||
|         ("disass,d", po::value<std::string>()->implicit_value(""), "Enables disassembly") | ||||
|         ("gdb-port,g", po::value<unsigned>()->default_value(0), "enable gdb server and specify port to use") | ||||
|         ("instructions,i", po::value<uint64_t>()->default_value(std::numeric_limits<uint64_t>::max()), "max. number of instructions to simulate") | ||||
|         ("reset,r", po::value<std::string>(), "reset address") | ||||
|         ("dump-ir", "dump the intermediate representation") | ||||
|         ("elf", po::value<std::vector<std::string>>(), "ELF file(s) to load") | ||||
|         ("elf,f", po::value<std::vector<std::string>>(), "ELF file(s) to load") | ||||
|         ("mem,m", po::value<std::string>(), "the memory input file") | ||||
|         ("plugin,p", po::value<std::vector<std::string>>(), "plugin to activate") | ||||
|         ("backend", po::value<std::string>()->default_value("interp"), "the memory input file") | ||||
|   | ||||
| @@ -48,7 +48,7 @@ using namespace rapidjson; | ||||
| using namespace std; | ||||
|  | ||||
| iss::plugin::cycle_estimate::cycle_estimate(string const& config_file_name) | ||||
| : arch_instr(nullptr) | ||||
| : instr_if(nullptr) | ||||
| , config_file_name(config_file_name) | ||||
| { | ||||
| } | ||||
| @@ -57,9 +57,9 @@ iss::plugin::cycle_estimate::~cycle_estimate() { | ||||
| } | ||||
|  | ||||
| bool iss::plugin::cycle_estimate::registration(const char* const version, vm_if& vm) { | ||||
|     arch_instr = vm.get_arch()->get_instrumentation_if(); | ||||
|     if(!arch_instr) return false; | ||||
|     const string  core_name = arch_instr->core_type_name(); | ||||
|     instr_if = vm.get_arch()->get_instrumentation_if(); | ||||
|     if(!instr_if) return false; | ||||
|     const string  core_name = instr_if->core_type_name(); | ||||
|     if (config_file_name.length() > 0) { | ||||
|         ifstream is(config_file_name); | ||||
|         if (is.is_open()) { | ||||
| @@ -108,11 +108,11 @@ bool iss::plugin::cycle_estimate::registration(const char* const version, vm_if& | ||||
| } | ||||
|  | ||||
| void iss::plugin::cycle_estimate::callback(instr_info_t instr_info, exec_info const& exc_info) { | ||||
|     assert(arch_instr && "No instrumentation interface available but callback executed"); | ||||
|     assert(instr_if && "No instrumentation interface available but callback executed"); | ||||
|     auto entry = delays[instr_info.instr_id]; | ||||
|     bool taken = exc_info.branch_taken; | ||||
|     if (exc_info.branch_taken && (entry.taken > 1)) | ||||
|         arch_instr->set_curr_instr_cycles(entry.taken); | ||||
|         instr_if->set_curr_instr_cycles(entry.taken); | ||||
|     else if (entry.not_taken > 1) | ||||
|         arch_instr->set_curr_instr_cycles(entry.not_taken); | ||||
|         instr_if->set_curr_instr_cycles(entry.not_taken); | ||||
| } | ||||
|   | ||||
| @@ -9,27 +9,99 @@ | ||||
| #include <rapidjson/ostreamwrapper.h> | ||||
| #include <rapidjson/error/en.h> | ||||
| #include <fstream> | ||||
|  | ||||
| #include <iostream> | ||||
| #ifdef WITH_LZ4 | ||||
| #include <lz4frame.h> | ||||
| #endif | ||||
|  | ||||
| namespace iss { | ||||
| namespace plugin { | ||||
|  | ||||
| using namespace rapidjson; | ||||
| using namespace std; | ||||
|  | ||||
| iss::plugin::cov::cov(std::string const &filename) | ||||
|     : instr_if(nullptr) | ||||
|     , filename(filename) | ||||
| { | ||||
|     output.open("output.trc"); | ||||
|     jumped = false; | ||||
|     first = true; | ||||
| } | ||||
| #ifdef WITH_LZ4 | ||||
| class lz4compress_steambuf: public std::streambuf { | ||||
| public: | ||||
|     lz4compress_steambuf(const lz4compress_steambuf&) = delete; | ||||
|     lz4compress_steambuf& operator=(const lz4compress_steambuf&) = delete; | ||||
|     lz4compress_steambuf(std::ostream &sink, size_t buf_size) | ||||
|     : sink(sink) | ||||
|     , src_buf(buf_size) | ||||
|     , dest_buf(LZ4F_compressBound(buf_size, nullptr)) | ||||
|     { | ||||
|         auto errCode = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); | ||||
|         if (LZ4F_isError(errCode) != 0) | ||||
|             throw std::runtime_error(std::string("Failed to create LZ4 context: ") + LZ4F_getErrorName(errCode)); | ||||
|         size_t ret = LZ4F_compressBegin(ctx, &dest_buf.front(), dest_buf.capacity(), nullptr); | ||||
|         if (LZ4F_isError(ret) != 0) | ||||
|             throw std::runtime_error(std::string("Failed to start LZ4 compression: ") + LZ4F_getErrorName(ret)); | ||||
|         setp(src_buf.data(), src_buf.data() + src_buf.size() - 1); | ||||
|         sink.write(dest_buf.data(), ret); | ||||
|     } | ||||
|  | ||||
| iss::plugin::cov::~cov() { | ||||
|     output.close(); | ||||
| } | ||||
|     ~lz4compress_steambuf() { | ||||
|         close(); | ||||
|     } | ||||
|  | ||||
| bool iss::plugin::cov::registration(const char *const version, vm_if& vm) { | ||||
|     void close() { | ||||
|         if (closed) | ||||
|             return; | ||||
|         sync(); | ||||
|         auto ret = LZ4F_compressEnd(ctx, dest_buf.data(), dest_buf.capacity(), nullptr); | ||||
|         if (LZ4F_isError(ret) != 0) | ||||
|             throw std::runtime_error(std::string("Failed to finish LZ4 compression: ") + LZ4F_getErrorName(ret)); | ||||
|         sink.write(dest_buf.data(), ret); | ||||
|         LZ4F_freeCompressionContext(ctx); | ||||
|         closed = true; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     int_type overflow(int_type ch) override { | ||||
|         compress_and_write(); | ||||
|         *pptr() = static_cast<char_type>(ch); | ||||
|         pbump(1); | ||||
|         return ch; | ||||
|     } | ||||
|  | ||||
|     int_type sync() override { | ||||
|         compress_and_write(); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     void compress_and_write() { | ||||
|         if (closed) | ||||
|             throw std::runtime_error("Cannot write to closed stream"); | ||||
|         if(auto orig_size = pptr() - pbase()){ | ||||
|             auto ret = LZ4F_compressUpdate(ctx, dest_buf.data(), dest_buf.capacity(), pbase(), orig_size, nullptr); | ||||
|             if (LZ4F_isError(ret) != 0) | ||||
|                 throw std::runtime_error(std::string("LZ4 compression failed: ") + LZ4F_getErrorName(ret)); | ||||
|             if(ret) sink.write(dest_buf.data(), ret); | ||||
|             pbump(-orig_size); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     std::ostream &sink; | ||||
|     std::vector<char> src_buf; | ||||
|     std::vector<char> dest_buf; | ||||
|     LZ4F_compressionContext_t ctx{ nullptr }; | ||||
|     bool closed{ false }; | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| cov::cov(std::string const &filename) | ||||
| : instr_if(nullptr) | ||||
| , filename(filename) | ||||
| , output("output.trc") | ||||
| #ifdef WITH_LZ4 | ||||
| , strbuf(new lz4compress_steambuf(output, 4096)) | ||||
| , ostr(strbuf.get()) | ||||
| #endif | ||||
| { } | ||||
|  | ||||
| cov::~cov() { } | ||||
|  | ||||
| bool cov::registration(const char *const version, vm_if& vm) { | ||||
|     instr_if = vm.get_arch()->get_instrumentation_if(); | ||||
|     if(!instr_if) return false; | ||||
|     const string  core_name = instr_if->core_type_name(); | ||||
| @@ -63,11 +135,11 @@ bool iss::plugin::cov::registration(const char *const version, vm_if& vm) { | ||||
|                     } else { | ||||
|                         LOG(ERR)<<"plugin cycle_estimate: could not find an entry for "<<core_name<<" in JSON file"<<endl; | ||||
|                         return false; | ||||
|                    } | ||||
|                     } | ||||
|                 } else { | ||||
|                     LOG(ERR)<<"plugin cycle_estimate: could not parse in JSON file at "<< ok.Offset()<<": "<<GetParseError_En(ok.Code())<<endl; | ||||
|                     return false; | ||||
|                } | ||||
|                 } | ||||
|             } catch (runtime_error &e) { | ||||
|                 LOG(ERR) << "Could not parse input file " << filename << ", reason: " << e.what(); | ||||
|                 return false; | ||||
| @@ -78,60 +150,31 @@ bool iss::plugin::cov::registration(const char *const version, vm_if& vm) { | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| inline string formatPC(uint64_t pc) { | ||||
|     stringstream stream; | ||||
|     stream << "0x" << std::hex << pc; | ||||
|     return stream.str(); | ||||
| } | ||||
|  | ||||
| void iss::plugin::cov::callback(instr_info_t iinfo, const exec_info& einfo) { | ||||
| //    auto delay = 0; | ||||
| //    auto entry = delays[iinfo.instr_id]; | ||||
| //    bool taken = einfo.branch_taken; | ||||
| //    if (einfo.branch_taken) | ||||
| //        delay = entry.taken; | ||||
| //    else | ||||
| //        delay = entry.not_taken; | ||||
| // | ||||
| //    if (first){ | ||||
| //        output << formatPC(instr_if->get_pc()) << "," << delay; | ||||
| //        first = false; | ||||
| //    } | ||||
| //    if(instr_if->get_next_pc()-instr_if->get_pc() != delays[iinfo.instr_id].size/8){ | ||||
| //        //The goal is to keep the output in start-target pairs, so after a jump the target address needs to get written | ||||
| //        //to the output. If the target happens to also be a start, we keep the pairing by adding a 0-delay entry. | ||||
| //        if (jumped) | ||||
| //            output <<"\n" <<formatPC(instr_if->get_pc()) << "," << 0; | ||||
| //        output <<"\n" << formatPC(instr_if->get_pc()) << "," << delay; | ||||
| //        jumped = true; | ||||
| //    } | ||||
| //    else{ | ||||
| //        if (jumped){ | ||||
| //            output <<"\n" << formatPC(instr_if->get_pc()) << "," << delay; | ||||
| //            jumped = false; | ||||
| //        } | ||||
| //        else if(delay!=1){ | ||||
| //            output <<"\n" << formatPC(instr_if->get_pc()) << "," << delay; | ||||
| //            output <<"\n" << formatPC(instr_if->get_pc()) << "," << 0; | ||||
| //        } | ||||
| // | ||||
| //    } | ||||
|  | ||||
| //source code for the full output | ||||
| void cov::callback(instr_info_t iinfo, const exec_info& einfo) { | ||||
|     auto delay = 0; | ||||
|     size_t id = iinfo.instr_id; | ||||
|     auto entry = delays[id]; | ||||
|     auto instr = instr_if->get_instr_word(); | ||||
|     auto call = (id==2 || id==3) && bit_sub<7,5>(instr)!=0; | ||||
|     auto call = id==65 || id ==86 || ((id==2 || id==3) && bit_sub<7,5>(instr)!=0) ;//not taking care of tail calls (jalr with loading x6) | ||||
|     bool taken = einfo.branch_taken; | ||||
|     if (einfo.branch_taken) | ||||
|     bool compressed = (instr&0x3)!=0x3; | ||||
|     if (einfo.branch_taken) { | ||||
|         delay = entry.taken; | ||||
|     else | ||||
|        delay = entry.not_taken; | ||||
|     output<<std::hex <<"0x" << instr_if->get_pc() <<"," << delay << "\n"; | ||||
|         if(entry.taken > 1) | ||||
|             instr_if->set_curr_instr_cycles(entry.taken); | ||||
|     } else { | ||||
|         delay = entry.not_taken; | ||||
|         if (entry.not_taken > 1) | ||||
|             instr_if->set_curr_instr_cycles(entry.not_taken); | ||||
|     } | ||||
| #ifndef WITH_LZ4 | ||||
|     output<<std::hex <<"0x" << instr_if->get_pc() <<"," << delay <<"," << call<<","<<(compressed?2:4) <<"\n"; | ||||
| #else | ||||
|     auto rdbuf=ostr.rdbuf(); | ||||
|     ostr<<std::hex <<"0x" << instr_if->get_pc() <<"," << delay <<"," << call<<","<<(compressed?2:4) <<"\n"; | ||||
| #endif | ||||
| } | ||||
| } | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user