/*--------------------------------------------------------------------*/ /*--- valtaxi: The cachetool interface. vt_main.c ---*/ /*--------------------------------------------------------------------*/ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The GNU General Public License is contained in the file COPYING. */ #include "pub_tool_basics.h" #include "pub_tool_libcassert.h" #include "pub_tool_tooliface.h" #include "pub_tool_libcprint.h" //#include "pub_tool_hashtable.h" #include "pub_tool_libcbase.h" //#include "pub_tool_threadstate.h" //#include "../VEX/pub/libvex_guest_x86.h" //#include "pub_core_threadstate.h" #include "../coregrind/pub_core_basics.h" #include "../coregrind/pub_core_threadstate.h" #include "pub_tool_libcfile.h" #include "pub_tool_options.h" #define BUFSIZ 256 int memtrace_ptr,insntrace_ptr; unsigned long taxi_tag=0; unsigned long new_insn=1; char temp_st[BUFSIZ]; long long fastforward=0; long long x86_instructions=0; int print_results=1; int magic_fastforward=0; long long count_magic=0,count_magic_total=0; /* Tell Valgrind this function has one parameter */ /* write load information to trace file*/ static VG_REGPARM(1) void print_Load (Addr a) { if (print_results) { if (new_insn) { VG_(sprintf)(temp_st,"%x:\n",taxi_tag); VG_(write)(memtrace_ptr,temp_st,VG_(strlen)(temp_st)); taxi_tag++; } VG_(sprintf)(temp_st,"0x%x 0x%x L\n",(unsigned long)a,0xdeadbeef); VG_(write)(memtrace_ptr,temp_st,VG_(strlen)(temp_st)); new_insn=0; } } /* write store information to trace file*/ static VG_REGPARM(1) void print_Store (Addr a) { if (print_results) { if (new_insn) { VG_(sprintf)(temp_st,"%x:\n",taxi_tag); VG_(write)(memtrace_ptr,temp_st,VG_(strlen)(temp_st)); taxi_tag++; } VG_(sprintf)(temp_st,"0x%x 0x%x S\n",(unsigned long)a,0xcafebabe); VG_(write)(memtrace_ptr,temp_st,VG_(strlen)(temp_st)); new_insn=0; } } static VG_REGPARM(2) void dump_insn2 (Addr address,int len) { int tid,i; x86_instructions++; if (!print_results) { if ((fastforward) && (x86_instructions>fastforward)) { print_results=1; } if (magic_fastforward) { if (len==2) { //VG_(printf)("Ins= %d %d\n", if ((*((unsigned char *)address)==0x89) && (*((unsigned char *)(address+1))==0xc9)) { count_magic--; if (count_magic==0) { print_results=1; VG_(printf)("\nDetected magic instruction %lld", count_magic_total); VG_(printf)(" after %lld instructions, starting trace...\n", x86_instructions); } } } } } else { new_insn=1; tid=VG_(get_running_tid)(); VG_(sprintf)(temp_st,"%04x:%08x: ", VG_(threads)[tid].arch.vex.guest_CS, (unsigned long)address); VG_(write)(insntrace_ptr,temp_st,VG_(strlen)(temp_st)); for(i=0;i<(int)len;i++) { VG_(sprintf)(temp_st,"%02x",*(((unsigned char *)address)+i)); VG_(write)(insntrace_ptr,temp_st,VG_(strlen)(temp_st)); } VG_(sprintf)(temp_st,": %x: %d 1\n", LibVEX_GuestX86_get_eflags(&(VG_(threads)[tid].arch.vex)), taxi_tag); VG_(write)(insntrace_ptr,temp_st,VG_(strlen)(temp_st)); } } /*------------------------------------------------------------*/ /*--- Our instrumenter ---*/ /*--- Translates the Basic Block passed in as "bb_in" ---*/ /*--- into a new "instrumented" basic block "bb" ---*/ /*------------------------------------------------------------*/ //from ac_main.c static IRBB* vt_instrument ( VgCallbackClosure* closure, IRBB* bb_in, VexGuestLayout* layout, VexGuestExtents* vge, IRType gWordTy, IRType hWordTy ) { Int i, access_size, insn_label; IRStmt* st; IRExpr* data; IRExpr *access_address; IRExpr* guard; IRDirty *di,*di2=NULL; Bool isLoad; IRBB* bb; IRExpr *insn_address,*insn_len; /* Create a new basic block */ /* We'll put all of the original instructions, plus our */ /* instrumentations into it, and return it back to valgrind */ /* create an empty basic block */ bb = emptyIRBB(); /* copy over configuration from the original basic block */ bb->tyenv = dopyIRTypeEnv(bb_in->tyenv); bb->next = dopyIRExpr(bb_in->next); bb->jumpkind = bb_in->jumpkind; /* Walk through each statement, */ /* from first (0) to last (bb_in->stmts_used) */ for (i = 0; i < bb_in->stmts_used; i++) { insn_label=0; st = bb_in->stmts[i]; /* clear these variables */ access_size = 0; access_address = NULL; guard = NULL; isLoad = True; switch (st->tag) { /* Ist_Tmp means we are copying data into a */ /* "Temporary" register */ case Ist_Tmp: data = st->Ist.Tmp.data; /* We only care if it's a load instruction */ if (data->tag == Iex_Load) { access_address = data->Iex.Load.addr; access_size = sizeofIRType(data->Iex.Load.ty); isLoad = True; } break; /* Ist_Store means we are storing data */ case Ist_Store: data = st->Ist.Store.data; access_address = st->Ist.Store.addr; access_size = sizeofIRType(typeOfIRExpr(bb_in->tyenv, data)); isLoad = False; break; /* We ignore these */ case Ist_Put: /* We are copying some "guest state" */ case Ist_PutI:/* We are copying some "guest state" */ case Ist_Exit:/* We are conditionally leaving a basic block */ case Ist_NoOp: case Ist_MFence: break; case Ist_IMark: insn_address=mkIRExpr_HWord (st->Ist.IMark.addr); insn_len = mkIRExpr_HWord (st->Ist.IMark.len); di2 = unsafeIRDirty_0_N( 2, "dump_insn2", &dump_insn2, mkIRExprVec_2(insn_address, insn_len)); insn_label=1; break; /* ??????? *//* We are in a "dirty" function? */ case Ist_Dirty: if (st->Ist.Dirty.details->mFx != Ifx_None) { /* We classify Ifx_Modify as a load. */ isLoad = st->Ist.Dirty.details->mFx != Ifx_Write; access_size = st->Ist.Dirty.details->mSize; access_address = st->Ist.Dirty.details->mAddr; guard = st->Ist.Dirty.details->guard; } break; /* Print an error if an unknown statement type */ default: VG_(printf)("\n"); ppIRStmt(st); VG_(printf)("\n"); VG_(tool_panic)("unhandled IRStmt"); break; } /* If we were a load or store, add a call to print it */ if (access_address) { if (isLoad) { /* Create a new "instruction" called "di" */ /* This is a dirty instruction, meaning it has side effects */ /* the "0" means we don't expect a return value */ /* the "N" means we can pass many arguments */ /* We pass 1 argument, the name of the function, */ /* a pointer to the function, and an "argument vector" */ /* which in this case only has one, the address */ di = unsafeIRDirty_0_N( 1, "print_Load", &print_Load, mkIRExprVec_1(access_address)); } else { di = unsafeIRDirty_0_N( 1, "print_Store", &print_Store, mkIRExprVec_1(access_address)); } /* If the call has arisen as a result of a dirty helper which references memory, we need to inherit the guard from the dirty helper. */ /* ??? */ if (guard) { di->guard = dopyIRExpr(guard); } /* put the helper call into the new Basic Block */ /* before the load or store */ addStmtToIRBB( bb, IRStmt_Dirty(di) ); } if (insn_label) addStmtToIRBB( bb, IRStmt_Dirty(di2) ); /* Make sure the original instruction gets added to the basic block. */ addStmtToIRBB( bb, st ); } return bb; } char mem_filename[BUFSIZ]; char cpu_filename[BUFSIZ]; static void vt_post_clo_init(void) { SysRes sysr; sysr=VG_(open)(cpu_filename, VKI_O_CREAT|VKI_O_TRUNC|VKI_O_WRONLY|VKI_O_LARGEFILE, VKI_S_IRUSR|VKI_S_IWUSR); if (sysr.isError) { VG_(printf)("file can not be opened"); } else { VG_(printf)("trace file %s opened\n",cpu_filename); } insntrace_ptr=sysr.val; sysr=VG_(open)(mem_filename, VKI_O_CREAT|VKI_O_TRUNC|VKI_O_WRONLY|VKI_O_LARGEFILE, VKI_S_IRUSR|VKI_S_IWUSR); if (sysr.isError) { VG_(printf)("file can not be opened"); } else { VG_(printf)("trace file %s opened\n",mem_filename); } memtrace_ptr=sysr.val; VG_(printf)("header written\n"); } static void vt_fini(Int exitcode) { VG_(close)(memtrace_ptr); VG_(close)(insntrace_ptr); VG_(printf) ("\n\nValtaxi:Exiting! \n\n"); } static Bool vt_process_cmd_line_option(Char* arg) { // 11 is length of "--tracemem=" if (VG_CLO_STREQN(11, arg, "--tracemem=")) { VG_(sprintf)(mem_filename,"%s",&arg[11]); } else if (VG_CLO_STREQN(11, arg, "--tracecpu=")) { VG_(sprintf)(cpu_filename,"%s",&arg[11]); } else if (VG_CLO_STREQN(14, arg, "--fastforward=")) { fastforward=VG_(atoll)(&arg[14]); print_results=0; fastforward*=100000000LL; VG_(printf)("Fastforwarding %lld instructions...\n",fastforward); } else if (VG_CLO_STREQN(18, arg, "--magicfastforward")) { print_results=0; magic_fastforward=1; count_magic_total=1; count_magic=1; VG_(printf)("Waiting until magic instruction...\n"); } else if (VG_CLO_STREQN(13, arg, "--countmagic=")) { print_results=0; magic_fastforward=1; count_magic=VG_(atoll)(&arg[13]); count_magic_total=count_magic; VG_(printf)("Looking for magic instruction %d times\n",count_magic); } else { return False; } return True; } static void vt_print_usage(void) { VG_(printf)( " --tracecpu= file for cpu trace file\n" " --tracemem= file for mem trace file\n" " --fastforward= number of insns(*100 000 000) to ffwd\n" " --countmagic= ffwd until see the magic insn times\n" ); } static void vt_print_debug_usage(void) { VG_(printf)( " (none)\n" ); } static void vt_pre_clo_init(void) { VG_(details_name) ("valtaxi"); VG_(details_version) (NULL); VG_(details_description) ("a frontend for TAXI"); VG_(details_copyright_author)("Copyright (C) 2006 - Vince Weaver"); VG_(details_bug_reports_to) (VG_BUGS_TO); /* set up default output files */ VG_(sprintf)(mem_filename,"/tmp/mem_state.txt"); VG_(sprintf)(cpu_filename,"/tmp/trace_state.txt"); VG_(needs_command_line_options)(vt_process_cmd_line_option, vt_print_usage, vt_print_debug_usage); VG_(basic_tool_funcs) (vt_post_clo_init, vt_instrument, vt_fini); } VG_DETERMINE_INTERFACE_VERSION(vt_pre_clo_init) /*--------------------------------------------------------------------*/ /*--- end ---*/ /*--------------------------------------------------------------------*/