/* We collect the basic block information including offset from module base, * size, and num of instructions, and add it into a basic block table without * instrumentation. */ static dr_emit_flags_t event_basic_block_analysis(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating, OUT void **user_data) { per_thread_t *data; instr_t *instr; app_pc tag_pc, start_pc, end_pc; /* do nothing for translation */ if (translating) return DR_EMIT_DEFAULT; data = (per_thread_t *)drmgr_get_tls_field(drcontext, tls_idx); /* Collect the number of instructions and the basic block size, * assuming the basic block does not have any elision on control * transfer instructions, which is true for default options passed * to DR but not for -opt_speed. */ /* We separate the tag from the instr pc ranges to handle displaced code * such as for the vsyscall hook. */ tag_pc = dr_fragment_app_pc(tag); start_pc = instr_get_app_pc(instrlist_first_app(bb)); end_pc = start_pc; /* for finding the size */ for (instr = instrlist_first_app(bb); instr != NULL; instr = instr_get_next_app(instr)) { app_pc pc = instr_get_app_pc(instr); int len = instr_length(drcontext, instr); /* -opt_speed (elision) is not supported */ /* For rep str expansion pc may be one back from start pc but equal to the tag. */ ASSERT(pc != NULL && (pc >= start_pc || pc == tag_pc), "-opt_speed is not supported"); if (pc + len > end_pc) end_pc = pc + len; } /* We allow duplicated basic blocks for the following reasons: * 1. Avoids handling issues like code cache consistency, e.g., * module load/unload, self-modifying code, etc. * 2. Avoids the overhead on duplication check. * 3. Stores more information on code cache events, e.g., trace building, * repeated bb building, etc. * 4. The duplication can be easily handled in a post-processing step, * which is required anyway. */ bb_table_entry_add(drcontext, data, tag_pc, (uint)(end_pc - start_pc)); if (go_native) return DR_EMIT_GO_NATIVE; else 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_analysis(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating, OUT void **user_data) { instr_t *inst, *label; bool prev_was_mov_const = false; ptr_int_t val1, val2; *user_data = NULL; /* Look for duplicate mov immediates telling us which subtest we're in */ for (inst = instrlist_first_app(bb); inst != NULL; inst = instr_get_next_app(inst)) { if (instr_is_mov_constant(inst, prev_was_mov_const ? &val2 : &val1)) { if (prev_was_mov_const && val1 == val2 && val1 != 0 && /* rule out xor w/ self */ opnd_is_reg(instr_get_dst(inst, 0)) && opnd_get_reg(instr_get_dst(inst, 0)) == TEST_REG) { *user_data = (void *) val1; label = INSTR_CREATE_label(drcontext); instr_set_translation(label, instr_get_app_pc(inst)); instrlist_meta_postinsert(bb, inst, label); } else prev_was_mov_const = true; } else prev_was_mov_const = false; } return DR_EMIT_DEFAULT; }
static dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *instr, *next_instr; #ifdef VERBOSE dr_printf("in dr_basic_block(tag="PFX")\n", tag); # if VERBOSE_VERBOSE instrlist_disassemble(drcontext, tag, bb, STDOUT); # endif #endif for (instr = instrlist_first_app(bb); instr != NULL; instr = next_instr) { next_instr = instr_get_next_app(instr); if (!instr_opcode_valid(instr)) continue; /* instrument calls and returns -- ignore far calls/rets */ if (instr_is_call_direct(instr)) { dr_insert_call_instrumentation(drcontext, bb, instr, (app_pc)at_call); } else if (instr_is_call_indirect(instr)) { dr_insert_mbr_instrumentation(drcontext, bb, instr, (app_pc)at_call_ind, SPILL_SLOT_1); } else if (instr_is_return(instr)) { dr_insert_mbr_instrumentation(drcontext, bb, instr, (app_pc)at_return, SPILL_SLOT_1); } } return DR_EMIT_DEFAULT; }
/* This event is passed the instruction list for the whole bb. */ static dr_emit_flags_t event_analyze_bb(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating, void **user_data) { /* Count the instructions and pass the result to event_insert_instrumentation. */ instr_t *instr; uint num_instrs; for (instr = instrlist_first_app(bb), num_instrs = 0; instr != NULL; instr = instr_get_next_app(instr)) { num_instrs++; } *(uint *)user_data = num_instrs; return DR_EMIT_DEFAULT; }
static dr_emit_flags_t event_analyze_bb(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating, void **user_data) { instr_t *instr; trace_head_entry_t *e = NULL; if (translating) return DR_EMIT_DEFAULT; for (instr = instrlist_first_app(bb); instr != NULL; instr = instr_get_next_app(instr)) { /* Blocks containing calls are trace heads. */ if (instr_is_call(instr)) { dr_mark_trace_head(drcontext, tag); hashtable_lock(&head_table); e = hashtable_lookup(&head_table, tag); if (e == NULL) { e = create_trace_head_entry(tag); if (!hashtable_add(&head_table, tag, (void *)e)) DR_ASSERT(false); } else e->refcount++; e->is_trace_head = true; hashtable_unlock(&head_table); #ifdef VERBOSE dr_log(drcontext, DR_LOG_ALL, 3, "inline: marking bb "PFX" as call trace head\n", tag); #endif /* Doesn't matter what's in rest of the bb. */ return DR_EMIT_DEFAULT; } else if (instr_is_return(instr)) { hashtable_lock(&head_table); e = hashtable_lookup(&head_table, tag); if (e == NULL) { e = create_trace_head_entry(tag); if (!hashtable_add(&head_table, tag, (void *)e)) DR_ASSERT(false); } else e->refcount++; e->has_ret = true; hashtable_unlock(&head_table); #ifdef VERBOSE dr_log(drcontext, DR_LOG_ALL, 3, "inline: marking bb "PFX" as return trace head\n", tag); #endif } } return DR_EMIT_DEFAULT; }
static dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *instr; for (instr = instrlist_first_app(bb); instr != NULL; instr = instr_get_next_app(instr)) { if (instr_is_cbr(instr)) { dr_insert_cbr_instrumentation_ex (drcontext, bb, instr, (void *)at_cbr, OPND_CREATE_INTPTR(dr_fragment_app_pc(tag))); } } return DR_EMIT_DEFAULT; }
static dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *instr, *mbr = NULL; uint num_instrs; bool bb_in_app; #ifdef VERBOSE dr_printf("in dynamorio_basic_block(tag="PFX")\n", tag); # ifdef VERBOSE_VERBOSE instrlist_disassemble(drcontext, tag, bb, STDOUT); # endif #endif for (instr = instrlist_first_app(bb), num_instrs = 0; instr != NULL; instr = instr_get_next_app(instr)) { num_instrs++; /* Assuming most of the transfers between app and lib are paired, we * instrument indirect branches but not returns for better performance. */ if (instr_is_mbr(instr) && !instr_is_return(instr)) mbr = instr; } if (dr_fragment_app_pc(tag) >= app_base && dr_fragment_app_pc(tag) < app_end) bb_in_app = true; else bb_in_app = false; dr_insert_clean_call(drcontext, bb, instrlist_first(bb), (void *)(bb_in_app ? app_update : lib_update), false /* save fpstate */, 1, OPND_CREATE_INT32(num_instrs)); if (mbr != NULL) { dr_insert_mbr_instrumentation(drcontext, bb, mbr, (void *)(bb_in_app ? app_mbr : lib_mbr), SPILL_SLOT_1); } #if defined(VERBOSE) && defined(VERBOSE_VERBOSE) dr_printf("Finished instrumenting dynamorio_basic_block(tag="PFX")\n", tag); instrlist_disassemble(drcontext, tag, bb, STDOUT); #endif return DR_EMIT_DEFAULT; }
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) { uint num_instrs; /* By default drmgr enables auto-predication, which predicates all instructions with * the predicate of the current instruction on ARM. * We disable it here because we want to unconditionally execute the following * instrumentation. */ drmgr_disable_auto_predication(drcontext, bb); if (!drmgr_is_first_instr(drcontext, instr)) return DR_EMIT_DEFAULT; /* Only insert calls for in-app BBs */ if (user_data == NULL) return DR_EMIT_DEFAULT; /* Insert clean call */ num_instrs = (uint)(ptr_uint_t)user_data; dr_insert_clean_call(drcontext, bb, instrlist_first_app(bb), (void *)inscount, false /* save fpstate */, 1, OPND_CREATE_INT32(num_instrs)); return DR_EMIT_DEFAULT; }
void offline_instru_t::bb_analysis(void *drcontext, void *tag, void **bb_field, instrlist_t *ilist, bool repstr_expanded) { instr_t *instr; ptr_uint_t count = 0; app_pc last_xl8 = NULL; if (repstr_expanded) { // The same-translation check below is not sufficient as drutil uses // two different translations to deal with complexities. // Thus we hardcode this. count = 1; } else { for (instr = instrlist_first_app(ilist); instr != NULL; instr = instr_get_next_app(instr)) { // To deal with app2app changes, we do not double-count consecutive instrs // with the same translation. if (instr_get_app_pc(instr) != last_xl8) ++count; last_xl8 = instr_get_app_pc(instr); } } *bb_field = (void *)count; }
/* This event is passed the instruction list for the whole bb. */ static dr_emit_flags_t event_analyze_bb(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating, void **user_data) { /* Count the instructions and pass the result to event_insert_instrumentation. */ per_bb_data_t *per_bb = dr_thread_alloc(drcontext, sizeof(*per_bb)); instr_t *instr; uint num_instrs = 0; uint num_flops = 0; uint num_syscalls = 0; dr_fp_type_t fp_type; for (instr = instrlist_first_app(bb); instr != NULL; instr = instr_get_next_app(instr)) { num_instrs++; if (instr_is_floating_ex(instr, &fp_type) && /* We exclude loads and stores (and reg-reg moves) and state preservation */ (fp_type == DR_FP_CONVERT || fp_type == DR_FP_MATH)) { #ifdef VERBOSE dr_print_instr(drcontext, STDOUT, instr, "Found flop: "); #endif num_flops++; } if (instr_is_syscall(instr)) { num_syscalls++; } } per_bb->num_instrs = num_instrs; per_bb->num_flops = num_flops; per_bb->num_syscalls = num_syscalls; *(per_bb_data_t**)user_data = per_bb; return DR_EMIT_DEFAULT; }
static dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *instr, *next_instr; #ifdef VERBOSE dr_printf("in dynamorio_basic_block(tag="PFX")\n", tag); # ifdef VERBOSE_VERBOSE instrlist_disassemble(drcontext, tag, bb, STDOUT); # endif #endif for (instr = instrlist_first_app(bb); instr != NULL; instr = next_instr) { /* grab next now so we don't go over instructions we insert */ next_instr = instr_get_next_app(instr); /* instrument calls and returns -- ignore far calls/rets */ if (instr_is_call_direct(instr)) { insert_counter_update(drcontext, bb, instr, offsetof(per_thread_t, num_direct_calls)); } else if (instr_is_call_indirect(instr)) { insert_counter_update(drcontext, bb, instr, offsetof(per_thread_t, num_indirect_calls)); } else if (instr_is_return(instr)) { insert_counter_update(drcontext, bb, instr, offsetof(per_thread_t, num_returns)); } } #if defined(VERBOSE) && defined(VERBOSE_VERBOSE) dr_printf("Finished instrumenting dynamorio_basic_block(tag="PFX")\n", tag); instrlist_disassemble(drcontext, tag, bb, STDOUT); #endif return DR_EMIT_DEFAULT; }