florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 1 | /* -*- mode: C; c-basic-offset: 3; -*- */ |
| 2 | |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 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 | |
Elliott Hughes | ed39800 | 2017-06-21 14:41:24 -0700 | [diff] [blame^] | 11 | Copyright (C) 2006-2017 Vince Weaver |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 12 | vince _at_ csl.cornell.edu |
| 13 | |
Elliott Hughes | ed39800 | 2017-06-21 14:41:24 -0700 | [diff] [blame^] | 14 | pcfile code is Copyright (C) 2006-2017 Oriol Prat |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 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 | |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 40 | #include "pub_tool_vki.h" /* VKI_O_CREAT */ |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 41 | #include "pub_tool_libcbase.h" /* VG_(strlen) */ |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 42 | #include "pub_tool_libcprint.h" /* VG_(printf) */ |
| 43 | #include "pub_tool_libcassert.h" /* VG_(exit) */ |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 44 | #include "pub_tool_mallocfree.h" /* VG_(malloc) */ |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 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 */ |
florian | 19f91bb | 2012-11-10 22:29:54 +0000 | [diff] [blame] | 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; |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 63 | |
| 64 | |
| 65 | /* output parameters */ |
| 66 | static Bool instr_count_only=False; |
| 67 | static Bool generate_pc_file=False; |
| 68 | |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 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 */ |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 85 | VgFile *bbtrace_fp; /* file pointer */ |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 86 | }; |
| 87 | |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 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 */ |
florian | 46cc045 | 2014-10-25 19:20:38 +0000 | [diff] [blame] | 94 | const HChar *fn_name; /* Function block is in */ |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 95 | }; |
| 96 | |
| 97 | |
| 98 | /* dump the optional PC file, which contains basic block number to */ |
| 99 | /* instruction address and function name mappings */ |
| 100 | static void dumpPcFile(void) |
| 101 | { |
| 102 | struct BB_info *bb_elem; |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 103 | VgFile *fp; |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 104 | |
| 105 | pc_out_file = |
| 106 | VG_(expand_file_name)("--pc-out-file", clo_pc_out_file); |
| 107 | |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 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) { |
sewardj | 0cf3e78 | 2009-07-15 14:50:37 +0000 | [diff] [blame] | 111 | VG_(umsg)("Error: cannot create pc file %s\n", pc_out_file); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 112 | VG_(exit)(1); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 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)) ) { |
florian | 16eef85 | 2015-08-03 21:05:20 +0000 | [diff] [blame] | 119 | VG_(fprintf)( fp, "F:%d:%lx:%s\n", bb_elem->block_num, |
| 120 | bb_elem->BB_addr, bb_elem->fn_name); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 121 | } |
| 122 | |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 123 | VG_(fclose)(fp); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 124 | } |
| 125 | |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 126 | static VgFile *open_tracefile(Int thread_num) |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 127 | { |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 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]; |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 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) { |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 136 | VG_(strcpy)(temp_string, bb_out_file); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 137 | } |
| 138 | else { |
| 139 | VG_(sprintf)(temp_string,"%s.%d",bb_out_file,thread_num); |
| 140 | } |
| 141 | |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 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); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 144 | |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 145 | if (fp == NULL) { |
sewardj | 0cf3e78 | 2009-07-15 14:50:37 +0000 | [diff] [blame] | 146 | VG_(umsg)("Error: cannot create bb file %s\n",temp_string); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 147 | VG_(exit)(1); |
| 148 | } |
| 149 | |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 150 | return fp; |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 151 | } |
| 152 | |
| 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 | |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 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); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 164 | } |
| 165 | |
| 166 | /* put an entry to the bb.out file */ |
| 167 | |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 168 | VG_(fprintf)(bbv_thread[current_thread].bbtrace_fp, "T"); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 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 ) { |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 173 | VG_(fprintf)(bbv_thread[current_thread].bbtrace_fp, ":%d:%d ", |
| 174 | bb_elem->block_num, |
| 175 | bb_elem->inst_counter[current_thread]); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 176 | bb_elem->inst_counter[current_thread] = 0; |
| 177 | } |
| 178 | } |
| 179 | |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 180 | VG_(fprintf)(bbv_thread[current_thread].bbtrace_fp, "\n"); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 181 | } |
| 182 | |
| 183 | bbv_thread[current_thread].dyn_instr -= interval_size; |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | |
| 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 */ |
| 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 */ |
| 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 */ |
| 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. */ |
florian | f466eef | 2015-01-02 17:32:40 +0000 | [diff] [blame] | 260 | static Int get_inst_type(UInt len, Addr addr) |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 261 | { |
| 262 | int result=0; |
| 263 | |
| 264 | #if defined(VGA_x86) || defined(VGA_amd64) |
| 265 | |
florian | 19f91bb | 2012-11-10 22:29:54 +0000 | [diff] [blame] | 266 | UChar *inst_pointer; |
| 267 | UChar inst_byte; |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 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 | |
florian | 19f91bb | 2012-11-10 22:29:54 +0000 | [diff] [blame] | 277 | inst_pointer=(UChar *)addr; |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 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 | |
florian | 19f91bb | 2012-11-10 22:29:54 +0000 | [diff] [blame] | 313 | inst_pointer=(UChar *)addr; |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 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 */ |
| 335 | static IRSB* bbv_instrument ( VgCallbackClosure* closure, |
florian | 3c0c947 | 2014-09-24 12:06:55 +0000 | [diff] [blame] | 336 | IRSB* sbIn, const VexGuestLayout* layout, |
| 337 | const VexGuestExtents* vge, |
| 338 | const VexArchInfo* archinfo_host, |
| 339 | IRType gWordTy, IRType hWordTy ) |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 340 | { |
| 341 | Int i,n_instrs=1; |
| 342 | IRSB *sbOut; |
| 343 | IRStmt *st; |
| 344 | struct BB_info *bbInfo; |
florian | f466eef | 2015-01-02 17:32:40 +0000 | [diff] [blame] | 345 | Addr origAddr,ourAddr; |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 346 | IRDirty *di; |
| 347 | IRExpr **argv, *arg1; |
| 348 | Int regparms,opcode_type; |
| 349 | |
| 350 | /* We don't handle a host/guest word size mismatch */ |
| 351 | if (gWordTy != hWordTy) { |
| 352 | VG_(tool_panic)("host/guest word size mismatch"); |
| 353 | } |
| 354 | |
| 355 | /* Set up SB */ |
| 356 | sbOut = deepCopyIRSBExceptStmts(sbIn); |
| 357 | |
| 358 | /* Copy verbatim any IR preamble preceding the first IMark */ |
| 359 | i = 0; |
| 360 | while ( (i < sbIn->stmts_used) && (sbIn->stmts[i]->tag!=Ist_IMark)) { |
| 361 | addStmtToIRSB( sbOut, sbIn->stmts[i] ); |
| 362 | i++; |
| 363 | } |
| 364 | |
| 365 | /* Get the first statement */ |
| 366 | tl_assert(sbIn->stmts_used > 0); |
| 367 | st = sbIn->stmts[i]; |
| 368 | |
| 369 | /* double check we are at a Mark statement */ |
| 370 | tl_assert(Ist_IMark == st->tag); |
| 371 | |
| 372 | origAddr=st->Ist.IMark.addr; |
| 373 | |
| 374 | /* Get the BB_info */ |
| 375 | bbInfo = VG_(OSetGen_Lookup)(instr_info_table, &origAddr); |
| 376 | |
| 377 | if (bbInfo==NULL) { |
| 378 | |
| 379 | /* BB never translated before (at this address, at least; */ |
| 380 | /* could have been unloaded and then reloaded elsewhere in memory) */ |
| 381 | |
| 382 | /* allocate and initialize a new basic block structure */ |
| 383 | bbInfo=VG_(OSetGen_AllocNode)(instr_info_table, sizeof(struct BB_info)); |
| 384 | bbInfo->BB_addr = origAddr; |
| 385 | bbInfo->n_instrs = n_instrs; |
| 386 | bbInfo->inst_counter=VG_(calloc)("bbv_instrument", |
| 387 | allocated_threads, |
| 388 | sizeof(Int)); |
| 389 | |
| 390 | /* assign a unique block number */ |
| 391 | bbInfo->block_num=block_num; |
| 392 | block_num++; |
| 393 | /* get function name and entry point information */ |
florian | 46cc045 | 2014-10-25 19:20:38 +0000 | [diff] [blame] | 394 | const HChar *fn_name; |
florian | dc4ff1a | 2015-03-12 18:56:21 +0000 | [diff] [blame] | 395 | VG_(get_fnname)(origAddr, &fn_name); |
florian | 46cc045 | 2014-10-25 19:20:38 +0000 | [diff] [blame] | 396 | bbInfo->is_entry=VG_(get_fnname_if_entry)(origAddr, &fn_name); |
| 397 | bbInfo->fn_name =VG_(strdup)("bbv_strings", fn_name); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 398 | /* insert structure into table */ |
| 399 | VG_(OSetGen_Insert)( instr_info_table, bbInfo ); |
| 400 | } |
| 401 | |
| 402 | /* Iterate through the basic block, putting the original */ |
| 403 | /* instructions in place, plus putting a call to updateBBV */ |
| 404 | /* for each original instruction */ |
| 405 | |
| 406 | /* This is less efficient than only instrumenting the BB */ |
| 407 | /* But it gives proper results given the fact that */ |
| 408 | /* valgrind uses superblocks (not basic blocks) by default */ |
| 409 | |
| 410 | |
| 411 | while(i < sbIn->stmts_used) { |
| 412 | st=sbIn->stmts[i]; |
| 413 | |
| 414 | if (st->tag == Ist_IMark) { |
| 415 | |
| 416 | ourAddr = st->Ist.IMark.addr; |
| 417 | |
| 418 | opcode_type=get_inst_type(st->Ist.IMark.len,ourAddr); |
| 419 | |
| 420 | regparms=1; |
| 421 | arg1= mkIRExpr_HWord( (HWord)bbInfo); |
| 422 | argv= mkIRExprVec_1(arg1); |
| 423 | |
| 424 | |
| 425 | if (opcode_type&REP_INSTRUCTION) { |
| 426 | arg1= mkIRExpr_HWord(ourAddr); |
| 427 | argv= mkIRExprVec_1(arg1); |
| 428 | di= unsafeIRDirty_0_N( regparms, "per_instruction_BBV_rep", |
| 429 | VG_(fnptr_to_fnentry)( &per_instruction_BBV_rep ), |
| 430 | argv); |
| 431 | } |
| 432 | else if (opcode_type&FLDCW_INSTRUCTION) { |
| 433 | di= unsafeIRDirty_0_N( regparms, "per_instruction_BBV_fldcw", |
| 434 | VG_(fnptr_to_fnentry)( &per_instruction_BBV_fldcw ), |
| 435 | argv); |
| 436 | } |
| 437 | else { |
| 438 | di= unsafeIRDirty_0_N( regparms, "per_instruction_BBV", |
| 439 | VG_(fnptr_to_fnentry)( &per_instruction_BBV ), |
| 440 | argv); |
| 441 | } |
| 442 | |
| 443 | |
| 444 | /* Insert our call */ |
| 445 | addStmtToIRSB( sbOut, IRStmt_Dirty(di)); |
| 446 | } |
| 447 | |
| 448 | /* Insert the original instruction */ |
| 449 | addStmtToIRSB( sbOut, st ); |
| 450 | |
| 451 | i++; |
| 452 | } |
| 453 | |
| 454 | return sbOut; |
| 455 | } |
| 456 | |
| 457 | static struct thread_info *allocate_new_thread(struct thread_info *old, |
| 458 | Int old_number, Int new_number) |
| 459 | { |
| 460 | struct thread_info *temp; |
| 461 | struct BB_info *bb_elem; |
| 462 | Int i; |
| 463 | |
| 464 | temp=VG_(realloc)("bbv_main.c allocate_threads", |
| 465 | old, |
| 466 | new_number*sizeof(struct thread_info)); |
| 467 | |
| 468 | /* init the new thread */ |
| 469 | /* We loop in case the new thread is not contiguous */ |
| 470 | for(i=old_number;i<new_number;i++) { |
| 471 | temp[i].last_rep_addr=0; |
| 472 | temp[i].dyn_instr=0; |
| 473 | temp[i].total_instr=0; |
| 474 | temp[i].global_rep_count=0; |
| 475 | temp[i].unique_rep_count=0; |
| 476 | temp[i].rep_count=0; |
| 477 | temp[i].fldcw_count=0; |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 478 | temp[i].bbtrace_fp=NULL; |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 479 | } |
| 480 | /* expand the inst_counter on all allocated basic blocks */ |
| 481 | VG_(OSetGen_ResetIter)(instr_info_table); |
| 482 | while ( (bb_elem = VG_(OSetGen_Next)(instr_info_table)) ) { |
| 483 | bb_elem->inst_counter = |
| 484 | VG_(realloc)("bbv_main.c inst_counter", |
| 485 | bb_elem->inst_counter, |
| 486 | new_number*sizeof(Int)); |
| 487 | for(i=old_number;i<new_number;i++) { |
| 488 | bb_elem->inst_counter[i]=0; |
| 489 | } |
| 490 | } |
| 491 | |
| 492 | return temp; |
| 493 | } |
| 494 | |
| 495 | static void bbv_thread_called ( ThreadId tid, ULong nDisp ) |
| 496 | { |
| 497 | if (tid >= allocated_threads) { |
| 498 | bbv_thread=allocate_new_thread(bbv_thread,allocated_threads,tid+1); |
| 499 | allocated_threads=tid+1; |
| 500 | } |
| 501 | current_thread=tid; |
| 502 | } |
| 503 | |
| 504 | |
| 505 | |
| 506 | |
| 507 | /*--------------------------------------------------------------------*/ |
| 508 | /*--- Setup ---*/ |
| 509 | /*--------------------------------------------------------------------*/ |
| 510 | |
| 511 | static void bbv_post_clo_init(void) |
| 512 | { |
| 513 | bb_out_file = |
| 514 | VG_(expand_file_name)("--bb-out-file", clo_bb_out_file); |
| 515 | |
| 516 | /* Try a closer approximation of basic blocks */ |
| 517 | /* This is the same as the command line option */ |
| 518 | /* --vex-guest-chase-thresh=0 */ |
| 519 | VG_(clo_vex_control).guest_chase_thresh = 0; |
| 520 | } |
| 521 | |
| 522 | /* Parse the command line options */ |
florian | 19f91bb | 2012-11-10 22:29:54 +0000 | [diff] [blame] | 523 | static Bool bbv_process_cmd_line_option(const HChar* arg) |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 524 | { |
| 525 | if VG_INT_CLO (arg, "--interval-size", interval_size) {} |
| 526 | else if VG_STR_CLO (arg, "--bb-out-file", clo_bb_out_file) {} |
| 527 | else if VG_STR_CLO (arg, "--pc-out-file", clo_pc_out_file) { |
| 528 | generate_pc_file = True; |
| 529 | } |
njn | 738184a | 2009-08-05 23:59:05 +0000 | [diff] [blame] | 530 | else if VG_BOOL_CLO (arg, "--instr-count-only", instr_count_only) {} |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 531 | else { |
| 532 | return False; |
| 533 | } |
| 534 | |
| 535 | return True; |
| 536 | } |
| 537 | |
| 538 | static void bbv_print_usage(void) |
| 539 | { |
njn | 738184a | 2009-08-05 23:59:05 +0000 | [diff] [blame] | 540 | VG_(printf)( |
| 541 | " --bb-out-file=<file> filename for BBV info\n" |
| 542 | " --pc-out-file=<file> filename for BB addresses and function names\n" |
| 543 | " --interval-size=<num> interval size\n" |
| 544 | " --instr-count-only=yes|no only print total instruction count\n" |
| 545 | ); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 546 | } |
| 547 | |
| 548 | static void bbv_print_debug_usage(void) |
| 549 | { |
| 550 | VG_(printf)(" (none)\n"); |
| 551 | } |
| 552 | |
| 553 | static void bbv_fini(Int exitcode) |
| 554 | { |
| 555 | Int i; |
| 556 | |
| 557 | if (generate_pc_file) { |
| 558 | dumpPcFile(); |
| 559 | } |
| 560 | |
| 561 | for(i=0;i<allocated_threads;i++) { |
| 562 | |
| 563 | if (bbv_thread[i].total_instr!=0) { |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 564 | HChar buf[500]; // large enough |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 565 | VG_(sprintf)(buf,"\n\n" |
| 566 | "# Thread %d\n" |
| 567 | "# Total intervals: %d (Interval Size %d)\n" |
florian | 16eef85 | 2015-08-03 21:05:20 +0000 | [diff] [blame] | 568 | "# Total instructions: %llu\n" |
| 569 | "# Total reps: %llu\n" |
| 570 | "# Unique reps: %llu\n" |
| 571 | "# Total fldcw instructions: %llu\n\n", |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 572 | i, |
| 573 | (Int)(bbv_thread[i].total_instr/(ULong)interval_size), |
| 574 | interval_size, |
| 575 | bbv_thread[i].total_instr, |
| 576 | bbv_thread[i].global_rep_count, |
| 577 | bbv_thread[i].unique_rep_count, |
| 578 | bbv_thread[i].fldcw_count); |
| 579 | |
| 580 | /* Print results to display */ |
sewardj | 0cf3e78 | 2009-07-15 14:50:37 +0000 | [diff] [blame] | 581 | VG_(umsg)("%s\n", buf); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 582 | |
| 583 | /* open the output file if it hasn't already */ |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 584 | if (bbv_thread[i].bbtrace_fp == NULL) { |
| 585 | bbv_thread[i].bbtrace_fp=open_tracefile(i); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 586 | } |
| 587 | /* Also print to results file */ |
florian | 12d2eb5 | 2014-10-30 22:17:56 +0000 | [diff] [blame] | 588 | VG_(fprintf)(bbv_thread[i].bbtrace_fp, "%s", buf); |
| 589 | VG_(fclose)(bbv_thread[i].bbtrace_fp); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 590 | } |
| 591 | } |
| 592 | } |
| 593 | |
| 594 | static void bbv_pre_clo_init(void) |
| 595 | { |
| 596 | VG_(details_name) ("exp-bbv"); |
| 597 | VG_(details_version) (NULL); |
| 598 | VG_(details_description) ("a SimPoint basic block vector generator"); |
| 599 | VG_(details_copyright_author)( |
Elliott Hughes | ed39800 | 2017-06-21 14:41:24 -0700 | [diff] [blame^] | 600 | "Copyright (C) 2006-2017 Vince Weaver"); |
njn | dbebecc | 2009-07-14 01:39:54 +0000 | [diff] [blame] | 601 | VG_(details_bug_reports_to) (VG_BUGS_TO); |
| 602 | |
| 603 | VG_(basic_tool_funcs) (bbv_post_clo_init, |
| 604 | bbv_instrument, |
| 605 | bbv_fini); |
| 606 | |
| 607 | VG_(needs_command_line_options)(bbv_process_cmd_line_option, |
| 608 | bbv_print_usage, |
| 609 | bbv_print_debug_usage); |
| 610 | |
| 611 | VG_(track_start_client_code)( bbv_thread_called ); |
| 612 | |
| 613 | |
| 614 | instr_info_table = VG_(OSetGen_Create)(/*keyOff*/0, |
| 615 | NULL, |
| 616 | VG_(malloc), "bbv.1", VG_(free)); |
| 617 | |
| 618 | bbv_thread=allocate_new_thread(bbv_thread,0,allocated_threads); |
| 619 | } |
| 620 | |
| 621 | VG_DETERMINE_INTERFACE_VERSION(bbv_pre_clo_init) |
| 622 | |
| 623 | /*--------------------------------------------------------------------*/ |
| 624 | /*--- end ---*/ |
| 625 | /*--------------------------------------------------------------------*/ |