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