| /* ********************************************************** |
| * Copyright (c) 2011-2014 Google, Inc. All rights reserved. |
| * Copyright (c) 2007-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. |
| */ |
| |
| #include "dr_api.h" |
| #include "client_tools.h" |
| #ifdef UNIX |
| # include <sys/time.h> |
| #endif |
| |
| #ifdef WINDOWS |
| # define THREAD_ARG ((void *) dr_get_process_id()) |
| #else |
| /* thread actually has own pid so just using a constant to test arg passing */ |
| # define THREAD_ARG ((void *)37) |
| #endif |
| |
| /* Eventually this routine will test i/o by waiting on a file */ |
| |
| #define MINSERT instrlist_meta_preinsert |
| |
| static uint num_lea = 0; |
| |
| static uint tls_offs; |
| #define CANARY 0xbadcab42 |
| #define NUM_TLS_SLOTS 4 |
| |
| #ifdef X64 |
| # define ASM_XAX "rax" |
| # define ASM_XDX "rdx" |
| # define ASM_XBP "rbp" |
| # define ASM_XSP "rsp" |
| # define ASM_SEG "gs" |
| #else |
| # define ASM_XAX "eax" |
| # define ASM_XDX "edx" |
| # define ASM_XBP "ebp" |
| # define ASM_XSP "esp" |
| # define ASM_SEG "fs" |
| #endif |
| |
| static bool child_alive; |
| static bool child_continue; |
| static bool child_dead; |
| static bool nops_matched; |
| |
| #ifdef UNIX |
| /* test PR 368737: add client timer support */ |
| static void |
| event_timer(void *drcontext, dr_mcontext_t *mcontext) |
| { |
| dr_fprintf(STDERR, "event_timer fired\n"); |
| if (!dr_set_itimer(ITIMER_REAL, 0, event_timer)) |
| dr_fprintf(STDERR, "unable to disable timer\n"); |
| } |
| #endif |
| |
| static void |
| thread_func(void *arg) |
| { |
| /* FIXME: should really test corner cases: do raw system calls, etc. to |
| * ensure we're treating it as a true native thread |
| */ |
| ASSERT(arg == THREAD_ARG); |
| child_alive = true; |
| dr_fprintf(STDERR, "client thread is alive\n"); |
| #ifdef UNIX |
| if (!dr_set_itimer(ITIMER_REAL, 10, event_timer)) |
| dr_fprintf(STDERR, "unable to set timer callback\n"); |
| dr_sleep(30); |
| #endif |
| /* FIXME i#279: do we now have to provide condition vars, etc.?!? */ |
| while (!child_continue) |
| dr_thread_yield(); |
| dr_fprintf(STDERR, "client thread is dying\n"); |
| child_dead = true; |
| } |
| |
| static void |
| at_lea(uint opc, app_pc tag) |
| { |
| /* PR 223285: test (one side of) DR_ASSERT for something we know will succeed |
| * (we don't want msgboxes in regressions) |
| */ |
| DR_ASSERT(opc == OP_lea); |
| ASSERT((process_id_t)(ptr_uint_t) dr_get_tls_field(dr_get_current_drcontext()) == |
| dr_get_process_id() + 1 /*we added 1 inline*/); |
| dr_set_tls_field(dr_get_current_drcontext(), |
| (void *)(ptr_uint_t) dr_get_process_id()); |
| num_lea++; |
| /* FIXME: should do some fp ops and really test the fp state preservation */ |
| } |
| |
| static dr_emit_flags_t |
| bb_event(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) |
| { |
| instr_t *instr, *next_instr; |
| int num_nops = 0; |
| bool in_nops = false; |
| |
| for (instr = instrlist_first(bb); |
| instr != NULL; instr = next_instr) { |
| next_instr = instr_get_next(instr); |
| if (instr_get_opcode(instr) == OP_lea) { |
| /* PR 200411: test inline tls access by adding 1 */ |
| dr_save_reg(drcontext, bb, instr, REG_XAX, SPILL_SLOT_1); |
| dr_insert_read_tls_field(drcontext, bb, instr, REG_XAX); |
| instrlist_meta_preinsert(bb, instr, INSTR_CREATE_lea |
| (drcontext, opnd_create_reg(REG_XAX), |
| opnd_create_base_disp(REG_XAX, REG_NULL, 0, 1, |
| OPSZ_lea))); |
| dr_insert_write_tls_field(drcontext, bb, instr, REG_XAX); |
| dr_restore_reg(drcontext, bb, instr, REG_XAX, SPILL_SLOT_1); |
| dr_insert_clean_call(drcontext, bb, instr, at_lea, true/*save fp*/, |
| 2, OPND_CREATE_INT32(instr_get_opcode(instr)), |
| OPND_CREATE_INTPTR(tag)); |
| } |
| if (instr_get_opcode(instr) == OP_nop) { |
| if (!in_nops) { |
| in_nops = true; |
| num_nops = 1; |
| } else |
| num_nops++; |
| } else |
| in_nops = false; |
| } |
| if (num_nops == 9 && !nops_matched) { |
| /* PR 210591: test transparency by having client create a thread after |
| * app has loaded a library and ensure its DllMain is not notified |
| */ |
| bool success; |
| nops_matched = true; |
| /* reset cond vars */ |
| child_alive = false; |
| child_continue = false; |
| child_dead = false; |
| dr_fprintf(STDERR, "PR 210591: testing client transparency\n"); |
| success = dr_create_client_thread(thread_func, THREAD_ARG); |
| ASSERT(success); |
| while (!child_alive) |
| dr_thread_yield(); |
| /* We leave the client thread alive until the app exits, to test i#1489 */ |
| } |
| return DR_EMIT_DEFAULT; |
| } |
| |
| void exit_event(void) |
| { |
| bool success = dr_raw_tls_cfree(tls_offs, NUM_TLS_SLOTS); |
| ASSERT(success); |
| ASSERT(num_lea > 0); |
| /* DR should have terminated the client thread for us */ |
| } |
| |
| static bool |
| str_eq(const char *s1, const char *s2) |
| { |
| if (s1 == NULL || s2 == NULL) |
| return false; |
| while (*s1 == *s2) { |
| if (*s1 == '\0') |
| return true; |
| s1++; |
| s2++; |
| } |
| return false; |
| } |
| |
| static void |
| thread_init_event(void *drcontext) |
| { |
| int i; |
| for (i = 0; i < NUM_TLS_SLOTS; i++) { |
| int idx = tls_offs + i*sizeof(void*); |
| ptr_uint_t val = (ptr_uint_t) (CANARY+i); |
| #ifdef WINDOWS |
| IF_X64_ELSE(__writegsqword,__writefsdword)(idx, val); |
| #else |
| asm("mov %0, %%"ASM_XAX: : "m"((val)) : ASM_XAX); |
| asm("mov %0, %%edx" : : "m"((idx)) : ASM_XDX); |
| asm("mov %%"ASM_XAX", %%"ASM_SEG":(%%"ASM_XDX")" : : : ASM_XAX, ASM_XDX); |
| #endif |
| } |
| } |
| |
| static void |
| thread_exit_event(void *drcontext) |
| { |
| int i; |
| for (i = 0; i < NUM_TLS_SLOTS; i++) { |
| int idx = tls_offs + i*sizeof(void*); |
| ptr_uint_t val; |
| #ifdef WINDOWS |
| val = IF_X64_ELSE(__readgsqword,__readfsdword)(idx); |
| #else |
| asm("mov %0, %%eax": : "m"((idx)) : ASM_XAX); |
| asm("mov %%"ASM_SEG":(%%"ASM_XAX"), %%"ASM_XAX : : : ASM_XAX); |
| asm("mov %%"ASM_XAX", %0" : "=m"((val)) : : ASM_XAX); |
| #endif |
| dr_fprintf(STDERR, "TLS slot %d is "PFX"\n", i, val); |
| } |
| } |
| |
| DR_EXPORT |
| void dr_init(client_id_t id) |
| { |
| bool success; |
| reg_id_t seg; |
| /* PR 216931: client options */ |
| const char * ops = dr_get_options(id); |
| ASSERT(str_eq(ops, "-paramx -paramy")); |
| dr_fprintf(STDERR, "PR 216931: client options are %s\n", ops); |
| |
| dr_register_bb_event(bb_event); |
| dr_register_exit_event(exit_event); |
| dr_register_thread_init_event(thread_init_event); |
| dr_register_thread_exit_event(thread_exit_event); |
| |
| /* i#108: client raw TLS */ |
| success = dr_raw_tls_calloc(&seg, &tls_offs, NUM_TLS_SLOTS, 0); |
| ASSERT(success); |
| ASSERT(seg == IF_X64_ELSE(SEG_GS, SEG_FS)); |
| |
| /* PR 219381: dr_get_application_name() and dr_get_process_id() */ |
| #ifdef WINDOWS |
| dr_fprintf(STDERR, "inside app %s\n", dr_get_application_name()); |
| #else /* UNIX - append .exe so can use same expect file. */ |
| dr_fprintf(STDERR, "inside app %s.exe\n", dr_get_application_name()); |
| #endif |
| dr_set_tls_field(dr_get_current_drcontext(), |
| (void *)(ptr_uint_t) dr_get_process_id()); |
| |
| { |
| /* test PR 198871: client locks are all at same rank */ |
| void *lock1 = dr_mutex_create(); |
| void *lock2 = dr_mutex_create(); |
| dr_mutex_lock(lock1); |
| dr_mutex_lock(lock2); |
| dr_fprintf(STDERR, "PR 198871 locking test..."); |
| dr_mutex_unlock(lock2); |
| dr_mutex_unlock(lock1); |
| dr_mutex_destroy(lock1); |
| dr_mutex_destroy(lock2); |
| dr_fprintf(STDERR, "...passed\n"); |
| } |
| |
| /* PR 222812: start up and shut down a client thread */ |
| success = dr_create_client_thread(thread_func, THREAD_ARG); |
| ASSERT(success); |
| while (!child_alive) |
| dr_thread_yield(); |
| child_continue = true; |
| while (!child_dead) |
| dr_thread_yield(); |
| dr_fprintf(STDERR, "PR 222812: client thread test passed\n"); |
| } |