blob: c4f6294e23096988ee26e9236341c9c493ca4e38 [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
njn53612422005-03-12 16:22:54 +000010 Copyright (C) 2002-2005 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//
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.
njn9dd72772006-03-11 06:48:20 +0000129//
njnfd73ebb2005-12-30 22:39:58 +0000130// For further inspiration, you should look at cachegrind/cg_main.c which
njneaf0ca92006-04-09 01:23:29 +0000131// 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.
njnfd73ebb2005-12-30 22:39:58 +0000134
njnc7561b92005-06-19 01:24:32 +0000135#include "pub_tool_basics.h"
njn43b9a8a2005-05-10 04:37:01 +0000136#include "pub_tool_tooliface.h"
njnf39e9a32005-06-12 02:43:17 +0000137#include "pub_tool_libcassert.h"
njn36a20fa2005-06-03 03:08:39 +0000138#include "pub_tool_libcprint.h"
sewardj7a26f022005-11-01 17:52:34 +0000139#include "pub_tool_debuginfo.h"
140#include "pub_tool_libcbase.h"
141#include "pub_tool_options.h"
sewardj5fed8c02005-12-23 12:56:11 +0000142#include "pub_tool_machine.h" // VG_(fnptr_to_fnentry)
sewardj7a26f022005-11-01 17:52:34 +0000143
njnd99644d2006-04-07 11:52:55 +0000144/*------------------------------------------------------------*/
145/*--- Command line options ---*/
146/*------------------------------------------------------------*/
sewardj7a26f022005-11-01 17:52:34 +0000147
njnd99644d2006-04-07 11:52:55 +0000148/* 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;
sewardj7a26f022005-11-01 17:52:34 +0000153
njnd99644d2006-04-07 11:52:55 +0000154/* 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";
sewardj7a26f022005-11-01 17:52:34 +0000158
159static Bool lk_process_cmd_line_option(Char* arg)
160{
njnd99644d2006-04-07 11:52:55 +0000161 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)
sewardj7a26f022005-11-01 17:52:34 +0000165 else
166 return False;
167
njnd99644d2006-04-07 11:52:55 +0000168 tl_assert(clo_fnname);
169 tl_assert(clo_fnname[0]);
sewardj7a26f022005-11-01 17:52:34 +0000170 return True;
171}
172
173static void lk_print_usage(void)
174{
175 VG_(printf)(
njnd99644d2006-04-07 11:52:55 +0000176" --basic-counts=no|yes count instructions, jumps, etc. [no]\n"
sewardj7a26f022005-11-01 17:52:34 +0000177" --detailed-counts=no|yes count loads, stores and alu ops [no]\n"
njnd99644d2006-04-07 11:52:55 +0000178" --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"
sewardj7a26f022005-11-01 17:52:34 +0000181 );
182}
183
184static void lk_print_debug_usage(void)
185{
njneaf0ca92006-04-09 01:23:29 +0000186 VG_(printf)(
187" (none)\n"
188 );
sewardj7a26f022005-11-01 17:52:34 +0000189}
190
njnd99644d2006-04-07 11:52:55 +0000191/*------------------------------------------------------------*/
njneaf0ca92006-04-09 01:23:29 +0000192/*--- Stuff for --basic-counts ---*/
njnd99644d2006-04-07 11:52:55 +0000193/*------------------------------------------------------------*/
njn25e49d8e72002-09-23 09:36:25 +0000194
njn25e49d8e72002-09-23 09:36:25 +0000195/* Nb: use ULongs because the numbers can get very big */
sewardj7a26f022005-11-01 17:52:34 +0000196static ULong n_func_calls = 0;
197static ULong n_BBs_entered = 0;
198static ULong n_BBs_completed = 0;
njneaf0ca92006-04-09 01:23:29 +0000199static ULong n_IRStmts = 0;
sewardj7a26f022005-11-01 17:52:34 +0000200static ULong n_guest_instrs = 0;
201static ULong n_Jccs = 0;
202static ULong n_Jccs_untaken = 0;
njn25e49d8e72002-09-23 09:36:25 +0000203
sewardj7a26f022005-11-01 17:52:34 +0000204static void add_one_func_call(void)
njn25e49d8e72002-09-23 09:36:25 +0000205{
sewardj7a26f022005-11-01 17:52:34 +0000206 n_func_calls++;
njn25e49d8e72002-09-23 09:36:25 +0000207}
208
sewardj7a26f022005-11-01 17:52:34 +0000209static void add_one_BB_entered(void)
njn25e49d8e72002-09-23 09:36:25 +0000210{
sewardj7a26f022005-11-01 17:52:34 +0000211 n_BBs_entered++;
212}
213
214static void add_one_BB_completed(void)
215{
216 n_BBs_completed++;
njn25e49d8e72002-09-23 09:36:25 +0000217}
218
sewardj7a26f022005-11-01 17:52:34 +0000219static void add_one_IRStmt(void)
njn25e49d8e72002-09-23 09:36:25 +0000220{
sewardj7a26f022005-11-01 17:52:34 +0000221 n_IRStmts++;
njn25e49d8e72002-09-23 09:36:25 +0000222}
223
sewardj9f649aa2004-11-22 20:38:40 +0000224static void add_one_guest_instr(void)
njn25e49d8e72002-09-23 09:36:25 +0000225{
sewardj9f649aa2004-11-22 20:38:40 +0000226 n_guest_instrs++;
njn25e49d8e72002-09-23 09:36:25 +0000227}
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
njnd99644d2006-04-07 11:52:55 +0000239/*------------------------------------------------------------*/
njneaf0ca92006-04-09 01:23:29 +0000240/*--- Stuff for --detailed-counts ---*/
njnd99644d2006-04-07 11:52:55 +0000241/*------------------------------------------------------------*/
sewardj7a26f022005-11-01 17:52:34 +0000242
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 )
njn25e49d8e72002-09-23 09:36:25 +0000255{
sewardj7a26f022005-11-01 17:52:34 +0000256 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 }
njn25e49d8e72002-09-23 09:36:25 +0000268}
269
sewardj7a26f022005-11-01 17:52:34 +0000270static 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}
njn25e49d8e72002-09-23 09:36:25 +0000285
njn25e49d8e72002-09-23 09:36:25 +0000286
sewardj7a26f022005-11-01 17:52:34 +0000287/* --- Counts --- */
njn25e49d8e72002-09-23 09:36:25 +0000288
sewardj7a26f022005-11-01 17:52:34 +0000289static ULong detailCounts[N_OPS][N_TYPES];
njn25e49d8e72002-09-23 09:36:25 +0000290
sewardj7a26f022005-11-01 17:52:34 +0000291/* The helper that is called from the instrumented code. */
292static VG_REGPARM(1)
293void increment_detail(ULong* detail)
294{
295 (*detail)++;
296}
njn25e49d8e72002-09-23 09:36:25 +0000297
sewardj7a26f022005-11-01 17:52:34 +0000298/* 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);
njn25e49d8e72002-09-23 09:36:25 +0000304
sewardj7a26f022005-11-01 17:52:34 +0000305 tl_assert(op < N_OPS);
306 tl_assert(typeIx < N_TYPES);
njn25e49d8e72002-09-23 09:36:25 +0000307
sewardj7a26f022005-11-01 17:52:34 +0000308 argv = mkIRExprVec_1( mkIRExpr_HWord( (HWord)&detailCounts[op][typeIx] ) );
sewardj5fed8c02005-12-23 12:56:11 +0000309 di = unsafeIRDirty_0_N( 1, "increment_detail",
310 VG_(fnptr_to_fnentry)( &increment_detail ),
311 argv);
sewardj7a26f022005-11-01 17:52:34 +0000312 addStmtToIRBB( bb, IRStmt_Dirty(di) );
313}
njn25e49d8e72002-09-23 09:36:25 +0000314
sewardj7a26f022005-11-01 17:52:34 +0000315/* Summarize and print the details. */
sewardj7a26f022005-11-01 17:52:34 +0000316static 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}
njn25e49d8e72002-09-23 09:36:25 +0000333
sewardj7a26f022005-11-01 17:52:34 +0000334
njnd99644d2006-04-07 11:52:55 +0000335/*------------------------------------------------------------*/
njneaf0ca92006-04-09 01:23:29 +0000336/*--- Stuff for --trace-mem ---*/
njnd99644d2006-04-07 11:52:55 +0000337/*------------------------------------------------------------*/
njnfd73ebb2005-12-30 22:39:58 +0000338
njneaf0ca92006-04-09 01:23:29 +0000339#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
njnfd73ebb2005-12-30 22:39:58 +0000398static VG_REGPARM(2) void trace_load(Addr addr, SizeT size)
399{
njneaf0ca92006-04-09 01:23:29 +0000400 VG_(printf)(" load : %08p, %d\n", addr, size);
njnfd73ebb2005-12-30 22:39:58 +0000401}
402
403static VG_REGPARM(2) void trace_store(Addr addr, SizeT size)
404{
njneaf0ca92006-04-09 01:23:29 +0000405 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++;
njnfd73ebb2005-12-30 22:39:58 +0000519}
520
njnd99644d2006-04-07 11:52:55 +0000521
522/*------------------------------------------------------------*/
523/*--- Basic tool functions ---*/
524/*------------------------------------------------------------*/
sewardj7a26f022005-11-01 17:52:34 +0000525
526static void lk_post_clo_init(void)
527{
528 Int op, tyIx;
529
njnd99644d2006-04-07 11:52:55 +0000530 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 }
sewardj7a26f022005-11-01 17:52:34 +0000535}
536
sewardj4ba057c2005-10-18 12:04:18 +0000537static
sewardj461df9c2006-01-17 02:06:39 +0000538IRBB* lk_instrument ( VgCallbackClosure* closure,
njneaf0ca92006-04-09 01:23:29 +0000539 IRBB* bbIn,
sewardj461df9c2006-01-17 02:06:39 +0000540 VexGuestLayout* layout,
541 VexGuestExtents* vge,
542 IRType gWordTy, IRType hWordTy )
njn25e49d8e72002-09-23 09:36:25 +0000543{
njneaf0ca92006-04-09 01:23:29 +0000544 IRDirty* di;
545 Int i;
546 IRBB* bbOut;
547 Char fnname[100];
548 IRType type;
549 IRTypeEnv* tyenv = bbIn->tyenv;
sewardjd54babf2005-03-21 00:55:49 +0000550
551 if (gWordTy != hWordTy) {
552 /* We don't currently support this case. */
553 VG_(tool_panic)("host/guest word size mismatch");
554 }
sewardj9f649aa2004-11-22 20:38:40 +0000555
556 /* Set up BB */
njneaf0ca92006-04-09 01:23:29 +0000557 bbOut = emptyIRBB();
558 bbOut->tyenv = dopyIRTypeEnv(bbIn->tyenv);
559 bbOut->next = dopyIRExpr(bbIn->next);
560 bbOut->jumpkind = bbIn->jumpkind;
sewardj9f649aa2004-11-22 20:38:40 +0000561
sewardj7a26f022005-11-01 17:52:34 +0000562 // Copy verbatim any IR preamble preceding the first IMark
563 i = 0;
njneaf0ca92006-04-09 01:23:29 +0000564 while (i < bbIn->stmts_used && bbIn->stmts[i]->tag != Ist_IMark) {
565 addStmtToIRBB( bbOut, bbIn->stmts[i] );
sewardj7a26f022005-11-01 17:52:34 +0000566 i++;
sewardj9f649aa2004-11-22 20:38:40 +0000567 }
sewardj9f649aa2004-11-22 20:38:40 +0000568
njnd99644d2006-04-07 11:52:55 +0000569 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() );
njneaf0ca92006-04-09 01:23:29 +0000574 addStmtToIRBB( bbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000575 }
sewardj9f649aa2004-11-22 20:38:40 +0000576
njneaf0ca92006-04-09 01:23:29 +0000577 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];
sewardj7a26f022005-11-01 17:52:34 +0000583 if (!st || st->tag == Ist_NoOp) continue;
sewardj9f649aa2004-11-22 20:38:40 +0000584
njnd99644d2006-04-07 11:52:55 +0000585 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() );
njneaf0ca92006-04-09 01:23:29 +0000590 addStmtToIRBB( bbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000591 }
sewardj7a26f022005-11-01 17:52:34 +0000592
sewardj9f649aa2004-11-22 20:38:40 +0000593 switch (st->tag) {
njneaf0ca92006-04-09 01:23:29 +0000594 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
sewardj7a26f022005-11-01 17:52:34 +0000602 case Ist_IMark:
njnd99644d2006-04-07 11:52:55 +0000603 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() );
njneaf0ca92006-04-09 01:23:29 +0000608 addStmtToIRBB( bbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000609
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() );
njneaf0ca92006-04-09 01:23:29 +0000631 addStmtToIRBB( bbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000632 }
sewardj7a26f022005-11-01 17:52:34 +0000633 }
njnd99644d2006-04-07 11:52:55 +0000634 if (clo_trace_mem) {
njneaf0ca92006-04-09 01:23:29 +0000635 // 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 );
njnfd73ebb2005-12-30 22:39:58 +0000640 }
njneaf0ca92006-04-09 01:23:29 +0000641 addStmtToIRBB( bbOut, st );
sewardj7a26f022005-11-01 17:52:34 +0000642 break;
643
644 case Ist_Tmp:
njnfd73ebb2005-12-30 22:39:58 +0000645 // Add a call to trace_load() if --trace-mem=yes.
njnd99644d2006-04-07 11:52:55 +0000646 if (clo_trace_mem) {
njnfd73ebb2005-12-30 22:39:58 +0000647 IRExpr* data = st->Ist.Tmp.data;
648 if (data->tag == Iex_Load) {
njneaf0ca92006-04-09 01:23:29 +0000649 addEvent_Dr( bbOut, data->Iex.Load.addr,
650 sizeofIRType(data->Iex.Load.ty) );
njnfd73ebb2005-12-30 22:39:58 +0000651 }
652 }
njnd99644d2006-04-07 11:52:55 +0000653 if (clo_detailed_counts) {
sewardj7a26f022005-11-01 17:52:34 +0000654 IRExpr* expr = st->Ist.Tmp.data;
njneaf0ca92006-04-09 01:23:29 +0000655 type = typeOfIRExpr(bbOut->tyenv, expr);
sewardj7a26f022005-11-01 17:52:34 +0000656 tl_assert(type != Ity_INVALID);
657 switch (expr->tag) {
658 case Iex_Load:
njneaf0ca92006-04-09 01:23:29 +0000659 instrument_detail( bbOut, OpLoad, type );
sewardj7a26f022005-11-01 17:52:34 +0000660 break;
661 case Iex_Unop:
662 case Iex_Binop:
sewardje91cea72006-02-08 19:32:02 +0000663 case Iex_Triop:
664 case Iex_Qop:
sewardj7a26f022005-11-01 17:52:34 +0000665 case Iex_Mux0X:
njneaf0ca92006-04-09 01:23:29 +0000666 instrument_detail( bbOut, OpAlu, type );
sewardj7a26f022005-11-01 17:52:34 +0000667 break;
668 default:
669 break;
670 }
671 }
njneaf0ca92006-04-09 01:23:29 +0000672 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 }
sewardj9f649aa2004-11-22 20:38:40 +0000731 break;
732
733 default:
njneaf0ca92006-04-09 01:23:29 +0000734 tl_assert(0);
sewardj9f649aa2004-11-22 20:38:40 +0000735 }
736 }
737
njnd99644d2006-04-07 11:52:55 +0000738 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() );
njneaf0ca92006-04-09 01:23:29 +0000743 addStmtToIRBB( bbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000744 }
sewardj7a26f022005-11-01 17:52:34 +0000745
njneaf0ca92006-04-09 01:23:29 +0000746 if (clo_trace_mem) {
747 /* At the end of the bbIn. Flush outstandings. */
748 flushEvents(bbOut);
749 }
750
751 return bbOut;
njn25e49d8e72002-09-23 09:36:25 +0000752}
753
njn51d827b2005-05-09 01:02:08 +0000754static void lk_fini(Int exitcode)
njn25e49d8e72002-09-23 09:36:25 +0000755{
sewardj7a26f022005-11-01 17:52:34 +0000756 char percentify_buf[4]; /* Two digits, '%' and 0. */
757 const int percentify_size = sizeof(percentify_buf);
758 const int percentify_decs = 0;
759
njnd99644d2006-04-07 11:52:55 +0000760 tl_assert(clo_fnname);
761 tl_assert(clo_fnname[0]);
njn25e49d8e72002-09-23 09:36:25 +0000762
njnd99644d2006-04-07 11:52:55 +0000763 if (clo_basic_counts) {
764 VG_(message)(Vg_UserMsg,
765 "Counted %,llu calls to %s()", n_func_calls, clo_fnname);
njn25e49d8e72002-09-23 09:36:25 +0000766
njnd99644d2006-04-07 11:52:55 +0000767 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) {
sewardj7a26f022005-11-01 17:52:34 +0000795 VG_(message)(Vg_UserMsg, "");
796 VG_(message)(Vg_UserMsg, "IR-level counts by type:");
797 print_details();
798 }
njn25e49d8e72002-09-23 09:36:25 +0000799
njnd99644d2006-04-07 11:52:55 +0000800 if (clo_basic_counts) {
801 VG_(message)(Vg_UserMsg, "");
802 VG_(message)(Vg_UserMsg, "Exit code: %d", exitcode);
803 }
njn25e49d8e72002-09-23 09:36:25 +0000804}
805
njn51d827b2005-05-09 01:02:08 +0000806static 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);
sewardj7a26f022005-11-01 17:52:34 +0000819 VG_(needs_command_line_options)(lk_process_cmd_line_option,
820 lk_print_usage,
821 lk_print_debug_usage);
njn51d827b2005-05-09 01:02:08 +0000822}
823
sewardj45f4e7c2005-09-27 19:20:21 +0000824VG_DETERMINE_INTERFACE_VERSION(lk_pre_clo_init)
fitzhardinge98abfc72003-12-16 02:05:15 +0000825
njn25e49d8e72002-09-23 09:36:25 +0000826/*--------------------------------------------------------------------*/
njn25cac76cb2002-09-23 11:21:57 +0000827/*--- end lk_main.c ---*/
njn25e49d8e72002-09-23 09:36:25 +0000828/*--------------------------------------------------------------------*/