1
2
3
4
5
6
7
8
9
10#include <inttypes.h>
11#include <assert.h>
12#include <stdlib.h>
13#include <inttypes.h>
14#include <string.h>
15#include <unistd.h>
16#include <stdio.h>
17#include <glib.h>
18
19#include <qemu-plugin.h>
20
21QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
22
23#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
24
25static uint64_t page_size = 4096;
26static uint64_t page_mask;
27static int limit = 50;
28static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW;
29static bool track_io;
30
31enum sort_type {
32 SORT_RW = 0,
33 SORT_R,
34 SORT_W,
35 SORT_A
36};
37
38static int sort_by = SORT_RW;
39
40typedef struct {
41 uint64_t page_address;
42 int cpu_read;
43 int cpu_write;
44 uint64_t reads;
45 uint64_t writes;
46} PageCounters;
47
48static GMutex lock;
49static GHashTable *pages;
50
51static gint cmp_access_count(gconstpointer a, gconstpointer b)
52{
53 PageCounters *ea = (PageCounters *) a;
54 PageCounters *eb = (PageCounters *) b;
55 int r;
56 switch (sort_by) {
57 case SORT_RW:
58 r = (ea->reads + ea->writes) > (eb->reads + eb->writes) ? -1 : 1;
59 break;
60 case SORT_R:
61 r = ea->reads > eb->reads ? -1 : 1;
62 break;
63 case SORT_W:
64 r = ea->writes > eb->writes ? -1 : 1;
65 break;
66 case SORT_A:
67 r = ea->page_address > eb->page_address ? -1 : 1;
68 break;
69 default:
70 g_assert_not_reached();
71 }
72 return r;
73}
74
75
76static void plugin_exit(qemu_plugin_id_t id, void *p)
77{
78 g_autoptr(GString) report = g_string_new("Addr, RCPUs, Reads, WCPUs, Writes\n");
79 int i;
80 GList *counts;
81
82 counts = g_hash_table_get_values(pages);
83 if (counts && g_list_next(counts)) {
84 GList *it;
85
86 it = g_list_sort(counts, cmp_access_count);
87
88 for (i = 0; i < limit && it->next; i++, it = it->next) {
89 PageCounters *rec = (PageCounters *) it->data;
90 g_string_append_printf(report,
91 "0x%016"PRIx64", 0x%04x, %"PRId64
92 ", 0x%04x, %"PRId64"\n",
93 rec->page_address,
94 rec->cpu_read, rec->reads,
95 rec->cpu_write, rec->writes);
96 }
97 g_list_free(it);
98 }
99
100 qemu_plugin_outs(report->str);
101}
102
103static void plugin_init(void)
104{
105 page_mask = (page_size - 1);
106 pages = g_hash_table_new(NULL, g_direct_equal);
107}
108
109static void vcpu_haddr(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo,
110 uint64_t vaddr, void *udata)
111{
112 struct qemu_plugin_hwaddr *hwaddr = qemu_plugin_get_hwaddr(meminfo, vaddr);
113 uint64_t page;
114 PageCounters *count;
115
116
117 if (track_io) {
118 if (hwaddr && qemu_plugin_hwaddr_is_io(hwaddr)) {
119 page = vaddr;
120 } else {
121 return;
122 }
123 } else {
124 if (hwaddr && !qemu_plugin_hwaddr_is_io(hwaddr)) {
125 page = (uint64_t) qemu_plugin_hwaddr_phys_addr(hwaddr);
126 } else {
127 page = vaddr;
128 }
129 }
130 page &= ~page_mask;
131
132 g_mutex_lock(&lock);
133 count = (PageCounters *) g_hash_table_lookup(pages, GUINT_TO_POINTER(page));
134
135 if (!count) {
136 count = g_new0(PageCounters, 1);
137 count->page_address = page;
138 g_hash_table_insert(pages, GUINT_TO_POINTER(page), (gpointer) count);
139 }
140 if (qemu_plugin_mem_is_store(meminfo)) {
141 count->writes++;
142 count->cpu_write |= (1 << cpu_index);
143 } else {
144 count->reads++;
145 count->cpu_read |= (1 << cpu_index);
146 }
147
148 g_mutex_unlock(&lock);
149}
150
151static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
152{
153 size_t n = qemu_plugin_tb_n_insns(tb);
154 size_t i;
155
156 for (i = 0; i < n; i++) {
157 struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
158 qemu_plugin_register_vcpu_mem_cb(insn, vcpu_haddr,
159 QEMU_PLUGIN_CB_NO_REGS,
160 rw, NULL);
161 }
162}
163
164QEMU_PLUGIN_EXPORT
165int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
166 int argc, char **argv)
167{
168 int i;
169
170 for (i = 0; i < argc; i++) {
171 char *opt = argv[i];
172 g_autofree char **tokens = g_strsplit(opt, "=", -1);
173
174 if (g_strcmp0(tokens[0], "sortby") == 0) {
175 if (g_strcmp0(tokens[1], "reads") == 0) {
176 sort_by = SORT_R;
177 } else if (g_strcmp0(tokens[1], "writes") == 0) {
178 sort_by = SORT_W;
179 } else if (g_strcmp0(tokens[1], "address") == 0) {
180 sort_by = SORT_A;
181 } else {
182 fprintf(stderr, "invalid value to sortby: %s\n", tokens[1]);
183 return -1;
184 }
185 } else if (g_strcmp0(tokens[0], "io") == 0) {
186 if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &track_io)) {
187 fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
188 return -1;
189 }
190 } else if (g_strcmp0(tokens[0], "pagesize") == 0) {
191 page_size = g_ascii_strtoull(tokens[1], NULL, 10);
192 } else {
193 fprintf(stderr, "option parsing failed: %s\n", opt);
194 return -1;
195 }
196 }
197
198 plugin_init();
199
200 qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
201 qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
202 return 0;
203}
204