Tweaked Lackey. Main change is that the default instrumentation is now only

added if you specify --basic-counts=yes (which is the default).  So
all of the instrumentation is now controlled by a command-line option (one
of --basic-counts, --detailed-counts or --trace-mem) and so if you turn them
all off it behaves like Nulgrind.  This makes it clearer what's going on and
easier for newbies to modify.



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@5834
This commit is contained in:
Nicholas Nethercote
2006-04-07 11:52:55 +00:00
parent db00007839
commit 32a4a5af5b

View File

@@ -1,12 +1,11 @@
/*--------------------------------------------------------------------*/
/*--- An example Valgrind tool. ---*/
/*--- lk_main.c ---*/
/*--- An example Valgrind tool. lk_main.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Lackey, an example Valgrind tool that does
some simple program measurement.
some simple program measurement and tracing.
Copyright (C) 2002-2005 Nicholas Nethercote
njn@valgrind.org
@@ -31,23 +30,36 @@
// This tool shows how to do some basic instrumentation.
//
// In particular, if you are interested in tracing every load and store a
// program does, use the --trace-mem=yes option. Please note that the
// address trace is good, but not perfect; see Section 3.3.7 of Nicholas
// Nethercote's PhD dissertation "Dynamic Binary Analysis and
// Instrumentation", 2004, for details about the few loads and stores that
// it misses, and other caveats about the accuracy of the address trace.
// There are three kinds of instrumentation it can do. They can be turned
// on/off independently with command line options:
//
// * --basic-counts : do basic counts, eg. number of instructions
// executed, jumps executed, etc.
// * --detailed-counts: do more detailed counts: number of loads, stores
// and ALU operations of different sizes.
// * --trace-mem=yes: trace all (data) memory accesses.
//
// The code for each kind of instrumentation is guarded by a clo_* variable:
// clo_basic_counts, clo_detailed_counts and clo_trace_mem.
//
// If you want to modify any of the instrumentation code, look for the code
// that is guarded by the relevant clo_* variable (eg. clo_trace_mem)
// If you're not interested in the other kinds of instrumentation you can
// remove them. If you want to do more complex modifications, please read
// VEX/pub/libvex_ir.h to understand the intermediate representation.
//
//
// Specific Details about --trace-mem=yes
// --------------------------------------
// The address trace produced by --trace-mem=yes is good, but not perfect;
// see Section 3.3.7 of Nicholas Nethercote's PhD dissertation "Dynamic
// Binary Analysis and Instrumentation", 2004, for details about the few
// loads and stores that it misses, and other caveats about the accuracy of
// the address trace.
//
// [Actually, the traces aren't quite right because instructions that modify
// a memory location are treated like a load followed by a store.]
//
// If you want to modify how the memory traces are printed/gathered, look at
// the code that is controlled by the variable 'lk_clo_trace_mem' and the
// functions 'trace_load()' and 'trace_mem'.. With a bit of effort you
// should be able to see which other bits of code can be removed, if that's
// what you want. If you want to do more complex modifications, please read
// VEX/pub/libvex_ir.h to understand the intermediate representation.
//
// For further inspiration, you should look at cachegrind/cg_main.c which
// handles memory accesses in a more sophisticated way -- it groups them
// together for processing into twos and threes so that fewer C calls are
@@ -62,40 +74,44 @@
#include "pub_tool_options.h"
#include "pub_tool_machine.h" // VG_(fnptr_to_fnentry)
/* The name of the function of which the number of calls is to be
* counted, with default. Override with command line option
* --fnname. */
static Char* lk_clo_fnname = "_dl_runtime_resolve";
/*------------------------------------------------------------*/
/*--- Command line options ---*/
/*------------------------------------------------------------*/
/* If true, show statistics about loads, stores and alu ops. Set
* with command line option --detailed-counts. */
static Bool lk_clo_detailed_counts = False;
/* Command line options controlling instrumentation kinds, as described at
* the top of this file. */
static Bool clo_basic_counts = True;
static Bool clo_detailed_counts = False;
static Bool clo_trace_mem = False;
/* If true, print the trace of loads and stores. Set with --trace-mem. */
static Bool lk_clo_trace_mem = False;
/***********************************************************************
* Implement the needs_command_line_options for Valgrind.
**********************************************************************/
/* The name of the function of which the number of calls (under
* --basic-counts=yes) is to be counted, with default. Override with command
* line option --fnname. */
static Char* clo_fnname = "_dl_runtime_resolve";
static Bool lk_process_cmd_line_option(Char* arg)
{
VG_STR_CLO(arg, "--fnname", lk_clo_fnname)
else VG_BOOL_CLO(arg, "--detailed-counts", lk_clo_detailed_counts)
else VG_BOOL_CLO(arg, "--trace-mem", lk_clo_trace_mem)
VG_STR_CLO(arg, "--fnname", clo_fnname)
else VG_BOOL_CLO(arg, "--basic-counts", clo_basic_counts)
else VG_BOOL_CLO(arg, "--detailed-counts", clo_detailed_counts)
else VG_BOOL_CLO(arg, "--trace-mem", clo_trace_mem)
else
return False;
tl_assert(lk_clo_fnname);
tl_assert(lk_clo_fnname[0]);
tl_assert(clo_fnname);
tl_assert(clo_fnname[0]);
return True;
}
static void lk_print_usage(void)
{
VG_(printf)(
" --fnname=<name> count calls to <name> [_dl_runtime_resolve]\n"
" --basic-counts=no|yes count instructions, jumps, etc. [no]\n"
" --detailed-counts=no|yes count loads, stores and alu ops [no]\n"
" --trace-mem=no|yes trace all loads and stores [no]\n"
" --fnname=<name> count calls to <name> (only used if\n"
" --basic-count=yes) [_dl_runtime_resolve]\n"
);
}
@@ -103,9 +119,9 @@ static void lk_print_debug_usage(void)
{
}
/***********************************************************************
* Data and helpers related to the default operation of Lackey.
**********************************************************************/
/*------------------------------------------------------------*/
/*--- Data and helpers for --basic-counts ---*/
/*------------------------------------------------------------*/
/* Nb: use ULongs because the numbers can get very big */
static ULong n_func_calls = 0;
@@ -151,9 +167,9 @@ static void add_one_Jcc_untaken(void)
n_Jccs_untaken++;
}
/***********************************************************************
* Data and helpers related to --detailed-counts.
**********************************************************************/
/*------------------------------------------------------------*/
/*--- Data and helpers for --detailed-counts ---*/
/*------------------------------------------------------------*/
/* --- Operations --- */
@@ -248,9 +264,9 @@ static void print_details ( void )
}
/***********************************************************************
* Data and helpers related to --trace-mem.
**********************************************************************/
/*------------------------------------------------------------*/
/*--- Data and helpers for --trace-mem ---*/
/*------------------------------------------------------------*/
static VG_REGPARM(2) void trace_load(Addr addr, SizeT size)
{
@@ -262,17 +278,20 @@ static VG_REGPARM(2) void trace_store(Addr addr, SizeT size)
VG_(printf)("store: %p, %d\n", addr, size);
}
/***********************************************************************
* Implement the basic_tool_funcs for Valgrind.
**********************************************************************/
/*------------------------------------------------------------*/
/*--- Basic tool functions ---*/
/*------------------------------------------------------------*/
static void lk_post_clo_init(void)
{
Int op, tyIx;
for (op = 0; op < N_OPS; op++)
for (tyIx = 0; tyIx < N_TYPES; tyIx++)
detailCounts[op][tyIx] = 0;
if (clo_detailed_counts) {
for (op = 0; op < N_OPS; op++)
for (tyIx = 0; tyIx < N_TYPES; tyIx++)
detailCounts[op][tyIx] = 0;
}
}
static
@@ -309,88 +328,86 @@ IRBB* lk_instrument ( VgCallbackClosure* closure,
i++;
}
/* Count this basic block. */
di = unsafeIRDirty_0_N( 0, "add_one_BB_entered",
VG_(fnptr_to_fnentry)( &add_one_BB_entered ),
mkIRExprVec_0() );
addStmtToIRBB( bb, IRStmt_Dirty(di) );
if (clo_basic_counts) {
/* Count this basic block. */
di = unsafeIRDirty_0_N( 0, "add_one_BB_entered",
VG_(fnptr_to_fnentry)( &add_one_BB_entered ),
mkIRExprVec_0() );
addStmtToIRBB( bb, IRStmt_Dirty(di) );
}
for (/*use current i*/; i < bb_in->stmts_used; i++) {
IRStmt* st = bb_in->stmts[i];
if (!st || st->tag == Ist_NoOp) continue;
/* Count one VEX statement. */
di = unsafeIRDirty_0_N( 0, "add_one_IRStmt",
VG_(fnptr_to_fnentry)( &add_one_IRStmt ),
mkIRExprVec_0() );
addStmtToIRBB( bb, IRStmt_Dirty(di) );
if (clo_basic_counts) {
/* Count one VEX statement. */
di = unsafeIRDirty_0_N( 0, "add_one_IRStmt",
VG_(fnptr_to_fnentry)( &add_one_IRStmt ),
mkIRExprVec_0() );
addStmtToIRBB( bb, IRStmt_Dirty(di) );
}
switch (st->tag) {
case Ist_IMark:
/* Count guest instruction. */
di = unsafeIRDirty_0_N( 0, "add_one_guest_instr",
VG_(fnptr_to_fnentry)( &add_one_guest_instr ),
mkIRExprVec_0() );
addStmtToIRBB( bb, IRStmt_Dirty(di) );
/* An unconditional branch to a known destination in the
* guest's instructions can be represented, in the IRBB to
* instrument, by the VEX statements that are the
* translation of that known destination. This feature is
* called 'BB chasing' and can be influenced by command
* line option --vex-guest-chase-thresh.
*
* To get an accurate count of the calls to a specific
* function, taking BB chasing into account, we need to
* check for each guest instruction (Ist_IMark) if it is
* the entry point of a function.
*/
tl_assert(lk_clo_fnname);
tl_assert(lk_clo_fnname[0]);
if (VG_(get_fnname_if_entry)(st->Ist.IMark.addr,
fnname, sizeof(fnname))
&& 0 == VG_(strcmp)(fnname, lk_clo_fnname)) {
di = unsafeIRDirty_0_N(
0, "add_one_func_call",
VG_(fnptr_to_fnentry)( &add_one_func_call ),
mkIRExprVec_0() );
if (clo_basic_counts) {
/* Count guest instruction. */
di = unsafeIRDirty_0_N( 0, "add_one_guest_instr",
VG_(fnptr_to_fnentry)( &add_one_guest_instr ),
mkIRExprVec_0() );
addStmtToIRBB( bb, IRStmt_Dirty(di) );
/* An unconditional branch to a known destination in the
* guest's instructions can be represented, in the IRBB to
* instrument, by the VEX statements that are the
* translation of that known destination. This feature is
* called 'BB chasing' and can be influenced by command
* line option --vex-guest-chase-thresh.
*
* To get an accurate count of the calls to a specific
* function, taking BB chasing into account, we need to
* check for each guest instruction (Ist_IMark) if it is
* the entry point of a function.
*/
tl_assert(clo_fnname);
tl_assert(clo_fnname[0]);
if (VG_(get_fnname_if_entry)(st->Ist.IMark.addr,
fnname, sizeof(fnname))
&& 0 == VG_(strcmp)(fnname, clo_fnname)) {
di = unsafeIRDirty_0_N(
0, "add_one_func_call",
VG_(fnptr_to_fnentry)( &add_one_func_call ),
mkIRExprVec_0() );
addStmtToIRBB( bb, IRStmt_Dirty(di) );
}
}
addStmtToIRBB( bb, st );
break;
case Ist_Exit:
/* Count Jcc */
di = unsafeIRDirty_0_N( 0, "add_one_Jcc",
VG_(fnptr_to_fnentry)( &add_one_Jcc ),
mkIRExprVec_0() );
addStmtToIRBB( bb, IRStmt_Dirty(di) );
if (clo_basic_counts) {
/* Count Jcc */
di = unsafeIRDirty_0_N( 0, "add_one_Jcc",
VG_(fnptr_to_fnentry)( &add_one_Jcc ),
mkIRExprVec_0() );
addStmtToIRBB( bb, IRStmt_Dirty(di) );
}
addStmtToIRBB( bb, st );
/* Count non-taken Jcc */
di = unsafeIRDirty_0_N( 0, "add_one_Jcc_untaken",
VG_(fnptr_to_fnentry)( &add_one_Jcc_untaken ),
mkIRExprVec_0() );
addStmtToIRBB( bb, IRStmt_Dirty(di) );
if (clo_basic_counts) {
/* Count non-taken Jcc */
di = unsafeIRDirty_0_N( 0, "add_one_Jcc_untaken",
VG_(fnptr_to_fnentry)(
&add_one_Jcc_untaken ),
mkIRExprVec_0() );
addStmtToIRBB( bb, IRStmt_Dirty(di) );
}
break;
/* Someone on the users list asked for something like this
* just the other day (Christian Stimming, "Fast profiling in
* valgrind?", 25 Oct). Personally I think it'd be a
* valuable addition.
*
* Not hard to do either: for stores, examine Ist_Store, and
* use typeOfIRExpr(bb->tyenv, st->Ist.Store.data) to get the
* store type. For loads and ALU ops, you only need to look
* at Ist_Tmp cases where the Ist.Tmp.data is either Iex_Load
* or Iex_{Unop,Binop}. All statements you will ever
* encounter will satisfy isFlatIRStmt which essentially
* constrains them to being flat SSA-style.
*/
case Ist_Store:
// Add a call to trace_store() if --trace-mem=yes.
if (lk_clo_trace_mem) {
if (clo_trace_mem) {
addr_expr = st->Ist.Store.addr;
size_expr = mkIRExpr_HWord(
sizeofIRType(
@@ -402,7 +419,7 @@ IRBB* lk_instrument ( VgCallbackClosure* closure,
argv );
addStmtToIRBB( bb, IRStmt_Dirty(di) );
}
if (lk_clo_detailed_counts) {
if (clo_detailed_counts) {
type = typeOfIRExpr(bb->tyenv, st->Ist.Store.data);
tl_assert(type != Ity_INVALID);
instrument_detail( bb, OpStore, type );
@@ -412,7 +429,7 @@ IRBB* lk_instrument ( VgCallbackClosure* closure,
case Ist_Tmp:
// Add a call to trace_load() if --trace-mem=yes.
if (lk_clo_trace_mem) {
if (clo_trace_mem) {
IRExpr* data = st->Ist.Tmp.data;
if (data->tag == Iex_Load) {
addr_expr = data->Iex.Load.addr;
@@ -425,7 +442,7 @@ IRBB* lk_instrument ( VgCallbackClosure* closure,
addStmtToIRBB( bb, IRStmt_Dirty(di) );
}
}
if (lk_clo_detailed_counts) {
if (clo_detailed_counts) {
IRExpr* expr = st->Ist.Tmp.data;
type = typeOfIRExpr(bb->tyenv, expr);
tl_assert(type != Ity_INVALID);
@@ -452,11 +469,13 @@ IRBB* lk_instrument ( VgCallbackClosure* closure,
}
}
/* Count this basic block. */
di = unsafeIRDirty_0_N( 0, "add_one_BB_completed",
VG_(fnptr_to_fnentry)( &add_one_BB_completed ),
mkIRExprVec_0() );
addStmtToIRBB( bb, IRStmt_Dirty(di) );
if (clo_basic_counts) {
/* Count this basic block. */
di = unsafeIRDirty_0_N( 0, "add_one_BB_completed",
VG_(fnptr_to_fnentry)( &add_one_BB_completed ),
mkIRExprVec_0() );
addStmtToIRBB( bb, IRStmt_Dirty(di) );
}
return bb;
}
@@ -467,45 +486,50 @@ static void lk_fini(Int exitcode)
const int percentify_size = sizeof(percentify_buf);
const int percentify_decs = 0;
tl_assert(lk_clo_fnname);
tl_assert(lk_clo_fnname[0]);
VG_(message)(Vg_UserMsg,
"Counted %,llu calls to %s()", n_func_calls, lk_clo_fnname);
tl_assert(clo_fnname);
tl_assert(clo_fnname[0]);
VG_(message)(Vg_UserMsg, "");
VG_(message)(Vg_UserMsg, "Jccs:");
VG_(message)(Vg_UserMsg, " total: %,llu", n_Jccs);
VG_(percentify)((n_Jccs - n_Jccs_untaken), (n_Jccs ? n_Jccs : 1),
percentify_decs, percentify_size, percentify_buf);
VG_(message)(Vg_UserMsg, " taken: %,llu (%s)",
(n_Jccs - n_Jccs_untaken), percentify_buf);
VG_(message)(Vg_UserMsg, "");
VG_(message)(Vg_UserMsg, "Executed:");
VG_(message)(Vg_UserMsg, " BBs entered: %,llu", n_BBs_entered);
VG_(message)(Vg_UserMsg, " BBs completed: %,llu", n_BBs_completed);
VG_(message)(Vg_UserMsg, " guest instrs: %,llu", n_guest_instrs);
VG_(message)(Vg_UserMsg, " IRStmts: %,llu", n_IRStmts);
VG_(message)(Vg_UserMsg, "");
VG_(message)(Vg_UserMsg, "Ratios:");
tl_assert(n_BBs_entered); // Paranoia time.
VG_(message)(Vg_UserMsg, " guest instrs : BB entered = %3u : 10",
10 * n_guest_instrs / n_BBs_entered);
VG_(message)(Vg_UserMsg, " IRStmts : BB entered = %3u : 10",
10 * n_IRStmts / n_BBs_entered);
tl_assert(n_guest_instrs); // Paranoia time.
VG_(message)(Vg_UserMsg, " IRStmts : guest instr = %3u : 10",
10 * n_IRStmts / n_guest_instrs);
if (clo_basic_counts) {
VG_(message)(Vg_UserMsg,
"Counted %,llu calls to %s()", n_func_calls, clo_fnname);
if (lk_clo_detailed_counts) {
VG_(message)(Vg_UserMsg, "");
VG_(message)(Vg_UserMsg, "Jccs:");
VG_(message)(Vg_UserMsg, " total: %,llu", n_Jccs);
VG_(percentify)((n_Jccs - n_Jccs_untaken), (n_Jccs ? n_Jccs : 1),
percentify_decs, percentify_size, percentify_buf);
VG_(message)(Vg_UserMsg, " taken: %,llu (%s)",
(n_Jccs - n_Jccs_untaken), percentify_buf);
VG_(message)(Vg_UserMsg, "");
VG_(message)(Vg_UserMsg, "Executed:");
VG_(message)(Vg_UserMsg, " BBs entered: %,llu", n_BBs_entered);
VG_(message)(Vg_UserMsg, " BBs completed: %,llu", n_BBs_completed);
VG_(message)(Vg_UserMsg, " guest instrs: %,llu", n_guest_instrs);
VG_(message)(Vg_UserMsg, " IRStmts: %,llu", n_IRStmts);
VG_(message)(Vg_UserMsg, "");
VG_(message)(Vg_UserMsg, "Ratios:");
tl_assert(n_BBs_entered); // Paranoia time.
VG_(message)(Vg_UserMsg, " guest instrs : BB entered = %3u : 10",
10 * n_guest_instrs / n_BBs_entered);
VG_(message)(Vg_UserMsg, " IRStmts : BB entered = %3u : 10",
10 * n_IRStmts / n_BBs_entered);
tl_assert(n_guest_instrs); // Paranoia time.
VG_(message)(Vg_UserMsg, " IRStmts : guest instr = %3u : 10",
10 * n_IRStmts / n_guest_instrs);
}
if (clo_detailed_counts) {
VG_(message)(Vg_UserMsg, "");
VG_(message)(Vg_UserMsg, "IR-level counts by type:");
print_details();
}
VG_(message)(Vg_UserMsg, "");
VG_(message)(Vg_UserMsg, "Exit code: %d", exitcode);
if (clo_basic_counts) {
VG_(message)(Vg_UserMsg, "");
VG_(message)(Vg_UserMsg, "Exit code: %d", exitcode);
}
}
static void lk_pre_clo_init(void)