blob: 646200ba628f4599b18e5442af7a8e057fae7a4f [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. */
sewardj0b9d74a2006-12-24 02:24:11 +0000305static void instrument_detail(IRSB* bb, Op op, IRType type)
sewardj7a26f022005-11-01 17:52:34 +0000306{
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);
sewardj0b9d74a2006-12-24 02:24:11 +0000318 addStmtToIRSB( bb, IRStmt_Dirty(di) );
sewardj7a26f022005-11-01 17:52:34 +0000319}
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
sewardj0b9d74a2006-12-24 02:24:11 +0000420static void flushEvents(IRSB* bb)
njneaf0ca92006-04-09 01:23:29 +0000421{
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 );
sewardj0b9d74a2006-12-24 02:24:11 +0000455 addStmtToIRSB( bb, IRStmt_Dirty(di) );
njneaf0ca92006-04-09 01:23:29 +0000456 }
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.
sewardj0b9d74a2006-12-24 02:24:11 +0000466static void addEvent_Ir ( IRSB* bb, IRAtom* iaddr, UInt isize )
njneaf0ca92006-04-09 01:23:29 +0000467{
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
sewardj0b9d74a2006-12-24 02:24:11 +0000482void addEvent_Dr ( IRSB* bb, IRAtom* daddr, Int dsize )
njneaf0ca92006-04-09 01:23:29 +0000483{
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
sewardj0b9d74a2006-12-24 02:24:11 +0000498void addEvent_Dw ( IRSB* bb, IRAtom* daddr, Int dsize )
njneaf0ca92006-04-09 01:23:29 +0000499{
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
sewardj0b9d74a2006-12-24 02:24:11 +0000544IRSB* lk_instrument ( VgCallbackClosure* closure,
545 IRSB* 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;
sewardj0b9d74a2006-12-24 02:24:11 +0000552 IRSB* bbOut;
njneaf0ca92006-04-09 01:23:29 +0000553 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 */
sewardj0b9d74a2006-12-24 02:24:11 +0000563 bbOut = deepCopyIRSBExceptStmts(bbIn);
sewardj9f649aa2004-11-22 20:38:40 +0000564
sewardj7a26f022005-11-01 17:52:34 +0000565 // Copy verbatim any IR preamble preceding the first IMark
566 i = 0;
njneaf0ca92006-04-09 01:23:29 +0000567 while (i < bbIn->stmts_used && bbIn->stmts[i]->tag != Ist_IMark) {
sewardj0b9d74a2006-12-24 02:24:11 +0000568 addStmtToIRSB( bbOut, bbIn->stmts[i] );
sewardj7a26f022005-11-01 17:52:34 +0000569 i++;
sewardj9f649aa2004-11-22 20:38:40 +0000570 }
sewardj9f649aa2004-11-22 20:38:40 +0000571
njnd99644d2006-04-07 11:52:55 +0000572 if (clo_basic_counts) {
573 /* Count this basic block. */
574 di = unsafeIRDirty_0_N( 0, "add_one_BB_entered",
575 VG_(fnptr_to_fnentry)( &add_one_BB_entered ),
576 mkIRExprVec_0() );
sewardj0b9d74a2006-12-24 02:24:11 +0000577 addStmtToIRSB( bbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000578 }
sewardj9f649aa2004-11-22 20:38:40 +0000579
njneaf0ca92006-04-09 01:23:29 +0000580 if (clo_trace_mem) {
581 events_used = 0;
582 }
583
584 for (/*use current i*/; i < bbIn->stmts_used; i++) {
585 IRStmt* st = bbIn->stmts[i];
sewardj7a26f022005-11-01 17:52:34 +0000586 if (!st || st->tag == Ist_NoOp) continue;
sewardj9f649aa2004-11-22 20:38:40 +0000587
njnd99644d2006-04-07 11:52:55 +0000588 if (clo_basic_counts) {
589 /* Count one VEX statement. */
590 di = unsafeIRDirty_0_N( 0, "add_one_IRStmt",
591 VG_(fnptr_to_fnentry)( &add_one_IRStmt ),
592 mkIRExprVec_0() );
sewardj0b9d74a2006-12-24 02:24:11 +0000593 addStmtToIRSB( bbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000594 }
sewardj7a26f022005-11-01 17:52:34 +0000595
sewardj9f649aa2004-11-22 20:38:40 +0000596 switch (st->tag) {
njneaf0ca92006-04-09 01:23:29 +0000597 case Ist_NoOp:
598 case Ist_AbiHint:
599 case Ist_Put:
600 case Ist_PutI:
601 case Ist_MFence:
sewardj0b9d74a2006-12-24 02:24:11 +0000602 addStmtToIRSB( bbOut, st );
njneaf0ca92006-04-09 01:23:29 +0000603 break;
604
sewardj7a26f022005-11-01 17:52:34 +0000605 case Ist_IMark:
njnd99644d2006-04-07 11:52:55 +0000606 if (clo_basic_counts) {
607 /* Count guest instruction. */
608 di = unsafeIRDirty_0_N( 0, "add_one_guest_instr",
609 VG_(fnptr_to_fnentry)( &add_one_guest_instr ),
610 mkIRExprVec_0() );
sewardj0b9d74a2006-12-24 02:24:11 +0000611 addStmtToIRSB( bbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000612
613 /* An unconditional branch to a known destination in the
sewardj0b9d74a2006-12-24 02:24:11 +0000614 * guest's instructions can be represented, in the IRSB to
njnd99644d2006-04-07 11:52:55 +0000615 * instrument, by the VEX statements that are the
616 * translation of that known destination. This feature is
617 * called 'BB chasing' and can be influenced by command
618 * line option --vex-guest-chase-thresh.
619 *
620 * To get an accurate count of the calls to a specific
621 * function, taking BB chasing into account, we need to
622 * check for each guest instruction (Ist_IMark) if it is
623 * the entry point of a function.
624 */
625 tl_assert(clo_fnname);
626 tl_assert(clo_fnname[0]);
627 if (VG_(get_fnname_if_entry)(st->Ist.IMark.addr,
628 fnname, sizeof(fnname))
629 && 0 == VG_(strcmp)(fnname, clo_fnname)) {
630 di = unsafeIRDirty_0_N(
631 0, "add_one_func_call",
632 VG_(fnptr_to_fnentry)( &add_one_func_call ),
633 mkIRExprVec_0() );
sewardj0b9d74a2006-12-24 02:24:11 +0000634 addStmtToIRSB( bbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000635 }
sewardj7a26f022005-11-01 17:52:34 +0000636 }
njnd99644d2006-04-07 11:52:55 +0000637 if (clo_trace_mem) {
njneaf0ca92006-04-09 01:23:29 +0000638 // WARNING: do not remove this function call, even if you
639 // aren't interested in instruction reads. See the comment
640 // above the function itself for more detail.
641 addEvent_Ir( bbOut, mkIRExpr_HWord( (HWord)st->Ist.IMark.addr ),
642 st->Ist.IMark.len );
njnfd73ebb2005-12-30 22:39:58 +0000643 }
sewardj0b9d74a2006-12-24 02:24:11 +0000644 addStmtToIRSB( bbOut, st );
sewardj7a26f022005-11-01 17:52:34 +0000645 break;
646
sewardj0b9d74a2006-12-24 02:24:11 +0000647 case Ist_WrTmp:
njnfd73ebb2005-12-30 22:39:58 +0000648 // Add a call to trace_load() if --trace-mem=yes.
njnd99644d2006-04-07 11:52:55 +0000649 if (clo_trace_mem) {
sewardj0b9d74a2006-12-24 02:24:11 +0000650 IRExpr* data = st->Ist.WrTmp.data;
njnfd73ebb2005-12-30 22:39:58 +0000651 if (data->tag == Iex_Load) {
njneaf0ca92006-04-09 01:23:29 +0000652 addEvent_Dr( bbOut, data->Iex.Load.addr,
653 sizeofIRType(data->Iex.Load.ty) );
njnfd73ebb2005-12-30 22:39:58 +0000654 }
655 }
njnd99644d2006-04-07 11:52:55 +0000656 if (clo_detailed_counts) {
sewardj0b9d74a2006-12-24 02:24:11 +0000657 IRExpr* expr = st->Ist.WrTmp.data;
njneaf0ca92006-04-09 01:23:29 +0000658 type = typeOfIRExpr(bbOut->tyenv, expr);
sewardj7a26f022005-11-01 17:52:34 +0000659 tl_assert(type != Ity_INVALID);
660 switch (expr->tag) {
661 case Iex_Load:
njneaf0ca92006-04-09 01:23:29 +0000662 instrument_detail( bbOut, OpLoad, type );
sewardj7a26f022005-11-01 17:52:34 +0000663 break;
664 case Iex_Unop:
665 case Iex_Binop:
sewardje91cea72006-02-08 19:32:02 +0000666 case Iex_Triop:
667 case Iex_Qop:
sewardj7a26f022005-11-01 17:52:34 +0000668 case Iex_Mux0X:
njneaf0ca92006-04-09 01:23:29 +0000669 instrument_detail( bbOut, OpAlu, type );
sewardj7a26f022005-11-01 17:52:34 +0000670 break;
671 default:
672 break;
673 }
674 }
sewardj0b9d74a2006-12-24 02:24:11 +0000675 addStmtToIRSB( bbOut, st );
njneaf0ca92006-04-09 01:23:29 +0000676 break;
677
678 case Ist_Store:
679 if (clo_trace_mem) {
680 IRExpr* data = st->Ist.Store.data;
681 addEvent_Dw( bbOut, st->Ist.Store.addr,
682 sizeofIRType(typeOfIRExpr(tyenv, data)) );
683 }
684 if (clo_detailed_counts) {
685 type = typeOfIRExpr(bbOut->tyenv, st->Ist.Store.data);
686 tl_assert(type != Ity_INVALID);
687 instrument_detail( bbOut, OpStore, type );
688 }
sewardj0b9d74a2006-12-24 02:24:11 +0000689 addStmtToIRSB( bbOut, st );
njneaf0ca92006-04-09 01:23:29 +0000690 break;
691
692 case Ist_Dirty: {
693 Int dsize;
694 IRDirty* d = st->Ist.Dirty.details;
695 if (d->mFx != Ifx_None) {
696 // This dirty helper accesses memory. Collect the details.
697 tl_assert(d->mAddr != NULL);
698 tl_assert(d->mSize != 0);
699 dsize = d->mSize;
700 if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify)
701 addEvent_Dr( bbOut, d->mAddr, dsize );
702 if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify)
703 addEvent_Dw( bbOut, d->mAddr, dsize );
704 } else {
705 tl_assert(d->mAddr == NULL);
706 tl_assert(d->mSize == 0);
707 }
sewardj0b9d74a2006-12-24 02:24:11 +0000708 addStmtToIRSB( bbOut, st );
njneaf0ca92006-04-09 01:23:29 +0000709 break;
710 }
711
712 case Ist_Exit:
713 if (clo_basic_counts) {
714 /* Count Jcc */
715 di = unsafeIRDirty_0_N( 0, "add_one_Jcc",
716 VG_(fnptr_to_fnentry)( &add_one_Jcc ),
717 mkIRExprVec_0() );
sewardj0b9d74a2006-12-24 02:24:11 +0000718 addStmtToIRSB( bbOut, IRStmt_Dirty(di) );
njneaf0ca92006-04-09 01:23:29 +0000719 }
720 if (clo_trace_mem) {
721 flushEvents(bbOut);
722 }
723
sewardj0b9d74a2006-12-24 02:24:11 +0000724 addStmtToIRSB( bbOut, st ); // Original statement
njneaf0ca92006-04-09 01:23:29 +0000725
726 if (clo_basic_counts) {
727 /* Count non-taken Jcc */
728 di = unsafeIRDirty_0_N( 0, "add_one_Jcc_untaken",
729 VG_(fnptr_to_fnentry)(
730 &add_one_Jcc_untaken ),
731 mkIRExprVec_0() );
sewardj0b9d74a2006-12-24 02:24:11 +0000732 addStmtToIRSB( bbOut, IRStmt_Dirty(di) );
njneaf0ca92006-04-09 01:23:29 +0000733 }
sewardj9f649aa2004-11-22 20:38:40 +0000734 break;
735
736 default:
njneaf0ca92006-04-09 01:23:29 +0000737 tl_assert(0);
sewardj9f649aa2004-11-22 20:38:40 +0000738 }
739 }
740
njnd99644d2006-04-07 11:52:55 +0000741 if (clo_basic_counts) {
742 /* Count this basic block. */
743 di = unsafeIRDirty_0_N( 0, "add_one_BB_completed",
744 VG_(fnptr_to_fnentry)( &add_one_BB_completed ),
745 mkIRExprVec_0() );
sewardj0b9d74a2006-12-24 02:24:11 +0000746 addStmtToIRSB( bbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000747 }
sewardj7a26f022005-11-01 17:52:34 +0000748
njneaf0ca92006-04-09 01:23:29 +0000749 if (clo_trace_mem) {
750 /* At the end of the bbIn. Flush outstandings. */
751 flushEvents(bbOut);
752 }
753
754 return bbOut;
njn25e49d8e72002-09-23 09:36:25 +0000755}
756
njn51d827b2005-05-09 01:02:08 +0000757static void lk_fini(Int exitcode)
njn25e49d8e72002-09-23 09:36:25 +0000758{
sewardj7a26f022005-11-01 17:52:34 +0000759 char percentify_buf[4]; /* Two digits, '%' and 0. */
760 const int percentify_size = sizeof(percentify_buf);
761 const int percentify_decs = 0;
762
njnd99644d2006-04-07 11:52:55 +0000763 tl_assert(clo_fnname);
764 tl_assert(clo_fnname[0]);
njn25e49d8e72002-09-23 09:36:25 +0000765
njnd99644d2006-04-07 11:52:55 +0000766 if (clo_basic_counts) {
767 VG_(message)(Vg_UserMsg,
768 "Counted %,llu calls to %s()", n_func_calls, clo_fnname);
njn25e49d8e72002-09-23 09:36:25 +0000769
njnd99644d2006-04-07 11:52:55 +0000770 VG_(message)(Vg_UserMsg, "");
771 VG_(message)(Vg_UserMsg, "Jccs:");
772 VG_(message)(Vg_UserMsg, " total: %,llu", n_Jccs);
773 VG_(percentify)((n_Jccs - n_Jccs_untaken), (n_Jccs ? n_Jccs : 1),
774 percentify_decs, percentify_size, percentify_buf);
775 VG_(message)(Vg_UserMsg, " taken: %,llu (%s)",
776 (n_Jccs - n_Jccs_untaken), percentify_buf);
777
778 VG_(message)(Vg_UserMsg, "");
779 VG_(message)(Vg_UserMsg, "Executed:");
780 VG_(message)(Vg_UserMsg, " BBs entered: %,llu", n_BBs_entered);
781 VG_(message)(Vg_UserMsg, " BBs completed: %,llu", n_BBs_completed);
782 VG_(message)(Vg_UserMsg, " guest instrs: %,llu", n_guest_instrs);
783 VG_(message)(Vg_UserMsg, " IRStmts: %,llu", n_IRStmts);
784
785 VG_(message)(Vg_UserMsg, "");
786 VG_(message)(Vg_UserMsg, "Ratios:");
787 tl_assert(n_BBs_entered); // Paranoia time.
788 VG_(message)(Vg_UserMsg, " guest instrs : BB entered = %3u : 10",
789 10 * n_guest_instrs / n_BBs_entered);
790 VG_(message)(Vg_UserMsg, " IRStmts : BB entered = %3u : 10",
791 10 * n_IRStmts / n_BBs_entered);
792 tl_assert(n_guest_instrs); // Paranoia time.
793 VG_(message)(Vg_UserMsg, " IRStmts : guest instr = %3u : 10",
794 10 * n_IRStmts / n_guest_instrs);
795 }
796
797 if (clo_detailed_counts) {
sewardj7a26f022005-11-01 17:52:34 +0000798 VG_(message)(Vg_UserMsg, "");
799 VG_(message)(Vg_UserMsg, "IR-level counts by type:");
800 print_details();
801 }
njn25e49d8e72002-09-23 09:36:25 +0000802
njnd99644d2006-04-07 11:52:55 +0000803 if (clo_basic_counts) {
804 VG_(message)(Vg_UserMsg, "");
805 VG_(message)(Vg_UserMsg, "Exit code: %d", exitcode);
806 }
njn25e49d8e72002-09-23 09:36:25 +0000807}
808
njn51d827b2005-05-09 01:02:08 +0000809static void lk_pre_clo_init(void)
810{
811 VG_(details_name) ("Lackey");
812 VG_(details_version) (NULL);
813 VG_(details_description) ("an example Valgrind tool");
814 VG_(details_copyright_author)(
sewardje4b0bf02006-06-05 23:21:15 +0000815 "Copyright (C) 2002-2006, and GNU GPL'd, by Nicholas Nethercote.");
njn51d827b2005-05-09 01:02:08 +0000816 VG_(details_bug_reports_to) (VG_BUGS_TO);
sewardj40823562006-10-17 02:21:17 +0000817 VG_(details_avg_translation_sizeB) ( 200 );
njn51d827b2005-05-09 01:02:08 +0000818
819 VG_(basic_tool_funcs) (lk_post_clo_init,
820 lk_instrument,
821 lk_fini);
sewardj7a26f022005-11-01 17:52:34 +0000822 VG_(needs_command_line_options)(lk_process_cmd_line_option,
823 lk_print_usage,
824 lk_print_debug_usage);
njn51d827b2005-05-09 01:02:08 +0000825}
826
sewardj45f4e7c2005-09-27 19:20:21 +0000827VG_DETERMINE_INTERFACE_VERSION(lk_pre_clo_init)
fitzhardinge98abfc72003-12-16 02:05:15 +0000828
njn25e49d8e72002-09-23 09:36:25 +0000829/*--------------------------------------------------------------------*/
njn25cac76cb2002-09-23 11:21:57 +0000830/*--- end lk_main.c ---*/
njn25e49d8e72002-09-23 09:36:25 +0000831/*--------------------------------------------------------------------*/