drcovlib_status_t drcovlib_init(drcovlib_options_t *ops) { int count = dr_atomic_add32_return_sum(&drcovlib_init_count, 1); if (count > 1) return DRCOVLIB_SUCCESS; if (ops->struct_size != sizeof(options)) return DRCOVLIB_ERROR_INVALID_PARAMETER; if ((ops->flags & (~(DRCOVLIB_DUMP_AS_TEXT|DRCOVLIB_THREAD_PRIVATE))) != 0) return DRCOVLIB_ERROR_INVALID_PARAMETER; if (TEST(DRCOVLIB_THREAD_PRIVATE, ops->flags)) { if (!dr_using_all_private_caches()) return DRCOVLIB_ERROR_INVALID_SETUP; drcov_per_thread = true; } options = *ops; if (options.logdir != NULL) dr_snprintf(logdir, BUFFER_SIZE_ELEMENTS(logdir), "%s", ops->logdir); else /* default */ dr_snprintf(logdir, BUFFER_SIZE_ELEMENTS(logdir), "."); NULL_TERMINATE_BUFFER(logdir); options.logdir = logdir; if (options.native_until_thread > 0) go_native = true; drmgr_init(); drx_init(); /* We follow a simple model of the caller requesting the coverage dump, * either via calling the exit routine, using its own soft_kills nudge, or * an explicit dump call for unusual cases. This means that drx's * soft_kills remains inside the outer later, i.e., the drcov client. This * is the easiest approach for coordinating soft_kills among many libraries. * Thus, we do *not* register for an exit event here. */ drmgr_register_thread_init_event(event_thread_init); drmgr_register_thread_exit_event(event_thread_exit); drmgr_register_bb_instrumentation_event(event_basic_block_analysis, NULL, NULL); dr_register_filter_syscall_event(event_filter_syscall); drmgr_register_pre_syscall_event(event_pre_syscall); #ifdef UNIX dr_register_fork_init_event(event_fork); #endif tls_idx = drmgr_register_tls_field(); if (tls_idx == -1) return DRCOVLIB_ERROR; return event_init(); }
static void insert_counter_update(void *drcontext, instrlist_t *bb, instr_t *where, int offset) { /* Since the inc instruction clobbers 5 of the arithmetic eflags, * we have to save them around the inc. We could be more efficient * by not bothering to save the overflow flag and constructing our * own sequence of instructions to save the other 5 flags (using * lahf). */ if (drreg_reserve_aflags(drcontext, bb, where) != DRREG_SUCCESS) { DR_ASSERT(false); /* cannot recover */ return; } /* Increment the global counter using the lock prefix to make it atomic * across threads. It would be cheaper to aggregate the thread counters * in the exit events, but this sample is intended to illustrate inserted * instrumentation. */ instrlist_meta_preinsert( bb, where, LOCK(INSTR_CREATE_inc( drcontext, OPND_CREATE_ABSMEM(((byte *)&global_count) + offset, OPSZ_4)))); /* Increment the thread private counter. */ if (dr_using_all_private_caches()) { per_thread_t *data = (per_thread_t *)drmgr_get_tls_field(drcontext, tls_idx); /* private caches - we can use an absolute address */ instrlist_meta_preinsert( bb, where, INSTR_CREATE_inc(drcontext, OPND_CREATE_ABSMEM(((byte *)&data) + offset, OPSZ_4))); } else { /* shared caches - we must indirect via thread local storage */ reg_id_t scratch; if (drreg_reserve_register(drcontext, bb, where, NULL, &scratch) != DRREG_SUCCESS) DR_ASSERT(false); drmgr_insert_read_tls_field(drcontext, tls_idx, bb, where, scratch); instrlist_meta_preinsert( bb, where, INSTR_CREATE_inc(drcontext, OPND_CREATE_MEM32(scratch, offset))); if (drreg_unreserve_register(drcontext, bb, where, scratch) != DRREG_SUCCESS) DR_ASSERT(false); } if (drreg_unreserve_aflags(drcontext, bb, where) != DRREG_SUCCESS) DR_ASSERT(false); /* cannot recover */ }
static void insert_counter_update(void *drcontext, instrlist_t *bb, instr_t *where, int offset) { /* Since the inc instruction clobbers 5 of the arithmetic eflags, * we have to save them around the inc. We could be more efficient * by not bothering to save the overflow flag and constructing our * own sequence of instructions to save the other 5 flags (using * lahf) or by doing a liveness analysis on the flags and saving * only if live. */ dr_save_reg(drcontext, bb, where, DR_REG_XAX, SPILL_SLOT_1); dr_save_arith_flags_to_xax(drcontext, bb, where); /* Increment the global counter using the lock prefix to make it atomic * across threads. It would be cheaper to aggregate the thread counters * in the exit events, but this sample is intended to illustrate inserted * instrumentation. */ instrlist_meta_preinsert(bb, where, LOCK(INSTR_CREATE_inc (drcontext, OPND_CREATE_ABSMEM(((byte *)&global_count) + offset, OPSZ_4)))); /* Increment the thread private counter. */ if (dr_using_all_private_caches()) { per_thread_t *data = (per_thread_t *) dr_get_tls_field(drcontext); /* private caches - we can use an absolute address */ instrlist_meta_preinsert(bb, where, INSTR_CREATE_inc(drcontext, OPND_CREATE_ABSMEM(((byte *)&data) + offset, OPSZ_4))); } else { /* shared caches - we must indirect via thread local storage */ /* We spill xbx to use a scratch register (we could do a liveness * analysis to try and find a dead register to use). Note that xax * is currently holding the saved eflags. */ dr_save_reg(drcontext, bb, where, DR_REG_XBX, SPILL_SLOT_2); dr_insert_read_tls_field(drcontext, bb, where, DR_REG_XBX); instrlist_meta_preinsert(bb, where, INSTR_CREATE_inc(drcontext, OPND_CREATE_MEM32(DR_REG_XBX, offset))); dr_restore_reg(drcontext, bb, where, DR_REG_XBX, SPILL_SLOT_2); } /* Restore flags and xax. */ dr_restore_arith_flags_from_xax(drcontext, bb, where); dr_restore_reg(drcontext, bb, where, DR_REG_XAX, SPILL_SLOT_1); }