xref: /qemu/contrib/plugins/hotblocks.c (revision 2d0d5514)
1c17a386bSAlex Bennée /*
2c17a386bSAlex Bennée  * Copyright (C) 2019, Alex Bennée <alex.bennee@linaro.org>
3c17a386bSAlex Bennée  *
4c17a386bSAlex Bennée  * License: GNU GPL, version 2 or later.
5c17a386bSAlex Bennée  *   See the COPYING file in the top-level directory.
6c17a386bSAlex Bennée  */
7c17a386bSAlex Bennée #include <inttypes.h>
8c17a386bSAlex Bennée #include <assert.h>
9c17a386bSAlex Bennée #include <stdlib.h>
10c17a386bSAlex Bennée #include <inttypes.h>
11c17a386bSAlex Bennée #include <string.h>
12c17a386bSAlex Bennée #include <unistd.h>
13c17a386bSAlex Bennée #include <stdio.h>
14c17a386bSAlex Bennée #include <glib.h>
15c17a386bSAlex Bennée 
16c17a386bSAlex Bennée #include <qemu-plugin.h>
17c17a386bSAlex Bennée 
18c17a386bSAlex Bennée QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
19c17a386bSAlex Bennée 
20c17a386bSAlex Bennée static bool do_inline;
21c17a386bSAlex Bennée 
22c17a386bSAlex Bennée /* Plugins need to take care of their own locking */
23c17a386bSAlex Bennée static GMutex lock;
24c17a386bSAlex Bennée static GHashTable *hotblocks;
25c17a386bSAlex Bennée static guint64 limit = 20;
26c17a386bSAlex Bennée 
27c17a386bSAlex Bennée /*
28c17a386bSAlex Bennée  * Counting Structure
29c17a386bSAlex Bennée  *
30c17a386bSAlex Bennée  * The internals of the TCG are not exposed to plugins so we can only
31c17a386bSAlex Bennée  * get the starting PC for each block. We cheat this slightly by
32c17a386bSAlex Bennée  * xor'ing the number of instructions to the hash to help
33c17a386bSAlex Bennée  * differentiate.
34c17a386bSAlex Bennée  */
35c17a386bSAlex Bennée typedef struct {
36c17a386bSAlex Bennée     uint64_t start_addr;
37*2d0d5514SPierrick Bouvier     struct qemu_plugin_scoreboard *exec_count;
38c17a386bSAlex Bennée     int trans_count;
39c17a386bSAlex Bennée     unsigned long insns;
40c17a386bSAlex Bennée } ExecCount;
41c17a386bSAlex Bennée 
cmp_exec_count(gconstpointer a,gconstpointer b)42c17a386bSAlex Bennée static gint cmp_exec_count(gconstpointer a, gconstpointer b)
43c17a386bSAlex Bennée {
44c17a386bSAlex Bennée     ExecCount *ea = (ExecCount *) a;
45c17a386bSAlex Bennée     ExecCount *eb = (ExecCount *) b;
46*2d0d5514SPierrick Bouvier     uint64_t count_a =
47*2d0d5514SPierrick Bouvier         qemu_plugin_u64_sum(qemu_plugin_scoreboard_u64(ea->exec_count));
48*2d0d5514SPierrick Bouvier     uint64_t count_b =
49*2d0d5514SPierrick Bouvier         qemu_plugin_u64_sum(qemu_plugin_scoreboard_u64(eb->exec_count));
50*2d0d5514SPierrick Bouvier     return count_a > count_b ? -1 : 1;
51*2d0d5514SPierrick Bouvier }
52*2d0d5514SPierrick Bouvier 
exec_count_free(gpointer key,gpointer value,gpointer user_data)53*2d0d5514SPierrick Bouvier static void exec_count_free(gpointer key, gpointer value, gpointer user_data)
54*2d0d5514SPierrick Bouvier {
55*2d0d5514SPierrick Bouvier     ExecCount *cnt = value;
56*2d0d5514SPierrick Bouvier     qemu_plugin_scoreboard_free(cnt->exec_count);
57c17a386bSAlex Bennée }
58c17a386bSAlex Bennée 
plugin_exit(qemu_plugin_id_t id,void * p)59c17a386bSAlex Bennée static void plugin_exit(qemu_plugin_id_t id, void *p)
60c17a386bSAlex Bennée {
61c17a386bSAlex Bennée     g_autoptr(GString) report = g_string_new("collected ");
62c17a386bSAlex Bennée     GList *counts, *it;
63c17a386bSAlex Bennée     int i;
64c17a386bSAlex Bennée 
65c17a386bSAlex Bennée     g_string_append_printf(report, "%d entries in the hash table\n",
66c17a386bSAlex Bennée                            g_hash_table_size(hotblocks));
67c17a386bSAlex Bennée     counts = g_hash_table_get_values(hotblocks);
68c17a386bSAlex Bennée     it = g_list_sort(counts, cmp_exec_count);
69c17a386bSAlex Bennée 
70c17a386bSAlex Bennée     if (it) {
71c17a386bSAlex Bennée         g_string_append_printf(report, "pc, tcount, icount, ecount\n");
72c17a386bSAlex Bennée 
73c17a386bSAlex Bennée         for (i = 0; i < limit && it->next; i++, it = it->next) {
74c17a386bSAlex Bennée             ExecCount *rec = (ExecCount *) it->data;
75*2d0d5514SPierrick Bouvier             g_string_append_printf(
76*2d0d5514SPierrick Bouvier                 report, "0x%016"PRIx64", %d, %ld, %"PRId64"\n",
77c17a386bSAlex Bennée                 rec->start_addr, rec->trans_count,
78*2d0d5514SPierrick Bouvier                 rec->insns,
79*2d0d5514SPierrick Bouvier                 qemu_plugin_u64_sum(
80*2d0d5514SPierrick Bouvier                     qemu_plugin_scoreboard_u64(rec->exec_count)));
81c17a386bSAlex Bennée         }
82c17a386bSAlex Bennée 
83c17a386bSAlex Bennée         g_list_free(it);
84c17a386bSAlex Bennée     }
85c17a386bSAlex Bennée 
86c17a386bSAlex Bennée     qemu_plugin_outs(report->str);
87*2d0d5514SPierrick Bouvier 
88*2d0d5514SPierrick Bouvier     g_hash_table_foreach(hotblocks, exec_count_free, NULL);
89*2d0d5514SPierrick Bouvier     g_hash_table_destroy(hotblocks);
90c17a386bSAlex Bennée }
91c17a386bSAlex Bennée 
plugin_init(void)92c17a386bSAlex Bennée static void plugin_init(void)
93c17a386bSAlex Bennée {
94c17a386bSAlex Bennée     hotblocks = g_hash_table_new(NULL, g_direct_equal);
95c17a386bSAlex Bennée }
96c17a386bSAlex Bennée 
vcpu_tb_exec(unsigned int cpu_index,void * udata)97c17a386bSAlex Bennée static void vcpu_tb_exec(unsigned int cpu_index, void *udata)
98c17a386bSAlex Bennée {
99*2d0d5514SPierrick Bouvier     ExecCount *cnt = (ExecCount *)udata;
100*2d0d5514SPierrick Bouvier     qemu_plugin_u64_add(qemu_plugin_scoreboard_u64(cnt->exec_count),
101*2d0d5514SPierrick Bouvier                         cpu_index, 1);
102c17a386bSAlex Bennée }
103c17a386bSAlex Bennée 
104c17a386bSAlex Bennée /*
105c17a386bSAlex Bennée  * When do_inline we ask the plugin to increment the counter for us.
106c17a386bSAlex Bennée  * Otherwise a helper is inserted which calls the vcpu_tb_exec
107c17a386bSAlex Bennée  * callback.
108c17a386bSAlex Bennée  */
vcpu_tb_trans(qemu_plugin_id_t id,struct qemu_plugin_tb * tb)109c17a386bSAlex Bennée static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
110c17a386bSAlex Bennée {
111c17a386bSAlex Bennée     ExecCount *cnt;
112c17a386bSAlex Bennée     uint64_t pc = qemu_plugin_tb_vaddr(tb);
11327d891bcSYonggang Luo     size_t insns = qemu_plugin_tb_n_insns(tb);
114c17a386bSAlex Bennée     uint64_t hash = pc ^ insns;
115c17a386bSAlex Bennée 
116c17a386bSAlex Bennée     g_mutex_lock(&lock);
117c17a386bSAlex Bennée     cnt = (ExecCount *) g_hash_table_lookup(hotblocks, (gconstpointer) hash);
118c17a386bSAlex Bennée     if (cnt) {
119c17a386bSAlex Bennée         cnt->trans_count++;
120c17a386bSAlex Bennée     } else {
121c17a386bSAlex Bennée         cnt = g_new0(ExecCount, 1);
122c17a386bSAlex Bennée         cnt->start_addr = pc;
123c17a386bSAlex Bennée         cnt->trans_count = 1;
124c17a386bSAlex Bennée         cnt->insns = insns;
125*2d0d5514SPierrick Bouvier         cnt->exec_count = qemu_plugin_scoreboard_new(sizeof(uint64_t));
126c17a386bSAlex Bennée         g_hash_table_insert(hotblocks, (gpointer) hash, (gpointer) cnt);
127c17a386bSAlex Bennée     }
128c17a386bSAlex Bennée 
129c17a386bSAlex Bennée     g_mutex_unlock(&lock);
130c17a386bSAlex Bennée 
131c17a386bSAlex Bennée     if (do_inline) {
132*2d0d5514SPierrick Bouvier         qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
133*2d0d5514SPierrick Bouvier             tb, QEMU_PLUGIN_INLINE_ADD_U64,
134*2d0d5514SPierrick Bouvier             qemu_plugin_scoreboard_u64(cnt->exec_count), 1);
135c17a386bSAlex Bennée     } else {
136c17a386bSAlex Bennée         qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec,
137c17a386bSAlex Bennée                                              QEMU_PLUGIN_CB_NO_REGS,
138*2d0d5514SPierrick Bouvier                                              (void *)cnt);
139c17a386bSAlex Bennée     }
140c17a386bSAlex Bennée }
141c17a386bSAlex Bennée 
142c17a386bSAlex Bennée QEMU_PLUGIN_EXPORT
qemu_plugin_install(qemu_plugin_id_t id,const qemu_info_t * info,int argc,char ** argv)143c17a386bSAlex Bennée int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
144c17a386bSAlex Bennée                         int argc, char **argv)
145c17a386bSAlex Bennée {
1468a3eab66SMahmoud Mandour     for (int i = 0; i < argc; i++) {
1478a3eab66SMahmoud Mandour         char *opt = argv[i];
14840258741SAlex Bennée         g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
1498a3eab66SMahmoud Mandour         if (g_strcmp0(tokens[0], "inline") == 0) {
1508a3eab66SMahmoud Mandour             if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) {
1518a3eab66SMahmoud Mandour                 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
1528a3eab66SMahmoud Mandour                 return -1;
1538a3eab66SMahmoud Mandour             }
1548a3eab66SMahmoud Mandour         } else {
1558a3eab66SMahmoud Mandour             fprintf(stderr, "option parsing failed: %s\n", opt);
1568a3eab66SMahmoud Mandour             return -1;
1578a3eab66SMahmoud Mandour         }
158c17a386bSAlex Bennée     }
159c17a386bSAlex Bennée 
160c17a386bSAlex Bennée     plugin_init();
161c17a386bSAlex Bennée 
162c17a386bSAlex Bennée     qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
163c17a386bSAlex Bennée     qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
164c17a386bSAlex Bennée     return 0;
165c17a386bSAlex Bennée }
166