static dr_emit_flags_t event_app_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t *instr, bool for_trace, bool translating, void *user_data) { /* We test reserving across app instrs by reserving on each store and * unreserving on the subsequent instr. */ drvector_t allowed; if (reg != DR_REG_NULL) { if (drreg_unreserve_register(drcontext, bb, instr, reg) != DRREG_SUCCESS) CHECK(false, "failed to unreserve"); reg = DR_REG_NULL; } if (!instr_is_app(instr)) return DR_EMIT_DEFAULT; if (!instr_writes_memory(instr)) return DR_EMIT_DEFAULT; if (!drmgr_is_last_instr(drcontext, instr)) { drreg_init_and_fill_vector(&allowed, true); /* Limit the registers for more of a stress test: */ drreg_set_vector_entry(&allowed, IF_X86_ELSE(DR_REG_XCX, DR_REG_R0), false); drreg_set_vector_entry(&allowed, IF_X86_ELSE(DR_REG_XDX, DR_REG_R1), false); if (drreg_reserve_register(drcontext, bb, instr, &allowed, ®) != DRREG_SUCCESS) DR_ASSERT(false); drvector_delete(&allowed); } return DR_EMIT_DEFAULT; }
static dr_emit_flags_t event_bb_insert(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst, bool for_trace, bool translating, void *user_data) { static int freq; reg_id_t reg1 = IF_X86_ELSE(DR_REG_XAX, DR_REG_R0); reg_id_t reg2 = IF_X86_ELSE(DR_REG_XCX, DR_REG_R1); CHECK(drmgr_is_first_instr(drcontext, instrlist_first_app(bb)), "first incorrect"); CHECK(!drmgr_is_first_instr(drcontext, instrlist_last(bb)) || instrlist_first_app(bb) == instrlist_last(bb), "first incorrect"); CHECK(drmgr_is_last_instr(drcontext, instrlist_last(bb)), "last incorrect"); CHECK(!drmgr_is_last_instr(drcontext, instrlist_first_app(bb)) || instrlist_first_app(bb) == instrlist_last(bb), "last incorrect"); /* hack to instrument every nth bb. assumes DR serializes bb events. */ freq++; if (freq % 100 == 0 && inst == (instr_t*)user_data/*first instr*/) { /* test read from cache */ dr_save_reg(drcontext, bb, inst, reg1, SPILL_SLOT_1); drmgr_insert_read_tls_field(drcontext, tls_idx, bb, inst, reg1); dr_insert_clean_call(drcontext, bb, inst, (void *)check_tls_from_cache, false, 1, opnd_create_reg(reg1)); drmgr_insert_read_cls_field(drcontext, cls_idx, bb, inst, reg1); dr_insert_clean_call(drcontext, bb, inst, (void *)check_cls_from_cache, false, 1, opnd_create_reg(reg1)); dr_restore_reg(drcontext, bb, inst, reg1, SPILL_SLOT_1); } if (freq % 300 == 0 && inst == (instr_t*)user_data/*first instr*/) { instr_t *first, *second; /* test write from cache */ dr_save_reg(drcontext, bb, inst, reg1, SPILL_SLOT_1); dr_save_reg(drcontext, bb, inst, reg2, SPILL_SLOT_2); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)MAGIC_NUMBER_FROM_CACHE, opnd_create_reg(reg1), bb, inst, &first, &second); instr_set_meta(first); if (second != NULL) instr_set_meta(second); drmgr_insert_write_tls_field(drcontext, tls_idx, bb, inst, reg1, reg2); dr_insert_clean_call(drcontext, bb, inst, (void *)check_tls_write_from_cache, false, 0); drmgr_insert_write_cls_field(drcontext, cls_idx, bb, inst, reg1, reg2); dr_insert_clean_call(drcontext, bb, inst, (void *)check_cls_write_from_cache, false, 0); dr_restore_reg(drcontext, bb, inst, reg2, SPILL_SLOT_2); dr_restore_reg(drcontext, bb, inst, reg1, SPILL_SLOT_1); } return DR_EMIT_DEFAULT; }
static dr_emit_flags_t event_app_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst, bool for_trace, bool translating, void *user_data) { reg_id_t reg_ptr = IF_X86_ELSE(DR_REG_XDX, TEST_REG); reg_id_t reg_tmp = IF_X86_ELSE(DR_REG_XCX, DR_REG_R3); /* We need a third register on ARM, because updating the buf pointer * requires a second scratch reg. */ reg_id_t scratch = IF_X86_ELSE(reg_tmp, DR_REG_R5); ptr_int_t subtest = (ptr_int_t) user_data; if (!instr_is_label(inst)) return DR_EMIT_DEFAULT; #ifdef X86 scratch = reg_resize_to_opsz(scratch, OPSZ_4); #endif if (subtest == DRX_BUF_TEST_1_C) { /* testing fast circular buffer */ /* test to make sure that on first invocation, the buffer is empty */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(circular_fast)); /* load the buf pointer, and then write a garbage element to the buffer */ drx_buf_insert_load_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 0); drx_buf_insert_update_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr, reg_tmp, sizeof(int)); /* verify the buffer was written to */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_dirty, false, 2, OPND_CREATE_INTPTR(circular_fast), opnd_create_reg(scratch)); /* fast circular buffer: trigger an overflow */ drx_buf_insert_load_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr); drx_buf_insert_update_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr, reg_tmp, CIRCULAR_FAST_SZ - sizeof(int)); /* the buffer is now clean */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(circular_fast)); } else if (subtest == DRX_BUF_TEST_2_C) { /* testing slow circular buffer */ /* test to make sure that on first invocation, the buffer is empty */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(circular_slow)); /* load the buf pointer, and then write an element to the buffer */ drx_buf_insert_load_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 0); drx_buf_insert_update_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr, DR_REG_NULL, sizeof(int)); /* verify the buffer was written to */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_dirty, false, 2, OPND_CREATE_INTPTR(circular_slow), opnd_create_reg(scratch)); /* slow circular buffer: trigger a fault */ drx_buf_insert_load_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr); drx_buf_insert_update_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr, DR_REG_NULL, CIRCULAR_SLOW_SZ - sizeof(int)); /* the "trigger" is a write, so we write whatever garbage is in reg_tmp */ drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 0); /* the buffer is now clean */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(circular_slow)); } else if (subtest == DRX_BUF_TEST_3_C) { /* testing trace buffer */ /* test to make sure that on first invocation, the buffer is empty */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(trace)); /* load the buf pointer, and then write an element to the buffer */ drx_buf_insert_load_buf_ptr(drcontext, trace, bb, inst, reg_ptr); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 0); drx_buf_insert_update_buf_ptr(drcontext, trace, bb, inst, reg_ptr, DR_REG_NULL, sizeof(int)); /* verify the buffer was written to */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_dirty, false, 2, OPND_CREATE_INTPTR(trace), opnd_create_reg(scratch)); /* trace buffer: trigger a fault and verify */ drx_buf_insert_load_buf_ptr(drcontext, trace, bb, inst, reg_ptr); drx_buf_insert_update_buf_ptr(drcontext, trace, bb, inst, reg_ptr, DR_REG_NULL, TRACE_SZ - sizeof(int)); /* the "trigger" is a write, so we write whatever garbage is in reg_tmp */ drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 0); /* the buffer is now clean */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(trace)); } else if (subtest == DRX_BUF_TEST_4_C) { /* test immediate store: 8 bytes (if possible), 4 bytes, 2 bytes and 1 byte */ /* "ABCDEFGH\x00" (x2 for x64) */ drx_buf_insert_load_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x41, OPSZ_1), OPSZ_1, 0); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x42, OPSZ_1), OPSZ_1, 1); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x4443, OPSZ_2), OPSZ_2, 2); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x48474645, OPSZ_4), OPSZ_4, 4); #ifdef X64 drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x4847464544434241, OPSZ_8), OPSZ_8, 8); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x00, OPSZ_1), OPSZ_1, 17); #else drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x00, OPSZ_1), OPSZ_1, 9); #endif dr_insert_clean_call(drcontext, bb, inst, verify_store, false, 1, OPND_CREATE_INTPTR(circular_fast)); } else if (subtest == DRX_BUF_TEST_5_C) { /* test register store: 8 bytes (if possible), 4 bytes, 2 bytes and 1 byte */ /* "ABCDEFGH\x00" (x2 for x64) */ drx_buf_insert_load_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr); scratch = reg_resize_to_opsz(scratch, OPSZ_1); MINSERT(bb, inst, XINST_CREATE_load_int (drcontext, opnd_create_reg(scratch), opnd_create_immed_int(0x41, OPSZ_1))); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_1, 0); MINSERT(bb, inst, XINST_CREATE_load_int (drcontext, opnd_create_reg(scratch), opnd_create_immed_int(0x42, OPSZ_1))); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_1, 1); scratch = reg_resize_to_opsz(scratch, OPSZ_2); MINSERT(bb, inst, XINST_CREATE_load_int (drcontext, opnd_create_reg(scratch), opnd_create_immed_int(0x4443, OPSZ_2))); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_2, 2); scratch = reg_resize_to_opsz(scratch, OPSZ_4); #ifdef X86 MINSERT(bb, inst, XINST_CREATE_load_int (drcontext, opnd_create_reg(scratch), opnd_create_immed_int(0x48474645, OPSZ_4))); #else instrlist_insert_mov_immed_ptrsz(drcontext, 0x48474645, opnd_create_reg(reg_resize_to_opsz (scratch, OPSZ_PTR)), bb, inst, NULL, NULL); #endif drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 4); #ifdef X64 scratch = reg_resize_to_opsz(scratch, OPSZ_8); /* only way to reliably move a 64 bit int into a register */ instrlist_insert_mov_immed_ptrsz(drcontext, 0x4847464544434241, opnd_create_reg(scratch), bb, inst, NULL, NULL); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_8, 8); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x00, OPSZ_1), OPSZ_1, 17); #else drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x00, OPSZ_1), OPSZ_1, 9); #endif dr_insert_clean_call(drcontext, bb, inst, verify_store, false, 1, OPND_CREATE_INTPTR(circular_fast)); } else if (subtest == DRX_BUF_TEST_6_C) { /* Currently, the fast circular buffer does not recommend variable-size * writes, for good reason. We don't test the memcpy operation on the * fast circular buffer. */ /* verify memcpy works on the slow clrcular buffer */ drx_buf_insert_load_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)test_copy, opnd_create_reg(reg_resize_to_opsz (scratch, OPSZ_PTR)), bb, inst, NULL, NULL); drx_buf_insert_buf_memcpy(drcontext, circular_slow, bb, inst, reg_ptr, reg_resize_to_opsz(scratch, OPSZ_PTR), sizeof(test_copy)); /* NULL out the buffer */ drx_buf_insert_load_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)test_null, opnd_create_reg(reg_resize_to_opsz (scratch, OPSZ_PTR)), bb, inst, NULL, NULL); drx_buf_insert_buf_memcpy(drcontext, circular_slow, bb, inst, reg_ptr, reg_resize_to_opsz(scratch, OPSZ_PTR), sizeof(test_null)); /* Unfortunately, we can't just use the check in verify_buffer_empty, because * drx_buf_insert_buf_memcpy() incrememnts the buffer pointer internally, unlike * drx_buf_insert_buf_store(). We simply check that the buffer was NULLed out. */ dr_insert_clean_call(drcontext, bb, inst, (void *)verify_buffers_nulled, false, 1, OPND_CREATE_INTPTR(circular_slow)); /* verify memcpy works on the trace buffer */ drx_buf_insert_load_buf_ptr(drcontext, trace, bb, inst, reg_ptr); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)test_copy, opnd_create_reg(reg_resize_to_opsz (scratch, OPSZ_PTR)), bb, inst, NULL, NULL); drx_buf_insert_buf_memcpy(drcontext, trace, bb, inst, reg_ptr, reg_resize_to_opsz(scratch, OPSZ_PTR), sizeof(test_copy)); /* NULL out the buffer */ drx_buf_insert_load_buf_ptr(drcontext, trace, bb, inst, reg_ptr); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)test_null, opnd_create_reg(reg_resize_to_opsz (scratch, OPSZ_PTR)), bb, inst, NULL, NULL); drx_buf_insert_buf_memcpy(drcontext, trace, bb, inst, reg_ptr, reg_resize_to_opsz(scratch, OPSZ_PTR), sizeof(test_null)); /* verify buffer was NULLed */ dr_insert_clean_call(drcontext, bb, inst, (void *)verify_buffers_nulled, false, 1, OPND_CREATE_INTPTR(trace)); } return DR_EMIT_DEFAULT; }