2 * Copyright (C) 2019, Alex Bennée <alex.bennee@linaro.org>
4 * Hot Pages - show which pages saw the most memory accesses.
6 * License: GNU GPL, version 2 or later.
7 * See the COPYING file in the top-level directory.
19 #include <qemu-plugin.h>
21 QEMU_PLUGIN_EXPORT
int qemu_plugin_version
= QEMU_PLUGIN_VERSION
;
23 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
25 static uint64_t page_size
= 4096;
26 static uint64_t page_mask
;
27 static int limit
= 50;
28 static enum qemu_plugin_mem_rw rw
= QEMU_PLUGIN_MEM_RW
;
38 static int sort_by
= SORT_RW
;
41 uint64_t page_address
;
49 static GHashTable
*pages
;
51 static gint
cmp_access_count(gconstpointer a
, gconstpointer b
)
53 PageCounters
*ea
= (PageCounters
*) a
;
54 PageCounters
*eb
= (PageCounters
*) b
;
58 r
= (ea
->reads
+ ea
->writes
) > (eb
->reads
+ eb
->writes
) ? -1 : 1;
61 r
= ea
->reads
> eb
->reads
? -1 : 1;
64 r
= ea
->writes
> eb
->writes
? -1 : 1;
67 r
= ea
->page_address
> eb
->page_address
? -1 : 1;
70 g_assert_not_reached();
76 static void plugin_exit(qemu_plugin_id_t id
, void *p
)
78 g_autoptr(GString
) report
= g_string_new("Addr, RCPUs, Reads, WCPUs, Writes\n");
82 counts
= g_hash_table_get_values(pages
);
83 if (counts
&& g_list_next(counts
)) {
86 it
= g_list_sort(counts
, cmp_access_count
);
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",
94 rec
->cpu_read
, rec
->reads
,
95 rec
->cpu_write
, rec
->writes
);
100 qemu_plugin_outs(report
->str
);
103 static void plugin_init(void)
105 page_mask
= (page_size
- 1);
106 pages
= g_hash_table_new(NULL
, g_direct_equal
);
109 static void vcpu_haddr(unsigned int cpu_index
, qemu_plugin_meminfo_t meminfo
,
110 uint64_t vaddr
, void *udata
)
112 struct qemu_plugin_hwaddr
*hwaddr
= qemu_plugin_get_hwaddr(meminfo
, vaddr
);
116 /* We only get a hwaddr for system emulation */
118 if (hwaddr
&& qemu_plugin_hwaddr_is_io(hwaddr
)) {
124 if (hwaddr
&& !qemu_plugin_hwaddr_is_io(hwaddr
)) {
125 page
= (uint64_t) qemu_plugin_hwaddr_phys_addr(hwaddr
);
133 count
= (PageCounters
*) g_hash_table_lookup(pages
, GUINT_TO_POINTER(page
));
136 count
= g_new0(PageCounters
, 1);
137 count
->page_address
= page
;
138 g_hash_table_insert(pages
, GUINT_TO_POINTER(page
), (gpointer
) count
);
140 if (qemu_plugin_mem_is_store(meminfo
)) {
142 count
->cpu_write
|= (1 << cpu_index
);
145 count
->cpu_read
|= (1 << cpu_index
);
148 g_mutex_unlock(&lock
);
151 static void vcpu_tb_trans(qemu_plugin_id_t id
, struct qemu_plugin_tb
*tb
)
153 size_t n
= qemu_plugin_tb_n_insns(tb
);
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
,
165 int qemu_plugin_install(qemu_plugin_id_t id
, const qemu_info_t
*info
,
166 int argc
, char **argv
)
170 for (i
= 0; i
< argc
; i
++) {
172 g_autofree
char **tokens
= g_strsplit(opt
, "=", -1);
174 if (g_strcmp0(tokens
[0], "sortby") == 0) {
175 if (g_strcmp0(tokens
[1], "reads") == 0) {
177 } else if (g_strcmp0(tokens
[1], "writes") == 0) {
179 } else if (g_strcmp0(tokens
[1], "address") == 0) {
182 fprintf(stderr
, "invalid value to sortby: %s\n", tokens
[1]);
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
);
190 } else if (g_strcmp0(tokens
[0], "pagesize") == 0) {
191 page_size
= g_ascii_strtoull(tokens
[1], NULL
, 10);
193 fprintf(stderr
, "option parsing failed: %s\n", opt
);
200 qemu_plugin_register_vcpu_tb_trans_cb(id
, vcpu_tb_trans
);
201 qemu_plugin_register_atexit_cb(id
, plugin_exit
, NULL
);