| /* | |
| * SPDX-License-Identifier: GPL-2.0-or-later | |
| * | |
| * This C++ plugin ensures we don't have regression when compiling C++. | |
| */ | |
| #include <qemu-plugin.h> | |
| /* | |
| * We include all C++ standard headers (without deprecated ones), | |
| * taken from: https://en.cppreference.com/w/cpp/headers.html | |
| * | |
| * To update, copy page text, and then: | |
| * grep '^<' | | |
| * sort -u | | |
| * grep -v strstream | | |
| * grep -v codecvt | | |
| * sed -e 's/\(.*\)/#if __has_include(\1)\n#include \1\n#endif/' | |
| */ | |
| #if __has_include(<algorithm>) | |
| #include <algorithm> | |
| #endif | |
| #if __has_include(<any>) | |
| #include <any> | |
| #endif | |
| #if __has_include(<array>) | |
| #include <array> | |
| #endif | |
| #if __has_include(<atomic>) | |
| #include <atomic> | |
| #endif | |
| #if __has_include(<barrier>) | |
| #include <barrier> | |
| #endif | |
| #if __has_include(<bit>) | |
| #include <bit> | |
| #endif | |
| #if __has_include(<bitset>) | |
| #include <bitset> | |
| #endif | |
| #if __has_include(<cassert>) | |
| #include <cassert> | |
| #endif | |
| #if __has_include(<cctype>) | |
| #include <cctype> | |
| #endif | |
| #if __has_include(<cerrno>) | |
| #include <cerrno> | |
| #endif | |
| #if __has_include(<cfenv>) | |
| #include <cfenv> | |
| #endif | |
| #if __has_include(<cfloat>) | |
| #include <cfloat> | |
| #endif | |
| #if __has_include(<charconv>) | |
| #include <charconv> | |
| #endif | |
| #if __has_include(<chrono>) | |
| #include <chrono> | |
| #endif | |
| #if __has_include(<cinttypes>) | |
| #include <cinttypes> | |
| #endif | |
| #if __has_include(<climits>) | |
| #include <climits> | |
| #endif | |
| #if __has_include(<clocale>) | |
| #include <clocale> | |
| #endif | |
| #if __has_include(<cmath>) | |
| #include <cmath> | |
| #endif | |
| #if __has_include(<compare>) | |
| #include <compare> | |
| #endif | |
| #if __has_include(<complex>) | |
| #include <complex> | |
| #endif | |
| #if __has_include(<concepts>) | |
| #include <concepts> | |
| #endif | |
| #if __has_include(<condition_variable>) | |
| #include <condition_variable> | |
| #endif | |
| #if __has_include(<contracts>) | |
| #include <contracts> | |
| #endif | |
| #if __has_include(<coroutine>) | |
| #include <coroutine> | |
| #endif | |
| #if __has_include(<csetjmp>) | |
| #include <csetjmp> | |
| #endif | |
| #if __has_include(<csignal>) | |
| #include <csignal> | |
| #endif | |
| #if __has_include(<cstdarg>) | |
| #include <cstdarg> | |
| #endif | |
| #if __has_include(<cstddef>) | |
| #include <cstddef> | |
| #endif | |
| #if __has_include(<cstdint>) | |
| #include <cstdint> | |
| #endif | |
| #if __has_include(<cstdio>) | |
| #include <cstdio> | |
| #endif | |
| #if __has_include(<cstdlib>) | |
| #include <cstdlib> | |
| #endif | |
| #if __has_include(<cstring>) | |
| #include <cstring> | |
| #endif | |
| #if __has_include(<ctime>) | |
| #include <ctime> | |
| #endif | |
| #if __has_include(<cuchar>) | |
| #include <cuchar> | |
| #endif | |
| #if __has_include(<cwchar>) | |
| #include <cwchar> | |
| #endif | |
| #if __has_include(<cwctype>) | |
| #include <cwctype> | |
| #endif | |
| #if __has_include(<debugging>) | |
| #include <debugging> | |
| #endif | |
| #if __has_include(<deque>) | |
| #include <deque> | |
| #endif | |
| #if __has_include(<exception>) | |
| #include <exception> | |
| #endif | |
| #if __has_include(<execution>) | |
| #include <execution> | |
| #endif | |
| #if __has_include(<expected>) | |
| #include <expected> | |
| #endif | |
| #if __has_include(<filesystem>) | |
| #include <filesystem> | |
| #endif | |
| #if __has_include(<flat_map>) | |
| #include <flat_map> | |
| #endif | |
| #if __has_include(<flat_set>) | |
| #include <flat_set> | |
| #endif | |
| #if __has_include(<format>) | |
| #include <format> | |
| #endif | |
| #if __has_include(<forward_list>) | |
| #include <forward_list> | |
| #endif | |
| #if __has_include(<fstream>) | |
| #include <fstream> | |
| #endif | |
| #if __has_include(<functional>) | |
| #include <functional> | |
| #endif | |
| #if __has_include(<future>) | |
| #include <future> | |
| #endif | |
| #if __has_include(<generator>) | |
| #include <generator> | |
| #endif | |
| #if __has_include(<hazard_pointer>) | |
| #include <hazard_pointer> | |
| #endif | |
| #if __has_include(<hive>) | |
| #include <hive> | |
| #endif | |
| #if __has_include(<initializer_list>) | |
| #include <initializer_list> | |
| #endif | |
| #if __has_include(<inplace_vector>) | |
| #include <inplace_vector> | |
| #endif | |
| #if __has_include(<iomanip>) | |
| #include <iomanip> | |
| #endif | |
| #if __has_include(<ios>) | |
| #include <ios> | |
| #endif | |
| #if __has_include(<iosfwd>) | |
| #include <iosfwd> | |
| #endif | |
| #if __has_include(<iostream>) | |
| #include <iostream> | |
| #endif | |
| #if __has_include(<istream>) | |
| #include <istream> | |
| #endif | |
| #if __has_include(<iterator>) | |
| #include <iterator> | |
| #endif | |
| #if __has_include(<latch>) | |
| #include <latch> | |
| #endif | |
| #if __has_include(<limits>) | |
| #include <limits> | |
| #endif | |
| #if __has_include(<linalg>) | |
| #include <linalg> | |
| #endif | |
| #if __has_include(<list>) | |
| #include <list> | |
| #endif | |
| #if __has_include(<locale>) | |
| #include <locale> | |
| #endif | |
| #if __has_include(<map>) | |
| #include <map> | |
| #endif | |
| #if __has_include(<mdspan>) | |
| #include <mdspan> | |
| #endif | |
| #if __has_include(<memory>) | |
| #include <memory> | |
| #endif | |
| #if __has_include(<memory_resource>) | |
| #include <memory_resource> | |
| #endif | |
| #if __has_include(<mutex>) | |
| #include <mutex> | |
| #endif | |
| #if __has_include(<new>) | |
| #include <new> | |
| #endif | |
| #if __has_include(<numbers>) | |
| #include <numbers> | |
| #endif | |
| #if __has_include(<numeric>) | |
| #include <numeric> | |
| #endif | |
| #if __has_include(<optional>) | |
| #include <optional> | |
| #endif | |
| #if __has_include(<ostream>) | |
| #include <ostream> | |
| #endif | |
| #if __has_include(<print>) | |
| #include <print> | |
| #endif | |
| #if __has_include(<queue>) | |
| #include <queue> | |
| #endif | |
| #if __has_include(<random>) | |
| #include <random> | |
| #endif | |
| #if __has_include(<ranges>) | |
| #include <ranges> | |
| #endif | |
| #if __has_include(<ratio>) | |
| #include <ratio> | |
| #endif | |
| #if __has_include(<rcu>) | |
| #include <rcu> | |
| #endif | |
| #if __has_include(<regex>) | |
| #include <regex> | |
| #endif | |
| #if __has_include(<scoped_allocator>) | |
| #include <scoped_allocator> | |
| #endif | |
| #if __has_include(<semaphore>) | |
| #include <semaphore> | |
| #endif | |
| #if __has_include(<set>) | |
| #include <set> | |
| #endif | |
| #if __has_include(<shared_mutex>) | |
| #include <shared_mutex> | |
| #endif | |
| #if __has_include(<simd>) | |
| #include <simd> | |
| #endif | |
| #if __has_include(<source_location>) | |
| #include <source_location> | |
| #endif | |
| #if __has_include(<span>) | |
| #include <span> | |
| #endif | |
| #if __has_include(<spanstream>) | |
| #include <spanstream> | |
| #endif | |
| #if __has_include(<sstream>) | |
| #include <sstream> | |
| #endif | |
| #if __has_include(<stack>) | |
| #include <stack> | |
| #endif | |
| #if __has_include(<stacktrace>) | |
| #include <stacktrace> | |
| #endif | |
| #if __has_include(<stdexcept>) | |
| #include <stdexcept> | |
| #endif | |
| #if __has_include(<stdfloat>) | |
| #include <stdfloat> | |
| #endif | |
| #if __has_include(<stop_token>) | |
| #include <stop_token> | |
| #endif | |
| #if __has_include(<streambuf>) | |
| #include <streambuf> | |
| #endif | |
| #if __has_include(<string>) | |
| #include <string> | |
| #endif | |
| #if __has_include(<string_view>) | |
| #include <string_view> | |
| #endif | |
| #if __has_include(<syncstream>) | |
| #include <syncstream> | |
| #endif | |
| #if __has_include(<system_error>) | |
| #include <system_error> | |
| #endif | |
| #if __has_include(<text_encoding>) | |
| #include <text_encoding> | |
| #endif | |
| #if __has_include(<thread>) | |
| #include <thread> | |
| #endif | |
| #if __has_include(<tuple>) | |
| #include <tuple> | |
| #endif | |
| #if __has_include(<typeindex>) | |
| #include <typeindex> | |
| #endif | |
| #if __has_include(<typeinfo>) | |
| #include <typeinfo> | |
| #endif | |
| #if __has_include(<type_traits>) | |
| #include <type_traits> | |
| #endif | |
| #if __has_include(<unordered_map>) | |
| #include <unordered_map> | |
| #endif | |
| #if __has_include(<unordered_set>) | |
| #include <unordered_set> | |
| #endif | |
| #if __has_include(<utility>) | |
| #include <utility> | |
| #endif | |
| #if __has_include(<valarray>) | |
| #include <valarray> | |
| #endif | |
| #if __has_include(<variant>) | |
| #include <variant> | |
| #endif | |
| #if __has_include(<vector>) | |
| #include <vector> | |
| #endif | |
| #if __has_include(<version>) | |
| #include <version> | |
| #endif | |
| QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; | |
| class Plugin | |
| { | |
| public: | |
| void tb_exec(std::atomic<uint64_t> &count) | |
| { | |
| count++; | |
| } | |
| void tb_trans(struct qemu_plugin_tb *tb) | |
| { | |
| auto vaddr = qemu_plugin_tb_vaddr(tb); | |
| TbData *data = [&]() { | |
| std::lock_guard l(tb_trans_lock); | |
| auto [it, is_new] = tb_data.try_emplace(vaddr); | |
| TbData &in = it->second; | |
| if (is_new) { | |
| in.first = 0; | |
| in.second = this; | |
| } | |
| return ∈ | |
| } (); | |
| qemu_plugin_register_vcpu_tb_exec_cb( | |
| tb, | |
| [](unsigned int cpu_index, void *udata) { | |
| auto &[counter, p] = *static_cast<TbData*>(udata); | |
| p->tb_exec(counter); | |
| }, | |
| QEMU_PLUGIN_CB_NO_REGS, | |
| data); | |
| } | |
| void at_exit() | |
| { | |
| std::vector<std::pair<Vaddr, uint64_t>> v; | |
| for (auto &[vaddr, data] : tb_data) { | |
| uint64_t count = data.first; | |
| v.push_back({vaddr, count}); | |
| } | |
| std::sort(v.begin(), v.end(), | |
| [](const auto &a, const auto &b) { | |
| return a.second > b.second; | |
| }); | |
| std::stringstream ss; | |
| ss << "Top 10 executed TB are:\n"; | |
| size_t idx = 0; | |
| for (auto &tb : v) { | |
| if (idx >= 10) { | |
| break; | |
| } | |
| ss << std::hex << "0x" << tb.first << std::dec << ": " | |
| << tb.second << '\n'; | |
| ++idx; | |
| } | |
| qemu_plugin_outs(ss.str().c_str()); | |
| } | |
| private: | |
| using Vaddr = uint64_t; | |
| using Counter = std::atomic<uint64_t>; | |
| using TbData = std::pair<Counter, Plugin*>; | |
| std::unordered_map<Vaddr, TbData> tb_data; | |
| std::mutex tb_trans_lock; | |
| }; | |
| QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, | |
| const qemu_info_t *info, | |
| int argc, char **argv) | |
| { | |
| Plugin *plugin = new Plugin; | |
| /* | |
| * We can register a cb with any function, which may be a free | |
| * function, a static class function, or a captureless lambda. | |
| */ | |
| qemu_plugin_register_vcpu_tb_trans_cb( | |
| id, | |
| [](struct qemu_plugin_tb *tb, void *userdata) { | |
| Plugin *p = static_cast<Plugin*>(userdata); | |
| p->tb_trans(tb); | |
| }, | |
| plugin); | |
| qemu_plugin_register_atexit_cb( | |
| id, | |
| [](void *userdata) { | |
| Plugin *p = static_cast<Plugin*>(userdata); | |
| p->at_exit(); | |
| delete p; | |
| }, | |
| plugin); | |
| return 0; | |
| } |