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