blob: c85638b5035830fda1d7ea762f1a905487fc8280 [file] [log] [blame]
njnc9539842002-10-02 13:26:35 +00001
njn25e49d8e72002-09-23 09:36:25 +00002/*--------------------------------------------------------------------*/
njnd99644d2006-04-07 11:52:55 +00003/*--- An example Valgrind tool. lk_main.c ---*/
njn25e49d8e72002-09-23 09:36:25 +00004/*--------------------------------------------------------------------*/
5
6/*
nethercote137bc552003-11-14 17:47:54 +00007 This file is part of Lackey, an example Valgrind tool that does
njnd99644d2006-04-07 11:52:55 +00008 some simple program measurement and tracing.
njn25e49d8e72002-09-23 09:36:25 +00009
sewardje4b0bf02006-06-05 23:21:15 +000010 Copyright (C) 2002-2006 Nicholas Nethercote
njn2bc10122005-05-08 02:10:27 +000011 njn@valgrind.org
njn25e49d8e72002-09-23 09:36:25 +000012
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
njnfd73ebb2005-12-30 22:39:58 +000031// This tool shows how to do some basic instrumentation.
32//
njnd99644d2006-04-07 11:52:55 +000033// 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// --------------------------------------
njneaf0ca92006-04-09 01:23:29 +000054// 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.
njnfd73ebb2005-12-30 22:39:58 +000059//
njneaf0ca92006-04-09 01:23:29 +000060// 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//
njne8d217d2006-10-18 23:46:26 +0000109// - It could model loads and stores done at the system call boundary using
110// the pre_mem_read/post_mem_write events. For example, if you call
111// fstat() you know that the passed in buffer has been written. But it
112// currently does not do this.
113//
114// - Valgrind replaces some code (not much) with its own, notably parts of
115// code for scheduling operations and signal handling. This code is not
116// traced.
njneaf0ca92006-04-09 01:23:29 +0000117//
118// - There is no consideration of virtual-to-physical address mapping.
119// This may not matter for many purposes.
120//
121// - Valgrind modifies the instruction stream in some very minor ways. For
122// example, on x86 the bts, btc, btr instructions are incorrectly
123// considered to always touch memory (this is a consequence of these
124// instructions being very difficult to simulate).
125//
126// - Valgrind tools layout memory differently to normal programs, so the
127// addresses you get will not be typical. Thus Lackey (and all Valgrind
128// tools) is suitable for getting relative memory traces -- eg. if you
129// want to analyse locality of memory accesses -- but is not good if
130// absolute addresses are important.
131//
132// Despite all these warnings, Dullard's results should be good enough for a
133// wide range of purposes. For example, Cachegrind shares all the above
134// shortcomings and it is still useful.
njn9dd72772006-03-11 06:48:20 +0000135//
njnfd73ebb2005-12-30 22:39:58 +0000136// For further inspiration, you should look at cachegrind/cg_main.c which
njneaf0ca92006-04-09 01:23:29 +0000137// uses the same basic technique for tracing memory accesses, but also groups
138// events together for processing into twos and threes so that fewer C calls
139// are made and things run faster.
njnfd73ebb2005-12-30 22:39:58 +0000140
njnc7561b92005-06-19 01:24:32 +0000141#include "pub_tool_basics.h"
njn43b9a8a2005-05-10 04:37:01 +0000142#include "pub_tool_tooliface.h"
njnf39e9a32005-06-12 02:43:17 +0000143#include "pub_tool_libcassert.h"
njn36a20fa2005-06-03 03:08:39 +0000144#include "pub_tool_libcprint.h"
sewardj7a26f022005-11-01 17:52:34 +0000145#include "pub_tool_debuginfo.h"
146#include "pub_tool_libcbase.h"
147#include "pub_tool_options.h"
sewardj5fed8c02005-12-23 12:56:11 +0000148#include "pub_tool_machine.h" // VG_(fnptr_to_fnentry)
sewardj7a26f022005-11-01 17:52:34 +0000149
njnd99644d2006-04-07 11:52:55 +0000150/*------------------------------------------------------------*/
151/*--- Command line options ---*/
152/*------------------------------------------------------------*/
sewardj7a26f022005-11-01 17:52:34 +0000153
njnd99644d2006-04-07 11:52:55 +0000154/* Command line options controlling instrumentation kinds, as described at
155 * the top of this file. */
156static Bool clo_basic_counts = True;
157static Bool clo_detailed_counts = False;
158static Bool clo_trace_mem = False;
sewardj7a26f022005-11-01 17:52:34 +0000159
njnd99644d2006-04-07 11:52:55 +0000160/* The name of the function of which the number of calls (under
161 * --basic-counts=yes) is to be counted, with default. Override with command
162 * line option --fnname. */
163static Char* clo_fnname = "_dl_runtime_resolve";
sewardj7a26f022005-11-01 17:52:34 +0000164
165static Bool lk_process_cmd_line_option(Char* arg)
166{
njnd99644d2006-04-07 11:52:55 +0000167 VG_STR_CLO(arg, "--fnname", clo_fnname)
168 else VG_BOOL_CLO(arg, "--basic-counts", clo_basic_counts)
169 else VG_BOOL_CLO(arg, "--detailed-counts", clo_detailed_counts)
170 else VG_BOOL_CLO(arg, "--trace-mem", clo_trace_mem)
sewardj7a26f022005-11-01 17:52:34 +0000171 else
172 return False;
173
njnd99644d2006-04-07 11:52:55 +0000174 tl_assert(clo_fnname);
175 tl_assert(clo_fnname[0]);
sewardj7a26f022005-11-01 17:52:34 +0000176 return True;
177}
178
179static void lk_print_usage(void)
180{
181 VG_(printf)(
njnd99644d2006-04-07 11:52:55 +0000182" --basic-counts=no|yes count instructions, jumps, etc. [no]\n"
sewardj7a26f022005-11-01 17:52:34 +0000183" --detailed-counts=no|yes count loads, stores and alu ops [no]\n"
njnd99644d2006-04-07 11:52:55 +0000184" --trace-mem=no|yes trace all loads and stores [no]\n"
185" --fnname=<name> count calls to <name> (only used if\n"
186" --basic-count=yes) [_dl_runtime_resolve]\n"
sewardj7a26f022005-11-01 17:52:34 +0000187 );
188}
189
190static void lk_print_debug_usage(void)
191{
njneaf0ca92006-04-09 01:23:29 +0000192 VG_(printf)(
193" (none)\n"
194 );
sewardj7a26f022005-11-01 17:52:34 +0000195}
196
njnd99644d2006-04-07 11:52:55 +0000197/*------------------------------------------------------------*/
njneaf0ca92006-04-09 01:23:29 +0000198/*--- Stuff for --basic-counts ---*/
njnd99644d2006-04-07 11:52:55 +0000199/*------------------------------------------------------------*/
njn25e49d8e72002-09-23 09:36:25 +0000200
njn25e49d8e72002-09-23 09:36:25 +0000201/* Nb: use ULongs because the numbers can get very big */
sewardj7a26f022005-11-01 17:52:34 +0000202static ULong n_func_calls = 0;
203static ULong n_BBs_entered = 0;
204static ULong n_BBs_completed = 0;
njneaf0ca92006-04-09 01:23:29 +0000205static ULong n_IRStmts = 0;
sewardj7a26f022005-11-01 17:52:34 +0000206static ULong n_guest_instrs = 0;
207static ULong n_Jccs = 0;
208static ULong n_Jccs_untaken = 0;
njn25e49d8e72002-09-23 09:36:25 +0000209
sewardj7a26f022005-11-01 17:52:34 +0000210static void add_one_func_call(void)
njn25e49d8e72002-09-23 09:36:25 +0000211{
sewardj7a26f022005-11-01 17:52:34 +0000212 n_func_calls++;
njn25e49d8e72002-09-23 09:36:25 +0000213}
214
sewardj7a26f022005-11-01 17:52:34 +0000215static void add_one_BB_entered(void)
njn25e49d8e72002-09-23 09:36:25 +0000216{
sewardj7a26f022005-11-01 17:52:34 +0000217 n_BBs_entered++;
218}
219
220static void add_one_BB_completed(void)
221{
222 n_BBs_completed++;
njn25e49d8e72002-09-23 09:36:25 +0000223}
224
sewardj7a26f022005-11-01 17:52:34 +0000225static void add_one_IRStmt(void)
njn25e49d8e72002-09-23 09:36:25 +0000226{
sewardj7a26f022005-11-01 17:52:34 +0000227 n_IRStmts++;
njn25e49d8e72002-09-23 09:36:25 +0000228}
229
sewardj9f649aa2004-11-22 20:38:40 +0000230static void add_one_guest_instr(void)
njn25e49d8e72002-09-23 09:36:25 +0000231{
sewardj9f649aa2004-11-22 20:38:40 +0000232 n_guest_instrs++;
njn25e49d8e72002-09-23 09:36:25 +0000233}
234
235static void add_one_Jcc(void)
236{
237 n_Jccs++;
238}
239
240static void add_one_Jcc_untaken(void)
241{
242 n_Jccs_untaken++;
243}
244
njnd99644d2006-04-07 11:52:55 +0000245/*------------------------------------------------------------*/
njneaf0ca92006-04-09 01:23:29 +0000246/*--- Stuff for --detailed-counts ---*/
njnd99644d2006-04-07 11:52:55 +0000247/*------------------------------------------------------------*/
sewardj7a26f022005-11-01 17:52:34 +0000248
249/* --- Operations --- */
250
251typedef enum { OpLoad=0, OpStore=1, OpAlu=2 } Op;
252
253#define N_OPS 3
254
255
256/* --- Types --- */
257
258#define N_TYPES 9
259
260static Int type2index ( IRType ty )
njn25e49d8e72002-09-23 09:36:25 +0000261{
sewardj7a26f022005-11-01 17:52:34 +0000262 switch (ty) {
263 case Ity_I1: return 0;
264 case Ity_I8: return 1;
265 case Ity_I16: return 2;
266 case Ity_I32: return 3;
267 case Ity_I64: return 4;
268 case Ity_I128: return 5;
269 case Ity_F32: return 6;
270 case Ity_F64: return 7;
271 case Ity_V128: return 8;
272 default: tl_assert(0); break;
273 }
njn25e49d8e72002-09-23 09:36:25 +0000274}
275
sewardj7a26f022005-11-01 17:52:34 +0000276static HChar* nameOfTypeIndex ( IRType ty )
277{
278 switch (ty) {
279 case 0: return "I1"; break;
280 case 1: return "I8"; break;
281 case 2: return "I16"; break;
282 case 3: return "I32"; break;
283 case 4: return "I64"; break;
284 case 5: return "I128"; break;
285 case 6: return "F32"; break;
286 case 7: return "F64"; break;
287 case 8: return "V128"; break;
288 default: tl_assert(0); break;
289 }
290}
njn25e49d8e72002-09-23 09:36:25 +0000291
njn25e49d8e72002-09-23 09:36:25 +0000292
sewardj7a26f022005-11-01 17:52:34 +0000293/* --- Counts --- */
njn25e49d8e72002-09-23 09:36:25 +0000294
sewardj7a26f022005-11-01 17:52:34 +0000295static ULong detailCounts[N_OPS][N_TYPES];
njn25e49d8e72002-09-23 09:36:25 +0000296
sewardj7a26f022005-11-01 17:52:34 +0000297/* The helper that is called from the instrumented code. */
298static VG_REGPARM(1)
299void increment_detail(ULong* detail)
300{
301 (*detail)++;
302}
njn25e49d8e72002-09-23 09:36:25 +0000303
sewardj7a26f022005-11-01 17:52:34 +0000304/* A helper that adds the instrumentation for a detail. */
305static void instrument_detail(IRBB* bb, Op op, IRType type)
306{
307 IRDirty* di;
308 IRExpr** argv;
309 const UInt typeIx = type2index(type);
njn25e49d8e72002-09-23 09:36:25 +0000310
sewardj7a26f022005-11-01 17:52:34 +0000311 tl_assert(op < N_OPS);
312 tl_assert(typeIx < N_TYPES);
njn25e49d8e72002-09-23 09:36:25 +0000313
sewardj7a26f022005-11-01 17:52:34 +0000314 argv = mkIRExprVec_1( mkIRExpr_HWord( (HWord)&detailCounts[op][typeIx] ) );
sewardj5fed8c02005-12-23 12:56:11 +0000315 di = unsafeIRDirty_0_N( 1, "increment_detail",
316 VG_(fnptr_to_fnentry)( &increment_detail ),
317 argv);
sewardj7a26f022005-11-01 17:52:34 +0000318 addStmtToIRBB( bb, IRStmt_Dirty(di) );
319}
njn25e49d8e72002-09-23 09:36:25 +0000320
sewardj7a26f022005-11-01 17:52:34 +0000321/* Summarize and print the details. */
sewardj7a26f022005-11-01 17:52:34 +0000322static void print_details ( void )
323{
324 Int typeIx;
325 VG_(message)(Vg_UserMsg,
326 " Type Loads Stores AluOps");
327 VG_(message)(Vg_UserMsg,
328 " -------------------------------------------");
329 for (typeIx = 0; typeIx < N_TYPES; typeIx++) {
330 VG_(message)(Vg_UserMsg,
331 " %4s %,12llu %,12llu %,12llu",
332 nameOfTypeIndex( typeIx ),
333 detailCounts[OpLoad ][typeIx],
334 detailCounts[OpStore][typeIx],
335 detailCounts[OpAlu ][typeIx]
336 );
337 }
338}
njn25e49d8e72002-09-23 09:36:25 +0000339
sewardj7a26f022005-11-01 17:52:34 +0000340
njnd99644d2006-04-07 11:52:55 +0000341/*------------------------------------------------------------*/
njneaf0ca92006-04-09 01:23:29 +0000342/*--- Stuff for --trace-mem ---*/
njnd99644d2006-04-07 11:52:55 +0000343/*------------------------------------------------------------*/
njnfd73ebb2005-12-30 22:39:58 +0000344
njneaf0ca92006-04-09 01:23:29 +0000345#define MAX_DSIZE 512
346
347typedef
348 IRExpr
349 IRAtom;
350
351typedef
352 enum { Event_Ir, Event_Dr, Event_Dw, Event_Dm }
353 EventKind;
354
355typedef
356 struct {
357 EventKind ekind;
358 IRAtom* addr;
359 Int size;
360 }
361 Event;
362
363/* Up to this many unnotified events are allowed. Must be at least two,
364 so that reads and writes to the same address can be merged into a modify.
365 Beyond that, larger numbers just potentially induce more spilling due to
366 extending live ranges of address temporaries. */
367#define N_EVENTS 4
368
369/* Maintain an ordered list of memory events which are outstanding, in
370 the sense that no IR has yet been generated to do the relevant
371 helper calls. The BB is scanned top to bottom and memory events
372 are added to the end of the list, merging with the most recent
373 notified event where possible (Dw immediately following Dr and
374 having the same size and EA can be merged).
375
376 This merging is done so that for architectures which have
377 load-op-store instructions (x86, amd64), the instr is treated as if
378 it makes just one memory reference (a modify), rather than two (a
379 read followed by a write at the same address).
380
381 At various points the list will need to be flushed, that is, IR
382 generated from it. That must happen before any possible exit from
383 the block (the end, or an IRStmt_Exit). Flushing also takes place
384 when there is no space to add a new event.
385
386 If we require the simulation statistics to be up to date with
387 respect to possible memory exceptions, then the list would have to
388 be flushed before each memory reference. That's a pain so we don't
389 bother.
390
391 Flushing the list consists of walking it start to end and emitting
392 instrumentation IR for each event, in the order in which they
393 appear. */
394
395static Event events[N_EVENTS];
396static Int events_used = 0;
397
398
399static VG_REGPARM(2) void trace_instr(Addr addr, SizeT size)
400{
401 VG_(printf)("instr : %08p, %d\n", addr, size);
402}
403
njnfd73ebb2005-12-30 22:39:58 +0000404static VG_REGPARM(2) void trace_load(Addr addr, SizeT size)
405{
njneaf0ca92006-04-09 01:23:29 +0000406 VG_(printf)(" load : %08p, %d\n", addr, size);
njnfd73ebb2005-12-30 22:39:58 +0000407}
408
409static VG_REGPARM(2) void trace_store(Addr addr, SizeT size)
410{
njneaf0ca92006-04-09 01:23:29 +0000411 VG_(printf)(" store : %08p, %d\n", addr, size);
412}
413
414static VG_REGPARM(2) void trace_modify(Addr addr, SizeT size)
415{
416 VG_(printf)(" modify: %08p, %d\n", addr, size);
417}
418
419
420static void flushEvents(IRBB* bb)
421{
422 Int i;
423 Char* helperName;
424 void* helperAddr;
425 IRExpr** argv;
426 IRDirty* di;
427 Event* ev;
428
429 for (i = 0; i < events_used; i++) {
430
431 ev = &events[i];
432
433 // Decide on helper fn to call and args to pass it.
434 switch (ev->ekind) {
435 case Event_Ir: helperName = "trace_instr";
436 helperAddr = trace_instr; break;
437
438 case Event_Dr: helperName = "trace_load";
439 helperAddr = trace_load; break;
440
441 case Event_Dw: helperName = "trace_store";
442 helperAddr = trace_store; break;
443
444 case Event_Dm: helperName = "trace_modify";
445 helperAddr = trace_modify; break;
446 default:
447 tl_assert(0);
448 }
449
450 // Add the helper.
451 argv = mkIRExprVec_2( ev->addr, mkIRExpr_HWord( ev->size ) );
452 di = unsafeIRDirty_0_N( /*regparms*/2,
453 helperName, VG_(fnptr_to_fnentry)( helperAddr ),
454 argv );
455 addStmtToIRBB( bb, IRStmt_Dirty(di) );
456 }
457
458 events_used = 0;
459}
460
461// WARNING: If you aren't interested in instruction reads, you can omit the
462// code that adds calls to trace_instr() in flushEvents(). However, you
463// must still call this function, addEvent_Ir() -- it is necessary to add
464// the Ir events to the events list so that merging of paired load/store
465// events into modify events works correctly.
466static void addEvent_Ir ( IRBB* bb, IRAtom* iaddr, UInt isize )
467{
468 Event* evt;
469 tl_assert( (VG_MIN_INSTR_SZB <= isize && isize <= VG_MAX_INSTR_SZB)
470 || VG_CLREQ_SZB == isize );
471 if (events_used == N_EVENTS)
472 flushEvents(bb);
473 tl_assert(events_used >= 0 && events_used < N_EVENTS);
474 evt = &events[events_used];
475 evt->ekind = Event_Ir;
476 evt->addr = iaddr;
477 evt->size = isize;
478 events_used++;
479}
480
481static
482void addEvent_Dr ( IRBB* bb, IRAtom* daddr, Int dsize )
483{
484 Event* evt;
485 tl_assert(isIRAtom(daddr));
486 tl_assert(dsize >= 1 && dsize <= MAX_DSIZE);
487 if (events_used == N_EVENTS)
488 flushEvents(bb);
489 tl_assert(events_used >= 0 && events_used < N_EVENTS);
490 evt = &events[events_used];
491 evt->ekind = Event_Dr;
492 evt->addr = daddr;
493 evt->size = dsize;
494 events_used++;
495}
496
497static
498void addEvent_Dw ( IRBB* bb, IRAtom* daddr, Int dsize )
499{
500 Event* lastEvt;
501 Event* evt;
502 tl_assert(isIRAtom(daddr));
503 tl_assert(dsize >= 1 && dsize <= MAX_DSIZE);
504
505 // Is it possible to merge this write with the preceding read?
506 lastEvt = &events[events_used-1];
507 if (events_used > 0
508 && lastEvt->ekind == Event_Dr
509 && lastEvt->size == dsize
510 && eqIRAtom(lastEvt->addr, daddr))
511 {
512 lastEvt->ekind = Event_Dm;
513 return;
514 }
515
516 // No. Add as normal.
517 if (events_used == N_EVENTS)
518 flushEvents(bb);
519 tl_assert(events_used >= 0 && events_used < N_EVENTS);
520 evt = &events[events_used];
521 evt->ekind = Event_Dw;
522 evt->size = dsize;
523 evt->addr = daddr;
524 events_used++;
njnfd73ebb2005-12-30 22:39:58 +0000525}
526
njnd99644d2006-04-07 11:52:55 +0000527
528/*------------------------------------------------------------*/
529/*--- Basic tool functions ---*/
530/*------------------------------------------------------------*/
sewardj7a26f022005-11-01 17:52:34 +0000531
532static void lk_post_clo_init(void)
533{
534 Int op, tyIx;
535
njnd99644d2006-04-07 11:52:55 +0000536 if (clo_detailed_counts) {
537 for (op = 0; op < N_OPS; op++)
538 for (tyIx = 0; tyIx < N_TYPES; tyIx++)
539 detailCounts[op][tyIx] = 0;
540 }
sewardj7a26f022005-11-01 17:52:34 +0000541}
542
sewardj4ba057c2005-10-18 12:04:18 +0000543static
sewardj461df9c2006-01-17 02:06:39 +0000544IRBB* lk_instrument ( VgCallbackClosure* closure,
njneaf0ca92006-04-09 01:23:29 +0000545 IRBB* bbIn,
sewardj461df9c2006-01-17 02:06:39 +0000546 VexGuestLayout* layout,
547 VexGuestExtents* vge,
548 IRType gWordTy, IRType hWordTy )
njn25e49d8e72002-09-23 09:36:25 +0000549{
njneaf0ca92006-04-09 01:23:29 +0000550 IRDirty* di;
551 Int i;
552 IRBB* bbOut;
553 Char fnname[100];
554 IRType type;
555 IRTypeEnv* tyenv = bbIn->tyenv;
sewardjd54babf2005-03-21 00:55:49 +0000556
557 if (gWordTy != hWordTy) {
558 /* We don't currently support this case. */
559 VG_(tool_panic)("host/guest word size mismatch");
560 }
sewardj9f649aa2004-11-22 20:38:40 +0000561
562 /* Set up BB */
njneaf0ca92006-04-09 01:23:29 +0000563 bbOut = emptyIRBB();
564 bbOut->tyenv = dopyIRTypeEnv(bbIn->tyenv);
565 bbOut->next = dopyIRExpr(bbIn->next);
566 bbOut->jumpkind = bbIn->jumpkind;
sewardj9f649aa2004-11-22 20:38:40 +0000567
sewardj7a26f022005-11-01 17:52:34 +0000568 // Copy verbatim any IR preamble preceding the first IMark
569 i = 0;
njneaf0ca92006-04-09 01:23:29 +0000570 while (i < bbIn->stmts_used && bbIn->stmts[i]->tag != Ist_IMark) {
571 addStmtToIRBB( bbOut, bbIn->stmts[i] );
sewardj7a26f022005-11-01 17:52:34 +0000572 i++;
sewardj9f649aa2004-11-22 20:38:40 +0000573 }
sewardj9f649aa2004-11-22 20:38:40 +0000574
njnd99644d2006-04-07 11:52:55 +0000575 if (clo_basic_counts) {
576 /* Count this basic block. */
577 di = unsafeIRDirty_0_N( 0, "add_one_BB_entered",
578 VG_(fnptr_to_fnentry)( &add_one_BB_entered ),
579 mkIRExprVec_0() );
njneaf0ca92006-04-09 01:23:29 +0000580 addStmtToIRBB( bbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000581 }
sewardj9f649aa2004-11-22 20:38:40 +0000582
njneaf0ca92006-04-09 01:23:29 +0000583 if (clo_trace_mem) {
584 events_used = 0;
585 }
586
587 for (/*use current i*/; i < bbIn->stmts_used; i++) {
588 IRStmt* st = bbIn->stmts[i];
sewardj7a26f022005-11-01 17:52:34 +0000589 if (!st || st->tag == Ist_NoOp) continue;
sewardj9f649aa2004-11-22 20:38:40 +0000590
njnd99644d2006-04-07 11:52:55 +0000591 if (clo_basic_counts) {
592 /* Count one VEX statement. */
593 di = unsafeIRDirty_0_N( 0, "add_one_IRStmt",
594 VG_(fnptr_to_fnentry)( &add_one_IRStmt ),
595 mkIRExprVec_0() );
njneaf0ca92006-04-09 01:23:29 +0000596 addStmtToIRBB( bbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000597 }
sewardj7a26f022005-11-01 17:52:34 +0000598
sewardj9f649aa2004-11-22 20:38:40 +0000599 switch (st->tag) {
njneaf0ca92006-04-09 01:23:29 +0000600 case Ist_NoOp:
601 case Ist_AbiHint:
602 case Ist_Put:
603 case Ist_PutI:
604 case Ist_MFence:
605 addStmtToIRBB( bbOut, st );
606 break;
607
sewardj7a26f022005-11-01 17:52:34 +0000608 case Ist_IMark:
njnd99644d2006-04-07 11:52:55 +0000609 if (clo_basic_counts) {
610 /* Count guest instruction. */
611 di = unsafeIRDirty_0_N( 0, "add_one_guest_instr",
612 VG_(fnptr_to_fnentry)( &add_one_guest_instr ),
613 mkIRExprVec_0() );
njneaf0ca92006-04-09 01:23:29 +0000614 addStmtToIRBB( bbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000615
616 /* An unconditional branch to a known destination in the
617 * guest's instructions can be represented, in the IRBB to
618 * instrument, by the VEX statements that are the
619 * translation of that known destination. This feature is
620 * called 'BB chasing' and can be influenced by command
621 * line option --vex-guest-chase-thresh.
622 *
623 * To get an accurate count of the calls to a specific
624 * function, taking BB chasing into account, we need to
625 * check for each guest instruction (Ist_IMark) if it is
626 * the entry point of a function.
627 */
628 tl_assert(clo_fnname);
629 tl_assert(clo_fnname[0]);
630 if (VG_(get_fnname_if_entry)(st->Ist.IMark.addr,
631 fnname, sizeof(fnname))
632 && 0 == VG_(strcmp)(fnname, clo_fnname)) {
633 di = unsafeIRDirty_0_N(
634 0, "add_one_func_call",
635 VG_(fnptr_to_fnentry)( &add_one_func_call ),
636 mkIRExprVec_0() );
njneaf0ca92006-04-09 01:23:29 +0000637 addStmtToIRBB( bbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000638 }
sewardj7a26f022005-11-01 17:52:34 +0000639 }
njnd99644d2006-04-07 11:52:55 +0000640 if (clo_trace_mem) {
njneaf0ca92006-04-09 01:23:29 +0000641 // WARNING: do not remove this function call, even if you
642 // aren't interested in instruction reads. See the comment
643 // above the function itself for more detail.
644 addEvent_Ir( bbOut, mkIRExpr_HWord( (HWord)st->Ist.IMark.addr ),
645 st->Ist.IMark.len );
njnfd73ebb2005-12-30 22:39:58 +0000646 }
njneaf0ca92006-04-09 01:23:29 +0000647 addStmtToIRBB( bbOut, st );
sewardj7a26f022005-11-01 17:52:34 +0000648 break;
649
650 case Ist_Tmp:
njnfd73ebb2005-12-30 22:39:58 +0000651 // Add a call to trace_load() if --trace-mem=yes.
njnd99644d2006-04-07 11:52:55 +0000652 if (clo_trace_mem) {
njnfd73ebb2005-12-30 22:39:58 +0000653 IRExpr* data = st->Ist.Tmp.data;
654 if (data->tag == Iex_Load) {
njneaf0ca92006-04-09 01:23:29 +0000655 addEvent_Dr( bbOut, data->Iex.Load.addr,
656 sizeofIRType(data->Iex.Load.ty) );
njnfd73ebb2005-12-30 22:39:58 +0000657 }
658 }
njnd99644d2006-04-07 11:52:55 +0000659 if (clo_detailed_counts) {
sewardj7a26f022005-11-01 17:52:34 +0000660 IRExpr* expr = st->Ist.Tmp.data;
njneaf0ca92006-04-09 01:23:29 +0000661 type = typeOfIRExpr(bbOut->tyenv, expr);
sewardj7a26f022005-11-01 17:52:34 +0000662 tl_assert(type != Ity_INVALID);
663 switch (expr->tag) {
664 case Iex_Load:
njneaf0ca92006-04-09 01:23:29 +0000665 instrument_detail( bbOut, OpLoad, type );
sewardj7a26f022005-11-01 17:52:34 +0000666 break;
667 case Iex_Unop:
668 case Iex_Binop:
sewardje91cea72006-02-08 19:32:02 +0000669 case Iex_Triop:
670 case Iex_Qop:
sewardj7a26f022005-11-01 17:52:34 +0000671 case Iex_Mux0X:
njneaf0ca92006-04-09 01:23:29 +0000672 instrument_detail( bbOut, OpAlu, type );
sewardj7a26f022005-11-01 17:52:34 +0000673 break;
674 default:
675 break;
676 }
677 }
njneaf0ca92006-04-09 01:23:29 +0000678 addStmtToIRBB( bbOut, st );
679 break;
680
681 case Ist_Store:
682 if (clo_trace_mem) {
683 IRExpr* data = st->Ist.Store.data;
684 addEvent_Dw( bbOut, st->Ist.Store.addr,
685 sizeofIRType(typeOfIRExpr(tyenv, data)) );
686 }
687 if (clo_detailed_counts) {
688 type = typeOfIRExpr(bbOut->tyenv, st->Ist.Store.data);
689 tl_assert(type != Ity_INVALID);
690 instrument_detail( bbOut, OpStore, type );
691 }
692 addStmtToIRBB( bbOut, st );
693 break;
694
695 case Ist_Dirty: {
696 Int dsize;
697 IRDirty* d = st->Ist.Dirty.details;
698 if (d->mFx != Ifx_None) {
699 // This dirty helper accesses memory. Collect the details.
700 tl_assert(d->mAddr != NULL);
701 tl_assert(d->mSize != 0);
702 dsize = d->mSize;
703 if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify)
704 addEvent_Dr( bbOut, d->mAddr, dsize );
705 if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify)
706 addEvent_Dw( bbOut, d->mAddr, dsize );
707 } else {
708 tl_assert(d->mAddr == NULL);
709 tl_assert(d->mSize == 0);
710 }
711 addStmtToIRBB( bbOut, st );
712 break;
713 }
714
715 case Ist_Exit:
716 if (clo_basic_counts) {
717 /* Count Jcc */
718 di = unsafeIRDirty_0_N( 0, "add_one_Jcc",
719 VG_(fnptr_to_fnentry)( &add_one_Jcc ),
720 mkIRExprVec_0() );
721 addStmtToIRBB( bbOut, IRStmt_Dirty(di) );
722 }
723 if (clo_trace_mem) {
724 flushEvents(bbOut);
725 }
726
727 addStmtToIRBB( bbOut, st ); // Original statement
728
729 if (clo_basic_counts) {
730 /* Count non-taken Jcc */
731 di = unsafeIRDirty_0_N( 0, "add_one_Jcc_untaken",
732 VG_(fnptr_to_fnentry)(
733 &add_one_Jcc_untaken ),
734 mkIRExprVec_0() );
735 addStmtToIRBB( bbOut, IRStmt_Dirty(di) );
736 }
sewardj9f649aa2004-11-22 20:38:40 +0000737 break;
738
739 default:
njneaf0ca92006-04-09 01:23:29 +0000740 tl_assert(0);
sewardj9f649aa2004-11-22 20:38:40 +0000741 }
742 }
743
njnd99644d2006-04-07 11:52:55 +0000744 if (clo_basic_counts) {
745 /* Count this basic block. */
746 di = unsafeIRDirty_0_N( 0, "add_one_BB_completed",
747 VG_(fnptr_to_fnentry)( &add_one_BB_completed ),
748 mkIRExprVec_0() );
njneaf0ca92006-04-09 01:23:29 +0000749 addStmtToIRBB( bbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000750 }
sewardj7a26f022005-11-01 17:52:34 +0000751
njneaf0ca92006-04-09 01:23:29 +0000752 if (clo_trace_mem) {
753 /* At the end of the bbIn. Flush outstandings. */
754 flushEvents(bbOut);
755 }
756
757 return bbOut;
njn25e49d8e72002-09-23 09:36:25 +0000758}
759
njn51d827b2005-05-09 01:02:08 +0000760static void lk_fini(Int exitcode)
njn25e49d8e72002-09-23 09:36:25 +0000761{
sewardj7a26f022005-11-01 17:52:34 +0000762 char percentify_buf[4]; /* Two digits, '%' and 0. */
763 const int percentify_size = sizeof(percentify_buf);
764 const int percentify_decs = 0;
765
njnd99644d2006-04-07 11:52:55 +0000766 tl_assert(clo_fnname);
767 tl_assert(clo_fnname[0]);
njn25e49d8e72002-09-23 09:36:25 +0000768
njnd99644d2006-04-07 11:52:55 +0000769 if (clo_basic_counts) {
770 VG_(message)(Vg_UserMsg,
771 "Counted %,llu calls to %s()", n_func_calls, clo_fnname);
njn25e49d8e72002-09-23 09:36:25 +0000772
njnd99644d2006-04-07 11:52:55 +0000773 VG_(message)(Vg_UserMsg, "");
774 VG_(message)(Vg_UserMsg, "Jccs:");
775 VG_(message)(Vg_UserMsg, " total: %,llu", n_Jccs);
776 VG_(percentify)((n_Jccs - n_Jccs_untaken), (n_Jccs ? n_Jccs : 1),
777 percentify_decs, percentify_size, percentify_buf);
778 VG_(message)(Vg_UserMsg, " taken: %,llu (%s)",
779 (n_Jccs - n_Jccs_untaken), percentify_buf);
780
781 VG_(message)(Vg_UserMsg, "");
782 VG_(message)(Vg_UserMsg, "Executed:");
783 VG_(message)(Vg_UserMsg, " BBs entered: %,llu", n_BBs_entered);
784 VG_(message)(Vg_UserMsg, " BBs completed: %,llu", n_BBs_completed);
785 VG_(message)(Vg_UserMsg, " guest instrs: %,llu", n_guest_instrs);
786 VG_(message)(Vg_UserMsg, " IRStmts: %,llu", n_IRStmts);
787
788 VG_(message)(Vg_UserMsg, "");
789 VG_(message)(Vg_UserMsg, "Ratios:");
790 tl_assert(n_BBs_entered); // Paranoia time.
791 VG_(message)(Vg_UserMsg, " guest instrs : BB entered = %3u : 10",
792 10 * n_guest_instrs / n_BBs_entered);
793 VG_(message)(Vg_UserMsg, " IRStmts : BB entered = %3u : 10",
794 10 * n_IRStmts / n_BBs_entered);
795 tl_assert(n_guest_instrs); // Paranoia time.
796 VG_(message)(Vg_UserMsg, " IRStmts : guest instr = %3u : 10",
797 10 * n_IRStmts / n_guest_instrs);
798 }
799
800 if (clo_detailed_counts) {
sewardj7a26f022005-11-01 17:52:34 +0000801 VG_(message)(Vg_UserMsg, "");
802 VG_(message)(Vg_UserMsg, "IR-level counts by type:");
803 print_details();
804 }
njn25e49d8e72002-09-23 09:36:25 +0000805
njnd99644d2006-04-07 11:52:55 +0000806 if (clo_basic_counts) {
807 VG_(message)(Vg_UserMsg, "");
808 VG_(message)(Vg_UserMsg, "Exit code: %d", exitcode);
809 }
njn25e49d8e72002-09-23 09:36:25 +0000810}
811
njn51d827b2005-05-09 01:02:08 +0000812static void lk_pre_clo_init(void)
813{
814 VG_(details_name) ("Lackey");
815 VG_(details_version) (NULL);
816 VG_(details_description) ("an example Valgrind tool");
817 VG_(details_copyright_author)(
sewardje4b0bf02006-06-05 23:21:15 +0000818 "Copyright (C) 2002-2006, and GNU GPL'd, by Nicholas Nethercote.");
njn51d827b2005-05-09 01:02:08 +0000819 VG_(details_bug_reports_to) (VG_BUGS_TO);
sewardj40823562006-10-17 02:21:17 +0000820 VG_(details_avg_translation_sizeB) ( 200 );
njn51d827b2005-05-09 01:02:08 +0000821
822 VG_(basic_tool_funcs) (lk_post_clo_init,
823 lk_instrument,
824 lk_fini);
sewardj7a26f022005-11-01 17:52:34 +0000825 VG_(needs_command_line_options)(lk_process_cmd_line_option,
826 lk_print_usage,
827 lk_print_debug_usage);
njn51d827b2005-05-09 01:02:08 +0000828}
829
sewardj45f4e7c2005-09-27 19:20:21 +0000830VG_DETERMINE_INTERFACE_VERSION(lk_pre_clo_init)
fitzhardinge98abfc72003-12-16 02:05:15 +0000831
njn25e49d8e72002-09-23 09:36:25 +0000832/*--------------------------------------------------------------------*/
njn25cac76cb2002-09-23 11:21:57 +0000833/*--- end lk_main.c ---*/
njn25e49d8e72002-09-23 09:36:25 +0000834/*--------------------------------------------------------------------*/