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 3240c4a553SMahmoud Mandour enum EvictionPolicy { 3340c4a553SMahmoud Mandour LRU, 3440c4a553SMahmoud Mandour FIFO, 3540c4a553SMahmoud Mandour RAND, 3640c4a553SMahmoud Mandour }; 3740c4a553SMahmoud Mandour 3840c4a553SMahmoud Mandour enum EvictionPolicy policy; 3940c4a553SMahmoud 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. 5940c4a553SMahmoud Mandour * 6040c4a553SMahmoud 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; 7040c4a553SMahmoud Mandour uint64_t *lru_priorities; 7140c4a553SMahmoud Mandour uint64_t lru_gen_counter; 7240c4a553SMahmoud 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 9340c4a553SMahmoud Mandour void (*update_hit)(Cache *cache, int set, int blk); 9440c4a553SMahmoud Mandour void (*update_miss)(Cache *cache, int set, int blk); 9540c4a553SMahmoud Mandour 9640c4a553SMahmoud Mandour void (*metadata_init)(Cache *cache); 9740c4a553SMahmoud Mandour void (*metadata_destroy)(Cache *cache); 9840c4a553SMahmoud 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 11140c4a553SMahmoud Mandour /* 11240c4a553SMahmoud Mandour * LRU evection policy: For each set, a generation counter is maintained 11340c4a553SMahmoud Mandour * alongside a priority array. 11440c4a553SMahmoud Mandour * 11540c4a553SMahmoud Mandour * On each set access, the generation counter is incremented. 11640c4a553SMahmoud Mandour * 11740c4a553SMahmoud Mandour * On a cache hit: The hit-block is assigned the current generation counter, 11840c4a553SMahmoud Mandour * indicating that it is the most recently used block. 11940c4a553SMahmoud Mandour * 12040c4a553SMahmoud Mandour * On a cache miss: The block with the least priority is searched and replaced 12140c4a553SMahmoud Mandour * with the newly-cached block, of which the priority is set to the current 12240c4a553SMahmoud Mandour * generation number. 12340c4a553SMahmoud Mandour */ 12440c4a553SMahmoud Mandour 12540c4a553SMahmoud Mandour static void lru_priorities_init(Cache *cache) 12640c4a553SMahmoud Mandour { 12740c4a553SMahmoud Mandour int i; 12840c4a553SMahmoud Mandour 12940c4a553SMahmoud Mandour for (i = 0; i < cache->num_sets; i++) { 13040c4a553SMahmoud Mandour cache->sets[i].lru_priorities = g_new0(uint64_t, cache->assoc); 13140c4a553SMahmoud Mandour cache->sets[i].lru_gen_counter = 0; 13240c4a553SMahmoud Mandour } 13340c4a553SMahmoud Mandour } 13440c4a553SMahmoud Mandour 13540c4a553SMahmoud Mandour static void lru_update_blk(Cache *cache, int set_idx, int blk_idx) 13640c4a553SMahmoud Mandour { 13740c4a553SMahmoud Mandour CacheSet *set = &cache->sets[set_idx]; 13840c4a553SMahmoud Mandour set->lru_priorities[blk_idx] = cache->sets[set_idx].lru_gen_counter; 13940c4a553SMahmoud Mandour set->lru_gen_counter++; 14040c4a553SMahmoud Mandour } 14140c4a553SMahmoud Mandour 14240c4a553SMahmoud Mandour static int lru_get_lru_block(Cache *cache, int set_idx) 14340c4a553SMahmoud Mandour { 14440c4a553SMahmoud Mandour int i, min_idx, min_priority; 14540c4a553SMahmoud Mandour 14640c4a553SMahmoud Mandour min_priority = cache->sets[set_idx].lru_priorities[0]; 14740c4a553SMahmoud Mandour min_idx = 0; 14840c4a553SMahmoud Mandour 14940c4a553SMahmoud Mandour for (i = 1; i < cache->assoc; i++) { 15040c4a553SMahmoud Mandour if (cache->sets[set_idx].lru_priorities[i] < min_priority) { 15140c4a553SMahmoud Mandour min_priority = cache->sets[set_idx].lru_priorities[i]; 15240c4a553SMahmoud Mandour min_idx = i; 15340c4a553SMahmoud Mandour } 15440c4a553SMahmoud Mandour } 15540c4a553SMahmoud Mandour return min_idx; 15640c4a553SMahmoud Mandour } 15740c4a553SMahmoud Mandour 15840c4a553SMahmoud Mandour static void lru_priorities_destroy(Cache *cache) 15940c4a553SMahmoud Mandour { 16040c4a553SMahmoud Mandour int i; 16140c4a553SMahmoud Mandour 16240c4a553SMahmoud Mandour for (i = 0; i < cache->num_sets; i++) { 16340c4a553SMahmoud Mandour g_free(cache->sets[i].lru_priorities); 16440c4a553SMahmoud Mandour } 16540c4a553SMahmoud Mandour } 16640c4a553SMahmoud Mandour 16740c4a553SMahmoud Mandour /* 16840c4a553SMahmoud Mandour * FIFO eviction policy: a FIFO queue is maintained for each CacheSet that 16940c4a553SMahmoud Mandour * stores accesses to the cache. 17040c4a553SMahmoud Mandour * 17140c4a553SMahmoud Mandour * On a compulsory miss: The block index is enqueued to the fifo_queue to 17240c4a553SMahmoud Mandour * indicate that it's the latest cached block. 17340c4a553SMahmoud Mandour * 17440c4a553SMahmoud Mandour * On a conflict miss: The first-in block is removed from the cache and the new 17540c4a553SMahmoud Mandour * block is put in its place and enqueued to the FIFO queue. 17640c4a553SMahmoud Mandour */ 17740c4a553SMahmoud Mandour 17840c4a553SMahmoud Mandour static void fifo_init(Cache *cache) 17940c4a553SMahmoud Mandour { 18040c4a553SMahmoud Mandour int i; 18140c4a553SMahmoud Mandour 18240c4a553SMahmoud Mandour for (i = 0; i < cache->num_sets; i++) { 18340c4a553SMahmoud Mandour cache->sets[i].fifo_queue = g_queue_new(); 18440c4a553SMahmoud Mandour } 18540c4a553SMahmoud Mandour } 18640c4a553SMahmoud Mandour 18740c4a553SMahmoud Mandour static int fifo_get_first_block(Cache *cache, int set) 18840c4a553SMahmoud Mandour { 18940c4a553SMahmoud Mandour GQueue *q = cache->sets[set].fifo_queue; 19040c4a553SMahmoud Mandour return GPOINTER_TO_INT(g_queue_pop_tail(q)); 19140c4a553SMahmoud Mandour } 19240c4a553SMahmoud Mandour 19340c4a553SMahmoud Mandour static void fifo_update_on_miss(Cache *cache, int set, int blk_idx) 19440c4a553SMahmoud Mandour { 19540c4a553SMahmoud Mandour GQueue *q = cache->sets[set].fifo_queue; 19640c4a553SMahmoud Mandour g_queue_push_head(q, GINT_TO_POINTER(blk_idx)); 19740c4a553SMahmoud Mandour } 19840c4a553SMahmoud Mandour 19940c4a553SMahmoud Mandour static void fifo_destroy(Cache *cache) 20040c4a553SMahmoud Mandour { 20140c4a553SMahmoud Mandour int i; 20240c4a553SMahmoud Mandour 203de9fc40fSMahmoud Mandour for (i = 0; i < cache->num_sets; i++) { 20440c4a553SMahmoud Mandour g_queue_free(cache->sets[i].fifo_queue); 20540c4a553SMahmoud Mandour } 20640c4a553SMahmoud Mandour } 20740c4a553SMahmoud 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); 25840c4a553SMahmoud Mandour 25940c4a553SMahmoud Mandour if (metadata_init) { 26040c4a553SMahmoud Mandour metadata_init(cache); 26140c4a553SMahmoud Mandour } 26240c4a553SMahmoud 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 27940c4a553SMahmoud Mandour static int get_replaced_block(Cache *cache, int set) 280e2c5557cSMahmoud Mandour { 28140c4a553SMahmoud Mandour switch (policy) { 28240c4a553SMahmoud Mandour case RAND: 283e2c5557cSMahmoud Mandour return g_rand_int_range(rng, 0, cache->assoc); 28440c4a553SMahmoud Mandour case LRU: 28540c4a553SMahmoud Mandour return lru_get_lru_block(cache, set); 28640c4a553SMahmoud Mandour case FIFO: 28740c4a553SMahmoud Mandour return fifo_get_first_block(cache, set); 28840c4a553SMahmoud Mandour default: 28940c4a553SMahmoud Mandour g_assert_not_reached(); 29040c4a553SMahmoud Mandour } 291e2c5557cSMahmoud Mandour } 292e2c5557cSMahmoud Mandour 29340c4a553SMahmoud 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) { 30440c4a553SMahmoud Mandour return i; 305e2c5557cSMahmoud Mandour } 306e2c5557cSMahmoud Mandour } 307e2c5557cSMahmoud Mandour 30840c4a553SMahmoud 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 { 32140c4a553SMahmoud 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 32740c4a553SMahmoud Mandour hit_blk = in_cache(cache, addr); 32840c4a553SMahmoud Mandour if (hit_blk != -1) { 32940c4a553SMahmoud Mandour if (update_hit) { 33040c4a553SMahmoud Mandour update_hit(cache, set, hit_blk); 33140c4a553SMahmoud Mandour } 33240c4a553SMahmoud Mandour return true; 33340c4a553SMahmoud Mandour } 33440c4a553SMahmoud Mandour 335e2c5557cSMahmoud Mandour replaced_blk = get_invalid_block(cache, set); 336e2c5557cSMahmoud Mandour 337e2c5557cSMahmoud Mandour if (replaced_blk == -1) { 33840c4a553SMahmoud Mandour replaced_blk = get_replaced_block(cache, set); 33940c4a553SMahmoud Mandour } 34040c4a553SMahmoud Mandour 34140c4a553SMahmoud Mandour if (update_miss) { 34240c4a553SMahmoud 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 hwaddr = qemu_plugin_get_hwaddr(info, vaddr); 359e2c5557cSMahmoud Mandour if (hwaddr && qemu_plugin_hwaddr_is_io(hwaddr)) { 360e2c5557cSMahmoud Mandour return; 361e2c5557cSMahmoud Mandour } 362e2c5557cSMahmoud Mandour 363e2c5557cSMahmoud Mandour effective_addr = hwaddr ? qemu_plugin_hwaddr_phys_addr(hwaddr) : vaddr; 364e2c5557cSMahmoud Mandour 365072c444bSMahmoud Mandour g_mutex_lock(&mtx); 366e2c5557cSMahmoud Mandour if (!access_cache(dcache, effective_addr)) { 367e2c5557cSMahmoud Mandour insn = (InsnData *) userdata; 368e2c5557cSMahmoud Mandour insn->dmisses++; 369e2c5557cSMahmoud Mandour dmisses++; 370e2c5557cSMahmoud Mandour } 371e2c5557cSMahmoud Mandour dmem_accesses++; 372e2c5557cSMahmoud Mandour g_mutex_unlock(&mtx); 373e2c5557cSMahmoud Mandour } 374e2c5557cSMahmoud Mandour 375e2c5557cSMahmoud Mandour static void vcpu_insn_exec(unsigned int vcpu_index, void *userdata) 376e2c5557cSMahmoud Mandour { 377e2c5557cSMahmoud Mandour uint64_t insn_addr; 378e2c5557cSMahmoud Mandour InsnData *insn; 379e2c5557cSMahmoud Mandour 380e2c5557cSMahmoud Mandour g_mutex_lock(&mtx); 381e2c5557cSMahmoud Mandour insn_addr = ((InsnData *) userdata)->addr; 382e2c5557cSMahmoud Mandour 383e2c5557cSMahmoud Mandour if (!access_cache(icache, insn_addr)) { 384e2c5557cSMahmoud Mandour insn = (InsnData *) userdata; 385e2c5557cSMahmoud Mandour insn->imisses++; 386e2c5557cSMahmoud Mandour imisses++; 387e2c5557cSMahmoud Mandour } 388e2c5557cSMahmoud Mandour imem_accesses++; 389e2c5557cSMahmoud Mandour g_mutex_unlock(&mtx); 390e2c5557cSMahmoud Mandour } 391e2c5557cSMahmoud Mandour 392e2c5557cSMahmoud Mandour static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) 393e2c5557cSMahmoud Mandour { 394e2c5557cSMahmoud Mandour size_t n_insns; 395e2c5557cSMahmoud Mandour size_t i; 396e2c5557cSMahmoud Mandour InsnData *data; 397e2c5557cSMahmoud Mandour 398e2c5557cSMahmoud Mandour n_insns = qemu_plugin_tb_n_insns(tb); 399e2c5557cSMahmoud Mandour for (i = 0; i < n_insns; i++) { 400e2c5557cSMahmoud Mandour struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); 401e2c5557cSMahmoud Mandour uint64_t effective_addr; 402e2c5557cSMahmoud Mandour 403e2c5557cSMahmoud Mandour if (sys) { 404e2c5557cSMahmoud Mandour effective_addr = (uint64_t) qemu_plugin_insn_haddr(insn); 405e2c5557cSMahmoud Mandour } else { 406e2c5557cSMahmoud Mandour effective_addr = (uint64_t) qemu_plugin_insn_vaddr(insn); 407e2c5557cSMahmoud Mandour } 408e2c5557cSMahmoud Mandour 409e2c5557cSMahmoud Mandour /* 410e2c5557cSMahmoud Mandour * Instructions might get translated multiple times, we do not create 411e2c5557cSMahmoud Mandour * new entries for those instructions. Instead, we fetch the same 412e2c5557cSMahmoud Mandour * entry from the hash table and register it for the callback again. 413e2c5557cSMahmoud Mandour */ 414e2c5557cSMahmoud Mandour g_mutex_lock(&mtx); 415e2c5557cSMahmoud Mandour data = g_hash_table_lookup(miss_ht, GUINT_TO_POINTER(effective_addr)); 416e2c5557cSMahmoud Mandour if (data == NULL) { 417e2c5557cSMahmoud Mandour data = g_new0(InsnData, 1); 418e2c5557cSMahmoud Mandour data->disas_str = qemu_plugin_insn_disas(insn); 419e2c5557cSMahmoud Mandour data->symbol = qemu_plugin_insn_symbol(insn); 420e2c5557cSMahmoud Mandour data->addr = effective_addr; 421e2c5557cSMahmoud Mandour g_hash_table_insert(miss_ht, GUINT_TO_POINTER(effective_addr), 422e2c5557cSMahmoud Mandour (gpointer) data); 423e2c5557cSMahmoud Mandour } 424e2c5557cSMahmoud Mandour g_mutex_unlock(&mtx); 425e2c5557cSMahmoud Mandour 426e2c5557cSMahmoud Mandour qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem_access, 427e2c5557cSMahmoud Mandour QEMU_PLUGIN_CB_NO_REGS, 428e2c5557cSMahmoud Mandour rw, data); 429e2c5557cSMahmoud Mandour 430e2c5557cSMahmoud Mandour qemu_plugin_register_vcpu_insn_exec_cb(insn, vcpu_insn_exec, 431e2c5557cSMahmoud Mandour QEMU_PLUGIN_CB_NO_REGS, data); 432e2c5557cSMahmoud Mandour } 433e2c5557cSMahmoud Mandour } 434e2c5557cSMahmoud Mandour 435e2c5557cSMahmoud Mandour static void insn_free(gpointer data) 436e2c5557cSMahmoud Mandour { 437e2c5557cSMahmoud Mandour InsnData *insn = (InsnData *) data; 438e2c5557cSMahmoud Mandour g_free(insn->disas_str); 439e2c5557cSMahmoud Mandour g_free(insn); 440e2c5557cSMahmoud Mandour } 441e2c5557cSMahmoud Mandour 442e2c5557cSMahmoud Mandour static void cache_free(Cache *cache) 443e2c5557cSMahmoud Mandour { 444e2c5557cSMahmoud Mandour for (int i = 0; i < cache->num_sets; i++) { 445e2c5557cSMahmoud Mandour g_free(cache->sets[i].blocks); 446e2c5557cSMahmoud Mandour } 447e2c5557cSMahmoud Mandour 44840c4a553SMahmoud Mandour if (metadata_destroy) { 44940c4a553SMahmoud Mandour metadata_destroy(cache); 45040c4a553SMahmoud Mandour } 45140c4a553SMahmoud Mandour 452e2c5557cSMahmoud Mandour g_free(cache->sets); 453e2c5557cSMahmoud Mandour g_free(cache); 454e2c5557cSMahmoud Mandour } 455e2c5557cSMahmoud Mandour 456e2c5557cSMahmoud Mandour static int dcmp(gconstpointer a, gconstpointer b) 457e2c5557cSMahmoud Mandour { 458e2c5557cSMahmoud Mandour InsnData *insn_a = (InsnData *) a; 459e2c5557cSMahmoud Mandour InsnData *insn_b = (InsnData *) b; 460e2c5557cSMahmoud Mandour 461e2c5557cSMahmoud Mandour return insn_a->dmisses < insn_b->dmisses ? 1 : -1; 462e2c5557cSMahmoud Mandour } 463e2c5557cSMahmoud Mandour 464e2c5557cSMahmoud Mandour static int icmp(gconstpointer a, gconstpointer b) 465e2c5557cSMahmoud Mandour { 466e2c5557cSMahmoud Mandour InsnData *insn_a = (InsnData *) a; 467e2c5557cSMahmoud Mandour InsnData *insn_b = (InsnData *) b; 468e2c5557cSMahmoud Mandour 469e2c5557cSMahmoud Mandour return insn_a->imisses < insn_b->imisses ? 1 : -1; 470e2c5557cSMahmoud Mandour } 471e2c5557cSMahmoud Mandour 472*c2888a67SMahmoud Mandour static void log_stats(void) 473e2c5557cSMahmoud Mandour { 474e2c5557cSMahmoud Mandour g_autoptr(GString) rep = g_string_new(""); 475e2c5557cSMahmoud Mandour g_string_append_printf(rep, 476e2c5557cSMahmoud Mandour "Data accesses: %lu, Misses: %lu\nMiss rate: %lf%%\n\n", 477e2c5557cSMahmoud Mandour dmem_accesses, 478e2c5557cSMahmoud Mandour dmisses, 479e2c5557cSMahmoud Mandour ((double) dmisses / (double) dmem_accesses) * 100.0); 480e2c5557cSMahmoud Mandour 481e2c5557cSMahmoud Mandour g_string_append_printf(rep, 482e2c5557cSMahmoud Mandour "Instruction accesses: %lu, Misses: %lu\nMiss rate: %lf%%\n\n", 483e2c5557cSMahmoud Mandour imem_accesses, 484e2c5557cSMahmoud Mandour imisses, 485e2c5557cSMahmoud Mandour ((double) imisses / (double) imem_accesses) * 100.0); 486e2c5557cSMahmoud Mandour 487e2c5557cSMahmoud Mandour qemu_plugin_outs(rep->str); 488e2c5557cSMahmoud Mandour } 489e2c5557cSMahmoud Mandour 490*c2888a67SMahmoud Mandour static void log_top_insns(void) 491e2c5557cSMahmoud Mandour { 492e2c5557cSMahmoud Mandour int i; 493e2c5557cSMahmoud Mandour GList *curr, *miss_insns; 494e2c5557cSMahmoud Mandour InsnData *insn; 495e2c5557cSMahmoud Mandour 496e2c5557cSMahmoud Mandour miss_insns = g_hash_table_get_values(miss_ht); 497e2c5557cSMahmoud Mandour miss_insns = g_list_sort(miss_insns, dcmp); 498e2c5557cSMahmoud Mandour g_autoptr(GString) rep = g_string_new(""); 499e2c5557cSMahmoud Mandour g_string_append_printf(rep, "%s", "address, data misses, instruction\n"); 500e2c5557cSMahmoud Mandour 501e2c5557cSMahmoud Mandour for (curr = miss_insns, i = 0; curr && i < limit; i++, curr = curr->next) { 502e2c5557cSMahmoud Mandour insn = (InsnData *) curr->data; 503e2c5557cSMahmoud Mandour g_string_append_printf(rep, "0x%" PRIx64, insn->addr); 504e2c5557cSMahmoud Mandour if (insn->symbol) { 505e2c5557cSMahmoud Mandour g_string_append_printf(rep, " (%s)", insn->symbol); 506e2c5557cSMahmoud Mandour } 507e2c5557cSMahmoud Mandour g_string_append_printf(rep, ", %ld, %s\n", insn->dmisses, 508e2c5557cSMahmoud Mandour insn->disas_str); 509e2c5557cSMahmoud Mandour } 510e2c5557cSMahmoud Mandour 511e2c5557cSMahmoud Mandour miss_insns = g_list_sort(miss_insns, icmp); 512e2c5557cSMahmoud Mandour g_string_append_printf(rep, "%s", "\naddress, fetch misses, instruction\n"); 513e2c5557cSMahmoud Mandour 514e2c5557cSMahmoud Mandour for (curr = miss_insns, i = 0; curr && i < limit; i++, curr = curr->next) { 515e2c5557cSMahmoud Mandour insn = (InsnData *) curr->data; 516e2c5557cSMahmoud Mandour g_string_append_printf(rep, "0x%" PRIx64, insn->addr); 517e2c5557cSMahmoud Mandour if (insn->symbol) { 518e2c5557cSMahmoud Mandour g_string_append_printf(rep, " (%s)", insn->symbol); 519e2c5557cSMahmoud Mandour } 520e2c5557cSMahmoud Mandour g_string_append_printf(rep, ", %ld, %s\n", insn->imisses, 521e2c5557cSMahmoud Mandour insn->disas_str); 522e2c5557cSMahmoud Mandour } 523e2c5557cSMahmoud Mandour 524e2c5557cSMahmoud Mandour qemu_plugin_outs(rep->str); 525e2c5557cSMahmoud Mandour g_list_free(miss_insns); 526e2c5557cSMahmoud Mandour } 527e2c5557cSMahmoud Mandour 528e2c5557cSMahmoud Mandour static void plugin_exit(qemu_plugin_id_t id, void *p) 529e2c5557cSMahmoud Mandour { 530e2c5557cSMahmoud Mandour log_stats(); 531e2c5557cSMahmoud Mandour log_top_insns(); 532e2c5557cSMahmoud Mandour 533e2c5557cSMahmoud Mandour cache_free(dcache); 534e2c5557cSMahmoud Mandour cache_free(icache); 535e2c5557cSMahmoud Mandour 536e2c5557cSMahmoud Mandour g_hash_table_destroy(miss_ht); 537e2c5557cSMahmoud Mandour } 538e2c5557cSMahmoud Mandour 539*c2888a67SMahmoud Mandour static void policy_init(void) 54040c4a553SMahmoud Mandour { 54140c4a553SMahmoud Mandour switch (policy) { 54240c4a553SMahmoud Mandour case LRU: 54340c4a553SMahmoud Mandour update_hit = lru_update_blk; 54440c4a553SMahmoud Mandour update_miss = lru_update_blk; 54540c4a553SMahmoud Mandour metadata_init = lru_priorities_init; 54640c4a553SMahmoud Mandour metadata_destroy = lru_priorities_destroy; 54740c4a553SMahmoud Mandour break; 54840c4a553SMahmoud Mandour case FIFO: 54940c4a553SMahmoud Mandour update_miss = fifo_update_on_miss; 55040c4a553SMahmoud Mandour metadata_init = fifo_init; 55140c4a553SMahmoud Mandour metadata_destroy = fifo_destroy; 55240c4a553SMahmoud Mandour break; 55340c4a553SMahmoud Mandour case RAND: 55440c4a553SMahmoud Mandour rng = g_rand_new(); 55540c4a553SMahmoud Mandour break; 55640c4a553SMahmoud Mandour default: 55740c4a553SMahmoud Mandour g_assert_not_reached(); 55840c4a553SMahmoud Mandour } 55940c4a553SMahmoud Mandour } 56040c4a553SMahmoud Mandour 561e2c5557cSMahmoud Mandour QEMU_PLUGIN_EXPORT 562e2c5557cSMahmoud Mandour int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, 563e2c5557cSMahmoud Mandour int argc, char **argv) 564e2c5557cSMahmoud Mandour { 565e2c5557cSMahmoud Mandour int i; 566e2c5557cSMahmoud Mandour int iassoc, iblksize, icachesize; 567e2c5557cSMahmoud Mandour int dassoc, dblksize, dcachesize; 568e2c5557cSMahmoud Mandour 569e2c5557cSMahmoud Mandour limit = 32; 570e2c5557cSMahmoud Mandour sys = info->system_emulation; 571e2c5557cSMahmoud Mandour 572e2c5557cSMahmoud Mandour dassoc = 8; 573e2c5557cSMahmoud Mandour dblksize = 64; 574e2c5557cSMahmoud Mandour dcachesize = dblksize * dassoc * 32; 575e2c5557cSMahmoud Mandour 576e2c5557cSMahmoud Mandour iassoc = 8; 577e2c5557cSMahmoud Mandour iblksize = 64; 578e2c5557cSMahmoud Mandour icachesize = iblksize * iassoc * 32; 579e2c5557cSMahmoud Mandour 58040c4a553SMahmoud Mandour policy = LRU; 581e2c5557cSMahmoud Mandour 582e2c5557cSMahmoud Mandour for (i = 0; i < argc; i++) { 583e2c5557cSMahmoud Mandour char *opt = argv[i]; 58486ae3a1dSMahmoud Mandour if (g_str_has_prefix(opt, "iblksize=")) { 58586ae3a1dSMahmoud Mandour iblksize = g_ascii_strtoll(opt + 9, NULL, 10); 58686ae3a1dSMahmoud Mandour } else if (g_str_has_prefix(opt, "iassoc=")) { 58786ae3a1dSMahmoud Mandour iassoc = g_ascii_strtoll(opt + 7, NULL, 10); 58886ae3a1dSMahmoud Mandour } else if (g_str_has_prefix(opt, "icachesize=")) { 58986ae3a1dSMahmoud Mandour icachesize = g_ascii_strtoll(opt + 11, NULL, 10); 59086ae3a1dSMahmoud Mandour } else if (g_str_has_prefix(opt, "dblksize=")) { 59186ae3a1dSMahmoud Mandour dblksize = g_ascii_strtoll(opt + 9, NULL, 10); 59286ae3a1dSMahmoud Mandour } else if (g_str_has_prefix(opt, "dassoc=")) { 59386ae3a1dSMahmoud Mandour dassoc = g_ascii_strtoll(opt + 7, NULL, 10); 59486ae3a1dSMahmoud Mandour } else if (g_str_has_prefix(opt, "dcachesize=")) { 59586ae3a1dSMahmoud Mandour dcachesize = g_ascii_strtoll(opt + 11, NULL, 10); 59686ae3a1dSMahmoud Mandour } else if (g_str_has_prefix(opt, "limit=")) { 597e2c5557cSMahmoud Mandour limit = g_ascii_strtoll(opt + 6, NULL, 10); 59840c4a553SMahmoud Mandour } else if (g_str_has_prefix(opt, "evict=")) { 59940c4a553SMahmoud Mandour gchar *p = opt + 6; 60040c4a553SMahmoud Mandour if (g_strcmp0(p, "rand") == 0) { 60140c4a553SMahmoud Mandour policy = RAND; 60240c4a553SMahmoud Mandour } else if (g_strcmp0(p, "lru") == 0) { 60340c4a553SMahmoud Mandour policy = LRU; 60440c4a553SMahmoud Mandour } else if (g_strcmp0(p, "fifo") == 0) { 60540c4a553SMahmoud Mandour policy = FIFO; 60640c4a553SMahmoud Mandour } else { 60740c4a553SMahmoud Mandour fprintf(stderr, "invalid eviction policy: %s\n", opt); 60840c4a553SMahmoud Mandour return -1; 60940c4a553SMahmoud Mandour } 610e2c5557cSMahmoud Mandour } else { 611e2c5557cSMahmoud Mandour fprintf(stderr, "option parsing failed: %s\n", opt); 612e2c5557cSMahmoud Mandour return -1; 613e2c5557cSMahmoud Mandour } 614e2c5557cSMahmoud Mandour } 615e2c5557cSMahmoud Mandour 61640c4a553SMahmoud Mandour policy_init(); 61740c4a553SMahmoud Mandour 618e2c5557cSMahmoud Mandour dcache = cache_init(dblksize, dassoc, dcachesize); 61986ae3a1dSMahmoud Mandour if (!dcache) { 62086ae3a1dSMahmoud Mandour const char *err = cache_config_error(dblksize, dassoc, dcachesize); 62186ae3a1dSMahmoud Mandour fprintf(stderr, "dcache cannot be constructed from given parameters\n"); 62286ae3a1dSMahmoud Mandour fprintf(stderr, "%s\n", err); 62386ae3a1dSMahmoud Mandour return -1; 62486ae3a1dSMahmoud Mandour } 62586ae3a1dSMahmoud Mandour 626e2c5557cSMahmoud Mandour icache = cache_init(iblksize, iassoc, icachesize); 62786ae3a1dSMahmoud Mandour if (!icache) { 62886ae3a1dSMahmoud Mandour const char *err = cache_config_error(iblksize, iassoc, icachesize); 62986ae3a1dSMahmoud Mandour fprintf(stderr, "icache cannot be constructed from given parameters\n"); 63086ae3a1dSMahmoud Mandour fprintf(stderr, "%s\n", err); 63186ae3a1dSMahmoud Mandour return -1; 63286ae3a1dSMahmoud Mandour } 633e2c5557cSMahmoud Mandour 634e2c5557cSMahmoud Mandour qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); 635e2c5557cSMahmoud Mandour qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); 636e2c5557cSMahmoud Mandour 637e2c5557cSMahmoud Mandour miss_ht = g_hash_table_new_full(NULL, g_direct_equal, NULL, insn_free); 638e2c5557cSMahmoud Mandour 639e2c5557cSMahmoud Mandour return 0; 640e2c5557cSMahmoud Mandour } 641