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