lk_main.c revision 5fed8c07ba7759b1e2eb0a5eae1b953577b308fa
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#include "pub_tool_machine.h" // VG_(fnptr_to_fnentry) 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", 200 VG_(fnptr_to_fnentry)( &increment_detail ), 201 argv); 202 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 203} 204 205/* Summarize and print the details. */ 206 207static void print_details ( void ) 208{ 209 Int typeIx; 210 VG_(message)(Vg_UserMsg, 211 " Type Loads Stores AluOps"); 212 VG_(message)(Vg_UserMsg, 213 " -------------------------------------------"); 214 for (typeIx = 0; typeIx < N_TYPES; typeIx++) { 215 VG_(message)(Vg_UserMsg, 216 " %4s %,12llu %,12llu %,12llu", 217 nameOfTypeIndex( typeIx ), 218 detailCounts[OpLoad ][typeIx], 219 detailCounts[OpStore][typeIx], 220 detailCounts[OpAlu ][typeIx] 221 ); 222 } 223} 224 225 226/*********************************************************************** 227 * Implement the basic_tool_funcs for Valgrind. 228 **********************************************************************/ 229 230static void lk_post_clo_init(void) 231{ 232 Int op, tyIx; 233 234 for (op = 0; op < N_OPS; op++) 235 for (tyIx = 0; tyIx < N_TYPES; tyIx++) 236 detailCounts[op][tyIx] = 0; 237} 238 239static 240IRBB* lk_instrument( IRBB* bb_in, VexGuestLayout* layout, 241 Addr64 orig_addr_noredir, VexGuestExtents* vge, 242 IRType gWordTy, IRType hWordTy ) 243{ 244 IRDirty* di; 245 Int i; 246 IRBB* bb; 247 Char fnname[100]; 248 IRType type; 249 250 if (gWordTy != hWordTy) { 251 /* We don't currently support this case. */ 252 VG_(tool_panic)("host/guest word size mismatch"); 253 } 254 255 /* Set up BB */ 256 bb = emptyIRBB(); 257 bb->tyenv = dopyIRTypeEnv(bb_in->tyenv); 258 bb->next = dopyIRExpr(bb_in->next); 259 bb->jumpkind = bb_in->jumpkind; 260 261 // Copy verbatim any IR preamble preceding the first IMark 262 i = 0; 263 while (i < bb_in->stmts_used && bb_in->stmts[i]->tag != Ist_IMark) { 264 addStmtToIRBB( bb, bb_in->stmts[i] ); 265 i++; 266 } 267 268 /* Count this basic block. */ 269 di = unsafeIRDirty_0_N( 0, "add_one_BB_entered", 270 VG_(fnptr_to_fnentry)( &add_one_BB_entered ), 271 mkIRExprVec_0() ); 272 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 273 274 for (/*use current i*/; i < bb_in->stmts_used; i++) { 275 IRStmt* st = bb_in->stmts[i]; 276 if (!st || st->tag == Ist_NoOp) continue; 277 278 /* Count one VEX statement. */ 279 di = unsafeIRDirty_0_N( 0, "add_one_IRStmt", 280 VG_(fnptr_to_fnentry)( &add_one_IRStmt ), 281 mkIRExprVec_0() ); 282 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 283 284 switch (st->tag) { 285 case Ist_IMark: 286 /* Count guest instruction. */ 287 di = unsafeIRDirty_0_N( 0, "add_one_guest_instr", 288 VG_(fnptr_to_fnentry)( &add_one_guest_instr ), 289 mkIRExprVec_0() ); 290 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 291 292 /* An unconditional branch to a known destination in the 293 * guest's instructions can be represented, in the IRBB to 294 * instrument, by the VEX statements that are the 295 * translation of that known destination. This feature is 296 * called 'BB chasing' and can be influenced by command 297 * line option --vex-guest-chase-thresh. 298 * 299 * To get an accurate count of the calls to a specific 300 * function, taking BB chasing into account, we need to 301 * check for each guest instruction (Ist_IMark) if it is 302 * the entry point of a function. 303 */ 304 tl_assert(lk_clo_fnname); 305 tl_assert(lk_clo_fnname[0]); 306 if (VG_(get_fnname_if_entry)(st->Ist.IMark.addr, 307 fnname, sizeof(fnname)) 308 && 0 == VG_(strcmp)(fnname, lk_clo_fnname)) { 309 di = unsafeIRDirty_0_N( 310 0, "add_one_func_call", 311 VG_(fnptr_to_fnentry)( &add_one_func_call ), 312 mkIRExprVec_0() ); 313 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 314 } 315 addStmtToIRBB( bb, st ); 316 break; 317 318 case Ist_Exit: 319 /* Count Jcc */ 320 di = unsafeIRDirty_0_N( 0, "add_one_Jcc", 321 VG_(fnptr_to_fnentry)( &add_one_Jcc ), 322 mkIRExprVec_0() ); 323 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 324 325 addStmtToIRBB( bb, st ); 326 327 /* Count non-taken Jcc */ 328 di = unsafeIRDirty_0_N( 0, "add_one_Jcc_untaken", 329 VG_(fnptr_to_fnentry)( &add_one_Jcc_untaken ), 330 mkIRExprVec_0() ); 331 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 332 break; 333 334 /* Someone on the users list asked for something like this 335 * just the other day (Christian Stimming, "Fast profiling in 336 * valgrind?", 25 Oct). Personally I think it'd be a 337 * valuable addition. 338 * 339 * Not hard to do either: for stores, examine Ist_Store, and 340 * use typeOfIRExpr(bb->tyenv, st->Ist.Store.data) to get the 341 * store type. For loads and ALU ops, you only need to look 342 * at Ist_Tmp cases where the Ist.Tmp.data is either Iex_Load 343 * or Iex_{Unop,Binop}. All statements you will ever 344 * encounter will satisfy isFlatIRStmt which essentially 345 * constrains them to being flat SSA-style. 346 */ 347 case Ist_Store: 348 if (lk_clo_detailed_counts) { 349 type = typeOfIRExpr(bb->tyenv, st->Ist.Store.data); 350 tl_assert(type != Ity_INVALID); 351 instrument_detail( bb, OpStore, type ); 352 } 353 addStmtToIRBB( bb, st ); 354 break; 355 356 case Ist_Tmp: 357 if (lk_clo_detailed_counts) { 358 IRExpr* expr = st->Ist.Tmp.data; 359 type = typeOfIRExpr(bb->tyenv, expr); 360 tl_assert(type != Ity_INVALID); 361 switch (expr->tag) { 362 case Iex_Load: 363 instrument_detail( bb, OpLoad, type ); 364 break; 365 case Iex_Unop: 366 case Iex_Binop: 367 case Iex_Mux0X: 368 instrument_detail( bb, OpAlu, type ); 369 break; 370 default: 371 break; 372 } 373 } 374 addStmtToIRBB( bb, st ); 375 break; 376 377 default: 378 addStmtToIRBB( bb, st ); 379 } 380 } 381 382 /* Count this basic block. */ 383 di = unsafeIRDirty_0_N( 0, "add_one_BB_completed", 384 VG_(fnptr_to_fnentry)( &add_one_BB_completed ), 385 mkIRExprVec_0() ); 386 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 387 388 return bb; 389} 390 391static void lk_fini(Int exitcode) 392{ 393 char percentify_buf[4]; /* Two digits, '%' and 0. */ 394 const int percentify_size = sizeof(percentify_buf); 395 const int percentify_decs = 0; 396 397 tl_assert(lk_clo_fnname); 398 tl_assert(lk_clo_fnname[0]); 399 VG_(message)(Vg_UserMsg, 400 "Counted %,llu calls to %s()", n_func_calls, lk_clo_fnname); 401 402 VG_(message)(Vg_UserMsg, ""); 403 VG_(message)(Vg_UserMsg, "Jccs:"); 404 VG_(message)(Vg_UserMsg, " total: %,llu", n_Jccs); 405 VG_(percentify)((n_Jccs - n_Jccs_untaken), (n_Jccs ? n_Jccs : 1), 406 percentify_decs, percentify_size, percentify_buf); 407 VG_(message)(Vg_UserMsg, " taken: %,llu (%s)", 408 (n_Jccs - n_Jccs_untaken), percentify_buf); 409 410 VG_(message)(Vg_UserMsg, ""); 411 VG_(message)(Vg_UserMsg, "Executed:"); 412 VG_(message)(Vg_UserMsg, " BBs entered: %,llu", n_BBs_entered); 413 VG_(message)(Vg_UserMsg, " BBs completed: %,llu", n_BBs_completed); 414 VG_(message)(Vg_UserMsg, " guest instrs: %,llu", n_guest_instrs); 415 VG_(message)(Vg_UserMsg, " IRStmts: %,llu", n_IRStmts); 416 417 VG_(message)(Vg_UserMsg, ""); 418 VG_(message)(Vg_UserMsg, "Ratios:"); 419 tl_assert(n_BBs_entered); // Paranoia time. 420 VG_(message)(Vg_UserMsg, " guest instrs : BB entered = %3u : 10", 421 10 * n_guest_instrs / n_BBs_entered); 422 VG_(message)(Vg_UserMsg, " IRStmts : BB entered = %3u : 10", 423 10 * n_IRStmts / n_BBs_entered); 424 tl_assert(n_guest_instrs); // Paranoia time. 425 VG_(message)(Vg_UserMsg, " IRStmts : guest instr = %3u : 10", 426 10 * n_IRStmts / n_guest_instrs); 427 428 if (lk_clo_detailed_counts) { 429 VG_(message)(Vg_UserMsg, ""); 430 VG_(message)(Vg_UserMsg, "IR-level counts by type:"); 431 print_details(); 432 } 433 434 VG_(message)(Vg_UserMsg, ""); 435 VG_(message)(Vg_UserMsg, "Exit code: %d", exitcode); 436} 437 438static void lk_pre_clo_init(void) 439{ 440 VG_(details_name) ("Lackey"); 441 VG_(details_version) (NULL); 442 VG_(details_description) ("an example Valgrind tool"); 443 VG_(details_copyright_author)( 444 "Copyright (C) 2002-2005, and GNU GPL'd, by Nicholas Nethercote."); 445 VG_(details_bug_reports_to) (VG_BUGS_TO); 446 VG_(details_avg_translation_sizeB) ( 175 ); 447 448 VG_(basic_tool_funcs) (lk_post_clo_init, 449 lk_instrument, 450 lk_fini); 451 VG_(needs_command_line_options)(lk_process_cmd_line_option, 452 lk_print_usage, 453 lk_print_debug_usage); 454} 455 456VG_DETERMINE_INTERFACE_VERSION(lk_pre_clo_init) 457 458/*--------------------------------------------------------------------*/ 459/*--- end lk_main.c ---*/ 460/*--------------------------------------------------------------------*/ 461