blob: dfb05a625525e4641caf57bb5c2a3ae8808985f5 [file] [log] [blame]
njnc9539842002-10-02 13:26:35 +00001
njn25e49d8e72002-09-23 09:36:25 +00002/*--------------------------------------------------------------------*/
sewardj7a26f022005-11-01 17:52:34 +00003/*--- An example Valgrind tool. ---*/
njn25cac76cb2002-09-23 11:21:57 +00004/*--- lk_main.c ---*/
njn25e49d8e72002-09-23 09:36:25 +00005/*--------------------------------------------------------------------*/
6
7/*
nethercote137bc552003-11-14 17:47:54 +00008 This file is part of Lackey, an example Valgrind tool that does
njnc9539842002-10-02 13:26:35 +00009 some simple program measurement.
njn25e49d8e72002-09-23 09:36:25 +000010
njn53612422005-03-12 16:22:54 +000011 Copyright (C) 2002-2005 Nicholas Nethercote
njn2bc10122005-05-08 02:10:27 +000012 njn@valgrind.org
njn25e49d8e72002-09-23 09:36:25 +000013
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
njnc7561b92005-06-19 01:24:32 +000032#include "pub_tool_basics.h"
njn43b9a8a2005-05-10 04:37:01 +000033#include "pub_tool_tooliface.h"
njnf39e9a32005-06-12 02:43:17 +000034#include "pub_tool_libcassert.h"
njn36a20fa2005-06-03 03:08:39 +000035#include "pub_tool_libcprint.h"
sewardj7a26f022005-11-01 17:52:34 +000036#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 **********************************************************************/
njn25e49d8e72002-09-23 09:36:25 +000081
njn25e49d8e72002-09-23 09:36:25 +000082/* Nb: use ULongs because the numbers can get very big */
sewardj7a26f022005-11-01 17:52:34 +000083static 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;
njn25e49d8e72002-09-23 09:36:25 +000090
njn69f80cf2005-06-11 01:29:23 +000091__attribute__((unused))
sewardj7a26f022005-11-01 17:52:34 +000092static void add_one_func_call(void)
njn25e49d8e72002-09-23 09:36:25 +000093{
sewardj7a26f022005-11-01 17:52:34 +000094 n_func_calls++;
njn25e49d8e72002-09-23 09:36:25 +000095}
96
sewardj7a26f022005-11-01 17:52:34 +000097static void add_one_BB_entered(void)
njn25e49d8e72002-09-23 09:36:25 +000098{
sewardj7a26f022005-11-01 17:52:34 +000099 n_BBs_entered++;
100}
101
102static void add_one_BB_completed(void)
103{
104 n_BBs_completed++;
njn25e49d8e72002-09-23 09:36:25 +0000105}
106
njn69f80cf2005-06-11 01:29:23 +0000107__attribute__((unused))
sewardj7a26f022005-11-01 17:52:34 +0000108static void add_one_IRStmt(void)
njn25e49d8e72002-09-23 09:36:25 +0000109{
sewardj7a26f022005-11-01 17:52:34 +0000110 n_IRStmts++;
njn25e49d8e72002-09-23 09:36:25 +0000111}
112
njn69f80cf2005-06-11 01:29:23 +0000113__attribute__((unused))
sewardj9f649aa2004-11-22 20:38:40 +0000114static void add_one_guest_instr(void)
njn25e49d8e72002-09-23 09:36:25 +0000115{
sewardj9f649aa2004-11-22 20:38:40 +0000116 n_guest_instrs++;
njn25e49d8e72002-09-23 09:36:25 +0000117}
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
sewardj7a26f022005-11-01 17:52:34 +0000129/***********************************************************************
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 )
njn25e49d8e72002-09-23 09:36:25 +0000145{
sewardj7a26f022005-11-01 17:52:34 +0000146 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 }
njn25e49d8e72002-09-23 09:36:25 +0000158}
159
sewardj7a26f022005-11-01 17:52:34 +0000160static 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}
njn25e49d8e72002-09-23 09:36:25 +0000175
njn25e49d8e72002-09-23 09:36:25 +0000176
sewardj7a26f022005-11-01 17:52:34 +0000177/* --- Counts --- */
njn25e49d8e72002-09-23 09:36:25 +0000178
sewardj7a26f022005-11-01 17:52:34 +0000179static ULong detailCounts[N_OPS][N_TYPES];
njn25e49d8e72002-09-23 09:36:25 +0000180
sewardj7a26f022005-11-01 17:52:34 +0000181/* The helper that is called from the instrumented code. */
182static VG_REGPARM(1)
183void increment_detail(ULong* detail)
184{
185 (*detail)++;
186}
njn25e49d8e72002-09-23 09:36:25 +0000187
sewardj7a26f022005-11-01 17:52:34 +0000188/* 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);
njn25e49d8e72002-09-23 09:36:25 +0000194
sewardj7a26f022005-11-01 17:52:34 +0000195 tl_assert(op < N_OPS);
196 tl_assert(typeIx < N_TYPES);
njn25e49d8e72002-09-23 09:36:25 +0000197
sewardj7a26f022005-11-01 17:52:34 +0000198 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}
njn25e49d8e72002-09-23 09:36:25 +0000202
sewardj7a26f022005-11-01 17:52:34 +0000203/* Summarize and print the details. */
njn25e49d8e72002-09-23 09:36:25 +0000204
sewardj7a26f022005-11-01 17:52:34 +0000205static 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}
njn25e49d8e72002-09-23 09:36:25 +0000222
sewardj7a26f022005-11-01 17:52:34 +0000223
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
sewardj4ba057c2005-10-18 12:04:18 +0000237static
238IRBB* lk_instrument( IRBB* bb_in, VexGuestLayout* layout,
239 Addr64 orig_addr_noredir, VexGuestExtents* vge,
240 IRType gWordTy, IRType hWordTy )
njn25e49d8e72002-09-23 09:36:25 +0000241{
sewardj9f649aa2004-11-22 20:38:40 +0000242 IRDirty* di;
243 Int i;
sewardjd54babf2005-03-21 00:55:49 +0000244 IRBB* bb;
sewardj7a26f022005-11-01 17:52:34 +0000245 Char fnname[100];
246 IRType type;
sewardjd54babf2005-03-21 00:55:49 +0000247
248 if (gWordTy != hWordTy) {
249 /* We don't currently support this case. */
250 VG_(tool_panic)("host/guest word size mismatch");
251 }
sewardj9f649aa2004-11-22 20:38:40 +0000252
253 /* Set up BB */
sewardjd54babf2005-03-21 00:55:49 +0000254 bb = emptyIRBB();
sewardj9f649aa2004-11-22 20:38:40 +0000255 bb->tyenv = dopyIRTypeEnv(bb_in->tyenv);
256 bb->next = dopyIRExpr(bb_in->next);
257 bb->jumpkind = bb_in->jumpkind;
258
sewardj7a26f022005-11-01 17:52:34 +0000259 // 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++;
sewardj9f649aa2004-11-22 20:38:40 +0000264 }
sewardj9f649aa2004-11-22 20:38:40 +0000265
sewardj7a26f022005-11-01 17:52:34 +0000266 /* Count this basic block. */
267 di = unsafeIRDirty_0_N( 0, "add_one_BB_entered", &add_one_BB_entered,
268 mkIRExprVec_0() );
sewardj9f649aa2004-11-22 20:38:40 +0000269 addStmtToIRBB( bb, IRStmt_Dirty(di) );
270
sewardj7a26f022005-11-01 17:52:34 +0000271 for (/*use current i*/; i < bb_in->stmts_used; i++) {
sewardj9f649aa2004-11-22 20:38:40 +0000272 IRStmt* st = bb_in->stmts[i];
sewardj7a26f022005-11-01 17:52:34 +0000273 if (!st || st->tag == Ist_NoOp) continue;
sewardj9f649aa2004-11-22 20:38:40 +0000274
sewardj7a26f022005-11-01 17:52:34 +0000275 /* 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
sewardj9f649aa2004-11-22 20:38:40 +0000280 switch (st->tag) {
sewardj7a26f022005-11-01 17:52:34 +0000281 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
sewardj9f649aa2004-11-22 20:38:40 +0000313 case Ist_Exit:
314 /* Count Jcc */
sewardj7a26f022005-11-01 17:52:34 +0000315 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
sewardj9f649aa2004-11-22 20:38:40 +0000321 /* Count non-taken Jcc */
sewardj7a26f022005-11-01 17:52:34 +0000322 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 );
sewardj9f649aa2004-11-22 20:38:40 +0000368 break;
369
370 default:
sewardjbd598e12005-01-07 12:10:21 +0000371 addStmtToIRBB( bb, st );
sewardj9f649aa2004-11-22 20:38:40 +0000372 }
373 }
374
sewardj7a26f022005-11-01 17:52:34 +0000375 /* 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
sewardj9f649aa2004-11-22 20:38:40 +0000380 return bb;
njn25e49d8e72002-09-23 09:36:25 +0000381}
382
njn51d827b2005-05-09 01:02:08 +0000383static void lk_fini(Int exitcode)
njn25e49d8e72002-09-23 09:36:25 +0000384{
sewardj7a26f022005-11-01 17:52:34 +0000385 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);
njn25e49d8e72002-09-23 09:36:25 +0000393
sewardj7a26f022005-11-01 17:52:34 +0000394 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);
njn25e49d8e72002-09-23 09:36:25 +0000419
sewardj7a26f022005-11-01 17:52:34 +0000420 if (lk_clo_detailed_counts) {
421 VG_(message)(Vg_UserMsg, "");
422 VG_(message)(Vg_UserMsg, "IR-level counts by type:");
423 print_details();
424 }
njn25e49d8e72002-09-23 09:36:25 +0000425
sewardj7a26f022005-11-01 17:52:34 +0000426 VG_(message)(Vg_UserMsg, "");
427 VG_(message)(Vg_UserMsg, "Exit code: %d", exitcode);
njn25e49d8e72002-09-23 09:36:25 +0000428}
429
njn51d827b2005-05-09 01:02:08 +0000430static 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);
sewardj7a26f022005-11-01 17:52:34 +0000443 VG_(needs_command_line_options)(lk_process_cmd_line_option,
444 lk_print_usage,
445 lk_print_debug_usage);
njn51d827b2005-05-09 01:02:08 +0000446}
447
sewardj45f4e7c2005-09-27 19:20:21 +0000448VG_DETERMINE_INTERFACE_VERSION(lk_pre_clo_init)
fitzhardinge98abfc72003-12-16 02:05:15 +0000449
njn25e49d8e72002-09-23 09:36:25 +0000450/*--------------------------------------------------------------------*/
njn25cac76cb2002-09-23 11:21:57 +0000451/*--- end lk_main.c ---*/
njn25e49d8e72002-09-23 09:36:25 +0000452/*--------------------------------------------------------------------*/