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