Commit 76b70dcb8f for qemu.org
commit 76b70dcb8f425363cf4c767cd84761e219349bdd
Author: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Date: Mon Jun 15 12:35:25 2026 -0700
plugins/cpp: register callbacks using captureless lambda
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>
diff --git a/contrib/plugins/cpp.cpp b/contrib/plugins/cpp.cpp
index a0bb261fbe..006fa6de24 100644
--- a/contrib/plugins/cpp.cpp
+++ b/contrib/plugins/cpp.cpp
@@ -363,14 +363,98 @@
QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
-static void vcpu_tb_trans(struct qemu_plugin_tb *tb, void *userdata)
+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)
{
- qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans, NULL);
+ 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;
}