• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //--------------------------------------------------------------------*/
2 //--- BBV: a SimPoint basic block vector generator      bbv_main.c ---*/
3 //--------------------------------------------------------------------*/
4 
5 /*
6    This file is part of BBV, a Valgrind tool for generating SimPoint
7    basic block vectors.
8 
9    Copyright (C) 2006-2011 Vince Weaver
10       vince _at_ csl.cornell.edu
11 
12    pcfile code is Copyright (C) 2006-2011 Oriol Prat
13       oriol.prat _at _ bsc.es
14 
15    This program is free software; you can redistribute it and/or
16    modify it under the terms of the GNU General Public License as
17    published by the Free Software Foundation; either version 2 of the
18    License, or (at your option) any later version.
19 
20    This program is distributed in the hope that it will be useful, but
21    WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23    General Public License for more details.
24 
25    You should have received a copy of the GNU General Public License
26    along with this program; if not, write to the Free Software
27    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
28    02111-1307, USA.
29 
30    The GNU General Public License is contained in the file COPYING.
31 */
32 
33 
34 #include "pub_tool_basics.h"
35 #include "pub_tool_tooliface.h"
36 #include "pub_tool_options.h"    /* command line options */
37 
38 #include "pub_tool_vki.h"        /* vki_stat */
39 #include "pub_tool_libcbase.h"   /* VG_(strlen) */
40 #include "pub_tool_libcfile.h"   /* VG_(write) */
41 #include "pub_tool_libcprint.h"  /* VG_(printf) */
42 #include "pub_tool_libcassert.h" /* VG_(exit) */
43 #include "pub_tool_mallocfree.h" /* plain_free */
44 #include "pub_tool_machine.h"    /* VG_(fnptr_to_fnentry) */
45 #include "pub_tool_debuginfo.h"  /* VG_(get_fnname) */
46 
47 #include "pub_tool_oset.h"       /* ordered set stuff */
48 
49    /* instruction special cases */
50 #define REP_INSTRUCTION   0x1
51 #define FLDCW_INSTRUCTION 0x2
52 
53    /* interval variables */
54 #define DEFAULT_GRAIN_SIZE 100000000  /* 100 million by default */
55 static Int interval_size=DEFAULT_GRAIN_SIZE;
56 
57    /* filenames */
58 static UChar *clo_bb_out_file="bb.out.%p";
59 static UChar *clo_pc_out_file="pc.out.%p";
60 static UChar *pc_out_file=NULL;
61 static UChar *bb_out_file=NULL;
62 
63 
64    /* output parameters */
65 static Bool instr_count_only=False;
66 static Bool generate_pc_file=False;
67 
68    /* write buffer */
69 static UChar buf[1024];
70 
71    /* Global values */
72 static OSet* instr_info_table;  /* table that holds the basic block info */
73 static Int block_num=1;         /* global next block number */
74 static Int current_thread=0;
75 static Int allocated_threads=1;
76 struct thread_info *bbv_thread=NULL;
77 
78    /* Per-thread variables */
79 struct thread_info {
80    ULong dyn_instr;         /* Current retired instruction count */
81    ULong total_instr;       /* Total retired instruction count   */
82    Addr last_rep_addr;      /* rep counting values */
83    ULong rep_count;
84    ULong global_rep_count;
85    ULong unique_rep_count;
86    ULong fldcw_count;       /* fldcw count */
87    Int bbtrace_fd;          /* file descriptor */
88 };
89 
90 #define FUNCTION_NAME_LENGTH 20
91 
92 struct BB_info {
93    Addr       BB_addr;           /* used as key, must be first           */
94    Int        n_instrs;          /* instructions in the basic block      */
95    Int        block_num;         /* unique block identifier              */
96    Int        *inst_counter;     /* times entered * num_instructions     */
97    Bool       is_entry;          /* is this block a function entry point */
98    UChar      fn_name[FUNCTION_NAME_LENGTH];  /* Function block is in    */
99 };
100 
101 
102    /* dump the optional PC file, which contains basic block number to */
103    /*   instruction address and function name mappings                */
dumpPcFile(void)104 static void dumpPcFile(void)
105 {
106    struct BB_info   *bb_elem;
107    Int              pctrace_fd;
108    SysRes           sres;
109 
110    pc_out_file =
111           VG_(expand_file_name)("--pc-out-file", clo_pc_out_file);
112 
113    sres = VG_(open)(pc_out_file, VKI_O_CREAT|VKI_O_TRUNC|VKI_O_WRONLY,
114                               VKI_S_IRUSR|VKI_S_IWUSR|VKI_S_IRGRP|VKI_S_IWGRP);
115    if (sr_isError(sres)) {
116       VG_(umsg)("Error: cannot create pc file %s\n", pc_out_file);
117       VG_(exit)(1);
118    } else {
119       pctrace_fd = sr_Res(sres);
120    }
121 
122       /* Loop through the table, printing the number, address, */
123       /*    and function name for each basic block             */
124    VG_(OSetGen_ResetIter)(instr_info_table);
125    while ( (bb_elem = VG_(OSetGen_Next)(instr_info_table)) ) {
126       VG_(write)(pctrace_fd,"F",1);
127       VG_(sprintf)( buf,":%d:%x:%s\n",
128                        bb_elem->block_num,
129                        (Int)bb_elem->BB_addr,
130                        bb_elem->fn_name);
131       VG_(write)(pctrace_fd, (void*)buf, VG_(strlen)(buf));
132    }
133 
134    VG_(close)(pctrace_fd);
135 }
136 
open_tracefile(Int thread_num)137 static Int open_tracefile(Int thread_num)
138 {
139    SysRes  sres;
140    UChar temp_string[2048];
141 
142       /* For thread 1, don't append any thread number  */
143       /* This lets the single-thread case not have any */
144       /* extra values appended to the file name.       */
145    if (thread_num==1) {
146       VG_(strncpy)(temp_string,bb_out_file,2047);
147    }
148    else {
149       VG_(sprintf)(temp_string,"%s.%d",bb_out_file,thread_num);
150    }
151 
152    sres = VG_(open)(temp_string, VKI_O_CREAT|VKI_O_TRUNC|VKI_O_WRONLY,
153                               VKI_S_IRUSR|VKI_S_IWUSR|VKI_S_IRGRP|VKI_S_IWGRP);
154 
155    if (sr_isError(sres)) {
156       VG_(umsg)("Error: cannot create bb file %s\n",temp_string);
157       VG_(exit)(1);
158    }
159 
160    return sr_Res(sres);
161 }
162 
handle_overflow(void)163 static void handle_overflow(void)
164 {
165    struct BB_info *bb_elem;
166 
167    if (bbv_thread[current_thread].dyn_instr > interval_size) {
168 
169       if (!instr_count_only) {
170 
171             /* If our output fd hasn't been opened, open it */
172          if (bbv_thread[current_thread].bbtrace_fd < 0) {
173             bbv_thread[current_thread].bbtrace_fd=open_tracefile(current_thread);
174          }
175 
176            /* put an entry to the bb.out file */
177 
178          VG_(write)(bbv_thread[current_thread].bbtrace_fd,"T",1);
179 
180          VG_(OSetGen_ResetIter)(instr_info_table);
181          while ( (bb_elem = VG_(OSetGen_Next)(instr_info_table)) ) {
182             if ( bb_elem->inst_counter[current_thread] != 0 ) {
183                VG_(sprintf)( buf,":%d:%d   ",
184                          bb_elem->block_num,
185                          bb_elem->inst_counter[current_thread]);
186                VG_(write)(bbv_thread[current_thread].bbtrace_fd,
187                           (void*)buf, VG_(strlen)(buf));
188                bb_elem->inst_counter[current_thread] = 0;
189             }
190          }
191 
192          VG_(write)(bbv_thread[current_thread].bbtrace_fd,"\n",1);
193       }
194 
195       bbv_thread[current_thread].dyn_instr -= interval_size;
196    }
197 }
198 
199 
close_out_reps(void)200 static void close_out_reps(void)
201 {
202    bbv_thread[current_thread].global_rep_count+=bbv_thread[current_thread].rep_count;
203    bbv_thread[current_thread].unique_rep_count++;
204    bbv_thread[current_thread].rep_count=0;
205 }
206 
207    /* Generic function to get called each instruction */
per_instruction_BBV(struct BB_info * bbInfo)208 static VG_REGPARM(1) void per_instruction_BBV(struct BB_info *bbInfo)
209 {
210    Int n_instrs=1;
211 
212    tl_assert(bbInfo);
213 
214       /* we finished rep but didn't clear out count */
215    if (bbv_thread[current_thread].rep_count) {
216       n_instrs++;
217       close_out_reps();
218    }
219 
220    bbInfo->inst_counter[current_thread]+=n_instrs;
221 
222    bbv_thread[current_thread].total_instr+=n_instrs;
223    bbv_thread[current_thread].dyn_instr +=n_instrs;
224 
225    handle_overflow();
226 }
227 
228    /* Function to get called if instruction has a rep prefix */
per_instruction_BBV_rep(Addr addr)229 static VG_REGPARM(1) void per_instruction_BBV_rep(Addr addr)
230 {
231       /* handle back-to-back rep instructions */
232    if (bbv_thread[current_thread].last_rep_addr!=addr) {
233       if (bbv_thread[current_thread].rep_count) {
234          close_out_reps();
235          bbv_thread[current_thread].total_instr++;
236          bbv_thread[current_thread].dyn_instr++;
237       }
238       bbv_thread[current_thread].last_rep_addr=addr;
239    }
240 
241    bbv_thread[current_thread].rep_count++;
242 
243 }
244 
245    /* Function to call if our instruction has a fldcw instruction */
per_instruction_BBV_fldcw(struct BB_info * bbInfo)246 static VG_REGPARM(1) void per_instruction_BBV_fldcw(struct BB_info *bbInfo)
247 {
248    Int n_instrs=1;
249 
250    tl_assert(bbInfo);
251 
252       /* we finished rep but didn't clear out count */
253    if (bbv_thread[current_thread].rep_count) {
254       n_instrs++;
255       close_out_reps();
256    }
257 
258       /* count fldcw instructions */
259    bbv_thread[current_thread].fldcw_count++;
260 
261    bbInfo->inst_counter[current_thread]+=n_instrs;
262 
263    bbv_thread[current_thread].total_instr+=n_instrs;
264    bbv_thread[current_thread].dyn_instr +=n_instrs;
265 
266    handle_overflow();
267 }
268 
269    /* Check if the instruction pointed to is one that needs */
270    /*   special handling.  If so, set a bit in the return   */
271    /*   value indicating what type.                         */
get_inst_type(Int len,Addr addr)272 static Int get_inst_type(Int len, Addr addr)
273 {
274    int result=0;
275 
276 #if defined(VGA_x86) || defined(VGA_amd64)
277 
278    unsigned char *inst_pointer;
279    unsigned char inst_byte;
280    int i,possible_rep;
281 
282    /* rep prefixed instructions are counted as one instruction on */
283    /*     x86 processors and must be handled as a special case    */
284 
285    /* Also, the rep prefix is re-used as part of the opcode for   */
286    /*     SSE instructions.  So we need to specifically check for */
287    /*     the following: movs, cmps, scas, lods, stos, ins, outs  */
288 
289    inst_pointer=(unsigned char *)addr;
290    i=0;
291    inst_byte=0;
292    possible_rep=0;
293 
294    while (i<len) {
295 
296       inst_byte=*inst_pointer;
297 
298       if ( (inst_byte == 0x67) ||            /* size override prefix */
299            (inst_byte == 0x66) ||            /* size override prefix */
300            (inst_byte == 0x48) ) {           /* 64-bit prefix */
301       } else if ( (inst_byte == 0xf2) ||     /* rep prefix    */
302                   (inst_byte == 0xf3) ) {    /* repne prefix  */
303          possible_rep=1;
304       } else {
305          break;                              /* other byte, exit */
306       }
307 
308       i++;
309       inst_pointer++;
310    }
311 
312    if ( possible_rep &&
313         ( ( (inst_byte >= 0xa4) &&     /* movs,cmps,scas */
314             (inst_byte <= 0xaf) ) ||   /* lods,stos      */
315           ( (inst_byte >= 0x6c) &&
316             (inst_byte <= 0x6f) ) ) ) {  /* ins,outs       */
317 
318       result|=REP_INSTRUCTION;
319    }
320 
321    /* fldcw instructions are double-counted by the hardware       */
322    /*     performance counters on pentium 4 processors so it is   */
323    /*     useful to have that count when doing validation work.   */
324 
325    inst_pointer=(unsigned char *)addr;
326    if (len>1) {
327          /* FLDCW detection */
328          /* opcode is 0xd9/5, ie 1101 1001 oo10 1mmm */
329       if ((*inst_pointer==0xd9) &&
330           (*(inst_pointer+1)<0xb0) &&  /* need this case of fldz, etc, count */
331           ( (*(inst_pointer+1) & 0x38) == 0x28)) {
332          result|=FLDCW_INSTRUCTION;
333       }
334    }
335 
336 #endif
337    return result;
338 }
339 
340 
341 
342    /* Our instrumentation function       */
343    /*    sbIn = super block to translate */
344    /*    layout = guest layout           */
345    /*    gWordTy = size of guest word    */
346    /*    hWordTy = size of host word     */
bbv_instrument(VgCallbackClosure * closure,IRSB * sbIn,VexGuestLayout * layout,VexGuestExtents * vge,IRType gWordTy,IRType hWordTy)347 static IRSB* bbv_instrument ( VgCallbackClosure* closure,
348                              IRSB* sbIn, VexGuestLayout* layout,
349                              VexGuestExtents* vge,
350                              IRType gWordTy, IRType hWordTy )
351 {
352    Int      i,n_instrs=1;
353    IRSB     *sbOut;
354    IRStmt   *st;
355    struct BB_info  *bbInfo;
356    Addr64   origAddr,ourAddr;
357    IRDirty  *di;
358    IRExpr   **argv, *arg1;
359    Int      regparms,opcode_type;
360 
361       /* We don't handle a host/guest word size mismatch */
362    if (gWordTy != hWordTy) {
363       VG_(tool_panic)("host/guest word size mismatch");
364    }
365 
366       /* Set up SB */
367    sbOut = deepCopyIRSBExceptStmts(sbIn);
368 
369       /* Copy verbatim any IR preamble preceding the first IMark */
370    i = 0;
371    while ( (i < sbIn->stmts_used) && (sbIn->stmts[i]->tag!=Ist_IMark)) {
372       addStmtToIRSB( sbOut, sbIn->stmts[i] );
373       i++;
374    }
375 
376       /* Get the first statement */
377    tl_assert(sbIn->stmts_used > 0);
378    st = sbIn->stmts[i];
379 
380       /* double check we are at a Mark statement */
381    tl_assert(Ist_IMark == st->tag);
382 
383    origAddr=st->Ist.IMark.addr;
384 
385       /* Get the BB_info */
386    bbInfo = VG_(OSetGen_Lookup)(instr_info_table, &origAddr);
387 
388    if (bbInfo==NULL) {
389 
390          /* BB never translated before (at this address, at least;          */
391          /* could have been unloaded and then reloaded elsewhere in memory) */
392 
393          /* allocate and initialize a new basic block structure */
394       bbInfo=VG_(OSetGen_AllocNode)(instr_info_table, sizeof(struct BB_info));
395       bbInfo->BB_addr = origAddr;
396       bbInfo->n_instrs = n_instrs;
397       bbInfo->inst_counter=VG_(calloc)("bbv_instrument",
398                                        allocated_threads,
399                                        sizeof(Int));
400 
401          /* assign a unique block number */
402       bbInfo->block_num=block_num;
403       block_num++;
404          /* get function name and entry point information */
405       VG_(get_fnname)(origAddr,bbInfo->fn_name,FUNCTION_NAME_LENGTH);
406       bbInfo->is_entry=VG_(get_fnname_if_entry)(origAddr, bbInfo->fn_name,
407                                                 FUNCTION_NAME_LENGTH);
408          /* insert structure into table */
409       VG_(OSetGen_Insert)( instr_info_table, bbInfo );
410    }
411 
412       /* Iterate through the basic block, putting the original   */
413       /* instructions in place, plus putting a call to updateBBV */
414       /* for each original instruction                           */
415 
416       /* This is less efficient than only instrumenting the BB   */
417       /* But it gives proper results given the fact that         */
418       /* valgrind uses superblocks (not basic blocks) by default */
419 
420 
421    while(i < sbIn->stmts_used) {
422       st=sbIn->stmts[i];
423 
424       if (st->tag == Ist_IMark) {
425 
426          ourAddr = st->Ist.IMark.addr;
427 
428          opcode_type=get_inst_type(st->Ist.IMark.len,ourAddr);
429 
430          regparms=1;
431          arg1= mkIRExpr_HWord( (HWord)bbInfo);
432          argv= mkIRExprVec_1(arg1);
433 
434 
435          if (opcode_type&REP_INSTRUCTION) {
436             arg1= mkIRExpr_HWord(ourAddr);
437             argv= mkIRExprVec_1(arg1);
438             di= unsafeIRDirty_0_N( regparms, "per_instruction_BBV_rep",
439                                 VG_(fnptr_to_fnentry)( &per_instruction_BBV_rep ),
440                                 argv);
441          }
442          else if (opcode_type&FLDCW_INSTRUCTION) {
443             di= unsafeIRDirty_0_N( regparms, "per_instruction_BBV_fldcw",
444                                 VG_(fnptr_to_fnentry)( &per_instruction_BBV_fldcw ),
445                                 argv);
446          }
447          else {
448          di= unsafeIRDirty_0_N( regparms, "per_instruction_BBV",
449                                 VG_(fnptr_to_fnentry)( &per_instruction_BBV ),
450                                 argv);
451          }
452 
453 
454             /* Insert our call */
455          addStmtToIRSB( sbOut,  IRStmt_Dirty(di));
456       }
457 
458          /* Insert the original instruction */
459       addStmtToIRSB( sbOut, st );
460 
461       i++;
462    }
463 
464    return sbOut;
465 }
466 
allocate_new_thread(struct thread_info * old,Int old_number,Int new_number)467 static struct thread_info *allocate_new_thread(struct thread_info *old,
468                                      Int old_number, Int new_number)
469 {
470    struct thread_info *temp;
471    struct BB_info   *bb_elem;
472    Int i;
473 
474    temp=VG_(realloc)("bbv_main.c allocate_threads",
475                      old,
476                      new_number*sizeof(struct thread_info));
477 
478       /* init the new thread */
479       /* We loop in case the new thread is not contiguous */
480    for(i=old_number;i<new_number;i++) {
481       temp[i].last_rep_addr=0;
482       temp[i].dyn_instr=0;
483       temp[i].total_instr=0;
484       temp[i].global_rep_count=0;
485       temp[i].unique_rep_count=0;
486       temp[i].rep_count=0;
487       temp[i].fldcw_count=0;
488       temp[i].bbtrace_fd=-1;
489    }
490       /* expand the inst_counter on all allocated basic blocks */
491    VG_(OSetGen_ResetIter)(instr_info_table);
492    while ( (bb_elem = VG_(OSetGen_Next)(instr_info_table)) ) {
493       bb_elem->inst_counter =
494                     VG_(realloc)("bbv_main.c inst_counter",
495                                  bb_elem->inst_counter,
496                                  new_number*sizeof(Int));
497       for(i=old_number;i<new_number;i++) {
498          bb_elem->inst_counter[i]=0;
499       }
500    }
501 
502    return temp;
503 }
504 
bbv_thread_called(ThreadId tid,ULong nDisp)505 static void bbv_thread_called ( ThreadId tid, ULong nDisp )
506 {
507    if (tid >= allocated_threads) {
508       bbv_thread=allocate_new_thread(bbv_thread,allocated_threads,tid+1);
509       allocated_threads=tid+1;
510    }
511    current_thread=tid;
512 }
513 
514 
515 
516 
517 /*--------------------------------------------------------------------*/
518 /*--- Setup                                                        ---*/
519 /*--------------------------------------------------------------------*/
520 
bbv_post_clo_init(void)521 static void bbv_post_clo_init(void)
522 {
523    bb_out_file =
524           VG_(expand_file_name)("--bb-out-file", clo_bb_out_file);
525 
526       /* Try a closer approximation of basic blocks  */
527       /* This is the same as the command line option */
528       /* --vex-guest-chase-thresh=0                  */
529    VG_(clo_vex_control).guest_chase_thresh = 0;
530 }
531 
532    /* Parse the command line options */
bbv_process_cmd_line_option(Char * arg)533 static Bool bbv_process_cmd_line_option(Char* arg)
534 {
535    if VG_INT_CLO       (arg, "--interval-size",    interval_size) {}
536    else if VG_STR_CLO  (arg, "--bb-out-file",      clo_bb_out_file) {}
537    else if VG_STR_CLO  (arg, "--pc-out-file",      clo_pc_out_file) {
538       generate_pc_file = True;
539    }
540    else if VG_BOOL_CLO (arg, "--instr-count-only", instr_count_only) {}
541    else {
542       return False;
543    }
544 
545    return True;
546 }
547 
bbv_print_usage(void)548 static void bbv_print_usage(void)
549 {
550    VG_(printf)(
551 "   --bb-out-file=<file>       filename for BBV info\n"
552 "   --pc-out-file=<file>       filename for BB addresses and function names\n"
553 "   --interval-size=<num>      interval size\n"
554 "   --instr-count-only=yes|no  only print total instruction count\n"
555    );
556 }
557 
bbv_print_debug_usage(void)558 static void bbv_print_debug_usage(void)
559 {
560    VG_(printf)("    (none)\n");
561 }
562 
bbv_fini(Int exitcode)563 static void bbv_fini(Int exitcode)
564 {
565    Int i;
566 
567    if (generate_pc_file) {
568       dumpPcFile();
569    }
570 
571    for(i=0;i<allocated_threads;i++) {
572 
573       if (bbv_thread[i].total_instr!=0) {
574 
575          VG_(sprintf)(buf,"\n\n"
576                           "# Thread %d\n"
577                           "#   Total intervals: %d (Interval Size %d)\n"
578                           "#   Total instructions: %lld\n"
579                           "#   Total reps: %lld\n"
580                           "#   Unique reps: %lld\n"
581                           "#   Total fldcw instructions: %lld\n\n",
582                 i,
583                 (Int)(bbv_thread[i].total_instr/(ULong)interval_size),
584                 interval_size,
585                 bbv_thread[i].total_instr,
586                 bbv_thread[i].global_rep_count,
587                 bbv_thread[i].unique_rep_count,
588                 bbv_thread[i].fldcw_count);
589 
590             /* Print results to display */
591          VG_(umsg)("%s\n", buf);
592 
593             /* open the output file if it hasn't already */
594          if (bbv_thread[i].bbtrace_fd < 0) {
595             bbv_thread[i].bbtrace_fd=open_tracefile(i);
596          }
597             /* Also print to results file */
598          VG_(write)(bbv_thread[i].bbtrace_fd,(void*)buf,VG_(strlen)(buf));
599          VG_(close)(bbv_thread[i].bbtrace_fd);
600       }
601    }
602 }
603 
bbv_pre_clo_init(void)604 static void bbv_pre_clo_init(void)
605 {
606    VG_(details_name)            ("exp-bbv");
607    VG_(details_version)         (NULL);
608    VG_(details_description)     ("a SimPoint basic block vector generator");
609    VG_(details_copyright_author)(
610       "Copyright (C) 2006-2011 Vince Weaver");
611    VG_(details_bug_reports_to)  (VG_BUGS_TO);
612 
613    VG_(basic_tool_funcs)          (bbv_post_clo_init,
614                                    bbv_instrument,
615                                    bbv_fini);
616 
617    VG_(needs_command_line_options)(bbv_process_cmd_line_option,
618                                    bbv_print_usage,
619                                    bbv_print_debug_usage);
620 
621    VG_(track_start_client_code)( bbv_thread_called );
622 
623 
624    instr_info_table = VG_(OSetGen_Create)(/*keyOff*/0,
625                                           NULL,
626                                           VG_(malloc), "bbv.1", VG_(free));
627 
628    bbv_thread=allocate_new_thread(bbv_thread,0,allocated_threads);
629 }
630 
631 VG_DETERMINE_INTERFACE_VERSION(bbv_pre_clo_init)
632 
633 /*--------------------------------------------------------------------*/
634 /*--- end                                                          ---*/
635 /*--------------------------------------------------------------------*/
636