blob: d119af9e0d0d6d754e7624f8692dfa54e4f4e77b [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2011-2013 Google, Inc. All rights reserved.
* Copyright (c) 2008-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.
*/
/*
* API regression test that registers for all supported event callbacks
* (except the nudge and security violation callback)
*/
#include "dr_api.h"
#include "client_tools.h"
#ifdef UNIX
# include <signal.h>
#endif
#ifdef WINDOWS
# pragma warning( disable : 4100) // unreferenced formal parameter
# pragma warning( disable : 4127) // conditional expression is constant
#endif
/* We compile this test as C and C++ with different target names. */
#ifdef __cplusplus
# define EVENTS "events_cpp"
#else
# define EVENTS "events"
#endif
void *mutex;
enum event_seq {
EVENT_MODULE_LOAD_1 = 0,
EVENT_MODULE_LOAD_2,
EVENT_THREAD_INIT_1,
EVENT_THREAD_INIT_2,
EVENT_BB_1,
EVENT_BB_2,
EVENT_END_TRACE_1,
EVENT_END_TRACE_2,
EVENT_TRACE_1,
EVENT_TRACE_2,
EVENT_DELETE_1,
EVENT_DELETE_2,
EVENT_FILTER_SYSCALL_1,
EVENT_FILTER_SYSCALL_2,
EVENT_PRE_SYSCALL_1,
EVENT_PRE_SYSCALL_2,
EVENT_POST_SYSCALL_1,
EVENT_POST_SYSCALL_2,
EVENT_MODULE_UNLOAD_1,
EVENT_MODULE_UNLOAD_2,
EVENT_THREAD_EXIT_1,
EVENT_THREAD_EXIT_2,
EVENT_FORK_INIT_1,
EVENT_FORK_INIT_2,
EVENT_SIGNAL_1,
EVENT_SIGNAL_2,
EVENT_EXCEPTION_1,
EVENT_EXCEPTION_2,
EVENT_RESTORE_STATE_1,
EVENT_RESTORE_STATE_2,
EVENT_RESTORE_STATE_EX_1,
EVENT_RESTORE_STATE_EX_2,
EVENT_last,
};
const char * const name[EVENT_last] = {
"module load event 1",
"module load event 2",
"thread init event 1",
"thread init event 2",
"bb event 1",
"bb event 2",
"end trace event 1",
"end trace event 2",
"trace event 1",
"trace event 2",
"delete event 1",
"delete event 2",
"filter syscall event 1",
"filter syscall event 2",
"pre syscall event 1",
"pre syscall event 2",
"post syscall event 1",
"post syscall event 2",
"module unload event 1",
"module unload event 2",
"thread exit event 1",
"thread exit event 2",
"fork init event 1",
"fork init event 2",
"signal event 1",
"signal event 2",
"exception event 1",
"exception event 2",
"restore state event 1",
"restore state event 2",
"restore state ex event 1",
"restore state ex event 2",
};
int counts[EVENT_last];
static void
inc_count_first(int first, int second)
{
dr_mutex_lock(mutex);
if (counts[second] == 0)
dr_fprintf(STDERR, "%s is called before %s\n", name[first], name[second]);
counts[first]++;
dr_mutex_unlock(mutex);
}
static void
inc_count_second(int second)
{
dr_mutex_lock(mutex);
counts[second]++;
dr_mutex_unlock(mutex);
}
static
void check_result(void)
{
int i;
for (i = 0; i < EVENT_last; i++) {
if (counts[i] == 0)
continue;
dr_fprintf(STDERR, "%s is called %d time(s)\n", name[i], counts[i]);
dr_flush_file(STDOUT);
}
}
static
void exit_event1(void)
{
dr_fprintf(STDERR, "exit event 1\n");
dr_flush_file(STDOUT);
if (!dr_unregister_exit_event(exit_event1))
dr_fprintf(STDERR, "unregister failed!\n");
check_result();
dr_mutex_destroy(mutex);
}
static
void exit_event2(void)
{
dr_fprintf(STDERR, "exit event 2\n");
dr_flush_file(STDOUT);
if (!dr_unregister_exit_event(exit_event2))
dr_fprintf(STDERR, "unregister failed!\n");
}
static
void thread_init_event1(void *drcontext)
{
inc_count_first(EVENT_THREAD_INIT_1, EVENT_THREAD_INIT_2);
if (!dr_unregister_thread_init_event(thread_init_event1))
dr_fprintf(STDERR, "unregister failed!\n");
}
static
void thread_init_event2(void *drcontext)
{
inc_count_second(EVENT_THREAD_INIT_2);
if (!dr_unregister_thread_init_event(thread_init_event2))
dr_fprintf(STDERR, "unregister failed!\n");
}
static
void thread_exit_event1(void *drcontext)
{
inc_count_first(EVENT_THREAD_EXIT_1, EVENT_THREAD_EXIT_2);
if (!dr_unregister_thread_exit_event(thread_exit_event1))
dr_fprintf(STDERR, "unregister failed!\n");
}
static
void thread_exit_event2(void *drcontext)
{
inc_count_second(EVENT_THREAD_EXIT_2);
if (!dr_unregister_thread_exit_event(thread_exit_event2))
dr_fprintf(STDERR, "unregister failed!\n");
}
#ifdef UNIX
static
void fork_init_event1(void *drcontext)
{
inc_count_first(EVENT_FORK_INIT_1, EVENT_FORK_INIT_2);
if (!dr_unregister_fork_init_event(fork_init_event1))
dr_fprintf(STDERR, "unregister failed!\n");
}
static
void fork_init_event2(void *drcontext)
{
int i;
dr_mutex_lock(mutex);
for (i = 0; i < EVENT_last; i++)
counts[i] = 0;
counts[EVENT_FORK_INIT_2]++;
dr_mutex_unlock(mutex);
if (!dr_unregister_fork_init_event(fork_init_event2))
dr_fprintf(STDERR, "unregister failed!\n");
}
#endif
static
dr_emit_flags_t bb_event1(void *dcontext, void *tag, instrlist_t *bb,
bool for_trace, bool translating)
{
inc_count_first(EVENT_BB_1, EVENT_BB_2);
if (!dr_unregister_bb_event(bb_event1))
dr_fprintf(STDERR, "unregister failed!\n");
return DR_EMIT_DEFAULT;
}
static
dr_emit_flags_t bb_event2(void *dcontext, void *tag, instrlist_t *bb,
bool for_trace, bool translating)
{
inc_count_second(EVENT_BB_2);
if (!dr_unregister_bb_event(bb_event2))
dr_fprintf(STDERR, "unregister failed!\n");
return DR_EMIT_DEFAULT;
}
static
dr_emit_flags_t trace_event1(void *dcontext, void *tag, instrlist_t *trace,
bool translating)
{
inc_count_first(EVENT_TRACE_1, EVENT_TRACE_2);
if (!dr_unregister_trace_event(trace_event1))
dr_fprintf(STDERR, "unregister failed!\n");
return DR_EMIT_DEFAULT;
}
static
dr_emit_flags_t trace_event2(void *dcontext, void *tag, instrlist_t *trace,
bool translating)
{
inc_count_second(EVENT_TRACE_2);
if (!dr_unregister_trace_event(trace_event2))
dr_fprintf(STDERR, "unregister failed!\n");
return DR_EMIT_DEFAULT;
}
static
dr_custom_trace_action_t end_trace_event1(void *dcontext, void *trace_tag, void *next_tag)
{
inc_count_first(EVENT_END_TRACE_1, EVENT_END_TRACE_2);
if (!dr_unregister_end_trace_event(end_trace_event1))
dr_fprintf(STDERR, "unregister failed!\n");
return CUSTOM_TRACE_DR_DECIDES;
}
static
dr_custom_trace_action_t end_trace_event2(void *dcontext, void *trace_tag, void *next_tag)
{
inc_count_second(EVENT_END_TRACE_2);
if (!dr_unregister_end_trace_event(end_trace_event2))
dr_fprintf(STDERR, "unregister failed!\n");
return CUSTOM_TRACE_DR_DECIDES;
}
static
void delete_event1(void *dcontext, void *tag)
{
inc_count_first(EVENT_DELETE_1, EVENT_DELETE_2);
if (!dr_unregister_delete_event(delete_event1))
dr_fprintf(STDERR, "unregister failed!\n");
}
static
void delete_event2(void *dcontext, void *tag)
{
inc_count_second(EVENT_DELETE_2);
if (!dr_unregister_delete_event(delete_event2))
dr_fprintf(STDERR, "unregister failed!\n");
}
static
void module_load_event_perm(void *drcontext, const module_data_t *info, bool loaded)
{
/* Test i#138 */
if (info->full_path == NULL || info->full_path[0] == '\0')
dr_fprintf(STDERR, "ERROR: full_path empty for %s\n", dr_module_preferred_name(info));
#ifdef WINDOWS
/* We do not expect \\server-style paths for this test */
else if (info->full_path[0] == '\\' || info->full_path[1] != ':')
dr_fprintf(STDERR, "ERROR: full_path is not in DOS format: %s\n", info->full_path);
#else
else if (info->full_path[0] != '/')
dr_fprintf(STDERR, "ERROR: full_path is not absolute: %s\n", info->full_path);
#endif
}
static
void module_load_event1(void *drcontext, const module_data_t *info, bool loaded)
{
inc_count_first(EVENT_MODULE_LOAD_1, EVENT_MODULE_LOAD_2);
if (!dr_unregister_module_load_event(module_load_event1))
dr_fprintf(STDERR, "unregister failed!\n");
}
static
void module_load_event2(void *drcontext, const module_data_t *info, bool loaded)
{
inc_count_second(EVENT_MODULE_LOAD_2);
if (!dr_unregister_module_load_event(module_load_event2))
dr_fprintf(STDERR, "unregister failed!\n");
}
static
void module_unload_event1(void *drcontext, const module_data_t *info)
{
inc_count_first(EVENT_MODULE_UNLOAD_1, EVENT_MODULE_UNLOAD_2);
if (!dr_unregister_module_unload_event(module_unload_event1))
dr_fprintf(STDERR, "unregister failed!\n");
}
static
void module_unload_event2(void *drcontext, const module_data_t *info)
{
inc_count_second(EVENT_MODULE_UNLOAD_2);
if (!dr_unregister_module_unload_event(module_unload_event2))
dr_fprintf(STDERR, "unregister failed!\n");
}
static
bool pre_syscall_event1(void *drcontext, int sysnum)
{
inc_count_first(EVENT_PRE_SYSCALL_1, EVENT_PRE_SYSCALL_2);
if (!dr_unregister_pre_syscall_event(pre_syscall_event1))
dr_fprintf(STDERR, "unregister failed!\n");
return true;
}
static
bool pre_syscall_event2(void *drcontext, int sysnum)
{
inc_count_second(EVENT_PRE_SYSCALL_2);
if (!dr_unregister_pre_syscall_event(pre_syscall_event2))
dr_fprintf(STDERR, "unregister failed!\n");
return true;
}
static
void post_syscall_event1(void *drcontext, int sysnum)
{
inc_count_first(EVENT_POST_SYSCALL_1, EVENT_POST_SYSCALL_2);
if (!dr_unregister_post_syscall_event(post_syscall_event1))
dr_fprintf(STDERR, "unregister failed!\n");
}
static
void post_syscall_event2(void *drcontext, int sysnum)
{
inc_count_second(EVENT_POST_SYSCALL_2);
if (!dr_unregister_post_syscall_event(post_syscall_event2))
dr_fprintf(STDERR, "unregister failed!\n");
}
static
bool filter_syscall_event1(void *drcontext, int sysnum)
{
inc_count_first(EVENT_FILTER_SYSCALL_1, EVENT_FILTER_SYSCALL_2);
if (!dr_unregister_filter_syscall_event(filter_syscall_event1))
dr_fprintf(STDERR, "unregister failed!\n");
return true;
}
static
bool filter_syscall_event2(void *drcontext, int sysnum)
{
inc_count_second(EVENT_FILTER_SYSCALL_2);
if (!dr_unregister_filter_syscall_event(filter_syscall_event2))
dr_fprintf(STDERR, "unregister failed!\n");
return true;
}
#ifdef WINDOWS
static
bool exception_event_redirect(void *dcontext, dr_exception_t *excpt)
{
app_pc addr;
dr_mcontext_t mcontext = {sizeof(mcontext),DR_MC_ALL,};
module_data_t *data = dr_lookup_module_by_name("client."EVENTS".exe");
dr_fprintf(STDERR, "exception event redirect\n");
if (data == NULL) {
dr_fprintf(STDERR, "couldn't find "EVENTS".exe module\n");
return true;
}
addr = (app_pc)dr_get_proc_address(data->handle, "redirect");
dr_free_module_data(data);
mcontext = *excpt->mcontext;
mcontext.pc = addr;
if (addr == NULL) {
dr_fprintf(STDERR, "Couldn't find function redirect in "EVENTS".exe\n");
return true;
}
#ifdef X64
/* align properly in case redirect function relies on conventions (i#419) */
mcontext.xsp = ALIGN_BACKWARD(mcontext.xsp, 16) - sizeof(void*);
#endif
dr_redirect_execution(&mcontext);
dr_fprintf(STDERR, "should not be reached, dr_redirect_execution() should not return\n");
return true;
}
static
bool exception_event1(void *dcontext, dr_exception_t *excpt)
{
if (excpt->record->ExceptionCode == STATUS_ACCESS_VIOLATION)
inc_count_first(EVENT_EXCEPTION_1, EVENT_EXCEPTION_2);
if (!dr_unregister_exception_event(exception_event1))
dr_fprintf(STDERR, "unregister failed!\n");
/* ensure we get our deletion events */
dr_flush_region((app_pc)excpt->record->ExceptionAddress, 1);
return true;
}
static
bool exception_event2(void *dcontext, dr_exception_t *excpt)
{
if (excpt->record->ExceptionCode == STATUS_ACCESS_VIOLATION)
inc_count_second(EVENT_EXCEPTION_2);
dr_register_exception_event(exception_event_redirect);
if (!dr_unregister_exception_event(exception_event2))
dr_fprintf(STDERR, "unregister failed!\n");
return true;
}
#else
static
dr_signal_action_t signal_event_redirect(void *dcontext, dr_siginfo_t *info)
{
if (info->sig == SIGSEGV) {
app_pc addr;
module_data_t *data = dr_lookup_module_by_name("client."EVENTS);
dr_fprintf(STDERR, "signal event redirect\n");
if (data == NULL) {
dr_fprintf(STDERR, "couldn't find client."EVENTS" module\n");
return DR_SIGNAL_DELIVER;
}
addr = (app_pc)dr_get_proc_address(data->handle, "redirect");
dr_free_module_data(data);
if (addr == NULL) {
dr_fprintf(STDERR, "Couldn't find function redirect in client."EVENTS"\n");
return DR_SIGNAL_DELIVER;
}
#ifdef X64
/* align properly in case redirect function relies on conventions (i#384) */
info->mcontext->xsp = ALIGN_BACKWARD(info->mcontext->xsp, 16) - sizeof(void*);
#endif
info->mcontext->pc = addr;
return DR_SIGNAL_REDIRECT;
}
return DR_SIGNAL_DELIVER;
}
static
dr_signal_action_t signal_event1(void *dcontext, dr_siginfo_t *info)
{
inc_count_first(EVENT_SIGNAL_1, EVENT_SIGNAL_2);
if (info->sig == SIGUSR2)
return DR_SIGNAL_SUPPRESS;
else if (info->sig == SIGURG) {
if (!dr_unregister_signal_event(signal_event1))
dr_fprintf(STDERR, "unregister failed!\n");
dr_register_signal_event(signal_event_redirect);
return DR_SIGNAL_BYPASS;
}
return DR_SIGNAL_DELIVER;
}
static
dr_signal_action_t signal_event2(void *dcontext, dr_siginfo_t *info)
{
inc_count_second(EVENT_SIGNAL_2);
if (!dr_unregister_signal_event(signal_event2))
dr_fprintf(STDERR, "unregister failed!\n");
return DR_SIGNAL_DELIVER;
}
#endif /* WINDOWS */
static void
restore_state_event1(void *drcontext, void *tag, dr_mcontext_t *mcontext,
bool restore_memory, bool app_code_consistent)
{
inc_count_first(EVENT_RESTORE_STATE_1, EVENT_RESTORE_STATE_2);
if (!dr_unregister_restore_state_event(restore_state_event1))
dr_fprintf(STDERR, "unregister failed!\n");
}
static void
restore_state_event2(void *drcontext, void *tag, dr_mcontext_t *mcontext,
bool restore_memory, bool app_code_consistent)
{
inc_count_second(EVENT_RESTORE_STATE_2);
if (!dr_unregister_restore_state_event(restore_state_event2))
dr_fprintf(STDERR, "unregister failed!\n");
}
static bool
restore_state_ex_event1(void *drcontext, bool restore_memory,
dr_restore_state_info_t *info)
{
/* i#488: test bool compatibility */
bool is_trace = info->fragment_info.is_trace;
if (sizeof(is_trace) != sizeof(info->fragment_info.is_trace))
dr_fprintf(STDERR, "bool size incompatibility %d!\n", is_trace/*force ref*/);
inc_count_first(EVENT_RESTORE_STATE_EX_1, EVENT_RESTORE_STATE_EX_2);
if (!dr_unregister_restore_state_ex_event(restore_state_ex_event1))
dr_fprintf(STDERR, "unregister failed!\n");
return true;
}
static bool
restore_state_ex_event2(void *drcontext, bool restore_memory,
dr_restore_state_info_t *info)
{
inc_count_second(EVENT_RESTORE_STATE_EX_2);
if (!dr_unregister_restore_state_ex_event(restore_state_ex_event2))
dr_fprintf(STDERR, "unregister failed!\n");
return true;
}
static size_t
event_persist_size(void *drcontext, void *perscxt, size_t file_offs,
void **user_data OUT)
{
return 0;
}
static bool
event_persist_patch(void *drcontext, void *perscxt,
byte *bb_start, size_t bb_size, void *user_data)
{
return true;
}
static bool
event_persist(void *drcontext, void *perscxt, file_t fd, void *user_data)
{
return true;
}
static bool
event_resurrect(void *drcontext, void *perscxt, byte **map INOUT)
{
return true;
}
DR_EXPORT
void dr_init(client_id_t id)
{
/* FIXME: we should test the nudge events as well, but that
* would require some extra stuff our testing infrastructure
* doesn't currently support.
*/
int i;
#ifdef WINDOWS
dr_os_version_info_t info = {sizeof(info),};
if (dr_is_notify_on())
dr_enable_console_printing();
/* a sanity check for console printing: no easy way to ensure it really
* prints to cmd w/o redirecting to a file which then ruins the test so we
* just make sure it doesn't mess up our broadest test, events.
*/
if (!dr_get_os_version(&info))
dr_fprintf(STDERR, "dr_get_os_version failed!\n");
#endif
for (i = 0; i < EVENT_last; i++)
counts[i] = 0;
mutex = dr_mutex_create();
dr_register_exit_event(exit_event1);
dr_register_exit_event(exit_event2);
dr_register_thread_init_event(thread_init_event1);
dr_register_thread_init_event(thread_init_event2);
dr_register_thread_exit_event(thread_exit_event1);
dr_register_thread_exit_event(thread_exit_event2);
#ifdef UNIX
dr_register_fork_init_event(fork_init_event1);
dr_register_fork_init_event(fork_init_event2);
#endif
dr_register_bb_event(bb_event1);
dr_register_bb_event(bb_event2);
dr_register_trace_event(trace_event1);
dr_register_trace_event(trace_event2);
dr_register_end_trace_event(end_trace_event1);
dr_register_end_trace_event(end_trace_event2);
dr_register_delete_event(delete_event1);
dr_register_delete_event(delete_event2);
dr_register_restore_state_event(restore_state_event1);
dr_register_restore_state_event(restore_state_event2);
dr_register_restore_state_ex_event(restore_state_ex_event1);
dr_register_restore_state_ex_event(restore_state_ex_event2);
dr_register_module_load_event(module_load_event_perm);
dr_register_module_load_event(module_load_event1);
dr_register_module_load_event(module_load_event2);
dr_register_module_unload_event(module_unload_event1);
dr_register_module_unload_event(module_unload_event2);
dr_register_pre_syscall_event(pre_syscall_event1);
dr_register_pre_syscall_event(pre_syscall_event2);
dr_register_post_syscall_event(post_syscall_event1);
dr_register_post_syscall_event(post_syscall_event2);
dr_register_filter_syscall_event(filter_syscall_event1);
dr_register_filter_syscall_event(filter_syscall_event2);
#ifdef WINDOWS
dr_register_exception_event(exception_event1);
dr_register_exception_event(exception_event2);
#else
dr_register_signal_event(signal_event1);
dr_register_signal_event(signal_event2);
#endif
if (!dr_register_persist_ro(event_persist_size, event_persist, event_resurrect))
dr_fprintf(STDERR, "failed to register for persist ro events");
if (!dr_register_persist_rx(event_persist_size, event_persist, event_resurrect))
dr_fprintf(STDERR, "failed to register for persist rx events");
if (!dr_register_persist_rw(event_persist_size, event_persist, event_resurrect))
dr_fprintf(STDERR, "failed to register for persist rw events");
if (!dr_register_persist_patch(event_persist_patch))
dr_fprintf(STDERR, "failed to register for persist patch event");
if (!dr_unregister_persist_ro(event_persist_size, event_persist, event_resurrect))
dr_fprintf(STDERR, "failed to unregister for persist ro events");
if (!dr_unregister_persist_rx(event_persist_size, event_persist, event_resurrect))
dr_fprintf(STDERR, "failed to unregister for persist rx events");
if (!dr_unregister_persist_rw(event_persist_size, event_persist, event_resurrect))
dr_fprintf(STDERR, "failed to unregister for persist rw events");
if (!dr_unregister_persist_patch(event_persist_patch))
dr_fprintf(STDERR, "failed to unregister for persist patch event");
}