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; }
bool hashtable_remove_range(hashtable_t *table, void *start, void *end) { bool res = false; uint i; hash_entry_t *e, *prev_e, *next_e; if (table->synch) hashtable_lock(table); for (i = 0; i < HASHTABLE_SIZE(table->table_bits); i++) { for (e = table->table[i], prev_e = NULL; e != NULL; e = next_e) { next_e = e->next; if (e->key >= start && e->key < end) { if (prev_e == NULL) table->table[i] = e->next; else prev_e->next = e->next; if (table->str_dup) hash_free(e->key, strlen((const char *)e->key) + 1); if (table->free_payload_func != NULL) (table->free_payload_func)(e->payload); hash_free(e, sizeof(*e)); table->entries--; res = true; } else prev_e = e; } } if (table->synch) hashtable_unlock(table); return res; }
static void onUnload( void* ctx, const module_data_t* m ) { dr_printf( "In onUnload(%s, %p-%p)\n", m->full_path, m->start, m->end ); dr_mutex_lock( outMutex ); dr_fprintf( outFile, "m %p %p %s\n", (void*)m->start, (void*)m->end, m->full_path ); dr_mutex_unlock( outMutex ); hashtable_lock( &wraps ); hashtable_remove_range( &wraps, (void*)m->start, (void*)m->end ); hashtable_unlock( &wraps ); }
/* To keep the size of our hashtable down. */ static void event_fragment_deleted(void *drcontext, void *tag) { trace_head_entry_t *e; hashtable_lock(&head_table); e = hashtable_lookup(&head_table, tag); if (e != NULL) { e->refcount--; if (e->refcount == 0) hashtable_remove(&head_table, tag); } hashtable_unlock(&head_table); }
/** Installs wrapper for a given symbol. */ static void wrapSymbol( const char* sym, module_data_t* module, size_t offset, struct demangle_component* qual, struct demangle_component* info ) { app_pc pc = module->start + offset; if( offset == 0 || pc == 0 ) { dr_printf( "Skipping symbol %s\n", sym ); return; } size_t size; char* className = cplus_demangle_print( DMGL_NO_OPTS, qual, 64, &size ); hashtable_lock( &wraps ); struct wrap* wrap = ( struct wrap* )hashtable_lookup( &wraps, (void*)pc ); if( wrap == NULL ) { // Not wrapped yet. if( !drwrap_wrap( pc, &wrapperPre, NULL ) ) { dr_printf( "drwrap_wrap(%s %s of kind %i = +%p = %p) failed\n", className, info->type == DEMANGLE_COMPONENT_CTOR ? "ctor" : "dtor", info->type == DEMANGLE_COMPONENT_CTOR ? info->u.s_ctor.kind : info->u.s_dtor.kind, (void*)offset, (void*)pc ); exit( 1 ); } wrap = alloc_wrap(); memset( wrap, 0, sizeof( struct wrap ) ); wrap->address = pc; hashtable_add( &wraps, (void*)pc, wrap ); } else { // Already wrapped - do nothing. } char* eventType = "?"; if( info->type == DEMANGLE_COMPONENT_CTOR ) { switch( info->u.s_ctor.kind ) { case gnu_v3_complete_object_allocating_ctor: wrap->flags |= FLAG_ALLOCATING_CONSTRUCTOR; eventType = "A"; break; case gnu_v3_complete_object_ctor: wrap->flags |= FLAG_COMPLETE_OBJECT_CONSTRUCTOR; eventType = "C"; break; case gnu_v3_base_object_ctor: wrap->flags |= FLAG_BASE_OBJECT_CONSTRUCTOR; eventType = "B"; break; } } else { switch( info->u.s_dtor.kind ) { case gnu_v3_deleting_dtor: wrap->flags |= FLAG_DEALLOCATING_DESTRUCTOR; eventType ="a"; break; case gnu_v3_complete_object_dtor: wrap->flags |= FLAG_COMPLETE_OBJECT_DESTRUCTOR; eventType = "c"; break; case gnu_v3_base_object_dtor: wrap->flags |= FLAG_BASE_OBJECT_DESTRUCTOR; eventType = "b"; break; } } hashtable_unlock( &wraps ); dr_mutex_lock( outMutex ); dr_fprintf( outFile, "%s %p %s\n", eventType, (void*)pc, className ); dr_mutex_unlock( outMutex ); free( className ); }
/* Ask whether to end trace prior to adding next_tag fragment. * Return values: * CUSTOM_TRACE_DR_DECIDES = use standard termination criteria * CUSTOM_TRACE_END_NOW = end trace * CUSTOM_TRACE_CONTINUE = do not end trace */ static dr_custom_trace_action_t query_end_trace(void *drcontext, void *trace_tag, void *next_tag) { /* If this is a call trace, only end on the block AFTER a return * (need to get the return inlined!). * If this is a standard back branch trace, end it if we see a * block with a call (so that we'll go into the call trace). * otherwise return 0 and let DynamoRIO determine whether to * terminate the trace now. */ trace_head_entry_t *e; hashtable_lock(&head_table); e = hashtable_lookup(&head_table, trace_tag); if (e == NULL || !e->is_trace_head) { e = hashtable_lookup(&head_table, next_tag); if (e == NULL || !e->is_trace_head) { hashtable_unlock(&head_table); return CUSTOM_TRACE_DR_DECIDES; } else { /* We've found a call: end this trace now so it won't keep going and * end up never entering the call trace. */ #ifdef VERBOSE dr_log(drcontext, DR_LOG_ALL, 3, "inline: ending trace "PFX" before block "PFX" containing call\n", trace_tag, next_tag); #endif #ifdef SHOW_RESULTS num_traces++; #endif hashtable_unlock(&head_table); return CUSTOM_TRACE_END_NOW; } } else if (e->end_next > 0) { e->end_next--; if (e->end_next == 0) { #ifdef VERBOSE dr_log(drcontext, DR_LOG_ALL, 3, "inline: ending trace "PFX" before "PFX"\n", trace_tag, next_tag); #endif #ifdef SHOW_RESULTS num_complete_inlines++; num_traces++; #endif hashtable_unlock(&head_table); return CUSTOM_TRACE_END_NOW; } } else { trace_head_entry_t *nxte = hashtable_lookup(&head_table, next_tag); uint size = dr_fragment_size(drcontext, next_tag); e->size += size; if (e->size > INLINE_SIZE_LIMIT) { #ifdef VERBOSE dr_log(drcontext, DR_LOG_ALL, 3, "inline: ending trace "PFX" before "PFX" because reached size limit\n", trace_tag, next_tag); #endif #ifdef SHOW_RESULTS num_traces++; #endif hashtable_unlock(&head_table); return CUSTOM_TRACE_END_NOW; } if (nxte != NULL && nxte->has_ret && !nxte->is_trace_head) { /* End trace after NEXT block */ e->end_next = 2; #ifdef VERBOSE dr_log(drcontext, DR_LOG_ALL, 3, "inline: going to be ending trace "PFX" after "PFX"\n", trace_tag, next_tag); #endif hashtable_unlock(&head_table); return CUSTOM_TRACE_CONTINUE; } } /* Do not end trace */ #ifdef VERBOSE dr_log(drcontext, DR_LOG_ALL, 3, "inline: NOT ending trace "PFX" after "PFX"\n", trace_tag, next_tag); #endif hashtable_unlock(&head_table); return CUSTOM_TRACE_CONTINUE; }