lk_main.c revision fd73ebb56f414b2d1807160cabffab4ebb20e3ab
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// This tool shows how to do some basic instrumentation. 33// 34// In particular, if you are interested in tracing every load and store a 35// program does, use the --trace-mem=yes option. Please note that the 36// address trace is good, but not perfect; see Section 3.3.7 of Nicholas 37// Nethercote's PhD dissertation "Dynamic Binary Analysis and 38// Instrumentation", 2004, for details about the few loads and stores that 39// it misses, and other caveats about the accuracy of the address trace. 40// 41// If you want to modify how the memory traces are printed/gathered, look at 42// the code that is controlled by the variable 'lk_clo_trace_mem' and the 43// functions 'trace_load()' and 'trace_mem'.. With a bit of effort you 44// should be able to see which other bits of code can be removed, if that's 45// what you want. If you want to do more complex modifications, please read 46// VEX/pub/libvex_ir.h to understand the intermediate representation. 47// 48// For further inspiration, you should look at cachegrind/cg_main.c which 49// handles memory accesses in a more sophisticated way -- it groups them 50// together for processing into twos and threes so that fewer C calls are 51// made and things run faster. 52 53#include "pub_tool_basics.h" 54#include "pub_tool_tooliface.h" 55#include "pub_tool_libcassert.h" 56#include "pub_tool_libcprint.h" 57#include "pub_tool_debuginfo.h" 58#include "pub_tool_libcbase.h" 59#include "pub_tool_options.h" 60#include "pub_tool_machine.h" // VG_(fnptr_to_fnentry) 61 62/* The name of the function of which the number of calls is to be 63 * counted, with default. Override with command line option 64 * --fnname. */ 65static Char* lk_clo_fnname = "_dl_runtime_resolve"; 66 67/* If true, show statistics about loads, stores and alu ops. Set 68 * with command line option --detailed-counts. */ 69static Bool lk_clo_detailed_counts = False; 70 71/* If true, print the trace of loads and stores. Set with --trace-mem. */ 72static Bool lk_clo_trace_mem = False; 73 74/*********************************************************************** 75 * Implement the needs_command_line_options for Valgrind. 76 **********************************************************************/ 77 78static Bool lk_process_cmd_line_option(Char* arg) 79{ 80 VG_STR_CLO(arg, "--fnname", lk_clo_fnname) 81 else VG_BOOL_CLO(arg, "--detailed-counts", lk_clo_detailed_counts) 82 else VG_BOOL_CLO(arg, "--trace-mem", lk_clo_trace_mem) 83 else 84 return False; 85 86 tl_assert(lk_clo_fnname); 87 tl_assert(lk_clo_fnname[0]); 88 return True; 89} 90 91static void lk_print_usage(void) 92{ 93 VG_(printf)( 94" --fnname=<name> count calls to <name> [_dl_runtime_resolve]\n" 95" --detailed-counts=no|yes count loads, stores and alu ops [no]\n" 96 ); 97} 98 99static void lk_print_debug_usage(void) 100{ 101} 102 103/*********************************************************************** 104 * Data and helpers related to the default operation of Lackey. 105 **********************************************************************/ 106 107/* Nb: use ULongs because the numbers can get very big */ 108static ULong n_func_calls = 0; 109static ULong n_BBs_entered = 0; 110static ULong n_BBs_completed = 0; 111static ULong n_IRStmts = 0; 112static ULong n_guest_instrs = 0; 113static ULong n_Jccs = 0; 114static ULong n_Jccs_untaken = 0; 115 116static void add_one_func_call(void) 117{ 118 n_func_calls++; 119} 120 121static void add_one_BB_entered(void) 122{ 123 n_BBs_entered++; 124} 125 126static void add_one_BB_completed(void) 127{ 128 n_BBs_completed++; 129} 130 131static void add_one_IRStmt(void) 132{ 133 n_IRStmts++; 134} 135 136static void add_one_guest_instr(void) 137{ 138 n_guest_instrs++; 139} 140 141static void add_one_Jcc(void) 142{ 143 n_Jccs++; 144} 145 146static void add_one_Jcc_untaken(void) 147{ 148 n_Jccs_untaken++; 149} 150 151/*********************************************************************** 152 * Data and helpers related to --detailed-counts. 153 **********************************************************************/ 154 155/* --- Operations --- */ 156 157typedef enum { OpLoad=0, OpStore=1, OpAlu=2 } Op; 158 159#define N_OPS 3 160 161 162/* --- Types --- */ 163 164#define N_TYPES 9 165 166static Int type2index ( IRType ty ) 167{ 168 switch (ty) { 169 case Ity_I1: return 0; 170 case Ity_I8: return 1; 171 case Ity_I16: return 2; 172 case Ity_I32: return 3; 173 case Ity_I64: return 4; 174 case Ity_I128: return 5; 175 case Ity_F32: return 6; 176 case Ity_F64: return 7; 177 case Ity_V128: return 8; 178 default: tl_assert(0); break; 179 } 180} 181 182static HChar* nameOfTypeIndex ( IRType ty ) 183{ 184 switch (ty) { 185 case 0: return "I1"; break; 186 case 1: return "I8"; break; 187 case 2: return "I16"; break; 188 case 3: return "I32"; break; 189 case 4: return "I64"; break; 190 case 5: return "I128"; break; 191 case 6: return "F32"; break; 192 case 7: return "F64"; break; 193 case 8: return "V128"; break; 194 default: tl_assert(0); break; 195 } 196} 197 198 199/* --- Counts --- */ 200 201static ULong detailCounts[N_OPS][N_TYPES]; 202 203/* The helper that is called from the instrumented code. */ 204static VG_REGPARM(1) 205void increment_detail(ULong* detail) 206{ 207 (*detail)++; 208} 209 210/* A helper that adds the instrumentation for a detail. */ 211static void instrument_detail(IRBB* bb, Op op, IRType type) 212{ 213 IRDirty* di; 214 IRExpr** argv; 215 const UInt typeIx = type2index(type); 216 217 tl_assert(op < N_OPS); 218 tl_assert(typeIx < N_TYPES); 219 220 argv = mkIRExprVec_1( mkIRExpr_HWord( (HWord)&detailCounts[op][typeIx] ) ); 221 di = unsafeIRDirty_0_N( 1, "increment_detail", 222 VG_(fnptr_to_fnentry)( &increment_detail ), 223 argv); 224 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 225} 226 227/* Summarize and print the details. */ 228 229static void print_details ( void ) 230{ 231 Int typeIx; 232 VG_(message)(Vg_UserMsg, 233 " Type Loads Stores AluOps"); 234 VG_(message)(Vg_UserMsg, 235 " -------------------------------------------"); 236 for (typeIx = 0; typeIx < N_TYPES; typeIx++) { 237 VG_(message)(Vg_UserMsg, 238 " %4s %,12llu %,12llu %,12llu", 239 nameOfTypeIndex( typeIx ), 240 detailCounts[OpLoad ][typeIx], 241 detailCounts[OpStore][typeIx], 242 detailCounts[OpAlu ][typeIx] 243 ); 244 } 245} 246 247 248/*********************************************************************** 249 * Data and helpers related to --trace-mem. 250 **********************************************************************/ 251 252static VG_REGPARM(2) void trace_load(Addr addr, SizeT size) 253{ 254 VG_(printf)("load : %p, %d\n", addr, size); 255} 256 257static VG_REGPARM(2) void trace_store(Addr addr, SizeT size) 258{ 259 VG_(printf)("store: %p, %d\n", addr, size); 260} 261 262/*********************************************************************** 263 * Implement the basic_tool_funcs for Valgrind. 264 **********************************************************************/ 265 266static void lk_post_clo_init(void) 267{ 268 Int op, tyIx; 269 270 for (op = 0; op < N_OPS; op++) 271 for (tyIx = 0; tyIx < N_TYPES; tyIx++) 272 detailCounts[op][tyIx] = 0; 273} 274 275static 276IRBB* lk_instrument( IRBB* bb_in, VexGuestLayout* layout, 277 Addr64 orig_addr_noredir, VexGuestExtents* vge, 278 IRType gWordTy, IRType hWordTy ) 279{ 280 IRDirty* di; 281 Int i; 282 IRBB* bb; 283 Char fnname[100]; 284 IRType type; 285 IRExpr** argv; 286 IRExpr* addr_expr; 287 IRExpr* size_expr; 288 289 if (gWordTy != hWordTy) { 290 /* We don't currently support this case. */ 291 VG_(tool_panic)("host/guest word size mismatch"); 292 } 293 294 /* Set up BB */ 295 bb = emptyIRBB(); 296 bb->tyenv = dopyIRTypeEnv(bb_in->tyenv); 297 bb->next = dopyIRExpr(bb_in->next); 298 bb->jumpkind = bb_in->jumpkind; 299 300 // Copy verbatim any IR preamble preceding the first IMark 301 i = 0; 302 while (i < bb_in->stmts_used && bb_in->stmts[i]->tag != Ist_IMark) { 303 addStmtToIRBB( bb, bb_in->stmts[i] ); 304 i++; 305 } 306 307 /* Count this basic block. */ 308 di = unsafeIRDirty_0_N( 0, "add_one_BB_entered", 309 VG_(fnptr_to_fnentry)( &add_one_BB_entered ), 310 mkIRExprVec_0() ); 311 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 312 313 for (/*use current i*/; i < bb_in->stmts_used; i++) { 314 IRStmt* st = bb_in->stmts[i]; 315 if (!st || st->tag == Ist_NoOp) continue; 316 317 /* Count one VEX statement. */ 318 di = unsafeIRDirty_0_N( 0, "add_one_IRStmt", 319 VG_(fnptr_to_fnentry)( &add_one_IRStmt ), 320 mkIRExprVec_0() ); 321 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 322 323 switch (st->tag) { 324 case Ist_IMark: 325 /* Count guest instruction. */ 326 di = unsafeIRDirty_0_N( 0, "add_one_guest_instr", 327 VG_(fnptr_to_fnentry)( &add_one_guest_instr ), 328 mkIRExprVec_0() ); 329 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 330 331 /* An unconditional branch to a known destination in the 332 * guest's instructions can be represented, in the IRBB to 333 * instrument, by the VEX statements that are the 334 * translation of that known destination. This feature is 335 * called 'BB chasing' and can be influenced by command 336 * line option --vex-guest-chase-thresh. 337 * 338 * To get an accurate count of the calls to a specific 339 * function, taking BB chasing into account, we need to 340 * check for each guest instruction (Ist_IMark) if it is 341 * the entry point of a function. 342 */ 343 tl_assert(lk_clo_fnname); 344 tl_assert(lk_clo_fnname[0]); 345 if (VG_(get_fnname_if_entry)(st->Ist.IMark.addr, 346 fnname, sizeof(fnname)) 347 && 0 == VG_(strcmp)(fnname, lk_clo_fnname)) { 348 di = unsafeIRDirty_0_N( 349 0, "add_one_func_call", 350 VG_(fnptr_to_fnentry)( &add_one_func_call ), 351 mkIRExprVec_0() ); 352 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 353 } 354 addStmtToIRBB( bb, st ); 355 break; 356 357 case Ist_Exit: 358 /* Count Jcc */ 359 di = unsafeIRDirty_0_N( 0, "add_one_Jcc", 360 VG_(fnptr_to_fnentry)( &add_one_Jcc ), 361 mkIRExprVec_0() ); 362 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 363 364 addStmtToIRBB( bb, st ); 365 366 /* Count non-taken Jcc */ 367 di = unsafeIRDirty_0_N( 0, "add_one_Jcc_untaken", 368 VG_(fnptr_to_fnentry)( &add_one_Jcc_untaken ), 369 mkIRExprVec_0() ); 370 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 371 break; 372 373 /* Someone on the users list asked for something like this 374 * just the other day (Christian Stimming, "Fast profiling in 375 * valgrind?", 25 Oct). Personally I think it'd be a 376 * valuable addition. 377 * 378 * Not hard to do either: for stores, examine Ist_Store, and 379 * use typeOfIRExpr(bb->tyenv, st->Ist.Store.data) to get the 380 * store type. For loads and ALU ops, you only need to look 381 * at Ist_Tmp cases where the Ist.Tmp.data is either Iex_Load 382 * or Iex_{Unop,Binop}. All statements you will ever 383 * encounter will satisfy isFlatIRStmt which essentially 384 * constrains them to being flat SSA-style. 385 */ 386 case Ist_Store: 387 // Add a call to trace_store() if --trace-mem=yes. 388 if (lk_clo_trace_mem) { 389 addr_expr = st->Ist.Store.addr; 390 size_expr = mkIRExpr_HWord( 391 sizeofIRType( 392 typeOfIRExpr(bb->tyenv, st->Ist.Store.data))); 393 argv = mkIRExprVec_2( addr_expr, size_expr ); 394 di = unsafeIRDirty_0_N( /*regparms*/2, 395 "trace_store", 396 VG_(fnptr_to_fnentry)( trace_store ), 397 argv ); 398 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 399 } 400 if (lk_clo_detailed_counts) { 401 type = typeOfIRExpr(bb->tyenv, st->Ist.Store.data); 402 tl_assert(type != Ity_INVALID); 403 instrument_detail( bb, OpStore, type ); 404 } 405 addStmtToIRBB( bb, st ); 406 break; 407 408 case Ist_Tmp: 409 // Add a call to trace_load() if --trace-mem=yes. 410 if (lk_clo_trace_mem) { 411 IRExpr* data = st->Ist.Tmp.data; 412 if (data->tag == Iex_Load) { 413 addr_expr = data->Iex.Load.addr; 414 size_expr = mkIRExpr_HWord( sizeofIRType(data->Iex.Load.ty) ); 415 argv = mkIRExprVec_2( addr_expr, size_expr ); 416 di = unsafeIRDirty_0_N( /*regparms*/2, 417 "trace_load", 418 VG_(fnptr_to_fnentry)( trace_load ), 419 argv ); 420 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 421 } 422 } 423 if (lk_clo_detailed_counts) { 424 IRExpr* expr = st->Ist.Tmp.data; 425 type = typeOfIRExpr(bb->tyenv, expr); 426 tl_assert(type != Ity_INVALID); 427 switch (expr->tag) { 428 case Iex_Load: 429 instrument_detail( bb, OpLoad, type ); 430 break; 431 case Iex_Unop: 432 case Iex_Binop: 433 case Iex_Mux0X: 434 instrument_detail( bb, OpAlu, type ); 435 break; 436 default: 437 break; 438 } 439 } 440 addStmtToIRBB( bb, st ); 441 break; 442 443 default: 444 addStmtToIRBB( bb, st ); 445 } 446 } 447 448 /* Count this basic block. */ 449 di = unsafeIRDirty_0_N( 0, "add_one_BB_completed", 450 VG_(fnptr_to_fnentry)( &add_one_BB_completed ), 451 mkIRExprVec_0() ); 452 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 453 454 return bb; 455} 456 457static void lk_fini(Int exitcode) 458{ 459 char percentify_buf[4]; /* Two digits, '%' and 0. */ 460 const int percentify_size = sizeof(percentify_buf); 461 const int percentify_decs = 0; 462 463 tl_assert(lk_clo_fnname); 464 tl_assert(lk_clo_fnname[0]); 465 VG_(message)(Vg_UserMsg, 466 "Counted %,llu calls to %s()", n_func_calls, lk_clo_fnname); 467 468 VG_(message)(Vg_UserMsg, ""); 469 VG_(message)(Vg_UserMsg, "Jccs:"); 470 VG_(message)(Vg_UserMsg, " total: %,llu", n_Jccs); 471 VG_(percentify)((n_Jccs - n_Jccs_untaken), (n_Jccs ? n_Jccs : 1), 472 percentify_decs, percentify_size, percentify_buf); 473 VG_(message)(Vg_UserMsg, " taken: %,llu (%s)", 474 (n_Jccs - n_Jccs_untaken), percentify_buf); 475 476 VG_(message)(Vg_UserMsg, ""); 477 VG_(message)(Vg_UserMsg, "Executed:"); 478 VG_(message)(Vg_UserMsg, " BBs entered: %,llu", n_BBs_entered); 479 VG_(message)(Vg_UserMsg, " BBs completed: %,llu", n_BBs_completed); 480 VG_(message)(Vg_UserMsg, " guest instrs: %,llu", n_guest_instrs); 481 VG_(message)(Vg_UserMsg, " IRStmts: %,llu", n_IRStmts); 482 483 VG_(message)(Vg_UserMsg, ""); 484 VG_(message)(Vg_UserMsg, "Ratios:"); 485 tl_assert(n_BBs_entered); // Paranoia time. 486 VG_(message)(Vg_UserMsg, " guest instrs : BB entered = %3u : 10", 487 10 * n_guest_instrs / n_BBs_entered); 488 VG_(message)(Vg_UserMsg, " IRStmts : BB entered = %3u : 10", 489 10 * n_IRStmts / n_BBs_entered); 490 tl_assert(n_guest_instrs); // Paranoia time. 491 VG_(message)(Vg_UserMsg, " IRStmts : guest instr = %3u : 10", 492 10 * n_IRStmts / n_guest_instrs); 493 494 if (lk_clo_detailed_counts) { 495 VG_(message)(Vg_UserMsg, ""); 496 VG_(message)(Vg_UserMsg, "IR-level counts by type:"); 497 print_details(); 498 } 499 500 VG_(message)(Vg_UserMsg, ""); 501 VG_(message)(Vg_UserMsg, "Exit code: %d", exitcode); 502} 503 504static void lk_pre_clo_init(void) 505{ 506 VG_(details_name) ("Lackey"); 507 VG_(details_version) (NULL); 508 VG_(details_description) ("an example Valgrind tool"); 509 VG_(details_copyright_author)( 510 "Copyright (C) 2002-2005, and GNU GPL'd, by Nicholas Nethercote."); 511 VG_(details_bug_reports_to) (VG_BUGS_TO); 512 VG_(details_avg_translation_sizeB) ( 175 ); 513 514 VG_(basic_tool_funcs) (lk_post_clo_init, 515 lk_instrument, 516 lk_fini); 517 VG_(needs_command_line_options)(lk_process_cmd_line_option, 518 lk_print_usage, 519 lk_print_debug_usage); 520} 521 522VG_DETERMINE_INTERFACE_VERSION(lk_pre_clo_init) 523 524/*--------------------------------------------------------------------*/ 525/*--- end lk_main.c ---*/ 526/*--------------------------------------------------------------------*/ 527