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