1 /* -*- mode: C; c-basic-offset: 3; -*- */
3 //--------------------------------------------------------------------*/
4 //--- BBV: a SimPoint basic block vector generator bbv_main.c ---*/
5 //--------------------------------------------------------------------*/
8 This file is part of BBV, a Valgrind tool for generating SimPoint
11 Copyright (C) 2006-2017 Vince Weaver
12 vince _at_ csl.cornell.edu
14 pcfile code is Copyright (C) 2006-2017 Oriol Prat
15 oriol.prat _at _ bsc.es
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.
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.
27 You should have received a copy of the GNU General Public License
28 along with this program; if not, see <http://www.gnu.org/licenses/>.
30 The GNU General Public License is contained in the file COPYING.
34 #include "pub_tool_basics.h"
35 #include "pub_tool_tooliface.h"
36 #include "pub_tool_options.h" /* command line options */
38 #include "pub_tool_vki.h" /* VKI_O_CREAT */
39 #include "pub_tool_libcbase.h" /* VG_(strlen) */
40 #include "pub_tool_libcprint.h" /* VG_(printf) */
41 #include "pub_tool_libcassert.h" /* VG_(exit) */
42 #include "pub_tool_mallocfree.h" /* VG_(malloc) */
43 #include "pub_tool_machine.h" /* VG_(fnptr_to_fnentry) */
44 #include "pub_tool_debuginfo.h" /* VG_(get_fnname) */
46 #include "pub_tool_oset.h" /* ordered set stuff */
48 /* instruction special cases */
49 #define REP_INSTRUCTION 0x1
50 #define FLDCW_INSTRUCTION 0x2
52 /* interval variables */
53 #define DEFAULT_GRAIN_SIZE 100000000 /* 100 million by default */
54 static Int interval_size
=DEFAULT_GRAIN_SIZE
;
57 static const HChar
*clo_bb_out_file
="bb.out.%p";
58 static const HChar
*clo_pc_out_file
="pc.out.%p";
59 static HChar
*pc_out_file
=NULL
;
60 static HChar
*bb_out_file
=NULL
;
63 /* output parameters */
64 static Bool instr_count_only
=False
;
65 static Bool generate_pc_file
=False
;
68 static OSet
* instr_info_table
; /* table that holds the basic block info */
69 static Int block_num
=1; /* global next block number */
70 static Int current_thread
=0;
71 static Int allocated_threads
=1;
72 struct thread_info
*bbv_thread
=NULL
;
74 /* Per-thread variables */
76 ULong dyn_instr
; /* Current retired instruction count */
77 ULong total_instr
; /* Total retired instruction count */
78 Addr last_rep_addr
; /* rep counting values */
80 ULong global_rep_count
;
81 ULong unique_rep_count
;
82 ULong fldcw_count
; /* fldcw count */
83 VgFile
*bbtrace_fp
; /* file pointer */
87 Addr BB_addr
; /* used as key, must be first */
88 Int n_instrs
; /* instructions in the basic block */
89 Int block_num
; /* unique block identifier */
90 Int
*inst_counter
; /* times entered * num_instructions */
91 Bool is_entry
; /* is this block a function entry point */
92 const HChar
*fn_name
; /* Function block is in */
96 /* dump the optional PC file, which contains basic block number to */
97 /* instruction address and function name mappings */
98 static void dumpPcFile(void)
100 struct BB_info
*bb_elem
;
104 VG_(expand_file_name
)("--pc-out-file", clo_pc_out_file
);
106 fp
= VG_(fopen
)(pc_out_file
, VKI_O_CREAT
|VKI_O_TRUNC
|VKI_O_WRONLY
,
107 VKI_S_IRUSR
|VKI_S_IWUSR
|VKI_S_IRGRP
|VKI_S_IWGRP
);
109 VG_(umsg
)("Error: cannot create pc file %s\n", pc_out_file
);
113 /* Loop through the table, printing the number, address, */
114 /* and function name for each basic block */
115 VG_(OSetGen_ResetIter
)(instr_info_table
);
116 while ( (bb_elem
= VG_(OSetGen_Next
)(instr_info_table
)) ) {
117 VG_(fprintf
)( fp
, "F:%d:%lx:%s\n", bb_elem
->block_num
,
118 bb_elem
->BB_addr
, bb_elem
->fn_name
);
124 static VgFile
*open_tracefile(Int thread_num
)
127 // Allocate a buffer large enough for the general case "%s.%d" below
128 HChar temp_string
[VG_(strlen
)(bb_out_file
) + 1 + 10 + 1];
130 /* For thread 1, don't append any thread number */
131 /* This lets the single-thread case not have any */
132 /* extra values appended to the file name. */
134 VG_(strcpy
)(temp_string
, bb_out_file
);
137 VG_(sprintf
)(temp_string
,"%s.%d",bb_out_file
,thread_num
);
140 fp
= VG_(fopen
)(temp_string
, VKI_O_CREAT
|VKI_O_TRUNC
|VKI_O_WRONLY
,
141 VKI_S_IRUSR
|VKI_S_IWUSR
|VKI_S_IRGRP
|VKI_S_IWGRP
);
144 VG_(umsg
)("Error: cannot create bb file %s\n",temp_string
);
151 static void handle_overflow(void)
153 struct BB_info
*bb_elem
;
155 if (bbv_thread
[current_thread
].dyn_instr
> interval_size
) {
157 if (!instr_count_only
) {
159 /* If our output file hasn't been opened, open it */
160 if (bbv_thread
[current_thread
].bbtrace_fp
== NULL
) {
161 bbv_thread
[current_thread
].bbtrace_fp
=open_tracefile(current_thread
);
164 /* put an entry to the bb.out file */
166 VG_(fprintf
)(bbv_thread
[current_thread
].bbtrace_fp
, "T");
168 VG_(OSetGen_ResetIter
)(instr_info_table
);
169 while ( (bb_elem
= VG_(OSetGen_Next
)(instr_info_table
)) ) {
170 if ( bb_elem
->inst_counter
[current_thread
] != 0 ) {
171 VG_(fprintf
)(bbv_thread
[current_thread
].bbtrace_fp
, ":%d:%d ",
173 bb_elem
->inst_counter
[current_thread
]);
174 bb_elem
->inst_counter
[current_thread
] = 0;
178 VG_(fprintf
)(bbv_thread
[current_thread
].bbtrace_fp
, "\n");
181 bbv_thread
[current_thread
].dyn_instr
-= interval_size
;
186 static void close_out_reps(void)
188 bbv_thread
[current_thread
].global_rep_count
+=bbv_thread
[current_thread
].rep_count
;
189 bbv_thread
[current_thread
].unique_rep_count
++;
190 bbv_thread
[current_thread
].rep_count
=0;
193 /* Generic function to get called each instruction */
194 static VG_REGPARM(1) void per_instruction_BBV(struct BB_info
*bbInfo
)
200 /* we finished rep but didn't clear out count */
201 if (bbv_thread
[current_thread
].rep_count
) {
206 bbInfo
->inst_counter
[current_thread
]+=n_instrs
;
208 bbv_thread
[current_thread
].total_instr
+=n_instrs
;
209 bbv_thread
[current_thread
].dyn_instr
+=n_instrs
;
214 /* Function to get called if instruction has a rep prefix */
215 static VG_REGPARM(1) void per_instruction_BBV_rep(Addr addr
)
217 /* handle back-to-back rep instructions */
218 if (bbv_thread
[current_thread
].last_rep_addr
!=addr
) {
219 if (bbv_thread
[current_thread
].rep_count
) {
221 bbv_thread
[current_thread
].total_instr
++;
222 bbv_thread
[current_thread
].dyn_instr
++;
224 bbv_thread
[current_thread
].last_rep_addr
=addr
;
227 bbv_thread
[current_thread
].rep_count
++;
231 /* Function to call if our instruction has a fldcw instruction */
232 static VG_REGPARM(1) void per_instruction_BBV_fldcw(struct BB_info
*bbInfo
)
238 /* we finished rep but didn't clear out count */
239 if (bbv_thread
[current_thread
].rep_count
) {
244 /* count fldcw instructions */
245 bbv_thread
[current_thread
].fldcw_count
++;
247 bbInfo
->inst_counter
[current_thread
]+=n_instrs
;
249 bbv_thread
[current_thread
].total_instr
+=n_instrs
;
250 bbv_thread
[current_thread
].dyn_instr
+=n_instrs
;
255 /* Check if the instruction pointed to is one that needs */
256 /* special handling. If so, set a bit in the return */
257 /* value indicating what type. */
258 static Int
get_inst_type(UInt len
, Addr addr
)
262 #if defined(VGA_x86) || defined(VGA_amd64)
268 /* rep prefixed instructions are counted as one instruction on */
269 /* x86 processors and must be handled as a special case */
271 /* Also, the rep prefix is re-used as part of the opcode for */
272 /* SSE instructions. So we need to specifically check for */
273 /* the following: movs, cmps, scas, lods, stos, ins, outs */
275 inst_pointer
=(UChar
*)addr
;
282 inst_byte
=*inst_pointer
;
284 if ( (inst_byte
== 0x67) || /* size override prefix */
285 (inst_byte
== 0x66) || /* size override prefix */
286 (inst_byte
== 0x48) ) { /* 64-bit prefix */
287 } else if ( (inst_byte
== 0xf2) || /* rep prefix */
288 (inst_byte
== 0xf3) ) { /* repne prefix */
291 break; /* other byte, exit */
299 ( ( (inst_byte
>= 0xa4) && /* movs,cmps,scas */
300 (inst_byte
<= 0xaf) ) || /* lods,stos */
301 ( (inst_byte
>= 0x6c) &&
302 (inst_byte
<= 0x6f) ) ) ) { /* ins,outs */
304 result
|=REP_INSTRUCTION
;
307 /* fldcw instructions are double-counted by the hardware */
308 /* performance counters on pentium 4 processors so it is */
309 /* useful to have that count when doing validation work. */
311 inst_pointer
=(UChar
*)addr
;
313 /* FLDCW detection */
314 /* opcode is 0xd9/5, ie 1101 1001 oo10 1mmm */
315 if ((*inst_pointer
==0xd9) &&
316 (*(inst_pointer
+1)<0xb0) && /* need this case of fldz, etc, count */
317 ( (*(inst_pointer
+1) & 0x38) == 0x28)) {
318 result
|=FLDCW_INSTRUCTION
;
328 /* Our instrumentation function */
329 /* sbIn = super block to translate */
330 /* layout = guest layout */
331 /* gWordTy = size of guest word */
332 /* hWordTy = size of host word */
333 static IRSB
* bbv_instrument ( VgCallbackClosure
* closure
,
334 IRSB
* sbIn
, const VexGuestLayout
* layout
,
335 const VexGuestExtents
* vge
,
336 const VexArchInfo
* archinfo_host
,
337 IRType gWordTy
, IRType hWordTy
)
342 struct BB_info
*bbInfo
;
343 Addr origAddr
,ourAddr
;
345 IRExpr
**argv
, *arg1
;
346 Int regparms
,opcode_type
;
347 DiEpoch ep
= VG_(current_DiEpoch
)();
349 /* We don't handle a host/guest word size mismatch */
350 if (gWordTy
!= hWordTy
) {
351 VG_(tool_panic
)("host/guest word size mismatch");
355 sbOut
= deepCopyIRSBExceptStmts(sbIn
);
357 /* Copy verbatim any IR preamble preceding the first IMark */
359 while ( (i
< sbIn
->stmts_used
) && (sbIn
->stmts
[i
]->tag
!=Ist_IMark
)) {
360 addStmtToIRSB( sbOut
, sbIn
->stmts
[i
] );
364 /* Get the first statement */
365 tl_assert(sbIn
->stmts_used
> 0);
368 /* double check we are at a Mark statement */
369 tl_assert(Ist_IMark
== st
->tag
);
371 origAddr
=st
->Ist
.IMark
.addr
;
373 /* Get the BB_info */
374 bbInfo
= VG_(OSetGen_Lookup
)(instr_info_table
, &origAddr
);
378 /* BB never translated before (at this address, at least; */
379 /* could have been unloaded and then reloaded elsewhere in memory) */
381 /* allocate and initialize a new basic block structure */
382 bbInfo
=VG_(OSetGen_AllocNode
)(instr_info_table
, sizeof(struct BB_info
));
383 bbInfo
->BB_addr
= origAddr
;
384 bbInfo
->n_instrs
= n_instrs
;
385 bbInfo
->inst_counter
=VG_(calloc
)("bbv_instrument",
389 /* assign a unique block number */
390 bbInfo
->block_num
=block_num
;
392 /* get function name and entry point information */
393 const HChar
*fn_name
;
394 VG_(get_fnname
)(ep
, origAddr
, &fn_name
);
395 bbInfo
->is_entry
=VG_(get_fnname_if_entry
)(ep
, origAddr
, &fn_name
);
396 bbInfo
->fn_name
=VG_(strdup
)("bbv_strings", fn_name
);
397 /* insert structure into table */
398 VG_(OSetGen_Insert
)( instr_info_table
, bbInfo
);
401 /* Iterate through the basic block, putting the original */
402 /* instructions in place, plus putting a call to updateBBV */
403 /* for each original instruction */
405 /* This is less efficient than only instrumenting the BB */
406 /* But it gives proper results given the fact that */
407 /* valgrind uses superblocks (not basic blocks) by default */
410 while(i
< sbIn
->stmts_used
) {
413 if (st
->tag
== Ist_IMark
) {
415 ourAddr
= st
->Ist
.IMark
.addr
;
417 opcode_type
=get_inst_type(st
->Ist
.IMark
.len
,ourAddr
);
420 arg1
= mkIRExpr_HWord( (HWord
)bbInfo
);
421 argv
= mkIRExprVec_1(arg1
);
424 if (opcode_type
&REP_INSTRUCTION
) {
425 arg1
= mkIRExpr_HWord(ourAddr
);
426 argv
= mkIRExprVec_1(arg1
);
427 di
= unsafeIRDirty_0_N( regparms
, "per_instruction_BBV_rep",
428 VG_(fnptr_to_fnentry
)( &per_instruction_BBV_rep
),
431 else if (opcode_type
&FLDCW_INSTRUCTION
) {
432 di
= unsafeIRDirty_0_N( regparms
, "per_instruction_BBV_fldcw",
433 VG_(fnptr_to_fnentry
)( &per_instruction_BBV_fldcw
),
437 di
= unsafeIRDirty_0_N( regparms
, "per_instruction_BBV",
438 VG_(fnptr_to_fnentry
)( &per_instruction_BBV
),
443 /* Insert our call */
444 addStmtToIRSB( sbOut
, IRStmt_Dirty(di
));
447 /* Insert the original instruction */
448 addStmtToIRSB( sbOut
, st
);
456 static struct thread_info
*allocate_new_thread(struct thread_info
*old
,
457 Int old_number
, Int new_number
)
459 struct thread_info
*temp
;
460 struct BB_info
*bb_elem
;
463 temp
=VG_(realloc
)("bbv_main.c allocate_threads",
465 new_number
*sizeof(struct thread_info
));
467 /* init the new thread */
468 /* We loop in case the new thread is not contiguous */
469 for(i
=old_number
;i
<new_number
;i
++) {
470 temp
[i
].last_rep_addr
=0;
472 temp
[i
].total_instr
=0;
473 temp
[i
].global_rep_count
=0;
474 temp
[i
].unique_rep_count
=0;
476 temp
[i
].fldcw_count
=0;
477 temp
[i
].bbtrace_fp
=NULL
;
479 /* expand the inst_counter on all allocated basic blocks */
480 VG_(OSetGen_ResetIter
)(instr_info_table
);
481 while ( (bb_elem
= VG_(OSetGen_Next
)(instr_info_table
)) ) {
482 bb_elem
->inst_counter
=
483 VG_(realloc
)("bbv_main.c inst_counter",
484 bb_elem
->inst_counter
,
485 new_number
*sizeof(Int
));
486 for(i
=old_number
;i
<new_number
;i
++) {
487 bb_elem
->inst_counter
[i
]=0;
494 static void bbv_thread_called ( ThreadId tid
, ULong nDisp
)
496 if (tid
>= allocated_threads
) {
497 bbv_thread
=allocate_new_thread(bbv_thread
,allocated_threads
,tid
+1);
498 allocated_threads
=tid
+1;
506 /*--------------------------------------------------------------------*/
508 /*--------------------------------------------------------------------*/
510 static void bbv_post_clo_init(void)
513 VG_(expand_file_name
)("--bb-out-file", clo_bb_out_file
);
515 /* Try a closer approximation of basic blocks */
516 /* This is the same as the command line option */
517 /* --vex-guest-chase=no */
518 VG_(clo_vex_control
).guest_chase
= False
;
521 /* Parse the command line options */
522 static Bool
bbv_process_cmd_line_option(const HChar
* arg
)
524 if VG_INT_CLO (arg
, "--interval-size", interval_size
) {}
525 else if VG_STR_CLO (arg
, "--bb-out-file", clo_bb_out_file
) {}
526 else if VG_STR_CLO (arg
, "--pc-out-file", clo_pc_out_file
) {
527 generate_pc_file
= True
;
529 else if VG_BOOL_CLO (arg
, "--instr-count-only", instr_count_only
) {}
537 static void bbv_print_usage(void)
540 " --bb-out-file=<file> filename for BBV info\n"
541 " --pc-out-file=<file> filename for BB addresses and function names\n"
542 " --interval-size=<num> interval size\n"
543 " --instr-count-only=yes|no only print total instruction count\n"
547 static void bbv_print_debug_usage(void)
549 VG_(printf
)(" (none)\n");
552 static void bbv_fini(Int exitcode
)
556 if (generate_pc_file
) {
560 for(i
=0;i
<allocated_threads
;i
++) {
562 if (bbv_thread
[i
].total_instr
!=0) {
563 HChar buf
[500]; // large enough
564 VG_(sprintf
)(buf
,"\n\n"
566 "# Total intervals: %d (Interval Size %d)\n"
567 "# Total instructions: %llu\n"
568 "# Total reps: %llu\n"
569 "# Unique reps: %llu\n"
570 "# Total fldcw instructions: %llu\n\n",
572 (Int
)(bbv_thread
[i
].total_instr
/(ULong
)interval_size
),
574 bbv_thread
[i
].total_instr
,
575 bbv_thread
[i
].global_rep_count
,
576 bbv_thread
[i
].unique_rep_count
,
577 bbv_thread
[i
].fldcw_count
);
579 /* Print results to display */
580 VG_(umsg
)("%s\n", buf
);
582 /* open the output file if it hasn't already */
583 if (bbv_thread
[i
].bbtrace_fp
== NULL
) {
584 bbv_thread
[i
].bbtrace_fp
=open_tracefile(i
);
586 /* Also print to results file */
587 VG_(fprintf
)(bbv_thread
[i
].bbtrace_fp
, "%s", buf
);
588 VG_(fclose
)(bbv_thread
[i
].bbtrace_fp
);
593 static void bbv_pre_clo_init(void)
595 VG_(details_name
) ("exp-bbv");
596 VG_(details_version
) (NULL
);
597 VG_(details_description
) ("a SimPoint basic block vector generator");
598 VG_(details_copyright_author
)(
599 "Copyright (C) 2006-2017 Vince Weaver");
600 VG_(details_bug_reports_to
) (VG_BUGS_TO
);
602 VG_(basic_tool_funcs
) (bbv_post_clo_init
,
606 VG_(needs_command_line_options
)(bbv_process_cmd_line_option
,
608 bbv_print_debug_usage
);
610 VG_(track_start_client_code
)( bbv_thread_called
);
613 instr_info_table
= VG_(OSetGen_Create
)(/*keyOff*/0,
615 VG_(malloc
), "bbv.1", VG_(free
));
617 bbv_thread
=allocate_new_thread(bbv_thread
,0,allocated_threads
);
620 VG_DETERMINE_INTERFACE_VERSION(bbv_pre_clo_init
)
622 /*--------------------------------------------------------------------*/
624 /*--------------------------------------------------------------------*/