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 32*40c4a553SMahmoud Mandour enum EvictionPolicy { 33*40c4a553SMahmoud Mandour LRU, 34*40c4a553SMahmoud Mandour FIFO, 35*40c4a553SMahmoud Mandour RAND, 36*40c4a553SMahmoud Mandour }; 37*40c4a553SMahmoud Mandour 38*40c4a553SMahmoud Mandour enum EvictionPolicy policy; 39*40c4a553SMahmoud Mandour 40e2c5557cSMahmoud Mandour /* 41e2c5557cSMahmoud Mandour * A CacheSet is a set of cache blocks. A memory block that maps to a set can be 42e2c5557cSMahmoud Mandour * put in any of the blocks inside the set. The number of block per set is 43e2c5557cSMahmoud Mandour * called the associativity (assoc). 44e2c5557cSMahmoud Mandour * 45e2c5557cSMahmoud Mandour * Each block contains the the stored tag and a valid bit. Since this is not 46e2c5557cSMahmoud Mandour * a functional simulator, the data itself is not stored. We only identify 47e2c5557cSMahmoud Mandour * whether a block is in the cache or not by searching for its tag. 48e2c5557cSMahmoud Mandour * 49e2c5557cSMahmoud Mandour * In order to search for memory data in the cache, the set identifier and tag 50e2c5557cSMahmoud Mandour * are extracted from the address and the set is probed to see whether a tag 51e2c5557cSMahmoud Mandour * match occur. 52e2c5557cSMahmoud Mandour * 53e2c5557cSMahmoud Mandour * An address is logically divided into three portions: The block offset, 54e2c5557cSMahmoud Mandour * the set number, and the tag. 55e2c5557cSMahmoud Mandour * 56e2c5557cSMahmoud Mandour * The set number is used to identify the set in which the block may exist. 57e2c5557cSMahmoud Mandour * The tag is compared against all the tags of a set to search for a match. If a 58e2c5557cSMahmoud Mandour * match is found, then the access is a hit. 59*40c4a553SMahmoud Mandour * 60*40c4a553SMahmoud Mandour * The CacheSet also contains bookkeaping information about eviction details. 61e2c5557cSMahmoud Mandour */ 62e2c5557cSMahmoud Mandour 63e2c5557cSMahmoud Mandour typedef struct { 64e2c5557cSMahmoud Mandour uint64_t tag; 65e2c5557cSMahmoud Mandour bool valid; 66e2c5557cSMahmoud Mandour } CacheBlock; 67e2c5557cSMahmoud Mandour 68e2c5557cSMahmoud Mandour typedef struct { 69e2c5557cSMahmoud Mandour CacheBlock *blocks; 70*40c4a553SMahmoud Mandour uint64_t *lru_priorities; 71*40c4a553SMahmoud Mandour uint64_t lru_gen_counter; 72*40c4a553SMahmoud Mandour GQueue *fifo_queue; 73e2c5557cSMahmoud Mandour } CacheSet; 74e2c5557cSMahmoud Mandour 75e2c5557cSMahmoud Mandour typedef struct { 76e2c5557cSMahmoud Mandour CacheSet *sets; 77e2c5557cSMahmoud Mandour int num_sets; 78e2c5557cSMahmoud Mandour int cachesize; 79e2c5557cSMahmoud Mandour int assoc; 80e2c5557cSMahmoud Mandour int blksize_shift; 81e2c5557cSMahmoud Mandour uint64_t set_mask; 82e2c5557cSMahmoud Mandour uint64_t tag_mask; 83e2c5557cSMahmoud Mandour } Cache; 84e2c5557cSMahmoud Mandour 85e2c5557cSMahmoud Mandour typedef struct { 86e2c5557cSMahmoud Mandour char *disas_str; 87e2c5557cSMahmoud Mandour const char *symbol; 88e2c5557cSMahmoud Mandour uint64_t addr; 89e2c5557cSMahmoud Mandour uint64_t dmisses; 90e2c5557cSMahmoud Mandour uint64_t imisses; 91e2c5557cSMahmoud Mandour } InsnData; 92e2c5557cSMahmoud Mandour 93*40c4a553SMahmoud Mandour void (*update_hit)(Cache *cache, int set, int blk); 94*40c4a553SMahmoud Mandour void (*update_miss)(Cache *cache, int set, int blk); 95*40c4a553SMahmoud Mandour 96*40c4a553SMahmoud Mandour void (*metadata_init)(Cache *cache); 97*40c4a553SMahmoud Mandour void (*metadata_destroy)(Cache *cache); 98*40c4a553SMahmoud Mandour 99e2c5557cSMahmoud Mandour Cache *dcache, *icache; 100e2c5557cSMahmoud Mandour 101e2c5557cSMahmoud Mandour static int pow_of_two(int num) 102e2c5557cSMahmoud Mandour { 103e2c5557cSMahmoud Mandour g_assert((num & (num - 1)) == 0); 104e2c5557cSMahmoud Mandour int ret = 0; 105e2c5557cSMahmoud Mandour while (num /= 2) { 106e2c5557cSMahmoud Mandour ret++; 107e2c5557cSMahmoud Mandour } 108e2c5557cSMahmoud Mandour return ret; 109e2c5557cSMahmoud Mandour } 110e2c5557cSMahmoud Mandour 111*40c4a553SMahmoud Mandour /* 112*40c4a553SMahmoud Mandour * LRU evection policy: For each set, a generation counter is maintained 113*40c4a553SMahmoud Mandour * alongside a priority array. 114*40c4a553SMahmoud Mandour * 115*40c4a553SMahmoud Mandour * On each set access, the generation counter is incremented. 116*40c4a553SMahmoud Mandour * 117*40c4a553SMahmoud Mandour * On a cache hit: The hit-block is assigned the current generation counter, 118*40c4a553SMahmoud Mandour * indicating that it is the most recently used block. 119*40c4a553SMahmoud Mandour * 120*40c4a553SMahmoud Mandour * On a cache miss: The block with the least priority is searched and replaced 121*40c4a553SMahmoud Mandour * with the newly-cached block, of which the priority is set to the current 122*40c4a553SMahmoud Mandour * generation number. 123*40c4a553SMahmoud Mandour */ 124*40c4a553SMahmoud Mandour 125*40c4a553SMahmoud Mandour static void lru_priorities_init(Cache *cache) 126*40c4a553SMahmoud Mandour { 127*40c4a553SMahmoud Mandour int i; 128*40c4a553SMahmoud Mandour 129*40c4a553SMahmoud Mandour for (i = 0; i < cache->num_sets; i++) { 130*40c4a553SMahmoud Mandour cache->sets[i].lru_priorities = g_new0(uint64_t, cache->assoc); 131*40c4a553SMahmoud Mandour cache->sets[i].lru_gen_counter = 0; 132*40c4a553SMahmoud Mandour } 133*40c4a553SMahmoud Mandour } 134*40c4a553SMahmoud Mandour 135*40c4a553SMahmoud Mandour static void lru_update_blk(Cache *cache, int set_idx, int blk_idx) 136*40c4a553SMahmoud Mandour { 137*40c4a553SMahmoud Mandour CacheSet *set = &cache->sets[set_idx]; 138*40c4a553SMahmoud Mandour set->lru_priorities[blk_idx] = cache->sets[set_idx].lru_gen_counter; 139*40c4a553SMahmoud Mandour set->lru_gen_counter++; 140*40c4a553SMahmoud Mandour } 141*40c4a553SMahmoud Mandour 142*40c4a553SMahmoud Mandour static int lru_get_lru_block(Cache *cache, int set_idx) 143*40c4a553SMahmoud Mandour { 144*40c4a553SMahmoud Mandour int i, min_idx, min_priority; 145*40c4a553SMahmoud Mandour 146*40c4a553SMahmoud Mandour min_priority = cache->sets[set_idx].lru_priorities[0]; 147*40c4a553SMahmoud Mandour min_idx = 0; 148*40c4a553SMahmoud Mandour 149*40c4a553SMahmoud Mandour for (i = 1; i < cache->assoc; i++) { 150*40c4a553SMahmoud Mandour if (cache->sets[set_idx].lru_priorities[i] < min_priority) { 151*40c4a553SMahmoud Mandour min_priority = cache->sets[set_idx].lru_priorities[i]; 152*40c4a553SMahmoud Mandour min_idx = i; 153*40c4a553SMahmoud Mandour } 154*40c4a553SMahmoud Mandour } 155*40c4a553SMahmoud Mandour return min_idx; 156*40c4a553SMahmoud Mandour } 157*40c4a553SMahmoud Mandour 158*40c4a553SMahmoud Mandour static void lru_priorities_destroy(Cache *cache) 159*40c4a553SMahmoud Mandour { 160*40c4a553SMahmoud Mandour int i; 161*40c4a553SMahmoud Mandour 162*40c4a553SMahmoud Mandour for (i = 0; i < cache->num_sets; i++) { 163*40c4a553SMahmoud Mandour g_free(cache->sets[i].lru_priorities); 164*40c4a553SMahmoud Mandour } 165*40c4a553SMahmoud Mandour } 166*40c4a553SMahmoud Mandour 167*40c4a553SMahmoud Mandour /* 168*40c4a553SMahmoud Mandour * FIFO eviction policy: a FIFO queue is maintained for each CacheSet that 169*40c4a553SMahmoud Mandour * stores accesses to the cache. 170*40c4a553SMahmoud Mandour * 171*40c4a553SMahmoud Mandour * On a compulsory miss: The block index is enqueued to the fifo_queue to 172*40c4a553SMahmoud Mandour * indicate that it's the latest cached block. 173*40c4a553SMahmoud Mandour * 174*40c4a553SMahmoud Mandour * On a conflict miss: The first-in block is removed from the cache and the new 175*40c4a553SMahmoud Mandour * block is put in its place and enqueued to the FIFO queue. 176*40c4a553SMahmoud Mandour */ 177*40c4a553SMahmoud Mandour 178*40c4a553SMahmoud Mandour static void fifo_init(Cache *cache) 179*40c4a553SMahmoud Mandour { 180*40c4a553SMahmoud Mandour int i; 181*40c4a553SMahmoud Mandour 182*40c4a553SMahmoud Mandour for (i = 0; i < cache->num_sets; i++) { 183*40c4a553SMahmoud Mandour cache->sets[i].fifo_queue = g_queue_new(); 184*40c4a553SMahmoud Mandour } 185*40c4a553SMahmoud Mandour } 186*40c4a553SMahmoud Mandour 187*40c4a553SMahmoud Mandour static int fifo_get_first_block(Cache *cache, int set) 188*40c4a553SMahmoud Mandour { 189*40c4a553SMahmoud Mandour GQueue *q = cache->sets[set].fifo_queue; 190*40c4a553SMahmoud Mandour return GPOINTER_TO_INT(g_queue_pop_tail(q)); 191*40c4a553SMahmoud Mandour } 192*40c4a553SMahmoud Mandour 193*40c4a553SMahmoud Mandour static void fifo_update_on_miss(Cache *cache, int set, int blk_idx) 194*40c4a553SMahmoud Mandour { 195*40c4a553SMahmoud Mandour GQueue *q = cache->sets[set].fifo_queue; 196*40c4a553SMahmoud Mandour g_queue_push_head(q, GINT_TO_POINTER(blk_idx)); 197*40c4a553SMahmoud Mandour } 198*40c4a553SMahmoud Mandour 199*40c4a553SMahmoud Mandour static void fifo_destroy(Cache *cache) 200*40c4a553SMahmoud Mandour { 201*40c4a553SMahmoud Mandour int i; 202*40c4a553SMahmoud Mandour 203*40c4a553SMahmoud Mandour for (i = 0; i < cache->assoc; i++) { 204*40c4a553SMahmoud Mandour g_queue_free(cache->sets[i].fifo_queue); 205*40c4a553SMahmoud Mandour } 206*40c4a553SMahmoud Mandour } 207*40c4a553SMahmoud Mandour 208e2c5557cSMahmoud Mandour static inline uint64_t extract_tag(Cache *cache, uint64_t addr) 209e2c5557cSMahmoud Mandour { 210e2c5557cSMahmoud Mandour return addr & cache->tag_mask; 211e2c5557cSMahmoud Mandour } 212e2c5557cSMahmoud Mandour 213e2c5557cSMahmoud Mandour static inline uint64_t extract_set(Cache *cache, uint64_t addr) 214e2c5557cSMahmoud Mandour { 215e2c5557cSMahmoud Mandour return (addr & cache->set_mask) >> cache->blksize_shift; 216e2c5557cSMahmoud Mandour } 217e2c5557cSMahmoud Mandour 21886ae3a1dSMahmoud Mandour static const char *cache_config_error(int blksize, int assoc, int cachesize) 21986ae3a1dSMahmoud Mandour { 22086ae3a1dSMahmoud Mandour if (cachesize % blksize != 0) { 22186ae3a1dSMahmoud Mandour return "cache size must be divisible by block size"; 22286ae3a1dSMahmoud Mandour } else if (cachesize % (blksize * assoc) != 0) { 22386ae3a1dSMahmoud Mandour return "cache size must be divisible by set size (assoc * block size)"; 22486ae3a1dSMahmoud Mandour } else { 22586ae3a1dSMahmoud Mandour return NULL; 22686ae3a1dSMahmoud Mandour } 22786ae3a1dSMahmoud Mandour } 22886ae3a1dSMahmoud Mandour 22986ae3a1dSMahmoud Mandour static bool bad_cache_params(int blksize, int assoc, int cachesize) 23086ae3a1dSMahmoud Mandour { 23186ae3a1dSMahmoud Mandour return (cachesize % blksize) != 0 || (cachesize % (blksize * assoc) != 0); 23286ae3a1dSMahmoud Mandour } 23386ae3a1dSMahmoud Mandour 234e2c5557cSMahmoud Mandour static Cache *cache_init(int blksize, int assoc, int cachesize) 235e2c5557cSMahmoud Mandour { 23686ae3a1dSMahmoud Mandour if (bad_cache_params(blksize, assoc, cachesize)) { 23786ae3a1dSMahmoud Mandour return NULL; 23886ae3a1dSMahmoud Mandour } 23986ae3a1dSMahmoud Mandour 240e2c5557cSMahmoud Mandour Cache *cache; 241e2c5557cSMahmoud Mandour int i; 242e2c5557cSMahmoud Mandour uint64_t blk_mask; 243e2c5557cSMahmoud Mandour 244e2c5557cSMahmoud Mandour cache = g_new(Cache, 1); 245e2c5557cSMahmoud Mandour cache->assoc = assoc; 246e2c5557cSMahmoud Mandour cache->cachesize = cachesize; 247e2c5557cSMahmoud Mandour cache->num_sets = cachesize / (blksize * assoc); 248e2c5557cSMahmoud Mandour cache->sets = g_new(CacheSet, cache->num_sets); 249e2c5557cSMahmoud Mandour cache->blksize_shift = pow_of_two(blksize); 250e2c5557cSMahmoud Mandour 251e2c5557cSMahmoud Mandour for (i = 0; i < cache->num_sets; i++) { 252e2c5557cSMahmoud Mandour cache->sets[i].blocks = g_new0(CacheBlock, assoc); 253e2c5557cSMahmoud Mandour } 254e2c5557cSMahmoud Mandour 255e2c5557cSMahmoud Mandour blk_mask = blksize - 1; 256e2c5557cSMahmoud Mandour cache->set_mask = ((cache->num_sets - 1) << cache->blksize_shift); 257e2c5557cSMahmoud Mandour cache->tag_mask = ~(cache->set_mask | blk_mask); 258*40c4a553SMahmoud Mandour 259*40c4a553SMahmoud Mandour if (metadata_init) { 260*40c4a553SMahmoud Mandour metadata_init(cache); 261*40c4a553SMahmoud Mandour } 262*40c4a553SMahmoud Mandour 263e2c5557cSMahmoud Mandour return cache; 264e2c5557cSMahmoud Mandour } 265e2c5557cSMahmoud Mandour 266e2c5557cSMahmoud Mandour static int get_invalid_block(Cache *cache, uint64_t set) 267e2c5557cSMahmoud Mandour { 268e2c5557cSMahmoud Mandour int i; 269e2c5557cSMahmoud Mandour 270e2c5557cSMahmoud Mandour for (i = 0; i < cache->assoc; i++) { 271e2c5557cSMahmoud Mandour if (!cache->sets[set].blocks[i].valid) { 272e2c5557cSMahmoud Mandour return i; 273e2c5557cSMahmoud Mandour } 274e2c5557cSMahmoud Mandour } 275e2c5557cSMahmoud Mandour 276e2c5557cSMahmoud Mandour return -1; 277e2c5557cSMahmoud Mandour } 278e2c5557cSMahmoud Mandour 279*40c4a553SMahmoud Mandour static int get_replaced_block(Cache *cache, int set) 280e2c5557cSMahmoud Mandour { 281*40c4a553SMahmoud Mandour switch (policy) { 282*40c4a553SMahmoud Mandour case RAND: 283e2c5557cSMahmoud Mandour return g_rand_int_range(rng, 0, cache->assoc); 284*40c4a553SMahmoud Mandour case LRU: 285*40c4a553SMahmoud Mandour return lru_get_lru_block(cache, set); 286*40c4a553SMahmoud Mandour case FIFO: 287*40c4a553SMahmoud Mandour return fifo_get_first_block(cache, set); 288*40c4a553SMahmoud Mandour default: 289*40c4a553SMahmoud Mandour g_assert_not_reached(); 290*40c4a553SMahmoud Mandour } 291e2c5557cSMahmoud Mandour } 292e2c5557cSMahmoud Mandour 293*40c4a553SMahmoud Mandour static int in_cache(Cache *cache, uint64_t addr) 294e2c5557cSMahmoud Mandour { 295e2c5557cSMahmoud Mandour int i; 296e2c5557cSMahmoud Mandour uint64_t tag, set; 297e2c5557cSMahmoud Mandour 298e2c5557cSMahmoud Mandour tag = extract_tag(cache, addr); 299e2c5557cSMahmoud Mandour set = extract_set(cache, addr); 300e2c5557cSMahmoud Mandour 301e2c5557cSMahmoud Mandour for (i = 0; i < cache->assoc; i++) { 302e2c5557cSMahmoud Mandour if (cache->sets[set].blocks[i].tag == tag && 303e2c5557cSMahmoud Mandour cache->sets[set].blocks[i].valid) { 304*40c4a553SMahmoud Mandour return i; 305e2c5557cSMahmoud Mandour } 306e2c5557cSMahmoud Mandour } 307e2c5557cSMahmoud Mandour 308*40c4a553SMahmoud Mandour return -1; 309e2c5557cSMahmoud Mandour } 310e2c5557cSMahmoud Mandour 311e2c5557cSMahmoud Mandour /** 312e2c5557cSMahmoud Mandour * access_cache(): Simulate a cache access 313e2c5557cSMahmoud Mandour * @cache: The cache under simulation 314e2c5557cSMahmoud Mandour * @addr: The address of the requested memory location 315e2c5557cSMahmoud Mandour * 316e2c5557cSMahmoud Mandour * Returns true if the requsted data is hit in the cache and false when missed. 317e2c5557cSMahmoud Mandour * The cache is updated on miss for the next access. 318e2c5557cSMahmoud Mandour */ 319e2c5557cSMahmoud Mandour static bool access_cache(Cache *cache, uint64_t addr) 320e2c5557cSMahmoud Mandour { 321*40c4a553SMahmoud Mandour int hit_blk, replaced_blk; 322e2c5557cSMahmoud Mandour uint64_t tag, set; 323e2c5557cSMahmoud Mandour 324e2c5557cSMahmoud Mandour tag = extract_tag(cache, addr); 325e2c5557cSMahmoud Mandour set = extract_set(cache, addr); 326e2c5557cSMahmoud Mandour 327*40c4a553SMahmoud Mandour hit_blk = in_cache(cache, addr); 328*40c4a553SMahmoud Mandour if (hit_blk != -1) { 329*40c4a553SMahmoud Mandour if (update_hit) { 330*40c4a553SMahmoud Mandour update_hit(cache, set, hit_blk); 331*40c4a553SMahmoud Mandour } 332*40c4a553SMahmoud Mandour return true; 333*40c4a553SMahmoud Mandour } 334*40c4a553SMahmoud Mandour 335e2c5557cSMahmoud Mandour replaced_blk = get_invalid_block(cache, set); 336e2c5557cSMahmoud Mandour 337e2c5557cSMahmoud Mandour if (replaced_blk == -1) { 338*40c4a553SMahmoud Mandour replaced_blk = get_replaced_block(cache, set); 339*40c4a553SMahmoud Mandour } 340*40c4a553SMahmoud Mandour 341*40c4a553SMahmoud Mandour if (update_miss) { 342*40c4a553SMahmoud Mandour update_miss(cache, set, replaced_blk); 343e2c5557cSMahmoud Mandour } 344e2c5557cSMahmoud Mandour 345e2c5557cSMahmoud Mandour cache->sets[set].blocks[replaced_blk].tag = tag; 346e2c5557cSMahmoud Mandour cache->sets[set].blocks[replaced_blk].valid = true; 347e2c5557cSMahmoud Mandour 348e2c5557cSMahmoud Mandour return false; 349e2c5557cSMahmoud Mandour } 350e2c5557cSMahmoud Mandour 351e2c5557cSMahmoud Mandour static void vcpu_mem_access(unsigned int vcpu_index, qemu_plugin_meminfo_t info, 352e2c5557cSMahmoud Mandour uint64_t vaddr, void *userdata) 353e2c5557cSMahmoud Mandour { 354e2c5557cSMahmoud Mandour uint64_t effective_addr; 355e2c5557cSMahmoud Mandour struct qemu_plugin_hwaddr *hwaddr; 356e2c5557cSMahmoud Mandour InsnData *insn; 357e2c5557cSMahmoud Mandour 358e2c5557cSMahmoud Mandour g_mutex_lock(&mtx); 359e2c5557cSMahmoud Mandour hwaddr = qemu_plugin_get_hwaddr(info, vaddr); 360e2c5557cSMahmoud Mandour if (hwaddr && qemu_plugin_hwaddr_is_io(hwaddr)) { 361e2c5557cSMahmoud Mandour g_mutex_unlock(&mtx); 362e2c5557cSMahmoud Mandour return; 363e2c5557cSMahmoud Mandour } 364e2c5557cSMahmoud Mandour 365e2c5557cSMahmoud Mandour effective_addr = hwaddr ? qemu_plugin_hwaddr_phys_addr(hwaddr) : vaddr; 366e2c5557cSMahmoud Mandour 367e2c5557cSMahmoud Mandour if (!access_cache(dcache, effective_addr)) { 368e2c5557cSMahmoud Mandour insn = (InsnData *) userdata; 369e2c5557cSMahmoud Mandour insn->dmisses++; 370e2c5557cSMahmoud Mandour dmisses++; 371e2c5557cSMahmoud Mandour } 372e2c5557cSMahmoud Mandour dmem_accesses++; 373e2c5557cSMahmoud Mandour g_mutex_unlock(&mtx); 374e2c5557cSMahmoud Mandour } 375e2c5557cSMahmoud Mandour 376e2c5557cSMahmoud Mandour static void vcpu_insn_exec(unsigned int vcpu_index, void *userdata) 377e2c5557cSMahmoud Mandour { 378e2c5557cSMahmoud Mandour uint64_t insn_addr; 379e2c5557cSMahmoud Mandour InsnData *insn; 380e2c5557cSMahmoud Mandour 381e2c5557cSMahmoud Mandour g_mutex_lock(&mtx); 382e2c5557cSMahmoud Mandour insn_addr = ((InsnData *) userdata)->addr; 383e2c5557cSMahmoud Mandour 384e2c5557cSMahmoud Mandour if (!access_cache(icache, insn_addr)) { 385e2c5557cSMahmoud Mandour insn = (InsnData *) userdata; 386e2c5557cSMahmoud Mandour insn->imisses++; 387e2c5557cSMahmoud Mandour imisses++; 388e2c5557cSMahmoud Mandour } 389e2c5557cSMahmoud Mandour imem_accesses++; 390e2c5557cSMahmoud Mandour g_mutex_unlock(&mtx); 391e2c5557cSMahmoud Mandour } 392e2c5557cSMahmoud Mandour 393e2c5557cSMahmoud Mandour static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) 394e2c5557cSMahmoud Mandour { 395e2c5557cSMahmoud Mandour size_t n_insns; 396e2c5557cSMahmoud Mandour size_t i; 397e2c5557cSMahmoud Mandour InsnData *data; 398e2c5557cSMahmoud Mandour 399e2c5557cSMahmoud Mandour n_insns = qemu_plugin_tb_n_insns(tb); 400e2c5557cSMahmoud Mandour for (i = 0; i < n_insns; i++) { 401e2c5557cSMahmoud Mandour struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); 402e2c5557cSMahmoud Mandour uint64_t effective_addr; 403e2c5557cSMahmoud Mandour 404e2c5557cSMahmoud Mandour if (sys) { 405e2c5557cSMahmoud Mandour effective_addr = (uint64_t) qemu_plugin_insn_haddr(insn); 406e2c5557cSMahmoud Mandour } else { 407e2c5557cSMahmoud Mandour effective_addr = (uint64_t) qemu_plugin_insn_vaddr(insn); 408e2c5557cSMahmoud Mandour } 409e2c5557cSMahmoud Mandour 410e2c5557cSMahmoud Mandour /* 411e2c5557cSMahmoud Mandour * Instructions might get translated multiple times, we do not create 412e2c5557cSMahmoud Mandour * new entries for those instructions. Instead, we fetch the same 413e2c5557cSMahmoud Mandour * entry from the hash table and register it for the callback again. 414e2c5557cSMahmoud Mandour */ 415e2c5557cSMahmoud Mandour g_mutex_lock(&mtx); 416e2c5557cSMahmoud Mandour data = g_hash_table_lookup(miss_ht, GUINT_TO_POINTER(effective_addr)); 417e2c5557cSMahmoud Mandour if (data == NULL) { 418e2c5557cSMahmoud Mandour data = g_new0(InsnData, 1); 419e2c5557cSMahmoud Mandour data->disas_str = qemu_plugin_insn_disas(insn); 420e2c5557cSMahmoud Mandour data->symbol = qemu_plugin_insn_symbol(insn); 421e2c5557cSMahmoud Mandour data->addr = effective_addr; 422e2c5557cSMahmoud Mandour g_hash_table_insert(miss_ht, GUINT_TO_POINTER(effective_addr), 423e2c5557cSMahmoud Mandour (gpointer) data); 424e2c5557cSMahmoud Mandour } 425e2c5557cSMahmoud Mandour g_mutex_unlock(&mtx); 426e2c5557cSMahmoud Mandour 427e2c5557cSMahmoud Mandour qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem_access, 428e2c5557cSMahmoud Mandour QEMU_PLUGIN_CB_NO_REGS, 429e2c5557cSMahmoud Mandour rw, data); 430e2c5557cSMahmoud Mandour 431e2c5557cSMahmoud Mandour qemu_plugin_register_vcpu_insn_exec_cb(insn, vcpu_insn_exec, 432e2c5557cSMahmoud Mandour QEMU_PLUGIN_CB_NO_REGS, data); 433e2c5557cSMahmoud Mandour } 434e2c5557cSMahmoud Mandour } 435e2c5557cSMahmoud Mandour 436e2c5557cSMahmoud Mandour static void insn_free(gpointer data) 437e2c5557cSMahmoud Mandour { 438e2c5557cSMahmoud Mandour InsnData *insn = (InsnData *) data; 439e2c5557cSMahmoud Mandour g_free(insn->disas_str); 440e2c5557cSMahmoud Mandour g_free(insn); 441e2c5557cSMahmoud Mandour } 442e2c5557cSMahmoud Mandour 443e2c5557cSMahmoud Mandour static void cache_free(Cache *cache) 444e2c5557cSMahmoud Mandour { 445e2c5557cSMahmoud Mandour for (int i = 0; i < cache->num_sets; i++) { 446e2c5557cSMahmoud Mandour g_free(cache->sets[i].blocks); 447e2c5557cSMahmoud Mandour } 448e2c5557cSMahmoud Mandour 449*40c4a553SMahmoud Mandour if (metadata_destroy) { 450*40c4a553SMahmoud Mandour metadata_destroy(cache); 451*40c4a553SMahmoud Mandour } 452*40c4a553SMahmoud Mandour 453e2c5557cSMahmoud Mandour g_free(cache->sets); 454e2c5557cSMahmoud Mandour g_free(cache); 455e2c5557cSMahmoud Mandour } 456e2c5557cSMahmoud Mandour 457e2c5557cSMahmoud Mandour static int dcmp(gconstpointer a, gconstpointer b) 458e2c5557cSMahmoud Mandour { 459e2c5557cSMahmoud Mandour InsnData *insn_a = (InsnData *) a; 460e2c5557cSMahmoud Mandour InsnData *insn_b = (InsnData *) b; 461e2c5557cSMahmoud Mandour 462e2c5557cSMahmoud Mandour return insn_a->dmisses < insn_b->dmisses ? 1 : -1; 463e2c5557cSMahmoud Mandour } 464e2c5557cSMahmoud Mandour 465e2c5557cSMahmoud Mandour static int icmp(gconstpointer a, gconstpointer b) 466e2c5557cSMahmoud Mandour { 467e2c5557cSMahmoud Mandour InsnData *insn_a = (InsnData *) a; 468e2c5557cSMahmoud Mandour InsnData *insn_b = (InsnData *) b; 469e2c5557cSMahmoud Mandour 470e2c5557cSMahmoud Mandour return insn_a->imisses < insn_b->imisses ? 1 : -1; 471e2c5557cSMahmoud Mandour } 472e2c5557cSMahmoud Mandour 473e2c5557cSMahmoud Mandour static void log_stats() 474e2c5557cSMahmoud Mandour { 475e2c5557cSMahmoud Mandour g_autoptr(GString) rep = g_string_new(""); 476e2c5557cSMahmoud Mandour g_string_append_printf(rep, 477e2c5557cSMahmoud Mandour "Data accesses: %lu, Misses: %lu\nMiss rate: %lf%%\n\n", 478e2c5557cSMahmoud Mandour dmem_accesses, 479e2c5557cSMahmoud Mandour dmisses, 480e2c5557cSMahmoud Mandour ((double) dmisses / (double) dmem_accesses) * 100.0); 481e2c5557cSMahmoud Mandour 482e2c5557cSMahmoud Mandour g_string_append_printf(rep, 483e2c5557cSMahmoud Mandour "Instruction accesses: %lu, Misses: %lu\nMiss rate: %lf%%\n\n", 484e2c5557cSMahmoud Mandour imem_accesses, 485e2c5557cSMahmoud Mandour imisses, 486e2c5557cSMahmoud Mandour ((double) imisses / (double) imem_accesses) * 100.0); 487e2c5557cSMahmoud Mandour 488e2c5557cSMahmoud Mandour qemu_plugin_outs(rep->str); 489e2c5557cSMahmoud Mandour } 490e2c5557cSMahmoud Mandour 491e2c5557cSMahmoud Mandour static void log_top_insns() 492e2c5557cSMahmoud Mandour { 493e2c5557cSMahmoud Mandour int i; 494e2c5557cSMahmoud Mandour GList *curr, *miss_insns; 495e2c5557cSMahmoud Mandour InsnData *insn; 496e2c5557cSMahmoud Mandour 497e2c5557cSMahmoud Mandour miss_insns = g_hash_table_get_values(miss_ht); 498e2c5557cSMahmoud Mandour miss_insns = g_list_sort(miss_insns, dcmp); 499e2c5557cSMahmoud Mandour g_autoptr(GString) rep = g_string_new(""); 500e2c5557cSMahmoud Mandour g_string_append_printf(rep, "%s", "address, data misses, instruction\n"); 501e2c5557cSMahmoud Mandour 502e2c5557cSMahmoud Mandour for (curr = miss_insns, i = 0; curr && i < limit; i++, curr = curr->next) { 503e2c5557cSMahmoud Mandour insn = (InsnData *) curr->data; 504e2c5557cSMahmoud Mandour g_string_append_printf(rep, "0x%" PRIx64, insn->addr); 505e2c5557cSMahmoud Mandour if (insn->symbol) { 506e2c5557cSMahmoud Mandour g_string_append_printf(rep, " (%s)", insn->symbol); 507e2c5557cSMahmoud Mandour } 508e2c5557cSMahmoud Mandour g_string_append_printf(rep, ", %ld, %s\n", insn->dmisses, 509e2c5557cSMahmoud Mandour insn->disas_str); 510e2c5557cSMahmoud Mandour } 511e2c5557cSMahmoud Mandour 512e2c5557cSMahmoud Mandour miss_insns = g_list_sort(miss_insns, icmp); 513e2c5557cSMahmoud Mandour g_string_append_printf(rep, "%s", "\naddress, fetch misses, instruction\n"); 514e2c5557cSMahmoud Mandour 515e2c5557cSMahmoud Mandour for (curr = miss_insns, i = 0; curr && i < limit; i++, curr = curr->next) { 516e2c5557cSMahmoud Mandour insn = (InsnData *) curr->data; 517e2c5557cSMahmoud Mandour g_string_append_printf(rep, "0x%" PRIx64, insn->addr); 518e2c5557cSMahmoud Mandour if (insn->symbol) { 519e2c5557cSMahmoud Mandour g_string_append_printf(rep, " (%s)", insn->symbol); 520e2c5557cSMahmoud Mandour } 521e2c5557cSMahmoud Mandour g_string_append_printf(rep, ", %ld, %s\n", insn->imisses, 522e2c5557cSMahmoud Mandour insn->disas_str); 523e2c5557cSMahmoud Mandour } 524e2c5557cSMahmoud Mandour 525e2c5557cSMahmoud Mandour qemu_plugin_outs(rep->str); 526e2c5557cSMahmoud Mandour g_list_free(miss_insns); 527e2c5557cSMahmoud Mandour } 528e2c5557cSMahmoud Mandour 529e2c5557cSMahmoud Mandour static void plugin_exit(qemu_plugin_id_t id, void *p) 530e2c5557cSMahmoud Mandour { 531e2c5557cSMahmoud Mandour log_stats(); 532e2c5557cSMahmoud Mandour log_top_insns(); 533e2c5557cSMahmoud Mandour 534e2c5557cSMahmoud Mandour cache_free(dcache); 535e2c5557cSMahmoud Mandour cache_free(icache); 536e2c5557cSMahmoud Mandour 537e2c5557cSMahmoud Mandour g_hash_table_destroy(miss_ht); 538e2c5557cSMahmoud Mandour } 539e2c5557cSMahmoud Mandour 540*40c4a553SMahmoud Mandour static void policy_init() 541*40c4a553SMahmoud Mandour { 542*40c4a553SMahmoud Mandour switch (policy) { 543*40c4a553SMahmoud Mandour case LRU: 544*40c4a553SMahmoud Mandour update_hit = lru_update_blk; 545*40c4a553SMahmoud Mandour update_miss = lru_update_blk; 546*40c4a553SMahmoud Mandour metadata_init = lru_priorities_init; 547*40c4a553SMahmoud Mandour metadata_destroy = lru_priorities_destroy; 548*40c4a553SMahmoud Mandour break; 549*40c4a553SMahmoud Mandour case FIFO: 550*40c4a553SMahmoud Mandour update_miss = fifo_update_on_miss; 551*40c4a553SMahmoud Mandour metadata_init = fifo_init; 552*40c4a553SMahmoud Mandour metadata_destroy = fifo_destroy; 553*40c4a553SMahmoud Mandour break; 554*40c4a553SMahmoud Mandour case RAND: 555*40c4a553SMahmoud Mandour rng = g_rand_new(); 556*40c4a553SMahmoud Mandour break; 557*40c4a553SMahmoud Mandour default: 558*40c4a553SMahmoud Mandour g_assert_not_reached(); 559*40c4a553SMahmoud Mandour } 560*40c4a553SMahmoud Mandour } 561*40c4a553SMahmoud Mandour 562e2c5557cSMahmoud Mandour QEMU_PLUGIN_EXPORT 563e2c5557cSMahmoud Mandour int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, 564e2c5557cSMahmoud Mandour int argc, char **argv) 565e2c5557cSMahmoud Mandour { 566e2c5557cSMahmoud Mandour int i; 567e2c5557cSMahmoud Mandour int iassoc, iblksize, icachesize; 568e2c5557cSMahmoud Mandour int dassoc, dblksize, dcachesize; 569e2c5557cSMahmoud Mandour 570e2c5557cSMahmoud Mandour limit = 32; 571e2c5557cSMahmoud Mandour sys = info->system_emulation; 572e2c5557cSMahmoud Mandour 573e2c5557cSMahmoud Mandour dassoc = 8; 574e2c5557cSMahmoud Mandour dblksize = 64; 575e2c5557cSMahmoud Mandour dcachesize = dblksize * dassoc * 32; 576e2c5557cSMahmoud Mandour 577e2c5557cSMahmoud Mandour iassoc = 8; 578e2c5557cSMahmoud Mandour iblksize = 64; 579e2c5557cSMahmoud Mandour icachesize = iblksize * iassoc * 32; 580e2c5557cSMahmoud Mandour 581*40c4a553SMahmoud Mandour policy = LRU; 582e2c5557cSMahmoud Mandour 583e2c5557cSMahmoud Mandour for (i = 0; i < argc; i++) { 584e2c5557cSMahmoud Mandour char *opt = argv[i]; 58586ae3a1dSMahmoud Mandour if (g_str_has_prefix(opt, "iblksize=")) { 58686ae3a1dSMahmoud Mandour iblksize = g_ascii_strtoll(opt + 9, NULL, 10); 58786ae3a1dSMahmoud Mandour } else if (g_str_has_prefix(opt, "iassoc=")) { 58886ae3a1dSMahmoud Mandour iassoc = g_ascii_strtoll(opt + 7, NULL, 10); 58986ae3a1dSMahmoud Mandour } else if (g_str_has_prefix(opt, "icachesize=")) { 59086ae3a1dSMahmoud Mandour icachesize = g_ascii_strtoll(opt + 11, NULL, 10); 59186ae3a1dSMahmoud Mandour } else if (g_str_has_prefix(opt, "dblksize=")) { 59286ae3a1dSMahmoud Mandour dblksize = g_ascii_strtoll(opt + 9, NULL, 10); 59386ae3a1dSMahmoud Mandour } else if (g_str_has_prefix(opt, "dassoc=")) { 59486ae3a1dSMahmoud Mandour dassoc = g_ascii_strtoll(opt + 7, NULL, 10); 59586ae3a1dSMahmoud Mandour } else if (g_str_has_prefix(opt, "dcachesize=")) { 59686ae3a1dSMahmoud Mandour dcachesize = g_ascii_strtoll(opt + 11, NULL, 10); 59786ae3a1dSMahmoud Mandour } else if (g_str_has_prefix(opt, "limit=")) { 598e2c5557cSMahmoud Mandour limit = g_ascii_strtoll(opt + 6, NULL, 10); 599*40c4a553SMahmoud Mandour } else if (g_str_has_prefix(opt, "evict=")) { 600*40c4a553SMahmoud Mandour gchar *p = opt + 6; 601*40c4a553SMahmoud Mandour if (g_strcmp0(p, "rand") == 0) { 602*40c4a553SMahmoud Mandour policy = RAND; 603*40c4a553SMahmoud Mandour } else if (g_strcmp0(p, "lru") == 0) { 604*40c4a553SMahmoud Mandour policy = LRU; 605*40c4a553SMahmoud Mandour } else if (g_strcmp0(p, "fifo") == 0) { 606*40c4a553SMahmoud Mandour policy = FIFO; 607*40c4a553SMahmoud Mandour } else { 608*40c4a553SMahmoud Mandour fprintf(stderr, "invalid eviction policy: %s\n", opt); 609*40c4a553SMahmoud Mandour return -1; 610*40c4a553SMahmoud Mandour } 611e2c5557cSMahmoud Mandour } else { 612e2c5557cSMahmoud Mandour fprintf(stderr, "option parsing failed: %s\n", opt); 613e2c5557cSMahmoud Mandour return -1; 614e2c5557cSMahmoud Mandour } 615e2c5557cSMahmoud Mandour } 616e2c5557cSMahmoud Mandour 617*40c4a553SMahmoud Mandour policy_init(); 618*40c4a553SMahmoud Mandour 619e2c5557cSMahmoud Mandour dcache = cache_init(dblksize, dassoc, dcachesize); 62086ae3a1dSMahmoud Mandour if (!dcache) { 62186ae3a1dSMahmoud Mandour const char *err = cache_config_error(dblksize, dassoc, dcachesize); 62286ae3a1dSMahmoud Mandour fprintf(stderr, "dcache cannot be constructed from given parameters\n"); 62386ae3a1dSMahmoud Mandour fprintf(stderr, "%s\n", err); 62486ae3a1dSMahmoud Mandour return -1; 62586ae3a1dSMahmoud Mandour } 62686ae3a1dSMahmoud Mandour 627e2c5557cSMahmoud Mandour icache = cache_init(iblksize, iassoc, icachesize); 62886ae3a1dSMahmoud Mandour if (!icache) { 62986ae3a1dSMahmoud Mandour const char *err = cache_config_error(iblksize, iassoc, icachesize); 63086ae3a1dSMahmoud Mandour fprintf(stderr, "icache cannot be constructed from given parameters\n"); 63186ae3a1dSMahmoud Mandour fprintf(stderr, "%s\n", err); 63286ae3a1dSMahmoud Mandour return -1; 63386ae3a1dSMahmoud Mandour } 634e2c5557cSMahmoud Mandour 635e2c5557cSMahmoud Mandour qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); 636e2c5557cSMahmoud Mandour qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); 637e2c5557cSMahmoud Mandour 638e2c5557cSMahmoud Mandour miss_ht = g_hash_table_new_full(NULL, g_direct_equal, NULL, insn_free); 639e2c5557cSMahmoud Mandour 640e2c5557cSMahmoud Mandour return 0; 641e2c5557cSMahmoud Mandour } 642