1*227b45dcSIvanov Arkady /* 2*227b45dcSIvanov Arkady * Copyright (C) 2021, Ivanov Arkady <arkadiy.ivanov@ispras.ru> 3*227b45dcSIvanov Arkady * 4*227b45dcSIvanov Arkady * Drcov - a DynamoRIO-based tool that collects coverage information 5*227b45dcSIvanov Arkady * from a binary. Primary goal this script is to have coverage log 6*227b45dcSIvanov Arkady * files that work in Lighthouse. 7*227b45dcSIvanov Arkady * 8*227b45dcSIvanov Arkady * License: GNU GPL, version 2 or later. 9*227b45dcSIvanov Arkady * See the COPYING file in the top-level directory. 10*227b45dcSIvanov Arkady */ 11*227b45dcSIvanov Arkady 12*227b45dcSIvanov Arkady #include <inttypes.h> 13*227b45dcSIvanov Arkady #include <assert.h> 14*227b45dcSIvanov Arkady #include <stdlib.h> 15*227b45dcSIvanov Arkady #include <inttypes.h> 16*227b45dcSIvanov Arkady #include <string.h> 17*227b45dcSIvanov Arkady #include <unistd.h> 18*227b45dcSIvanov Arkady #include <stdio.h> 19*227b45dcSIvanov Arkady #include <glib.h> 20*227b45dcSIvanov Arkady 21*227b45dcSIvanov Arkady #include <qemu-plugin.h> 22*227b45dcSIvanov Arkady 23*227b45dcSIvanov Arkady QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; 24*227b45dcSIvanov Arkady 25*227b45dcSIvanov Arkady static char header[] = "DRCOV VERSION: 2\n" 26*227b45dcSIvanov Arkady "DRCOV FLAVOR: drcov-64\n" 27*227b45dcSIvanov Arkady "Module Table: version 2, count 1\n" 28*227b45dcSIvanov Arkady "Columns: id, base, end, entry, path\n"; 29*227b45dcSIvanov Arkady 30*227b45dcSIvanov Arkady static FILE *fp; 31*227b45dcSIvanov Arkady static const char *file_name = "file.drcov.trace"; 32*227b45dcSIvanov Arkady static GMutex lock; 33*227b45dcSIvanov Arkady 34*227b45dcSIvanov Arkady typedef struct { 35*227b45dcSIvanov Arkady uint32_t start; 36*227b45dcSIvanov Arkady uint16_t size; 37*227b45dcSIvanov Arkady uint16_t mod_id; 38*227b45dcSIvanov Arkady bool exec; 39*227b45dcSIvanov Arkady } bb_entry_t; 40*227b45dcSIvanov Arkady 41*227b45dcSIvanov Arkady /* Translated blocks */ 42*227b45dcSIvanov Arkady static GPtrArray *blocks; 43*227b45dcSIvanov Arkady 44*227b45dcSIvanov Arkady static void printf_header(unsigned long count) 45*227b45dcSIvanov Arkady { 46*227b45dcSIvanov Arkady fprintf(fp, "%s", header); 47*227b45dcSIvanov Arkady const char *path = qemu_plugin_path_to_binary(); 48*227b45dcSIvanov Arkady uint64_t start_code = qemu_plugin_start_code(); 49*227b45dcSIvanov Arkady uint64_t end_code = qemu_plugin_end_code(); 50*227b45dcSIvanov Arkady uint64_t entry = qemu_plugin_entry_code(); 51*227b45dcSIvanov Arkady fprintf(fp, "0, 0x%lx, 0x%lx, 0x%lx, %s\n", 52*227b45dcSIvanov Arkady start_code, end_code, entry, path); 53*227b45dcSIvanov Arkady fprintf(fp, "BB Table: %ld bbs\n", count); 54*227b45dcSIvanov Arkady } 55*227b45dcSIvanov Arkady 56*227b45dcSIvanov Arkady static void printf_char_array32(uint32_t data) 57*227b45dcSIvanov Arkady { 58*227b45dcSIvanov Arkady const uint8_t *bytes = (const uint8_t *)(&data); 59*227b45dcSIvanov Arkady fwrite(bytes, sizeof(char), sizeof(data), fp); 60*227b45dcSIvanov Arkady } 61*227b45dcSIvanov Arkady 62*227b45dcSIvanov Arkady static void printf_char_array16(uint16_t data) 63*227b45dcSIvanov Arkady { 64*227b45dcSIvanov Arkady const uint8_t *bytes = (const uint8_t *)(&data); 65*227b45dcSIvanov Arkady fwrite(bytes, sizeof(char), sizeof(data), fp); 66*227b45dcSIvanov Arkady } 67*227b45dcSIvanov Arkady 68*227b45dcSIvanov Arkady 69*227b45dcSIvanov Arkady static void printf_el(gpointer data, gpointer user_data) 70*227b45dcSIvanov Arkady { 71*227b45dcSIvanov Arkady bb_entry_t *bb = (bb_entry_t *)data; 72*227b45dcSIvanov Arkady if (bb->exec) { 73*227b45dcSIvanov Arkady printf_char_array32(bb->start); 74*227b45dcSIvanov Arkady printf_char_array16(bb->size); 75*227b45dcSIvanov Arkady printf_char_array16(bb->mod_id); 76*227b45dcSIvanov Arkady } 77*227b45dcSIvanov Arkady g_free(bb); 78*227b45dcSIvanov Arkady } 79*227b45dcSIvanov Arkady 80*227b45dcSIvanov Arkady static void count_block(gpointer data, gpointer user_data) 81*227b45dcSIvanov Arkady { 82*227b45dcSIvanov Arkady unsigned long *count = (unsigned long *) user_data; 83*227b45dcSIvanov Arkady bb_entry_t *bb = (bb_entry_t *)data; 84*227b45dcSIvanov Arkady if (bb->exec) { 85*227b45dcSIvanov Arkady *count = *count + 1; 86*227b45dcSIvanov Arkady } 87*227b45dcSIvanov Arkady } 88*227b45dcSIvanov Arkady 89*227b45dcSIvanov Arkady static void plugin_exit(qemu_plugin_id_t id, void *p) 90*227b45dcSIvanov Arkady { 91*227b45dcSIvanov Arkady unsigned long count = 0; 92*227b45dcSIvanov Arkady g_mutex_lock(&lock); 93*227b45dcSIvanov Arkady g_ptr_array_foreach(blocks, count_block, &count); 94*227b45dcSIvanov Arkady 95*227b45dcSIvanov Arkady /* Print function */ 96*227b45dcSIvanov Arkady printf_header(count); 97*227b45dcSIvanov Arkady g_ptr_array_foreach(blocks, printf_el, NULL); 98*227b45dcSIvanov Arkady 99*227b45dcSIvanov Arkady /* Clear */ 100*227b45dcSIvanov Arkady g_ptr_array_free(blocks, true); 101*227b45dcSIvanov Arkady 102*227b45dcSIvanov Arkady fclose(fp); 103*227b45dcSIvanov Arkady 104*227b45dcSIvanov Arkady g_mutex_unlock(&lock); 105*227b45dcSIvanov Arkady } 106*227b45dcSIvanov Arkady 107*227b45dcSIvanov Arkady static void plugin_init(void) 108*227b45dcSIvanov Arkady { 109*227b45dcSIvanov Arkady fp = fopen(file_name, "wb"); 110*227b45dcSIvanov Arkady blocks = g_ptr_array_sized_new(128); 111*227b45dcSIvanov Arkady } 112*227b45dcSIvanov Arkady 113*227b45dcSIvanov Arkady static void vcpu_tb_exec(unsigned int cpu_index, void *udata) 114*227b45dcSIvanov Arkady { 115*227b45dcSIvanov Arkady bb_entry_t *bb = (bb_entry_t *) udata; 116*227b45dcSIvanov Arkady 117*227b45dcSIvanov Arkady g_mutex_lock(&lock); 118*227b45dcSIvanov Arkady bb->exec = true; 119*227b45dcSIvanov Arkady g_mutex_unlock(&lock); 120*227b45dcSIvanov Arkady } 121*227b45dcSIvanov Arkady 122*227b45dcSIvanov Arkady static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) 123*227b45dcSIvanov Arkady { 124*227b45dcSIvanov Arkady uint64_t pc = qemu_plugin_tb_vaddr(tb); 125*227b45dcSIvanov Arkady size_t n = qemu_plugin_tb_n_insns(tb); 126*227b45dcSIvanov Arkady 127*227b45dcSIvanov Arkady g_mutex_lock(&lock); 128*227b45dcSIvanov Arkady 129*227b45dcSIvanov Arkady bb_entry_t *bb = g_new0(bb_entry_t, 1); 130*227b45dcSIvanov Arkady for (int i = 0; i < n; i++) { 131*227b45dcSIvanov Arkady bb->size += qemu_plugin_insn_size(qemu_plugin_tb_get_insn(tb, i)); 132*227b45dcSIvanov Arkady } 133*227b45dcSIvanov Arkady 134*227b45dcSIvanov Arkady bb->start = pc; 135*227b45dcSIvanov Arkady bb->mod_id = 0; 136*227b45dcSIvanov Arkady bb->exec = false; 137*227b45dcSIvanov Arkady g_ptr_array_add(blocks, bb); 138*227b45dcSIvanov Arkady 139*227b45dcSIvanov Arkady g_mutex_unlock(&lock); 140*227b45dcSIvanov Arkady qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec, 141*227b45dcSIvanov Arkady QEMU_PLUGIN_CB_NO_REGS, 142*227b45dcSIvanov Arkady (void *)bb); 143*227b45dcSIvanov Arkady 144*227b45dcSIvanov Arkady } 145*227b45dcSIvanov Arkady 146*227b45dcSIvanov Arkady QEMU_PLUGIN_EXPORT 147*227b45dcSIvanov Arkady int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, 148*227b45dcSIvanov Arkady int argc, char **argv) 149*227b45dcSIvanov Arkady { 150*227b45dcSIvanov Arkady for (int i = 0; i < argc; i++) { 151*227b45dcSIvanov Arkady g_autofree char **tokens = g_strsplit(argv[i], "=", 2); 152*227b45dcSIvanov Arkady if (g_strcmp0(tokens[0], "filename") == 0) { 153*227b45dcSIvanov Arkady file_name = g_strdup(tokens[1]); 154*227b45dcSIvanov Arkady } 155*227b45dcSIvanov Arkady } 156*227b45dcSIvanov Arkady 157*227b45dcSIvanov Arkady plugin_init(); 158*227b45dcSIvanov Arkady 159*227b45dcSIvanov Arkady qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); 160*227b45dcSIvanov Arkady qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); 161*227b45dcSIvanov Arkady 162*227b45dcSIvanov Arkady return 0; 163*227b45dcSIvanov Arkady } 164