blob: c8d9c9c447cf4f8065ac4024f2a17e4292d45bb9 [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
sewardj9ebd6e02007-01-08 06:01:59 +000010 Copyright (C) 2002-2007 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//
sewardj5d1c9012007-02-12 08:42:13 +000033// There are four kinds of instrumentation it can do. They can be turned
njnd99644d2006-04-07 11:52:55 +000034// 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.
sewardj5d1c9012007-02-12 08:42:13 +000041// * --trace-superblocks=yes:
42// trace all superblock entries. Mostly of interest
43// to the Valgrind developers.
njnd99644d2006-04-07 11:52:55 +000044//
45// The code for each kind of instrumentation is guarded by a clo_* variable:
sewardj5d1c9012007-02-12 08:42:13 +000046// clo_basic_counts, clo_detailed_counts, clo_trace_mem and clo_trace_sbs.
njnd99644d2006-04-07 11:52:55 +000047//
48// If you want to modify any of the instrumentation code, look for the code
49// that is guarded by the relevant clo_* variable (eg. clo_trace_mem)
50// If you're not interested in the other kinds of instrumentation you can
51// remove them. If you want to do more complex modifications, please read
52// VEX/pub/libvex_ir.h to understand the intermediate representation.
53//
54//
55// Specific Details about --trace-mem=yes
56// --------------------------------------
njneaf0ca92006-04-09 01:23:29 +000057// Lackey's --trace-mem code is a good starting point for building Valgrind
58// tools that act on memory loads and stores. It also could be used as is,
59// with its output used as input to a post-mortem processing step. However,
60// because memory traces can be very large, online analysis is generally
61// better.
njnfd73ebb2005-12-30 22:39:58 +000062//
njneaf0ca92006-04-09 01:23:29 +000063// It prints memory data access traces that look like this:
64//
sewardj5d1c9012007-02-12 08:42:13 +000065// I 0023C790,2 # instruction read at 0x0023C790 of size 2
66// I 0023C792,5
67// S BE80199C,4 # data store at 0xBE80199C of size 4
68// I 0025242B,3
69// L BE801950,4 # data load at 0xBE801950 of size 4
70// I 0023D476,7
71// M 0025747C,1 # data modify at 0x0025747C of size 1
72// I 0023DC20,2
73// L 00254962,1
74// L BE801FB3,1
75// I 00252305,1
76// L 00254AEB,1
77// S 00257998,1
njneaf0ca92006-04-09 01:23:29 +000078//
79// Every instruction executed has an "instr" event representing it.
80// Instructions that do memory accesses are followed by one or more "load",
81// "store" or "modify" events. Some instructions do more than one load or
82// store, as in the last two examples in the above trace.
83//
84// Here are some examples of x86 instructions that do different combinations
85// of loads, stores, and modifies.
86//
87// Instruction Memory accesses Event sequence
88// ----------- --------------- --------------
89// add %eax, %ebx No loads or stores instr
90//
91// movl (%eax), %ebx loads (%eax) instr, load
92//
93// movl %eax, (%ebx) stores (%ebx) instr, store
94//
95// incl (%ecx) modifies (%ecx) instr, modify
96//
97// cmpsb loads (%esi), loads(%edi) instr, load, load
98//
99// call*l (%edx) loads (%edx), stores -4(%esp) instr, load, store
100// pushl (%edx) loads (%edx), stores -4(%esp) instr, load, store
101// movsw loads (%esi), stores (%edi) instr, load, store
102//
103// Instructions using x86 "rep" prefixes are traced as if they are repeated
104// N times.
105//
106// Lackey with --trace-mem gives good traces, but they are not perfect, for
107// the following reasons:
108//
109// - It does not trace into the OS kernel, so system calls and other kernel
110// operations (eg. some scheduling and signal handling code) are ignored.
111//
njne8d217d2006-10-18 23:46:26 +0000112// - It could model loads and stores done at the system call boundary using
113// the pre_mem_read/post_mem_write events. For example, if you call
114// fstat() you know that the passed in buffer has been written. But it
115// currently does not do this.
116//
117// - Valgrind replaces some code (not much) with its own, notably parts of
118// code for scheduling operations and signal handling. This code is not
119// traced.
njneaf0ca92006-04-09 01:23:29 +0000120//
121// - There is no consideration of virtual-to-physical address mapping.
122// This may not matter for many purposes.
123//
124// - Valgrind modifies the instruction stream in some very minor ways. For
125// example, on x86 the bts, btc, btr instructions are incorrectly
126// considered to always touch memory (this is a consequence of these
127// instructions being very difficult to simulate).
128//
129// - Valgrind tools layout memory differently to normal programs, so the
130// addresses you get will not be typical. Thus Lackey (and all Valgrind
131// tools) is suitable for getting relative memory traces -- eg. if you
132// want to analyse locality of memory accesses -- but is not good if
133// absolute addresses are important.
134//
135// Despite all these warnings, Dullard's results should be good enough for a
136// wide range of purposes. For example, Cachegrind shares all the above
137// shortcomings and it is still useful.
njn9dd72772006-03-11 06:48:20 +0000138//
njnfd73ebb2005-12-30 22:39:58 +0000139// For further inspiration, you should look at cachegrind/cg_main.c which
njneaf0ca92006-04-09 01:23:29 +0000140// uses the same basic technique for tracing memory accesses, but also groups
141// events together for processing into twos and threes so that fewer C calls
142// are made and things run faster.
sewardj5d1c9012007-02-12 08:42:13 +0000143//
144// Specific Details about --trace-superblocks=yes
145// ----------------------------------------------
146// Valgrind splits code up into single entry, multiple exit blocks
147// known as superblocks. By itself, --trace-superblocks=yes just
148// prints a message as each superblock is run:
149//
150// SB 04013170
151// SB 04013177
152// SB 04013173
153// SB 04013177
154//
155// The hex number is the address of the first instruction in the
156// superblock. You can see the relationship more obviously if you use
157// --trace-superblocks=yes and --trace-mem=yes together. Then a "SB"
158// message at address X is immediately followed by an "instr:" message
159// for that address, as the first instruction in the block is
160// executed, for example:
161//
162// SB 04014073
163// I 04014073,3
164// L 7FEFFF7F8,8
165// I 04014076,4
166// I 0401407A,3
167// I 0401407D,3
168// I 04014080,3
169// I 04014083,6
170
njnfd73ebb2005-12-30 22:39:58 +0000171
njnc7561b92005-06-19 01:24:32 +0000172#include "pub_tool_basics.h"
njn43b9a8a2005-05-10 04:37:01 +0000173#include "pub_tool_tooliface.h"
njnf39e9a32005-06-12 02:43:17 +0000174#include "pub_tool_libcassert.h"
njn36a20fa2005-06-03 03:08:39 +0000175#include "pub_tool_libcprint.h"
sewardj7a26f022005-11-01 17:52:34 +0000176#include "pub_tool_debuginfo.h"
177#include "pub_tool_libcbase.h"
178#include "pub_tool_options.h"
sewardj5fed8c02005-12-23 12:56:11 +0000179#include "pub_tool_machine.h" // VG_(fnptr_to_fnentry)
sewardj7a26f022005-11-01 17:52:34 +0000180
njnd99644d2006-04-07 11:52:55 +0000181/*------------------------------------------------------------*/
182/*--- Command line options ---*/
183/*------------------------------------------------------------*/
sewardj7a26f022005-11-01 17:52:34 +0000184
njnd99644d2006-04-07 11:52:55 +0000185/* Command line options controlling instrumentation kinds, as described at
186 * the top of this file. */
187static Bool clo_basic_counts = True;
188static Bool clo_detailed_counts = False;
189static Bool clo_trace_mem = False;
sewardj5d1c9012007-02-12 08:42:13 +0000190static Bool clo_trace_sbs = False;
sewardj7a26f022005-11-01 17:52:34 +0000191
njnd99644d2006-04-07 11:52:55 +0000192/* The name of the function of which the number of calls (under
193 * --basic-counts=yes) is to be counted, with default. Override with command
194 * line option --fnname. */
195static Char* clo_fnname = "_dl_runtime_resolve";
sewardj7a26f022005-11-01 17:52:34 +0000196
197static Bool lk_process_cmd_line_option(Char* arg)
198{
njnd99644d2006-04-07 11:52:55 +0000199 VG_STR_CLO(arg, "--fnname", clo_fnname)
sewardj5d1c9012007-02-12 08:42:13 +0000200 else VG_BOOL_CLO(arg, "--basic-counts", clo_basic_counts)
201 else VG_BOOL_CLO(arg, "--detailed-counts", clo_detailed_counts)
202 else VG_BOOL_CLO(arg, "--trace-mem", clo_trace_mem)
203 else VG_BOOL_CLO(arg, "--trace-superblocks", clo_trace_sbs)
sewardj7a26f022005-11-01 17:52:34 +0000204 else
205 return False;
206
njnd99644d2006-04-07 11:52:55 +0000207 tl_assert(clo_fnname);
208 tl_assert(clo_fnname[0]);
sewardj7a26f022005-11-01 17:52:34 +0000209 return True;
210}
211
212static void lk_print_usage(void)
213{
214 VG_(printf)(
njnd99644d2006-04-07 11:52:55 +0000215" --basic-counts=no|yes count instructions, jumps, etc. [no]\n"
sewardj7a26f022005-11-01 17:52:34 +0000216" --detailed-counts=no|yes count loads, stores and alu ops [no]\n"
njnd99644d2006-04-07 11:52:55 +0000217" --trace-mem=no|yes trace all loads and stores [no]\n"
sewardj5d1c9012007-02-12 08:42:13 +0000218" --trace-superblocks=no|yes trace all superblock entries [no]\n"
njnd99644d2006-04-07 11:52:55 +0000219" --fnname=<name> count calls to <name> (only used if\n"
220" --basic-count=yes) [_dl_runtime_resolve]\n"
sewardj7a26f022005-11-01 17:52:34 +0000221 );
222}
223
224static void lk_print_debug_usage(void)
225{
njneaf0ca92006-04-09 01:23:29 +0000226 VG_(printf)(
227" (none)\n"
228 );
sewardj7a26f022005-11-01 17:52:34 +0000229}
230
njnd99644d2006-04-07 11:52:55 +0000231/*------------------------------------------------------------*/
njneaf0ca92006-04-09 01:23:29 +0000232/*--- Stuff for --basic-counts ---*/
njnd99644d2006-04-07 11:52:55 +0000233/*------------------------------------------------------------*/
njn25e49d8e72002-09-23 09:36:25 +0000234
njn25e49d8e72002-09-23 09:36:25 +0000235/* Nb: use ULongs because the numbers can get very big */
sewardj7a26f022005-11-01 17:52:34 +0000236static ULong n_func_calls = 0;
sewardj5d1c9012007-02-12 08:42:13 +0000237static ULong n_SBs_entered = 0;
238static ULong n_SBs_completed = 0;
njneaf0ca92006-04-09 01:23:29 +0000239static ULong n_IRStmts = 0;
sewardj7a26f022005-11-01 17:52:34 +0000240static ULong n_guest_instrs = 0;
241static ULong n_Jccs = 0;
242static ULong n_Jccs_untaken = 0;
njn25e49d8e72002-09-23 09:36:25 +0000243
sewardj7a26f022005-11-01 17:52:34 +0000244static void add_one_func_call(void)
njn25e49d8e72002-09-23 09:36:25 +0000245{
sewardj7a26f022005-11-01 17:52:34 +0000246 n_func_calls++;
njn25e49d8e72002-09-23 09:36:25 +0000247}
248
sewardj5d1c9012007-02-12 08:42:13 +0000249static void add_one_SB_entered(void)
njn25e49d8e72002-09-23 09:36:25 +0000250{
sewardj5d1c9012007-02-12 08:42:13 +0000251 n_SBs_entered++;
sewardj7a26f022005-11-01 17:52:34 +0000252}
253
sewardj5d1c9012007-02-12 08:42:13 +0000254static void add_one_SB_completed(void)
sewardj7a26f022005-11-01 17:52:34 +0000255{
sewardj5d1c9012007-02-12 08:42:13 +0000256 n_SBs_completed++;
njn25e49d8e72002-09-23 09:36:25 +0000257}
258
sewardj7a26f022005-11-01 17:52:34 +0000259static void add_one_IRStmt(void)
njn25e49d8e72002-09-23 09:36:25 +0000260{
sewardj7a26f022005-11-01 17:52:34 +0000261 n_IRStmts++;
njn25e49d8e72002-09-23 09:36:25 +0000262}
263
sewardj9f649aa2004-11-22 20:38:40 +0000264static void add_one_guest_instr(void)
njn25e49d8e72002-09-23 09:36:25 +0000265{
sewardj9f649aa2004-11-22 20:38:40 +0000266 n_guest_instrs++;
njn25e49d8e72002-09-23 09:36:25 +0000267}
268
269static void add_one_Jcc(void)
270{
271 n_Jccs++;
272}
273
274static void add_one_Jcc_untaken(void)
275{
276 n_Jccs_untaken++;
277}
278
njnd99644d2006-04-07 11:52:55 +0000279/*------------------------------------------------------------*/
njneaf0ca92006-04-09 01:23:29 +0000280/*--- Stuff for --detailed-counts ---*/
njnd99644d2006-04-07 11:52:55 +0000281/*------------------------------------------------------------*/
sewardj7a26f022005-11-01 17:52:34 +0000282
283/* --- Operations --- */
284
285typedef enum { OpLoad=0, OpStore=1, OpAlu=2 } Op;
286
287#define N_OPS 3
288
289
290/* --- Types --- */
291
292#define N_TYPES 9
293
294static Int type2index ( IRType ty )
njn25e49d8e72002-09-23 09:36:25 +0000295{
sewardj7a26f022005-11-01 17:52:34 +0000296 switch (ty) {
297 case Ity_I1: return 0;
298 case Ity_I8: return 1;
299 case Ity_I16: return 2;
300 case Ity_I32: return 3;
301 case Ity_I64: return 4;
302 case Ity_I128: return 5;
303 case Ity_F32: return 6;
304 case Ity_F64: return 7;
305 case Ity_V128: return 8;
306 default: tl_assert(0); break;
307 }
njn25e49d8e72002-09-23 09:36:25 +0000308}
309
sewardj7a26f022005-11-01 17:52:34 +0000310static HChar* nameOfTypeIndex ( IRType ty )
311{
312 switch (ty) {
313 case 0: return "I1"; break;
314 case 1: return "I8"; break;
315 case 2: return "I16"; break;
316 case 3: return "I32"; break;
317 case 4: return "I64"; break;
318 case 5: return "I128"; break;
319 case 6: return "F32"; break;
320 case 7: return "F64"; break;
321 case 8: return "V128"; break;
322 default: tl_assert(0); break;
323 }
324}
njn25e49d8e72002-09-23 09:36:25 +0000325
njn25e49d8e72002-09-23 09:36:25 +0000326
sewardj7a26f022005-11-01 17:52:34 +0000327/* --- Counts --- */
njn25e49d8e72002-09-23 09:36:25 +0000328
sewardj7a26f022005-11-01 17:52:34 +0000329static ULong detailCounts[N_OPS][N_TYPES];
njn25e49d8e72002-09-23 09:36:25 +0000330
sewardj7a26f022005-11-01 17:52:34 +0000331/* The helper that is called from the instrumented code. */
332static VG_REGPARM(1)
333void increment_detail(ULong* detail)
334{
335 (*detail)++;
336}
njn25e49d8e72002-09-23 09:36:25 +0000337
sewardj7a26f022005-11-01 17:52:34 +0000338/* A helper that adds the instrumentation for a detail. */
sewardj5d1c9012007-02-12 08:42:13 +0000339static void instrument_detail(IRSB* sb, Op op, IRType type)
sewardj7a26f022005-11-01 17:52:34 +0000340{
341 IRDirty* di;
342 IRExpr** argv;
343 const UInt typeIx = type2index(type);
njn25e49d8e72002-09-23 09:36:25 +0000344
sewardj7a26f022005-11-01 17:52:34 +0000345 tl_assert(op < N_OPS);
346 tl_assert(typeIx < N_TYPES);
njn25e49d8e72002-09-23 09:36:25 +0000347
sewardj7a26f022005-11-01 17:52:34 +0000348 argv = mkIRExprVec_1( mkIRExpr_HWord( (HWord)&detailCounts[op][typeIx] ) );
sewardj5fed8c02005-12-23 12:56:11 +0000349 di = unsafeIRDirty_0_N( 1, "increment_detail",
350 VG_(fnptr_to_fnentry)( &increment_detail ),
351 argv);
sewardj5d1c9012007-02-12 08:42:13 +0000352 addStmtToIRSB( sb, IRStmt_Dirty(di) );
sewardj7a26f022005-11-01 17:52:34 +0000353}
njn25e49d8e72002-09-23 09:36:25 +0000354
sewardj7a26f022005-11-01 17:52:34 +0000355/* Summarize and print the details. */
sewardj7a26f022005-11-01 17:52:34 +0000356static void print_details ( void )
357{
358 Int typeIx;
359 VG_(message)(Vg_UserMsg,
360 " Type Loads Stores AluOps");
361 VG_(message)(Vg_UserMsg,
362 " -------------------------------------------");
363 for (typeIx = 0; typeIx < N_TYPES; typeIx++) {
364 VG_(message)(Vg_UserMsg,
365 " %4s %,12llu %,12llu %,12llu",
366 nameOfTypeIndex( typeIx ),
367 detailCounts[OpLoad ][typeIx],
368 detailCounts[OpStore][typeIx],
369 detailCounts[OpAlu ][typeIx]
370 );
371 }
372}
njn25e49d8e72002-09-23 09:36:25 +0000373
sewardj7a26f022005-11-01 17:52:34 +0000374
njnd99644d2006-04-07 11:52:55 +0000375/*------------------------------------------------------------*/
njneaf0ca92006-04-09 01:23:29 +0000376/*--- Stuff for --trace-mem ---*/
njnd99644d2006-04-07 11:52:55 +0000377/*------------------------------------------------------------*/
njnfd73ebb2005-12-30 22:39:58 +0000378
njneaf0ca92006-04-09 01:23:29 +0000379#define MAX_DSIZE 512
380
381typedef
382 IRExpr
383 IRAtom;
384
385typedef
386 enum { Event_Ir, Event_Dr, Event_Dw, Event_Dm }
387 EventKind;
388
389typedef
390 struct {
391 EventKind ekind;
392 IRAtom* addr;
393 Int size;
394 }
395 Event;
396
397/* Up to this many unnotified events are allowed. Must be at least two,
398 so that reads and writes to the same address can be merged into a modify.
399 Beyond that, larger numbers just potentially induce more spilling due to
400 extending live ranges of address temporaries. */
401#define N_EVENTS 4
402
403/* Maintain an ordered list of memory events which are outstanding, in
404 the sense that no IR has yet been generated to do the relevant
sewardj5d1c9012007-02-12 08:42:13 +0000405 helper calls. The SB is scanned top to bottom and memory events
njneaf0ca92006-04-09 01:23:29 +0000406 are added to the end of the list, merging with the most recent
407 notified event where possible (Dw immediately following Dr and
408 having the same size and EA can be merged).
409
410 This merging is done so that for architectures which have
411 load-op-store instructions (x86, amd64), the instr is treated as if
412 it makes just one memory reference (a modify), rather than two (a
413 read followed by a write at the same address).
414
415 At various points the list will need to be flushed, that is, IR
416 generated from it. That must happen before any possible exit from
417 the block (the end, or an IRStmt_Exit). Flushing also takes place
418 when there is no space to add a new event.
419
420 If we require the simulation statistics to be up to date with
421 respect to possible memory exceptions, then the list would have to
422 be flushed before each memory reference. That's a pain so we don't
423 bother.
424
425 Flushing the list consists of walking it start to end and emitting
426 instrumentation IR for each event, in the order in which they
427 appear. */
428
429static Event events[N_EVENTS];
430static Int events_used = 0;
431
432
433static VG_REGPARM(2) void trace_instr(Addr addr, SizeT size)
434{
sewardj5d1c9012007-02-12 08:42:13 +0000435 VG_(printf)("I %08lx,%d\n", addr, size);
njneaf0ca92006-04-09 01:23:29 +0000436}
437
njnfd73ebb2005-12-30 22:39:58 +0000438static VG_REGPARM(2) void trace_load(Addr addr, SizeT size)
439{
sewardj5d1c9012007-02-12 08:42:13 +0000440 VG_(printf)(" L %08lx,%d\n", addr, size);
njnfd73ebb2005-12-30 22:39:58 +0000441}
442
443static VG_REGPARM(2) void trace_store(Addr addr, SizeT size)
444{
sewardj5d1c9012007-02-12 08:42:13 +0000445 VG_(printf)(" S %08lx,%d\n", addr, size);
njneaf0ca92006-04-09 01:23:29 +0000446}
447
448static VG_REGPARM(2) void trace_modify(Addr addr, SizeT size)
449{
sewardj5d1c9012007-02-12 08:42:13 +0000450 VG_(printf)(" M %08lx,%d\n", addr, size);
njneaf0ca92006-04-09 01:23:29 +0000451}
452
453
sewardj5d1c9012007-02-12 08:42:13 +0000454static void flushEvents(IRSB* sb)
njneaf0ca92006-04-09 01:23:29 +0000455{
456 Int i;
457 Char* helperName;
458 void* helperAddr;
459 IRExpr** argv;
460 IRDirty* di;
461 Event* ev;
462
463 for (i = 0; i < events_used; i++) {
464
465 ev = &events[i];
466
467 // Decide on helper fn to call and args to pass it.
468 switch (ev->ekind) {
469 case Event_Ir: helperName = "trace_instr";
470 helperAddr = trace_instr; break;
471
472 case Event_Dr: helperName = "trace_load";
473 helperAddr = trace_load; break;
474
475 case Event_Dw: helperName = "trace_store";
476 helperAddr = trace_store; break;
477
478 case Event_Dm: helperName = "trace_modify";
479 helperAddr = trace_modify; break;
480 default:
481 tl_assert(0);
482 }
483
484 // Add the helper.
485 argv = mkIRExprVec_2( ev->addr, mkIRExpr_HWord( ev->size ) );
486 di = unsafeIRDirty_0_N( /*regparms*/2,
487 helperName, VG_(fnptr_to_fnentry)( helperAddr ),
488 argv );
sewardj5d1c9012007-02-12 08:42:13 +0000489 addStmtToIRSB( sb, IRStmt_Dirty(di) );
njneaf0ca92006-04-09 01:23:29 +0000490 }
491
492 events_used = 0;
493}
494
495// WARNING: If you aren't interested in instruction reads, you can omit the
496// code that adds calls to trace_instr() in flushEvents(). However, you
497// must still call this function, addEvent_Ir() -- it is necessary to add
498// the Ir events to the events list so that merging of paired load/store
499// events into modify events works correctly.
sewardj5d1c9012007-02-12 08:42:13 +0000500static void addEvent_Ir ( IRSB* sb, IRAtom* iaddr, UInt isize )
njneaf0ca92006-04-09 01:23:29 +0000501{
502 Event* evt;
503 tl_assert( (VG_MIN_INSTR_SZB <= isize && isize <= VG_MAX_INSTR_SZB)
504 || VG_CLREQ_SZB == isize );
505 if (events_used == N_EVENTS)
sewardj5d1c9012007-02-12 08:42:13 +0000506 flushEvents(sb);
njneaf0ca92006-04-09 01:23:29 +0000507 tl_assert(events_used >= 0 && events_used < N_EVENTS);
508 evt = &events[events_used];
509 evt->ekind = Event_Ir;
510 evt->addr = iaddr;
511 evt->size = isize;
512 events_used++;
513}
514
515static
sewardj5d1c9012007-02-12 08:42:13 +0000516void addEvent_Dr ( IRSB* sb, IRAtom* daddr, Int dsize )
njneaf0ca92006-04-09 01:23:29 +0000517{
518 Event* evt;
519 tl_assert(isIRAtom(daddr));
520 tl_assert(dsize >= 1 && dsize <= MAX_DSIZE);
521 if (events_used == N_EVENTS)
sewardj5d1c9012007-02-12 08:42:13 +0000522 flushEvents(sb);
njneaf0ca92006-04-09 01:23:29 +0000523 tl_assert(events_used >= 0 && events_used < N_EVENTS);
524 evt = &events[events_used];
525 evt->ekind = Event_Dr;
526 evt->addr = daddr;
527 evt->size = dsize;
528 events_used++;
529}
530
531static
sewardj5d1c9012007-02-12 08:42:13 +0000532void addEvent_Dw ( IRSB* sb, IRAtom* daddr, Int dsize )
njneaf0ca92006-04-09 01:23:29 +0000533{
534 Event* lastEvt;
535 Event* evt;
536 tl_assert(isIRAtom(daddr));
537 tl_assert(dsize >= 1 && dsize <= MAX_DSIZE);
538
539 // Is it possible to merge this write with the preceding read?
540 lastEvt = &events[events_used-1];
541 if (events_used > 0
542 && lastEvt->ekind == Event_Dr
543 && lastEvt->size == dsize
544 && eqIRAtom(lastEvt->addr, daddr))
545 {
546 lastEvt->ekind = Event_Dm;
547 return;
548 }
549
550 // No. Add as normal.
551 if (events_used == N_EVENTS)
sewardj5d1c9012007-02-12 08:42:13 +0000552 flushEvents(sb);
njneaf0ca92006-04-09 01:23:29 +0000553 tl_assert(events_used >= 0 && events_used < N_EVENTS);
554 evt = &events[events_used];
555 evt->ekind = Event_Dw;
556 evt->size = dsize;
557 evt->addr = daddr;
558 events_used++;
njnfd73ebb2005-12-30 22:39:58 +0000559}
560
njnd99644d2006-04-07 11:52:55 +0000561
562/*------------------------------------------------------------*/
sewardj5d1c9012007-02-12 08:42:13 +0000563/*--- Stuff for --trace-superblocks ---*/
564/*------------------------------------------------------------*/
565
566static void trace_superblock(Addr addr)
567{
568 VG_(printf)("SB %08lx\n", addr);
569}
570
571
572/*------------------------------------------------------------*/
njnd99644d2006-04-07 11:52:55 +0000573/*--- Basic tool functions ---*/
574/*------------------------------------------------------------*/
sewardj7a26f022005-11-01 17:52:34 +0000575
576static void lk_post_clo_init(void)
577{
578 Int op, tyIx;
579
njnd99644d2006-04-07 11:52:55 +0000580 if (clo_detailed_counts) {
581 for (op = 0; op < N_OPS; op++)
582 for (tyIx = 0; tyIx < N_TYPES; tyIx++)
583 detailCounts[op][tyIx] = 0;
584 }
sewardj7a26f022005-11-01 17:52:34 +0000585}
586
sewardj4ba057c2005-10-18 12:04:18 +0000587static
sewardj0b9d74a2006-12-24 02:24:11 +0000588IRSB* lk_instrument ( VgCallbackClosure* closure,
sewardj5d1c9012007-02-12 08:42:13 +0000589 IRSB* sbIn,
sewardj461df9c2006-01-17 02:06:39 +0000590 VexGuestLayout* layout,
591 VexGuestExtents* vge,
592 IRType gWordTy, IRType hWordTy )
njn25e49d8e72002-09-23 09:36:25 +0000593{
njneaf0ca92006-04-09 01:23:29 +0000594 IRDirty* di;
595 Int i;
sewardj5d1c9012007-02-12 08:42:13 +0000596 IRSB* sbOut;
njneaf0ca92006-04-09 01:23:29 +0000597 Char fnname[100];
598 IRType type;
sewardj5d1c9012007-02-12 08:42:13 +0000599 IRTypeEnv* tyenv = sbIn->tyenv;
sewardjd54babf2005-03-21 00:55:49 +0000600
601 if (gWordTy != hWordTy) {
602 /* We don't currently support this case. */
603 VG_(tool_panic)("host/guest word size mismatch");
604 }
sewardj9f649aa2004-11-22 20:38:40 +0000605
sewardj5d1c9012007-02-12 08:42:13 +0000606 /* Set up SB */
607 sbOut = deepCopyIRSBExceptStmts(sbIn);
sewardj9f649aa2004-11-22 20:38:40 +0000608
sewardj7a26f022005-11-01 17:52:34 +0000609 // Copy verbatim any IR preamble preceding the first IMark
610 i = 0;
sewardj5d1c9012007-02-12 08:42:13 +0000611 while (i < sbIn->stmts_used && sbIn->stmts[i]->tag != Ist_IMark) {
612 addStmtToIRSB( sbOut, sbIn->stmts[i] );
sewardj7a26f022005-11-01 17:52:34 +0000613 i++;
sewardj9f649aa2004-11-22 20:38:40 +0000614 }
sewardj9f649aa2004-11-22 20:38:40 +0000615
njnd99644d2006-04-07 11:52:55 +0000616 if (clo_basic_counts) {
sewardj5d1c9012007-02-12 08:42:13 +0000617 /* Count this superblock. */
618 di = unsafeIRDirty_0_N( 0, "add_one_SB_entered",
619 VG_(fnptr_to_fnentry)( &add_one_SB_entered ),
njnd99644d2006-04-07 11:52:55 +0000620 mkIRExprVec_0() );
sewardj5d1c9012007-02-12 08:42:13 +0000621 addStmtToIRSB( sbOut, IRStmt_Dirty(di) );
622 }
623
624 if (clo_trace_sbs) {
625 /* Print this superblock's address. */
626 di = unsafeIRDirty_0_N(
627 0, "trace_superblock",
628 VG_(fnptr_to_fnentry)( &trace_superblock ),
629 mkIRExprVec_1( mkIRExpr_HWord( vge->base[0] ) )
630 );
631 addStmtToIRSB( sbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000632 }
sewardj9f649aa2004-11-22 20:38:40 +0000633
njneaf0ca92006-04-09 01:23:29 +0000634 if (clo_trace_mem) {
635 events_used = 0;
636 }
637
sewardj5d1c9012007-02-12 08:42:13 +0000638 for (/*use current i*/; i < sbIn->stmts_used; i++) {
639 IRStmt* st = sbIn->stmts[i];
sewardj7a26f022005-11-01 17:52:34 +0000640 if (!st || st->tag == Ist_NoOp) continue;
sewardj9f649aa2004-11-22 20:38:40 +0000641
njnd99644d2006-04-07 11:52:55 +0000642 if (clo_basic_counts) {
643 /* Count one VEX statement. */
644 di = unsafeIRDirty_0_N( 0, "add_one_IRStmt",
645 VG_(fnptr_to_fnentry)( &add_one_IRStmt ),
646 mkIRExprVec_0() );
sewardj5d1c9012007-02-12 08:42:13 +0000647 addStmtToIRSB( sbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000648 }
sewardj7a26f022005-11-01 17:52:34 +0000649
sewardj9f649aa2004-11-22 20:38:40 +0000650 switch (st->tag) {
njneaf0ca92006-04-09 01:23:29 +0000651 case Ist_NoOp:
652 case Ist_AbiHint:
653 case Ist_Put:
654 case Ist_PutI:
655 case Ist_MFence:
sewardj5d1c9012007-02-12 08:42:13 +0000656 addStmtToIRSB( sbOut, st );
njneaf0ca92006-04-09 01:23:29 +0000657 break;
658
sewardj7a26f022005-11-01 17:52:34 +0000659 case Ist_IMark:
njnd99644d2006-04-07 11:52:55 +0000660 if (clo_basic_counts) {
661 /* Count guest instruction. */
662 di = unsafeIRDirty_0_N( 0, "add_one_guest_instr",
663 VG_(fnptr_to_fnentry)( &add_one_guest_instr ),
664 mkIRExprVec_0() );
sewardj5d1c9012007-02-12 08:42:13 +0000665 addStmtToIRSB( sbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000666
667 /* An unconditional branch to a known destination in the
sewardj0b9d74a2006-12-24 02:24:11 +0000668 * guest's instructions can be represented, in the IRSB to
njnd99644d2006-04-07 11:52:55 +0000669 * instrument, by the VEX statements that are the
670 * translation of that known destination. This feature is
sewardj5d1c9012007-02-12 08:42:13 +0000671 * called 'SB chasing' and can be influenced by command
njnd99644d2006-04-07 11:52:55 +0000672 * line option --vex-guest-chase-thresh.
673 *
674 * To get an accurate count of the calls to a specific
sewardj5d1c9012007-02-12 08:42:13 +0000675 * function, taking SB chasing into account, we need to
njnd99644d2006-04-07 11:52:55 +0000676 * check for each guest instruction (Ist_IMark) if it is
677 * the entry point of a function.
678 */
679 tl_assert(clo_fnname);
680 tl_assert(clo_fnname[0]);
681 if (VG_(get_fnname_if_entry)(st->Ist.IMark.addr,
682 fnname, sizeof(fnname))
683 && 0 == VG_(strcmp)(fnname, clo_fnname)) {
684 di = unsafeIRDirty_0_N(
685 0, "add_one_func_call",
686 VG_(fnptr_to_fnentry)( &add_one_func_call ),
687 mkIRExprVec_0() );
sewardj5d1c9012007-02-12 08:42:13 +0000688 addStmtToIRSB( sbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000689 }
sewardj7a26f022005-11-01 17:52:34 +0000690 }
njnd99644d2006-04-07 11:52:55 +0000691 if (clo_trace_mem) {
njneaf0ca92006-04-09 01:23:29 +0000692 // WARNING: do not remove this function call, even if you
693 // aren't interested in instruction reads. See the comment
694 // above the function itself for more detail.
sewardj5d1c9012007-02-12 08:42:13 +0000695 addEvent_Ir( sbOut, mkIRExpr_HWord( (HWord)st->Ist.IMark.addr ),
njneaf0ca92006-04-09 01:23:29 +0000696 st->Ist.IMark.len );
njnfd73ebb2005-12-30 22:39:58 +0000697 }
sewardj5d1c9012007-02-12 08:42:13 +0000698 addStmtToIRSB( sbOut, st );
sewardj7a26f022005-11-01 17:52:34 +0000699 break;
700
sewardj0b9d74a2006-12-24 02:24:11 +0000701 case Ist_WrTmp:
njnfd73ebb2005-12-30 22:39:58 +0000702 // Add a call to trace_load() if --trace-mem=yes.
njnd99644d2006-04-07 11:52:55 +0000703 if (clo_trace_mem) {
sewardj0b9d74a2006-12-24 02:24:11 +0000704 IRExpr* data = st->Ist.WrTmp.data;
njnfd73ebb2005-12-30 22:39:58 +0000705 if (data->tag == Iex_Load) {
sewardj5d1c9012007-02-12 08:42:13 +0000706 addEvent_Dr( sbOut, data->Iex.Load.addr,
njneaf0ca92006-04-09 01:23:29 +0000707 sizeofIRType(data->Iex.Load.ty) );
njnfd73ebb2005-12-30 22:39:58 +0000708 }
709 }
njnd99644d2006-04-07 11:52:55 +0000710 if (clo_detailed_counts) {
sewardj0b9d74a2006-12-24 02:24:11 +0000711 IRExpr* expr = st->Ist.WrTmp.data;
sewardj5d1c9012007-02-12 08:42:13 +0000712 type = typeOfIRExpr(sbOut->tyenv, expr);
sewardj7a26f022005-11-01 17:52:34 +0000713 tl_assert(type != Ity_INVALID);
714 switch (expr->tag) {
715 case Iex_Load:
sewardj5d1c9012007-02-12 08:42:13 +0000716 instrument_detail( sbOut, OpLoad, type );
sewardj7a26f022005-11-01 17:52:34 +0000717 break;
718 case Iex_Unop:
719 case Iex_Binop:
sewardje91cea72006-02-08 19:32:02 +0000720 case Iex_Triop:
721 case Iex_Qop:
sewardj7a26f022005-11-01 17:52:34 +0000722 case Iex_Mux0X:
sewardj5d1c9012007-02-12 08:42:13 +0000723 instrument_detail( sbOut, OpAlu, type );
sewardj7a26f022005-11-01 17:52:34 +0000724 break;
725 default:
726 break;
727 }
728 }
sewardj5d1c9012007-02-12 08:42:13 +0000729 addStmtToIRSB( sbOut, st );
njneaf0ca92006-04-09 01:23:29 +0000730 break;
731
732 case Ist_Store:
733 if (clo_trace_mem) {
734 IRExpr* data = st->Ist.Store.data;
sewardj5d1c9012007-02-12 08:42:13 +0000735 addEvent_Dw( sbOut, st->Ist.Store.addr,
njneaf0ca92006-04-09 01:23:29 +0000736 sizeofIRType(typeOfIRExpr(tyenv, data)) );
737 }
738 if (clo_detailed_counts) {
sewardj5d1c9012007-02-12 08:42:13 +0000739 type = typeOfIRExpr(sbOut->tyenv, st->Ist.Store.data);
njneaf0ca92006-04-09 01:23:29 +0000740 tl_assert(type != Ity_INVALID);
sewardj5d1c9012007-02-12 08:42:13 +0000741 instrument_detail( sbOut, OpStore, type );
njneaf0ca92006-04-09 01:23:29 +0000742 }
sewardj5d1c9012007-02-12 08:42:13 +0000743 addStmtToIRSB( sbOut, st );
njneaf0ca92006-04-09 01:23:29 +0000744 break;
745
746 case Ist_Dirty: {
747 Int dsize;
748 IRDirty* d = st->Ist.Dirty.details;
749 if (d->mFx != Ifx_None) {
750 // This dirty helper accesses memory. Collect the details.
751 tl_assert(d->mAddr != NULL);
752 tl_assert(d->mSize != 0);
753 dsize = d->mSize;
754 if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify)
sewardj5d1c9012007-02-12 08:42:13 +0000755 addEvent_Dr( sbOut, d->mAddr, dsize );
njneaf0ca92006-04-09 01:23:29 +0000756 if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify)
sewardj5d1c9012007-02-12 08:42:13 +0000757 addEvent_Dw( sbOut, d->mAddr, dsize );
njneaf0ca92006-04-09 01:23:29 +0000758 } else {
759 tl_assert(d->mAddr == NULL);
760 tl_assert(d->mSize == 0);
761 }
sewardj5d1c9012007-02-12 08:42:13 +0000762 addStmtToIRSB( sbOut, st );
njneaf0ca92006-04-09 01:23:29 +0000763 break;
764 }
765
766 case Ist_Exit:
767 if (clo_basic_counts) {
768 /* Count Jcc */
769 di = unsafeIRDirty_0_N( 0, "add_one_Jcc",
770 VG_(fnptr_to_fnentry)( &add_one_Jcc ),
771 mkIRExprVec_0() );
sewardj5d1c9012007-02-12 08:42:13 +0000772 addStmtToIRSB( sbOut, IRStmt_Dirty(di) );
njneaf0ca92006-04-09 01:23:29 +0000773 }
774 if (clo_trace_mem) {
sewardj5d1c9012007-02-12 08:42:13 +0000775 flushEvents(sbOut);
njneaf0ca92006-04-09 01:23:29 +0000776 }
777
sewardj5d1c9012007-02-12 08:42:13 +0000778 addStmtToIRSB( sbOut, st ); // Original statement
njneaf0ca92006-04-09 01:23:29 +0000779
780 if (clo_basic_counts) {
781 /* Count non-taken Jcc */
782 di = unsafeIRDirty_0_N( 0, "add_one_Jcc_untaken",
783 VG_(fnptr_to_fnentry)(
784 &add_one_Jcc_untaken ),
785 mkIRExprVec_0() );
sewardj5d1c9012007-02-12 08:42:13 +0000786 addStmtToIRSB( sbOut, IRStmt_Dirty(di) );
njneaf0ca92006-04-09 01:23:29 +0000787 }
sewardj9f649aa2004-11-22 20:38:40 +0000788 break;
789
790 default:
njneaf0ca92006-04-09 01:23:29 +0000791 tl_assert(0);
sewardj9f649aa2004-11-22 20:38:40 +0000792 }
793 }
794
njnd99644d2006-04-07 11:52:55 +0000795 if (clo_basic_counts) {
796 /* Count this basic block. */
sewardj5d1c9012007-02-12 08:42:13 +0000797 di = unsafeIRDirty_0_N( 0, "add_one_SB_completed",
798 VG_(fnptr_to_fnentry)( &add_one_SB_completed ),
njnd99644d2006-04-07 11:52:55 +0000799 mkIRExprVec_0() );
sewardj5d1c9012007-02-12 08:42:13 +0000800 addStmtToIRSB( sbOut, IRStmt_Dirty(di) );
njnd99644d2006-04-07 11:52:55 +0000801 }
sewardj7a26f022005-11-01 17:52:34 +0000802
njneaf0ca92006-04-09 01:23:29 +0000803 if (clo_trace_mem) {
sewardj5d1c9012007-02-12 08:42:13 +0000804 /* At the end of the sbIn. Flush outstandings. */
805 flushEvents(sbOut);
njneaf0ca92006-04-09 01:23:29 +0000806 }
807
sewardj5d1c9012007-02-12 08:42:13 +0000808 return sbOut;
njn25e49d8e72002-09-23 09:36:25 +0000809}
810
njn51d827b2005-05-09 01:02:08 +0000811static void lk_fini(Int exitcode)
njn25e49d8e72002-09-23 09:36:25 +0000812{
sewardj7a26f022005-11-01 17:52:34 +0000813 char percentify_buf[4]; /* Two digits, '%' and 0. */
814 const int percentify_size = sizeof(percentify_buf);
815 const int percentify_decs = 0;
816
njnd99644d2006-04-07 11:52:55 +0000817 tl_assert(clo_fnname);
818 tl_assert(clo_fnname[0]);
njn25e49d8e72002-09-23 09:36:25 +0000819
njnd99644d2006-04-07 11:52:55 +0000820 if (clo_basic_counts) {
821 VG_(message)(Vg_UserMsg,
822 "Counted %,llu calls to %s()", n_func_calls, clo_fnname);
njn25e49d8e72002-09-23 09:36:25 +0000823
njnd99644d2006-04-07 11:52:55 +0000824 VG_(message)(Vg_UserMsg, "");
825 VG_(message)(Vg_UserMsg, "Jccs:");
826 VG_(message)(Vg_UserMsg, " total: %,llu", n_Jccs);
827 VG_(percentify)((n_Jccs - n_Jccs_untaken), (n_Jccs ? n_Jccs : 1),
828 percentify_decs, percentify_size, percentify_buf);
829 VG_(message)(Vg_UserMsg, " taken: %,llu (%s)",
830 (n_Jccs - n_Jccs_untaken), percentify_buf);
831
832 VG_(message)(Vg_UserMsg, "");
833 VG_(message)(Vg_UserMsg, "Executed:");
sewardj5d1c9012007-02-12 08:42:13 +0000834 VG_(message)(Vg_UserMsg, " SBs entered: %,llu", n_SBs_entered);
835 VG_(message)(Vg_UserMsg, " SBs completed: %,llu", n_SBs_completed);
njnd99644d2006-04-07 11:52:55 +0000836 VG_(message)(Vg_UserMsg, " guest instrs: %,llu", n_guest_instrs);
837 VG_(message)(Vg_UserMsg, " IRStmts: %,llu", n_IRStmts);
838
839 VG_(message)(Vg_UserMsg, "");
840 VG_(message)(Vg_UserMsg, "Ratios:");
sewardj5d1c9012007-02-12 08:42:13 +0000841 tl_assert(n_SBs_entered); // Paranoia time.
842 VG_(message)(Vg_UserMsg, " guest instrs : SB entered = %3u : 10",
843 10 * n_guest_instrs / n_SBs_entered);
844 VG_(message)(Vg_UserMsg, " IRStmts : SB entered = %3u : 10",
845 10 * n_IRStmts / n_SBs_entered);
njnd99644d2006-04-07 11:52:55 +0000846 tl_assert(n_guest_instrs); // Paranoia time.
847 VG_(message)(Vg_UserMsg, " IRStmts : guest instr = %3u : 10",
848 10 * n_IRStmts / n_guest_instrs);
849 }
850
851 if (clo_detailed_counts) {
sewardj7a26f022005-11-01 17:52:34 +0000852 VG_(message)(Vg_UserMsg, "");
853 VG_(message)(Vg_UserMsg, "IR-level counts by type:");
854 print_details();
855 }
njn25e49d8e72002-09-23 09:36:25 +0000856
njnd99644d2006-04-07 11:52:55 +0000857 if (clo_basic_counts) {
858 VG_(message)(Vg_UserMsg, "");
859 VG_(message)(Vg_UserMsg, "Exit code: %d", exitcode);
860 }
njn25e49d8e72002-09-23 09:36:25 +0000861}
862
njn51d827b2005-05-09 01:02:08 +0000863static void lk_pre_clo_init(void)
864{
865 VG_(details_name) ("Lackey");
866 VG_(details_version) (NULL);
867 VG_(details_description) ("an example Valgrind tool");
868 VG_(details_copyright_author)(
sewardj9ebd6e02007-01-08 06:01:59 +0000869 "Copyright (C) 2002-2007, and GNU GPL'd, by Nicholas Nethercote.");
njn51d827b2005-05-09 01:02:08 +0000870 VG_(details_bug_reports_to) (VG_BUGS_TO);
sewardj40823562006-10-17 02:21:17 +0000871 VG_(details_avg_translation_sizeB) ( 200 );
njn51d827b2005-05-09 01:02:08 +0000872
873 VG_(basic_tool_funcs) (lk_post_clo_init,
874 lk_instrument,
875 lk_fini);
sewardj7a26f022005-11-01 17:52:34 +0000876 VG_(needs_command_line_options)(lk_process_cmd_line_option,
877 lk_print_usage,
878 lk_print_debug_usage);
njn51d827b2005-05-09 01:02:08 +0000879}
880
sewardj45f4e7c2005-09-27 19:20:21 +0000881VG_DETERMINE_INTERFACE_VERSION(lk_pre_clo_init)
fitzhardinge98abfc72003-12-16 02:05:15 +0000882
njn25e49d8e72002-09-23 09:36:25 +0000883/*--------------------------------------------------------------------*/
njn25cac76cb2002-09-23 11:21:57 +0000884/*--- end lk_main.c ---*/
njn25e49d8e72002-09-23 09:36:25 +0000885/*--------------------------------------------------------------------*/