lk_main.c revision eaf0ca90c6d7d62b3cedb7e2ebf3add3a692db7a
1 2/*--------------------------------------------------------------------*/ 3/*--- An example Valgrind tool. lk_main.c ---*/ 4/*--------------------------------------------------------------------*/ 5 6/* 7 This file is part of Lackey, an example Valgrind tool that does 8 some simple program measurement and tracing. 9 10 Copyright (C) 2002-2005 Nicholas Nethercote 11 njn@valgrind.org 12 13 This program is free software; you can redistribute it and/or 14 modify it under the terms of the GNU General Public License as 15 published by the Free Software Foundation; either version 2 of the 16 License, or (at your option) any later version. 17 18 This program is distributed in the hope that it will be useful, but 19 WITHOUT ANY WARRANTY; without even the implied warranty of 20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 General Public License for more details. 22 23 You should have received a copy of the GNU General Public License 24 along with this program; if not, write to the Free Software 25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 26 02111-1307, USA. 27 28 The GNU General Public License is contained in the file COPYING. 29*/ 30 31// This tool shows how to do some basic instrumentation. 32// 33// There are three kinds of instrumentation it can do. They can be turned 34// on/off independently with command line options: 35// 36// * --basic-counts : do basic counts, eg. number of instructions 37// executed, jumps executed, etc. 38// * --detailed-counts: do more detailed counts: number of loads, stores 39// and ALU operations of different sizes. 40// * --trace-mem=yes: trace all (data) memory accesses. 41// 42// The code for each kind of instrumentation is guarded by a clo_* variable: 43// clo_basic_counts, clo_detailed_counts and clo_trace_mem. 44// 45// If you want to modify any of the instrumentation code, look for the code 46// that is guarded by the relevant clo_* variable (eg. clo_trace_mem) 47// If you're not interested in the other kinds of instrumentation you can 48// remove them. If you want to do more complex modifications, please read 49// VEX/pub/libvex_ir.h to understand the intermediate representation. 50// 51// 52// Specific Details about --trace-mem=yes 53// -------------------------------------- 54// Lackey's --trace-mem code is a good starting point for building Valgrind 55// tools that act on memory loads and stores. It also could be used as is, 56// with its output used as input to a post-mortem processing step. However, 57// because memory traces can be very large, online analysis is generally 58// better. 59// 60// It prints memory data access traces that look like this: 61// 62// instr : 0x0023C790, 2 # instruction read at 0x0023C790 of size 2 63// instr : 0x0023C792, 5 64// store : 0xBE80199C, 4 # data store at 0xBE80199C of size 4 65// instr : 0x0025242B, 3 66// load : 0xBE801950, 4 # data load at 0xBE801950 of size 4 67// instr : 0x0023D476, 7 68// modify: 0x0025747C, 1 # data modify at 0x0025747C of size 1 69// instr : 0x0023DC20, 2 70// load : 0x00254962, 1 71// load : 0xBE801FB3, 1 72// instr : 0x00252305, 1 73// load : 0x00254AEB, 1 74// store : 0x00257998, 1 75// 76// Every instruction executed has an "instr" event representing it. 77// Instructions that do memory accesses are followed by one or more "load", 78// "store" or "modify" events. Some instructions do more than one load or 79// store, as in the last two examples in the above trace. 80// 81// Here are some examples of x86 instructions that do different combinations 82// of loads, stores, and modifies. 83// 84// Instruction Memory accesses Event sequence 85// ----------- --------------- -------------- 86// add %eax, %ebx No loads or stores instr 87// 88// movl (%eax), %ebx loads (%eax) instr, load 89// 90// movl %eax, (%ebx) stores (%ebx) instr, store 91// 92// incl (%ecx) modifies (%ecx) instr, modify 93// 94// cmpsb loads (%esi), loads(%edi) instr, load, load 95// 96// call*l (%edx) loads (%edx), stores -4(%esp) instr, load, store 97// pushl (%edx) loads (%edx), stores -4(%esp) instr, load, store 98// movsw loads (%esi), stores (%edi) instr, load, store 99// 100// Instructions using x86 "rep" prefixes are traced as if they are repeated 101// N times. 102// 103// Lackey with --trace-mem gives good traces, but they are not perfect, for 104// the following reasons: 105// 106// - It does not trace into the OS kernel, so system calls and other kernel 107// operations (eg. some scheduling and signal handling code) are ignored. 108// 109// - Valgrind replaces some code with its own, notably parts of code for 110// scheduling operations and signal handling. This code is not traced. 111// 112// - There is no consideration of virtual-to-physical address mapping. 113// This may not matter for many purposes. 114// 115// - Valgrind modifies the instruction stream in some very minor ways. For 116// example, on x86 the bts, btc, btr instructions are incorrectly 117// considered to always touch memory (this is a consequence of these 118// instructions being very difficult to simulate). 119// 120// - Valgrind tools layout memory differently to normal programs, so the 121// addresses you get will not be typical. Thus Lackey (and all Valgrind 122// tools) is suitable for getting relative memory traces -- eg. if you 123// want to analyse locality of memory accesses -- but is not good if 124// absolute addresses are important. 125// 126// Despite all these warnings, Dullard's results should be good enough for a 127// wide range of purposes. For example, Cachegrind shares all the above 128// shortcomings and it is still useful. 129// 130// For further inspiration, you should look at cachegrind/cg_main.c which 131// uses the same basic technique for tracing memory accesses, but also groups 132// events together for processing into twos and threes so that fewer C calls 133// are made and things run faster. 134 135#include "pub_tool_basics.h" 136#include "pub_tool_tooliface.h" 137#include "pub_tool_libcassert.h" 138#include "pub_tool_libcprint.h" 139#include "pub_tool_debuginfo.h" 140#include "pub_tool_libcbase.h" 141#include "pub_tool_options.h" 142#include "pub_tool_machine.h" // VG_(fnptr_to_fnentry) 143 144/*------------------------------------------------------------*/ 145/*--- Command line options ---*/ 146/*------------------------------------------------------------*/ 147 148/* Command line options controlling instrumentation kinds, as described at 149 * the top of this file. */ 150static Bool clo_basic_counts = True; 151static Bool clo_detailed_counts = False; 152static Bool clo_trace_mem = False; 153 154/* The name of the function of which the number of calls (under 155 * --basic-counts=yes) is to be counted, with default. Override with command 156 * line option --fnname. */ 157static Char* clo_fnname = "_dl_runtime_resolve"; 158 159static Bool lk_process_cmd_line_option(Char* arg) 160{ 161 VG_STR_CLO(arg, "--fnname", clo_fnname) 162 else VG_BOOL_CLO(arg, "--basic-counts", clo_basic_counts) 163 else VG_BOOL_CLO(arg, "--detailed-counts", clo_detailed_counts) 164 else VG_BOOL_CLO(arg, "--trace-mem", clo_trace_mem) 165 else 166 return False; 167 168 tl_assert(clo_fnname); 169 tl_assert(clo_fnname[0]); 170 return True; 171} 172 173static void lk_print_usage(void) 174{ 175 VG_(printf)( 176" --basic-counts=no|yes count instructions, jumps, etc. [no]\n" 177" --detailed-counts=no|yes count loads, stores and alu ops [no]\n" 178" --trace-mem=no|yes trace all loads and stores [no]\n" 179" --fnname=<name> count calls to <name> (only used if\n" 180" --basic-count=yes) [_dl_runtime_resolve]\n" 181 ); 182} 183 184static void lk_print_debug_usage(void) 185{ 186 VG_(printf)( 187" (none)\n" 188 ); 189} 190 191/*------------------------------------------------------------*/ 192/*--- Stuff for --basic-counts ---*/ 193/*------------------------------------------------------------*/ 194 195/* Nb: use ULongs because the numbers can get very big */ 196static ULong n_func_calls = 0; 197static ULong n_BBs_entered = 0; 198static ULong n_BBs_completed = 0; 199static ULong n_IRStmts = 0; 200static ULong n_guest_instrs = 0; 201static ULong n_Jccs = 0; 202static ULong n_Jccs_untaken = 0; 203 204static void add_one_func_call(void) 205{ 206 n_func_calls++; 207} 208 209static void add_one_BB_entered(void) 210{ 211 n_BBs_entered++; 212} 213 214static void add_one_BB_completed(void) 215{ 216 n_BBs_completed++; 217} 218 219static void add_one_IRStmt(void) 220{ 221 n_IRStmts++; 222} 223 224static void add_one_guest_instr(void) 225{ 226 n_guest_instrs++; 227} 228 229static void add_one_Jcc(void) 230{ 231 n_Jccs++; 232} 233 234static void add_one_Jcc_untaken(void) 235{ 236 n_Jccs_untaken++; 237} 238 239/*------------------------------------------------------------*/ 240/*--- Stuff for --detailed-counts ---*/ 241/*------------------------------------------------------------*/ 242 243/* --- Operations --- */ 244 245typedef enum { OpLoad=0, OpStore=1, OpAlu=2 } Op; 246 247#define N_OPS 3 248 249 250/* --- Types --- */ 251 252#define N_TYPES 9 253 254static Int type2index ( IRType ty ) 255{ 256 switch (ty) { 257 case Ity_I1: return 0; 258 case Ity_I8: return 1; 259 case Ity_I16: return 2; 260 case Ity_I32: return 3; 261 case Ity_I64: return 4; 262 case Ity_I128: return 5; 263 case Ity_F32: return 6; 264 case Ity_F64: return 7; 265 case Ity_V128: return 8; 266 default: tl_assert(0); break; 267 } 268} 269 270static HChar* nameOfTypeIndex ( IRType ty ) 271{ 272 switch (ty) { 273 case 0: return "I1"; break; 274 case 1: return "I8"; break; 275 case 2: return "I16"; break; 276 case 3: return "I32"; break; 277 case 4: return "I64"; break; 278 case 5: return "I128"; break; 279 case 6: return "F32"; break; 280 case 7: return "F64"; break; 281 case 8: return "V128"; break; 282 default: tl_assert(0); break; 283 } 284} 285 286 287/* --- Counts --- */ 288 289static ULong detailCounts[N_OPS][N_TYPES]; 290 291/* The helper that is called from the instrumented code. */ 292static VG_REGPARM(1) 293void increment_detail(ULong* detail) 294{ 295 (*detail)++; 296} 297 298/* A helper that adds the instrumentation for a detail. */ 299static void instrument_detail(IRBB* bb, Op op, IRType type) 300{ 301 IRDirty* di; 302 IRExpr** argv; 303 const UInt typeIx = type2index(type); 304 305 tl_assert(op < N_OPS); 306 tl_assert(typeIx < N_TYPES); 307 308 argv = mkIRExprVec_1( mkIRExpr_HWord( (HWord)&detailCounts[op][typeIx] ) ); 309 di = unsafeIRDirty_0_N( 1, "increment_detail", 310 VG_(fnptr_to_fnentry)( &increment_detail ), 311 argv); 312 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 313} 314 315/* Summarize and print the details. */ 316static void print_details ( void ) 317{ 318 Int typeIx; 319 VG_(message)(Vg_UserMsg, 320 " Type Loads Stores AluOps"); 321 VG_(message)(Vg_UserMsg, 322 " -------------------------------------------"); 323 for (typeIx = 0; typeIx < N_TYPES; typeIx++) { 324 VG_(message)(Vg_UserMsg, 325 " %4s %,12llu %,12llu %,12llu", 326 nameOfTypeIndex( typeIx ), 327 detailCounts[OpLoad ][typeIx], 328 detailCounts[OpStore][typeIx], 329 detailCounts[OpAlu ][typeIx] 330 ); 331 } 332} 333 334 335/*------------------------------------------------------------*/ 336/*--- Stuff for --trace-mem ---*/ 337/*------------------------------------------------------------*/ 338 339#define MAX_DSIZE 512 340 341typedef 342 IRExpr 343 IRAtom; 344 345typedef 346 enum { Event_Ir, Event_Dr, Event_Dw, Event_Dm } 347 EventKind; 348 349typedef 350 struct { 351 EventKind ekind; 352 IRAtom* addr; 353 Int size; 354 } 355 Event; 356 357/* Up to this many unnotified events are allowed. Must be at least two, 358 so that reads and writes to the same address can be merged into a modify. 359 Beyond that, larger numbers just potentially induce more spilling due to 360 extending live ranges of address temporaries. */ 361#define N_EVENTS 4 362 363/* Maintain an ordered list of memory events which are outstanding, in 364 the sense that no IR has yet been generated to do the relevant 365 helper calls. The BB is scanned top to bottom and memory events 366 are added to the end of the list, merging with the most recent 367 notified event where possible (Dw immediately following Dr and 368 having the same size and EA can be merged). 369 370 This merging is done so that for architectures which have 371 load-op-store instructions (x86, amd64), the instr is treated as if 372 it makes just one memory reference (a modify), rather than two (a 373 read followed by a write at the same address). 374 375 At various points the list will need to be flushed, that is, IR 376 generated from it. That must happen before any possible exit from 377 the block (the end, or an IRStmt_Exit). Flushing also takes place 378 when there is no space to add a new event. 379 380 If we require the simulation statistics to be up to date with 381 respect to possible memory exceptions, then the list would have to 382 be flushed before each memory reference. That's a pain so we don't 383 bother. 384 385 Flushing the list consists of walking it start to end and emitting 386 instrumentation IR for each event, in the order in which they 387 appear. */ 388 389static Event events[N_EVENTS]; 390static Int events_used = 0; 391 392 393static VG_REGPARM(2) void trace_instr(Addr addr, SizeT size) 394{ 395 VG_(printf)("instr : %08p, %d\n", addr, size); 396} 397 398static VG_REGPARM(2) void trace_load(Addr addr, SizeT size) 399{ 400 VG_(printf)(" load : %08p, %d\n", addr, size); 401} 402 403static VG_REGPARM(2) void trace_store(Addr addr, SizeT size) 404{ 405 VG_(printf)(" store : %08p, %d\n", addr, size); 406} 407 408static VG_REGPARM(2) void trace_modify(Addr addr, SizeT size) 409{ 410 VG_(printf)(" modify: %08p, %d\n", addr, size); 411} 412 413 414static void flushEvents(IRBB* bb) 415{ 416 Int i; 417 Char* helperName; 418 void* helperAddr; 419 IRExpr** argv; 420 IRDirty* di; 421 Event* ev; 422 423 for (i = 0; i < events_used; i++) { 424 425 ev = &events[i]; 426 427 // Decide on helper fn to call and args to pass it. 428 switch (ev->ekind) { 429 case Event_Ir: helperName = "trace_instr"; 430 helperAddr = trace_instr; break; 431 432 case Event_Dr: helperName = "trace_load"; 433 helperAddr = trace_load; break; 434 435 case Event_Dw: helperName = "trace_store"; 436 helperAddr = trace_store; break; 437 438 case Event_Dm: helperName = "trace_modify"; 439 helperAddr = trace_modify; break; 440 default: 441 tl_assert(0); 442 } 443 444 // Add the helper. 445 argv = mkIRExprVec_2( ev->addr, mkIRExpr_HWord( ev->size ) ); 446 di = unsafeIRDirty_0_N( /*regparms*/2, 447 helperName, VG_(fnptr_to_fnentry)( helperAddr ), 448 argv ); 449 addStmtToIRBB( bb, IRStmt_Dirty(di) ); 450 } 451 452 events_used = 0; 453} 454 455// WARNING: If you aren't interested in instruction reads, you can omit the 456// code that adds calls to trace_instr() in flushEvents(). However, you 457// must still call this function, addEvent_Ir() -- it is necessary to add 458// the Ir events to the events list so that merging of paired load/store 459// events into modify events works correctly. 460static void addEvent_Ir ( IRBB* bb, IRAtom* iaddr, UInt isize ) 461{ 462 Event* evt; 463 tl_assert( (VG_MIN_INSTR_SZB <= isize && isize <= VG_MAX_INSTR_SZB) 464 || VG_CLREQ_SZB == isize ); 465 if (events_used == N_EVENTS) 466 flushEvents(bb); 467 tl_assert(events_used >= 0 && events_used < N_EVENTS); 468 evt = &events[events_used]; 469 evt->ekind = Event_Ir; 470 evt->addr = iaddr; 471 evt->size = isize; 472 events_used++; 473} 474 475static 476void addEvent_Dr ( IRBB* bb, IRAtom* daddr, Int dsize ) 477{ 478 Event* evt; 479 tl_assert(isIRAtom(daddr)); 480 tl_assert(dsize >= 1 && dsize <= MAX_DSIZE); 481 if (events_used == N_EVENTS) 482 flushEvents(bb); 483 tl_assert(events_used >= 0 && events_used < N_EVENTS); 484 evt = &events[events_used]; 485 evt->ekind = Event_Dr; 486 evt->addr = daddr; 487 evt->size = dsize; 488 events_used++; 489} 490 491static 492void addEvent_Dw ( IRBB* bb, IRAtom* daddr, Int dsize ) 493{ 494 Event* lastEvt; 495 Event* evt; 496 tl_assert(isIRAtom(daddr)); 497 tl_assert(dsize >= 1 && dsize <= MAX_DSIZE); 498 499 // Is it possible to merge this write with the preceding read? 500 lastEvt = &events[events_used-1]; 501 if (events_used > 0 502 && lastEvt->ekind == Event_Dr 503 && lastEvt->size == dsize 504 && eqIRAtom(lastEvt->addr, daddr)) 505 { 506 lastEvt->ekind = Event_Dm; 507 return; 508 } 509 510 // No. Add as normal. 511 if (events_used == N_EVENTS) 512 flushEvents(bb); 513 tl_assert(events_used >= 0 && events_used < N_EVENTS); 514 evt = &events[events_used]; 515 evt->ekind = Event_Dw; 516 evt->size = dsize; 517 evt->addr = daddr; 518 events_used++; 519} 520 521 522/*------------------------------------------------------------*/ 523/*--- Basic tool functions ---*/ 524/*------------------------------------------------------------*/ 525 526static void lk_post_clo_init(void) 527{ 528 Int op, tyIx; 529 530 if (clo_detailed_counts) { 531 for (op = 0; op < N_OPS; op++) 532 for (tyIx = 0; tyIx < N_TYPES; tyIx++) 533 detailCounts[op][tyIx] = 0; 534 } 535} 536 537static 538IRBB* lk_instrument ( VgCallbackClosure* closure, 539 IRBB* bbIn, 540 VexGuestLayout* layout, 541 VexGuestExtents* vge, 542 IRType gWordTy, IRType hWordTy ) 543{ 544 IRDirty* di; 545 Int i; 546 IRBB* bbOut; 547 Char fnname[100]; 548 IRType type; 549 IRTypeEnv* tyenv = bbIn->tyenv; 550 551 if (gWordTy != hWordTy) { 552 /* We don't currently support this case. */ 553 VG_(tool_panic)("host/guest word size mismatch"); 554 } 555 556 /* Set up BB */ 557 bbOut = emptyIRBB(); 558 bbOut->tyenv = dopyIRTypeEnv(bbIn->tyenv); 559 bbOut->next = dopyIRExpr(bbIn->next); 560 bbOut->jumpkind = bbIn->jumpkind; 561 562 // Copy verbatim any IR preamble preceding the first IMark 563 i = 0; 564 while (i < bbIn->stmts_used && bbIn->stmts[i]->tag != Ist_IMark) { 565 addStmtToIRBB( bbOut, bbIn->stmts[i] ); 566 i++; 567 } 568 569 if (clo_basic_counts) { 570 /* Count this basic block. */ 571 di = unsafeIRDirty_0_N( 0, "add_one_BB_entered", 572 VG_(fnptr_to_fnentry)( &add_one_BB_entered ), 573 mkIRExprVec_0() ); 574 addStmtToIRBB( bbOut, IRStmt_Dirty(di) ); 575 } 576 577 if (clo_trace_mem) { 578 events_used = 0; 579 } 580 581 for (/*use current i*/; i < bbIn->stmts_used; i++) { 582 IRStmt* st = bbIn->stmts[i]; 583 if (!st || st->tag == Ist_NoOp) continue; 584 585 if (clo_basic_counts) { 586 /* Count one VEX statement. */ 587 di = unsafeIRDirty_0_N( 0, "add_one_IRStmt", 588 VG_(fnptr_to_fnentry)( &add_one_IRStmt ), 589 mkIRExprVec_0() ); 590 addStmtToIRBB( bbOut, IRStmt_Dirty(di) ); 591 } 592 593 switch (st->tag) { 594 case Ist_NoOp: 595 case Ist_AbiHint: 596 case Ist_Put: 597 case Ist_PutI: 598 case Ist_MFence: 599 addStmtToIRBB( bbOut, st ); 600 break; 601 602 case Ist_IMark: 603 if (clo_basic_counts) { 604 /* Count guest instruction. */ 605 di = unsafeIRDirty_0_N( 0, "add_one_guest_instr", 606 VG_(fnptr_to_fnentry)( &add_one_guest_instr ), 607 mkIRExprVec_0() ); 608 addStmtToIRBB( bbOut, IRStmt_Dirty(di) ); 609 610 /* An unconditional branch to a known destination in the 611 * guest's instructions can be represented, in the IRBB to 612 * instrument, by the VEX statements that are the 613 * translation of that known destination. This feature is 614 * called 'BB chasing' and can be influenced by command 615 * line option --vex-guest-chase-thresh. 616 * 617 * To get an accurate count of the calls to a specific 618 * function, taking BB chasing into account, we need to 619 * check for each guest instruction (Ist_IMark) if it is 620 * the entry point of a function. 621 */ 622 tl_assert(clo_fnname); 623 tl_assert(clo_fnname[0]); 624 if (VG_(get_fnname_if_entry)(st->Ist.IMark.addr, 625 fnname, sizeof(fnname)) 626 && 0 == VG_(strcmp)(fnname, clo_fnname)) { 627 di = unsafeIRDirty_0_N( 628 0, "add_one_func_call", 629 VG_(fnptr_to_fnentry)( &add_one_func_call ), 630 mkIRExprVec_0() ); 631 addStmtToIRBB( bbOut, IRStmt_Dirty(di) ); 632 } 633 } 634 if (clo_trace_mem) { 635 // WARNING: do not remove this function call, even if you 636 // aren't interested in instruction reads. See the comment 637 // above the function itself for more detail. 638 addEvent_Ir( bbOut, mkIRExpr_HWord( (HWord)st->Ist.IMark.addr ), 639 st->Ist.IMark.len ); 640 } 641 addStmtToIRBB( bbOut, st ); 642 break; 643 644 case Ist_Tmp: 645 // Add a call to trace_load() if --trace-mem=yes. 646 if (clo_trace_mem) { 647 IRExpr* data = st->Ist.Tmp.data; 648 if (data->tag == Iex_Load) { 649 addEvent_Dr( bbOut, data->Iex.Load.addr, 650 sizeofIRType(data->Iex.Load.ty) ); 651 } 652 } 653 if (clo_detailed_counts) { 654 IRExpr* expr = st->Ist.Tmp.data; 655 type = typeOfIRExpr(bbOut->tyenv, expr); 656 tl_assert(type != Ity_INVALID); 657 switch (expr->tag) { 658 case Iex_Load: 659 instrument_detail( bbOut, OpLoad, type ); 660 break; 661 case Iex_Unop: 662 case Iex_Binop: 663 case Iex_Triop: 664 case Iex_Qop: 665 case Iex_Mux0X: 666 instrument_detail( bbOut, OpAlu, type ); 667 break; 668 default: 669 break; 670 } 671 } 672 addStmtToIRBB( bbOut, st ); 673 break; 674 675 case Ist_Store: 676 if (clo_trace_mem) { 677 IRExpr* data = st->Ist.Store.data; 678 addEvent_Dw( bbOut, st->Ist.Store.addr, 679 sizeofIRType(typeOfIRExpr(tyenv, data)) ); 680 } 681 if (clo_detailed_counts) { 682 type = typeOfIRExpr(bbOut->tyenv, st->Ist.Store.data); 683 tl_assert(type != Ity_INVALID); 684 instrument_detail( bbOut, OpStore, type ); 685 } 686 addStmtToIRBB( bbOut, st ); 687 break; 688 689 case Ist_Dirty: { 690 Int dsize; 691 IRDirty* d = st->Ist.Dirty.details; 692 if (d->mFx != Ifx_None) { 693 // This dirty helper accesses memory. Collect the details. 694 tl_assert(d->mAddr != NULL); 695 tl_assert(d->mSize != 0); 696 dsize = d->mSize; 697 if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) 698 addEvent_Dr( bbOut, d->mAddr, dsize ); 699 if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) 700 addEvent_Dw( bbOut, d->mAddr, dsize ); 701 } else { 702 tl_assert(d->mAddr == NULL); 703 tl_assert(d->mSize == 0); 704 } 705 addStmtToIRBB( bbOut, st ); 706 break; 707 } 708 709 case Ist_Exit: 710 if (clo_basic_counts) { 711 /* Count Jcc */ 712 di = unsafeIRDirty_0_N( 0, "add_one_Jcc", 713 VG_(fnptr_to_fnentry)( &add_one_Jcc ), 714 mkIRExprVec_0() ); 715 addStmtToIRBB( bbOut, IRStmt_Dirty(di) ); 716 } 717 if (clo_trace_mem) { 718 flushEvents(bbOut); 719 } 720 721 addStmtToIRBB( bbOut, st ); // Original statement 722 723 if (clo_basic_counts) { 724 /* Count non-taken Jcc */ 725 di = unsafeIRDirty_0_N( 0, "add_one_Jcc_untaken", 726 VG_(fnptr_to_fnentry)( 727 &add_one_Jcc_untaken ), 728 mkIRExprVec_0() ); 729 addStmtToIRBB( bbOut, IRStmt_Dirty(di) ); 730 } 731 break; 732 733 default: 734 tl_assert(0); 735 } 736 } 737 738 if (clo_basic_counts) { 739 /* Count this basic block. */ 740 di = unsafeIRDirty_0_N( 0, "add_one_BB_completed", 741 VG_(fnptr_to_fnentry)( &add_one_BB_completed ), 742 mkIRExprVec_0() ); 743 addStmtToIRBB( bbOut, IRStmt_Dirty(di) ); 744 } 745 746 if (clo_trace_mem) { 747 /* At the end of the bbIn. Flush outstandings. */ 748 flushEvents(bbOut); 749 } 750 751 return bbOut; 752} 753 754static void lk_fini(Int exitcode) 755{ 756 char percentify_buf[4]; /* Two digits, '%' and 0. */ 757 const int percentify_size = sizeof(percentify_buf); 758 const int percentify_decs = 0; 759 760 tl_assert(clo_fnname); 761 tl_assert(clo_fnname[0]); 762 763 if (clo_basic_counts) { 764 VG_(message)(Vg_UserMsg, 765 "Counted %,llu calls to %s()", n_func_calls, clo_fnname); 766 767 VG_(message)(Vg_UserMsg, ""); 768 VG_(message)(Vg_UserMsg, "Jccs:"); 769 VG_(message)(Vg_UserMsg, " total: %,llu", n_Jccs); 770 VG_(percentify)((n_Jccs - n_Jccs_untaken), (n_Jccs ? n_Jccs : 1), 771 percentify_decs, percentify_size, percentify_buf); 772 VG_(message)(Vg_UserMsg, " taken: %,llu (%s)", 773 (n_Jccs - n_Jccs_untaken), percentify_buf); 774 775 VG_(message)(Vg_UserMsg, ""); 776 VG_(message)(Vg_UserMsg, "Executed:"); 777 VG_(message)(Vg_UserMsg, " BBs entered: %,llu", n_BBs_entered); 778 VG_(message)(Vg_UserMsg, " BBs completed: %,llu", n_BBs_completed); 779 VG_(message)(Vg_UserMsg, " guest instrs: %,llu", n_guest_instrs); 780 VG_(message)(Vg_UserMsg, " IRStmts: %,llu", n_IRStmts); 781 782 VG_(message)(Vg_UserMsg, ""); 783 VG_(message)(Vg_UserMsg, "Ratios:"); 784 tl_assert(n_BBs_entered); // Paranoia time. 785 VG_(message)(Vg_UserMsg, " guest instrs : BB entered = %3u : 10", 786 10 * n_guest_instrs / n_BBs_entered); 787 VG_(message)(Vg_UserMsg, " IRStmts : BB entered = %3u : 10", 788 10 * n_IRStmts / n_BBs_entered); 789 tl_assert(n_guest_instrs); // Paranoia time. 790 VG_(message)(Vg_UserMsg, " IRStmts : guest instr = %3u : 10", 791 10 * n_IRStmts / n_guest_instrs); 792 } 793 794 if (clo_detailed_counts) { 795 VG_(message)(Vg_UserMsg, ""); 796 VG_(message)(Vg_UserMsg, "IR-level counts by type:"); 797 print_details(); 798 } 799 800 if (clo_basic_counts) { 801 VG_(message)(Vg_UserMsg, ""); 802 VG_(message)(Vg_UserMsg, "Exit code: %d", exitcode); 803 } 804} 805 806static void lk_pre_clo_init(void) 807{ 808 VG_(details_name) ("Lackey"); 809 VG_(details_version) (NULL); 810 VG_(details_description) ("an example Valgrind tool"); 811 VG_(details_copyright_author)( 812 "Copyright (C) 2002-2005, and GNU GPL'd, by Nicholas Nethercote."); 813 VG_(details_bug_reports_to) (VG_BUGS_TO); 814 VG_(details_avg_translation_sizeB) ( 175 ); 815 816 VG_(basic_tool_funcs) (lk_post_clo_init, 817 lk_instrument, 818 lk_fini); 819 VG_(needs_command_line_options)(lk_process_cmd_line_option, 820 lk_print_usage, 821 lk_print_debug_usage); 822} 823 824VG_DETERMINE_INTERFACE_VERSION(lk_pre_clo_init) 825 826/*--------------------------------------------------------------------*/ 827/*--- end lk_main.c ---*/ 828/*--------------------------------------------------------------------*/ 829