diff --git a/cpu/ppc/ppcexec.cpp b/cpu/ppc/ppcexec.cpp index 6f53fb6..8834f2a 100644 --- a/cpu/ppc/ppcexec.cpp +++ b/cpu/ppc/ppcexec.cpp @@ -23,6 +23,7 @@ along with this program. If not, see . #include #include "ppcemu.h" #include "ppcmmu.h" +#include "ppcdisasm.h" #include #include @@ -109,6 +110,9 @@ uint64_t num_supervisor_instrs; uint64_t num_int_loads; uint64_t num_int_stores; uint64_t exceptions_processed; +#ifdef CPU_PROFILING_OPS +std::unordered_map num_opcodes; +#endif #include "utils/profiler.h" #include @@ -139,6 +143,30 @@ public: vars.push_back({.name = "Exceptions processed", .format = ProfileVarFmt::DEC, .value = exceptions_processed}); + + // Generate top N op counts with readable names. +#ifdef CPU_PROFILING_OPS + PPCDisasmContext ctx; + ctx.instr_addr = 0; + ctx.simplified = false; + std::vector> op_name_counts; + for (const auto& pair : num_opcodes) { + ctx.instr_code = pair.first; + auto op_name = disassemble_single(&ctx); + op_name_counts.emplace_back(op_name, pair.second); + } + size_t top_ops_size = std::min(op_name_counts.size(), size_t(20)); + std::partial_sort(op_name_counts.begin(), op_name_counts.begin() + top_ops_size, op_name_counts.end(), [](const auto& a, const auto& b) { + return b.second < a.second; + }); + op_name_counts.resize(top_ops_size); + for (const auto& pair : op_name_counts) { + vars.push_back({.name = "Instruction " + pair.first, + .format = ProfileVarFmt::COUNT, + .value = pair.second, + .count_total = num_executed_instrs}); + } +#endif }; void reset() { @@ -147,6 +175,9 @@ public: num_int_loads = 0; num_int_stores = 0; exceptions_processed = 0; +#ifdef CPU_PROFILING_OPS + num_opcodes.clear(); +#endif }; }; @@ -289,6 +320,9 @@ void ppc_main_opcode() { #ifdef CPU_PROFILING num_executed_instrs++; +#if defined(CPU_PROFILING_OPS) + num_opcodes[ppc_cur_instruction]++; +#endif #endif OpcodeGrabber[(ppc_cur_instruction >> 26) & 0x3F](); } diff --git a/main.cpp b/main.cpp index 93ba4a2..531ba59 100644 --- a/main.cpp +++ b/main.cpp @@ -62,7 +62,7 @@ static string appDescription = string( "\n" ); -void run_machine(std::string machine_str, std::string bootrom_path, uint32_t execution_mode); +void run_machine(std::string machine_str, std::string bootrom_path, uint32_t execution_mode, uint32_t profiling_interval_ms); int main(int argc, char** argv) { @@ -85,6 +85,12 @@ int main(int argc, char** argv) { app.add_option("-b,--bootrom", bootrom_path, "Specifies BootROM path") ->check(CLI::ExistingFile); + uint32_t profiling_interval_ms = 0; +#ifdef CPU_PROFILING + app.add_option("--profiling-interval-ms", profiling_interval_ms, + "Specifies periodic interval (in ms) at which to output CPU profiling information"); +#endif + CLI::Option* machine_opt = app.add_option("-m,--machine", machine_str, "Specify machine ID"); @@ -188,7 +194,7 @@ int main(int argc, char** argv) { signal(SIGABRT, sigabrt_handler); while (true) { - run_machine(machine_str, bootrom_path, execution_mode); + run_machine(machine_str, bootrom_path, execution_mode, profiling_interval_ms); if (power_off_reason == po_restarting) { LOG_F(INFO, "Restarting..."); power_on = true; @@ -202,7 +208,7 @@ int main(int argc, char** argv) { return 0; } -void run_machine(std::string machine_str, std::string bootrom_path, uint32_t execution_mode) { +void run_machine(std::string machine_str, std::string bootrom_path, uint32_t execution_mode, uint32_t profiling_interval_ms) { if (MachineFactory::create_machine_for_id(machine_str, bootrom_path) < 0) { return; } @@ -213,6 +219,15 @@ void run_machine(std::string machine_str, std::string bootrom_path, uint32_t exe EventManager::get_instance()->poll_events(); }); +#ifdef CPU_PROFILING + uint32_t profiling_timer; + if (profiling_interval_ms > 0) { + profiling_timer = TimerManager::get_instance()->add_cyclic_timer(MSECS_TO_NSECS(profiling_interval_ms), [] { + gProfilerObj->print_profile("PPC_CPU"); + }); + } +#endif + switch (execution_mode) { case interpreter: power_off_reason = po_starting_up; @@ -229,6 +244,11 @@ void run_machine(std::string machine_str, std::string bootrom_path, uint32_t exe LOG_F(INFO, "Cleaning up..."); TimerManager::get_instance()->cancel_timer(event_timer); +#ifdef CPU_PROFILING + if (profiling_interval_ms > 0) { + TimerManager::get_instance()->cancel_timer(profiling_timer); + } +#endif EventManager::get_instance()->disconnect_handlers(); delete gMachineObj.release(); } diff --git a/utils/profiler.cpp b/utils/profiler.cpp index 65dac50..d9e07dd 100644 --- a/utils/profiler.cpp +++ b/utils/profiler.cpp @@ -20,6 +20,7 @@ along with this program. If not, see . */ #include "profiler.h" +#include #include #include @@ -50,6 +51,11 @@ void Profiler::print_profile(std::string name) return; } + // Set a locale so we get thousands separators when outputting numbers. + // Would be nice if we could use the default locale, but that does not + // appear to work. + std::cout.imbue(std::locale("en_US.UTF-8")); + std::cout << std::endl; std::cout << "Summary of the profile '" << name << "':" << std::endl; std::cout << "------------------------------------------" << std::endl; @@ -61,13 +67,24 @@ void Profiler::print_profile(std::string name) // ask the corresponding profile class to fill its variables for us prof_it->second->populate_variables(vars); + int name_width = 10; for (auto& var : vars) { + name_width = std::max(name_width, int(var.name.length())); + } + + for (auto& var : vars) { + std::cout << std::left << std::setw(name_width) << var.name << " : "; switch(var.format) { case ProfileVarFmt::DEC: - std::cout << var.name << " : " << var.value << std::endl; + std::cout << var.value << std::endl; break; case ProfileVarFmt::HEX: - std::cout << var.name << " : " << std::hex << var.value << std::endl; + std::cout << std::hex << var.value << std::endl; + break; + case ProfileVarFmt::COUNT: + std::cout << var.value << std::fixed << std::setprecision(2) << " (" + << (double(var.value) / double(var.count_total) * 100) + << "%)" << std::endl; break; default: std::cout << "Unknown value in variable " << var.name << std::endl; diff --git a/utils/profiler.h b/utils/profiler.h index 03d8204..fad3b6c 100644 --- a/utils/profiler.h +++ b/utils/profiler.h @@ -31,13 +31,14 @@ along with this program. If not, see . #include #include -enum class ProfileVarFmt { DEC, HEX }; +enum class ProfileVarFmt { DEC, HEX, COUNT }; /** Define a special data type for profile variables. */ typedef struct ProfileVar { std::string name; ProfileVarFmt format; uint64_t value; + uint64_t count_total; } ProfileVar; /** Base class for user-defined profiles. */