| /* ********************************************************** |
| * Copyright (c) 2010-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 */ |
| |
| /* |
| * utils.h - miscellenaous utilities |
| */ |
| |
| #ifndef _UTILS_H_ |
| #define _UTILS_H_ 1 |
| |
| #ifdef assert |
| # undef assert |
| #endif |
| /* avoid mistake of lower-case assert */ |
| #define assert assert_no_good_use_ASSERT_instead |
| |
| /* we provide a less-than-default level of checking */ |
| #define CHKLVL_ASSERTS 1 |
| #define CHKLVL_DEFAULT 2 |
| #if defined(DEBUG) && \ |
| !defined(NOT_DYNAMORIO_CORE_PROPER) && \ |
| !defined(NOT_DYNAMORIO_CORE) && \ |
| !defined(STANDALONE_DECODER) |
| /* we can't use DYNAMO_OPTION() b/c that has an assert inside it */ |
| # define DEBUG_CHECKS(level) (dynamo_options.checklevel >= (level)) |
| #else |
| # define DEBUG_CHECKS(level) true |
| #endif |
| |
| #if defined(DEBUG) && !defined(STANDALONE_DECODER) |
| # ifdef INTERNAL |
| /* cast to void to avoid gcc warning "statement with no effect" when used as |
| * a statement and x is a compile-time false |
| * FIXME: cl debug build allocates a local for each ?:, so if this gets |
| * unrolled in some optionsx or other expansion we could have stack problems! |
| */ |
| # define ASSERT(x) \ |
| ((void)((DEBUG_CHECKS(CHKLVL_ASSERTS) && !(x)) ? \ |
| (internal_error(__FILE__, __LINE__, #x), 0) : 0)) |
| /* make type void to avoid gcc 4.1 warnings about "value computed is not used" |
| * (case 7851). can also use statement expr ({;}) but only w/ gcc, not w/ cl. |
| */ |
| # define ASSERT_MESSAGE(level, msg, x) ((DEBUG_CHECKS(level) && !(x)) ? \ |
| (internal_error(msg " @" __FILE__, __LINE__, #x), (void)0) : (void)0) |
| # define REPORT_CURIOSITY(x) do { \ |
| if (!ignore_assert(__FILE__":"STRINGIFY(__LINE__), "curiosity : "#x)) { \ |
| report_dynamorio_problem(NULL, DUMPCORE_CURIOSITY, NULL, NULL, \ |
| "CURIOSITY : %s in file %s line %d", #x, \ |
| __FILE__, __LINE__); \ |
| } \ |
| } while (0) |
| # define ASSERT_CURIOSITY(x) do { \ |
| if (DEBUG_CHECKS(CHKLVL_ASSERTS) && !(x)) { \ |
| REPORT_CURIOSITY(x); \ |
| } \ |
| } while (0) |
| # define ASSERT_CURIOSITY_ONCE(x) do { \ |
| if (DEBUG_CHECKS(CHKLVL_ASSERTS) && !(x)) { \ |
| DO_ONCE({REPORT_CURIOSITY(x);}); \ |
| } \ |
| } while (0) |
| # else |
| /* cast to void to avoid gcc warning "statement with no effect" */ |
| # define ASSERT(x) \ |
| ((void)((DEBUG_CHECKS(CHKLVL_ASSERTS) && !(x)) ? \ |
| (internal_error(__FILE__, __LINE__, ""), 0) : 0)) |
| # define ASSERT_MESSAGE(level, msg, x) \ |
| ((void)((DEBUG_CHECKS(level) && !(x)) ? \ |
| (internal_error(__FILE__, __LINE__, ""), 0) : 0)) |
| # define ASSERT_CURIOSITY(x) ((void) 0) |
| # define ASSERT_CURIOSITY_ONCE(x) ((void) 0) |
| # endif /* INTERNAL */ |
| # define ASSERT_NOT_TESTED() SYSLOG_INTERNAL_WARNING_ONCE("Not tested @%s:%d", \ |
| __FILE__, __LINE__) |
| #else |
| # define ASSERT(x) ((void) 0) |
| # define ASSERT_MESSAGE(level, msg, x) ASSERT(x) |
| # define ASSERT_NOT_TESTED() ASSERT(true) |
| # define ASSERT_CURIOSITY(x) ASSERT(true) |
| # define ASSERT_CURIOSITY_ONCE(x) ASSERT(true) |
| #endif /* DEBUG */ |
| |
| #define ASSERT_NOT_REACHED() ASSERT(false) |
| #define ASSERT_BUG_NUM(num, x) ASSERT_MESSAGE(CHKLVL_ASSERTS, "Bug #"#num, x) |
| #define ASSERT_NOT_IMPLEMENTED(x) ASSERT_MESSAGE(CHKLVL_ASSERTS, "Not implemented", x) |
| #define EXEMPT_TEST(tests) check_filter(tests, get_short_name(get_application_name())) |
| |
| #if defined(INTERNAL) || defined(DEBUG) |
| void internal_error(const char *file, int line, const char *expr); |
| bool ignore_assert(const char *assert_file_line, const char *expr); |
| #endif |
| |
| /* we now use apicheck as a SYSLOG + abort even for non-API builds */ |
| #define apicheck(x, msg) \ |
| ((void)((DEBUG_CHECKS(CHKLVL_ASSERTS) && !(x)) ? \ |
| (external_error(__FILE__, __LINE__, msg), 0) : 0)) |
| void external_error(const char *file, int line, const char *msg); |
| |
| #ifdef CLIENT_INTERFACE |
| # ifdef DEBUG |
| # define CLIENT_ASSERT(x, msg) apicheck(x, msg) |
| # else |
| # define CLIENT_ASSERT(x, msg) /* PR 215261: nothing in release builds */ |
| # endif |
| #else |
| # define CLIENT_ASSERT(x, msg) ASSERT_MESSAGE(CHKLVL_ASSERTS, msg, x) |
| #endif |
| |
| #ifdef DR_APP_EXPORTS |
| # define APP_EXPORT_ASSERT(x, msg) apicheck(x, msg) |
| #else |
| # define APP_EXPORT_ASSERT(x, msg) ASSERT_MESSAGE(CHKLVL_ASSERTS, msg, x) |
| #endif |
| |
| /* truncation assert - should be used wherever addressing cl warning 4244 */ |
| /* an explicit type cast to lower precision should match the type used here |
| * Later compiler versions cl.13 have an option /RCTc that will make make all |
| * explicit casts have that extra check, so these ASSERTs can then be removed. |
| */ |
| /* Assumption: int64 is our largest signed type (so casting to it never loses precision). |
| * Assumption C99 promotion rules (if same signess promote to size of larger, if |
| * different signess promote to size and signess of larger, if different signess and |
| * same size promote to unsigned). */ |
| #define CHECK_TRUNCATE_TYPE_byte(val) ((val) >= 0 && (val) <= UCHAR_MAX) |
| #define CHECK_TRUNCATE_TYPE_sbyte(val) ((val) <= SCHAR_MAX && ((int64)(val)) >= SCHAR_MIN) |
| #define CHECK_TRUNCATE_TYPE_ushort(val) ((val) >= 0 && (val) <= USHRT_MAX) |
| #define CHECK_TRUNCATE_TYPE_short(val) ((val) <= SHRT_MAX && ((int64)(val)) >= SHRT_MIN) |
| #define CHECK_TRUNCATE_TYPE_uint(val) ((val) >= 0 && (val) <= UINT_MAX) |
| #ifdef UNIX |
| /* The check causes a gcc warning that can't be disabled in gcc 4.2 and earlier |
| * (in gcc 4.3 it was moved to -Wextra under -Wtype-limits). |
| * The warning is on ((int64)(val)) >= INT_MIN |
| * if val has type uint that I can't seem to cast around and is impossible to ignore - |
| * "comparison is always true due to limited range of data type". |
| * See http://gcc.gnu.org/ml/gcc/2006-01/msg00784.html and note that the suggested |
| * workaround option doesn't exist as far as I can tell. We are potentially in trouble |
| * if val has type int64, is negative, and too big to fit in an int. |
| */ |
| # ifdef HAVE_TYPELIMITS_CONTROL |
| # define CHECK_TRUNCATE_TYPE_int(val) ((val) <= INT_MAX && ((int64)(val)) >= INT_MIN) |
| # else |
| # define CHECK_TRUNCATE_TYPE_int(val) \ |
| ((val) <= INT_MAX && (-(int64)(val)) <= ((int64)INT_MAX)+1) |
| # endif |
| #else |
| # define CHECK_TRUNCATE_TYPE_int(val) ((val) <= INT_MAX && ((int64)(val)) >= INT_MIN) |
| #endif |
| #ifdef X64 |
| # define CHECK_TRUNCATE_TYPE_size_t(val) ((val) >= 0) |
| /* avoid gcc warning: always true anyway since stats_int_t == int64 */ |
| # define CHECK_TRUNCATE_TYPE_stats_int_t(val) (true) |
| #else |
| # define CHECK_TRUNCATE_TYPE_size_t CHECK_TRUNCATE_TYPE_uint |
| # define CHECK_TRUNCATE_TYPE_stats_int_t CHECK_TRUNCATE_TYPE_int |
| #endif |
| |
| /* FIXME: too bad typeof is a GCC only extension - so need to pass both var and type */ |
| |
| /* var = (type) val; should always be preceded by a call to ASSERT_TRUNCATE */ |
| /* note that it is also OK to use ASSERT_TRUNCATE(type, type, val) for return values */ |
| #define ASSERT_TRUNCATE(var, type, val) ASSERT(sizeof(type) == sizeof(var) && \ |
| CHECK_TRUNCATE_TYPE_##type(val) && \ |
| "truncating "#var" to "#type) |
| #define CURIOSITY_TRUNCATE(var, type, val) \ |
| ASSERT_CURIOSITY(sizeof(type) == sizeof(var) && \ |
| CHECK_TRUNCATE_TYPE_##type(val) && \ |
| "truncating "#var" to "#type) |
| #define CLIENT_ASSERT_TRUNCATE(var, type, val, msg) \ |
| CLIENT_ASSERT(sizeof(type) == sizeof(var) && CHECK_TRUNCATE_TYPE_##type(val), msg) |
| |
| /* assumes val is unsigned and width<32 */ |
| #define ASSERT_BITFIELD_TRUNCATE(width, val) \ |
| ASSERT((val) < (1 << ((width)+1)) && "truncating to "#width" bits"); |
| #define CLIENT_ASSERT_BITFIELD_TRUNCATE(width, val, msg) \ |
| CLIENT_ASSERT((val) < (1 << ((width)+1)), msg); |
| |
| /* thread synchronization */ |
| #define LOCK_FREE_STATE -1 /* allows a quick >=0 test for contention */ |
| #define LOCK_SET_STATE (LOCK_FREE_STATE + 1) /* set when requested by a single thread */ |
| /* any value greater than LOCK_SET_STATE means multiple threads requested the lock */ |
| #define LOCK_CONTENDED_STATE (LOCK_SET_STATE + 1) |
| |
| #define SPINLOCK_FREE_STATE 0 /* for initstack_mutex is a spin lock with values 0 or 1 */ |
| |
| /* We want lazy init of the contended event (which can avoid creating |
| * dozens of kernel objects), but to initialize it atomically, we need |
| * either a pointer to separately-initialized memory or an inlined |
| * kernel handle. We can't allocate heap for locks b/c they're used |
| * too early and late, and it seems ugly to use a static array, so we |
| * end up having to expose KSYNCH_TYPE for Mac, resulting in a |
| * non-uniform initialization of the field. |
| * We can't keep this in os_exports.h due to header ordering constraints. |
| */ |
| #ifdef WINDOWS |
| # define KSYNCH_TYPE HANDLE |
| # define KSYNCH_TYPE_STATIC_INIT NULL |
| #elif defined(LINUX) |
| # define KSYNCH_TYPE volatile int |
| # define KSYNCH_TYPE_STATIC_INIT -1 |
| #elif defined(MACOS) |
| # include <mach/semaphore.h> |
| typedef struct _mac_synch_t { |
| semaphore_t sem; |
| volatile int value; |
| } mac_synch_t; |
| # define KSYNCH_TYPE mac_synch_t |
| # define KSYNCH_TYPE_STATIC_INIT {0,0} |
| #else |
| # error Unknown operating system |
| #endif |
| |
| typedef KSYNCH_TYPE contention_event_t; |
| |
| typedef struct _mutex_t { |
| volatile int lock_requests; /* number of threads requesting this lock minus 1 */ |
| /* a value greater than LOCK_FREE_STATE means the lock has been requested */ |
| contention_event_t contended_event; /* event object to wait on when contended */ |
| #ifdef DEADLOCK_AVOIDANCE |
| /* These fields are initialized with the INIT_LOCK_NO_TYPE macro */ |
| const char *name; /* set to variable lock name and location */ |
| /* We flag as a violation if a lock with rank numerically smaller |
| * or equal to the rank of a lock already held by the owning thread is acquired |
| */ |
| uint rank; /* sets rank order in which this lock can be set */ |
| thread_id_t owner; /* TID of owner (reusable, not available before initialization) */ |
| /* Above here is explicitly set w/ INIT_LOCK_NO_TYPE macro so update |
| * it if changing them. Below here is filled with 0's. |
| */ |
| |
| dcontext_t *owning_dcontext; /* dcontext responsible (reusable, multiple per thread) */ |
| struct _mutex_t *prev_owned_lock; /* linked list of thread owned locks */ |
| uint count_times_acquired; /* count total times this lock was acquired */ |
| uint count_times_contended; /* count total times this lock was contended upon */ |
| uint count_times_spin_pause; /* count total times this lock was contended in a spin pause loop */ |
| uint max_contended_requests;/* maximum number of simultaneous requests when contended */ |
| uint count_times_spin_only; /* count times contended but grabbed after spinning without yielding */ |
| /* we need to register all locks in the process to be able to dump regular statistics */ |
| /* linked list of all live locks (for all threads), another ad hoc double linked circular list */ |
| struct _mutex_t *prev_process_lock; |
| struct _mutex_t *next_process_lock; |
| /* TODO: we should also add cycles spent while holding the lock, KSTATS-like */ |
| #ifdef MUTEX_CALLSTACK |
| /* FIXME: case 5378: to avoid allocating memory in mutexes use a |
| * static array that will get too fat if larger than 4 |
| */ |
| # define MAX_MUTEX_CALLSTACK 4 |
| byte *callstack[MAX_MUTEX_CALLSTACK]; |
| /* keep as last item so not initialized in INIT_LOCK_NO_TYPE */ |
| # ifdef CLIENT_INTERFACE |
| /* i#779: support DR locks used as app locks */ |
| bool app_lock; |
| # endif |
| #else |
| # define MAX_MUTEX_CALLSTACK 0 /* cannot use */ |
| #endif /* MUTEX_CALLSTACK */ |
| bool deleted; /* this lock has been deleted at least once */ |
| #endif /* DEADLOCK_AVOIDANCE */ |
| /* Any new field needs to be initialized with INIT_LOCK_NO_TYPE */ |
| } mutex_t; |
| |
| /* A spin_mutex_t is the same thing as a mutex_t (and all utils.c users use it as |
| * such). It exists only to enforce type separation outside of utils.c which |
| * is why we can't simply do a typedef mutex_t spin_mutex_t. */ |
| typedef struct _spin_mutex_t { |
| mutex_t lock; |
| } spin_mutex_t; |
| |
| /* perhaps for DEBUG all locks should record owner? */ |
| typedef struct _recursive_lock_t { |
| mutex_t lock; |
| /* ASSUMPTION: reading owner field is atomic! |
| * Thus must allocate this statically (compiler should 4-byte-align |
| * this field, which is good enough) or align it manually! |
| * FIXME: provide creation routine that does that for non-static locks |
| */ |
| thread_id_t owner; |
| uint count; |
| } recursive_lock_t; |
| |
| typedef struct _read_write_lock_t { |
| mutex_t lock; |
| /* FIXME: could be merged w/ lock->state if want to get more sophisticated... |
| * we could use the lock->state as a 32-bit counter, incremented |
| * by readers, and with the MSB bit (sign) set by writers |
| */ |
| volatile int num_readers; |
| /* we store the writer so that writers can be readers */ |
| thread_id_t writer; |
| volatile int num_pending_readers; /* readers that have contended with a writer */ |
| contention_event_t writer_waiting_readers; /* event object for writer to wait on */ |
| contention_event_t readers_waiting_writer; /* event object for readers to wait on */ |
| /* make sure to update the two INIT_READWRITE_LOCK cases if you add new fields */ |
| } read_write_lock_t; |
| |
| #ifdef DEADLOCK_AVOIDANCE |
| # define LOCK_RANK(lock) lock##_rank |
| /* This should be the single place where all ranks are declared */ |
| /* Your lock should preferably take the last possible rank in this |
| * list, usually at the location marked as ADD HERE */ |
| |
| enum { |
| LOCK_RANK(outermost_lock), /* pseudo lock, sentinel of thread owned locks list */ |
| LOCK_RANK(thread_in_DR_exclusion), /* outermost */ |
| LOCK_RANK(all_threads_synch_lock), /* < thread_initexit_lock */ |
| |
| LOCK_RANK(trace_building_lock), /* < bb_building_lock, < table_rwlock */ |
| |
| /* decode exception -> check if should_intercept requires all_threads |
| * FIXME: any other locks that could be interrupted by exception that |
| * could be app's fault? |
| */ |
| LOCK_RANK(thread_initexit_lock), /* < all_threads_lock, < snapshot_lock */ |
| |
| LOCK_RANK(bb_building_lock), /* < change_linking_lock + all vm and heap locks */ |
| |
| #if defined(WINDOWS) && defined(STACK_GUARD_PAGE) |
| LOCK_RANK(exception_stack_lock), /* < all_threads_lock */ |
| #endif |
| /* FIXME: grabbed on an exception, which could happen anywhere! |
| * possible deadlock if already held */ |
| LOCK_RANK(all_threads_lock), /* < global_alloc_lock */ |
| |
| LOCK_RANK(linking_lock), /* < dynamo_areas < global_alloc_lock */ |
| |
| #ifdef SHARING_STUDY |
| LOCK_RANK(shared_blocks_lock), /* < global_alloc_lock */ |
| LOCK_RANK(shared_traces_lock), /* < global_alloc_lock */ |
| #endif |
| |
| LOCK_RANK(synch_lock), /* per thread, < protect_info */ |
| |
| LOCK_RANK(protect_info), /* < cache and heap traversal locks */ |
| |
| #if defined(CLIENT_SIDELINE) && defined(CLIENT_INTERFACE) |
| LOCK_RANK(sideline_mutex), |
| #endif |
| |
| LOCK_RANK(shared_cache_flush_lock), /* < shared_cache_count_lock, |
| < shared_delete_lock, |
| < change_linking_lock */ |
| LOCK_RANK(shared_delete_lock), /* < change_linking_lock < shared_vm_areas */ |
| LOCK_RANK(lazy_delete_lock), /* > shared_delete_lock, < shared_cache_lock */ |
| |
| LOCK_RANK(shared_cache_lock), /* < dynamo_areas, < allunits_lock, |
| * < table_rwlock for shared cache regen/replace, |
| * < shared_vm_areas for cache unit flush, |
| * < change_linking_lock for add_to_free_list */ |
| |
| LOCK_RANK(change_linking_lock), /* < shared_vm_areas, < all heap locks */ |
| |
| LOCK_RANK(shared_vm_areas), /* > change_linking_lock, < executable_areas */ |
| LOCK_RANK(shared_cache_count_lock), |
| |
| #if defined(CLIENT_SIDELINE) && defined(CLIENT_INTERFACE) |
| LOCK_RANK(fragment_delete_mutex), /* > shared_vm_areas */ |
| #endif |
| |
| LOCK_RANK(tracedump_mutex), /* > fragment_delete_mutex, > shared_vm_areas */ |
| |
| LOCK_RANK(emulate_write_lock), /* in future may be < emulate_write_areas */ |
| |
| LOCK_RANK(unit_flush_lock), /* > shared_delete_lock */ |
| |
| #ifdef LINUX |
| LOCK_RANK(maps_iter_buf_lock), /* < executable_areas, < module_data_lock, |
| * < hotp_vul_table_lock */ |
| #endif |
| |
| #ifdef HOT_PATCHING_INTERFACE |
| /* This lock's rank needs to be after bb_building_lock because |
| * build_bb_ilist() is where injection takes place, which means the |
| * bb lock has been acquired before any hot patching related work is done |
| * on a bb. |
| */ |
| LOCK_RANK(hotp_vul_table_lock), /* > bb_building_lock, |
| * < dynamo_areas, < heap_unit_lock. */ |
| #endif |
| LOCK_RANK(coarse_info_lock), /* < special_heap_lock, < global_alloc_lock, |
| * > change_linking_lock */ |
| |
| LOCK_RANK(executable_areas), /* < dynamo_areas < global_alloc_lock |
| * < process_module_vector_lock (diagnostics) |
| */ |
| #ifdef RCT_IND_BRANCH |
| LOCK_RANK(rct_module_lock), /* > coarse_info_lock, > executable_areas, |
| * < module_data_lock, < heap allocation */ |
| #endif |
| #ifdef RETURN_AFTER_CALL |
| LOCK_RANK(after_call_lock), /* < table_rwlock, > bb_building_lock, |
| * > coarse_info_lock, > executable_areas, |
| * < module_data_lock */ |
| #endif |
| LOCK_RANK(written_areas), /* > executable_areas, < module_data_lock, |
| * < dynamo_areas < global_alloc_lock */ |
| LOCK_RANK(module_data_lock), /* < loaded_module_areas, < special_heap_lock, |
| * > executable_areas */ |
| LOCK_RANK(special_units_list_lock), /* < special_heap_lock */ |
| LOCK_RANK(special_heap_lock), /* > bb_building_lock, > hotp_vul_table_lock |
| * < dynamo_areas, < heap_unit_lock */ |
| LOCK_RANK(coarse_info_incoming_lock), /* < coarse_table_rwlock |
| * > special_heap_lock, > coarse_info_lock, |
| * > change_linking_lock */ |
| |
| /* (We don't technically need a coarse_table_rwlock separate from table_rwlock |
| * anymore but having it gives us flexibility so I'm leaving it) |
| */ |
| LOCK_RANK(coarse_table_rwlock), /* < global_alloc_lock, < coarse_th_table_rwlock */ |
| /* We make the pc table separate (we write it while holding master table lock) */ |
| LOCK_RANK(coarse_pclookup_table_rwlock), /* < global_alloc_lock, |
| * < coarse_th_table_rwlock */ |
| /* We make the th table separate (we look in it while holding master table lock) */ |
| LOCK_RANK(coarse_th_table_rwlock), /* < global_alloc_lock */ |
| |
| LOCK_RANK(process_module_vector_lock), /* < snapshot_lock > all_threads_synch_lock */ |
| /* For Loglevel 1 and higher, with LOG_MEMSTATS, the snapshot lock is |
| * grabbed on an exception, possible deadlock if already held FIXME */ |
| LOCK_RANK(snapshot_lock), /* < dynamo_areas */ |
| #ifdef PROGRAM_SHEPHERDING |
| LOCK_RANK(futureexec_areas), /* > executable_areas |
| * < dynamo_areas < global_alloc_lock */ |
| # ifdef WINDOWS |
| LOCK_RANK(app_flushed_areas), /* < dynamo_areas < global_alloc_lock */ |
| # endif |
| #endif |
| LOCK_RANK(pretend_writable_areas), /* < dynamo_areas < global_alloc_lock */ |
| LOCK_RANK(patch_proof_areas), /* < dynamo_areas < global_alloc_lock */ |
| LOCK_RANK(emulate_write_areas), /* < dynamo_areas < global_alloc_lock */ |
| LOCK_RANK(IAT_areas), /* < dynamo_areas < global_alloc_lock */ |
| #ifdef CLIENT_INTERFACE |
| /* PR 198871: this same label is used for all client locks */ |
| LOCK_RANK(dr_client_mutex), /* > module_data_lock */ |
| LOCK_RANK(client_thread_count_lock), /* > dr_client_mutex */ |
| LOCK_RANK(client_flush_request_lock), /* > dr_client_mutex */ |
| LOCK_RANK(callback_registration_lock), /* > dr_client_mutex */ |
| LOCK_RANK(client_tls_lock), /* > dr_client_mutex */ |
| #endif |
| LOCK_RANK(intercept_hook_lock), /* < table_rwlock */ |
| LOCK_RANK(privload_lock), /* < modlist_areas, < table_rwlock */ |
| #ifdef LINUX |
| LOCK_RANK(sigfdtable_lock), /* < table_rwlock */ |
| #endif |
| LOCK_RANK(table_rwlock), /* > dr_client_mutex */ |
| LOCK_RANK(loaded_module_areas), /* < dynamo_areas < global_alloc_lock */ |
| LOCK_RANK(aslr_areas), /* < dynamo_areas < global_alloc_lock */ |
| LOCK_RANK(aslr_pad_areas), /* < dynamo_areas < global_alloc_lock */ |
| LOCK_RANK(native_exec_areas), /* < dynamo_areas < global_alloc_lock */ |
| LOCK_RANK(thread_vm_areas), /* currently never used */ |
| |
| LOCK_RANK(app_pc_table_rwlock), /* > after_call_lock, > rct_module_lock, |
| * > module_data_lock */ |
| |
| LOCK_RANK(dead_tables_lock), /* < heap_unit_lock */ |
| LOCK_RANK(aslr_lock), |
| |
| #ifdef HOT_PATCHING_INTERFACE |
| LOCK_RANK(hotp_only_tramp_areas_lock), /* > hotp_vul_table_lock, |
| * < global_alloc_lock */ |
| LOCK_RANK(hotp_patch_point_areas_lock), /* > hotp_vul_table_lock, |
| * < global_alloc_lock */ |
| #endif |
| #ifdef CALL_PROFILE |
| LOCK_RANK(profile_callers_lock), /* < global_alloc_lock */ |
| #endif |
| LOCK_RANK(coarse_stub_areas), /* < global_alloc_lock */ |
| LOCK_RANK(moduledb_lock), /* < global heap allocation */ |
| LOCK_RANK(pcache_dir_check_lock), |
| #ifdef UNIX |
| LOCK_RANK(suspend_lock), |
| LOCK_RANK(shared_lock), |
| #endif |
| LOCK_RANK(modlist_areas), /* < dynamo_areas < global_alloc_lock */ |
| #ifdef WINDOWS |
| LOCK_RANK(drwinapi_localheap_lock), /* < global_alloc_lock */ |
| #endif |
| #ifdef CLIENT_INTERFACE |
| LOCK_RANK(client_aux_libs), |
| # ifdef WINDOWS |
| LOCK_RANK(client_aux_lib64_lock), |
| # endif |
| #endif |
| #ifdef WINDOWS |
| LOCK_RANK(alt_tls_lock), |
| #endif |
| /* ADD HERE a lock around section that may allocate memory */ |
| |
| /* N.B.: the order of allunits < global_alloc < heap_unit is relied on |
| * in the {fcache,heap}_low_on_memory routines. IMPORTANT - any locks |
| * added between the allunits_lock and heap_unit_lock must have special |
| * handling in the fcache_low_on_memory() routine. |
| */ |
| LOCK_RANK(allunits_lock), /* < global_alloc_lock */ |
| LOCK_RANK(fcache_unit_areas), /* > allunits_lock, |
| < dynamo_areas, < global_alloc_lock */ |
| IF_NO_MEMQUERY_(LOCK_RANK(all_memory_areas)) /* < dynamo_areas */ |
| IF_UNIX_(LOCK_RANK(set_thread_area_lock)) /* no constraints */ |
| LOCK_RANK(landing_pad_areas_lock), /* < global_alloc_lock, < dynamo_areas */ |
| LOCK_RANK(dynamo_areas), /* < global_alloc_lock */ |
| LOCK_RANK(map_intercept_pc_lock), /* < global_alloc_lock */ |
| LOCK_RANK(global_alloc_lock),/* < heap_unit_lock */ |
| LOCK_RANK(heap_unit_lock), /* recursive */ |
| LOCK_RANK(vmh_lock), /* lowest level */ |
| LOCK_RANK(last_deallocated_lock), |
| /*---- no one below here can be held at a memory allocation site ----*/ |
| |
| #ifdef UNIX |
| LOCK_RANK(tls_lock), /* if used for get_thread_private_dcontext() may |
| * need to be even lower: as it is, only used for set */ |
| #endif |
| LOCK_RANK(reset_pending_lock), /* > heap_unit_lock */ |
| |
| LOCK_RANK(initstack_mutex), /* FIXME: NOT TESTED */ |
| |
| LOCK_RANK(event_lock), /* FIXME: NOT TESTED */ |
| LOCK_RANK(do_threshold_mutex), /* FIXME: NOT TESTED */ |
| LOCK_RANK(threads_killed_lock), /* FIXME: NOT TESTED */ |
| LOCK_RANK(child_lock), /* FIXME: NOT TESTED */ |
| |
| #ifdef SIDELINE |
| LOCK_RANK(sideline_lock), /* FIXME: NOT TESTED */ |
| LOCK_RANK(do_not_delete_lock),/* FIXME: NOT TESTED */ |
| LOCK_RANK(remember_lock),/* FIXME: NOT TESTED */ |
| LOCK_RANK(sideline_table_lock),/* FIXME: NOT TESTED */ |
| #endif |
| #ifdef SIMULATE_ATTACK |
| LOCK_RANK(simulate_lock), |
| #endif |
| #ifdef KSTATS |
| LOCK_RANK(process_kstats_lock), |
| #endif |
| #ifdef X64 |
| LOCK_RANK(request_region_be_heap_reachable_lock), /* > heap_unit_lock, vmh_lock |
| * < report_buf_lock (for assert) */ |
| #endif |
| LOCK_RANK(report_buf_lock), |
| /* FIXME: if we crash while holding the all_threads_lock, snapshot_lock |
| * (for loglevel 1+, logmask LOG_MEMSTATS), or any lock below this |
| * line (except the profile_dump_lock, and possibly others depending on |
| * options) we will deadlock |
| */ |
| #ifdef LINUX |
| LOCK_RANK(memory_info_buf_lock), |
| #elif defined(MACOS) |
| LOCK_RANK(memquery_backing_lock), |
| #endif |
| #ifdef WINDOWS |
| LOCK_RANK(dump_core_lock), |
| #endif |
| |
| LOCK_RANK(logdir_mutex), /* recursive */ |
| LOCK_RANK(diagnost_reg_mutex), |
| #ifdef WINDOWS_PC_SAMPLE |
| LOCK_RANK(profile_dump_lock), |
| #endif |
| |
| LOCK_RANK(prng_lock), |
| /* ---------------------------------------------------------- */ |
| /* No new locks below this line, reserved for innermost ASSERT, |
| * SYSLOG and STATS facilities */ |
| LOCK_RANK(options_lock), |
| #ifdef WINDOWS |
| LOCK_RANK(debugbox_lock), |
| #endif |
| LOCK_RANK(eventlog_mutex), /* < datasec_selfprot_lock only for hello_message */ |
| LOCK_RANK(datasec_selfprot_lock), |
| LOCK_RANK(thread_stats_lock), |
| #ifdef UNIX |
| /* shared_itimer_lock is used in timer signal handling, which could happen at |
| * anytime, so we put it at the innermost. |
| */ |
| LOCK_RANK(shared_itimer_lock), |
| #endif |
| LOCK_RANK(innermost_lock), /* innermost internal lock, head of all locks list */ |
| }; |
| |
| struct _thread_locks_t; |
| typedef struct _thread_locks_t thread_locks_t; |
| |
| extern mutex_t outermost_lock; |
| |
| void locks_thread_init(dcontext_t *dcontext); |
| void locks_thread_exit(dcontext_t *dcontext); |
| uint locks_not_closed(void); |
| bool thread_owns_no_locks(dcontext_t *dcontext); |
| bool thread_owns_one_lock(dcontext_t *dcontext, mutex_t *lock); |
| bool thread_owns_two_locks(dcontext_t *dcontext, mutex_t *lock1, mutex_t *lock2); |
| bool thread_owns_first_or_both_locks_only(dcontext_t *dcontext, mutex_t *lock1, mutex_t *lock2); |
| |
| /* We need the (mutex_t) type specifier for direct initialization, |
| but not when defining compound structures, hence NO_TYPE */ |
| # define INIT_LOCK_NO_TYPE(name, rank) {LOCK_FREE_STATE, \ |
| KSYNCH_TYPE_STATIC_INIT, \ |
| name, rank, \ |
| INVALID_THREAD_ID,} |
| #else |
| /* Ignore the arguments */ |
| # define INIT_LOCK_NO_TYPE(name, rank) {LOCK_FREE_STATE, KSYNCH_TYPE_STATIC_INIT} |
| #endif /* DEADLOCK_AVOIDANCE */ |
| |
| /* Structure assignments and initialization don't work the same in gcc and cl |
| in gcc it is ok to have assignment {gcc; var = (mutex_t) {0}; } |
| and so it helped to have the type specifier. |
| Windows however doesn't like that at all even for initialization, |
| so I have to go and add explicit initialized temporaries to be assigned to a variable |
| i.e. {cl; {mutex_t temp = {0}; var = temp; }; } |
| } |
| */ |
| #ifdef WINDOWS |
| # define STRUCTURE_TYPE(x) |
| #else |
| /* FIXME: gcc 4.1.1 complains "initializer element is not constant" |
| * if we have our old (x) here; presumably some older gcc needed it; |
| * once sure nobody does let's remove this define altogether. |
| */ |
| # define STRUCTURE_TYPE(x) |
| #endif |
| |
| #define INIT_LOCK_FREE(lock) STRUCTURE_TYPE(mutex_t) INIT_LOCK_NO_TYPE( \ |
| #lock "(mutex)" "@" __FILE__ ":" STRINGIFY(__LINE__), \ |
| LOCK_RANK(lock)) |
| |
| #define ASSIGN_INIT_LOCK_FREE(var, lock) do { \ |
| mutex_t initializer_##lock = STRUCTURE_TYPE(mutex_t) INIT_LOCK_NO_TYPE(\ |
| #lock "(mutex)" "@" __FILE__ ":" STRINGIFY(__LINE__), \ |
| LOCK_RANK(lock)); \ |
| var = initializer_##lock; \ |
| } while (0) |
| |
| #define ASSIGN_INIT_SPINMUTEX_FREE(var, spinmutex) \ |
| ASSIGN_INIT_LOCK_FREE((var).lock, spinmutex) |
| |
| #define INIT_RECURSIVE_LOCK(lock) STRUCTURE_TYPE(recursive_lock_t) { \ |
| INIT_LOCK_NO_TYPE( \ |
| #lock "(recursive)" "@" __FILE__ ":" STRINGIFY(__LINE__) , \ |
| LOCK_RANK(lock)), \ |
| INVALID_THREAD_ID, 0 \ |
| } |
| |
| #define INIT_READWRITE_LOCK(lock) STRUCTURE_TYPE(read_write_lock_t) { \ |
| INIT_LOCK_NO_TYPE( \ |
| #lock "(readwrite)" "@" __FILE__ ":" STRINGIFY(__LINE__) , \ |
| LOCK_RANK(lock)), \ |
| 0, INVALID_THREAD_ID, \ |
| 0, \ |
| KSYNCH_TYPE_STATIC_INIT, KSYNCH_TYPE_STATIC_INIT \ |
| } |
| |
| #define ASSIGN_INIT_READWRITE_LOCK_FREE(var, lock) do { \ |
| read_write_lock_t initializer_##lock = STRUCTURE_TYPE(read_write_lock_t) \ |
| {INIT_LOCK_NO_TYPE( \ |
| #lock "(readwrite)" "@" __FILE__ ":" STRINGIFY(__LINE__), \ |
| LOCK_RANK(lock)), \ |
| 0, INVALID_THREAD_ID, \ |
| 0, \ |
| KSYNCH_TYPE_STATIC_INIT, KSYNCH_TYPE_STATIC_INIT \ |
| }; \ |
| var = initializer_##lock; \ |
| } while (0) |
| |
| #define ASSIGN_INIT_RECURSIVE_LOCK_FREE(var, lock) do { \ |
| recursive_lock_t initializer_##lock = STRUCTURE_TYPE(recursive_lock_t) \ |
| {INIT_LOCK_NO_TYPE( \ |
| #lock "(recursive)" "@" __FILE__ ":" STRINGIFY(__LINE__), \ |
| LOCK_RANK(lock)), \ |
| INVALID_THREAD_ID, 0}; \ |
| var = initializer_##lock; \ |
| } while (0) |
| |
| #define INIT_SPINLOCK_FREE(lock) STRUCTURE_TYPE(mutex_t) {SPINLOCK_FREE_STATE, } |
| |
| /* in order to use parallel names to the above INIT_*LOCK routines */ |
| #define DELETE_LOCK(lock) mutex_delete(&lock) |
| #define DELETE_SPINMUTEX(spinmutex) spinmutex_delete(&spinmutex) |
| #define DELETE_RECURSIVE_LOCK(rec_lock) mutex_delete(&(rec_lock).lock) |
| #define DELETE_READWRITE_LOCK(rwlock) mutex_delete(&(rwlock).lock) |
| /* mutexes need to release any kernel objects that were created */ |
| void mutex_delete(mutex_t *lock); |
| |
| /* basic synchronization functions */ |
| void mutex_lock(mutex_t *mutex); |
| bool mutex_trylock(mutex_t *mutex); |
| void mutex_unlock(mutex_t *mutex); |
| #ifdef UNIX |
| void mutex_fork_reset(mutex_t *mutex); |
| #endif |
| #ifdef CLIENT_INTERFACE |
| void mutex_mark_as_app(mutex_t *lock); |
| #endif |
| |
| /* spinmutex synchronization */ |
| bool spinmutex_trylock(spin_mutex_t *spin_lock); |
| void spinmutex_lock(spin_mutex_t *spin_lock); |
| void spinmutex_lock_no_yield(spin_mutex_t *spin_lock); |
| void spinmutex_unlock(spin_mutex_t *spin_lock); |
| void spinmutex_delete(spin_mutex_t *spin_lock); |
| |
| /* tests if a lock is held, but doesn't grab it */ |
| /* note that this is not a synchronizing function, its intended uses are : |
| * 1. for synch code to guarantee that a thread it has suspened isn't holding |
| * a lock (note that a return of true doesn't mean that the suspended thread |
| * is holding the lock, could be some other thread) |
| * 2. for when you want to assert that you hold a lock, while you can't |
| * actually do that, you can assert with this function that the lock is held |
| * by someone |
| * 3. read_{un,}lock use this function to check the state of write lock mutex |
| */ |
| static inline bool |
| mutex_testlock(mutex_t *lock) |
| { |
| return lock->lock_requests > LOCK_FREE_STATE; |
| } |
| |
| |
| /* A recursive lock can be taken more than once by the owning thread */ |
| void acquire_recursive_lock(recursive_lock_t *lock); |
| bool try_recursive_lock(recursive_lock_t *lock); |
| void release_recursive_lock(recursive_lock_t *lock); |
| bool self_owns_recursive_lock(recursive_lock_t *lock); |
| |
| /* A read write lock allows multiple readers or alternatively a single writer */ |
| void read_lock(read_write_lock_t *rw); |
| void write_lock(read_write_lock_t *rw); |
| bool write_trylock(read_write_lock_t *rw); |
| void read_unlock(read_write_lock_t *rw); |
| void write_unlock(read_write_lock_t *rw); |
| bool self_owns_write_lock(read_write_lock_t *rw); |
| |
| /* a broadcast event wakes all waiting threads when signalled */ |
| struct _broadcast_event_t; |
| typedef struct _broadcast_event_t broadcast_event_t; |
| broadcast_event_t * create_broadcast_event(void); |
| void destroy_broadcast_event(broadcast_event_t *be); |
| /* NOTE : to avoid races a signaler should always do the required action to |
| * make any wait_condition(s) for the WAIT_FOR_BROADCAST_EVENT(s) on the |
| * event false BEFORE signaling the event |
| */ |
| void signal_broadcast_event(broadcast_event_t *be); |
| /* the wait macro, we force people to use a while loop since our current |
| * implementation has a race (ATOMIC_INC, else clause ATOMIC_DEC, in the |
| * middle someone could signal thinking we are waiting) |
| * |
| * event is the broadcast event |
| * wait_condition is the conditional expression we want to become true |
| * pre is code we should execute before we actually do a wait |
| * post is code we should execute after we return from a wait |
| * |
| * in typical usage pre and post might be empty, or might free and regrab a |
| * lock we shouldn't hold during the wait |
| * NOTE : because we loop to handle certain race conditions, wait_condition |
| * pre and post might all be evaluated several times |
| */ |
| #define WAIT_FOR_BROADCAST_EVENT(wait_condition, pre, post, event) do { \ |
| while (wait_condition) { \ |
| intend_wait_broadcast_event_helper(event); \ |
| if (wait_condition) { \ |
| pre; \ |
| wait_broadcast_event_helper(event); \ |
| post; \ |
| } else { \ |
| unintend_wait_broadcast_event_helper(event); \ |
| } \ |
| } \ |
| } while (0) |
| /* helpers for the broadcast event wait macro, don't call these directly */ |
| void intend_wait_broadcast_event_helper(broadcast_event_t *be); |
| void unintend_wait_broadcast_event_helper(broadcast_event_t *be); |
| void wait_broadcast_event_helper(broadcast_event_t *be); |
| |
| /* test whether locks are held at all */ |
| #define WRITE_LOCK_HELD(rw) (mutex_testlock(&(rw)->lock) && ((rw)->num_readers == 0)) |
| #define READ_LOCK_HELD(rw) ((rw)->num_readers > 0) |
| |
| /* test whether current thread owns locks |
| * for non-DEADLOCK_AVOIDANCE, cannot tell who owns it, so we bundle |
| * into asserts to make sure not used in a way that counts on it |
| * knowing for sure. |
| */ |
| #ifdef DEADLOCK_AVOIDANCE |
| # define OWN_MUTEX(m) ((m)->owner == get_thread_id()) |
| # define ASSERT_OWN_MUTEX(pred, m) \ |
| ASSERT(!(pred) || OWN_MUTEX(m)) |
| # define ASSERT_DO_NOT_OWN_MUTEX(pred, m) \ |
| ASSERT(!(pred) || !OWN_MUTEX(m)) |
| # define OWN_NO_LOCKS(dc) thread_owns_no_locks(dc) |
| # define ASSERT_OWN_NO_LOCKS() do { \ |
| dcontext_t *dc = get_thread_private_dcontext(); \ |
| ASSERT(dc == NULL /* no way to tell */ || OWN_NO_LOCKS(dc)); \ |
| } while (0) |
| #else |
| /* don't know for sure: imprecise in a conservative direction */ |
| # define OWN_MUTEX(m) (mutex_testlock(m)) |
| # define ASSERT_OWN_MUTEX(pred, m) \ |
| ASSERT(!(pred) || OWN_MUTEX(m)) |
| # define ASSERT_DO_NOT_OWN_MUTEX(pred, m) \ |
| ASSERT(!(pred) || true/*no way to tell*/) |
| # define OWN_NO_LOCKS(dc) true /* no way to tell */ |
| # define ASSERT_OWN_NO_LOCKS() /* no way to tell */ |
| #endif |
| #define ASSERT_OWN_WRITE_LOCK(pred, rw) \ |
| ASSERT(!(pred) || self_owns_write_lock(rw)) |
| #define ASSERT_DO_NOT_OWN_WRITE_LOCK(pred, rw) \ |
| ASSERT(!(pred) || !self_owns_write_lock(rw)) |
| /* FIXME: no way to tell if current thread is one of the readers */ |
| #define ASSERT_OWN_READ_LOCK(pred, rw) \ |
| ASSERT(!(pred) || READ_LOCK_HELD(rw)) |
| #define READWRITE_LOCK_HELD(rw) (READ_LOCK_HELD(rw) || self_owns_write_lock(rw)) |
| #define ASSERT_OWN_READWRITE_LOCK(pred, rw) \ |
| ASSERT(!(pred) || READWRITE_LOCK_HELD(rw)) |
| #define ASSERT_OWN_RECURSIVE_LOCK(pred, l) \ |
| ASSERT(!(pred) || self_owns_recursive_lock(l)) |
| |
| /* in machine-specific assembly file: */ |
| int atomic_swap(volatile int *addr, int value); |
| |
| #define SHARED_MUTEX(operation, lock) do { \ |
| if (SHARED_FRAGMENTS_ENABLED() && !INTERNAL_OPTION(single_thread_in_DR)) \ |
| mutex_##operation(&(lock)); \ |
| } while (0) |
| #define SHARED_RECURSIVE_LOCK(operation, lock) do { \ |
| if (SHARED_FRAGMENTS_ENABLED() && !INTERNAL_OPTION(single_thread_in_DR)) \ |
| operation##_recursive_lock(&(lock)); \ |
| } while (0) |
| /* internal use only */ |
| #define USE_BB_BUILDING_LOCK_STEADY_STATE() \ |
| (DYNAMO_OPTION(shared_bbs) && !INTERNAL_OPTION(single_thread_in_DR)) |
| /* anyone guarding the bb_building_lock with this must use SHARED_BB_{UN,}LOCK */ |
| #define USE_BB_BUILDING_LOCK() \ |
| (USE_BB_BUILDING_LOCK_STEADY_STATE() && bb_lock_start) |
| #define SHARED_BB_LOCK() do { \ |
| if (USE_BB_BUILDING_LOCK()) \ |
| mutex_lock(&(bb_building_lock)); \ |
| } while (0) |
| /* We explicitly check the lock_requests to handle a thread from appearing |
| * suddenly and causing USE_BB_BUILDING_LOCK() to return true while we're |
| * about to unlock it. |
| * We'll still have a race where the original thread and the new thread |
| * add to the cache simultaneously, and the original thread can do the |
| * unlock (with the 2nd thread's unlock then being a nop), but it should |
| * only happen in extreme corner cases. In debug it could raise an |
| * error about the non-owner releasing the mutex. |
| */ |
| #define SHARED_BB_UNLOCK() do { \ |
| if (USE_BB_BUILDING_LOCK() && bb_building_lock.lock_requests > LOCK_FREE_STATE) \ |
| mutex_unlock(&(bb_building_lock)); \ |
| } while (0) |
| /* we assume dynamo_resetting is only done w/ all threads suspended */ |
| #define NEED_SHARED_LOCK(flags) \ |
| (TEST(FRAG_SHARED, (flags)) && !INTERNAL_OPTION(single_thread_in_DR) \ |
| && !dynamo_exited && !dynamo_resetting) |
| #define SHARED_FLAGS_MUTEX(flags, operation, lock) do { \ |
| if (NEED_SHARED_LOCK((flags))) \ |
| mutex_##operation(&(lock)); \ |
| } while (0) |
| #define SHARED_FLAGS_RECURSIVE_LOCK(flags, operation, lock) do { \ |
| if (NEED_SHARED_LOCK((flags))) \ |
| operation##_recursive_lock(&(lock)); \ |
| } while (0) |
| |
| /* Our parameters (option string, logdir, etc.) are configured |
| * through files and secondarily environment variables. |
| * On Windows we also support using the registry. |
| * The routines below are used to check both of those places. |
| * value is a buffer allocated by the caller to hold the |
| * resulting value. |
| */ |
| #include "config.h" |
| |
| /**************************************************************************** |
| * hashing functions |
| */ |
| /* bits=entries: 8=256, 12=4096, 13=8192, 14=16384, 16=65536 */ |
| |
| /* Our default hash function is a bitwise & with a mask (HASH_FUNC_BITS) |
| * to select index bits, and maybe offset to ignore least significant bits. |
| * (as we're dealing with pcs here that is generally enough, but for larger |
| * table sizes a more complicated function is sometimes needed so the |
| * HASH_FUNC macro supports other hashing functions) |
| * These macros would be typed as follows: |
| * extern uint HASH_FUNC(uint val, table* table); |
| * where table is any struct that contains |
| * hash_func, hash_mask and hash_offset elements |
| * extern uint HASH_FUNC_BITS(uint val, int num_bits); |
| * extern uint HASH_MASK(int num_bits); |
| * extern uint HASHTABLE_SIZE(int num_bits); |
| */ |
| |
| /* FIXME - xref 8139 which is best 2654435769U 2654435761U or 0x9e379e37 |
| * FIXME PR 212574 (==8139): would we want the 32-bit one for smaller index values? |
| */ |
| #define PHI_2_32 2654435769U /* (sqrt(5)-1)/2 * (2^32) */ |
| #define PHI_2_64 11400714819323198485U /* (sqrt(5)-1)/2 * (2^64) */ |
| #ifdef X64 |
| # define HASH_PHI PHI_2_64 |
| # define HASH_TAG_BITS 64 |
| #else |
| # define HASH_PHI PHI_2_32 |
| # define HASH_TAG_BITS 32 |
| #endif |
| |
| /* bitmask selecting num_bits least significant bits */ |
| #define HASH_MASK(num_bits) ((~PTR_UINT_0)>>(HASH_TAG_BITS-(num_bits))) |
| |
| /* evaluate hash function and select index bits. Although bit |
| * selection and shifting could be done in reverse, better assembly |
| * code can be emitted when hash_mask selects the index bits */ |
| #define HASH_FUNC(val, table) \ |
| ((uint)((HASH_VALUE_FOR_TABLE(val, table) & ((table)->hash_mask)) \ |
| >> (table)->hash_mask_offset)) |
| |
| #ifdef X86 |
| /* no instruction alignment -> use the lsb! |
| * for 64-bit we assume the mask is taking out everything beyond uint range |
| */ |
| # define HASH_FUNC_BITS(val, num_bits) ((uint)((val) & (HASH_MASK(num_bits)))) |
| #else |
| /* do not use the lsb (alignment!) */ |
| /* FIXME: this function in product builds is in fact not used on |
| * addresses so ignoring the LSB is not helping. |
| * Better use the more generic HASH_FUNC that allows for hash_offset other than 1 |
| */ |
| # define HASH_FUNC_BITS(val, num_bits) (((val) & (HASH_MASK(num_bits))) >> 1) |
| #endif |
| |
| /* FIXME - xref 8139, what's the best shift for multiply phi? In theory for |
| * m bit table should take the middle m bits of the qword result. We currently |
| * take the top m bits of the lower dword which is prob. almost as good, but |
| * could experiment (however, I suspect probing strategy might make more of a |
| * difference at that point). You're not allowed to touch this code without |
| * first reading Knuth vol 3 sec 6.4 */ |
| #define HASH_VALUE_FOR_TABLE(val, table) \ |
| ((table)->hash_func == HASH_FUNCTION_NONE ? \ |
| (val) : \ |
| ((table)->hash_func == HASH_FUNCTION_MULTIPLY_PHI ? \ |
| /* All ibl tables use NONE so don't need to worry about \ |
| * the later hash_mask_offset shift. FIXME - structure all \ |
| * these macros a little clearly/efficiently. */ \ |
| /* case 8457: keep in sync w/ hash_value()'s calc */ \ |
| (((val) * HASH_PHI) >> (HASH_TAG_BITS - (table)->hash_bits)) : \ |
| hash_value((val), (table)->hash_func, (table)->hash_mask, \ |
| (table)->hash_bits))) |
| |
| #define HASHTABLE_SIZE(num_bits) (1U << (num_bits)) |
| |
| /* Note that 1 will be the default */ |
| typedef enum { |
| HASH_FUNCTION_NONE = 0, |
| HASH_FUNCTION_MULTIPLY_PHI = 1, |
| #ifdef INTERNAL |
| HASH_FUNCTION_LOWER_BSWAP = 2, |
| HASH_FUNCTION_BSWAP_XOR = 3, |
| HASH_FUNCTION_SWAP_12TO15 = 4, |
| HASH_FUNCTION_SWAP_12TO15_AND_NONE = 5, |
| HASH_FUNCTION_SHIFT_XOR = 6, |
| #endif |
| HASH_FUNCTION_STRING = 7, |
| HASH_FUNCTION_STRING_NOCASE = 8, |
| HASH_FUNCTION_ENUM_MAX, |
| } hash_function_t; |
| |
| ptr_uint_t hash_value(ptr_uint_t val, hash_function_t func, ptr_uint_t mask, uint bits); |
| uint hashtable_num_bits(uint size); |
| |
| /****************************************************************************/ |
| |
| /* Reachability helpers */ |
| /* Given a region, returns the start of the enclosing region that can reached by a |
| * 32bit displacement from everywhere in the supplied region. Checks for underflow. If the |
| * supplied region is too large then returned value may be > reachable_region_start |
| * (caller should check) as the constraint may not be satisfiable. */ |
| /* i#14: gcc 4.3.0 treats "ptr - const < ptr" as always true, |
| * so we work around that here. could adapt POINTER_OVERFLOW_ON_ADD |
| * or cast to ptr_uint_t like it does, instead. |
| */ |
| # define REACHABLE_32BIT_START(reachable_region_start, reachable_region_end) \ |
| (((reachable_region_end) > ((byte *)(ptr_uint_t)(uint)(INT_MIN))) ? \ |
| (reachable_region_end) + INT_MIN : (byte *)PTR_UINT_0) |
| /* Given a region, returns the end of the enclosing region that can reached by a |
| * 32bit displacement from everywhere in the supplied region. Checks for overflow. If the |
| * supplied region is too large then returned value may be < reachable_region_end |
| * (caller should check) as the constraint may not be satisfiable. */ |
| # define REACHABLE_32BIT_END(reachable_region_start, reachable_region_end) \ |
| (((reachable_region_start) < ((byte *)POINTER_MAX) - INT_MAX) ? \ |
| (reachable_region_start) + INT_MAX : (byte *)POINTER_MAX) |
| |
| #define MAX_LOW_2GB ((byte*)(ptr_uint_t)INT_MAX) |
| |
| /* alignment helpers, alignment must be power of 2 */ |
| #define ALIGNED(x, alignment) ((((ptr_uint_t)x) & ((alignment)-1)) == 0) |
| #define ALIGN_FORWARD(x, alignment) \ |
| ((((ptr_uint_t)x) + ((alignment)-1)) & (~((ptr_uint_t)(alignment)-1))) |
| #define ALIGN_FORWARD_UINT(x, alignment) \ |
| ((((uint)x) + ((alignment)-1)) & (~((alignment)-1))) |
| #define ALIGN_BACKWARD(x, alignment) (((ptr_uint_t)x) & (~((ptr_uint_t)(alignment)-1))) |
| #define PAD(length, alignment) (ALIGN_FORWARD((length), (alignment)) - (length)) |
| #define ALIGN_MOD(addr, size, alignment) \ |
| ((((ptr_uint_t)addr)+(size)-1) & ((alignment)-1)) |
| #define CROSSES_ALIGNMENT(addr, size, alignment) \ |
| (ALIGN_MOD(addr, size, alignment) < (size)-1) |
| /* number of bytes you need to shift addr forward so that it's !CROSSES_ALIGNMENT */ |
| #define ALIGN_SHIFT_SIZE(addr, size, alignment) \ |
| (CROSSES_ALIGNMENT(addr, size, alignment) ? \ |
| ((size) - 1 - ALIGN_MOD(addr, size, alignment)) : 0) |
| |
| #define IS_POWER_OF_2(x) ((x) == 0 || ((x) & ((x)-1)) == 0) |
| |
| /* C standard has pointer overflow as undefined so cast to unsigned |
| * (i#14 and drmem i#302) |
| */ |
| #define POINTER_OVERFLOW_ON_ADD(ptr, add) \ |
| (((ptr_uint_t)(ptr)) + (add) < ((ptr_uint_t)(ptr))) |
| #define POINTER_UNDERFLOW_ON_SUB(ptr, sub) \ |
| (((ptr_uint_t)(ptr)) - (sub) > ((ptr_uint_t)(ptr))) |
| |
| /* bitmap_t operations */ |
| |
| /* Current implementation uses integers representing 32 bits */ |
| typedef uint bitmap_element_t; |
| typedef bitmap_element_t bitmap_t[]; |
| |
| /* Note that we have some bitmap operations in unix/signal.c for |
| * kernel version of sigset_t as well as in |
| * win32/ntdll.c:tls_{alloc,free} which could use some of these |
| * facilities, but for now we leave those as more OS specific |
| */ |
| |
| #define BITMAP_DENSITY 32 |
| #define BITMAP_MASK(i) (1 << ((i) % BITMAP_DENSITY)) |
| #define BITMAP_INDEX(i) ((i) / BITMAP_DENSITY) |
| #define BITMAP_NOT_FOUND ((uint)-1) |
| |
| /* bitmap_t primitives */ |
| |
| /* TODO: could use BT for bit test, BTS/BTR for set/clear, and BSF for |
| * bit scan forward. See /usr/src/linux-2.4/include/asm/bitops.h for |
| * an all assembly implementation. Here we stick to plain C. |
| */ |
| |
| /* returns non-zero value if bit is set */ |
| static inline bool |
| bitmap_test(bitmap_t b, uint i) |
| { |
| return ((b[BITMAP_INDEX(i)] & BITMAP_MASK(i)) != 0); |
| } |
| |
| static inline void |
| bitmap_set(bitmap_t b, uint i) |
| { |
| b[BITMAP_INDEX(i)] |= BITMAP_MASK(i); |
| } |
| |
| static inline void |
| bitmap_clear(bitmap_t b, uint i) |
| { |
| b[BITMAP_INDEX(i)] &= ~BITMAP_MASK(i); |
| } |
| |
| /* bitmap_size is number of bits in the bitmap_t */ |
| void bitmap_initialize_free(bitmap_t b, uint bitmap_size); |
| uint bitmap_allocate_blocks(bitmap_t b, uint bitmap_size, uint request_blocks); |
| void bitmap_free_blocks(bitmap_t b, uint bitmap_size, uint first_block, uint num_free); |
| |
| #ifdef DEBUG |
| /* used only for ASSERTs */ |
| bool bitmap_are_reserved_blocks(bitmap_t b, uint bitmap_size, uint first_block, uint num_blocks); |
| bool bitmap_check_consistency(bitmap_t b, uint bitmap_size, uint expect_free); |
| #endif /* DEBUG */ |
| |
| /* logging functions */ |
| /* use the following three defines to control the logging directory format */ |
| #define LOGDIR_MAX_NUM 1000 |
| #define LOGDIR_FORMAT_STRING "%s.%03d" |
| #define LOGDIR_FORMAT_ARGS(num) "dynamorio", num |
| /* longest message we would put in a log or messagebox |
| * 512 is too short for internal exception w/ app + options + callstack |
| */ |
| /* We define MAX_LOG_LENGTH_MINUS_ONE for splitting long buffers. |
| * It must be a raw numeric constant as we STRINGIFY it. |
| */ |
| #if defined(PARAMS_IN_REGISTRY) || !defined(CLIENT_INTERFACE) |
| # define MAX_LOG_LENGTH IF_X64_ELSE(1280, 768) |
| # define MAX_LOG_LENGTH_MINUS_ONE IF_X64_ELSE(1279, 767) |
| #else |
| /* need more space for printing out longer option strings */ |
| /* CLIENT_INTERFACE build has larger stack and 2048 option length so go bigger |
| * so clients don't have dr_printf truncated as often |
| */ |
| # define MAX_LOG_LENGTH IF_CLIENT_INTERFACE_ELSE(2048,1384) |
| # define MAX_LOG_LENGTH_MINUS_ONE IF_CLIENT_INTERFACE_ELSE(2047,1383) |
| #endif |
| |
| #if defined(DEBUG) && !defined(STANDALONE_DECODER) |
| # define LOG(file, mask, level, ...) do { \ |
| if (stats != NULL && \ |
| stats->loglevel >= (level) && \ |
| (stats->logmask & (mask)) != 0) \ |
| print_log(file, mask, level, __VA_ARGS__); \ |
| } while (0) |
| /* use DOELOG for customer visible logging. statement can be a {} block */ |
| # define DOELOG(level, mask, statement) do { \ |
| if (stats != NULL && \ |
| stats->loglevel >= (level) && \ |
| (stats->logmask & (mask)) != 0) \ |
| statement \ |
| } while (0) |
| /* not using DYNAMO_OPTION b/c it contains ASSERT */ |
| # define DOCHECK(level, statement) do { \ |
| if (DEBUG_CHECKS(level)) \ |
| statement \ |
| } while (0) |
| # ifdef INTERNAL |
| # define DOLOG DOELOG |
| # define LOG_DECLARE(declaration) declaration |
| # else |
| /* XXX: this means LOG_DECLARE and LOG are different for non-INTERNAL */ |
| # define DOLOG(level, mask, statement) |
| # define LOG_DECLARE(declaration) |
| # endif /* INTERNAL */ |
| # define THREAD ((dcontext == NULL) ? INVALID_FILE : \ |
| ((dcontext == GLOBAL_DCONTEXT) ? main_logfile : dcontext->logfile)) |
| # define THREAD_GET get_thread_private_logfile() |
| # define GLOBAL main_logfile |
| #else /* !DEBUG */ |
| /* make use of gcc macro varargs, LOG's args may be ifdef DEBUG */ |
| /* the macro is actually ,fmt... but C99 requires one+ argument which we just strip */ |
| # define LOG(file, mask, level, ...) |
| # define DOLOG(level, mask, statement) |
| # define DOELOG DOLOG |
| # define LOG_DECLARE(declaration) |
| # define DOCHECK(level, statement) /* nothing */ |
| #endif |
| void print_log(file_t logfile, uint mask, uint level, const char *fmt, ...); |
| void print_file(file_t f, const char *fmt, ...); |
| |
| /* For repeated appending to a buffer. The "sofar" var should be set |
| * to 0 by the caller before the first call to print_to_buffer. |
| */ |
| bool print_to_buffer(char *buf, size_t bufsz, size_t *sofar INOUT, const char *fmt, ...); |
| |
| const char *memprot_string(uint prot); |
| |
| char * double_strchr(char *string, char c1, char c2); |
| #ifndef WINDOWS |
| const char * double_strrchr(const char *string, char c1, char c2); |
| #else |
| /* double_strrchr() defined in win32/inject_shared.h */ |
| /* wcsnlen is provided in ntdll only from win7 onward */ |
| size_t our_wcsnlen(const wchar_t *str, size_t max); |
| # define wcsnlen our_wcsnlen |
| #endif |
| bool str_case_prefix(const char *str, const char *pfx); |
| |
| bool is_region_memset_to_char(byte *addr, size_t size, byte val); |
| |
| /* calculates intersection of two regions defined as open ended intervals |
| * [region1_start, region1_start + region1_len) \intersect |
| * [region2_start, region2_start + region2_len) |
| * |
| * intersection_len is set to 0 if the regions do not overlap |
| * otherwise returns the intersecting region as |
| * [intersection_start, intersection_start + intersection_len) |
| */ |
| void |
| region_intersection(app_pc *intersection_start /* OUT */, |
| size_t *intersection_len /* OUT */, |
| const app_pc region1_start, size_t region1_len, |
| const app_pc region2_start, size_t region2_len); |
| |
| bool check_filter(const char *filter, const char *short_name); |
| bool check_filter_with_wildcards(const char *filter, const char *short_name); |
| |
| typedef enum { |
| BASE_DIR, /* Only creates directory specified in env */ |
| PROCESS_DIR /* Creates a process subdir off of base (e.g. dynamorio.000) */ |
| } log_dir_t; |
| void enable_new_log_dir(void); /* enable creating a new base logdir (for a fork, e.g.) */ |
| void create_log_dir(int dir_type); |
| bool get_log_dir(log_dir_t dir_type, char *buffer, uint *buffer_length); |
| |
| /* must use close_log_file() to close */ |
| file_t open_log_file(const char *basename, char *finalname_with_path, uint maxlen); |
| void close_log_file(file_t f); |
| |
| file_t get_thread_private_logfile(void); |
| bool get_unique_logfile(const char *file_type, char *filename_buffer, uint maxlen, bool open_directory, file_t *file); |
| const char *get_app_name_for_path(void); |
| const char *get_short_name(const char *exename); |
| |
| /* Self-protection: we can't use pragmas in local scopes so no convenient way to |
| * place the do_once var elsewhere than .data. Since it's only written once we |
| * go ahead and unprotect here. Even if we have dozens of these (there aren't |
| * that many in release builds currently) it shouldn't hurt us. |
| * FIXME: this means that if the protection routines call a routine that has |
| * a do-once, we have a deadlock! Could switch to a recursive lock. |
| */ |
| #define DO_ONCE(statement) { \ |
| /* no mutual exclusion, should be used only with logging */ \ |
| static int do_once = 0; \ |
| if (!do_once) { \ |
| SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT);\ |
| do_once = 1; \ |
| SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT); \ |
| statement; \ |
| } \ |
| } |
| |
| /* This is more heavy-weight and includes its own static mutex |
| * The counter is only incremented if it is less than the threshold |
| */ |
| /* Self-protection case 8075: can't use pragma at local scope. We put |
| * the burden on the caller to make the do_threshold_cur in .data |
| * writable. For do_threshold_mutex, even if it's writable at the |
| * macro site, {add,remove}_process_lock will crash on adjacent |
| * entries in the lock list (and an attempt there to unprot .data will |
| * deadlock as the datasec lock is acquired and hits the same |
| * unprot!). So we use a single global mutex in debug builds. There |
| * aren't currently any uses of this macro that will be hurt by this |
| * serialization so we could also do it in release builds. |
| */ |
| #ifdef DEADLOCK_AVOIDANCE |
| extern mutex_t do_threshold_mutex; |
| # define DECLARE_THRESHOLD_LOCK(section) /* nothing */ |
| #else |
| # define DECLARE_THRESHOLD_LOCK(section) \ |
| static mutex_t do_threshold_mutex VAR_IN_SECTION(section) \ |
| = INIT_LOCK_FREE(do_threshold_mutex); |
| #endif |
| /* The section argument is our support for the user wrapping entire |
| * function in a separate section, which for gcc also requires |
| * annotating each var declaration. |
| */ |
| #define DO_THRESHOLD_SAFE(threshold, section, statement_below, statement_after) {\ |
| DECLARE_THRESHOLD_LOCK(section) \ |
| static uint do_threshold_cur VAR_IN_SECTION(section) = 0; \ |
| mutex_lock(&do_threshold_mutex); \ |
| if (do_threshold_cur < threshold) { \ |
| do_threshold_cur++; \ |
| mutex_unlock(&do_threshold_mutex); \ |
| statement_below; \ |
| } else { \ |
| mutex_unlock(&do_threshold_mutex); \ |
| statement_after; /* or at */ \ |
| } \ |
| } |
| |
| /* TRY/EXCEPT and TRY/FINALLY |
| * usage notes: |
| * any automatic variables that you want to use in the except block |
| * should be declared as volatile - see case 5891 |
| * |
| * Note we do not have language support - do not use return within a |
| * TRY block! otherwise we can't rollback or execute FINALLY. |
| * |
| * Note we do not support filters in EXCEPT statements so the |
| * innermost handler will be called. Also finally blocks are not |
| * implemented (FIXME: we would have to unwind all nested finally blocks |
| * before the EXCEPT block). |
| * |
| * Note no locks should be grabbed within a TRY/EXCEPT block (FIXME: |
| * until we have FINALLY support to release them). |
| * |
| * (tip: compile your TRY blocks first outside of this macro for |
| * easier line matching and debugging) |
| */ |
| /* This form allows GLOBAL_DCONTEXT or NULL dcontext if !dynamo_initialized. |
| * In release build we'll run w/o crashing if dcontext is NULL and we're |
| * post-dynamo_initialized and so can't use global_try_except w/o a race, |
| * but we don't want to do this and we assert on it. It should only |
| * happen during late thread exit and currently there are no instances of it. |
| */ |
| #define TRY_EXCEPT_ALLOW_NO_DCONTEXT(dcontext, try_statement, except_statement) do { \ |
| try_except_t *try__except = NULL; \ |
| dcontext_t *dc__local = dcontext; \ |
| if ((dc__local == NULL || dc__local == GLOBAL_DCONTEXT) && !dynamo_initialized) { \ |
| try__except = &global_try_except; \ |
| } else { \ |
| if (dc__local == GLOBAL_DCONTEXT) \ |
| dc__local = get_thread_private_dcontext(); \ |
| if (dc__local != NULL) \ |
| try__except = &dc__local->try_except; \ |
| } \ |
| ASSERT(try__except != NULL); \ |
| TRY(try__except, try_statement, \ |
| EXCEPT(try__except, except_statement)); \ |
| } while (0) |
| |
| /* these use do..while w/ a local to avoid double-eval of dcontext */ |
| #define TRY_EXCEPT(dcontext, try_statement, except_statement) do { \ |
| try_except_t *try__except = &(dcontext)->try_except; \ |
| ASSERT((dcontext) != NULL && (dcontext) != GLOBAL_DCONTEXT); \ |
| TRY(try__except, try_statement, EXCEPT(try__except, except_statement)); \ |
| } while (0) |
| |
| #define TRY_FINALLY(dcontext, try_statement, finally_statement) do { \ |
| try_except_t *try__except = &(dcontext)->try_except; \ |
| ASSERT((dcontext) != NULL && (dcontext) != GLOBAL_DCONTEXT); \ |
| TRY(try__except, try_statement, FINALLY(try__except, except_statement)); \ |
| } while (0) |
| |
| /* internal versions */ |
| #define TRY(try_pointer, try_statement, except_or_finally) do { \ |
| try_except_context_t try__state; \ |
| /* must be current thread -> where we'll fault */ \ |
| /* We allow NULL solely to avoid duplicating try_statement in \ |
| * TRY_EXCEPT_ALLOW_NO_DCONTEXT. \ |
| */ \ |
| ASSERT((try_pointer) == &global_try_except || \ |
| (try_pointer) == NULL || \ |
| (try_pointer) == &get_thread_private_dcontext()->try_except); \ |
| if ((try_pointer) != NULL) { \ |
| try__state.prev_context = (try_pointer)->try_except_state; \ |
| (try_pointer)->try_except_state = &try__state; \ |
| } \ |
| if ((try_pointer) == NULL || DR_SETJMP(&try__state.context) == 0) { \ |
| try_statement /* TRY block */ \ |
| /* make sure there is no return in try_statement */ \ |
| if ((try_pointer) != NULL) { \ |
| POP_TRY_BLOCK(try_pointer, try__state); \ |
| } \ |
| } \ |
| except_or_finally \ |
| /* EXCEPT or FINALLY will POP_TRY_BLOCK on exception */ \ |
| } while (0) |
| |
| /* implementation notes: */ |
| /* FIXME: it is more secure yet not as flexible to use a scheme |
| * like the Exception Tables in the Linux kernel where a static |
| * mapping from a faulting PC to a fixup code (in |
| * exception_table_entry) can be kept in read-only memory. That |
| * scheme works really well for tight assembly, and the fast path |
| * is somewhat faster than the 10 instructions in dr_setjmp(). |
| * Note however, that a return address on our thread stack is just |
| * as vulnerable, so the security advantage is minor. |
| * The tighter scheme also makes it hard to cover up faults that are |
| * at unexpected instructions in a block. |
| */ |
| |
| |
| /* no filters |
| * FIXME: we may want filters in debug builds to make sure we can |
| * detect the proper EXCEPT condition (need to register in the TRY). |
| * Alternatively, should match against a list of instructions that are |
| * the only ones known to possibly fail. |
| * |
| * Note we also don't provide any access to the exception context, |
| * since we don't plan on recovering at the fault point (which a |
| * filter may recommend). |
| */ |
| |
| /* Only called within a TRY block that contains the proper try__state */ |
| #define EXCEPT(try_pointer, statement) else { /* EXCEPT */ \ |
| /* a failure in the EXCEPT should be thrown higher up */ \ |
| /* rollback first */ \ |
| POP_TRY_BLOCK(try_pointer, try__state); \ |
| statement; \ |
| /* FIXME: stop unwinding */ \ |
| } |
| |
| /* FIXME: should be called only nested within another TRY/EXCEPT |
| * block. (We don't support __leave so there is no other use). If it |
| * was called not nested in an EXCEPT handler, we can't just hide |
| * there was an exception at all, otherwise this will change behavior |
| * if it is ever nested. |
| */ |
| /* Only called within a TRY block */ |
| /* NYI */ |
| #define FINALLY(try_pointer, statement) /* ALWAYS */ { \ |
| ASSERT_NOT_IMPLEMENTED(false); \ |
| ASSERT((try_pointer) != NULL); \ |
| if ((try_pointer)->unwinding_exception) { \ |
| /* only on exception we have to POP here */ \ |
| /* normal execution would have already POPped */ \ |
| \ |
| /* pop before executing finally statement */ \ |
| /* so an exception in it is delivered to the */ \ |
| /* previous handler */ \ |
| /* only parent TRY block has proper try__state */ \ |
| POP_TRY_BLOCK(try_pointer, try__state); \ |
| } \ |
| ASSERT((try_pointer)->try_except_state != NULL \ |
| && "try/finally should be nested in try/except"); \ |
| /* executed for both normal execution, or exception */ \ |
| statement; \ |
| if ((try_pointer)->unwinding_exception) { \ |
| /* FIXME: on nested exception must keep UNWINDing */ \ |
| /* and give control to the previous nested handler */ \ |
| /* until an EXCEPT handler resumes to normal execution */ \ |
| /* we don't keep any exception context */ \ |
| ASSERT_NOT_IMPLEMENTED(false); \ |
| } \ |
| } |
| |
| /* internal helper */ |
| #define POP_TRY_BLOCK(try_pointer, state) \ |
| ASSERT((try_pointer) != NULL); \ |
| ASSERT((try_pointer)->try_except_state == &(state)); \ |
| (try_pointer)->try_except_state = \ |
| (try_pointer)->try_except_state->prev_context; |
| |
| enum {LONGJMP_EXCEPTION = 1}; |
| /* the return value of setjmp() returned on exception (or unwinding) */ |
| |
| /* volatile to ensure the compiler doesn't completely skip a READ */ |
| #define PROBE_READ_PC(pc) ((*(volatile char *)(pc))) |
| #define PROBE_WRITE_PC(pc) ATOMIC_ADD_PTR(volatile char*, (*(volatile char *)(pc)), 0) |
| /* FIXME: while handling a read exception thread stack expansion in |
| * other threads may lose its guard page. Since current thread won't |
| * know if it is ok to expand, therefore the stacks won't grow any |
| * further. See MSDN: IsBadReadPtr(), We may want to mark back any |
| * PAGE_GUARD faults before we handle them in our EXCEPT block. For |
| * most purposes it is unlikely to be an issue that is not already an |
| * app bug causing us to touch these. Our LOCKed ADD is somewhat |
| * better than IsBadWritePtr() but is best not to have to use it. |
| */ |
| |
| #include "stats.h" |
| |
| /* Use to shut up the compiler about an unused variable when the |
| * alternative is a painful modification of more src code. Our |
| * standard is to use this macro just after the variable is declared |
| * and to use it judiciously. |
| */ |
| #define UNUSED_VARIABLE(pv) {void *unused_pv = (void*) pv; unused_pv = NULL; } |
| |
| /* Both release and debug builds share these common stats macros */ |
| /* If -no_global_rstats, all values will be 0, so user does not have to |
| * use DO_GLOBAL_STATS or check runtime option. |
| */ |
| #define GLOBAL_STAT(stat) stats->stat##_pair.value |
| /* explicit macro for addr so no assumptions on GLOBAL_STAT being lvalue */ |
| #define GLOBAL_STAT_ADDR(stat) &(stats->stat##_pair.value) |
| #define DO_GLOBAL_STATS(statement) do { \ |
| if (GLOBAL_STATS_ON()) { \ |
| statement; \ |
| } \ |
| } while (0) |
| |
| #ifdef X64 |
| # define XSTATS_ATOMIC_INC(var) ATOMIC_INC(int64, var) |
| # define XSTATS_ATOMIC_DEC(var) ATOMIC_DEC(int64, var) |
| # define XSTATS_ATOMIC_ADD(var, val) ATOMIC_ADD(int64, var, val) |
| # define XSTATS_ATOMIC_MAX(max, cur) ATOMIC_MAX(int64, max, cur) |
| # define XSTATS_ATOMIC_ADD_EXCHANGE(var, val) atomic_add_exchange_int64(var, val) |
| #else |
| # define XSTATS_ATOMIC_INC(var) ATOMIC_INC(int, var) |
| # define XSTATS_ATOMIC_DEC(var) ATOMIC_DEC(int, var) |
| # define XSTATS_ATOMIC_ADD(var, val) ATOMIC_ADD(int, var, val) |
| # define XSTATS_ATOMIC_MAX(max, cur) ATOMIC_MAX(int, max, cur) |
| # define XSTATS_ATOMIC_ADD_EXCHANGE(var, val) atomic_add_exchange_int(var, val) |
| #endif |
| |
| /* XSTAT_* macros are pointed at by either STATS_* or RSTATS_* |
| * XSTAT_* should not be called directly outside this file |
| */ |
| #define XSTATS_INC_DC(dcontext, stat) do { \ |
| DO_THREAD_STATS(dcontext, THREAD_STAT(dcontext, stat) += 1); \ |
| DO_GLOBAL_STATS(XSTATS_ATOMIC_INC(GLOBAL_STAT(stat))); \ |
| } while (0) |
| #define XSTATS_INC(stat) \ |
| XSTATS_WITH_DC(stats_inc__dcontext, \ |
| XSTATS_INC_DC(stats_inc__dcontext, stat)) |
| |
| #define XSTATS_DEC_DC(dcontext, stat) do { \ |
| DO_THREAD_STATS(dcontext, THREAD_STAT(dcontext, stat) -= 1); \ |
| DO_GLOBAL_STATS(XSTATS_ATOMIC_DEC(GLOBAL_STAT(stat))); \ |
| } while (0) |
| #define XSTATS_DEC(stat) \ |
| XSTATS_WITH_DC(stats_dec__dcontext, \ |
| XSTATS_DEC_DC(stats_dec__dcontext, stat)) |
| |
| #define XSTATS_ADD_DC(dcontext, stat, value) do { \ |
| stats_int_t stats_add_dc__value = (stats_int_t) (value); \ |
| CURIOSITY_TRUNCATE(stats_add_dc__value, stats_int_t, value); \ |
| DO_THREAD_STATS(dcontext, THREAD_STAT(dcontext, stat) += stats_add_dc__value); \ |
| DO_GLOBAL_STATS(XSTATS_ATOMIC_ADD(GLOBAL_STAT(stat), stats_add_dc__value)); \ |
| } while (0) |
| #define XSTATS_ADD(stat, value) \ |
| XSTATS_WITH_DC(stats_add__dcontext, \ |
| XSTATS_ADD_DC(stats_add__dcontext, stat, value)) |
| #define XSTATS_SUB(stat, value) XSTATS_ADD(stat, -(stats_int_t)(value)) |
| #define XSTATS_ADD_ASSIGN_DC(dcontext, stat, var, value) do { \ |
| stats_int_t stats_add_assign_dc__value = (stats_int_t) (value); \ |
| CURIOSITY_TRUNCATE(stats_add_assign_dc__value, stats_int_t, value); \ |
| DO_THREAD_STATS(dcontext, \ |
| THREAD_STAT(dcontext, stat) += stats_add_assign_dc__value); \ |
| /* would normally DO_GLOBAL_STATS(), but need to assign var */ \ |
| var = XSTATS_ATOMIC_ADD_EXCHANGE(&GLOBAL_STAT(stat), \ |
| stats_add_assign_dc__value); \ |
| } while (0) |
| #define XSTATS_INC_ASSIGN_DC(dcontext, stat, var) \ |
| XSTATS_ADD_ASSIGN_DC(dcontext, stats, var, 1) |
| #define XSTATS_ADD_ASSIGN(stat, var, value) \ |
| XSTATS_WITH_DC(stats_add_assign__dcontext, \ |
| XSTATS_ADD_ASSIGN_DC(stats_add_assign__dcontext, stat, var, value)) |
| #define XSTATS_INC_ASSIGN(stat, var) XSTATS_ADD_ASSIGN(stat, var, 1) |
| |
| |
| #define XSTATS_MAX_HELPER(dcontext, stat, global_val, thread_val) do { \ |
| DO_THREAD_STATS(dcontext, { \ |
| stats_int_t stats_max_helper__value; \ |
| stats_max_helper__value = (thread_val); \ |
| if (THREAD_STAT(dcontext, stat) < stats_max_helper__value) \ |
| THREAD_STAT(dcontext, stat) = stats_max_helper__value; \ |
| }); \ |
| DO_GLOBAL_STATS({ \ |
| XSTATS_ATOMIC_MAX(GLOBAL_STAT(stat), global_val); \ |
| }); \ |
| } while (0) |
| #define XSTATS_MAX_DC(dcontext, stat_max, stat_cur) \ |
| XSTATS_MAX_HELPER(dcontext, stat_max, GLOBAL_STAT(stat_cur), \ |
| THREAD_STAT(dcontext, stat_cur)) |
| #define XSTATS_PEAK_DC(dcontext, stat) \ |
| XSTATS_MAX_DC(dcontext, peak_##stat, stat) |
| #define XSTATS_MAX(stat_max, stat_cur) \ |
| XSTATS_WITH_DC(stats_max__dcontext, \ |
| XSTATS_MAX_DC(stats_max__dcontext, stat_max, stat_cur)) |
| #define XSTATS_TRACK_MAX(stats_track_max, val) do { \ |
| stats_int_t stats_track_max__value = (stats_int_t) (val); \ |
| CURIOSITY_TRUNCATE(stats_track_max__value, stats_int_t, val); \ |
| XSTATS_WITH_DC(stats_track_max__dcontext, \ |
| XSTATS_MAX_HELPER(stats_track_max__dcontext, \ |
| stats_track_max, \ |
| stats_track_max__value, \ |
| stats_track_max__value)); \ |
| } while (0) |
| #define XSTATS_PEAK(stat) \ |
| XSTATS_WITH_DC(stats_peak__dcontext, \ |
| XSTATS_PEAK_DC(stats_peak__dcontext, stat)) |
| |
| |
| #define XSTATS_ADD_MAX_DC(dcontext, stat_max, stat_cur, value) do { \ |
| stats_int_t stats_add_max__temp; \ |
| XSTATS_ADD_ASSIGN_DC(dcontext, stat_cur, stats_add_max__temp, value); \ |
| XSTATS_MAX_HELPER(dcontext, stat_max, stats_add_max__temp, \ |
| THREAD_STAT(dcontext, stat_cur)); \ |
| } while (0) |
| #define XSTATS_ADD_MAX(stat_max, stat_cur, value) \ |
| XSTATS_WITH_DC(stats_add_max__dcontext, \ |
| XSTATS_ADD_MAX_DC(stats_add_max__dcontext, stat_max, \ |
| stat_cur, value)) |
| #define XSTATS_ADD_PEAK_DC(dcontext, stat, value) \ |
| XSTATS_ADD_MAX_DC(dcontext, peak_##stat, stat, value) |
| #define XSTATS_ADD_PEAK(stat, value) \ |
| XSTATS_WITH_DC(stats_add_peak__dcontext, \ |
| XSTATS_ADD_PEAK_DC(stats_add_peak__dcontext, stat, value)) |
| |
| |
| #define XSTATS_RESET_DC(dcontext, stat) do { \ |
| DO_THREAD_STATS(dcontext, THREAD_STAT(dcontext, stat) = 0); \ |
| DO_GLOBAL_STATS(GLOBAL_STAT(stat) = 0); \ |
| } while (0) |
| #define XSTATS_RESET(stat) \ |
| XSTATS_WITH_DC(stats_reset__dcontext, \ |
| XSTATS_RESET_DC(stats_reset__dcontext, stat)) |
| |
| /* common to both release and debug build */ |
| #define RSTATS_INC XSTATS_INC |
| #define RSTATS_DEC XSTATS_DEC |
| #define RSTATS_ADD XSTATS_ADD |
| #define RSTATS_SUB XSTATS_SUB |
| #define RSTATS_ADD_PEAK XSTATS_ADD_PEAK |
| |
| #if defined(DEBUG) && defined(INTERNAL) |
| # define DODEBUGINT DODEBUG |
| # define DOCHECKINT DOCHECK |
| #else |
| # define DODEBUGINT(statement) /* nothing */ |
| # define DOCHECKINT(level, statement) /* nothing */ |
| #endif |
| |
| /* for use in CLIENT_ASSERT or elsewhere that exists even if |
| * STANDALONE_DECODER is defined, unless CLIENT_INTERFACE is off |
| */ |
| #if defined(DEBUG) && (defined(CLIENT_INTERFACE) || !defined(STANDALONE_DECODER)) |
| # define DEBUG_EXT_DECLARE(declaration) declaration |
| #else |
| # define DEBUG_EXT_DECLARE(declaration) |
| #endif |
| |
| #if defined(DEBUG) && !defined(STANDALONE_DECODER) |
| # define DODEBUG(statement) do { statement } while (0) |
| # define DEBUG_DECLARE(declaration) declaration |
| # define DOSTATS(statement) do { statement } while (0) |
| /* FIXME: move to stats.h */ |
| /* Note : stats macros are called in places where it is not safe to hold any lock |
| * (such as special_heap_create_unit, others?), if ever go back to using a mutex |
| * to protect the stats need to update such places |
| */ |
| /* global and thread local stats, can be used as lvalues, |
| * not used if not DEBUG */ |
| /* We assume below that all stats are aligned and thus reading and writing |
| * stats are atomic operations on Intel x86 */ |
| /* In general should prob. be using stats_add_[peak, max] instead of |
| * stats_[peak/max] since they tie the adjustment of the stat to the |
| * setting of the max, otherwise you're open to race conditions involving |
| * multiple threads adjusting the same stats and setting peak/max FIXME |
| */ |
| # define GLOBAL_STATS_ON() (stats != NULL && INTERNAL_OPTION(global_stats)) |
| # define THREAD_STAT(dcontext, stat) \ |
| (dcontext->thread_stats)->stat##_thread |
| # define THREAD_STATS_ON(dcontext) \ |
| (dcontext != NULL && INTERNAL_OPTION(thread_stats) && \ |
| dcontext != GLOBAL_DCONTEXT && dcontext->thread_stats != NULL) |
| # define DO_THREAD_STATS(dcontext, statement) do { \ |
| if (THREAD_STATS_ON(dcontext)) { \ |
| statement; \ |
| } \ |
| } while (0) |
| # define XSTATS_WITH_DC(var, statement) do { \ |
| dcontext_t *var = NULL; \ |
| if (INTERNAL_OPTION(thread_stats)) \ |
| var = get_thread_private_dcontext(); \ |
| statement; \ |
| } while (0) |
| |
| # define STATS_INC XSTATS_INC |
| /* we'll expose more *_DC as we need them */ |
| # define STATS_INC_DC XSTATS_INC_DC |
| # define STATS_DEC XSTATS_DEC |
| # define STATS_ADD XSTATS_ADD |
| # define STATS_SUB XSTATS_SUB |
| # define STATS_INC_ASSIGN XSTATS_INC_ASSIGN |
| # define STATS_ADD_ASSIGN XSTATS_ADD_ASSIGN |
| # define STATS_MAX XSTATS_MAX |
| # define STATS_TRACK_MAX XSTATS_TRACK_MAX |
| # define STATS_PEAK XSTATS_PEAK |
| # define STATS_ADD_MAX XSTATS_ADD_MAX |
| # define STATS_ADD_PEAK XSTATS_ADD_PEAK |
| # define STATS_RESET XSTATS_RESET |
| |
| #else |
| # define DODEBUG(statement) |
| # define DEBUG_DECLARE(declaration) |
| # define DOSTATS(statement) |
| # define THREAD_STATS_ON(dcontext) false |
| # define XSTATS_WITH_DC(var, statement) statement |
| # define DO_THREAD_STATS(dcontext, statement) /* nothing */ |
| # define GLOBAL_STATS_ON() (stats != NULL && DYNAMO_OPTION(global_rstats)) |
| |
| /* Would be nice to catch incorrect usage of STATS_INC on a release-build |
| * stat: if rename release vars, have to use separate GLOBAL_RSTAT though. |
| */ |
| # define STATS_INC(stat) /* nothing */ |
| # define STATS_INC_DC(dcontext, stat) /* nothing */ |
| # define STATS_DEC(stat) /* nothing */ |
| # define STATS_ADD(stat, value) /* nothing */ |
| # define STATS_SUB(stat, value) /* nothing */ |
| # define STATS_INC_ASSIGN(stat, var) /* nothing */ |
| # define STATS_ADD_ASSIGN(stat, var, value) /* nothing */ |
| # define STATS_MAX(stat_max, stat_cur) /* nothing */ |
| # define STATS_TRACK_MAX(stats_track_max, val) /* nothing */ |
| # define STATS_PEAK(stat) /* nothing */ |
| # define STATS_ADD_MAX(stat_max, stat_cur, value) /* nothing */ |
| # define STATS_ADD_PEAK(stat, value) /* nothing */ |
| # define STATS_RESET(stat) /* nothing */ |
| #endif /* DEBUG */ |
| |
| #ifdef KSTATS |
| # define DOKSTATS(statement) do { statement } while (0) |
| |
| /* The proper use is most commonly KSTART(name)/KSTOP(name), or |
| * occasionally KSTART(name)/KSWITCH(better_name)/KSTOP(name), and in |
| * ignorable cases KSTART(name)/KSTOP_NOT_PROPAGATED(name) |
| */ |
| /* starts a timer */ |
| # define KSTART(name) KSTAT_THREAD(name, kstat_start_var(ks, pv)) |
| |
| /* makes sure we're matching start/stop */ |
| # define KSTOP(name) KSTAT_THREAD(name, kstat_stop_matching_var(ks, pv)) |
| |
| /* modifies the variable against which this path should be counted */ |
| # define KSWITCH(name) KSTAT_THREAD(name, kstat_switch_var(ks, pv)) |
| |
| /* allow mismatched start/stop - for use with KSWITCH */ |
| # define KSTOP_NOT_MATCHING(name) \ |
| KSTAT_THREAD_NO_PV_START(get_thread_private_dcontext()) \ |
| ASSERT(ks->depth > 2 && "stop_not_matching not allowed to clear kstack"); \ |
| kstat_stop_not_matching_var(ks, ignored); \ |
| KSTAT_THREAD_NO_PV_END() |
| |
| /* rewind the callstack exiting multiple entries - for exception cases */ |
| # define KSTOP_REWIND(name) KSTAT_THREAD(name, kstat_stop_rewind_var(ks, pv)) |
| # define KSTOP_REWIND_UNTIL(name) KSTAT_THREAD(name, kstat_stop_longjmp_var(ks, pv)) |
| |
| /* simultanously switch to a path and stop timer */ |
| # define KSWITCH_STOP(name) KSTAT_THREAD(name, { \ |
| kstat_switch_var(ks, pv); \ |
| kstat_stop_not_matching_var(ks, ignored); \ |
| }) |
| |
| /* simultanously switch to a path and stop timer w/o propagating to parent */ |
| # define KSWITCH_STOP_NOT_PROPAGATED(name) KSTAT_THREAD(name, { \ |
| timestamp_t ignore_cum; \ |
| kstat_switch_var(ks, pv); \ |
| kstat_stop_not_propagated_var(ks, ignored, &ignore_cum); \ |
| }) |
| |
| /* do not propagate subpath time to parent */ |
| # define KSTOP_NOT_MATCHING_NOT_PROPAGATED(name) KSTAT_THREAD(name, { \ |
| timestamp_t ignore_cum; \ |
| ASSERT(ks->depth > 2 && "stop_not_matching_np not allowed to clear kstack"); \ |
| kstat_stop_not_propagated_var(ks, pv, &ignore_cum); \ |
| }) |
| |
| /* do not propagate subpath time to parent */ |
| # define KSTOP_NOT_PROPAGATED(name) KSTAT_THREAD(name, { \ |
| timestamp_t ignore_cum; \ |
| DODEBUG({if (ks->node[ks->depth - 1].var != pv) \ |
| kstats_dump_stack(cur_dcontext);}); \ |
| ASSERT(ks->node[ks->depth - 1].var == pv \ |
| && "stop not matching TOS"); \ |
| kstat_stop_not_propagated_var(ks, pv, &ignore_cum); \ |
| }) |
| |
| |
| /* in some cases we do need to pass a dcontext for another thread */ |
| /* since get_thread_private_dcontext() may be expensive we can pass a dcontext |
| * to this version of the macro, however we should then use this everywhere |
| * to have comparable overheads |
| */ |
| # define KSTART_DC(dc, name) \ |
| KSTAT_OTHER_THREAD(dc, name, kstat_start_var(ks, pv)) |
| # define KSTOP_DC(dc, name) \ |
| KSTAT_OTHER_THREAD(dc, name, kstat_stop_matching_var(ks, pv)) |
| # define KSTOP_NOT_MATCHING_DC(dc, name) \ |
| KSTAT_THREAD_NO_PV_START(dc) \ |
| kstat_stop_not_matching_var(ks, ignored); \ |
| KSTAT_THREAD_NO_PV_END() |
| # define KSTOP_REWIND_DC(dc, name) KSTAT_OTHER_THREAD(dc, name, kstat_stop_rewind_var(ks, pv)) |
| #else /* !KSTATS */ |
| # define DOKSTATS(statement) /* nothing */ |
| # define KSTART(name) /* nothing */ |
| # define KSWITCH(name) /* nothing */ |
| # define KSWITCH_STOP(name) /* nothing */ |
| # define KSWITCH_STOP_NOT_PROPAGATED(name) /* nothing */ |
| # define KSTOP_NOT_MATCHING_NOT_PROPAGATED(name) /* nothing */ |
| # define KSTOP_NOT_PROPAGATED(name) /* nothing */ |
| # define KSTOP_NOT_MATCHING(name) /* nothing */ |
| # define KSTOP(name) /* nothing */ |
| # define KSTOP_REWIND(name) /* nothing */ |
| # define KSTOP_REWIND_UNTIL(name) /* nothing */ |
| |
| # define KSTART_DC(dc, name) /* nothing */ |
| # define KSTOP_DC(dc, name) /* nothing */ |
| # define KSTOP_NOT_MATCHING_DC(dc, name) /* nothing */ |
| # define KSTOP_REWIND_DC(dc, name) /* nothing */ |
| #endif /* KSTATS */ |
| |
| #ifdef INTERNAL |
| # define DODEBUG_ONCE(statement) DODEBUG(DO_ONCE(statement)) |
| # define DOLOG_ONCE(level, mask, statement) DOLOG(level, mask, DO_ONCE(statement)) |
| #else |
| # define DODEBUG_ONCE(statement) /* nothing */ |
| # define DOLOG_ONCE(level, mask, statement) /* nothing */ |
| #endif /* INTERNAL */ |
| |
| #define MAX_FP_STATE_SIZE (512+16) /* maximum buffer size plus alignment */ |
| /* for convenice when want to save floating point state around a statement |
| * that contains ifdefs, need to be used at same nesting depth */ |
| /* fpstate_junk is used so that this macro can be used before, or in the |
| * middle of a list of declerations without bothering the compiler or creating |
| * a new nesting block */ |
| |
| /* keep in mind that each use of PRESERVE_FLOATING_POINT_STATE takes |
| * 512 bytes - avoid nesting uses |
| */ |
| /* We call dr_fpu_exception_init() to avoid the app clearing float and xmm |
| * exception flags and messing up our code (i#1213). |
| */ |
| |
| #define PRESERVE_FLOATING_POINT_STATE_START() \ |
| { \ |
| byte fpstate_buf[MAX_FP_STATE_SIZE]; \ |
| byte *fpstate = (byte*)ALIGN_FORWARD(fpstate_buf, 16); \ |
| size_t fpstate_junk = proc_save_fpstate(fpstate); \ |
| dr_fpu_exception_init() |
| |
| #define PRESERVE_FLOATING_POINT_STATE_END() \ |
| proc_restore_fpstate(fpstate); \ |
| fpstate_junk = 0xdead; \ |
| } |
| |
| #define PRESERVE_FLOATING_POINT_STATE(statement) do { \ |
| PRESERVE_FLOATING_POINT_STATE_START(); \ |
| statement; \ |
| PRESERVE_FLOATING_POINT_STATE_END(); \ |
| } while (0) |
| |
| /* argument counting, http://gcc.gnu.org/ml/gcc/2000-09/msg00604.html */ |
| #define ARGUMENT_COUNTER(...) _ARGUMENT_COUNT1( , ## __VA_ARGS__) |
| #define _ARGUMENT_COUNT1(...) _ARGUMENT_COUNT2(__VA_ARGS__, 5,4,3,2,1,0) |
| #define _ARGUMENT_COUNT2(_,x0,x1,x2,x3,x4, argc,...) argc |
| |
| /* write to the system event log facilities - using syslogd or EventLog */ |
| /* For true portability we have to get the message strings as well as |
| the argument count out of the .mc files. It will be nice to get them |
| compile time type checked by way of having the format strings as |
| static arguments for LOGging. (As for the explicit number of substitions, |
| we can easily count args with macros, but for now we'll at least have to |
| lookup the number of arguments in events.mc.) |
| */ |
| #include "event_strings.h" |
| #ifdef WINDOWS |
| # include "events.h" |
| #endif |
| |
| extern const char *exception_label_core; |
| #ifdef CLIENT_INTERFACE |
| extern const char *exception_label_client; |
| #endif |
| #define CRASH_NAME "internal crash" |
| |
| /* pass NULL to use defaults */ |
| void |
| set_exception_strings(const char *override_label, const char *override_url); |
| |
| void |
| report_dynamorio_problem(dcontext_t *dcontext, uint dumpcore_flag, |
| app_pc exception_addr, app_pc report_ebp, |
| const char *fmt, ...); |
| |
| void |
| report_app_problem(dcontext_t *dcontext, uint appfault_flag, |
| app_pc pc, app_pc report_ebp, const char *fmt, ...); |
| |
| void |
| notify(syslog_event_type_t priority, bool internal, bool synch, |
| IF_WINDOWS_(uint message_id) uint substitution_nam, const char *prefix, |
| const char *fmt, ...); |
| |
| #define SYSLOG_COMMON(synch, type, id, sub, ...) \ |
| notify(type, false, synch, IF_WINDOWS_(MSG_##id) sub, #type, MSG_##id##_STRING, __VA_ARGS__) |
| |
| #define SYSLOG_INTERNAL_COMMON(synch, type, ...) \ |
| notify(type, true, synch, IF_WINDOWS_(MSG_INTERNAL_##type) 0, #type, __VA_ARGS__) |
| |
| /* For security messages use passed in fmt string instead of eventlog fmt |
| * string for LOG/stderr/msgbox to avoid breaking our regression suite, |
| * NOTE assumes actual id passed, not name of id less MSG_ (so can use array |
| * of id's in vmareas.c, another reason need separate fmt string) |
| * FIXME : use events.mc string instead (SYSLOG_COMMON), breaks regression |
| * This is now used for out-of-memory as well, for the same reason -- |
| * we should have a mechanism to strip the application name & pid prefix, |
| * then we could use the eventlog string. |
| */ |
| #define SYSLOG_CUSTOM_NOTIFY(type, id, sub, ...) \ |
| notify(type, false, true, IF_WINDOWS_(id) sub, #type, __VA_ARGS__) |
| |
| #define SYSLOG(type, id, sub, ...) \ |
| SYSLOG_COMMON(true, type, id, sub, __VA_ARGS__) |
| #define SYSLOG_NO_OPTION_SYNCH(type, id, sub, ...) \ |
| SYSLOG_COMMON(false, type, id, sub, __VA_ARGS__) |
| |
| #if defined(INTERNAL) && !defined(STANDALONE_DECODER) |
| # define SYSLOG_INTERNAL(type, ...) \ |
| SYSLOG_INTERNAL_COMMON(true, type, __VA_ARGS__) |
| # define SYSLOG_INTERNAL_NO_OPTION_SYNCH(type, ...) \ |
| SYSLOG_INTERNAL_COMMON(false, type, __VA_ARGS__) |
| #else |
| # define SYSLOG_INTERNAL(...) |
| # define SYSLOG_INTERNAL_NO_OPTION_SYNCH(...) |
| #endif /* INTERNAL */ |
| |
| /* for convenience */ |
| #define SYSLOG_INTERNAL_INFO(...) \ |
| SYSLOG_INTERNAL(SYSLOG_INFORMATION, __VA_ARGS__) |
| #define SYSLOG_INTERNAL_WARNING(...) \ |
| SYSLOG_INTERNAL(SYSLOG_WARNING, __VA_ARGS__) |
| #define SYSLOG_INTERNAL_ERROR(...) \ |
| SYSLOG_INTERNAL(SYSLOG_ERROR, __VA_ARGS__) |
| #define SYSLOG_INTERNAL_CRITICAL(...) \ |
| SYSLOG_INTERNAL(SYSLOG_CRITICAL, __VA_ARGS__) |
| |
| #define SYSLOG_INTERNAL_INFO_ONCE(...) \ |
| DODEBUG_ONCE(SYSLOG_INTERNAL_INFO(__VA_ARGS__)) |
| #define SYSLOG_INTERNAL_WARNING_ONCE(...) \ |
| DODEBUG_ONCE(SYSLOG_INTERNAL_WARNING(__VA_ARGS__)) |
| #define SYSLOG_INTERNAL_ERROR_ONCE(...) \ |
| DODEBUG_ONCE(SYSLOG_INTERNAL_ERROR(__VA_ARGS__)) |
| #define SYSLOG_INTERNAL_CRITICAL_ONCE(...) \ |
| DODEBUG_ONCE(SYSLOG_INTERNAL_CRITICAL(__VA_ARGS__)) |
| |
| /* FIXME, eventually want usage_error to also be external (may also eventually |
| * need non dynamic option synch form as well for usage errors while updating |
| * dynamic options), but lot of work to get all in eventlog and currently only |
| * really triggered by internal options */ |
| /* FIXME : could leave out the asserts, this is a recoverable error */ |
| #define USAGE_ERROR(...) \ |
| do { \ |
| SYSLOG_INTERNAL_ERROR(__VA_ARGS__); \ |
| ASSERT_NOT_REACHED(); \ |
| } while (0) |
| #define FATAL_USAGE_ERROR(id, sub, ...) \ |
| do { \ |
| /* synchronize dynamic options for dumpcore_mask */ \ |
| synchronize_dynamic_options(); \ |
| if (TEST(DUMPCORE_FATAL_USAGE_ERROR, DYNAMO_OPTION_NOT_STRING(dumpcore_mask))) \ |
| os_dump_core("fatal usage error"); \ |
| SYSLOG(SYSLOG_CRITICAL, id, sub, __VA_ARGS__); \ |
| os_terminate(NULL, TERMINATE_PROCESS); \ |
| } while (0) |
| #define OPTION_PARSE_ERROR(id, sub, ...) \ |
| do { \ |
| SYSLOG_NO_OPTION_SYNCH(SYSLOG_ERROR, id, sub, __VA_ARGS__); \ |
| DODEBUG(os_terminate(NULL, TERMINATE_PROCESS);); \ |
| } while (0) |
| |
| #ifdef DEBUG |
| /* only for temporary tracing - do not leave in source, use make ugly to remind you */ |
| # define TRACELOG(level) LOG(GLOBAL, LOG_TOP, level, "%s:%d ", __FILE__, __LINE__) |
| #else |
| # define TRACELOG(level) |
| #endif |
| |
| /* what-to-log bitmask values */ |
| /* N.B.: if these constants are changed, win32gui must also be changed! |
| * They are also duplicated in instrument.h -- too hard to get them to |
| * automatically show up in right place in header files for release. |
| */ |
| #define LOG_NONE 0x00000000 |
| #define LOG_STATS 0x00000001 |
| #define LOG_TOP 0x00000002 |
| #define LOG_THREADS 0x00000004 |
| #define LOG_SYSCALLS 0x00000008 |
| #define LOG_ASYNCH 0x00000010 |
| #define LOG_INTERP 0x00000020 |
| #define LOG_EMIT 0x00000040 |
| #define LOG_LINKS 0x00000080 |
| #define LOG_CACHE 0x00000100 |
| #define LOG_FRAGMENT 0x00000200 |
| #define LOG_DISPATCH 0x00000400 |
| #define LOG_MONITOR 0x00000800 |
| #define LOG_HEAP 0x00001000 |
| #define LOG_VMAREAS 0x00002000 |
| #define LOG_SYNCH 0x00004000 |
| #define LOG_MEMSTATS 0x00008000 |
| #define LOG_OPTS 0x00010000 |
| #define LOG_SIDELINE 0x00020000 |
| #define LOG_SYMBOLS 0x00040000 |
| #define LOG_RCT 0x00080000 |
| #define LOG_NT 0x00100000 |
| #define LOG_HOT_PATCHING 0x00200000 |
| #define LOG_HTABLE 0x00400000 |
| #define LOG_MODULEDB 0x00800000 |
| #define LOG_LOADER 0x01000000 |
| #define LOG_CLEANCALL 0x02000000 |
| #define LOG_ANNOTATIONS 0x04000000 |
| #define LOG_VIA_ANNOTATIONS 0x08000000 |
| |
| #define LOG_ALL_RELEASE 0x0fe0ffff |
| #define LOG_ALL 0x0fffffff |
| |
| #ifdef WINDOWS_PC_SAMPLE |
| # define LOG_PROFILE LOG_ALL |
| #endif |
| |
| /* buffer size supposed to handle undecorated names like |
| * "kernel32!CreateFile" or "kernel32.dll!CreateProcess" or ranges as |
| * needed by print_symbolic_address() */ |
| #define MAXIMUM_SYMBOL_LENGTH 80 |
| |
| #ifdef DEBUG |
| # define PRINT_TIMESTAMP_MAX_LENGTH 32 |
| |
| /* given an array of size size of integers, computes and prints the |
| * min, max, mean, and stddev |
| */ |
| void print_statistics(int *data, int size); |
| /* global and thread local STATS */ |
| void dump_global_stats(bool raw); |
| void dump_thread_stats(dcontext_t *dcontext, bool raw); |
| void stats_thread_init(dcontext_t *dcontext); |
| void stats_thread_exit(dcontext_t *dcontext); |
| uint print_timestamp_to_buffer(char *buffer, size_t len); |
| uint print_timestamp(file_t logfile); |
| |
| /* prints a symbolic name, or best guess of it into a caller provided buffer */ |
| void |
| print_symbolic_address(app_pc tag, char *buf, int max_chars, bool exact_only); |
| |
| #endif /* DEBUG */ |
| |
| bool |
| under_internal_exception(void); |
| |
| enum { |
| DUMP_NO_QUOTING = 0x01000, // no quoting for C string replay |
| DUMP_OCTAL = 0x02000, // hex otherwise |
| DUMP_NO_CHARS = 0x04000, // no printable characters |
| DUMP_RAW = 0x08000, // do not keep as a string |
| DUMP_DWORD = 0x10000, // dump as 4-byte chunks |
| DUMP_ADDRESS = 0x20000, // prepend address before each line of output |
| DUMP_APPEND_ASCII = 0x40000, // append printable ASCII after each line |
| DUMP_PER_LINE = 0x000ff // mask for bytes per line flag |
| }; |
| #define DUMP_PER_LINE_DEFAULT 16 |
| |
| /* dump buffer in a desired hex/octal/char combination */ |
| void dump_buffer_as_bytes (file_t logfile, void *buf, size_t len, int flags); |
| |
| bool |
| is_readable_without_exception_try(byte *pc, size_t size); |
| /* Note: this is OS independent version |
| * cf. is_readable_without_exception() and is_readable_without_exception_query_os() |
| */ |
| |
| /* NOTE - uses try/except when possible, is_readable_without_exception otherwise. NOTE - |
| * like our other is_readable_without_exception routines this routine offers no guarantee |
| * that the string will remain valid after the call returns. FIXME - may be worthwhile |
| * to extend this routine to copy the string into a buffer while in the try/using |
| * safe_read instead of making the caller do it. */ |
| bool |
| is_string_readable_without_exception(char *str, size_t *str_length /* OPTIONAL OUT */); |
| |
| #ifdef DEBUG |
| bool |
| is_valid_xml_char(char c); |
| #endif |
| |
| /* prints str, escaping out char or char seq. for a xml CDATA block */ |
| void |
| print_xml_cdata(file_t f, const char *str); |
| |
| /* Reads the full file, returns it in a buffer and sets buf_len to the size |
| * of the buffer allocated on the specified heap, if successful. Returns NULL |
| * and sets buf_len to 0 on failure. |
| */ |
| char *read_entire_file(const char *file, size_t *buf_len /* OUT */ |
| HEAPACCT(which_heap_t heap)); |
| |
| /* returns false if we are too low on disk to create a file of desired size */ |
| bool |
| check_low_disk_threshold(file_t f, uint64 new_file_size); |
| |
| /* MD5 */ |
| /* Note: MD5 is only 16 bytes in length, but it is usually used as a |
| * string, so each byte will result in 2 chars being used. |
| */ |
| #define MD5_BLOCK_LENGTH 64 |
| #define MD5_RAW_BYTES 16 |
| #define MD5_STRING_LENGTH (2*MD5_RAW_BYTES) |
| |
| /* To compute the message digest of several chunks of bytes, declare |
| * an MD5Context structure, pass it to MD5Init, call MD5Update as |
| * needed on buffers full of bytes, and then call MD5Final, which will |
| * fill a supplied 16-byte array with the digest. |
| */ |
| struct MD5Context { |
| uint32 state[4]; /* state */ |
| uint64 count; /* number of bits, mod 2^64 */ |
| unsigned char buffer[MD5_BLOCK_LENGTH]; /* input buffer */ |
| }; |
| |
| void MD5Init(struct MD5Context *ctx); |
| void MD5Update(struct MD5Context *ctx, const unsigned char *buf, size_t len); |
| void MD5Final(unsigned char digest[16], struct MD5Context *ctx); |
| |
| #ifdef PROCESS_CONTROL |
| bool get_md5_for_file(const char *file, char *hash_buf /* OUT */); |
| #endif |
| const char *get_application_md5(void); |
| void get_md5_for_region(const byte *region_start, uint len, |
| unsigned char digest[MD5_RAW_BYTES] /* OUT */); |
| bool md5_digests_equal(const byte digest1[MD5_RAW_BYTES], |
| const byte digest2[MD5_RAW_BYTES]); |
| |
| void |
| print_version_and_app_info(file_t file); |
| |
| /* returns a pseudo random number in [0, max_offset) |
| * Not crypto strong, and not thread safe! |
| */ |
| size_t |
| get_random_offset(size_t max_offset); |
| |
| void |
| set_random_seed(uint seed); |
| |
| uint |
| get_random_seed(void); |
| |
| void |
| convert_millis_to_date(uint64 millis, dr_time_t *time OUT); |
| |
| void |
| convert_date_to_millis(const dr_time_t *dr_time, uint64 *millis OUT); |
| |
| uint crc32(const char *buf, const uint len); |
| void utils_init(void); |
| void utils_exit(void); |
| |
| #ifdef NOLIBC |
| # ifdef WINDOWS |
| # ifdef isprint |
| # undef isprint |
| bool isprint(int c); |
| # endif |
| |
| # ifdef isdigit |
| # undef isdigit |
| bool isdigit(int c); |
| # endif |
| # endif |
| #endif |
| |
| #define isprint_fast(c) (((c) >= 0x20) && ((c) < 0x7f)) |
| #define isdigit_fast(c) (((c) >= '0') && ((c) <= '9')) |
| |
| #ifdef UNIX |
| /* PR 251709 / PR 257565: avoid __ctype_b linking issues for standalone |
| * and start/stop clients. We simply avoid linking with the locale code |
| * altogether. |
| */ |
| # ifdef isprint |
| # undef isprint |
| # endif |
| # ifdef isdigit |
| # undef isdigit |
| # endif |
| # define isprint isprint_HAS_LINK_ERRORS_USE_isprint_fast_INSTEAD |
| # define isdigit isdigit_HAS_LINK_ERRORS_USE_isdigit_fast_INSTEAD |
| /* to avoid calling isprint()/isdigit() and localization tables |
| * xref PR 251709 / PR 257565 |
| */ |
| #endif |
| |
| /* a little utiliy for printing a float that is formed by dividing 2 ints, |
| * gives back high and low parts for printing, also supports percentages |
| * Usage : given a, b (uint[64]); d_int()==divide_uint64_print(); |
| * uint c, d tmp; parameterized on precision p and width w |
| * note that %f is eqv. to %.6f, 3rd ex. is a percentage ex. |
| * "%.pf", a/(float)b => d_int(a, b, false, p, &c, &d); "%u.%.pu", c,d |
| * "%w.pf", a/(float)b => d_int(a, b, false, p, &c, &d); "%(w-p)u.%.pu", c,d |
| * "%.pf%%", 100*(a/(float)b) => d_int(a, b, true, p, &c, &d); "%u.%.pu%%", c,d |
| */ |
| void |
| divide_uint64_print(uint64 numerator, uint64 denominator, bool percentage, |
| uint precision, uint *top, uint *bottom); |
| |
| #if defined(DEBUG) || defined(INTERNAL) || defined(CLIENT_INTERFACE) |
| /* for printing a float (can't use %f on windows with NOLIBC), NOTE: you must |
| * preserve floating point state to call this function!! |
| * Usage : given double/float a; uint c, d and char *s tmp; dp==double_print |
| * parameterized on precision p width w |
| * note that %f is eqv. to %.6f |
| * "%.pf", a => dp(a, p, &c, &d, &s) "%s%u.%.pu", s, c, d |
| * "%w.pf", a => dp(a, p, &c, &d, &s) "%s%(w-p)u.%.pu", s, c, d |
| */ |
| void |
| double_print(double val, uint precision, uint *top, uint *bottom, |
| const char **sign); |
| #endif /* DEBUG || INTERNAL */ |
| |
| #ifdef CALL_PROFILE |
| /* Max depth of call stack to maintain. |
| * We actually maintain DYNAMO_OPTION(prof_caller) depth. |
| */ |
| #define MAX_CALL_PROFILE_DEPTH 8 |
| void profile_callers(void); |
| void profile_callers_exit(void); |
| #endif |
| |
| #ifdef STANDALONE_UNIT_TEST |
| |
| /* an ASSERT replacement for use in unit tests */ |
| #define EXPECT(expr, expected) do { \ |
| ptr_uint_t value_once = (ptr_uint_t) (expr); \ |
| EXPECT_RELATION_INTERNAL(#expr, value_once, ==, expected); \ |
| } while (0) |
| |
| #define EXPECT_NE(expr, expected) do { \ |
| ptr_uint_t value_once = (ptr_uint_t) (expr); \ |
| EXPECT_RELATION_INTERNAL(#expr, value_once, !=, expected); \ |
| } while (0) |
| |
| #define EXPECT_RELATION(expr, REL, expected) do { \ |
| ptr_uint_t value_once = (ptr_uint_t) (expr); \ |
| EXPECT_RELATION_INTERNAL(#expr, value_once, REL, expected); \ |
| } while (0) |
| |
| #define EXPECT_RELATION_INTERNAL(exprstr, value, REL, expected) do { \ |
| LOG(GLOBAL, LOG_ALL, 1, "%s = %d [expected "#REL" %d] %s\n", \ |
| exprstr, value_once, expected, \ |
| value_once REL expected ? "good" : "BAD"); \ |
| /* Avoid ASSERT to support a release build. */ \ |
| if (!(value REL expected)) { \ |
| print_file(STDERR, \ |
| "EXPECT failed at %s:%d in test %s: %s\n", \ |
| __FILE__, __LINE__, __FUNCTION__, exprstr); \ |
| os_terminate(NULL, TERMINATE_PROCESS); \ |
| } \ |
| } while (0) |
| |
| #define TESTRUN(test) do { \ |
| test_number++; \ |
| print_file(STDERR, \ |
| "Test %d: "#test":\n", test_number); \ |
| test; \ |
| print_file(STDERR, \ |
| "\t"#test" [OK]\n"); \ |
| } while (0) |
| |
| /* define this on top of each unit test, and of course unit_main() |
| * has to be declared. |
| * Note that unit_main() is |
| * responsible for calling standalone_init() and any other |
| * initialization routines as needed |
| */ |
| #define UNIT_TEST_MAIN \ |
| uint test_number = 0; \ |
| static int unit_main(void); \ |
| int main() { \ |
| print_file(STDERR, "%s:\n", __FILE__); \ |
| unit_main(); \ |
| print_file(STDERR, "%d tests\n", test_number); \ |
| print_file(STDERR, "%s: SUCCESS\n", __FILE__); \ |
| return 0; \ |
| } |
| |
| #endif /* STANDALONE_UNIT_TEST */ |
| |
| /* internal strdup */ |
| char *dr_strdup(const char *str HEAPACCT(which_heap_t which)); |
| |
| #ifdef WINDOWS |
| /* Allocates a new char* (NOT a new wchar_t*) from a wchar_t* */ |
| char * |
| dr_wstrdup(const wchar_t *str HEAPACCT(which_heap_t which)); |
| #endif |
| |
| /* Frees a char* string (NOT a wchar_t*) allocated via dr_strdup or |
| * dr_wstrdup that has not been modified since being copied! |
| */ |
| void |
| dr_strfree(const char *str HEAPACCT(which_heap_t which)); |
| |
| /* Merges two arrays, treating their elements as type void*. |
| * Allocates a new array on dcontext's heap for the result |
| * and returns it and its size. |
| */ |
| void |
| array_merge(dcontext_t *dcontext, bool intersect /* else union */, |
| void **src1, uint src1_num, void **src2, uint src2_num, |
| /*OUT*/ void ***dst, /*OUT*/ uint *dst_num |
| HEAPACCT(which_heap_t which)); |
| |
| #endif /* _UTILS_H_ */ |