* Copyright (c) 2010-2022 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.
*/
* utils.h - miscellenaous utilities
*/
#ifndef _UTILS_H_
#define _UTILS_H_ 1
#ifdef assert
# undef assert
#endif
#define assert assert_no_good_use_ASSERT_instead
#define CHKLVL_ASSERTS 1
#define CHKLVL_DEFAULT 2
#if defined(DEBUG) && !defined(NOT_DYNAMORIO_CORE_PROPER) && \
!defined(NOT_DYNAMORIO_CORE) && !defined(STANDALONE_DECODER)
# define DEBUG_CHECKS(level) (dynamo_options.checklevel >= (level))
#else
# define DEBUG_CHECKS(level) true
#endif
#if defined(DEBUG) && !defined(STANDALONE_DECODER)
# ifdef INTERNAL
* 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)) \
? (d_r_internal_error(__FILE__, __LINE__, #x), 0) \
: 0))
* (case 7851). can also use statement expr ({;}) but only w/ gcc, not w/ cl.
*/
# define ASSERT_MESSAGE(level, msg, x) \
((DEBUG_CHECKS(level) && !(x)) \
? (d_r_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
# define ASSERT(x) \
((void)((DEBUG_CHECKS(CHKLVL_ASSERTS) && !(x)) \
? (d_r_internal_error(__FILE__, __LINE__, ""), 0) \
: 0))
# define ASSERT_MESSAGE(level, msg, x) \
((void)((DEBUG_CHECKS(level) && !(x)) \
? (d_r_internal_error(__FILE__, __LINE__, ""), 0) \
: 0))
# define ASSERT_CURIOSITY(x) ((void)0)
# define ASSERT_CURIOSITY_ONCE(x) ((void)0)
# endif
# 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
#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
d_r_internal_error(const char *file, int line, const char *expr);
bool
ignore_assert(const char *assert_file_line, const char *expr);
#endif
#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 DEBUG
# define CLIENT_ASSERT(x, msg) apicheck(x, msg)
#else
# define CLIENT_ASSERT(x, msg)
#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
* 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 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
* (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)
# 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
#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)
#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);
#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)
#define ALIGN_SHIFT_SIZE(addr, size, alignment) \
(CROSSES_ALIGNMENT(addr, size, alignment) \
? ((size)-1 - ALIGN_MOD(addr, size, alignment)) \
: 0)
* Synchronization
*/
#include "atomic_exports.h"
#define LOCK_FREE_STATE -1
#define LOCK_SET_STATE (LOCK_FREE_STATE + 1)
#define LOCK_CONTENDED_STATE (LOCK_SET_STATE + 1)
#define SPINLOCK_FREE_STATE \
0
*/
* 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;
contention_event_t contended_event;
#ifdef DEADLOCK_AVOIDANCE
const char *name;
* or equal to the rank of a lock already held by the owning thread is acquired
*/
uint rank;
thread_id_t owner;
* it if changing them. Below here is filled with 0's.
*/
dcontext_t *owning_dcontext;
struct _mutex_t *prev_owned_lock;
uint count_times_acquired;
uint count_times_contended;
uint count_times_spin_pause;
uint max_contended_requests;
uint count_times_spin_only;
* 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;
# ifdef MUTEX_CALLSTACK
* static array that will get too fat if larger than 4
*/
# define MAX_MUTEX_CALLSTACK 4
byte *callstack[MAX_MUTEX_CALLSTACK];
bool app_lock;
# else
# define MAX_MUTEX_CALLSTACK 0
# endif
bool deleted;
#endif
}
IF_MACOS(IF_AARCH64(__attribute__((aligned(8))))) mutex_t;
* 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;
typedef struct _recursive_lock_t {
mutex_t lock;
* Thus must allocate this statically (compiler should 4-byte-align
* this field, which is good enough) or align it manually!
* XXX: 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;
* 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;
thread_id_t writer;
volatile int num_pending_readers;
contention_event_t writer_waiting_readers;
contention_event_t readers_waiting_writer;
} read_write_lock_t;
#ifdef DEADLOCK_AVOIDANCE
# define LOCK_RANK(lock) lock##_rank
* list, usually at the location marked as ADD HERE */
enum {
LOCK_RANK(outermost_lock),
LOCK_RANK(thread_in_DR_exclusion),
LOCK_RANK(all_threads_synch_lock),
LOCK_RANK(trace_building_lock),
* FIXME: any other locks that could be interrupted by exception that
* could be app's fault?
*/
LOCK_RANK(thread_initexit_lock),
LOCK_RANK(bb_building_lock),
# ifdef WINDOWS
LOCK_RANK(exception_stack_lock),
# endif
* possible deadlock if already held */
LOCK_RANK(all_threads_lock),
LOCK_RANK(linking_lock),
# ifdef SHARING_STUDY
LOCK_RANK(shared_blocks_lock),
LOCK_RANK(shared_traces_lock),
# endif
LOCK_RANK(synch_lock),
LOCK_RANK(protect_info),
LOCK_RANK(sideline_mutex),
LOCK_RANK(shared_cache_flush_lock),
< shared_delete_lock,
< change_linking_lock */
LOCK_RANK(shared_delete_lock),
LOCK_RANK(lazy_delete_lock),
LOCK_RANK(shared_cache_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),
LOCK_RANK(shared_vm_areas),
LOCK_RANK(shared_cache_count_lock),
LOCK_RANK(fragment_delete_mutex),
LOCK_RANK(tracedump_mutex),
LOCK_RANK(emulate_write_lock),
LOCK_RANK(unit_flush_lock),
# ifdef LINUX
LOCK_RANK(maps_iter_buf_lock),
* < hotp_vul_table_lock */
# endif
# ifdef HOT_PATCHING_INTERFACE
* 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),
* < dynamo_areas, < heap_unit_lock. */
# endif
LOCK_RANK(coarse_info_lock),
* > change_linking_lock */
LOCK_RANK(executable_areas),
* < process_module_vector_lock (diagnostics)
*/
# ifdef RCT_IND_BRANCH
LOCK_RANK(rct_module_lock),
* < module_data_lock, < heap allocation */
# endif
# ifdef RETURN_AFTER_CALL
LOCK_RANK(after_call_lock),
* > coarse_info_lock, > executable_areas,
* < module_data_lock */
# endif
LOCK_RANK(written_areas),
* < dynamo_areas < global_alloc_lock */
# ifdef LINUX
LOCK_RANK(rseq_trigger_lock),
# endif
LOCK_RANK(module_data_lock),
* > executable_areas */
# ifdef LINUX
LOCK_RANK(rseq_areas),
# endif
LOCK_RANK(special_units_list_lock),
LOCK_RANK(special_heap_lock),
* < dynamo_areas, < heap_unit_lock */
LOCK_RANK(coarse_info_incoming_lock),
* > special_heap_lock, > coarse_info_lock,
* > change_linking_lock */
* anymore but having it gives us flexibility so I'm leaving it)
*/
LOCK_RANK(coarse_table_rwlock),
LOCK_RANK(coarse_pclookup_table_rwlock),
* < coarse_th_table_rwlock */
LOCK_RANK(coarse_th_table_rwlock),
LOCK_RANK(process_module_vector_lock),
* grabbed on an exception, possible deadlock if already held FIXME */
LOCK_RANK(snapshot_lock),
# ifdef PROGRAM_SHEPHERDING
LOCK_RANK(futureexec_areas),
* < dynamo_areas < global_alloc_lock */
# ifdef WINDOWS
LOCK_RANK(app_flushed_areas),
# endif
# endif
LOCK_RANK(pretend_writable_areas),
LOCK_RANK(patch_proof_areas),
LOCK_RANK(emulate_write_areas),
LOCK_RANK(IAT_areas),
LOCK_RANK(dr_client_mutex),
LOCK_RANK(client_thread_count_lock),
LOCK_RANK(client_flush_request_lock),
LOCK_RANK(low_on_memory_pending_lock),
global_alloc_lock */
LOCK_RANK(callback_registration_lock),
LOCK_RANK(client_tls_lock),
LOCK_RANK(intercept_hook_lock),
LOCK_RANK(privload_lock),
# ifdef LINUX
LOCK_RANK(sigfdtable_lock),
# endif
LOCK_RANK(table_rwlock),
LOCK_RANK(loaded_module_areas),
LOCK_RANK(aslr_areas),
LOCK_RANK(aslr_pad_areas),
LOCK_RANK(native_exec_areas),
LOCK_RANK(thread_vm_areas),
LOCK_RANK(app_pc_table_rwlock),
* > module_data_lock */
LOCK_RANK(dead_tables_lock),
LOCK_RANK(aslr_lock),
# ifdef HOT_PATCHING_INTERFACE
LOCK_RANK(hotp_only_tramp_areas_lock),
* < global_alloc_lock */
LOCK_RANK(hotp_patch_point_areas_lock),
* < global_alloc_lock */
# endif
# ifdef CALL_PROFILE
LOCK_RANK(profile_callers_lock),
# endif
LOCK_RANK(coarse_stub_areas),
LOCK_RANK(moduledb_lock),
LOCK_RANK(pcache_dir_check_lock),
# ifdef UNIX
LOCK_RANK(suspend_lock),
LOCK_RANK(sighand_lock),
LOCK_RANK(sigmask_lock),
# endif
LOCK_RANK(modlist_areas),
# ifdef WINDOWS
LOCK_RANK(drwinapi_localheap_lock),
# endif
LOCK_RANK(client_aux_libs),
# ifdef WINDOWS
LOCK_RANK(client_aux_lib64_lock),
# endif
# ifdef WINDOWS
LOCK_RANK(alt_tls_lock),
# endif
# ifdef UNIX
LOCK_RANK(detached_sigact_lock),
# endif
* 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),
LOCK_RANK(fcache_unit_areas),
< dynamo_areas, < global_alloc_lock */
IF_NO_MEMQUERY_(LOCK_RANK(all_memory_areas))
IF_UNIX_(LOCK_RANK(set_thread_area_lock))
LOCK_RANK(landing_pad_areas_lock),
LOCK_RANK(dynamo_areas),
LOCK_RANK(map_intercept_pc_lock),
LOCK_RANK(global_alloc_lock),
LOCK_RANK(heap_unit_lock),
LOCK_RANK(vmh_lock),
LOCK_RANK(last_deallocated_lock),
# ifdef UNIX
LOCK_RANK(tls_lock),
* need to be even lower: as it is, only used for set */
# endif
LOCK_RANK(reset_pending_lock),
LOCK_RANK(initstack_mutex),
LOCK_RANK(event_lock),
LOCK_RANK(do_threshold_mutex),
LOCK_RANK(threads_killed_lock),
LOCK_RANK(child_lock),
# ifdef SIDELINE
LOCK_RANK(sideline_lock),
LOCK_RANK(do_not_delete_lock),
LOCK_RANK(remember_lock),
LOCK_RANK(sideline_table_lock),
# 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),
* < report_buf_lock (for assert) */
# endif
LOCK_RANK(report_buf_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),
LOCK_RANK(diagnost_reg_mutex),
# ifdef WINDOWS_PC_SAMPLE
LOCK_RANK(profile_dump_lock),
# endif
LOCK_RANK(prng_lock),
* SYSLOG and STATS facilities */
LOCK_RANK(options_lock),
# ifdef WINDOWS
LOCK_RANK(debugbox_lock),
# endif
LOCK_RANK(eventlog_mutex),
LOCK_RANK(datasec_selfprot_lock),
LOCK_RANK(thread_stats_lock),
# ifdef UNIX
* anytime, so we put it at the innermost.
*/
LOCK_RANK(shared_itimer_lock),
# endif
LOCK_RANK(innermost_lock),
};
struct _thread_locks_t;
typedef struct _thread_locks_t thread_locks_t;
extern mutex_t outermost_lock;
extern mutex_t innermost_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);
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
# define INIT_LOCK_NO_TYPE(name, rank) \
{ \
LOCK_FREE_STATE, KSYNCH_TYPE_STATIC_INIT \
}
#endif
* 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
* 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, \
}
#define DELETE_LOCK(lock) d_r_mutex_delete(&lock)
#define DELETE_SPINMUTEX(spinmutex) spinmutex_delete(&spinmutex)
#define DELETE_RECURSIVE_LOCK(rec_lock) d_r_mutex_delete(&(rec_lock).lock)
#define DELETE_READWRITE_LOCK(rwlock) d_r_mutex_delete(&(rwlock).lock)
void
d_r_mutex_delete(mutex_t *lock);
void
d_r_mutex_lock(mutex_t *mutex);
bool
d_r_mutex_trylock(mutex_t *mutex);
void
d_r_mutex_unlock(mutex_t *mutex);
#ifdef UNIX
void
d_r_mutex_fork_reset(mutex_t *mutex);
#endif
void
d_r_mutex_mark_as_app(mutex_t *lock);
* case that there is contention on this lock, this thread will be marked safe
* to be relocated and even detached. The current thread's mcontext may be
* clobbered with the provided value even if the thread is not suspended.
*/
void
d_r_mutex_lock_app(mutex_t *mutex, priv_mcontext_t *mc);
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);
* 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 atomic_aligned_read_int(&lock->lock_requests) > LOCK_FREE_STATE;
}
static inline bool
spinmutex_testlock(spin_mutex_t *spin_lock)
{
return mutex_testlock(&spin_lock->lock);
}
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);
* case that there is contention on this lock, this thread will be marked safe
* to be relocated and even detached. The current thread's mcontext may be
* clobbered with the provided value even if the thread is not suspended.
*/
void
acquire_recursive_app_lock(recursive_lock_t *mutex, priv_mcontext_t *mc);
void
d_r_read_lock(read_write_lock_t *rw);
void
d_r_write_lock(read_write_lock_t *rw);
bool
d_r_write_trylock(read_write_lock_t *rw);
void
d_r_read_unlock(read_write_lock_t *rw);
void
d_r_write_unlock(read_write_lock_t *rw);
bool
self_owns_write_lock(read_write_lock_t *rw);
#if defined(MACOS) || (defined(X64) && defined(WINDOWS))
# define ATOMIC_READ_THREAD_ID(id) \
((thread_id_t)atomic_aligned_read_int64((volatile int64 *)(id)))
#else
# define ATOMIC_READ_THREAD_ID(id) \
((thread_id_t)atomic_aligned_read_int((volatile int *)(id)))
#endif
#define WRITE_LOCK_HELD(rw) \
(mutex_testlock(&(rw)->lock) && atomic_aligned_read_int(&(rw)->num_readers) == 0)
#define READ_LOCK_HELD(rw) (atomic_aligned_read_int(&(rw)->num_readers) > 0)
* 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) (ATOMIC_READ_THREAD_ID(&(m)->owner) == d_r_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 || OWN_NO_LOCKS(dc)); \
} while (0)
#else
# 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 )
# define OWN_NO_LOCKS(dc) true
# define ASSERT_OWN_NO_LOCKS()
#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))
#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))
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)
* events (i#884).
*/
extern bool
dr_modload_hook_exists(void);
#define USE_BB_BUILDING_LOCK_STEADY_STATE() \
((DYNAMO_OPTION(shared_bbs) && !INTERNAL_OPTION(single_thread_in_DR)) || \
dr_modload_hook_exists())
#define USE_BB_BUILDING_LOCK() (USE_BB_BUILDING_LOCK_STEADY_STATE() && bb_lock_start)
#define SHARED_BB_LOCK() \
do { \
if (USE_BB_BUILDING_LOCK()) \
d_r_mutex_lock(&(bb_building_lock)); \
} while (0)
* 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) \
d_r_mutex_unlock(&(bb_building_lock)); \
} while (0)
#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)
* 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
*/
* 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 PR 212574 (==8139): would we want the 32-bit one for smaller index values?
*/
#define PHI_2_32 2654435769U
#define PHI_2_64 11400714819323198485U
#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
#define HASH_MASK(num_bits) ((~PTR_UINT_0) >> (HASH_TAG_BITS - (num_bits)))
* 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
* 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
* 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
* 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 ? \
* the later hash_mask_offset shift. FIXME - structure all \
* these macros a little clearly/efficiently. */ \
\
(((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))
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);
* 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. */
* 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)
* 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)
#define IS_POWER_OF_2(x) ((x) == 0 || ((x) & ((x)-1)) == 0)
* (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)))
typedef uint bitmap_element_t;
typedef bitmap_element_t bitmap_t[];
* 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)
* bit scan forward. See /usr/src/linux-2.4/include/asm/bitops.h for
* an all assembly implementation. Here we stick to plain C.
*/
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);
}
void
bitmap_initialize_free(bitmap_t b, uint bitmap_size);
uint
bitmap_allocate_blocks(bitmap_t b, uint bitmap_size, uint request_blocks,
uint start_block);
void
bitmap_free_blocks(bitmap_t b, uint bitmap_size, uint first_block, uint num_free);
#ifdef DEBUG
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
#define LOGDIR_MAX_NUM 1000
#define LOGDIR_FORMAT_STRING "%s.%03d"
#define LOGDIR_FORMAT_ARGS(num) "dynamorio", num
* 512 is too short for internal exception w/ app + options + callstack
*/
* It must be a raw numeric constant as we STRINGIFY it.
*/
#ifdef PARAMS_IN_REGISTRY
# define MAX_LOG_LENGTH IF_X64_ELSE(1280, 768)
# define MAX_LOG_LENGTH_MINUS_ONE IF_X64_ELSE(1279, 767)
#else
* so clients don't have dr_printf truncated as often.
*/
# define MAX_LOG_LENGTH 2048
# define MAX_LOG_LENGTH_MINUS_ONE 2047
#endif
#if defined(DEBUG) && !defined(STANDALONE_DECODER)
# define LOG(file, mask, level, ...) \
do { \
if (d_r_stats != NULL && d_r_stats->loglevel >= (level) && \
(d_r_stats->logmask & (mask)) != 0) \
d_r_print_log(file, mask, level, __VA_ARGS__); \
} while (0)
# define DOELOG(level, mask, statement) \
do { \
if (d_r_stats != NULL && d_r_stats->loglevel >= (level) && \
(d_r_stats->logmask & (mask)) != 0) \
statement \
} while (0)
# define DOCHECK(level, statement) \
do { \
if (DEBUG_CHECKS(level)) \
statement \
} while (0)
# ifdef INTERNAL
# define DOLOG DOELOG
# define LOG_DECLARE(declaration) declaration
# else
# define DOLOG(level, mask, statement)
# define LOG_DECLARE(declaration)
# endif
# define THREAD \
((dcontext == NULL) \
? main_logfile \
: ((dcontext == GLOBAL_DCONTEXT) ? main_logfile : dcontext->logfile))
# define THREAD_GET get_thread_private_logfile()
# define GLOBAL main_logfile
#else
# define LOG(file, mask, level, ...)
# define DOLOG(level, mask, statement)
# define DOELOG DOLOG
# define LOG_DECLARE(declaration)
# define DOCHECK(level, statement)
#endif
void
d_r_print_log(file_t logfile, uint mask, uint level, const char *fmt, ...);
void
print_file(file_t f, const char *fmt, ...);
* 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
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);
* [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 ,
size_t *intersection_len , 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,
PROCESS_DIR
} log_dir_t;
void
enable_new_log_dir(void);
void
create_log_dir(int dir_type);
bool
get_log_dir(log_dir_t dir_type, char *buffer, uint *buffer_length);
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);
* 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.
*/
extern int do_once_generation;
#define DO_ONCE(statement) \
{ \
\
static int do_once = 0; \
if (do_once < do_once_generation) { \
SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT); \
do_once++; \
SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT); \
statement; \
} \
}
* The counter is only incremented if it is less than the threshold
*/
* 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)
#else
# define DECLARE_THRESHOLD_LOCK(section) \
static mutex_t do_threshold_mutex VAR_IN_SECTION(section) = \
INIT_LOCK_FREE(do_threshold_mutex);
#endif
* 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; \
d_r_mutex_lock(&do_threshold_mutex); \
if (do_threshold_cur < threshold) { \
do_threshold_cur++; \
d_r_mutex_unlock(&do_threshold_mutex); \
statement_below; \
} else { \
d_r_mutex_unlock(&do_threshold_mutex); \
statement_after; \
} \
}
* 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)
*/
* 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; \
IF_UNIX(global_try_tid = get_sys_thread_id()); \
} 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)); \
IF_UNIX(global_try_tid = INVALID_THREAD_ID); \
} while (0)
#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)
#define TRY(try_pointer, try_statement, except_or_finally) \
do { \
try_except_context_t try__state; \
\
* TRY_EXCEPT_ALLOW_NO_DCONTEXT. \
*/ \
ASSERT( \
(try_pointer) == &global_try_except || (try_pointer) == NULL || \
(get_thread_private_dcontext() != \
NULL &&
* result of get_thread_private_dcontext() (because we need \
* just the offset of a data member). Still, when the null \
* sanitizer is enabled, it performs the is-null check, which \
* can fail if the returned value is NULL. So, we need this \
* is-null check of our own. \
*/ \
(try_pointer) == &get_thread_private_dcontext()->try_except) || \
(try_pointer) == \
&thread_lookup(IF_UNIX_ELSE(get_sys_thread_id(), d_r_get_thread_id())) \
->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_statement */ \
if ((try_pointer) != NULL) \
{ \
POP_TRY_BLOCK(try_pointer, try__state); \
} \
} \
except_or_finally \
\
} while (0)
* 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.
*/
* 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).
*/
#define EXCEPT(try_pointer, statement) \
else \
{ \
\
\
POP_TRY_BLOCK(try_pointer, try__state); \
statement; \
\
}
* 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.
*/
#define FINALLY(try_pointer, statement) \
{ \
ASSERT_NOT_IMPLEMENTED(false); \
ASSERT((try_pointer) != NULL); \
if ((try_pointer)->unwinding_exception) { \
\
\
\
\
\
\
\
POP_TRY_BLOCK(try_pointer, try__state); \
} \
ASSERT((try_pointer)->try_except_state != NULL && \
"try/finally should be nested in try/except"); \
\
statement; \
if ((try_pointer)->unwinding_exception) { \
\
\
\
\
ASSERT_NOT_IMPLEMENTED(false); \
} \
}
#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 };
#define PROBE_READ_PC(pc) ((*(volatile char *)(pc)))
#define PROBE_WRITE_PC(pc) ATOMIC_ADD_PTR(volatile char *, (*(volatile char *)(pc)), 0)
* 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"
#ifdef UNIX
# define UNUSED __attribute__((unused))
#else
# define UNUSED
#endif
* 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 UNUSED = (void *)pv; \
unused_pv = NULL; \
}
* use DO_GLOBAL_STATS or check runtime option.
*/
#define GLOBAL_STAT(stat) d_r_stats->stat##_pair.value
#define GLOBAL_STAT_ADDR(stat) &(d_r_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_* 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); \
\
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))
#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)
# define DOCHECKINT(level, statement)
#endif
* STANDALONE_DECODER is defined.
*/
#ifdef DEBUG
# 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)
* (such as special_heap_create_unit, others?), if ever go back to using a mutex
* to protect the stats need to update such places
*/
* not used if not DEBUG */
* stats are atomic operations on Intel x86 */
* 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() (d_r_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
# 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)
# define GLOBAL_STATS_ON() (d_r_stats != NULL && DYNAMO_OPTION(global_rstats))
* stat: if rename release vars, have to use separate GLOBAL_RSTAT though.
*/
# define STATS_INC(stat)
# define STATS_INC_DC(dcontext, stat)
# define STATS_DEC(stat)
# define STATS_ADD(stat, value)
# define STATS_SUB(stat, value)
# define STATS_INC_ASSIGN(stat, var)
# define STATS_ADD_ASSIGN(stat, var, value)
# define STATS_MAX(stat_max, stat_cur)
# define STATS_TRACK_MAX(stats_track_max, val)
# define STATS_PEAK(stat)
# define STATS_ADD_MAX(stat_max, stat_cur, value)
# define STATS_ADD_PEAK(stat, value)
# define STATS_RESET(stat)
#endif
#ifdef KSTATS
# define DOKSTATS(statement) \
do { \
statement \
} while (0)
* occasionally KSTART(name)/KSWITCH(better_name)/KSTOP(name), and in
* ignorable cases KSTART(name)/KSTOP_NOT_PROPAGATED(name)
*/
# define KSTART(name) KSTAT_THREAD(name, kstat_start_var(ks, pv))
# define KSTOP(name) KSTAT_THREAD(name, kstat_stop_matching_var(ks, pv))
# define KSWITCH(name) KSTAT_THREAD(name, kstat_switch_var(ks, pv))
# 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()
# 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))
# define KSWITCH_STOP(name) \
KSTAT_THREAD(name, { \
kstat_switch_var(ks, pv); \
kstat_stop_not_matching_var(ks, ignored); \
})
# 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); \
})
# 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); \
})
# 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); \
})
* 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
# define DOKSTATS(statement)
# define KSTART(name)
# define KSWITCH(name)
# define KSWITCH_STOP(name)
# define KSWITCH_STOP_NOT_PROPAGATED(name)
# define KSTOP_NOT_MATCHING_NOT_PROPAGATED(name)
# define KSTOP_NOT_PROPAGATED(name)
# define KSTOP_NOT_MATCHING(name)
# define KSTOP(name)
# define KSTOP_REWIND(name)
# define KSTOP_REWIND_UNTIL(name)
# define KSTART_DC(dc, name)
# define KSTOP_DC(dc, name)
# define KSTOP_NOT_MATCHING_DC(dc, name)
# define KSTOP_REWIND_DC(dc, name)
#endif
#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)
# define DOLOG_ONCE(level, mask, statement)
#endif
#define MAX_FP_STATE_SIZE (512 + 16)
* that contains ifdefs, need to be used at same nesting depth */
* middle of a list of declerations without bothering the compiler or creating
* a new nesting block */
* 512 bytes - avoid nesting uses
*/
* 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 UNUSED = 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)
#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
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;
extern const char *exception_label_client;
#define CRASH_NAME "internal crash"
#define STACK_OVERFLOW_NAME "stack overflow"
void
set_exception_strings(const char *override_label, const char *override_url);
void
set_display_version(const char *ver);
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
d_r_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, ...) \
d_r_notify(type, false, synch, IF_WINDOWS_(MSG_##id) sub, #type, MSG_##id##_STRING, \
__VA_ARGS__)
#define SYSLOG_INTERNAL_COMMON(synch, type, ...) \
d_r_notify(type, true, synch, IF_WINDOWS_(MSG_INTERNAL_##type) 0, #type, __VA_ARGS__)
* 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, ...) \
d_r_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
#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__))
#define FATAL_ERROR_EXIT_CODE 40
#define REPORT_FATAL_ERROR_AND_EXIT(msg_id, arg_count, ...) \
do { \
* it may make sense to generate a core dump too. \
*/ \
SYSLOG_COMMON(false, SYSLOG_CRITICAL, msg_id, arg_count, __VA_ARGS__); \
* with TERMINATE_PROCESS or our specific error code. \
*/ \
os_terminate_with_code(NULL, TERMINATE_PROCESS, FATAL_ERROR_EXIT_CODE); \
ASSERT_NOT_REACHED(); \
} while (0)
* 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 */
#define USAGE_ERROR(...) \
do { \
SYSLOG_INTERNAL_ERROR(__VA_ARGS__); \
ASSERT_NOT_REACHED(); \
} while (0)
#define FATAL_USAGE_ERROR(id, sub, ...) \
do { \
\
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
# define TRACELOG(level) LOG(GLOBAL, LOG_TOP, level, "%s:%d ", __FILE__, __LINE__)
#else
# define TRACELOG(level)
#endif
* 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
* "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
* min, max, mean, and stddev
*/
void
print_statistics(int *data, int size);
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
d_r_print_timestamp(file_t logfile);
void
print_symbolic_address(app_pc tag, char *buf, int max_chars, bool exact_only);
#endif
void
dump_global_rstats_to_stderr(void);
bool
under_internal_exception(void);
enum {
DUMP_NO_QUOTING = 0x01000,
DUMP_OCTAL = 0x02000,
DUMP_NO_CHARS = 0x04000,
DUMP_RAW = 0x08000,
DUMP_DWORD = 0x10000,
DUMP_ADDRESS = 0x20000,
DUMP_APPEND_ASCII = 0x40000,
DUMP_PER_LINE = 0x000ff
};
#define DUMP_PER_LINE_DEFAULT 16
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);
* cf. is_readable_without_exception() and is_readable_without_exception_query_os()
*/
* 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 );
bool
safe_write_try_except(void *base, size_t size, const void *in_buf, size_t *bytes_written);
#ifdef DEBUG
bool
is_valid_xml_char(char c);
#endif
void
print_xml_cdata(file_t f, const char *str);
* 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
HEAPACCT(which_heap_t heap));
bool
check_low_disk_threshold(file_t f, uint64 new_file_size);
* 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)
* an MD5Context structure, pass it to d_r_md5_init, call d_r_md5_update as
* needed on buffers full of bytes, and then call d_r_md5_final, which will
* fill a supplied 16-byte array with the digest.
*/
struct MD5Context {
uint32 state[4];
uint64 count;
unsigned char buffer[MD5_BLOCK_LENGTH];
};
void
d_r_md5_init(struct MD5Context *ctx);
void
d_r_md5_update(struct MD5Context *ctx, const unsigned char *buf, size_t len);
void
d_r_md5_final(unsigned char digest[16], struct MD5Context *ctx);
#ifdef PROCESS_CONTROL
bool
get_md5_for_file(const char *file, char *hash_buf );
#endif
const char *
get_application_md5(void);
void
get_md5_for_region(const byte *region_start, uint len,
unsigned char digest[MD5_RAW_BYTES] );
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);
* Not crypto strong, and not thread safe!
*/
size_t
get_random_offset(size_t max_offset);
void
d_r_set_random_seed(uint seed);
uint
d_r_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
d_r_crc32(const char *buf, const uint len);
void
utils_init(void);
void
utils_exit(void);
#ifdef WINDOWS
# ifdef isprint
# undef isprint
bool
isprint(int c);
# endif
# ifdef isdigit
# undef isdigit
bool
isdigit(int c);
# endif
#endif
#define isprint_fast(c) (((c) >= 0x20) && ((c) < 0x7f))
#define isdigit_fast(c) (((c) >= '0') && ((c) <= '9'))
#ifdef UNIX
* 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
* xref PR 251709 / PR 257565
*/
#endif
* 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);
* NOTE: You must preserve x87 floating point state to call this function, unless
* you can prove the compiler will never use x87 state for float operations.
* 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);
#ifdef CALL_PROFILE
* 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
# define FAIL() EXPECT(true, false)
# 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_EQ(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(ptr_uint_t) expected ? "good" : "BAD"); \
\
if (!(value REL(ptr_uint_t) 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 EXPECT_STR(expr, expected, n) \
do { \
const char *value_once = expr; \
bool ok = strncmp(value_once, expected, n) == 0; \
LOG(GLOBAL, LOG_ALL, 1, #expr " = %s [expected == %s] %s\n", value_once, \
expected, ok ? "good" : "BAD"); \
\
if (!ok) { \
print_file(STDERR, "EXPECT failed at %s:%d in test %s: %s\n", __FILE__, \
__LINE__, __FUNCTION__, #expr); \
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)
* 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
char *
dr_strdup(const char *str HEAPACCT(which_heap_t which));
#ifdef WINDOWS
char *
dr_wstrdup(const wchar_t *str HEAPACCT(which_heap_t which));
#endif
* dr_wstrdup that has not been modified since being copied!
*/
void
dr_strfree(const char *str HEAPACCT(which_heap_t which));
* 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 , void **src1,
uint src1_num, void **src2, uint src2_num,
void ***dst, uint *dst_num HEAPACCT(which_heap_t which));
bool
stats_get_snapshot(dr_stats_t *drstats);
#endif