| /* ********************************************************** |
| * Copyright (c) 2012-2014 Google, Inc. All rights reserved. |
| * Copyright (c) 2000-2010 VMware, Inc. All rights reserved. |
| * **********************************************************/ |
| |
| /* |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * * Neither the name of VMware, Inc. nor the names of its contributors may be |
| * used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| /* Copyright (c) 2003-2007 Determina Corp. */ |
| /* Copyright (c) 2001-2003 Massachusetts Institute of Technology */ |
| /* Copyright (c) 2000-2001 Hewlett-Packard Company */ |
| |
| /* file "monitor.c" |
| */ |
| |
| #include "globals.h" |
| #include "fragment.h" |
| #include "link.h" |
| #include "utils.h" |
| #include "emit.h" |
| #include "fcache.h" |
| #include "monitor.h" |
| #ifdef CUSTOM_TRACES |
| # include "instrument.h" |
| #endif |
| #include <string.h> /* for memset */ |
| #include "instr.h" |
| #include "perscache.h" |
| #include "disassemble.h" |
| |
| #ifdef CLIENT_INTERFACE |
| /* in interp.c. not declared in arch_exports.h to avoid having to go |
| * make monitor_data_t opaque in globals.h. |
| */ |
| extern bool mangle_trace(dcontext_t *dcontext, instrlist_t *ilist, monitor_data_t *md); |
| #endif |
| |
| /* SPEC2000 applu has a trace head entry fragment of size 40K! */ |
| /* streamit's fft had a 944KB bb (ridiculous unrolling) */ |
| /* no reason to have giant traces, second half will become 2ndary trace */ |
| /* The instrumentation easily makes trace large, |
| * so we should make the buffer size bigger, if a client is used.*/ |
| #ifdef CLIENT_INTERFACE |
| # define MAX_TRACE_BUFFER_SIZE MAX_FRAGMENT_SIZE |
| #else |
| # define MAX_TRACE_BUFFER_SIZE (16*1024) /* in bytes */ |
| #endif |
| /* most traces are under 1024 bytes. |
| * for x64, the rip-rel instrs prevent a memcpy on a resize |
| */ |
| #ifdef X64 |
| # define INITIAL_TRACE_BUFFER_SIZE MAX_TRACE_BUFFER_SIZE |
| #else |
| # define INITIAL_TRACE_BUFFER_SIZE 1024 /* in bytes */ |
| #endif |
| |
| #define INITIAL_NUM_BLKS 8 |
| |
| #define INIT_COUNTER_TABLE_SIZE 9 |
| #define COUNTER_TABLE_LOAD 75 |
| /* counters must be in unprotected memory |
| * we don't support local unprotected so we use global |
| */ |
| /* cannot use HEAPACCT here so we use ... */ |
| #define COUNTER_ALLOC(dc, ...) \ |
| (TEST(SELFPROT_LOCAL, dynamo_options.protect_mask) ? \ |
| global_unprotected_heap_alloc(__VA_ARGS__) : \ |
| heap_alloc(dc, __VA_ARGS__)) |
| #define COUNTER_FREE(dc, p, ...) \ |
| (TEST(SELFPROT_LOCAL, dynamo_options.protect_mask) ? \ |
| global_unprotected_heap_free(p, __VA_ARGS__) : \ |
| heap_free(dc, p, __VA_ARGS__)) |
| |
| static void reset_trace_state(dcontext_t *dcontext, bool grab_link_lock); |
| |
| /* synchronization of shared traces */ |
| DECLARE_CXTSWPROT_VAR(mutex_t trace_building_lock, INIT_LOCK_FREE(trace_building_lock)); |
| |
| /* For clearing counters on trace deletion we follow a lazy strategy |
| * using a sentinel value to determine whether we've built a trace or not |
| */ |
| #define TH_COUNTER_CREATED_TRACE_VALUE() (INTERNAL_OPTION(trace_threshold) + 1U) |
| |
| static void |
| delete_private_copy(dcontext_t *dcontext) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| if (md->last_copy != NULL) { |
| LOG(THREAD, LOG_MONITOR, 4, |
| "Deleting previous private copy F%d ("PFX")\n", |
| md->last_copy->id, md->last_copy->tag); |
| /* cannot have monitor_remove_fragment called, since that would abort trace |
| * if last_copy is last_fragment |
| */ |
| if (md->last_copy == md->last_fragment) { |
| /* don't have to do internal_restore_last since deleting the thing */ |
| md->last_fragment = NULL; |
| } |
| if (md->last_copy == dcontext->last_fragment) |
| last_exit_deleted(dcontext); |
| if (TEST(FRAG_WAS_DELETED, md->last_copy->flags)) { |
| /* case 8083: private copy can't be deleted in trace_abort() since |
| * needed for pc translation (at least until -safe_translate_flushed |
| * is on by default), so if we come here later we must check |
| * for an intervening flush to avoid double-deletion. |
| */ |
| LOG(THREAD, LOG_MONITOR, 4, |
| "\tprivate copy was flushed so not re-deleting\n"); |
| STATS_INC(num_trace_private_deletions_flushed); |
| } else { |
| fragment_delete(dcontext, md->last_copy, FRAGDEL_NO_MONITOR |
| /* private fragments are invisible */ |
| IF_CLIENT_INTERFACE(|FRAGDEL_NO_HTABLE)); |
| } |
| md->last_copy = NULL; |
| STATS_INC(num_trace_private_deletions); |
| } |
| } |
| |
| /* Returns false if f has been deleted and no copy can be made. FIXME - also returns |
| * false if the emitted private fragment can't be added to a trace (for pad_jmps |
| * insertion reasons xref PR 215179). */ |
| static bool |
| create_private_copy(dcontext_t *dcontext, fragment_t *f) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| /* trying to share the tail of the trace ilist is a bad idea -- |
| * violates instrlist_t abstraction, have to deal w/ changes for |
| * bb->trace (like ibl target) and worry about encoding process |
| * changing instr_t state in a way that will affect the trace... |
| * |
| * instead we re-decode the thing, for simplicity |
| */ |
| |
| KSTART(temp_private_bb); |
| LOG(THREAD, LOG_MONITOR, 4, |
| "Creating private copy of F%d ("PFX") for trace creation\n", f->id, f->tag); |
| |
| ASSERT(dr_get_isa_mode(dcontext) == FRAG_ISA_MODE(f->flags) |
| IF_X64(|| (dr_get_isa_mode(dcontext) == DR_ISA_IA32 && |
| !FRAG_IS_32(f->flags) && DYNAMO_OPTION(x86_to_x64)))); |
| |
| /* only keep one private copy around at a time |
| * we delete here, when we add a new copy, and not in internal_restore_last |
| * since if we do it there we'll clobber last_exit too early |
| */ |
| if (md->last_copy != NULL) |
| delete_private_copy(dcontext); |
| |
| /* PR 213760/PR 299808: rather than decode_fragment(), which is expensive for |
| * frozen coarse fragments, we re-build from app code (which also simplifies |
| * our client trace model). If the existing f was flushed/deleted, that won't |
| * stop us from creating a new one for our trace. |
| */ |
| /* emitting could clobber last_fragment, which will abort this trace, |
| * unfortunately -- need to be transactional so we finish building this guy, |
| * and then just stop (will delete on next trace build) |
| */ |
| md->last_fragment = |
| build_basic_block_fragment(dcontext, f->tag, FRAG_TEMP_PRIVATE, |
| true/*link*/, |
| /* for clients we make temp-private even when |
| * thread-private versions already exist, so |
| * we have to make them invisible */ |
| IF_CLIENT_INTERFACE_ELSE(false, true) |
| _IF_CLIENT(true/*for_trace*/) |
| _IF_CLIENT(md->pass_to_client ? |
| &md->unmangled_bb_ilist : NULL)); |
| md->last_copy = md->last_fragment; |
| |
| STATS_INC(num_trace_private_copies); |
| LOG(THREAD, LOG_MONITOR, 4, |
| "Created private copy F%d of original F%d ("PFX") for trace creation\n", |
| md->last_fragment->id, f->id, f->tag); |
| DOLOG(2, LOG_INTERP, { disassemble_fragment(dcontext, md->last_fragment, |
| stats->loglevel <= 3); }); |
| KSTOP(temp_private_bb); |
| /* FIXME - PR 215179, with current hack pad_jmps sometimes marks fragments as |
| * CANNOT_BE_TRACE during emit (since we don't yet have a good way to handle |
| * inserted nops during tracing). This can happen for UNIX syscall fence exits and |
| * CLIENT_INTERFACE added exits. However, it depends on the starting alignment of |
| * the fragment! So it's possible to have a fragment that is ok turn into a |
| * fragment that isn't when re-emitted here. We could keep the ilist around and |
| * use that to extend the trace later (instead of decoding the TEMP bb), but better |
| * to come up with a general fix for tracing through nops. |
| */ |
| /* PR 299808: this can happen even more easily now that we re-pass the bb |
| * to the client. FRAG_MUST_END_TRACE is handled in internal_extend_trace. |
| */ |
| if (TEST(FRAG_CANNOT_BE_TRACE, md->last_fragment->flags)) { |
| delete_private_copy(dcontext); |
| md->last_fragment = NULL; |
| md->last_copy = NULL; |
| STATS_INC(pad_jmps_block_trace_reemit); |
| return false; |
| } |
| return true; |
| } |
| |
| #ifdef CLIENT_INTERFACE |
| static void |
| extend_unmangled_ilist(dcontext_t *dcontext, fragment_t *f) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| if (md->pass_to_client) { |
| instr_t *inst; |
| /* FIXME: pass out exit_type from build_basic_block_fragment instead |
| * of walking exit stubs here? |
| * FIXME: remove once we have PR 307284. |
| */ |
| linkstub_t *l; |
| ASSERT(md->last_copy != NULL); |
| ASSERT(!TEST(FRAG_COARSE_GRAIN, md->last_copy->flags)); |
| for (l = FRAGMENT_EXIT_STUBS(md->last_copy); |
| LINKSTUB_NEXT_EXIT(l) != NULL; l = LINKSTUB_NEXT_EXIT(l)) |
| ; /* nothing */ |
| md->final_exit_flags = l->flags; |
| LOG(THREAD, LOG_MONITOR, 2, "final exit flags: %x\n", md->final_exit_flags); |
| |
| /* PR 299808: we need to keep a list of pre-mangled code */ |
| ASSERT(md->unmangled_bb_ilist != NULL); |
| if (instrlist_first(md->unmangled_bb_ilist) != NULL) { |
| instrlist_append(&md->unmangled_ilist, |
| instrlist_first(md->unmangled_bb_ilist)); |
| } |
| DOLOG(4, LOG_INTERP, { |
| LOG(THREAD, LOG_INTERP, 4, "unmangled ilist with F%d("PFX"):\n", |
| md->last_copy->id, md->last_copy->tag); |
| instrlist_disassemble(dcontext, md->trace_tag, &md->unmangled_ilist, THREAD); |
| }); |
| |
| /* PR 299808: we need the end pc for boundary finding later */ |
| ASSERT(md->num_blks < md->blk_info_length); |
| inst = instrlist_last(md->unmangled_bb_ilist); |
| |
| md->blk_info[md->num_blks].vmlist = NULL; |
| if (inst != NULL) { /* PR 366232: handle empty bbs */ |
| vm_area_add_to_list(dcontext, f->tag, |
| &(md->blk_info[md->num_blks].vmlist), |
| md->trace_flags, f, false/*have no locks*/); |
| md->blk_info[md->num_blks].final_cti = |
| instr_is_cti(instrlist_last(md->unmangled_bb_ilist)); |
| } else |
| md->blk_info[md->num_blks].final_cti = false; |
| |
| instrlist_init(md->unmangled_bb_ilist); /* clear fields to make destroy happy */ |
| instrlist_destroy(dcontext, md->unmangled_bb_ilist); |
| md->unmangled_bb_ilist = NULL; |
| } |
| /* If any constituent block wants to store (or the final trace hook wants to), |
| * then store for the trace. |
| */ |
| if (md->last_copy != NULL && TEST(FRAG_HAS_TRANSLATION_INFO, md->last_copy->flags)) |
| md->trace_flags |= FRAG_HAS_TRANSLATION_INFO; |
| } |
| #endif |
| |
| bool |
| mangle_trace_at_end(void) |
| { |
| /* There's no reason to keep an unmangled list and mangle at the end |
| * unless there's a client bb or trace hook, for a for-cache trace |
| * or a recreate-state trace. |
| */ |
| #ifdef CLIENT_INTERFACE |
| return (dr_bb_hook_exists() || dr_trace_hook_exists()); |
| #else |
| return false; |
| #endif |
| } |
| |
| /* Initialization */ |
| /* thread-shared init does nothing, thread-private init does it all */ |
| void |
| monitor_init() |
| { |
| /* to reduce memory, we use ushorts for some offsets in fragment bodies, |
| * so we have to stop a trace at that size |
| * this does not include exit stubs |
| */ |
| ASSERT(MAX_TRACE_BUFFER_SIZE <= MAX_FRAGMENT_SIZE); |
| } |
| |
| /* re-initializes non-persistent memory */ |
| void |
| monitor_thread_reset_init(dcontext_t *dcontext) |
| { |
| } |
| |
| /* frees all non-persistent memory */ |
| void |
| monitor_thread_reset_free(dcontext_t *dcontext) |
| { |
| trace_abort_and_delete(dcontext); |
| } |
| |
| void |
| trace_abort_and_delete(dcontext_t *dcontext) |
| { |
| /* remove any MultiEntries */ |
| trace_abort(dcontext); |
| /* case 8083: we have to explicitly remove last copy since it can't be |
| * removed in trace_abort (at least until -safe_translate_flushed is on) |
| */ |
| delete_private_copy(dcontext); |
| } |
| |
| void |
| monitor_exit() |
| { |
| LOG(GLOBAL, LOG_MONITOR|LOG_STATS, 1, |
| "Trace fragments generated: %d\n", GLOBAL_STAT(num_traces)); |
| DELETE_LOCK(trace_building_lock); |
| } |
| |
| void |
| monitor_thread_init(dcontext_t *dcontext) |
| { |
| monitor_data_t *md; |
| |
| md = (monitor_data_t *) |
| heap_alloc(dcontext, sizeof(monitor_data_t) HEAPACCT(ACCT_TRACE)); |
| dcontext->monitor_field = (void *) md; |
| memset(md, 0, sizeof(monitor_data_t)); |
| reset_trace_state(dcontext, false /* link lock not needed */); |
| |
| /* case 7966: don't initialize un-needed things for hotp_only & thin_client |
| * FIXME: could set initial sizes to 0 for all configurations, instead |
| * FIXME: we can optimize even more to not allocate md at all, but would need |
| * to have hotp_only checks in monitor_cache_exit(), etc. |
| */ |
| if (RUNNING_WITHOUT_CODE_CACHE()) |
| return; |
| |
| /* FIXME : we should gather statistics on the hash table */ |
| /* trace head counters are thread-private and must be kept in a |
| * separate table and not in the fragment_t structure. |
| */ |
| md->thead_table.hash_bits = INIT_COUNTER_TABLE_SIZE; |
| md->thead_table.hash_mask = HASH_MASK(md->thead_table.hash_bits); |
| md->thead_table.hash_mask_offset = 0; |
| md->thead_table.hash_func = (hash_function_t)INTERNAL_OPTION(alt_hash_func); |
| md->thead_table.capacity = HASHTABLE_SIZE(md->thead_table.hash_bits); |
| md->thead_table.entries = 0; |
| md->thead_table.load_factor_percent = COUNTER_TABLE_LOAD; |
| md->thead_table.resize_threshold = |
| md->thead_table.capacity * md->thead_table.load_factor_percent / 100; |
| md->thead_table.counter_table = (trace_head_counter_t **) |
| COUNTER_ALLOC(dcontext, md->thead_table.capacity*sizeof(trace_head_counter_t*) |
| HEAPACCT(ACCT_THCOUNTER)); |
| memset(md->thead_table.counter_table, 0, md->thead_table.capacity* |
| sizeof(trace_head_counter_t*)); |
| } |
| |
| /* atexit cleanup */ |
| void |
| monitor_thread_exit(dcontext_t *dcontext) |
| { |
| DEBUG_DECLARE(uint i;) |
| DEBUG_DECLARE(monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field;) |
| |
| /* For non-debug we do fast exit path and don't free local heap. |
| * We call trace_abort so that in case the thread is terminated in |
| * the middle of trace building from a shared trace head, it has a |
| * chance to clear the FRAG_TRACE_BUILDING flag. Otherwise, a trace |
| * can never be built from that particular trace head. |
| */ |
| trace_abort(dcontext); |
| #ifdef DEBUG |
| if (md->trace_buf != NULL) |
| heap_free(dcontext, md->trace_buf, md->trace_buf_size HEAPACCT(ACCT_TRACE)); |
| if (md->blk_info != NULL) { |
| heap_free(dcontext, md->blk_info, md->blk_info_length*sizeof(trace_bb_build_t) |
| HEAPACCT(ACCT_TRACE)); |
| } |
| |
| /* case 7966: don't initialize at all for hotp_only |
| * FIXME: could set initial sizes to 0 for all configurations, instead |
| */ |
| if (!RUNNING_WITHOUT_CODE_CACHE()) { |
| for (i = 0; i < md->thead_table.capacity; i++) { |
| trace_head_counter_t *e = md->thead_table.counter_table[i]; |
| while (e) { |
| trace_head_counter_t *nexte = e->next; |
| COUNTER_FREE(dcontext, e, sizeof(trace_head_counter_t) |
| HEAPACCT(ACCT_THCOUNTER)); |
| e = nexte; |
| } |
| md->thead_table.counter_table[i] = NULL; |
| } |
| COUNTER_FREE(dcontext, md->thead_table.counter_table, |
| md->thead_table.capacity*sizeof(trace_head_counter_t*) |
| HEAPACCT(ACCT_THCOUNTER)); |
| } |
| heap_free(dcontext, md, sizeof(monitor_data_t) HEAPACCT(ACCT_TRACE)); |
| #endif |
| } |
| |
| static trace_head_counter_t * |
| thcounter_lookup(dcontext_t *dcontext, app_pc tag) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| trace_head_counter_t *e; |
| uint hindex = HASH_FUNC((ptr_uint_t)tag, &md->thead_table); |
| for (e = md->thead_table.counter_table[hindex]; e; e = e->next) { |
| if (e->tag == tag) |
| return e; |
| } |
| return NULL; |
| } |
| |
| static trace_head_counter_t * |
| thcounter_add(dcontext_t *dcontext, app_pc tag) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| uint hindex; |
| /* counters may be persistent while fragment comes and goes from cache */ |
| trace_head_counter_t *e = thcounter_lookup(dcontext, tag); |
| if (e) |
| return e; |
| e = (trace_head_counter_t *) |
| COUNTER_ALLOC(dcontext, sizeof(trace_head_counter_t) HEAPACCT(ACCT_THCOUNTER)); |
| e->tag = tag; |
| e->counter = 0; |
| hindex = HASH_FUNC((ptr_uint_t)e->tag, &md->thead_table); |
| e->next = md->thead_table.counter_table[hindex]; |
| md->thead_table.counter_table[hindex] = e; |
| return e; |
| } |
| |
| #if 0 /* not used */ |
| /* delete the trace head entry corresponding to tag if it exists */ |
| static void |
| thcounter_remove(dcontext_t *dcontext, app_pc tag) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| trace_head_counter_t *e, *prev_e = NULL; |
| uint hindex = HASH_FUNC((ptr_uint_t)tag, &md->thead_table); |
| for (e = md->thead_table.counter_table[hindex]; e; prev_e = e, e = e->next) { |
| if (e->tag == tag) { |
| if (prev_e) |
| prev_e->next = e->next; |
| else |
| md->thead_table.counter_table[hindex] = e->next; |
| COUNTER_FREE(dcontext, e, sizeof(trace_head_counter_t) HEAPACCT(ACCT_THCOUNTER)); |
| break; |
| } |
| } |
| } |
| #endif |
| |
| /* Deletes all trace head entries in [start,end) */ |
| void |
| thcounter_range_remove(dcontext_t *dcontext, app_pc start, app_pc end) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| trace_head_counter_t *e, *prev_e = NULL, *next_e; |
| uint i; |
| /* ensure no synch needed */ |
| ASSERT(dcontext == get_thread_private_dcontext() || |
| is_self_flushing() || is_self_allsynch_flushing()); |
| for (i = 0; i < md->thead_table.capacity; i++) { |
| for (e = md->thead_table.counter_table[i], prev_e = NULL; |
| e != NULL; e = next_e) { |
| next_e = e->next; |
| if (e->tag >= start && e->tag < end) { |
| if (prev_e != NULL) |
| prev_e->next = next_e; |
| else |
| md->thead_table.counter_table[i] = next_e; |
| COUNTER_FREE(dcontext, e, sizeof(trace_head_counter_t) |
| HEAPACCT(ACCT_THCOUNTER)); |
| } else |
| prev_e = e; |
| } |
| } |
| } |
| |
| bool |
| is_building_trace(dcontext_t *dcontext) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| return (md->trace_tag != NULL); |
| } |
| |
| app_pc |
| cur_trace_tag(dcontext_t *dcontext) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| return md->trace_tag; |
| } |
| |
| void * |
| cur_trace_vmlist(dcontext_t *dcontext) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| return md->trace_vmlist; |
| } |
| |
| static void |
| reset_trace_state(dcontext_t *dcontext, bool grab_link_lock) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| uint i; |
| /* reset the trace buffer */ |
| instrlist_init(&(md->trace)); |
| #ifdef CLIENT_INTERFACE |
| if (instrlist_first(&md->unmangled_ilist) != NULL) |
| instrlist_clear(dcontext, &md->unmangled_ilist); |
| instrlist_init(&md->unmangled_ilist); |
| if (md->unmangled_bb_ilist != NULL) |
| instrlist_clear_and_destroy(dcontext, md->unmangled_bb_ilist); |
| md->unmangled_bb_ilist = NULL; |
| #endif |
| md->trace_buf_top = 0; |
| ASSERT(md->trace_vmlist == NULL); |
| for (i = 0; i < md->num_blks; i++) { |
| vm_area_destroy_list(dcontext, md->blk_info[i].vmlist); |
| md->blk_info[i].vmlist = NULL; |
| } |
| md->num_blks = 0; |
| |
| /* If shared BBs are being used to build a shared trace, we may have |
| * FRAG_TRACE_BUILDING set on a shared BB w/the same tag (if there is a |
| * BB present -- it could've been deleted for cache management or cache |
| * consistency). Unset the flag so that a trace can be built from it |
| * in the future. |
| */ |
| if (TEST(FRAG_SHARED, md->trace_flags) && DYNAMO_OPTION(shared_bbs)) { |
| /* Look in the shared BB table only since we're only interested |
| * if a shared BB is present. */ |
| fragment_t *bb = fragment_lookup_shared_bb(dcontext, md->trace_tag); |
| /* FRAG_TRACE_BUILDING may not be set if the BB was regenerated, so |
| * we can't expect it to be set simply because the BB is shared. Check |
| * just for the trace bulding flag. |
| */ |
| if (grab_link_lock) |
| acquire_recursive_lock(&change_linking_lock); |
| if (bb != NULL && TEST(FRAG_TRACE_BUILDING, bb->flags)) { |
| /* The regenerate scenario is still racy w/respect to clearing the |
| * flag. The regenerated fragment could have another thread building |
| * a trace from it so the clear would be for the the wrong thread |
| * here. It doesn't cause a correctness problem because the |
| * emit-time race detection logic will catch it. (In testing w/IIS, |
| * we've seen very, very few emit-time aborts -- < 1% of all races.) |
| */ |
| ASSERT(TESTALL(FRAG_SHARED | FRAG_IS_TRACE_HEAD, bb->flags)); |
| STATS_INC(num_trace_building_ip_cleared); |
| bb->flags &= ~FRAG_TRACE_BUILDING; |
| } |
| #ifdef DEBUG |
| /* As noted above, the trace head BB may no longer be present. This |
| * should be rare in most apps but we'll track it w/a counter in case |
| * we see lots of emit-time aborts. |
| */ |
| else { |
| STATS_INC(num_reset_trace_no_trace_head); |
| /* The shared BB may been evicted during trace building and subsequently |
| * re-genned and so wouldn't be marked as FRAG_TRACE_BUILDING. It might |
| * be marked as a trace head, though, so we don't assert anything about |
| * that trait. |
| * FIXME We could add a strong ASSERT about the regen case if we added |
| * a trace_head_id field to monitor_data_t. The field would store the id |
| * of the shared BB trace head that caused trace building to begin. If |
| * a shared trace head isn't found but a shared BB is, the shared BB |
| * id should be greater than trace_head_id. |
| */ |
| } |
| #endif |
| if (grab_link_lock) |
| release_recursive_lock(&change_linking_lock); |
| } |
| md->trace_tag = NULL; /* indicate return to search mode */ |
| md->trace_flags = 0; |
| md->emitted_size = 0; |
| /* flags may not match, e.g., if frag was marked as trace head */ |
| ASSERT(md->last_fragment == NULL || |
| (md->last_fragment_flags & (FRAG_CANNOT_DELETE|FRAG_LINKED_OUTGOING)) == |
| (md->last_fragment->flags & (FRAG_CANNOT_DELETE|FRAG_LINKED_OUTGOING))); |
| md->last_fragment_flags = 0; |
| /* we don't delete last_copy here to avoid issues w/ trace_abort deleting |
| * a fragment we're examining (seg fault, etc.) |
| */ |
| md->last_fragment = NULL; |
| /* note that we don't delete last_copy here as it's needed for pc translation |
| * (at least until -safe_translate_flushed is on) (xref case 8083) |
| */ |
| #ifdef CUSTOM_TRACES_RET_REMOVAL |
| dcontext->call_depth = 0; |
| #endif |
| } |
| |
| bool |
| monitor_delete_would_abort_trace(dcontext_t *dcontext, fragment_t *f) |
| { |
| monitor_data_t *md; |
| if (dcontext == GLOBAL_DCONTEXT) |
| dcontext = get_thread_private_dcontext(); |
| if (dcontext == NULL) |
| return false; |
| md = (monitor_data_t *) dcontext->monitor_field; |
| return ((md->last_fragment == f || dcontext->last_fragment == f) && |
| md->trace_tag > 0); |
| } |
| |
| /* called when a fragment is deleted */ |
| void |
| monitor_remove_fragment(dcontext_t *dcontext, fragment_t *f) |
| { |
| monitor_data_t *md; |
| /* may be a global fragment -- but we still want our local trace data */ |
| if (dcontext == GLOBAL_DCONTEXT) { |
| ASSERT(TEST(FRAG_SHARED, f->flags)); |
| dcontext = get_thread_private_dcontext(); |
| /* may still be null if exiting process -- in which case a nop for us */ |
| if (dcontext == NULL) { |
| if (dynamo_exited) |
| return; |
| ASSERT_NOT_REACHED(); |
| return; /* safe default */ |
| } |
| } |
| md = (monitor_data_t *) dcontext->monitor_field; |
| if (md->last_copy == f) { |
| md->last_copy = NULL; /* no other action required */ |
| STATS_INC(num_trace_private_deletions); |
| } |
| /* Must check to see if the last fragment, which was added to the |
| * trace, is being deleted before we're done with it. |
| * This can happen due to a flush from self-modifying code, |
| * or an munmap. |
| * Must check both last_fragment and last_exit. |
| * May come here before last_exit is set, or may come here after |
| * last_fragment is restored but before last_exit is used. |
| * FIXME: if we do manage to remove the check for last_fragment |
| * here, remove the last_exit clear in end_and_emit_trace |
| */ |
| /* FIXME: case 5593 we may also unnecessarily abort a trace that |
| * starts at the next_tag and last_fragment is really not |
| * related. |
| */ |
| if ((md->last_fragment == f || dcontext->last_fragment == f) && |
| !TEST(FRAG_TEMP_PRIVATE, f->flags)) { |
| if (md->trace_tag > 0) { |
| LOG(THREAD, LOG_MONITOR, 2, |
| "Aborting current trace since F%d was deleted\n", |
| f->id); |
| /* abort current trace, we've lost a link */ |
| trace_abort(dcontext); |
| } |
| /* trace_abort clears last_fragment -- and if not in trace-building |
| * mode, it should not be set! |
| */ |
| ASSERT(md->last_fragment == NULL); |
| if (dcontext->last_fragment == f) |
| last_exit_deleted(dcontext); |
| } |
| } |
| |
| /* Unlink the trace head fragment from any IBT tables in which it is in */ |
| static inline void |
| unlink_ibt_trace_head(dcontext_t *dcontext, fragment_t *f) |
| { |
| ASSERT(TEST(FRAG_IS_TRACE_HEAD, f->flags)); |
| if (DYNAMO_OPTION(shared_bb_ibt_tables)) { |
| ASSERT(TEST(FRAG_SHARED, f->flags)); |
| if (fragment_prepare_for_removal(GLOBAL_DCONTEXT, f)) { |
| LOG(THREAD, LOG_FRAGMENT, 3, |
| " F%d("PFX") removed as trace head IBT\n", f->id, f->tag); |
| STATS_INC(num_th_bb_ibt_unlinked); |
| } |
| } |
| else { |
| |
| /* To preserve the current paradigm of trace head-ness as a shared |
| * property, we must unlink the fragment from every thread's IBT tables. |
| * This is a heavyweight operation compared to the use of a shared table |
| * and requires additional changes -- for example, get_list_of_threads() |
| * can't currently be called from here. If we change trace head-ness |
| * to a private property, this becomes very easy and more performant |
| * than the use of a shared table. (Case 3530 discusses private vs shared |
| * trace head-ness.) |
| */ |
| thread_record_t **threads; |
| int num_threads; |
| int i; |
| |
| ASSERT_NOT_IMPLEMENTED(false); |
| /* fragment_prepare_for_removal will unlink from shared ibt; we cannot |
| * remove completely here */ |
| fragment_remove_from_ibt_tables(dcontext, f, false/*leave in shared ibt*/); |
| /* Remove the fragment from other thread's tables. */ |
| mutex_lock(&thread_initexit_lock); |
| get_list_of_threads(&threads, &num_threads); |
| mutex_unlock(&thread_initexit_lock); |
| for (i = 0; i < num_threads; i++) { |
| dcontext_t *tgt_dcontext = threads[i]->dcontext; |
| LOG(THREAD, LOG_FRAGMENT, 2, |
| " considering thread %d/%d = "TIDFMT"\n", i+1, num_threads, |
| threads[i]->id); |
| ASSERT(is_thread_known(tgt_dcontext->owning_thread)); |
| fragment_prepare_for_removal(TEST(FRAG_SHARED, f->flags) ? |
| GLOBAL_DCONTEXT : tgt_dcontext, f); |
| } |
| global_heap_free(threads, num_threads*sizeof(thread_record_t*) |
| HEAPACCT(ACCT_THREAD_MGT)); |
| } |
| } |
| |
| /* if f is shared, caller MUST hold the change_linking_lock */ |
| void |
| mark_trace_head(dcontext_t *dcontext_in, fragment_t *f, fragment_t *src_f, |
| linkstub_t *src_l) |
| { |
| bool protected = false; |
| cache_pc coarse_stub = NULL, coarse_body = NULL; |
| coarse_info_t *info = NULL; |
| /* Case 9021: handle GLOBAL_DCONTEXT coming in via flush_fragments_synchall |
| * removing a fine trace that triggers a shift to its shadowed coarse trace |
| * head and a link_fragment_incoming on that head. |
| * Using the flushing thread's dcontext for the trace head counter is fine |
| * and shouldn't limit its becoming a new trace again. |
| */ |
| dcontext_t *dcontext = |
| (dcontext_in == GLOBAL_DCONTEXT) ? get_thread_private_dcontext() : dcontext_in; |
| ASSERT(dcontext != NULL); |
| |
| LOG(THREAD, LOG_MONITOR, 4, "marking F%d ("PFX") as trace head\n", f->id, f->tag); |
| ASSERT(!TEST(FRAG_IS_TRACE, f->flags)); |
| ASSERT(!NEED_SHARED_LOCK(f->flags) || |
| self_owns_recursive_lock(&change_linking_lock)); |
| |
| if (thcounter_lookup(dcontext, f->tag) == NULL) { |
| protected = local_heap_protected(dcontext); |
| if (protected) { |
| /* unprotect local heap */ |
| protect_local_heap(dcontext, WRITABLE); |
| } |
| /* FIXME: private counter tables are used even for !shared_bbs since the |
| * counter field is not in fragment_t... |
| * Move counters to Future for all uses, giving us persistent counters too! |
| */ |
| thcounter_add(dcontext, f->tag); |
| } else { |
| /* This does happen for resurrected fragments and coarse-grain fragments */ |
| STATS_INC(trace_head_remark); |
| } |
| LOG(THREAD, LOG_MONITOR, 4, "mark_trace_head: flags 0x%08x\n", f->flags); |
| f->flags |= FRAG_IS_TRACE_HEAD; |
| LOG(THREAD, LOG_MONITOR, 4, "\tnow, flags 0x%08x\n", f->flags); |
| /* must unlink incoming links so that the counter will increment */ |
| LOG(THREAD, LOG_MONITOR, 4, "unlinking incoming for new trace head F%d ("PFX")\n", |
| f->id, f->tag); |
| |
| if (TEST(FRAG_COARSE_GRAIN, f->flags)) { |
| /* For coarse trace heads, trace headness depends on the path taken |
| * (more specifically, on the entrance stub taken). If we don't have |
| * any info on src_f we use f's unit. |
| */ |
| info = get_fragment_coarse_info(src_f == NULL ? f : src_f); |
| if (info == NULL) { |
| /* case 8632: A fine source may not be in a coarse region, |
| * so there is nothing to unlink. |
| */ |
| } else { |
| /* See if there is an entrance stub for this target in the source unit */ |
| fragment_coarse_lookup_in_unit(dcontext, info, f->tag, |
| &coarse_stub, &coarse_body); |
| /* FIXME: don't allow marking for frozen units w/ no src info: |
| * shouldn't happen, except perhaps CLIENT_INTERFACE |
| */ |
| ASSERT(src_f != NULL || !info->frozen); |
| if (src_f != NULL && TEST(FRAG_COARSE_GRAIN, src_f->flags) && |
| src_l != NULL && LINKSTUB_NORMAL_DIRECT(src_l->flags)) { |
| direct_linkstub_t *dl = (direct_linkstub_t *) src_l; |
| if (dl->stub_pc != NULL && coarse_is_entrance_stub(dl->stub_pc)) { |
| if (coarse_stub == NULL) { |
| /* Case 9708: For a new fragment whose target exists but |
| * is another unit and does not yet have an entrance |
| * stub in the new fragment's unit, we will come here |
| * w/o that entrance stub being in the htable. We rely |
| * on dl->stub_pc being set to that entrance stub. |
| */ |
| coarse_stub = dl->stub_pc; |
| } else |
| ASSERT(dl->stub_pc == NULL || dl->stub_pc == coarse_stub); |
| } |
| } |
| if (coarse_stub != NULL) { |
| ASSERT(coarse_is_entrance_stub(coarse_stub)); |
| /* FIXME: our coarse lookups do not always mark trace headness |
| * (in particular, fragment_coarse_link_wrapper() calling |
| * fragment_coarse_lookup_wrapper() does not), and we |
| * un-mark as trace heads when linking incoming (case 8907), |
| * so we may get here for an existing trace head. |
| */ |
| if (!coarse_is_trace_head_in_own_unit(dcontext, f->tag, coarse_stub, |
| coarse_body, true, |
| (src_f == NULL) ? info : NULL)) { |
| ASSERT(coarse_body == NULL /* new fragment, or in other unit */ || |
| entrance_stub_jmp_target(coarse_stub) == coarse_body); |
| if (coarse_body == NULL && |
| /* if stub is from tag's own unit */ |
| (src_f == NULL || get_fragment_coarse_info(f) == info)) { |
| /* if marking new fragment, not in htable yet */ |
| coarse_body = FCACHE_ENTRY_PC(f); |
| } |
| coarse_mark_trace_head(dcontext, f, info, coarse_stub, coarse_body); |
| } |
| } else { |
| LOG(THREAD, LOG_MONITOR, 4, "\tno local stub, deferring th unlink\n"); |
| /* Could be that this is a new fragment, in which case its entrance |
| * stub will be unlinked and its body pc added to the th table in |
| * link_new_coarse_grain_fragment(); or the source is a fine |
| * fragment corresponding to another unit and thus no entrance stub |
| * or htable changes are necessary. |
| */ |
| STATS_INC(coarse_th_from_fine); |
| /* id comparison could have a race w/ private frag gen so a curiosity */ |
| ASSERT_CURIOSITY(GLOBAL_STAT(num_fragments) == f->id || |
| (src_f != NULL && |
| !TEST(FRAG_COARSE_GRAIN, src_f->flags))); |
| } |
| } |
| } else |
| unlink_fragment_incoming(dcontext, f); |
| |
| if (DYNAMO_OPTION(bb_ibl_targets)) |
| unlink_ibt_trace_head(dcontext, f); |
| #ifdef TRACE_HEAD_CACHE_INCR |
| /* we deliberately link to THCI in two steps (unlink and then |
| * re-link), since combined they aren't atomic, separate atomic |
| * steps w/ ok intermediate (go back to DR) is fine |
| */ |
| /* must re-link incoming links to point to trace_head_incr routine |
| * FIXME: we get called in the middle of linking new fragments, so |
| * we end up linking some incoming links twice (no harm done except |
| * a waste of time) -- how fix it? |
| * When fix it, change link_branch to assert that !already linked |
| */ |
| link_fragment_incoming(dcontext, f, false/*not new*/); |
| #endif |
| STATS_INC(num_trace_heads_marked); |
| /* caller is either dispatch or inside emit_fragment, they take care of |
| * re-protecting fcache |
| */ |
| if (protected) { |
| /* re-protect local heap */ |
| protect_local_heap(dcontext, READONLY); |
| } |
| } |
| |
| /* can ONLY be called by should_be_trace_head_internal, separated out |
| * to avoid recursion when re-verifying with change_linking_lock held |
| */ |
| static bool |
| should_be_trace_head_internal_unsafe(dcontext_t *dcontext, fragment_t *from_f, |
| linkstub_t *from_l, app_pc to_tag, uint to_flags, |
| bool trace_sysenter_exit) |
| { |
| app_pc from_tag; |
| uint from_flags; |
| |
| if (DYNAMO_OPTION(disable_traces) || |
| TEST(FRAG_IS_TRACE, to_flags) || |
| TEST(FRAG_IS_TRACE_HEAD, to_flags) || |
| TEST(FRAG_CANNOT_BE_TRACE, to_flags)) |
| return false; |
| |
| /* We know that the to_flags pass the test. */ |
| if (trace_sysenter_exit) |
| return true; |
| |
| from_tag = from_f->tag; |
| from_flags = from_f->flags; |
| |
| /* A trace head is either |
| * 1) a link from a trace, or |
| * 2) a backward direct branch |
| * Watch out -- since we stop building traces at trace heads, |
| * too many can hurt performance, especially if bbs do not follow |
| * direct ctis. We can use shadowed bbs to go through trace |
| * head and trace boundaries (CUSTOM_TRACES). |
| */ |
| /* trace heads can be created across private/shared cache bounds */ |
| if (TEST(FRAG_IS_TRACE, from_flags) || |
| (to_tag <= from_tag && LINKSTUB_DIRECT(from_l->flags))) |
| return true; |
| |
| DOSTATS({ |
| if (!DYNAMO_OPTION(disable_traces) && |
| !TEST(FRAG_IS_TRACE, to_flags) && |
| !TEST(FRAG_IS_TRACE_HEAD, to_flags) && |
| !TEST(FRAG_CANNOT_BE_TRACE, to_flags)) { |
| STATS_INC(num_wannabe_traces); |
| } |
| }); |
| return false; |
| } |
| |
| |
| /* Returns TRACE_HEAD_* flags indicating whether to_tag should be a |
| * trace head based on fragment traits and/or control flow between the |
| * link stub and the to_tag/to_flags. |
| * |
| * For -shared_bbs, will return TRACE_HEAD_OBTAINED_LOCK if the |
| * change_linking_lock is not already held (meaning from_l->fragment is |
| * private) and the to_tag is FRAG_SHARED and TRACE_HEAD_YES is being returned, |
| * since the change_linking_lock must be held and the TRACE_HEAD_YES result |
| * re-verified. In that case the caller must free the change_linking_lock. |
| * If trace_sysenter_exit = true, control flow rules are not checked, i.e., the |
| * from_l and to_tag params are not checked. This is provided to capture |
| * the case where the most recent cache exit was prior to a non-ignorable |
| * syscall via a SYSENTER instruction. See comments in monitor_cache_exit for |
| * details. This is the exception, not the norm. |
| * |
| * If the link stub is non-NULL, trace_sysenter_exit does NOT need to |
| * be set. |
| * |
| * FIXME This is a stopgap soln. The long-term fix is to not count on |
| * a link stub being passed in but rather pass in the most recent fragment's |
| * flags & tag explicitly. The flags & tag can be stored in a dcontext-private |
| * monitor structure, one that is not shared across call backs. |
| */ |
| static uint |
| should_be_trace_head_internal(dcontext_t *dcontext, fragment_t *from_f, linkstub_t *from_l, |
| app_pc to_tag, uint to_flags, bool have_link_lock, |
| bool trace_sysenter_exit) |
| { |
| uint result = 0; |
| if (should_be_trace_head_internal_unsafe(dcontext, from_f, from_l, to_tag, to_flags, |
| trace_sysenter_exit)) { |
| result |= TRACE_HEAD_YES; |
| ASSERT(!have_link_lock || self_owns_recursive_lock(&change_linking_lock)); |
| if (!have_link_lock) { |
| /* If the target is shared, we must obtain the change_linking_lock and |
| * re-verify that it hasn't already been marked. |
| * If source is also shared then lock should already be held . |
| */ |
| ASSERT(from_l == NULL || !NEED_SHARED_LOCK(from_f->flags)); |
| if (NEED_SHARED_LOCK(to_flags)) { |
| acquire_recursive_lock(&change_linking_lock); |
| if (should_be_trace_head_internal_unsafe(dcontext, from_f, from_l, |
| to_tag, to_flags, |
| trace_sysenter_exit)) { |
| result |= TRACE_HEAD_OBTAINED_LOCK; |
| } else { |
| result &= ~TRACE_HEAD_YES; |
| release_recursive_lock(&change_linking_lock); |
| } |
| } |
| } |
| } |
| return result; |
| } |
| |
| /* Returns TRACE_HEAD_* flags indicating whether to_tag should be a |
| * trace head based on fragment traits and/or control flow between the |
| * link stub and the to_tag/to_flags. |
| * |
| * For -shared_bbs, will return TRACE_HEAD_OBTAINED_LOCK if the |
| * change_linking_lock is not already held (meaning from_l->fragment is |
| * private) and the to_tag is FRAG_SHARED and TRACE_HEAD_YES is being returned, |
| * since the change_linking_lock must be held and the TRACE_HEAD_YES result |
| * re-verified. In that case the caller must free the change_linking_lock. |
| */ |
| uint |
| should_be_trace_head(dcontext_t *dcontext, fragment_t *from_f, linkstub_t *from_l, |
| app_pc to_tag, uint to_flags, bool have_link_lock) |
| { |
| return should_be_trace_head_internal(dcontext, from_f, from_l, to_tag, to_flags, |
| have_link_lock, false); |
| } |
| |
| /* If upgrades to_f to a trace head, returns true, else returns false. |
| */ |
| static bool |
| check_for_trace_head(dcontext_t *dcontext, fragment_t *from_f, linkstub_t *from_l, |
| fragment_t *to_f, bool have_link_lock, bool trace_sysenter_exit) |
| { |
| if (!DYNAMO_OPTION(disable_traces)) { |
| uint th = should_be_trace_head_internal(dcontext, from_f, from_l, to_f->tag, |
| to_f->flags, have_link_lock, |
| trace_sysenter_exit); |
| if (TEST(TRACE_HEAD_YES, th)) { |
| mark_trace_head(dcontext, to_f, from_f, from_l); |
| if (TEST(TRACE_HEAD_OBTAINED_LOCK, th)) |
| release_recursive_lock(&change_linking_lock); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* Linkability rules involving traces and trace heads. |
| * This routines also marks new traces heads if mark_new_trace_head is true. |
| * The current implementation of this routine assumes that we don't |
| * want to link potential trace heads. A potential trace head is any |
| * block fragment that is reached by a backward (direct) branch. |
| */ |
| bool |
| monitor_is_linkable(dcontext_t *dcontext, fragment_t *from_f, linkstub_t *from_l, |
| fragment_t *to_f, bool have_link_lock, bool mark_new_trace_head) |
| { |
| /* common case: both traces */ |
| if (TEST(FRAG_IS_TRACE, from_f->flags) && TEST(FRAG_IS_TRACE, to_f->flags)) |
| return true; |
| if (DYNAMO_OPTION(disable_traces)) |
| return true; |
| #ifndef TRACE_HEAD_CACHE_INCR |
| /* no link case -- block is a trace head */ |
| if (TEST(FRAG_IS_TRACE_HEAD, to_f->flags) && !DYNAMO_OPTION(disable_traces)) |
| return false; |
| #endif |
| if (mark_new_trace_head) { |
| uint th = should_be_trace_head(dcontext, from_f, from_l, to_f->tag, to_f->flags, |
| have_link_lock); |
| if (TEST(TRACE_HEAD_YES, th)) { |
| mark_trace_head(dcontext, to_f, from_f, from_l); |
| if (TEST(TRACE_HEAD_OBTAINED_LOCK, th)) |
| release_recursive_lock(&change_linking_lock); |
| #ifdef TRACE_HEAD_CACHE_INCR |
| /* fine to link to trace head |
| * link will end up pointing not to fcache_return but to trace_head_incr |
| */ |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| } |
| return true; /* otherwise */ |
| } |
| |
| /* If necessary, re-allocates the trace buffer to a larger size to |
| * hold add_size more bytes. |
| * If the resulting size will exceed the maximum trace |
| * buffer size, returns false, else returns true. |
| * FIXME: now that we have a real max limit on emitted trace size, |
| * should we have an unbounded trace buffer size? |
| * Also increases the size of the block array if necessary. |
| */ |
| static bool |
| make_room_in_trace_buffer(dcontext_t *dcontext, uint add_size, fragment_t *f) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| uint size; |
| uint new_blks; |
| ssize_t realloc_shift; |
| instr_t *instr; |
| instrlist_t *trace = &md->trace; |
| |
| size = md->trace_buf_size; |
| if (add_size > (size - md->trace_buf_top)) { |
| byte *new_tbuf; |
| |
| if (size == 0) |
| size = INITIAL_TRACE_BUFFER_SIZE; |
| while (add_size > (size - md->trace_buf_top)) |
| size *= 2; |
| if (size > MAX_TRACE_BUFFER_SIZE) { |
| LOG(THREAD, LOG_MONITOR, 2, |
| "Not letting trace buffer grow to %d bytes\n", size); |
| return false; |
| } |
| /* re-allocate trace buf */ |
| LOG(THREAD, LOG_MONITOR, 3, |
| "\nRe-allocating trace buffer from %d to %d bytes\n", |
| md->trace_buf_size, size); |
| new_tbuf = heap_alloc(dcontext, size HEAPACCT(ACCT_TRACE)); |
| if (md->trace_buf != NULL) { |
| /* copy entire thing, just in case */ |
| IF_X64(ASSERT_NOT_REACHED()); /* can't copy w/o re-relativizing! */ |
| memcpy(new_tbuf, md->trace_buf, md->trace_buf_size); |
| heap_free(dcontext, md->trace_buf, md->trace_buf_size HEAPACCT(ACCT_TRACE)); |
| realloc_shift = new_tbuf - md->trace_buf; |
| /* need to walk through trace instr_t list and update addresses */ |
| instr = instrlist_first(trace); |
| while (instr != NULL) { |
| byte *b = instr_get_raw_bits(instr); |
| if (b >= md->trace_buf && |
| b < md->trace_buf+md->trace_buf_size) |
| instr_shift_raw_bits(instr, realloc_shift); |
| instr = instr_get_next(instr); |
| } |
| } |
| md->trace_buf = new_tbuf; |
| md->trace_buf_size = size; |
| } |
| if ((f->flags & FRAG_IS_TRACE) != 0) { |
| trace_only_t *t = TRACE_FIELDS(f); |
| new_blks = t->num_bbs; |
| } else |
| new_blks = 1; |
| if (md->num_blks + new_blks >= md->blk_info_length) { |
| trace_bb_build_t *new_buf; |
| uint new_len = md->blk_info_length; |
| if (new_len == 0) |
| new_len = INITIAL_NUM_BLKS; |
| do { |
| new_len *= 2; |
| } while (md->num_blks + new_blks >= new_len); |
| new_buf = (trace_bb_build_t *) |
| HEAP_ARRAY_ALLOC(dcontext, trace_bb_build_t, new_len, ACCT_TRACE, true); |
| /* PR 306761 relies on being zeroed, as does reset_trace_state to free vmlists */ |
| memset(new_buf, 0, sizeof(trace_bb_build_t)*new_len); |
| LOG(THREAD, LOG_MONITOR, 3, "\nRe-allocating trace blks from %d to %d\n", |
| md->blk_info_length, new_len); |
| if (md->blk_info != NULL) { |
| memcpy(new_buf, md->blk_info, md->blk_info_length*sizeof(trace_bb_build_t)); |
| HEAP_ARRAY_FREE(dcontext, md->blk_info, trace_bb_build_t, md->blk_info_length, |
| ACCT_TRACE, true); |
| } |
| md->blk_info = new_buf; |
| md->blk_info_length = new_len; |
| } |
| return true; |
| } |
| |
| static int |
| trace_exit_stub_size_diff(dcontext_t *dcontext, fragment_t *f) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| int size = 0; |
| linkstub_t *l; |
| for (l = FRAGMENT_EXIT_STUBS(f); l != NULL; l = LINKSTUB_NEXT_EXIT(l)) { |
| if (linkstub_shares_next_stub(dcontext, f, l)) { |
| /* add stub size back in since we don't know if trace will also |
| * share (if client adds custom code, etc.) |
| * this also makes fixup_last_cti() code simpler since it can |
| * blindly remove and ignore sharing. |
| * if the trace does share for a final bb, we remove in |
| * end_and_emit_trace(). |
| */ |
| size += local_exit_stub_size(dcontext, EXIT_TARGET_TAG(dcontext, f, l), |
| md->trace_flags); |
| } else { |
| /* f's stub size will be considered as part of f->size so we need |
| * the difference here, not the absolute new size |
| */ |
| size += local_exit_stub_size(dcontext, EXIT_TARGET_TAG(dcontext, f, l), |
| md->trace_flags) - |
| local_exit_stub_size(dcontext, EXIT_TARGET_TAG(dcontext, f, l), |
| f->flags); |
| } |
| } |
| return size; |
| } |
| |
| /* don't build a single trace more than 1/8 of max trace cache size */ |
| enum { MAX_TRACE_FRACTION_OF_CACHE = 8 }; |
| |
| /* Estimates the increase in the emitted size of the current trace if f were |
| * to be added to it. |
| * If that size exceeds the maximum fragment size, or a fraction of the maximum |
| * trace cache size, returns false. |
| * Returns the size calculations in two different parts: |
| * res_add_size is the accurate value of the body and exit stubs addition, while |
| * res_prev_mangle_size is an upper bound estimate of the change in size when |
| * the prior block in the trace is mangled to connect to f. |
| */ |
| static bool |
| get_and_check_add_size(dcontext_t *dcontext, fragment_t *f, uint *res_add_size, |
| uint *res_prev_mangle_size) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| uint add_size = f->size - fragment_prefix_size(f->flags) + |
| trace_exit_stub_size_diff(dcontext, f) + |
| (PAD_FRAGMENT_JMPS(md->trace_flags) ? extend_trace_pad_bytes(f) : 0); |
| /* we estimate the size change from mangling the previous block to |
| * connect to this block if we were to add it |
| */ |
| uint prev_mangle_size = TRACE_CTI_MANGLE_SIZE_UPPER_BOUND; |
| uint total_size = md->emitted_size + add_size + prev_mangle_size; |
| /* check whether adding f will push the trace over the edge */ |
| bool ok = (total_size <= MAX_FRAGMENT_SIZE); |
| ASSERT(!TEST(FRAG_SELFMOD_SANDBOXED, f->flags)); /* no support for selfmod */ |
| ASSERT(!TEST(FRAG_IS_TRACE, f->flags)); /* no support for traces */ |
| LOG(THREAD, LOG_MONITOR, 4, "checking trace size: currently %d, add estimate %d\n" |
| "\t(body: %d, stubs: %d, pad: %d, mangle est: %d)\n" |
| "\t=> %d vs %d, %d vs %d\n", |
| md->emitted_size, add_size + prev_mangle_size, |
| f->size - fragment_prefix_size(f->flags), |
| trace_exit_stub_size_diff(dcontext, f), |
| (PAD_FRAGMENT_JMPS(md->trace_flags) ? extend_trace_pad_bytes(f) : 0), |
| prev_mangle_size, total_size, MAX_FRAGMENT_SIZE, |
| total_size*MAX_TRACE_FRACTION_OF_CACHE, DYNAMO_OPTION(cache_trace_max)); |
| /* don't create traces anywhere near max trace cache size */ |
| if (ok && DYNAMO_OPTION(cache_trace_max) > 0 && |
| total_size*MAX_TRACE_FRACTION_OF_CACHE > DYNAMO_OPTION(cache_trace_max)) |
| ok = false; |
| if (res_add_size != NULL) |
| *res_add_size = add_size; |
| if (res_prev_mangle_size != NULL) |
| *res_prev_mangle_size = prev_mangle_size; |
| return ok; |
| } |
| |
| /* propagate flags from a non-head bb component of a trace to the trace itself */ |
| static inline uint |
| trace_flags_from_component_flags(uint flags) |
| { |
| return (flags & (FRAG_HAS_SYSCALL | FRAG_HAS_DIRECT_CTI IF_X64(| FRAG_32_BIT))); |
| } |
| |
| static inline uint |
| trace_flags_from_trace_head_flags(uint head_flags) |
| { |
| uint trace_flags = 0; |
| if (!INTERNAL_OPTION(unsafe_ignore_eflags_prefix)) { |
| trace_flags |= (head_flags & FRAG_WRITES_EFLAGS_6); |
| trace_flags |= (head_flags & FRAG_WRITES_EFLAGS_OF); |
| } |
| trace_flags |= FRAG_IS_TRACE; |
| trace_flags |= trace_flags_from_component_flags(head_flags); |
| if (DYNAMO_OPTION(shared_traces)) { |
| /* for now, all traces are shared */ |
| trace_flags |= FRAG_SHARED; |
| } |
| return trace_flags; |
| } |
| |
| /* Be careful with the case where the current fragment f to be executed |
| * has the same tag as the one we're emitting as a trace. |
| */ |
| static fragment_t * |
| end_and_emit_trace(dcontext_t *dcontext, fragment_t *cur_f) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| fragment_t *trace_head_f = NULL; |
| app_pc tag = md->trace_tag; |
| app_pc cur_f_tag = cur_f->tag; /* grab now before potential cur_f deletion */ |
| instrlist_t *trace = &md->trace; |
| fragment_t *trace_f; |
| trace_only_t *trace_tr; |
| bool replace_trace_head = false; |
| fragment_t wrapper; |
| uint i; |
| #if defined(DEBUG) || defined(INTERNAL) || defined(CLIENT_INTERFACE) |
| /* was the trace passed through optimizations or the client interface? */ |
| bool externally_mangled = false; |
| #endif |
| /* we cannot simply upgrade a basic block fragment |
| * to a trace b/c traces have prefixes that basic blocks don't! |
| */ |
| |
| DOSTATS({ |
| /* static count last_exit statistics case 4817 */ |
| if (LINKSTUB_INDIRECT(dcontext->last_exit->flags)) { |
| STATS_INC(num_traces_end_at_ibl); |
| if (EXIT_IS_CALL(dcontext->last_exit->flags)) { |
| STATS_INC(num_traces_end_at_ibl_ind_call); |
| } else if (EXIT_IS_JMP(dcontext->last_exit->flags)) { |
| /* shared system call (case 4995) */ |
| if (IS_SHARED_SYSCALLS_LINKSTUB(dcontext->last_exit)) |
| STATS_INC(num_traces_end_at_ibl_syscall); |
| else |
| STATS_INC(num_traces_end_at_ibl_ind_jump); |
| } else if (TEST(LINK_RETURN, dcontext->last_exit->flags)) { |
| STATS_INC(num_traces_end_at_ibl_return); |
| }; |
| } |
| }); |
| |
| #ifdef CLIENT_INTERFACE |
| if (md->pass_to_client) { |
| /* PR 299808: we pass the unmangled ilist we've been maintaining to the |
| * client, and we have to then re-mangle and re-connect. |
| */ |
| dr_emit_flags_t emitflags = instrument_trace(dcontext, tag, &md->unmangled_ilist, |
| false/*!recreating*/); |
| externally_mangled = true; |
| if (TEST(DR_EMIT_STORE_TRANSLATIONS, emitflags)) { |
| /* PR 214962: let client request storage instead of recreation */ |
| md->trace_flags |= FRAG_HAS_TRANSLATION_INFO; |
| } /* else, leave translation flag if any bb requested it */ |
| |
| /* We now have to re-mangle and re-chain */ |
| if (!mangle_trace(dcontext, &md->unmangled_ilist, md)) { |
| trace_abort(dcontext); |
| STATS_INC(num_aborted_traces_client); |
| trace_f = NULL; |
| goto end_and_emit_trace_return; |
| } |
| instrlist_clear(dcontext, &md->trace); |
| md->trace = md->unmangled_ilist; |
| instrlist_init(&md->unmangled_ilist); |
| } |
| #endif |
| |
| if (INTERNAL_OPTION(cbr_single_stub) && |
| final_exit_shares_prev_stub(dcontext, trace, md->trace_flags)) { |
| /* while building, we re-add shared stub since not sure if |
| * trace will also share -- here we find out and adjust |
| */ |
| instr_t *last = instrlist_last(trace); |
| app_pc target; |
| ASSERT(last != NULL && instr_is_exit_cti(last)); |
| target = opnd_get_pc(instr_get_target(last)); |
| md->emitted_size -= local_exit_stub_size(dcontext, target, md->trace_flags); |
| } |
| |
| if (DYNAMO_OPTION(speculate_last_exit) |
| #ifdef HASHTABLE_STATISTICS |
| || INTERNAL_OPTION(speculate_last_exit_stats) || INTERNAL_OPTION(stay_on_trace_stats) |
| #endif |
| ) { |
| /* FIXME: speculation of last exit (case 4817) is currently |
| * only implemented for traces. If we have a sharable version |
| * of fixup_last_cti() to pass that information based on instr |
| * list information about last exit we can use in |
| * emit_fragment_common(). That way both bb's and traces may |
| * have speculation added. |
| */ |
| if (TEST(FRAG_MUST_END_TRACE, cur_f->flags)) { |
| /* This routine may be also reached on MUST_END_TRACE |
| * and in that case we haven't executed yet the last |
| * bb, so don't really know how to fix the last IBL |
| */ |
| /* FIXME: add a stat when such are ending at an IBL */ |
| |
| ASSERT_CURIOSITY(dcontext->next_tag == cur_f->tag); |
| STATS_INC(num_traces_at_must_end_trace); |
| } else { |
| /* otherwise last_exit is the last trace BB and next_tag |
| * is the current IBL target that we'll always speculate */ |
| if (LINKSTUB_INDIRECT(dcontext->last_exit->flags)) { |
| LOG(THREAD, LOG_MONITOR, 2, |
| "Last trace IBL exit (trace "PFX", next_tag "PFX")\n", |
| tag, dcontext->next_tag); |
| ASSERT_CURIOSITY(dcontext->next_tag != NULL); |
| if (DYNAMO_OPTION(speculate_last_exit)) { |
| app_pc speculate_next_tag = dcontext->next_tag; |
| #ifdef SPECULATE_LAST_EXIT_STUDY |
| /* for a performance study: add overhead on |
| * all IBLs that never hit by comparing to a 0xbad tag */ |
| speculate_next_tag = 0xbad; |
| #endif |
| md->emitted_size += |
| append_trace_speculate_last_ibl(dcontext, trace, |
| speculate_next_tag, |
| false); |
| } else { |
| #ifdef HASHTABLE_STATISTICS |
| ASSERT(INTERNAL_OPTION(stay_on_trace_stats) || |
| INTERNAL_OPTION(speculate_last_exit_stats)); |
| DOSTATS({ |
| md->emitted_size += |
| append_ib_trace_last_ibl_exit_stat(dcontext, trace, |
| INTERNAL_OPTION(speculate_last_exit_stats) ? |
| dcontext->next_tag : NULL); |
| }); |
| #endif |
| } |
| } |
| } |
| } |
| |
| DOLOG(2, LOG_MONITOR, { |
| uint i; |
| LOG(THREAD, LOG_MONITOR, 2, "Ending and emitting hot trace (tag "PFX")\n", tag); |
| if (stats->loglevel >= 4) { |
| instrlist_disassemble(dcontext, md->trace_tag, trace, THREAD); |
| LOG(THREAD, LOG_MONITOR, 4, "\n"); |
| } |
| LOG(THREAD, LOG_MONITOR, 2, "Trace blocks are:\n"); |
| for (i=0; i<md->num_blks; i++) { |
| LOG(THREAD, LOG_MONITOR, 2, "\tblock %3d == "PFX" (%d exit(s))\n", |
| i, md->blk_info[i].info.tag, |
| IF_RETURN_AFTER_CALL_ELSE(md->blk_info[i].info.num_exits, 0)); |
| } |
| }); |
| |
| /* WARNING: if you change how optimizations are performed, you |
| * must change recreate_app_state in arch/arch.c as well |
| */ |
| |
| #ifdef INTERNAL |
| if (dynamo_options.optimize |
| # ifdef SIDELINE |
| && !dynamo_options.sideline |
| # endif |
| ) { |
| optimize_trace(dcontext, tag, trace); |
| externally_mangled = true; |
| } |
| #endif /* INTERNAL */ |
| |
| #ifdef PROFILE_RDTSC |
| if (dynamo_options.profile_times) { |
| /* space was already reserved in buffer and in md->emitted_size */ |
| add_profile_call(dcontext); |
| } |
| #endif |
| |
| #ifdef SIDELINE |
| if (dynamo_options.sideline) { |
| /* FIXME: add size to emitted_size when start building trace to |
| * ensure room in buffer and in cache |
| */ |
| add_sideline_prefix(dcontext, trace); |
| } |
| #endif |
| |
| /* delete any private copy now and use its space for this trace |
| * for private traces: |
| * this way we use the head of FIFO for all our private copies, and |
| * then replace w/ the trace, avoiding any fragmentation from the copies. |
| * for shared traces: FIXME: case 5137: move temps to private bb cache? |
| */ |
| if (md->last_copy != NULL) { |
| if (cur_f == md->last_copy) |
| cur_f = NULL; |
| delete_private_copy(dcontext); |
| } |
| |
| /* Shared trace synchronization model: |
| * We can't hold locks across cache executions, and we wouldn't want to have a |
| * massive trace building lock anyway, so we only grab a lock at the final emit |
| * moment and if there's a conflict the loser tosses his trace. |
| * We hold the lock across the trace head removal as well to avoid races there. |
| */ |
| if (TEST(FRAG_SHARED, md->trace_flags)) { |
| ASSERT(DYNAMO_OPTION(shared_traces)); |
| mutex_lock(&trace_building_lock); |
| /* we left the bb there, so we rely on any shared trace shadowing it */ |
| trace_f = fragment_lookup_trace(dcontext, tag); |
| if (trace_f != NULL) { |
| /* someone beat us to it! tough luck -- throw it all away */ |
| ASSERT(TEST(FRAG_IS_TRACE, trace_f->flags)); |
| mutex_unlock(&trace_building_lock); |
| trace_abort(dcontext); |
| STATS_INC(num_aborted_traces_race); |
| #ifdef DEBUG |
| /* We expect to see this very rarely since we expect to detect |
| * practically all races (w/shared BBs anyway) much earlier. |
| * FIXME case 8769: we may need another way to prevent races w/ |
| * -coarse_units! |
| */ |
| if (DYNAMO_OPTION(shared_bbs) && !DYNAMO_OPTION(coarse_units)) |
| ASSERT_CURIOSITY(false); |
| #endif |
| /* deliberately leave trace_f as it is */ |
| goto end_and_emit_trace_return; |
| } |
| } |
| |
| /* Delete existing fragment(s) with tag value. |
| * |
| * For shared traces, if -no_remove_shared_trace_heads, we do not remove |
| * shared trace heads and only transfer their links |
| * over to the new trace (and if the trace is deleted we transfer the |
| * links back). We leave them alone otherwise, shadowed in both the DR |
| * lookup tables and ibl tables. |
| * FIXME: trace head left w/ no incoming -- will this break assumptions? |
| * What if someone who held ptr before trace emit, or does a different |
| * lookup, tries to mess w/ trace head's links? |
| */ |
| if (cur_f != NULL && cur_f->tag == tag) { |
| /* Optimization: could repeat for shared as well but we don't bother */ |
| if (!TEST(FRAG_SHARED, cur_f->flags)) |
| trace_head_f = cur_f; |
| /* Yipes, we're deleting the fragment we're supposed to execute next. |
| * Set cur_f to NULL even if not deleted, since we want to |
| * execute the trace in preference to the trace head. |
| */ |
| cur_f = NULL; |
| } |
| /* remove private trace head fragment, if any */ |
| if (trace_head_f == NULL) /* from cur_f */ |
| trace_head_f = fragment_lookup_same_sharing(dcontext, tag, 0/*FRAG_PRIVATE*/); |
| /* We do not go through other threads and delete their private trace heads, |
| * presuming that they have them for a reason and don't want this shared trace |
| */ |
| if (trace_head_f != NULL) { |
| LOG(THREAD, LOG_MONITOR, 4, "deleting private trace head fragment\n"); |
| /* we have to manually check last_exit -- can't have fragment_delete() |
| * call monitor_remove_fragment() to avoid aborting our trace |
| */ |
| if (trace_head_f == dcontext->last_fragment) |
| last_exit_deleted(dcontext); |
| #ifdef CUSTOM_TRACES |
| /* If the trace is private, don't delete the head: the trace will simply |
| * shadow it. If the trace is shared, we have to delete it. We'll re-create |
| * the head as a shared bb if we ever do build a custom trace through it. |
| */ |
| if (!TEST(FRAG_SHARED, md->trace_flags)) { |
| replace_trace_head = true; |
| /* we can't have our trace_head_f clobbered below */ |
| CLIENT_ASSERT(!DYNAMO_OPTION(shared_bbs), "invalid private trace head and " |
| "private traces but -shared_bbs for CUSTOM_TRACES"); |
| } else { |
| #endif |
| fragment_delete(dcontext, trace_head_f, |
| FRAGDEL_NO_OUTPUT | FRAGDEL_NO_MONITOR); |
| #ifdef CUSTOM_TRACES |
| } |
| #endif |
| if (!replace_trace_head) { |
| trace_head_f = NULL; |
| STATS_INC(num_fragments_deleted_trace_heads); |
| } |
| } |
| /* find shared trace head fragment, if any */ |
| if (DYNAMO_OPTION(shared_bbs)) { |
| trace_head_f = |
| fragment_lookup_fine_and_coarse_sharing(dcontext, tag, &wrapper, |
| NULL, FRAG_SHARED); |
| if (!TEST(FRAG_SHARED, md->trace_flags)) { |
| /* trace is private, so we can emit as a shadow of trace head */ |
| } else if (trace_head_f != NULL) { |
| /* we don't remove until after emitting a shared trace to avoid races |
| * with trace head being re-created before the trace is visible |
| */ |
| replace_trace_head = true; |
| if (!TEST(FRAG_IS_TRACE_HEAD, trace_head_f->flags)) { |
| ASSERT(TEST(FRAG_COARSE_GRAIN, trace_head_f->flags)); |
| /* local wrapper so change_linking_lock not needed to change flags */ |
| trace_head_f->flags |= FRAG_IS_TRACE_HEAD; |
| } |
| } |
| } |
| |
| /* Prevent deletion of last_fragment, which may be in the same |
| * cache as our trace (esp. w/ a MUST_END_TRACE trace head, since then the |
| * last_fragment can be another trace) from clobbering our trace! |
| * FIXME: would be cleaner to remove the need to abort the trace if |
| * last_fragment is deleted, but tricky to do that (see |
| * monitor_remove_fragment). Could also use a special monitor_data_t field |
| * saying "ignore last_exit, I'm emitting now." |
| */ |
| if (!LINKSTUB_FAKE(dcontext->last_exit)) /* head delete may have already done this */ |
| last_exit_deleted(dcontext); |
| ASSERT(md->last_fragment == NULL); |
| ASSERT(md->last_copy == NULL); |
| /* ensure trace was NOT aborted */ |
| ASSERT(md->trace_tag == tag); |
| |
| /* emit trace fragment into fcache with tag value */ |
| if (replace_trace_head) { |
| #ifndef CUSTOM_TRACES |
| ASSERT(TEST(FRAG_SHARED, md->trace_flags)); |
| #endif |
| trace_f = emit_fragment_as_replacement(dcontext, tag, trace, md->trace_flags, |
| md->trace_vmlist, trace_head_f); |
| } else { |
| trace_f = emit_fragment(dcontext, tag, trace, md->trace_flags, md->trace_vmlist, |
| true/*link*/); |
| } |
| ASSERT(trace_f != NULL); |
| /* our estimate should be conservative |
| * if externally mangled, all bets are off for now -- |
| * FIXME: would be nice to gracefully handle opt or client |
| * making the trace too big, and pass back an error msg? |
| * Perhaps have lower size bounds when optimization or client |
| * interface are on. |
| */ |
| LOG(THREAD, LOG_MONITOR, 3, "Trace estimated size %d vs actual size %d\n", |
| md->emitted_size, trace_f->size); |
| ASSERT(trace_f->size <= md->emitted_size || externally_mangled); |
| /* our calculations should be exact, actually */ |
| /* with -pad_jmps not exact anymore, we should be able to figure out |
| * by how much though FIXME */ |
| ASSERT_CURIOSITY(trace_f->size == md->emitted_size || externally_mangled || |
| PAD_FRAGMENT_JMPS(trace_f->flags)); |
| trace_tr = TRACE_FIELDS(trace_f); |
| trace_tr->num_bbs = md->num_blks; |
| trace_tr->bbs = (trace_bb_info_t *) |
| nonpersistent_heap_alloc(FRAGMENT_ALLOC_DC(dcontext, trace_f->flags), |
| md->num_blks*sizeof(trace_bb_info_t) |
| HEAPACCT(ACCT_TRACE)); |
| for (i = 0; i < md->num_blks; i++) |
| trace_tr->bbs[i] = md->blk_info[i].info; |
| |
| if (TEST(FRAG_SHARED, md->trace_flags)) |
| mutex_unlock(&trace_building_lock); |
| |
| RSTATS_INC(num_traces); |
| DOSTATS({ IF_X64(if (FRAG_IS_32(trace_f->flags)) STATS_INC(num_32bit_traces);) }); |
| STATS_ADD(num_bbs_in_all_traces, md->num_blks); |
| STATS_TRACK_MAX(max_bbs_in_a_trace, md->num_blks); |
| DOLOG(2, LOG_MONITOR, { |
| LOG(THREAD, LOG_MONITOR, 1, "Generated trace fragment #%d for tag "PFX"\n", |
| GLOBAL_STAT(num_traces), tag); |
| disassemble_fragment(dcontext, trace_f, stats->loglevel < 3); |
| }); |
| |
| #ifdef INTERNAL |
| DODEBUG({ |
| if (INTERNAL_OPTION(stress_recreate_pc)) { |
| /* verify trace recreation - done here after bb_tag[] is in place */ |
| stress_test_recreate(dcontext, trace_f, trace); |
| } |
| }); |
| #endif |
| |
| /* we can't call reset_trace_state() until after -remove_trace_components, |
| * but we must clear these two before enter_nolinking so that a flusher |
| * doesn't access them in an inconsistent state (trace_vmlist is invalid |
| * once also pointers are transferred to real fragment) |
| */ |
| md->trace_vmlist = NULL; |
| md->trace_tag = NULL; |
| |
| /* these calls to fragment_remove_shared_no_flush may become |
| * nolinking, meaning we need to hold no locks here, and that when |
| * we get back our local fragment_t pointers may be invalid. |
| */ |
| /* remove shared trace head fragment */ |
| if (trace_head_f != NULL && DYNAMO_OPTION(shared_bbs) && |
| TEST(FRAG_SHARED, md->trace_flags) && |
| /* We leave the head in the coarse table and let the trace shadow it. |
| * If we were to remove it we would need a solution to finding it for |
| * pc translation, which currently walks the htable. |
| */ |
| !TEST(FRAG_COARSE_GRAIN, trace_head_f->flags) && |
| /* if both shared only remove if option on, and no custom tracing */ |
| IF_CUSTOM_TRACES(!dr_end_trace_hook_exists() &&) |
| INTERNAL_OPTION(remove_shared_trace_heads)) { |
| fragment_remove_shared_no_flush(dcontext, trace_head_f); |
| trace_head_f = NULL; |
| } |
| |
| if (DYNAMO_OPTION(remove_trace_components)) { |
| uint i; |
| fragment_t *f; |
| /* use private md values, don't trust trace_tr */ |
| for (i = 1/*skip trace head*/; i < md->num_blks; i++) { |
| f = fragment_lookup_bb(dcontext, md->blk_info[i].info.tag); |
| if (f != NULL) { |
| if (TEST(FRAG_SHARED, f->flags) && !TEST(FRAG_COARSE_GRAIN, f->flags)) { |
| /* FIXME: grab locks up front instead of on each delete */ |
| fragment_remove_shared_no_flush(dcontext, f); |
| trace_head_f = NULL; /* be safe */ |
| } else |
| fragment_delete(dcontext, f, FRAGDEL_NO_OUTPUT | FRAGDEL_NO_MONITOR); |
| STATS_INC(trace_components_deleted); |
| } |
| } |
| } |
| |
| /* free the instrlist_t elements */ |
| instrlist_clear(dcontext, trace); |
| |
| md->trace_tag = tag; /* reinstate for reset */ |
| reset_trace_state(dcontext, true /* might need change_linking_lock */); |
| |
| #ifdef DEBUG |
| /* If we're building shared traces and using shared BBs, FRAG_TRACE_BUILDING |
| * shouldn't be set on the trace head fragment. If we're not using shared |
| * BBs or are not building shared traces, the flag shouldn't be set then |
| * either. Basically, it should never be set at this point, after the call |
| * to reset_trace_state() just above. |
| */ |
| if (trace_head_f != NULL) |
| ASSERT(!TEST(FRAG_TRACE_BUILDING, trace_head_f->flags)); |
| #endif |
| |
| end_and_emit_trace_return: |
| if (cur_f == NULL && cur_f_tag == tag) |
| return trace_f; |
| else { |
| /* emitting the new trace may have deleted the next fragment to execute |
| * best way to find out is to re-look-up the next fragment (this only |
| * happens when emitting trace, so rare enough) |
| */ |
| cur_f = fragment_lookup(dcontext, cur_f_tag); |
| return cur_f; |
| } |
| } |
| |
| /* Note: The trace being built currently can be emitted in |
| * internal_extend_trace() rather than the next time into monitor_cache_enter() |
| * if fragment results in a system call (sysenter) or callback (int 2b), i.e., |
| * is marked FRAG_MUST_END_TRACE. |
| */ |
| static fragment_t * |
| internal_extend_trace(dcontext_t *dcontext, fragment_t *f, linkstub_t *prev_l, |
| uint add_size) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| bool have_locks = false; |
| DEBUG_DECLARE(uint pre_emitted_size = md->emitted_size;) |
| |
| #ifdef CLIENT_INTERFACE |
| extend_unmangled_ilist(dcontext, f); |
| #endif |
| |
| /* if prev_l is fake, NULL it out */ |
| if (is_ibl_sourceless_linkstub((const linkstub_t *)prev_l)) { |
| ASSERT(!DYNAMO_OPTION(indirect_stubs)); |
| prev_l = NULL; |
| } |
| ASSERT(prev_l == NULL || !LINKSTUB_FAKE(prev_l) || |
| /* we track the ordinal of the del linkstub so it's ok */ |
| prev_l == get_deleted_linkstub(dcontext)); |
| |
| if (TEST(FRAG_SHARED, f->flags)) { |
| /* Case 8419: we must hold a lock to ensure f is not |
| * fragment_remove_shared_no_flush()-ed underneath us, eliminating its |
| * also fields needed for vm_area_add_to_list() (plus w/ the also field |
| * re-used for case 3559 we have crash potential). |
| */ |
| have_locks = true; |
| /* lock rank order requires cll before shared_vm_areas */ |
| SHARED_FLAGS_RECURSIVE_LOCK(f->flags, acquire, change_linking_lock); |
| acquire_vm_areas_lock(dcontext, f->flags); |
| } |
| if (TEST(FRAG_WAS_DELETED, f->flags)) { |
| /* We cannot continue if f is FRAG_WAS_DELETED (case 8419) since |
| * fragment_t.also is now invalid! |
| */ |
| STATS_INC(num_trace_next_bb_deleted); |
| ASSERT(have_locks); |
| if (have_locks) { |
| release_vm_areas_lock(dcontext, f->flags); |
| SHARED_FLAGS_RECURSIVE_LOCK(f->flags, release, change_linking_lock); |
| } |
| return end_and_emit_trace(dcontext, f); |
| } |
| |
| /* We have to calculate the added size before we extend, so we |
| * have that passed in, though without the estimate for the mangling |
| * of the previous block (thus including only f->size and the exit stub |
| * size changes), which we calculate in extend_trace. |
| * Existing custom stub code should already be in f->size. |
| * FIXME: if we ever have decode_fragment() convert, say, dcontext |
| * save/restore to tls, then we'll have to add in its size increases |
| * as well. |
| */ |
| md->emitted_size += add_size; |
| |
| md->trace_flags |= trace_flags_from_component_flags(f->flags); |
| |
| /* call routine in interp.c */ |
| md->emitted_size += extend_trace(dcontext, f, prev_l); |
| |
| LOG(THREAD, LOG_MONITOR, 3, "extending added %d to size of trace => %d total\n", |
| md->emitted_size - pre_emitted_size, md->emitted_size); |
| |
| vm_area_add_to_list(dcontext, md->trace_tag, &(md->trace_vmlist), md->trace_flags, |
| f, have_locks); |
| if (have_locks) { |
| /* We must give up change_linking_lock in order to execute |
| * create_private_copy (it calls emit()) but we're at a stable state |
| * now. |
| */ |
| release_vm_areas_lock(dcontext, f->flags); |
| SHARED_FLAGS_RECURSIVE_LOCK(f->flags, release, change_linking_lock); |
| } |
| |
| DOLOG(3, LOG_MONITOR, { |
| LOG(THREAD, LOG_MONITOR, 4, "After extending, trace looks like this:\n"); |
| instrlist_disassemble(dcontext, md->trace_tag, &md->trace, THREAD); |
| }); |
| /* trace_t extended; prepare bb for execution to find where to go next. */ |
| |
| /* For FRAG_MUST_END_TRACE fragments emit trace immediately to prevent |
| * trace aborts due to syscalls and callbacks. See case 3541. |
| */ |
| if (TEST(FRAG_MUST_END_TRACE, f->flags)) { |
| /* We don't need to unlink f, but we would need to set FRAG_CANNOT_DELETE to |
| * prevent its deletion during emitting from clobbering the trace in the case |
| * that last_fragment==f (requires that f targets itself, and f is |
| * private like traces -- not possible w/ today's syscall-only MUST_END_TRACE |
| * fragments but could happen in the future) -- except that that's a general |
| * problem handled by clearing last_exit in end_and_emit_trace, so we do |
| * nothing here. |
| */ |
| return end_and_emit_trace(dcontext, f); |
| } |
| |
| ASSERT(!TEST(FRAG_SHARED, f->flags)); |
| if (TEST(FRAG_TEMP_PRIVATE, f->flags)) { |
| /* We make a private copy earlier for everything other than a normal |
| * thread private fragment. |
| */ |
| ASSERT(md->last_fragment == f); |
| ASSERT(md->last_copy != NULL); |
| ASSERT(md->last_copy->tag == f->tag); |
| ASSERT(md->last_fragment == md->last_copy); |
| } else { |
| /* must store this fragment, and also duplicate its flags so know what |
| * to restore. can't rely on last_exit for restoring since could end up |
| * not coming out of cache from last_fragment (e.g., if hit sigreturn) |
| */ |
| md->last_fragment = f; |
| } |
| |
| /* hold lock across cannot delete changes too, and store of flags */ |
| SHARED_FLAGS_RECURSIVE_LOCK(f->flags, acquire, change_linking_lock); |
| |
| md->last_fragment_flags = f->flags; |
| if ((f->flags & FRAG_CANNOT_DELETE) == 0) { |
| /* don't let this fragment be deleted, we'll need it as |
| * dcontext->last_exit for extend_trace |
| */ |
| f->flags |= FRAG_CANNOT_DELETE; |
| LOG(THREAD, LOG_MONITOR, 4, |
| "monitor marked F%d ("PFX") as un-deletable\n", f->id, f->tag); |
| } |
| |
| #ifdef CUSTOM_TRACES |
| /* may end up going through trace head, etc. that isn't linked */ |
| if ((f->flags & FRAG_LINKED_OUTGOING) != 0) { |
| #endif |
| /* unlink so monitor invoked on fragment exit */ |
| unlink_fragment_outgoing(dcontext, f); |
| LOG(THREAD, LOG_MONITOR|LOG_LINKS, 4, |
| "monitor unlinked F%d ("PFX")\n", f->id, f->tag); |
| #ifdef CUSTOM_TRACES |
| } |
| #endif |
| |
| SHARED_FLAGS_RECURSIVE_LOCK(f->flags, release, change_linking_lock); |
| |
| return f; |
| } |
| |
| /* we use last_fragment to hold bb that needs to be restored. |
| * it's a field used only by us. |
| */ |
| static void |
| internal_restore_last(dcontext_t *dcontext) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| if (md->last_fragment == NULL) |
| return; |
| /* must restore fragment used to extend trace to pre-trace-building state. |
| * sometimes we come in here from trace_abort and we've already restored |
| * the last exit, so check before linking. |
| */ |
| /* need to hold lock for any shared link modification */ |
| SHARED_FLAGS_RECURSIVE_LOCK(md->last_fragment->flags, acquire, change_linking_lock); |
| if ((md->last_fragment_flags & FRAG_LINKED_OUTGOING) != 0 && |
| (md->last_fragment->flags & FRAG_LINKED_OUTGOING) == 0) { |
| LOG(THREAD, LOG_MONITOR, 4, |
| "internal monitor: relinking last fragment F%d\n", |
| md->last_fragment->id); |
| link_fragment_outgoing(dcontext, md->last_fragment, false); |
| } |
| if ((md->last_fragment_flags & FRAG_CANNOT_DELETE) == 0 && |
| (md->last_fragment->flags & FRAG_CANNOT_DELETE) != 0) { |
| LOG(THREAD, LOG_MONITOR, 4, |
| "internal monitor: re-marking last fragment F%d as deletable\n", |
| md->last_fragment->id); |
| md->last_fragment->flags &= ~FRAG_CANNOT_DELETE; |
| } |
| /* flags may not match, e.g., if frag was marked as trace head */ |
| ASSERT((md->last_fragment_flags & |
| (FRAG_CANNOT_DELETE|FRAG_LINKED_OUTGOING)) == |
| (md->last_fragment->flags & |
| (FRAG_CANNOT_DELETE|FRAG_LINKED_OUTGOING))); |
| /* hold lock across FRAG_CANNOT_DELETE changes and all other flag checks, too */ |
| SHARED_FLAGS_RECURSIVE_LOCK(md->last_fragment->flags, release, change_linking_lock); |
| |
| /* last_fragment is ONLY used for restoring, so kill now, else our own |
| * deletion of trace head will cause use to abort single-bb trace |
| * (see monitor_remove_fragment) |
| * |
| * Do NOT reset last_fragment_flags as that field is needed prior to the |
| * cache entry and is referenced in monitor_cache_enter(). |
| */ |
| if (!TEST(FRAG_TEMP_PRIVATE, md->last_fragment->flags)) |
| md->last_fragment = NULL; |
| } |
| |
| /* if we are building a trace, unfreezes and relinks the last_fragment */ |
| void |
| monitor_cache_exit(dcontext_t *dcontext) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| /* where processing */ |
| ASSERT(dcontext->whereami == WHERE_DISPATCH); |
| dcontext->whereami = WHERE_MONITOR; |
| if (md->trace_tag > 0 && md->last_fragment != NULL) { |
| /* unprotect local heap */ |
| SELF_PROTECT_LOCAL(dcontext, WRITABLE); |
| /* must restore fragment to pre-trace-building state */ |
| internal_restore_last(dcontext); |
| /* re-protect local heap */ |
| SELF_PROTECT_LOCAL(dcontext, READONLY); |
| } |
| else if (md->trace_tag == NULL) { |
| /* Capture the case where the most recent cache exit was prior to a |
| * non-ignorable syscall that used the SYSENTER instruction, which |
| * we've seen on XP and 2003. The 'ret' after the SYSENTER executes |
| * natively, and this piece of control flow isn't captured during |
| * linking so link-time trace head marking doesn't work. (The exit |
| * stub is marked as a direct exit.) The exit stub is reset during |
| * syscall handling so indirect-exit trace head marking isn't |
| * possible either, so we have to use a dedicated var to capture |
| * this case. |
| * |
| * We need to set trace_sysenter_exit to true or false to prevent a |
| * stale value from reaching a later read of the flag. |
| * |
| * FIXME Rework this to store the last (pre-syscall) exit's fragment flags & tag |
| * in a dcontext-private place such as non-shared monitor data. |
| * Such a general mechanism will permit us to capture all |
| * trace head marking within should_be_trace_head(). |
| */ |
| dcontext->trace_sysenter_exit = |
| (TEST(FRAG_IS_TRACE, dcontext->last_fragment->flags) && |
| TEST(LINK_NI_SYSCALL, dcontext->last_exit->flags)); |
| } |
| dcontext->whereami = WHERE_DISPATCH; |
| } |
| |
| static void |
| check_fine_to_coarse_trace_head(dcontext_t *dcontext, fragment_t *f) |
| { |
| /* Case 8632: When a fine fragment targets a coarse trace head, we have |
| * no way to indicate that (there is no entrance stub for the fine |
| * fragments, as once the coarse unit is frozen we can't use its |
| * entrance stub). So we assume that an exit is due to trace headness |
| * discovered at link time iff it would now be considered a trace head. |
| * FIXME: any cleaner way? |
| */ |
| if (TEST(FRAG_COARSE_GRAIN, f->flags) && |
| !TEST(FRAG_IS_TRACE_HEAD, f->flags) && |
| /* FIXME: We rule out empty fragments -- but in so doing we rule out deleted |
| * fragments. Oh well. |
| */ |
| !TESTANY(FRAG_COARSE_GRAIN|FRAG_FAKE, dcontext->last_fragment->flags)) { |
| /* we lock up front since check_for_trace_head() expects it for shared2shared */ |
| acquire_recursive_lock(&change_linking_lock); |
| if (check_for_trace_head(dcontext, dcontext->last_fragment, |
| dcontext->last_exit, f, true/*have lock*/, |
| false/*not sysenter exit*/)) { |
| STATS_INC(num_exits_fine2th_coarse); |
| } else { |
| /* This does happen: e.g., if we abort a trace, we came from a private fine |
| * bb and may target a coarse bb |
| */ |
| STATS_INC(num_exits_fine2non_th_coarse); |
| } |
| release_recursive_lock(&change_linking_lock); |
| } |
| } |
| |
| /* This routine maintains the statistics that identify hot code |
| * regions, and it controls the building and installation of trace |
| * fragments. |
| */ |
| fragment_t * |
| monitor_cache_enter(dcontext_t *dcontext, fragment_t *f) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| bool start_trace = false; |
| bool end_trace = false; |
| #ifdef CUSTOM_TRACES |
| dr_custom_trace_action_t client = CUSTOM_TRACE_DR_DECIDES; |
| #endif |
| trace_head_counter_t *ctr; |
| uint add_size = 0, prev_mangle_size = 0; /* NOTE these aren't set if end_trace */ |
| |
| if (DYNAMO_OPTION(disable_traces) || f == NULL) { |
| /* nothing to do */ |
| ASSERT(md->trace_tag == NULL); |
| return f; |
| } |
| |
| /* where processing */ |
| ASSERT(dcontext->whereami == WHERE_DISPATCH); |
| dcontext->whereami = WHERE_MONITOR; |
| |
| /* default internal routine */ |
| |
| /* Ensure we know whether f is a trace head, before we do anything else |
| * (xref bug 8637 on not terminating traces b/c we marked as head too late) |
| */ |
| check_fine_to_coarse_trace_head(dcontext, f); |
| |
| if (md->trace_tag > 0) { /* in trace selection mode */ |
| KSTART(trace_building); |
| |
| /* unprotect local heap */ |
| SELF_PROTECT_LOCAL(dcontext, WRITABLE); |
| /* should have restored last fragment on cache exit */ |
| ASSERT(md->last_fragment == NULL || |
| TEST(FRAG_TEMP_PRIVATE, md->last_fragment->flags)); |
| |
| /* check for trace ending conditions that can be overridden by client */ |
| end_trace = (end_trace || |
| TEST(FRAG_IS_TRACE, f->flags ) || |
| TEST(FRAG_IS_TRACE_HEAD, f->flags)); |
| #ifdef CUSTOM_TRACES |
| if (dr_end_trace_hook_exists()) { |
| client = instrument_end_trace(dcontext, md->trace_tag, f->tag); |
| /* Return values: |
| * CUSTOM_TRACE_DR_DECIDES = use standard termination criteria |
| * CUSTOM_TRACE_END_NOW = end trace |
| * CUSTOM_TRACE_CONTINUE = do not end trace |
| */ |
| if (client == CUSTOM_TRACE_END_NOW) { |
| DOSTATS({ |
| if (!end_trace) { |
| LOG(THREAD, LOG_MONITOR, 3, |
| "Client ending 0x%08x trace early @0x%08x\n", |
| md->trace_tag, f->tag); |
| STATS_INC(custom_traces_stop_early); |
| } |
| }); |
| end_trace = true; |
| } else if (client == CUSTOM_TRACE_CONTINUE) { |
| DOSTATS({ |
| if (end_trace) { |
| LOG(THREAD, LOG_MONITOR, 3, |
| "Client not ending 0x%08x trace @ normal stop @0x%08x\n", |
| md->trace_tag, f->tag); |
| STATS_INC(custom_traces_stop_late); |
| } |
| }); |
| end_trace = false; |
| } |
| LOG(THREAD, LOG_MONITOR, 4, "Client instrument_end_trace returned %d\n", |
| client); |
| } |
| #endif |
| /* check for conditions signaling end of trace regardless of client */ |
| end_trace = end_trace || TEST(FRAG_CANNOT_BE_TRACE, f->flags); |
| |
| #ifdef X64 |
| /* no traces that mix 32 and 64: decode_fragment not set up for it */ |
| if (TEST(FRAG_32_BIT, f->flags) != TEST(FRAG_32_BIT, md->trace_flags)) |
| end_trace = true; |
| #endif |
| |
| if (!end_trace) { |
| #ifdef CUSTOM_TRACES |
| /* we need a regular bb here, not a trace */ |
| if (TEST(FRAG_IS_TRACE, f->flags)) { |
| /* We create an official, shared bb (we DO want to call the client bb |
| * hook, right?). We do not link the new, shadowed bb. |
| */ |
| fragment_t *head = NULL; |
| if (USE_BB_BUILDING_LOCK()) |
| mutex_lock(&bb_building_lock); |
| if (DYNAMO_OPTION(coarse_units)) { |
| /* the existing lookup routines will shadow a coarse bb so we do |
| * a custom lookup |
| */ |
| head = fragment_coarse_lookup_wrapper(dcontext, f->tag, |
| &md->wrapper); |
| } |
| if (head == NULL) |
| head = fragment_lookup_bb(dcontext, f->tag); |
| if (head == NULL) { |
| LOG(THREAD, LOG_MONITOR, 3, |
| "Client custom trace 0x%08x requiring shadow bb 0x%08x\n", |
| md->trace_tag, f->tag); |
| SELF_PROTECT_LOCAL(dcontext, WRITABLE); |
| /* We need to mark as trace head to hit the shadowing checks |
| * and asserts when adding to fragment htable and unlinking |
| * on delete. |
| */ |
| head = build_basic_block_fragment |
| (dcontext, f->tag, FRAG_IS_TRACE_HEAD, false/*do not link*/, |
| true/*visible*/ _IF_CLIENT(true/*for trace*/) _IF_CLIENT(NULL)); |
| SELF_PROTECT_LOCAL(dcontext, READONLY); |
| STATS_INC(custom_traces_bbs_built); |
| ASSERT(head != NULL); |
| /* If it's not shadowing we should have linked before htable add. |
| * We shouldn't end up w/ a bb of different sharing than the |
| * trace: CUSTOM_TRACES rules out private traces and shared bbs, |
| * and if circumstances changed since the original trace head bb |
| * was made then the trace should have been flushed. |
| */ |
| ASSERT((head->flags & FRAG_SHARED) == (f->flags & FRAG_SHARED)); |
| if (TEST(FRAG_COARSE_GRAIN, head->flags)) { |
| /* we need a local copy before releasing the lock. |
| * FIXME: share this code sequence w/ dispatch(). |
| */ |
| ASSERT(USE_BB_BUILDING_LOCK()); |
| fragment_coarse_wrapper(&md->wrapper, f->tag, |
| FCACHE_ENTRY_PC(head)); |
| md->wrapper.flags |= FRAG_IS_TRACE_HEAD; |
| head = &md->wrapper; |
| } |
| } |
| if (USE_BB_BUILDING_LOCK()) |
| mutex_unlock(&bb_building_lock); |
| /* use the bb from here on out */ |
| f = head; |
| } |
| #endif |
| if (TEST(FRAG_COARSE_GRAIN, f->flags) || TEST(FRAG_SHARED, f->flags) |
| IF_CLIENT_INTERFACE(|| md->pass_to_client)) { |
| /* We need linkstub_t info for trace_exit_stub_size_diff() so we go |
| * ahead and make a private copy here. |
| * For shared fragments, we make a private copy of f to avoid |
| * synch issues with other threads modifying its linkage before |
| * we get back here. We do it up front now (i#940) to avoid |
| * determinism issues that arise when check_thread_vm_area() |
| * changes its mind over time. |
| */ |
| if (create_private_copy(dcontext, f)) { |
| /* operate on new f from here on */ |
| if (md->trace_tag == NULL) { |
| /* trace was aborted b/c our new fragment clobbered |
| * someone (see comments in create_private_copy) -- |
| * when emitting our private bb we can kill the |
| * last_fragment): just exit now |
| */ |
| LOG(THREAD, LOG_MONITOR, 4, |
| "Private copy ended up aborting trace!\n"); |
| STATS_INC(num_trace_private_copy_abort); |
| /* trace abort happened in emit_fragment, so we went and |
| * undid the clearing of last_fragment by assigning it |
| * to last_copy, must re-clear! |
| */ |
| md->last_fragment = NULL; |
| return f; |
| } |
| f = md->last_fragment; |
| } else { |
| end_trace = true; |
| } |
| } |
| |
| if (!end_trace && |
| !get_and_check_add_size(dcontext, f, &add_size, &prev_mangle_size)) { |
| STATS_INC(num_max_trace_size_enforced); |
| end_trace = true; |
| } |
| } |
| if (DYNAMO_OPTION(max_trace_bbs) > 0 && |
| md->num_blks >= DYNAMO_OPTION(max_trace_bbs) && !end_trace) { |
| end_trace = true; |
| STATS_INC(num_max_trace_bbs_enforced); |
| } |
| end_trace = (end_trace || |
| /* mangling may never use trace buffer memory but just in case */ |
| !make_room_in_trace_buffer(dcontext, add_size + prev_mangle_size, f)); |
| |
| #ifdef CUSTOM_TRACES |
| if (end_trace && client == CUSTOM_TRACE_CONTINUE) { |
| /* had to overide client, log */ |
| LOG(THREAD, LOG_MONITOR, 2, PRODUCT_NAME" ignoring Client's decision to continue trace (cannot trace through next fragment), ending trace now\n"); |
| } |
| #endif |
| |
| if (end_trace) { |
| LOG(THREAD, LOG_MONITOR, 3, |
| "NOT extending hot trace (tag "PFX") with F%d ("PFX")\n", |
| md->trace_tag, f->id, f->tag); |
| |
| f = end_and_emit_trace(dcontext, f); |
| LOG(THREAD, LOG_MONITOR, 3, "Returning to search mode f="PFX"\n", f); |
| } else { |
| LOG(THREAD, LOG_MONITOR, 3, |
| "Extending hot trace (tag "PFX") with F%d ("PFX")\n", |
| md->trace_tag, f->id, f->tag); |
| /* add_size is set when !end_trace */ |
| f = internal_extend_trace(dcontext, f, dcontext->last_exit, add_size); |
| } |
| dcontext->whereami = WHERE_DISPATCH; |
| /* re-protect local heap */ |
| SELF_PROTECT_LOCAL(dcontext, READONLY); |
| KSTOP(trace_building); |
| return f; |
| } |
| |
| /* if got here, md->trace_tag == NULL */ |
| |
| /* searching for a hot trace head */ |
| |
| if (TEST(FRAG_IS_TRACE, f->flags)) { |
| /* nothing to do */ |
| dcontext->whereami = WHERE_DISPATCH; |
| return f; |
| } |
| |
| if (!TEST(FRAG_IS_TRACE_HEAD, f->flags)) { |
| |
| bool trace_head; |
| |
| /* Dynamic marking of trace heads for: |
| * - indirect exits |
| * - an exit from a trace that ends just before a SYSENTER. |
| * - private secondary trace heads targeted by shared traces |
| * |
| * FIXME Rework this to use the last exit's fragment flags & tag that were |
| * stored in a dcontext-private place such as non-shared monitor data. |
| */ |
| if (LINKSTUB_INDIRECT(dcontext->last_exit->flags) || |
| dcontext->trace_sysenter_exit || |
| /* mark private secondary trace heads from shared traces */ |
| (TESTALL(FRAG_SHARED|FRAG_IS_TRACE, dcontext->last_fragment->flags) && |
| !TESTANY(FRAG_SHARED|FRAG_IS_TRACE, f->flags))) { |
| |
| bool need_lock = NEED_SHARED_LOCK(dcontext->last_fragment->flags); |
| if (need_lock) |
| acquire_recursive_lock(&change_linking_lock); |
| |
| /* The exit stub is fake if trace_sysenter_exit is true, but the |
| * path thru check_for_trace_head() accounts for that. |
| */ |
| trace_head = |
| check_for_trace_head(dcontext, dcontext->last_fragment, |
| dcontext->last_exit, f, |
| need_lock, dcontext->trace_sysenter_exit); |
| |
| if (need_lock) |
| release_recursive_lock(&change_linking_lock); |
| |
| /* link routines will unprotect as necessary, we then re-protect |
| * entire fcache |
| */ |
| SELF_PROTECT_CACHE(dcontext, NULL, READONLY); |
| } |
| else { |
| /* whether direct or fake, not marking a trace head */ |
| trace_head = false; |
| } |
| |
| if (!trace_head) { |
| dcontext->whereami = WHERE_DISPATCH; |
| return f; |
| } |
| } |
| |
| /* Found a trace head, increment its counter */ |
| ctr = thcounter_lookup(dcontext, f->tag); |
| /* May not have been added for this thread yet */ |
| if (ctr == NULL) |
| ctr = thcounter_add(dcontext, f->tag); |
| ASSERT(ctr != NULL); |
| |
| if (ctr->counter == TH_COUNTER_CREATED_TRACE_VALUE()) { |
| /* trace_t head counter values are persistent, so we do not remove them on |
| * deletion. However, when a trace is deleted we clear the counter, to |
| * prevent the new bb from immediately being considered hot, to help |
| * with phased execution (trace may no longer be hot). To avoid having |
| * walk every thread for every trace deleted we use a lazy strategy, |
| * recognizing a counter that has already reached the threshold with a |
| * sentinel value. |
| */ |
| ctr->counter = INTERNAL_OPTION(trace_counter_on_delete); |
| STATS_INC(th_counter_reset); |
| } |
| |
| ctr->counter++; |
| /* Should never be > here (assert is down below) but we check just in case */ |
| if (ctr->counter >= INTERNAL_OPTION(trace_threshold)) { |
| /* if cannot delete fragment, do not start trace -- wait until |
| * can delete it (w/ exceptions, deletion status changes). */ |
| if (!TEST(FRAG_CANNOT_DELETE, f->flags)) { |
| if (!DYNAMO_OPTION(shared_traces)) |
| start_trace = true; |
| /* FIXME To detect a trace building race w/private BBs at this point, |
| * we need a presence table to mark that a tag is being used for trace |
| * building. Generic hashtables can help with this (case 6206). |
| */ |
| else if (!DYNAMO_OPTION(shared_bbs) || !TEST(FRAG_SHARED,f->flags)) |
| start_trace = true; |
| else { |
| /* Check if trace building is in progress and act accordingly. */ |
| ASSERT(TEST(FRAG_SHARED, f->flags)); |
| /* Hold the change linking lock for flags changes. */ |
| acquire_recursive_lock(&change_linking_lock); |
| if (TEST(FRAG_TRACE_BUILDING, f->flags)) { |
| /* trace_t building w/this tag is already in-progress. */ |
| STATS_INC(num_trace_building_race); |
| } |
| else { |
| f->flags |= FRAG_TRACE_BUILDING; |
| start_trace = true; |
| } |
| release_recursive_lock(&change_linking_lock); |
| } |
| } |
| if (!start_trace) { |
| /* Back up the counter by one. This ensures that the |
| * counter will be == trace_threshold if this thread is later |
| * able to start building a trace w/this tag and ensures |
| * that our one-up sentinel works for lazy clearing. |
| */ |
| ctr->counter--; |
| ASSERT(ctr->counter < INTERNAL_OPTION(trace_threshold)); |
| } |
| } |
| |
| #ifdef CLIENT_INTERFACE |
| if (start_trace) { |
| /* We need to set pass_to_client before cloning */ |
| /* PR 299808: cache whether we need to re-build bbs for clients up front, |
| * to be consistent across whole trace. If client later unregisters bb |
| * hook then it will miss our call on constituent bbs: that's its problem. |
| * We document that trace and bb hooks should not be unregistered. |
| */ |
| md->pass_to_client = mangle_trace_at_end(); |
| /* should already be initialized */ |
| ASSERT(instrlist_first(&md->unmangled_ilist) == NULL); |
| } |
| #endif |
| if (start_trace && |
| (TEST(FRAG_COARSE_GRAIN, f->flags) || TEST(FRAG_SHARED, f->flags) |
| IF_CLIENT_INTERFACE(|| md->pass_to_client))) { |
| ASSERT(TEST(FRAG_IS_TRACE_HEAD, f->flags)); |
| /* We need linkstub_t info for trace_exit_stub_size_diff() so we go |
| * ahead and make a private copy here. |
| * For shared fragments, we make a private copy of f to avoid |
| * synch issues with other threads modifying its linkage before |
| * we get back here. We do it up front now (i#940) to avoid |
| * determinism issues that arise when check_thread_vm_area() |
| * changes its mind over time. |
| */ |
| if (create_private_copy(dcontext, f)) { |
| /* operate on new f from here on */ |
| f = md->last_fragment; |
| } else { |
| start_trace = false; |
| } |
| } |
| if (start_trace) { |
| KSTART(trace_building); |
| /* ensure our sentinel counter value for counter clearing will work */ |
| ASSERT(ctr->counter == INTERNAL_OPTION(trace_threshold)); |
| ctr->counter = TH_COUNTER_CREATED_TRACE_VALUE(); |
| /* Found a hot trace head. Switch this thread into trace |
| selection mode, and initialize the instrlist_t for the new |
| trace fragment with this block fragment. Leave the |
| trace head entry locked so no one else tries to build |
| a trace from it. Assume that a trace would never |
| contain just one block, and thus we don't have to check |
| for end of trace condition here. */ |
| /* unprotect local heap */ |
| SELF_PROTECT_LOCAL(dcontext, WRITABLE); |
| #ifdef TRACE_HEAD_CACHE_INCR |
| /* we don't have to worry about skipping the cache incr routine link |
| * in the future since we can only encounter the trace head in our |
| * no-link trace-building mode, then we will delete it |
| */ |
| #endif |
| md->trace_tag = f->tag; |
| md->trace_flags = trace_flags_from_trace_head_flags(f->flags); |
| md->emitted_size = fragment_prefix_size(md->trace_flags); |
| #ifdef PROFILE_RDTSC |
| if (dynamo_options.profile_times) |
| md->emitted_size += profile_call_size(); |
| #endif |
| LOG(THREAD, LOG_MONITOR, 2, |
| "Found hot trace head F%d (tag "PFX")\n", f->id, f->tag); |
| LOG(THREAD, LOG_MONITOR, 3, "Entering trace selection mode\n"); |
| /* allocate trace buffer space */ |
| #ifdef CUSTOM_TRACES |
| /* we should have a bb here, since if a trace can't also be a trace head */ |
| ASSERT(!TEST(FRAG_IS_TRACE, f->flags)); |
| #endif |
| if (!get_and_check_add_size(dcontext, f, &add_size, &prev_mangle_size) || |
| /* mangling may never use trace buffer memory but just in case */ |
| !make_room_in_trace_buffer(dcontext, md->emitted_size + add_size + |
| prev_mangle_size, f)) { |
| LOG(THREAD, LOG_MONITOR, 1, |
| "bb %d ("PFX") too big (%d) %s\n", |
| f->id, f->tag, f->size, |
| get_and_check_add_size(dcontext, f, NULL, NULL) ? |
| "trace buffer" : "trace body limit / trace cache size"); |
| /* turn back into a non-trace head */ |
| SHARED_FLAGS_RECURSIVE_LOCK(f->flags, acquire, change_linking_lock); |
| f->flags &= ~FRAG_IS_TRACE_HEAD; |
| /* make sure not marked as trace head again */ |
| f->flags |= FRAG_CANNOT_BE_TRACE; |
| STATS_INC(num_huge_fragments); |
| /* have to relink incoming frags */ |
| link_fragment_incoming(dcontext, f, false/*not new*/); |
| /* call reset_trace_state while holding the lock since it |
| * may manipulate frag flags */ |
| reset_trace_state(dcontext, |
| false /* already own change_linking_lock */); |
| SHARED_FLAGS_RECURSIVE_LOCK(f->flags, release, change_linking_lock); |
| /* FIXME: set CANNOT_BE_TRACE when first create a too-big fragment? |
| * export the size expansion factors considered? |
| */ |
| /* now return */ |
| dcontext->whereami = WHERE_DISPATCH; |
| /* link unprotects on demand, we then re-protect all */ |
| SELF_PROTECT_CACHE(dcontext, NULL, READONLY); |
| /* re-protect local heap */ |
| SELF_PROTECT_LOCAL(dcontext, READONLY); |
| KSTOP(trace_building); |
| return f; |
| } |
| f = internal_extend_trace(dcontext, f, NULL, add_size); |
| |
| /* re-protect local heap */ |
| SELF_PROTECT_LOCAL(dcontext, READONLY); |
| KSTOP(trace_building); |
| } else { |
| /* Not yet hot */ |
| KSWITCH(monitor_enter_thci); |
| } |
| |
| /* release rest of state */ |
| dcontext->whereami = WHERE_DISPATCH; |
| return f; |
| } |
| |
| |
| /* This routine internally calls enter_couldbelinking, thus it is safe |
| * to call from any linking state. Restores linking to previous state at exit. |
| * If calling on another thread, caller should be synchronized with that thread |
| * (either via flushing synch or thread_synch methods) FIXME : verify all users |
| * on other threads are properly synchronized |
| */ |
| void |
| trace_abort(dcontext_t *dcontext) |
| { |
| monitor_data_t *md = (monitor_data_t *) dcontext->monitor_field; |
| instrlist_t *trace; |
| bool prevlinking = true; |
| |
| if (md->trace_tag == NULL && md->last_copy == NULL) |
| return; /* NOT in trace selection mode */ |
| |
| /* we're changing linking state -- and we're often called from |
| * non-could-be-linking locations, so we synch w/ flusher here. |
| * additionally we are changing trace state that the flusher |
| * reads, and we could have a race condition, so we consider |
| * that to be a linking change as well. If we are the flusher |
| * then the synch is unnecessary and could even cause a livelock. |
| */ |
| if (!is_self_flushing()) { |
| if (!is_couldbelinking(dcontext)) { |
| prevlinking = false; |
| enter_couldbelinking(dcontext, NULL, |
| false/*not a cache transition*/); |
| } |
| } |
| |
| /* must relink unlinked trace-extending fragment |
| * cannot use last_exit, must use our own last_fragment just for this |
| * purpose, b/c may not exit cache from last_fragment |
| * (e.g., if hit sigreturn!) |
| */ |
| if (md->last_fragment != NULL) { |
| internal_restore_last(dcontext); |
| } |
| |
| /* i#791: We can't delete last copy yet because we could still be executing |
| * in that fragment. For example, a client could have a clean call that |
| * flushes. We'll delete the last_copy when we start the next trace or at |
| * thread exit instead. |
| */ |
| |
| /* free the instrlist_t elements */ |
| trace = &md->trace; |
| instrlist_clear(dcontext, trace); |
| |
| if (md->trace_vmlist != NULL) { |
| vm_area_destroy_list(dcontext, md->trace_vmlist); |
| md->trace_vmlist = NULL; |
| } |
| STATS_INC(num_aborted_traces); |
| STATS_ADD(num_bbs_in_all_aborted_traces, md->num_blks); |
| reset_trace_state(dcontext, true /* might need change_linking_lock */); |
| |
| if (!prevlinking) |
| enter_nolinking(dcontext, NULL, false/*not a cache transition*/); |
| } |
| |
| #if defined(RETURN_AFTER_CALL) || defined(RCT_IND_BRANCH) |
| /* PR 204770: use trace component bb tag for RCT source address */ |
| app_pc |
| get_trace_exit_component_tag(dcontext_t *dcontext, fragment_t *f, linkstub_t *l) |
| { |
| linkstub_t *stub; |
| uint exitnum = 0; |
| uint i, num; |
| app_pc tag = f->tag; |
| bool found = false; |
| trace_only_t *t = TRACE_FIELDS(f); |
| ASSERT(TEST(FRAG_IS_TRACE, f->flags)); |
| ASSERT(linkstub_fragment(dcontext, l) == f); |
| for (stub = FRAGMENT_EXIT_STUBS(f); stub != NULL; stub = LINKSTUB_NEXT_EXIT(stub)) { |
| if (stub == l) { |
| found = true; |
| break; |
| } |
| exitnum++; |
| } |
| ASSERT(found); |
| if (!found) { |
| LOG(THREAD, LOG_MONITOR, 2, |
| "get_trace_exit_component_tag F%d("PFX"): can't find exit!\n", |
| f->id, f->tag); |
| return f->tag; |
| } |
| ASSERT(exitnum < t->num_bbs); |
| /* If we have coarse bbs, or max_elide_* is 0, we won't elide during bb building |
| * but we will during trace building. Rather than recreate each bb and figure |
| * out how many exits it contributed, we store that information. |
| */ |
| found = false; |
| for (i = 0, num = 0; i < t->num_bbs; i++) { |
| if (exitnum < num + t->bbs[i].num_exits) { |
| found = true; |
| tag = t->bbs[i].tag; |
| break; |
| } |
| num += t->bbs[i].num_exits; |
| } |
| ASSERT(found); |
| LOG(THREAD, LOG_MONITOR, 4, |
| "get_trace_exit_component_tag F%d("PFX") => bb #%d (exit #%d): "PFX"\n", |
| f->id, f->tag, i, exitnum, tag); |
| return tag; |
| } |
| #endif /* defined(RETURN_AFTER_CALL) || defined(RCT_IND_BRANCH) */ |