lk_main.c revision 7a26f022e1879920c6306f26f00577edd4b449cb
1 2/*--------------------------------------------------------------------*/ 3/*--- An example Valgrind tool. ---*/ 4/*--- lk_main.c ---*/ 5/*--------------------------------------------------------------------*/ 6 7/* 8 This file is part of Lackey, an example Valgrind tool that does 9 some simple program measurement. 10 11 Copyright (C) 2002-2005 Nicholas Nethercote 12 njn@valgrind.org 13 14 This program is free software; you can redistribute it and/or 15 modify it under the terms of the GNU General Public License as 16 published by the Free Software Foundation; either version 2 of the 17 License, or (at your option) any later version. 18 19 This program is distributed in the hope that it will be useful, but 20 WITHOUT ANY WARRANTY; without even the implied warranty of 21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 General Public License for more details. 23 24 You should have received a copy of the GNU General Public License 25 along with this program; if not, write to the Free Software 26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 27 02111-1307, USA. 28 29 The GNU General Public License is contained in the file COPYING. 30*/ 31 32#include "pub_tool_basics.h" 33#include "pub_tool_tooliface.h" 34#include "pub_tool_libcassert.h" 35#include "pub_tool_libcprint.h" 36#include "pub_tool_debuginfo.h" 37#include "pub_tool_libcbase.h" 38#include "pub_tool_options.h" 39 40 41/* The name of the function of which the number of calls is to be 42 * counted, with default. Override with command line option 43 * --fnname. */ 44static Char* lk_clo_fnname = "_dl_runtime_resolve"; 45 46/* If true, show statistics about loads, stores and alu ops. Set 47 * with command line option --detailed-counts. */ 48static Bool lk_clo_detailed_counts = False; 49 50/*********************************************************************** 51 * Implement the needs_command_line_options for Valgrind. 52 **********************************************************************/ 53 54static Bool lk_process_cmd_line_option(Char* arg) 55{ 56 VG_STR_CLO(arg, "--fnname", lk_clo_fnname) 57 else VG_BOOL_CLO(arg, "--detailed-counts", lk_clo_detailed_counts) 58 else 59 return False; 60 61 tl_assert(lk_clo_fnname); 62 tl_assert(lk_clo_fnname[0]); 63 return True; 64} 65 66static void lk_print_usage(void) 67{ 68 VG_(printf)( 69" --fnname=<name> count calls to <name> [_dl_runtime_resolve]\n" 70" --detailed-counts=no|yes count loads, stores and alu ops [no]\n" 71 ); 72} 73 74static void lk_print_debug_usage(void) 75{ 76} 77 78/*********************************************************************** 79 * Data and helpers related to the default operation of Lackey. 80 **********************************************************************/ 81 82/* Nb: use ULongs because the numbers can get very big */ 83static ULong n_func_calls = 0; 84static ULong n_BBs_entered = 0; 85static ULong n_BBs_completed = 0; 86static ULong n_IRStmts = 0; 87static ULong n_guest_instrs = 0; 88static ULong n_Jccs = 0; 89static ULong n_Jccs_untaken = 0; 90 91__attribute__((unused)) 92static void add_one_func_call(void) 93{ 94 n_func_calls++; 95} 96 97static void add_one_BB_entered(void) 98{ 99 n_BBs_entered++; 100} 101 102static void add_one_BB_completed(void) 103{ 104 n_BBs_completed++; 105} 106 107__attribute__((unused)) 108static void add_one_IRStmt(void) 109{ 110 n_IRStmts++; 111} 112 113__attribute__((unused)) 114static void add_one_guest_instr(void) 115{ 116 n_guest_instrs++; 117} 118 119static void add_one_Jcc(void) 120{ 121 n_Jccs++; 122} 123 124static void add_one_Jcc_untaken(void) 125{ 126 n_Jccs_untaken++; 127} 128 129/*********************************************************************** 130 * Data and helpers related to --detailed-counts. 131 **********************************************************************/ 132 133/* --- Operations --- */ 134 135typedef enum { OpLoad=0, OpStore=1, OpAlu=2 } Op; 136 137#define N_OPS 3 138 139 140/* --- Types --- */ 141 142#define N_TYPES 9 143 144static Int type2index ( IRType ty ) 145{ 146 switch (ty) { 147 case Ity_I1: return 0; 148 case Ity_I8: return 1; 149 case Ity_I16: return 2; 150 case Ity_I32: return 3; 151 case Ity_I64: return 4; 152 case Ity_I128: return 5; 153 case Ity_F32: return 6; 154 case Ity_F64: return 7; 155 case Ity_V128: return 8; 156 default: tl_assert(0); break; 157 } 158} 159 160static HChar* nameOfTypeIndex ( IRType ty ) 161{ 162 switch (ty) { 163 case 0: return "I1"; break; 164 case 1: return "I8"; break; 165 case 2: return "I16"; break; 166 case 3: return "I32"; break; 167 case 4: return "I64"; break; 168 case 5: return "I128"; break; 169 case 6: return "F32"; break; 170 case 7: return "F64"; break; 171 case 8: return "V128"; break; 172 default: tl_assert(0); break; 173 } 174} 175 176 177/* --- Counts --- */ 178 179static ULong detailCounts[N_OPS][N_TYPES]; 180 181/* The helper that is called from the instrumented code. */ 182static VG_REGPARM(1) 183void increment_detail(ULong* detail) 184{ 185 (*detail)++; 186} 187 188/* A helper that adds the instrumentation for a detail. */ 189static void instrument_detail(IRBB* bb, Op op, IRType type) 190{ 191 IRDirty* di; 192 IRExpr** argv; 193 const UInt typeIx = type2index(type); 194 195 tl_assert(op < N_OPS); 196 tl_assert(typeIx < N_TYPES); 197 198 argv = mkIRExprVec_1( mkIRExpr_HWord( (HWord)&detailCounts[op][typeIx] ) ); 199 di = unsafeIRDirty_0_N( 1, "increment_detail", &increment_detail, argv); 200 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 201} 202 203/* Summarize and print the details. */ 204 205static void print_details ( void ) 206{ 207 Int typeIx; 208 VG_(message)(Vg_UserMsg, 209 " Type Loads Stores AluOps"); 210 VG_(message)(Vg_UserMsg, 211 " -------------------------------------------"); 212 for (typeIx = 0; typeIx < N_TYPES; typeIx++) { 213 VG_(message)(Vg_UserMsg, 214 " %4s %,12llu %,12llu %,12llu", 215 nameOfTypeIndex( typeIx ), 216 detailCounts[OpLoad ][typeIx], 217 detailCounts[OpStore][typeIx], 218 detailCounts[OpAlu ][typeIx] 219 ); 220 } 221} 222 223 224/*********************************************************************** 225 * Implement the basic_tool_funcs for Valgrind. 226 **********************************************************************/ 227 228static void lk_post_clo_init(void) 229{ 230 Int op, tyIx; 231 232 for (op = 0; op < N_OPS; op++) 233 for (tyIx = 0; tyIx < N_TYPES; tyIx++) 234 detailCounts[op][tyIx] = 0; 235} 236 237static 238IRBB* lk_instrument( IRBB* bb_in, VexGuestLayout* layout, 239 Addr64 orig_addr_noredir, VexGuestExtents* vge, 240 IRType gWordTy, IRType hWordTy ) 241{ 242 IRDirty* di; 243 Int i; 244 IRBB* bb; 245 Char fnname[100]; 246 IRType type; 247 248 if (gWordTy != hWordTy) { 249 /* We don't currently support this case. */ 250 VG_(tool_panic)("host/guest word size mismatch"); 251 } 252 253 /* Set up BB */ 254 bb = emptyIRBB(); 255 bb->tyenv = dopyIRTypeEnv(bb_in->tyenv); 256 bb->next = dopyIRExpr(bb_in->next); 257 bb->jumpkind = bb_in->jumpkind; 258 259 // Copy verbatim any IR preamble preceding the first IMark 260 i = 0; 261 while (i < bb_in->stmts_used && bb_in->stmts[i]->tag != Ist_IMark) { 262 addStmtToIRBB( bb, bb_in->stmts[i] ); 263 i++; 264 } 265 266 /* Count this basic block. */ 267 di = unsafeIRDirty_0_N( 0, "add_one_BB_entered", &add_one_BB_entered, 268 mkIRExprVec_0() ); 269 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 270 271 for (/*use current i*/; i < bb_in->stmts_used; i++) { 272 IRStmt* st = bb_in->stmts[i]; 273 if (!st || st->tag == Ist_NoOp) continue; 274 275 /* Count one VEX statement. */ 276 di = unsafeIRDirty_0_N( 0, "add_one_IRStmt", &add_one_IRStmt, 277 mkIRExprVec_0() ); 278 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 279 280 switch (st->tag) { 281 case Ist_IMark: 282 /* Count guest instruction. */ 283 di = unsafeIRDirty_0_N( 0, "add_one_guest_instr", 284 &add_one_guest_instr, 285 mkIRExprVec_0() ); 286 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 287 288 /* An unconditional branch to a known destination in the 289 * guest's instructions can be represented, in the IRBB to 290 * instrument, by the VEX statements that are the 291 * translation of that known destination. This feature is 292 * called 'BB chasing' and can be influenced by command 293 * line option --vex-guest-chase-thresh. 294 * 295 * To get an accurate count of the calls to a specific 296 * function, taking BB chasing into account, we need to 297 * check for each guest instruction (Ist_IMark) if it is 298 * the entry point of a function. 299 */ 300 tl_assert(lk_clo_fnname); 301 tl_assert(lk_clo_fnname[0]); 302 if (VG_(get_fnname_if_entry)(st->Ist.IMark.addr, 303 fnname, sizeof(fnname)) 304 && 0 == VG_(strcmp)(fnname, lk_clo_fnname)) { 305 di = unsafeIRDirty_0_N( 0, "add_one_func_call", 306 &add_one_func_call, 307 mkIRExprVec_0() ); 308 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 309 } 310 addStmtToIRBB( bb, st ); 311 break; 312 313 case Ist_Exit: 314 /* Count Jcc */ 315 di = unsafeIRDirty_0_N( 0, "add_one_Jcc", &add_one_Jcc, 316 mkIRExprVec_0() ); 317 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 318 319 addStmtToIRBB( bb, st ); 320 321 /* Count non-taken Jcc */ 322 di = unsafeIRDirty_0_N( 0, "add_one_Jcc_untaken", 323 &add_one_Jcc_untaken, mkIRExprVec_0() ); 324 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 325 break; 326 327 /* Someone on the users list asked for something like this 328 * just the other day (Christian Stimming, "Fast profiling in 329 * valgrind?", 25 Oct). Personally I think it'd be a 330 * valuable addition. 331 * 332 * Not hard to do either: for stores, examine Ist_Store, and 333 * use typeOfIRExpr(bb->tyenv, st->Ist.Store.data) to get the 334 * store type. For loads and ALU ops, you only need to look 335 * at Ist_Tmp cases where the Ist.Tmp.data is either Iex_Load 336 * or Iex_{Unop,Binop}. All statements you will ever 337 * encounter will satisfy isFlatIRStmt which essentially 338 * constrains them to being flat SSA-style. 339 */ 340 case Ist_Store: 341 if (lk_clo_detailed_counts) { 342 type = typeOfIRExpr(bb->tyenv, st->Ist.Store.data); 343 tl_assert(type != Ity_INVALID); 344 instrument_detail( bb, OpStore, type ); 345 } 346 addStmtToIRBB( bb, st ); 347 break; 348 349 case Ist_Tmp: 350 if (lk_clo_detailed_counts) { 351 IRExpr* expr = st->Ist.Tmp.data; 352 type = typeOfIRExpr(bb->tyenv, expr); 353 tl_assert(type != Ity_INVALID); 354 switch (expr->tag) { 355 case Iex_Load: 356 instrument_detail( bb, OpLoad, type ); 357 break; 358 case Iex_Unop: 359 case Iex_Binop: 360 case Iex_Mux0X: 361 instrument_detail( bb, OpAlu, type ); 362 break; 363 default: 364 break; 365 } 366 } 367 addStmtToIRBB( bb, st ); 368 break; 369 370 default: 371 addStmtToIRBB( bb, st ); 372 } 373 } 374 375 /* Count this basic block. */ 376 di = unsafeIRDirty_0_N( 0, "add_one_BB_completed", 377 &add_one_BB_completed, mkIRExprVec_0() ); 378 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 379 380 return bb; 381} 382 383static void lk_fini(Int exitcode) 384{ 385 char percentify_buf[4]; /* Two digits, '%' and 0. */ 386 const int percentify_size = sizeof(percentify_buf); 387 const int percentify_decs = 0; 388 389 tl_assert(lk_clo_fnname); 390 tl_assert(lk_clo_fnname[0]); 391 VG_(message)(Vg_UserMsg, 392 "Counted %,llu calls to %s()", n_func_calls, lk_clo_fnname); 393 394 VG_(message)(Vg_UserMsg, ""); 395 VG_(message)(Vg_UserMsg, "Jccs:"); 396 VG_(message)(Vg_UserMsg, " total: %,llu", n_Jccs); 397 VG_(percentify)((n_Jccs - n_Jccs_untaken), (n_Jccs ? n_Jccs : 1), 398 percentify_decs, percentify_size, percentify_buf); 399 VG_(message)(Vg_UserMsg, " taken: %,llu (%s)", 400 (n_Jccs - n_Jccs_untaken), percentify_buf); 401 402 VG_(message)(Vg_UserMsg, ""); 403 VG_(message)(Vg_UserMsg, "Executed:"); 404 VG_(message)(Vg_UserMsg, " BBs entered: %,llu", n_BBs_entered); 405 VG_(message)(Vg_UserMsg, " BBs completed: %,llu", n_BBs_completed); 406 VG_(message)(Vg_UserMsg, " guest instrs: %,llu", n_guest_instrs); 407 VG_(message)(Vg_UserMsg, " IRStmts: %,llu", n_IRStmts); 408 409 VG_(message)(Vg_UserMsg, ""); 410 VG_(message)(Vg_UserMsg, "Ratios:"); 411 tl_assert(n_BBs_entered); // Paranoia time. 412 VG_(message)(Vg_UserMsg, " guest instrs : BB entered = %3u : 10", 413 10 * n_guest_instrs / n_BBs_entered); 414 VG_(message)(Vg_UserMsg, " IRStmts : BB entered = %3u : 10", 415 10 * n_IRStmts / n_BBs_entered); 416 tl_assert(n_guest_instrs); // Paranoia time. 417 VG_(message)(Vg_UserMsg, " IRStmts : guest instr = %3u : 10", 418 10 * n_IRStmts / n_guest_instrs); 419 420 if (lk_clo_detailed_counts) { 421 VG_(message)(Vg_UserMsg, ""); 422 VG_(message)(Vg_UserMsg, "IR-level counts by type:"); 423 print_details(); 424 } 425 426 VG_(message)(Vg_UserMsg, ""); 427 VG_(message)(Vg_UserMsg, "Exit code: %d", exitcode); 428} 429 430static void lk_pre_clo_init(void) 431{ 432 VG_(details_name) ("Lackey"); 433 VG_(details_version) (NULL); 434 VG_(details_description) ("an example Valgrind tool"); 435 VG_(details_copyright_author)( 436 "Copyright (C) 2002-2005, and GNU GPL'd, by Nicholas Nethercote."); 437 VG_(details_bug_reports_to) (VG_BUGS_TO); 438 VG_(details_avg_translation_sizeB) ( 175 ); 439 440 VG_(basic_tool_funcs) (lk_post_clo_init, 441 lk_instrument, 442 lk_fini); 443 VG_(needs_command_line_options)(lk_process_cmd_line_option, 444 lk_print_usage, 445 lk_print_debug_usage); 446} 447 448VG_DETERMINE_INTERFACE_VERSION(lk_pre_clo_init) 449 450/*--------------------------------------------------------------------*/ 451/*--- end lk_main.c ---*/ 452/*--------------------------------------------------------------------*/ 453