mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:46:10 +00:00
We can now demonstrate what previous changes allow us to do. Since all callbacks have a userdata pointer, we can use that mechanism to move an object through all of them. In other words, we can now have stateful plugins without resorting to any global variable. As an example, we implement tb counting plugin with our cpp plugin. It produces an output similar to hotblocks, with same performance. Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org> Link: https://lore.kernel.org/qemu-devel/20260615193526.2883349-27-pierrick.bouvier@oss.qualcomm.com Signed-off-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
461 lines
9.3 KiB
C++
461 lines
9.3 KiB
C++
/*
|
|
* 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;
|
|
}
|