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