]> git.proxmox.com Git - mirror_qemu.git/blame - tests/plugin/hotpages.c
Merge remote-tracking branch 'remotes/amarkovic/tags/mips-queue-feb-27-2020' into...
[mirror_qemu.git] / tests / plugin / hotpages.c
CommitLineData
12268df3
AB
1/*
2 * Copyright (C) 2019, Alex Bennée <alex.bennee@linaro.org>
3 *
4 * Hot Pages - show which pages saw the most memory accesses.
5 *
6 * License: GNU GPL, version 2 or later.
7 * See the COPYING file in the top-level directory.
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
3fb356cc
AB
21QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
22
12268df3
AB
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 "%#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 /* We only get a hwaddr for system emulation */
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_device_offset(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 if (g_strcmp0(opt, "reads") == 0) {
173 sort_by = SORT_R;
174 } else if (g_strcmp0(opt, "writes") == 0) {
175 sort_by = SORT_W;
176 } else if (g_strcmp0(opt, "address") == 0) {
177 sort_by = SORT_A;
178 } else if (g_strcmp0(opt, "io") == 0) {
179 track_io = true;
180 } else if (g_str_has_prefix(opt, "pagesize=")) {
181 page_size = g_ascii_strtoull(opt + 9, NULL, 10);
182 } else {
183 fprintf(stderr, "option parsing failed: %s\n", opt);
184 return -1;
185 }
186 }
187
188 plugin_init();
189
190 qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
191 qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
192 return 0;
193}