xref: /openbmc/qemu/contrib/plugins/cache.c (revision 86ae3a1daad3c5b7ca039770d505574c08647e07)
1e2c5557cSMahmoud Mandour /*
2e2c5557cSMahmoud Mandour  * Copyright (C) 2021, Mahmoud Mandour <ma.mandourr@gmail.com>
3e2c5557cSMahmoud Mandour  *
4e2c5557cSMahmoud Mandour  * License: GNU GPL, version 2 or later.
5e2c5557cSMahmoud Mandour  *   See the COPYING file in the top-level directory.
6e2c5557cSMahmoud Mandour  */
7e2c5557cSMahmoud Mandour 
8e2c5557cSMahmoud Mandour #include <inttypes.h>
9e2c5557cSMahmoud Mandour #include <stdio.h>
10e2c5557cSMahmoud Mandour #include <glib.h>
11e2c5557cSMahmoud Mandour 
12e2c5557cSMahmoud Mandour #include <qemu-plugin.h>
13e2c5557cSMahmoud Mandour 
14e2c5557cSMahmoud Mandour QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
15e2c5557cSMahmoud Mandour 
16e2c5557cSMahmoud Mandour static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW;
17e2c5557cSMahmoud Mandour 
18e2c5557cSMahmoud Mandour static GHashTable *miss_ht;
19e2c5557cSMahmoud Mandour 
20e2c5557cSMahmoud Mandour static GMutex mtx;
21e2c5557cSMahmoud Mandour static GRand *rng;
22e2c5557cSMahmoud Mandour 
23e2c5557cSMahmoud Mandour static int limit;
24e2c5557cSMahmoud Mandour static bool sys;
25e2c5557cSMahmoud Mandour 
26e2c5557cSMahmoud Mandour static uint64_t dmem_accesses;
27e2c5557cSMahmoud Mandour static uint64_t dmisses;
28e2c5557cSMahmoud Mandour 
29e2c5557cSMahmoud Mandour static uint64_t imem_accesses;
30e2c5557cSMahmoud Mandour static uint64_t imisses;
31e2c5557cSMahmoud Mandour 
32e2c5557cSMahmoud Mandour /*
33e2c5557cSMahmoud Mandour  * A CacheSet is a set of cache blocks. A memory block that maps to a set can be
34e2c5557cSMahmoud Mandour  * put in any of the blocks inside the set. The number of block per set is
35e2c5557cSMahmoud Mandour  * called the associativity (assoc).
36e2c5557cSMahmoud Mandour  *
37e2c5557cSMahmoud Mandour  * Each block contains the the stored tag and a valid bit. Since this is not
38e2c5557cSMahmoud Mandour  * a functional simulator, the data itself is not stored. We only identify
39e2c5557cSMahmoud Mandour  * whether a block is in the cache or not by searching for its tag.
40e2c5557cSMahmoud Mandour  *
41e2c5557cSMahmoud Mandour  * In order to search for memory data in the cache, the set identifier and tag
42e2c5557cSMahmoud Mandour  * are extracted from the address and the set is probed to see whether a tag
43e2c5557cSMahmoud Mandour  * match occur.
44e2c5557cSMahmoud Mandour  *
45e2c5557cSMahmoud Mandour  * An address is logically divided into three portions: The block offset,
46e2c5557cSMahmoud Mandour  * the set number, and the tag.
47e2c5557cSMahmoud Mandour  *
48e2c5557cSMahmoud Mandour  * The set number is used to identify the set in which the block may exist.
49e2c5557cSMahmoud Mandour  * The tag is compared against all the tags of a set to search for a match. If a
50e2c5557cSMahmoud Mandour  * match is found, then the access is a hit.
51e2c5557cSMahmoud Mandour  */
52e2c5557cSMahmoud Mandour 
53e2c5557cSMahmoud Mandour typedef struct {
54e2c5557cSMahmoud Mandour     uint64_t tag;
55e2c5557cSMahmoud Mandour     bool valid;
56e2c5557cSMahmoud Mandour } CacheBlock;
57e2c5557cSMahmoud Mandour 
58e2c5557cSMahmoud Mandour typedef struct {
59e2c5557cSMahmoud Mandour     CacheBlock *blocks;
60e2c5557cSMahmoud Mandour } CacheSet;
61e2c5557cSMahmoud Mandour 
62e2c5557cSMahmoud Mandour typedef struct {
63e2c5557cSMahmoud Mandour     CacheSet *sets;
64e2c5557cSMahmoud Mandour     int num_sets;
65e2c5557cSMahmoud Mandour     int cachesize;
66e2c5557cSMahmoud Mandour     int assoc;
67e2c5557cSMahmoud Mandour     int blksize_shift;
68e2c5557cSMahmoud Mandour     uint64_t set_mask;
69e2c5557cSMahmoud Mandour     uint64_t tag_mask;
70e2c5557cSMahmoud Mandour } Cache;
71e2c5557cSMahmoud Mandour 
72e2c5557cSMahmoud Mandour typedef struct {
73e2c5557cSMahmoud Mandour     char *disas_str;
74e2c5557cSMahmoud Mandour     const char *symbol;
75e2c5557cSMahmoud Mandour     uint64_t addr;
76e2c5557cSMahmoud Mandour     uint64_t dmisses;
77e2c5557cSMahmoud Mandour     uint64_t imisses;
78e2c5557cSMahmoud Mandour } InsnData;
79e2c5557cSMahmoud Mandour 
80e2c5557cSMahmoud Mandour Cache *dcache, *icache;
81e2c5557cSMahmoud Mandour 
82e2c5557cSMahmoud Mandour static int pow_of_two(int num)
83e2c5557cSMahmoud Mandour {
84e2c5557cSMahmoud Mandour     g_assert((num & (num - 1)) == 0);
85e2c5557cSMahmoud Mandour     int ret = 0;
86e2c5557cSMahmoud Mandour     while (num /= 2) {
87e2c5557cSMahmoud Mandour         ret++;
88e2c5557cSMahmoud Mandour     }
89e2c5557cSMahmoud Mandour     return ret;
90e2c5557cSMahmoud Mandour }
91e2c5557cSMahmoud Mandour 
92e2c5557cSMahmoud Mandour static inline uint64_t extract_tag(Cache *cache, uint64_t addr)
93e2c5557cSMahmoud Mandour {
94e2c5557cSMahmoud Mandour     return addr & cache->tag_mask;
95e2c5557cSMahmoud Mandour }
96e2c5557cSMahmoud Mandour 
97e2c5557cSMahmoud Mandour static inline uint64_t extract_set(Cache *cache, uint64_t addr)
98e2c5557cSMahmoud Mandour {
99e2c5557cSMahmoud Mandour     return (addr & cache->set_mask) >> cache->blksize_shift;
100e2c5557cSMahmoud Mandour }
101e2c5557cSMahmoud Mandour 
102*86ae3a1dSMahmoud Mandour static const char *cache_config_error(int blksize, int assoc, int cachesize)
103*86ae3a1dSMahmoud Mandour {
104*86ae3a1dSMahmoud Mandour     if (cachesize % blksize != 0) {
105*86ae3a1dSMahmoud Mandour         return "cache size must be divisible by block size";
106*86ae3a1dSMahmoud Mandour     } else if (cachesize % (blksize * assoc) != 0) {
107*86ae3a1dSMahmoud Mandour         return "cache size must be divisible by set size (assoc * block size)";
108*86ae3a1dSMahmoud Mandour     } else {
109*86ae3a1dSMahmoud Mandour         return NULL;
110*86ae3a1dSMahmoud Mandour     }
111*86ae3a1dSMahmoud Mandour }
112*86ae3a1dSMahmoud Mandour 
113*86ae3a1dSMahmoud Mandour static bool bad_cache_params(int blksize, int assoc, int cachesize)
114*86ae3a1dSMahmoud Mandour {
115*86ae3a1dSMahmoud Mandour     return (cachesize % blksize) != 0 || (cachesize % (blksize * assoc) != 0);
116*86ae3a1dSMahmoud Mandour }
117*86ae3a1dSMahmoud Mandour 
118e2c5557cSMahmoud Mandour static Cache *cache_init(int blksize, int assoc, int cachesize)
119e2c5557cSMahmoud Mandour {
120*86ae3a1dSMahmoud Mandour     if (bad_cache_params(blksize, assoc, cachesize)) {
121*86ae3a1dSMahmoud Mandour         return NULL;
122*86ae3a1dSMahmoud Mandour     }
123*86ae3a1dSMahmoud Mandour 
124e2c5557cSMahmoud Mandour     Cache *cache;
125e2c5557cSMahmoud Mandour     int i;
126e2c5557cSMahmoud Mandour     uint64_t blk_mask;
127e2c5557cSMahmoud Mandour 
128e2c5557cSMahmoud Mandour     cache = g_new(Cache, 1);
129e2c5557cSMahmoud Mandour     cache->assoc = assoc;
130e2c5557cSMahmoud Mandour     cache->cachesize = cachesize;
131e2c5557cSMahmoud Mandour     cache->num_sets = cachesize / (blksize * assoc);
132e2c5557cSMahmoud Mandour     cache->sets = g_new(CacheSet, cache->num_sets);
133e2c5557cSMahmoud Mandour     cache->blksize_shift = pow_of_two(blksize);
134e2c5557cSMahmoud Mandour 
135e2c5557cSMahmoud Mandour     for (i = 0; i < cache->num_sets; i++) {
136e2c5557cSMahmoud Mandour         cache->sets[i].blocks = g_new0(CacheBlock, assoc);
137e2c5557cSMahmoud Mandour     }
138e2c5557cSMahmoud Mandour 
139e2c5557cSMahmoud Mandour     blk_mask = blksize - 1;
140e2c5557cSMahmoud Mandour     cache->set_mask = ((cache->num_sets - 1) << cache->blksize_shift);
141e2c5557cSMahmoud Mandour     cache->tag_mask = ~(cache->set_mask | blk_mask);
142e2c5557cSMahmoud Mandour     return cache;
143e2c5557cSMahmoud Mandour }
144e2c5557cSMahmoud Mandour 
145e2c5557cSMahmoud Mandour static int get_invalid_block(Cache *cache, uint64_t set)
146e2c5557cSMahmoud Mandour {
147e2c5557cSMahmoud Mandour     int i;
148e2c5557cSMahmoud Mandour 
149e2c5557cSMahmoud Mandour     for (i = 0; i < cache->assoc; i++) {
150e2c5557cSMahmoud Mandour         if (!cache->sets[set].blocks[i].valid) {
151e2c5557cSMahmoud Mandour             return i;
152e2c5557cSMahmoud Mandour         }
153e2c5557cSMahmoud Mandour     }
154e2c5557cSMahmoud Mandour 
155e2c5557cSMahmoud Mandour     return -1;
156e2c5557cSMahmoud Mandour }
157e2c5557cSMahmoud Mandour 
158e2c5557cSMahmoud Mandour static int get_replaced_block(Cache *cache)
159e2c5557cSMahmoud Mandour {
160e2c5557cSMahmoud Mandour     return g_rand_int_range(rng, 0, cache->assoc);
161e2c5557cSMahmoud Mandour }
162e2c5557cSMahmoud Mandour 
163e2c5557cSMahmoud Mandour static bool in_cache(Cache *cache, uint64_t addr)
164e2c5557cSMahmoud Mandour {
165e2c5557cSMahmoud Mandour     int i;
166e2c5557cSMahmoud Mandour     uint64_t tag, set;
167e2c5557cSMahmoud Mandour 
168e2c5557cSMahmoud Mandour     tag = extract_tag(cache, addr);
169e2c5557cSMahmoud Mandour     set = extract_set(cache, addr);
170e2c5557cSMahmoud Mandour 
171e2c5557cSMahmoud Mandour     for (i = 0; i < cache->assoc; i++) {
172e2c5557cSMahmoud Mandour         if (cache->sets[set].blocks[i].tag == tag &&
173e2c5557cSMahmoud Mandour                 cache->sets[set].blocks[i].valid) {
174e2c5557cSMahmoud Mandour             return true;
175e2c5557cSMahmoud Mandour         }
176e2c5557cSMahmoud Mandour     }
177e2c5557cSMahmoud Mandour 
178e2c5557cSMahmoud Mandour     return false;
179e2c5557cSMahmoud Mandour }
180e2c5557cSMahmoud Mandour 
181e2c5557cSMahmoud Mandour /**
182e2c5557cSMahmoud Mandour  * access_cache(): Simulate a cache access
183e2c5557cSMahmoud Mandour  * @cache: The cache under simulation
184e2c5557cSMahmoud Mandour  * @addr: The address of the requested memory location
185e2c5557cSMahmoud Mandour  *
186e2c5557cSMahmoud Mandour  * Returns true if the requsted data is hit in the cache and false when missed.
187e2c5557cSMahmoud Mandour  * The cache is updated on miss for the next access.
188e2c5557cSMahmoud Mandour  */
189e2c5557cSMahmoud Mandour static bool access_cache(Cache *cache, uint64_t addr)
190e2c5557cSMahmoud Mandour {
191e2c5557cSMahmoud Mandour     uint64_t tag, set;
192e2c5557cSMahmoud Mandour     int replaced_blk;
193e2c5557cSMahmoud Mandour 
194e2c5557cSMahmoud Mandour     if (in_cache(cache, addr)) {
195e2c5557cSMahmoud Mandour         return true;
196e2c5557cSMahmoud Mandour     }
197e2c5557cSMahmoud Mandour 
198e2c5557cSMahmoud Mandour     tag = extract_tag(cache, addr);
199e2c5557cSMahmoud Mandour     set = extract_set(cache, addr);
200e2c5557cSMahmoud Mandour 
201e2c5557cSMahmoud Mandour     replaced_blk = get_invalid_block(cache, set);
202e2c5557cSMahmoud Mandour 
203e2c5557cSMahmoud Mandour     if (replaced_blk == -1) {
204e2c5557cSMahmoud Mandour         replaced_blk = get_replaced_block(cache);
205e2c5557cSMahmoud Mandour     }
206e2c5557cSMahmoud Mandour 
207e2c5557cSMahmoud Mandour     cache->sets[set].blocks[replaced_blk].tag = tag;
208e2c5557cSMahmoud Mandour     cache->sets[set].blocks[replaced_blk].valid = true;
209e2c5557cSMahmoud Mandour 
210e2c5557cSMahmoud Mandour     return false;
211e2c5557cSMahmoud Mandour }
212e2c5557cSMahmoud Mandour 
213e2c5557cSMahmoud Mandour static void vcpu_mem_access(unsigned int vcpu_index, qemu_plugin_meminfo_t info,
214e2c5557cSMahmoud Mandour                             uint64_t vaddr, void *userdata)
215e2c5557cSMahmoud Mandour {
216e2c5557cSMahmoud Mandour     uint64_t effective_addr;
217e2c5557cSMahmoud Mandour     struct qemu_plugin_hwaddr *hwaddr;
218e2c5557cSMahmoud Mandour     InsnData *insn;
219e2c5557cSMahmoud Mandour 
220e2c5557cSMahmoud Mandour     g_mutex_lock(&mtx);
221e2c5557cSMahmoud Mandour     hwaddr = qemu_plugin_get_hwaddr(info, vaddr);
222e2c5557cSMahmoud Mandour     if (hwaddr && qemu_plugin_hwaddr_is_io(hwaddr)) {
223e2c5557cSMahmoud Mandour         g_mutex_unlock(&mtx);
224e2c5557cSMahmoud Mandour         return;
225e2c5557cSMahmoud Mandour     }
226e2c5557cSMahmoud Mandour 
227e2c5557cSMahmoud Mandour     effective_addr = hwaddr ? qemu_plugin_hwaddr_phys_addr(hwaddr) : vaddr;
228e2c5557cSMahmoud Mandour 
229e2c5557cSMahmoud Mandour     if (!access_cache(dcache, effective_addr)) {
230e2c5557cSMahmoud Mandour         insn = (InsnData *) userdata;
231e2c5557cSMahmoud Mandour         insn->dmisses++;
232e2c5557cSMahmoud Mandour         dmisses++;
233e2c5557cSMahmoud Mandour     }
234e2c5557cSMahmoud Mandour     dmem_accesses++;
235e2c5557cSMahmoud Mandour     g_mutex_unlock(&mtx);
236e2c5557cSMahmoud Mandour }
237e2c5557cSMahmoud Mandour 
238e2c5557cSMahmoud Mandour static void vcpu_insn_exec(unsigned int vcpu_index, void *userdata)
239e2c5557cSMahmoud Mandour {
240e2c5557cSMahmoud Mandour     uint64_t insn_addr;
241e2c5557cSMahmoud Mandour     InsnData *insn;
242e2c5557cSMahmoud Mandour 
243e2c5557cSMahmoud Mandour     g_mutex_lock(&mtx);
244e2c5557cSMahmoud Mandour     insn_addr = ((InsnData *) userdata)->addr;
245e2c5557cSMahmoud Mandour 
246e2c5557cSMahmoud Mandour     if (!access_cache(icache, insn_addr)) {
247e2c5557cSMahmoud Mandour         insn = (InsnData *) userdata;
248e2c5557cSMahmoud Mandour         insn->imisses++;
249e2c5557cSMahmoud Mandour         imisses++;
250e2c5557cSMahmoud Mandour     }
251e2c5557cSMahmoud Mandour     imem_accesses++;
252e2c5557cSMahmoud Mandour     g_mutex_unlock(&mtx);
253e2c5557cSMahmoud Mandour }
254e2c5557cSMahmoud Mandour 
255e2c5557cSMahmoud Mandour static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
256e2c5557cSMahmoud Mandour {
257e2c5557cSMahmoud Mandour     size_t n_insns;
258e2c5557cSMahmoud Mandour     size_t i;
259e2c5557cSMahmoud Mandour     InsnData *data;
260e2c5557cSMahmoud Mandour 
261e2c5557cSMahmoud Mandour     n_insns = qemu_plugin_tb_n_insns(tb);
262e2c5557cSMahmoud Mandour     for (i = 0; i < n_insns; i++) {
263e2c5557cSMahmoud Mandour         struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
264e2c5557cSMahmoud Mandour         uint64_t effective_addr;
265e2c5557cSMahmoud Mandour 
266e2c5557cSMahmoud Mandour         if (sys) {
267e2c5557cSMahmoud Mandour             effective_addr = (uint64_t) qemu_plugin_insn_haddr(insn);
268e2c5557cSMahmoud Mandour         } else {
269e2c5557cSMahmoud Mandour             effective_addr = (uint64_t) qemu_plugin_insn_vaddr(insn);
270e2c5557cSMahmoud Mandour         }
271e2c5557cSMahmoud Mandour 
272e2c5557cSMahmoud Mandour         /*
273e2c5557cSMahmoud Mandour          * Instructions might get translated multiple times, we do not create
274e2c5557cSMahmoud Mandour          * new entries for those instructions. Instead, we fetch the same
275e2c5557cSMahmoud Mandour          * entry from the hash table and register it for the callback again.
276e2c5557cSMahmoud Mandour          */
277e2c5557cSMahmoud Mandour         g_mutex_lock(&mtx);
278e2c5557cSMahmoud Mandour         data = g_hash_table_lookup(miss_ht, GUINT_TO_POINTER(effective_addr));
279e2c5557cSMahmoud Mandour         if (data == NULL) {
280e2c5557cSMahmoud Mandour             data = g_new0(InsnData, 1);
281e2c5557cSMahmoud Mandour             data->disas_str = qemu_plugin_insn_disas(insn);
282e2c5557cSMahmoud Mandour             data->symbol = qemu_plugin_insn_symbol(insn);
283e2c5557cSMahmoud Mandour             data->addr = effective_addr;
284e2c5557cSMahmoud Mandour             g_hash_table_insert(miss_ht, GUINT_TO_POINTER(effective_addr),
285e2c5557cSMahmoud Mandour                                (gpointer) data);
286e2c5557cSMahmoud Mandour         }
287e2c5557cSMahmoud Mandour         g_mutex_unlock(&mtx);
288e2c5557cSMahmoud Mandour 
289e2c5557cSMahmoud Mandour         qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem_access,
290e2c5557cSMahmoud Mandour                                          QEMU_PLUGIN_CB_NO_REGS,
291e2c5557cSMahmoud Mandour                                          rw, data);
292e2c5557cSMahmoud Mandour 
293e2c5557cSMahmoud Mandour         qemu_plugin_register_vcpu_insn_exec_cb(insn, vcpu_insn_exec,
294e2c5557cSMahmoud Mandour                                                QEMU_PLUGIN_CB_NO_REGS, data);
295e2c5557cSMahmoud Mandour     }
296e2c5557cSMahmoud Mandour }
297e2c5557cSMahmoud Mandour 
298e2c5557cSMahmoud Mandour static void insn_free(gpointer data)
299e2c5557cSMahmoud Mandour {
300e2c5557cSMahmoud Mandour     InsnData *insn = (InsnData *) data;
301e2c5557cSMahmoud Mandour     g_free(insn->disas_str);
302e2c5557cSMahmoud Mandour     g_free(insn);
303e2c5557cSMahmoud Mandour }
304e2c5557cSMahmoud Mandour 
305e2c5557cSMahmoud Mandour static void cache_free(Cache *cache)
306e2c5557cSMahmoud Mandour {
307e2c5557cSMahmoud Mandour     for (int i = 0; i < cache->num_sets; i++) {
308e2c5557cSMahmoud Mandour         g_free(cache->sets[i].blocks);
309e2c5557cSMahmoud Mandour     }
310e2c5557cSMahmoud Mandour 
311e2c5557cSMahmoud Mandour     g_free(cache->sets);
312e2c5557cSMahmoud Mandour     g_free(cache);
313e2c5557cSMahmoud Mandour }
314e2c5557cSMahmoud Mandour 
315e2c5557cSMahmoud Mandour static int dcmp(gconstpointer a, gconstpointer b)
316e2c5557cSMahmoud Mandour {
317e2c5557cSMahmoud Mandour     InsnData *insn_a = (InsnData *) a;
318e2c5557cSMahmoud Mandour     InsnData *insn_b = (InsnData *) b;
319e2c5557cSMahmoud Mandour 
320e2c5557cSMahmoud Mandour     return insn_a->dmisses < insn_b->dmisses ? 1 : -1;
321e2c5557cSMahmoud Mandour }
322e2c5557cSMahmoud Mandour 
323e2c5557cSMahmoud Mandour static int icmp(gconstpointer a, gconstpointer b)
324e2c5557cSMahmoud Mandour {
325e2c5557cSMahmoud Mandour     InsnData *insn_a = (InsnData *) a;
326e2c5557cSMahmoud Mandour     InsnData *insn_b = (InsnData *) b;
327e2c5557cSMahmoud Mandour 
328e2c5557cSMahmoud Mandour     return insn_a->imisses < insn_b->imisses ? 1 : -1;
329e2c5557cSMahmoud Mandour }
330e2c5557cSMahmoud Mandour 
331e2c5557cSMahmoud Mandour static void log_stats()
332e2c5557cSMahmoud Mandour {
333e2c5557cSMahmoud Mandour     g_autoptr(GString) rep = g_string_new("");
334e2c5557cSMahmoud Mandour     g_string_append_printf(rep,
335e2c5557cSMahmoud Mandour         "Data accesses: %lu, Misses: %lu\nMiss rate: %lf%%\n\n",
336e2c5557cSMahmoud Mandour         dmem_accesses,
337e2c5557cSMahmoud Mandour         dmisses,
338e2c5557cSMahmoud Mandour         ((double) dmisses / (double) dmem_accesses) * 100.0);
339e2c5557cSMahmoud Mandour 
340e2c5557cSMahmoud Mandour     g_string_append_printf(rep,
341e2c5557cSMahmoud Mandour         "Instruction accesses: %lu, Misses: %lu\nMiss rate: %lf%%\n\n",
342e2c5557cSMahmoud Mandour         imem_accesses,
343e2c5557cSMahmoud Mandour         imisses,
344e2c5557cSMahmoud Mandour         ((double) imisses / (double) imem_accesses) * 100.0);
345e2c5557cSMahmoud Mandour 
346e2c5557cSMahmoud Mandour     qemu_plugin_outs(rep->str);
347e2c5557cSMahmoud Mandour }
348e2c5557cSMahmoud Mandour 
349e2c5557cSMahmoud Mandour static void log_top_insns()
350e2c5557cSMahmoud Mandour {
351e2c5557cSMahmoud Mandour     int i;
352e2c5557cSMahmoud Mandour     GList *curr, *miss_insns;
353e2c5557cSMahmoud Mandour     InsnData *insn;
354e2c5557cSMahmoud Mandour 
355e2c5557cSMahmoud Mandour     miss_insns = g_hash_table_get_values(miss_ht);
356e2c5557cSMahmoud Mandour     miss_insns = g_list_sort(miss_insns, dcmp);
357e2c5557cSMahmoud Mandour     g_autoptr(GString) rep = g_string_new("");
358e2c5557cSMahmoud Mandour     g_string_append_printf(rep, "%s", "address, data misses, instruction\n");
359e2c5557cSMahmoud Mandour 
360e2c5557cSMahmoud Mandour     for (curr = miss_insns, i = 0; curr && i < limit; i++, curr = curr->next) {
361e2c5557cSMahmoud Mandour         insn = (InsnData *) curr->data;
362e2c5557cSMahmoud Mandour         g_string_append_printf(rep, "0x%" PRIx64, insn->addr);
363e2c5557cSMahmoud Mandour         if (insn->symbol) {
364e2c5557cSMahmoud Mandour             g_string_append_printf(rep, " (%s)", insn->symbol);
365e2c5557cSMahmoud Mandour         }
366e2c5557cSMahmoud Mandour         g_string_append_printf(rep, ", %ld, %s\n", insn->dmisses,
367e2c5557cSMahmoud Mandour                                insn->disas_str);
368e2c5557cSMahmoud Mandour     }
369e2c5557cSMahmoud Mandour 
370e2c5557cSMahmoud Mandour     miss_insns = g_list_sort(miss_insns, icmp);
371e2c5557cSMahmoud Mandour     g_string_append_printf(rep, "%s", "\naddress, fetch misses, instruction\n");
372e2c5557cSMahmoud Mandour 
373e2c5557cSMahmoud Mandour     for (curr = miss_insns, i = 0; curr && i < limit; i++, curr = curr->next) {
374e2c5557cSMahmoud Mandour         insn = (InsnData *) curr->data;
375e2c5557cSMahmoud Mandour         g_string_append_printf(rep, "0x%" PRIx64, insn->addr);
376e2c5557cSMahmoud Mandour         if (insn->symbol) {
377e2c5557cSMahmoud Mandour             g_string_append_printf(rep, " (%s)", insn->symbol);
378e2c5557cSMahmoud Mandour         }
379e2c5557cSMahmoud Mandour         g_string_append_printf(rep, ", %ld, %s\n", insn->imisses,
380e2c5557cSMahmoud Mandour                                insn->disas_str);
381e2c5557cSMahmoud Mandour     }
382e2c5557cSMahmoud Mandour 
383e2c5557cSMahmoud Mandour     qemu_plugin_outs(rep->str);
384e2c5557cSMahmoud Mandour     g_list_free(miss_insns);
385e2c5557cSMahmoud Mandour }
386e2c5557cSMahmoud Mandour 
387e2c5557cSMahmoud Mandour static void plugin_exit(qemu_plugin_id_t id, void *p)
388e2c5557cSMahmoud Mandour {
389e2c5557cSMahmoud Mandour     log_stats();
390e2c5557cSMahmoud Mandour     log_top_insns();
391e2c5557cSMahmoud Mandour 
392e2c5557cSMahmoud Mandour     cache_free(dcache);
393e2c5557cSMahmoud Mandour     cache_free(icache);
394e2c5557cSMahmoud Mandour 
395e2c5557cSMahmoud Mandour     g_hash_table_destroy(miss_ht);
396e2c5557cSMahmoud Mandour }
397e2c5557cSMahmoud Mandour 
398e2c5557cSMahmoud Mandour QEMU_PLUGIN_EXPORT
399e2c5557cSMahmoud Mandour int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
400e2c5557cSMahmoud Mandour                         int argc, char **argv)
401e2c5557cSMahmoud Mandour {
402e2c5557cSMahmoud Mandour     int i;
403e2c5557cSMahmoud Mandour     int iassoc, iblksize, icachesize;
404e2c5557cSMahmoud Mandour     int dassoc, dblksize, dcachesize;
405e2c5557cSMahmoud Mandour 
406e2c5557cSMahmoud Mandour     limit = 32;
407e2c5557cSMahmoud Mandour     sys = info->system_emulation;
408e2c5557cSMahmoud Mandour 
409e2c5557cSMahmoud Mandour     dassoc = 8;
410e2c5557cSMahmoud Mandour     dblksize = 64;
411e2c5557cSMahmoud Mandour     dcachesize = dblksize * dassoc * 32;
412e2c5557cSMahmoud Mandour 
413e2c5557cSMahmoud Mandour     iassoc = 8;
414e2c5557cSMahmoud Mandour     iblksize = 64;
415e2c5557cSMahmoud Mandour     icachesize = iblksize * iassoc * 32;
416e2c5557cSMahmoud Mandour 
417e2c5557cSMahmoud Mandour 
418e2c5557cSMahmoud Mandour     for (i = 0; i < argc; i++) {
419e2c5557cSMahmoud Mandour         char *opt = argv[i];
420*86ae3a1dSMahmoud Mandour         if (g_str_has_prefix(opt, "iblksize=")) {
421*86ae3a1dSMahmoud Mandour             iblksize = g_ascii_strtoll(opt + 9, NULL, 10);
422*86ae3a1dSMahmoud Mandour         } else if (g_str_has_prefix(opt, "iassoc=")) {
423*86ae3a1dSMahmoud Mandour             iassoc = g_ascii_strtoll(opt + 7, NULL, 10);
424*86ae3a1dSMahmoud Mandour         } else if (g_str_has_prefix(opt, "icachesize=")) {
425*86ae3a1dSMahmoud Mandour             icachesize = g_ascii_strtoll(opt + 11, NULL, 10);
426*86ae3a1dSMahmoud Mandour         } else if (g_str_has_prefix(opt, "dblksize=")) {
427*86ae3a1dSMahmoud Mandour             dblksize = g_ascii_strtoll(opt + 9, NULL, 10);
428*86ae3a1dSMahmoud Mandour         } else if (g_str_has_prefix(opt, "dassoc=")) {
429*86ae3a1dSMahmoud Mandour             dassoc = g_ascii_strtoll(opt + 7, NULL, 10);
430*86ae3a1dSMahmoud Mandour         } else if (g_str_has_prefix(opt, "dcachesize=")) {
431*86ae3a1dSMahmoud Mandour             dcachesize = g_ascii_strtoll(opt + 11, NULL, 10);
432*86ae3a1dSMahmoud Mandour         } else if (g_str_has_prefix(opt, "limit=")) {
433e2c5557cSMahmoud Mandour             limit = g_ascii_strtoll(opt + 6, NULL, 10);
434e2c5557cSMahmoud Mandour         } else {
435e2c5557cSMahmoud Mandour             fprintf(stderr, "option parsing failed: %s\n", opt);
436e2c5557cSMahmoud Mandour             return -1;
437e2c5557cSMahmoud Mandour         }
438e2c5557cSMahmoud Mandour     }
439e2c5557cSMahmoud Mandour 
440e2c5557cSMahmoud Mandour     dcache = cache_init(dblksize, dassoc, dcachesize);
441*86ae3a1dSMahmoud Mandour     if (!dcache) {
442*86ae3a1dSMahmoud Mandour         const char *err = cache_config_error(dblksize, dassoc, dcachesize);
443*86ae3a1dSMahmoud Mandour         fprintf(stderr, "dcache cannot be constructed from given parameters\n");
444*86ae3a1dSMahmoud Mandour         fprintf(stderr, "%s\n", err);
445*86ae3a1dSMahmoud Mandour         return -1;
446*86ae3a1dSMahmoud Mandour     }
447*86ae3a1dSMahmoud Mandour 
448e2c5557cSMahmoud Mandour     icache = cache_init(iblksize, iassoc, icachesize);
449*86ae3a1dSMahmoud Mandour     if (!icache) {
450*86ae3a1dSMahmoud Mandour         const char *err = cache_config_error(iblksize, iassoc, icachesize);
451*86ae3a1dSMahmoud Mandour         fprintf(stderr, "icache cannot be constructed from given parameters\n");
452*86ae3a1dSMahmoud Mandour         fprintf(stderr, "%s\n", err);
453*86ae3a1dSMahmoud Mandour         return -1;
454*86ae3a1dSMahmoud Mandour     }
455e2c5557cSMahmoud Mandour 
456e2c5557cSMahmoud Mandour     rng = g_rand_new();
457e2c5557cSMahmoud Mandour 
458e2c5557cSMahmoud Mandour     qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
459e2c5557cSMahmoud Mandour     qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
460e2c5557cSMahmoud Mandour 
461e2c5557cSMahmoud Mandour     miss_ht = g_hash_table_new_full(NULL, g_direct_equal, NULL, insn_free);
462e2c5557cSMahmoud Mandour 
463e2c5557cSMahmoud Mandour     return 0;
464e2c5557cSMahmoud Mandour }
465