Add exp-bbv to the tool-suite.  I'm seeing a couple of amd64-linux test
failures, but they can be fixed up in-repo.  This resolves bug 198395.



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@10444 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/exp-bbv/bbv_main.c b/exp-bbv/bbv_main.c
new file mode 100644
index 0000000..b5db191
--- /dev/null
+++ b/exp-bbv/bbv_main.c
@@ -0,0 +1,633 @@
+//--------------------------------------------------------------------*/
+//--- BBV: a SimPoint basic block vector generator      bbv_main.c ---*/
+//--------------------------------------------------------------------*/
+
+/*
+   This file is part of BBV, a Valgrind tool for generating SimPoint
+   basic block vectors.
+
+   Copyright (C) 2006-2009 Vince Weaver
+      vince _at_ csl.cornell.edu
+
+   pcfile code is Copyright (C) 2006-2009 Oriol Prat
+      oriol.prat _at _ bsc.es
+
+   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_tooliface.h"
+#include "pub_tool_options.h"    /* command line options */
+
+#include "pub_tool_vki.h"        /* vki_stat */
+#include "pub_tool_libcbase.h"   /* VG_(strlen) */
+#include "pub_tool_libcfile.h"   /* VG_(write) */
+#include "pub_tool_libcprint.h"  /* VG_(printf) */
+#include "pub_tool_libcassert.h" /* VG_(exit) */
+#include "pub_tool_mallocfree.h" /* plain_free */
+#include "pub_tool_machine.h"    /* VG_(fnptr_to_fnentry) */
+#include "pub_tool_debuginfo.h"  /* VG_(get_fnname) */
+
+#include "pub_tool_oset.h"       /* ordered set stuff */
+
+   /* instruction special cases */
+#define REP_INSTRUCTION   0x1
+#define FLDCW_INSTRUCTION 0x2
+
+   /* interval variables */
+#define DEFAULT_GRAIN_SIZE 100000000  /* 100 million by default */
+static Int interval_size=DEFAULT_GRAIN_SIZE;
+
+   /* filenames */
+static UChar *clo_bb_out_file="bb.out.%p";
+static UChar *clo_pc_out_file="pc.out.%p";
+static UChar *pc_out_file=NULL;
+static UChar *bb_out_file=NULL;
+
+
+   /* output parameters */
+static Bool instr_count_only=False;
+static Bool generate_pc_file=False;
+
+   /* write buffer */
+static UChar buf[1024];
+
+   /* Global values */
+static OSet* instr_info_table;  /* table that holds the basic block info */
+static Int block_num=1;         /* global next block number */
+static Int current_thread=0;
+static Int allocated_threads=1;
+struct thread_info *bbv_thread=NULL;
+
+   /* Per-thread variables */
+struct thread_info {
+   ULong dyn_instr;         /* Current retired instruction count */
+   ULong total_instr;       /* Total retired instruction count   */
+   Addr last_rep_addr;      /* rep counting values */
+   ULong rep_count;
+   ULong global_rep_count;
+   ULong unique_rep_count;
+   ULong fldcw_count;       /* fldcw count */
+   Int bbtrace_fd;          /* file descriptor */
+};
+
+#define FUNCTION_NAME_LENGTH 20
+
+struct BB_info {
+   Addr       BB_addr;           /* used as key, must be first           */
+   Int        n_instrs;          /* instructions in the basic block      */
+   Int        block_num;         /* unique block identifier              */
+   Int        *inst_counter;     /* times entered * num_instructions     */
+   Bool       is_entry;          /* is this block a function entry point */
+   UChar      fn_name[FUNCTION_NAME_LENGTH];  /* Function block is in    */
+};
+
+
+   /* dump the optional PC file, which contains basic block number to */
+   /*   instruction address and function name mappings                */
+static void dumpPcFile(void)
+{
+   struct BB_info   *bb_elem;
+   Int              pctrace_fd;
+   SysRes           sres;
+
+   pc_out_file =
+          VG_(expand_file_name)("--pc-out-file", clo_pc_out_file);
+
+   sres = VG_(open)(pc_out_file, VKI_O_CREAT|VKI_O_TRUNC|VKI_O_WRONLY,
+                              VKI_S_IRUSR|VKI_S_IWUSR|VKI_S_IRGRP|VKI_S_IWGRP);
+   if (sr_isError(sres)) {
+      VG_UMSG("Error: cannot create pc file %s\n", pc_out_file);
+      VG_(exit)(1);
+   } else {
+      pctrace_fd = sr_Res(sres);
+   }
+
+      /* Loop through the table, printing the number, address, */
+      /*    and function name for each basic block             */
+   VG_(OSetGen_ResetIter)(instr_info_table);
+   while ( (bb_elem = VG_(OSetGen_Next)(instr_info_table)) ) {
+      VG_(write)(pctrace_fd,"F",1);
+      VG_(sprintf)( buf,":%d:%x:%s\n",
+                       bb_elem->block_num,
+                       (Int)bb_elem->BB_addr,
+                       bb_elem->fn_name);
+      VG_(write)(pctrace_fd, (void*)buf, VG_(strlen)(buf));
+   }
+
+   VG_(close)(pctrace_fd);
+}
+
+static Int open_tracefile(Int thread_num)
+{
+   SysRes  sres;
+   UChar temp_string[2048];
+
+      /* For thread 1, don't append any thread number  */
+      /* This lets the single-thread case not have any */
+      /* extra values appended to the file name.       */
+   if (thread_num==1) {
+      VG_(strncpy)(temp_string,bb_out_file,2047);
+   }
+   else {
+      VG_(sprintf)(temp_string,"%s.%d",bb_out_file,thread_num);
+   }
+
+   sres = VG_(open)(temp_string, VKI_O_CREAT|VKI_O_TRUNC|VKI_O_WRONLY,
+                              VKI_S_IRUSR|VKI_S_IWUSR|VKI_S_IRGRP|VKI_S_IWGRP);
+
+   if (sr_isError(sres)) {
+      VG_UMSG("Error: cannot create bb file %s\n",temp_string);
+      VG_(exit)(1);
+   }
+
+   return sr_Res(sres);
+}
+
+static void handle_overflow(void)
+{
+   struct BB_info *bb_elem;
+
+   if (bbv_thread[current_thread].dyn_instr > interval_size) {
+
+      if (!instr_count_only) {
+
+            /* If our output fd hasn't been opened, open it */
+         if (bbv_thread[current_thread].bbtrace_fd < 0) {
+            bbv_thread[current_thread].bbtrace_fd=open_tracefile(current_thread);
+         }
+
+           /* put an entry to the bb.out file */
+
+         VG_(write)(bbv_thread[current_thread].bbtrace_fd,"T",1);
+
+         VG_(OSetGen_ResetIter)(instr_info_table);
+         while ( (bb_elem = VG_(OSetGen_Next)(instr_info_table)) ) {
+            if ( bb_elem->inst_counter[current_thread] != 0 ) {
+               VG_(sprintf)( buf,":%d:%d   ",
+                         bb_elem->block_num,
+                         bb_elem->inst_counter[current_thread]);
+               VG_(write)(bbv_thread[current_thread].bbtrace_fd,
+                          (void*)buf, VG_(strlen)(buf));
+               bb_elem->inst_counter[current_thread] = 0;
+            }
+         }
+
+         VG_(write)(bbv_thread[current_thread].bbtrace_fd,"\n",1);
+      }
+
+      bbv_thread[current_thread].dyn_instr -= interval_size;
+   }
+}
+
+
+static void close_out_reps(void)
+{
+   bbv_thread[current_thread].global_rep_count+=bbv_thread[current_thread].rep_count;
+   bbv_thread[current_thread].unique_rep_count++;
+   bbv_thread[current_thread].rep_count=0;
+}
+
+   /* Generic function to get called each instruction */
+static VG_REGPARM(1) void per_instruction_BBV(struct BB_info *bbInfo)
+{
+   Int n_instrs=1;
+
+   tl_assert(bbInfo);
+
+      /* we finished rep but didn't clear out count */
+   if (bbv_thread[current_thread].rep_count) {
+      n_instrs++;
+      close_out_reps();
+   }
+
+   bbInfo->inst_counter[current_thread]+=n_instrs;
+
+   bbv_thread[current_thread].total_instr+=n_instrs;
+   bbv_thread[current_thread].dyn_instr +=n_instrs;
+
+   handle_overflow();
+}
+
+   /* Function to get called if instruction has a rep prefix */
+static VG_REGPARM(1) void per_instruction_BBV_rep(Addr addr)
+{
+      /* handle back-to-back rep instructions */
+   if (bbv_thread[current_thread].last_rep_addr!=addr) {
+      if (bbv_thread[current_thread].rep_count) {
+         close_out_reps();
+         bbv_thread[current_thread].total_instr++;
+         bbv_thread[current_thread].dyn_instr++;
+      }
+      bbv_thread[current_thread].last_rep_addr=addr;
+   }
+
+   bbv_thread[current_thread].rep_count++;
+
+}
+
+   /* Function to call if our instruction has a fldcw instruction */
+static VG_REGPARM(1) void per_instruction_BBV_fldcw(struct BB_info *bbInfo)
+{
+   Int n_instrs=1;
+
+   tl_assert(bbInfo);
+
+      /* we finished rep but didn't clear out count */
+   if (bbv_thread[current_thread].rep_count) {
+      n_instrs++;
+      close_out_reps();
+   }
+
+      /* count fldcw instructions */
+   bbv_thread[current_thread].fldcw_count++;
+
+   bbInfo->inst_counter[current_thread]+=n_instrs;
+
+   bbv_thread[current_thread].total_instr+=n_instrs;
+   bbv_thread[current_thread].dyn_instr +=n_instrs;
+
+   handle_overflow();
+}
+
+   /* Check if the instruction pointed to is one that needs */
+   /*   special handling.  If so, set a bit in the return   */
+   /*   value indicating what type.                         */
+static Int get_inst_type(Int len, Addr addr)
+{
+   int result=0;
+
+#if defined(VGA_x86) || defined(VGA_amd64)
+
+   unsigned char *inst_pointer;
+   unsigned char inst_byte;
+   int i,possible_rep;
+
+   /* rep prefixed instructions are counted as one instruction on */
+   /*     x86 processors and must be handled as a special case    */
+
+   /* Also, the rep prefix is re-used as part of the opcode for   */
+   /*     SSE instructions.  So we need to specifically check for */
+   /*     the following: movs, cmps, scas, lods, stos, ins, outs  */
+
+   inst_pointer=(unsigned char *)addr;
+   i=0;
+   inst_byte=0;
+   possible_rep=0;
+
+   while (i<len) {
+
+      inst_byte=*inst_pointer;
+
+      if ( (inst_byte == 0x67) ||            /* size override prefix */
+           (inst_byte == 0x66) ||            /* size override prefix */
+           (inst_byte == 0x48) ) {           /* 64-bit prefix */
+      } else if ( (inst_byte == 0xf2) ||     /* rep prefix    */
+                  (inst_byte == 0xf3) ) {    /* repne prefix  */
+         possible_rep=1;
+      } else {
+         break;                              /* other byte, exit */
+      }
+
+      i++;
+      inst_pointer++;
+   }
+
+   if ( possible_rep &&
+        ( ( (inst_byte >= 0xa4) &&     /* movs,cmps,scas */
+            (inst_byte <= 0xaf) ) ||   /* lods,stos      */
+          ( (inst_byte >= 0x6c) &&
+            (inst_byte <= 0x6f) ) ) ) {  /* ins,outs       */
+
+      result|=REP_INSTRUCTION;
+   }
+
+   /* fldcw instructions are double-counted by the hardware       */
+   /*     performance counters on pentium 4 processors so it is   */
+   /*     useful to have that count when doing validation work.   */
+
+   inst_pointer=(unsigned char *)addr;
+   if (len>1) {
+         /* FLDCW detection */
+         /* opcode is 0xd9/5, ie 1101 1001 oo10 1mmm */
+      if ((*inst_pointer==0xd9) &&
+          (*(inst_pointer+1)<0xb0) &&  /* need this case of fldz, etc, count */
+          ( (*(inst_pointer+1) & 0x38) == 0x28)) {
+         result|=FLDCW_INSTRUCTION;
+      }
+   }
+
+#endif
+   return result;
+}
+
+
+
+   /* Our instrumentation function       */
+   /*    sbIn = super block to translate */
+   /*    layout = guest layout           */
+   /*    gWordTy = size of guest word    */
+   /*    hWordTy = size of host word     */
+static IRSB* bbv_instrument ( VgCallbackClosure* closure,
+                             IRSB* sbIn, VexGuestLayout* layout,
+                             VexGuestExtents* vge,
+                             IRType gWordTy, IRType hWordTy )
+{
+   Int      i,n_instrs=1;
+   IRSB     *sbOut;
+   IRStmt   *st;
+   struct BB_info  *bbInfo;
+   Addr64   origAddr,ourAddr;
+   IRDirty  *di;
+   IRExpr   **argv, *arg1;
+   Int      regparms,opcode_type;
+
+      /* We don't handle a host/guest word size mismatch */
+   if (gWordTy != hWordTy) {
+      VG_(tool_panic)("host/guest word size mismatch");
+   }
+
+      /* Set up SB */
+   sbOut = deepCopyIRSBExceptStmts(sbIn);
+
+      /* Copy verbatim any IR preamble preceding the first IMark */
+   i = 0;
+   while ( (i < sbIn->stmts_used) && (sbIn->stmts[i]->tag!=Ist_IMark)) {
+      addStmtToIRSB( sbOut, sbIn->stmts[i] );
+      i++;
+   }
+
+      /* Get the first statement */
+   tl_assert(sbIn->stmts_used > 0);
+   st = sbIn->stmts[i];
+
+      /* double check we are at a Mark statement */
+   tl_assert(Ist_IMark == st->tag);
+
+   origAddr=st->Ist.IMark.addr;
+
+      /* Get the BB_info */
+   bbInfo = VG_(OSetGen_Lookup)(instr_info_table, &origAddr);
+
+   if (bbInfo==NULL) {
+
+         /* BB never translated before (at this address, at least;          */
+         /* could have been unloaded and then reloaded elsewhere in memory) */
+
+         /* allocate and initialize a new basic block structure */
+      bbInfo=VG_(OSetGen_AllocNode)(instr_info_table, sizeof(struct BB_info));
+      bbInfo->BB_addr = origAddr;
+      bbInfo->n_instrs = n_instrs;
+      bbInfo->inst_counter=VG_(calloc)("bbv_instrument",
+                                       allocated_threads,
+                                       sizeof(Int));
+
+         /* assign a unique block number */
+      bbInfo->block_num=block_num;
+      block_num++;
+         /* get function name and entry point information */
+      VG_(get_fnname)(origAddr,bbInfo->fn_name,FUNCTION_NAME_LENGTH);
+      bbInfo->is_entry=VG_(get_fnname_if_entry)(origAddr, bbInfo->fn_name,
+                                                FUNCTION_NAME_LENGTH);
+         /* insert structure into table */
+      VG_(OSetGen_Insert)( instr_info_table, bbInfo );
+   }
+
+      /* Iterate through the basic block, putting the original   */
+      /* instructions in place, plus putting a call to updateBBV */
+      /* for each original instruction                           */
+
+      /* This is less efficient than only instrumenting the BB   */
+      /* But it gives proper results given the fact that         */
+      /* valgrind uses superblocks (not basic blocks) by default */
+
+
+   while(i < sbIn->stmts_used) {
+      st=sbIn->stmts[i];
+
+      if (st->tag == Ist_IMark) {
+
+         ourAddr = st->Ist.IMark.addr;
+
+         opcode_type=get_inst_type(st->Ist.IMark.len,ourAddr);
+
+         regparms=1;
+         arg1= mkIRExpr_HWord( (HWord)bbInfo);
+         argv= mkIRExprVec_1(arg1);
+
+
+         if (opcode_type&REP_INSTRUCTION) {
+            arg1= mkIRExpr_HWord(ourAddr);
+            argv= mkIRExprVec_1(arg1);
+            di= unsafeIRDirty_0_N( regparms, "per_instruction_BBV_rep",
+                                VG_(fnptr_to_fnentry)( &per_instruction_BBV_rep ),
+                                argv);
+         }
+         else if (opcode_type&FLDCW_INSTRUCTION) {
+            di= unsafeIRDirty_0_N( regparms, "per_instruction_BBV_fldcw",
+                                VG_(fnptr_to_fnentry)( &per_instruction_BBV_fldcw ),
+                                argv);
+         }
+         else {
+         di= unsafeIRDirty_0_N( regparms, "per_instruction_BBV",
+                                VG_(fnptr_to_fnentry)( &per_instruction_BBV ),
+                                argv);
+         }
+
+
+            /* Insert our call */
+         addStmtToIRSB( sbOut,  IRStmt_Dirty(di));
+      }
+
+         /* Insert the original instruction */
+      addStmtToIRSB( sbOut, st );
+
+      i++;
+   }
+
+   return sbOut;
+}
+
+static struct thread_info *allocate_new_thread(struct thread_info *old,
+                                     Int old_number, Int new_number)
+{
+   struct thread_info *temp;
+   struct BB_info   *bb_elem;
+   Int i;
+
+   temp=VG_(realloc)("bbv_main.c allocate_threads",
+                     old,
+                     new_number*sizeof(struct thread_info));
+
+      /* init the new thread */
+      /* We loop in case the new thread is not contiguous */
+   for(i=old_number;i<new_number;i++) {
+      temp[i].last_rep_addr=0;
+      temp[i].dyn_instr=0;
+      temp[i].total_instr=0;
+      temp[i].global_rep_count=0;
+      temp[i].unique_rep_count=0;
+      temp[i].rep_count=0;
+      temp[i].fldcw_count=0;
+      temp[i].bbtrace_fd=-1;
+   }
+      /* expand the inst_counter on all allocated basic blocks */
+   VG_(OSetGen_ResetIter)(instr_info_table);
+   while ( (bb_elem = VG_(OSetGen_Next)(instr_info_table)) ) {
+      bb_elem->inst_counter =
+                    VG_(realloc)("bbv_main.c inst_counter",
+                                 bb_elem->inst_counter,
+                                 new_number*sizeof(Int));
+      for(i=old_number;i<new_number;i++) {
+         bb_elem->inst_counter[i]=0;
+      }
+   }
+
+   return temp;
+}
+
+static void bbv_thread_called ( ThreadId tid, ULong nDisp )
+{
+   if (tid >= allocated_threads) {
+      bbv_thread=allocate_new_thread(bbv_thread,allocated_threads,tid+1);
+      allocated_threads=tid+1;
+   }
+   current_thread=tid;
+}
+
+
+
+
+/*--------------------------------------------------------------------*/
+/*--- Setup                                                        ---*/
+/*--------------------------------------------------------------------*/
+
+static void bbv_post_clo_init(void)
+{
+   bb_out_file =
+          VG_(expand_file_name)("--bb-out-file", clo_bb_out_file);
+
+      /* Try a closer approximation of basic blocks  */
+      /* This is the same as the command line option */
+      /* --vex-guest-chase-thresh=0                  */
+   VG_(clo_vex_control).guest_chase_thresh = 0;
+}
+
+   /* Parse the command line options */
+static Bool bbv_process_cmd_line_option(Char* arg)
+{
+   if VG_INT_CLO       (arg, "--interval-size",    interval_size) {}
+   else if VG_STR_CLO  (arg, "--bb-out-file",      clo_bb_out_file) {}
+   else if VG_STR_CLO  (arg, "--pc-out-file",      clo_pc_out_file) {
+      generate_pc_file = True;
+   }
+   else if VG_XACT_CLO (arg, "--instr-count-only", instr_count_only, True) {}
+   else {
+      return False;
+   }
+
+   return True;
+}
+
+static void bbv_print_usage(void)
+{
+   VG_(printf) ("   --bb-out-file=<file>  filename for basic block vector info\n");
+   VG_(printf) ("   --pc-out-file=<file>  filename for basic block addresses and function names\n");
+   VG_(printf) ("   --interval-size=<num> interval size\n");
+   VG_(printf) ("   --instr-count-only    only print total instruction count\n");
+}
+
+static void bbv_print_debug_usage(void)
+{
+   VG_(printf)("    (none)\n");
+}
+
+static void bbv_fini(Int exitcode)
+{
+   Int i;
+
+   if (generate_pc_file) {
+      dumpPcFile();
+   }
+
+   for(i=0;i<allocated_threads;i++) {
+
+      if (bbv_thread[i].total_instr!=0) {
+
+         VG_(sprintf)(buf,"\n\n"
+                          "# Thread %d\n"
+                          "#   Total intervals: %d (Interval Size %d)\n"
+                          "#   Total instructions: %lld\n"
+                          "#   Total reps: %lld\n"
+                          "#   Unique reps: %lld\n"
+                          "#   Total fldcw instructions: %lld\n\n",
+                i,
+                (Int)(bbv_thread[i].total_instr/(ULong)interval_size),
+                interval_size,
+                bbv_thread[i].total_instr,
+                bbv_thread[i].global_rep_count,
+                bbv_thread[i].unique_rep_count,
+                bbv_thread[i].fldcw_count);
+
+            /* Print results to display */
+         VG_UMSG("%s", buf);
+
+            /* open the output file if it hasn't already */
+         if (bbv_thread[i].bbtrace_fd < 0) {
+            bbv_thread[i].bbtrace_fd=open_tracefile(i);
+         }
+            /* Also print to results file */
+         VG_(write)(bbv_thread[i].bbtrace_fd,(void*)buf,VG_(strlen)(buf));
+         VG_(close)(bbv_thread[i].bbtrace_fd);
+      }
+   }
+}
+
+static void bbv_pre_clo_init(void)
+{
+   VG_(details_name)            ("exp-bbv");
+   VG_(details_version)         (NULL);
+   VG_(details_description)     ("a SimPoint basic block vector generator");
+   VG_(details_copyright_author)(
+      "Copyright (C) 2006-2009 Vince Weaver");
+   VG_(details_bug_reports_to)  (VG_BUGS_TO);
+
+   VG_(basic_tool_funcs)          (bbv_post_clo_init,
+                                   bbv_instrument,
+                                   bbv_fini);
+
+   VG_(needs_command_line_options)(bbv_process_cmd_line_option,
+                                   bbv_print_usage,
+                                   bbv_print_debug_usage);
+
+   VG_(track_start_client_code)( bbv_thread_called );
+
+
+   instr_info_table = VG_(OSetGen_Create)(/*keyOff*/0,
+                                          NULL,
+                                          VG_(malloc), "bbv.1", VG_(free));
+
+   bbv_thread=allocate_new_thread(bbv_thread,0,allocated_threads);
+}
+
+VG_DETERMINE_INTERFACE_VERSION(bbv_pre_clo_init)
+
+/*--------------------------------------------------------------------*/
+/*--- end                                                          ---*/
+/*--------------------------------------------------------------------*/