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