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