C++程序  |  688行  |  33.29 KB


/*--------------------------------------------------------------------*/
/*--- The core/tool interface.                pub_tool_tooliface.h ---*/
/*--------------------------------------------------------------------*/

/*
   This file is part of Valgrind, a dynamic binary instrumentation
   framework.

   Copyright (C) 2000-2011 Julian Seward
      jseward@acm.org

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307, USA.

   The GNU General Public License is contained in the file COPYING.
*/

#ifndef __PUB_TOOL_TOOLIFACE_H
#define __PUB_TOOL_TOOLIFACE_H

#include "pub_tool_errormgr.h"   // for Error, Supp
#include "libvex.h"              // for all Vex stuff

/* ------------------------------------------------------------------ */
/* The interface version */

/* Initialise tool.   Must do the following:
   - initialise the `details' struct, via the VG_(details_*)() functions
   - register the basic tool functions, via VG_(basic_tool_funcs)().
   May do the following:
   - initialise the `needs' struct to indicate certain requirements, via
     the VG_(needs_*)() functions
   - any other tool-specific initialisation
*/
extern void (*VG_(tl_pre_clo_init)) ( void );

/* Every tool must include this macro somewhere, exactly once.  The
   interface version is no longer relevant, but we kept the same name
   to avoid requiring changes to tools.
*/
#define VG_DETERMINE_INTERFACE_VERSION(pre_clo_init) \
   void (*VG_(tl_pre_clo_init)) ( void ) = pre_clo_init;

/* ------------------------------------------------------------------ */
/* Basic tool functions */

/* The tool_instrument function is passed as a callback to
   LibVEX_Translate.  VgCallbackClosure carries additional info
   which the instrumenter might like to know, but which is opaque to
   Vex.
*/
typedef 
   struct {
      Addr64   nraddr; /* non-redirected guest address */
      Addr64   readdr; /* redirected guest address */
      ThreadId tid;    /* tid requesting translation */
   }
   VgCallbackClosure;

extern void VG_(basic_tool_funcs)(
   // Do any initialisation that can only be done after command line
   // processing.
   void  (*post_clo_init)(void),

   // Instrument a basic block.  Must be a true function, ie. the same
   // input always results in the same output, because basic blocks
   // can be retranslated, unless you're doing something really
   // strange.  Anyway, the arguments.  Mostly they are straightforward
   // except for the distinction between redirected and non-redirected
   // guest code addresses, which is important to understand.
   //
   // VgCallBackClosure* closure contains extra arguments passed
   // from Valgrind to the instrumenter, which Vex doesn't know about.
   // You are free to look inside this structure.
   //
   // * closure->tid is the ThreadId of the thread requesting the
   //   translation.  Not sure why this is here; perhaps callgrind
   //   uses it.
   //
   // * closure->nraddr is the non-redirected guest address of the
   //   start of the translation.  In other words, the translation is
   //   being constructed because the guest program jumped to 
   //   closure->nraddr but no translation of it was found.
   //
   // * closure->readdr is the redirected guest address, from which
   //   the translation was really made.
   //
   //   To clarify this, consider what happens when, in Memcheck, the
   //   first call to malloc() happens.  The guest program will be
   //   trying to jump to malloc() in libc; hence ->nraddr will contain
   //   that address.  However, Memcheck intercepts and replaces
   //   malloc, hence ->readdr will be the address of Memcheck's
   //   malloc replacement in
   //   coregrind/m_replacemalloc/vg_replacemalloc.c.  It follows
   //   that the first IMark in the translation will be labelled as
   //   from ->readdr rather than ->nraddr.
   //
   //   Since most functions are not redirected, the majority of the
   //   time ->nraddr will be the same as ->readdr.  However, you
   //   cannot assume this: if your tool has metadata associated
   //   with code addresses it will get into deep trouble if it does
   //   make this assumption.
   //
   // IRSB* sb_in is the incoming superblock to be instrumented,
   // in flat IR form.
   //
   // VexGuestLayout* layout contains limited info on the layout of
   // the guest state: where the stack pointer and program counter
   // are, and which fields should be regarded as 'always defined'.
   // Memcheck uses this.
   //
   // VexGuestExtents* vge points to a structure which states the
   // precise byte ranges of original code from which this translation
   // was made (there may be up to three different ranges involved).
   // Note again that these are the real addresses from which the code
   // came.  And so it should be the case that closure->readdr is the
   // same as vge->base[0]; indeed Cachegrind contains this assertion.
   //
   // Tools which associate shadow data with code addresses
   // (cachegrind, callgrind) need to be particularly clear about
   // whether they are making the association with redirected or
   // non-redirected code addresses.  Both approaches are viable
   // but you do need to understand what's going on.  See comments
   // below on discard_basic_block_info().
   //
   // IRType gWordTy and IRType hWordTy contain the types of native
   // words on the guest (simulated) and host (real) CPUs.  They will
   // by either Ity_I32 or Ity_I64.  So far we have never built a
   // cross-architecture Valgrind so they should always be the same.
   //
   /* --- Further comments about the IR that your --- */
   /* --- instrumentation function will receive. --- */
   /*
      In the incoming IRSB, the IR for each instruction begins with an
      IRStmt_IMark, which states the address and length of the
      instruction from which this IR came.  This makes it easy for
      profiling-style tools to know precisely which guest code
      addresses are being executed.

      However, before the first IRStmt_IMark, there may be other IR
      statements -- a preamble.  In most cases this preamble is empty,
      but when it isn't, what it contains is some supporting IR that
      the JIT uses to ensure control flow works correctly.  This
      preamble does not modify any architecturally defined guest state
      (registers or memory) and so does not contain anything that will
      be of interest to your tool.

      You should therefore 

      (1) copy any IR preceding the first IMark verbatim to the start
          of the output IRSB.

      (2) not try to instrument it or modify it in any way.

      For the record, stuff that may be in the preamble at
      present is:

      - A self-modifying-code check has been requested for this block.
        The preamble will contain instructions to checksum the block,
        compare against the expected value, and exit the dispatcher
        requesting a discard (hence forcing a retranslation) if they
        don't match.

      - This block is known to be the entry point of a wrapper of some
        function F.  In this case the preamble contains code to write
        the address of the original F (the fn being wrapped) into a
        'hidden' guest state register _NRADDR.  The wrapper can later
        read this register using a client request and make a
        non-redirected call to it using another client-request-like
        magic macro.

      - For platforms that use the AIX ABI (including ppc64-linux), it
        is necessary to have a preamble even for replacement functions
        (not just for wrappers), because it is necessary to switch the
        R2 register (constant-pool pointer) to a different value when
        swizzling the program counter.

        Hence the preamble pushes both R2 and LR (the return address)
        on a small 16-entry stack in the guest state and sets R2 to an
        appropriate value for the wrapper/replacement fn.  LR is then
        set so that the wrapper/replacement fn returns to a magic IR
        stub which restores R2 and LR and returns.

        It's all hugely ugly and fragile.  And it places a stringent
        requirement on m_debuginfo to find out the correct R2 (toc
        pointer) value for the wrapper/replacement function.  So much
        so that m_redir will refuse to honour a redirect-to-me request
        if it cannot find (by asking m_debuginfo) a plausible R2 value
        for 'me'.

        Because this mechanism maintains a shadow stack of (R2,LR)
        pairs in the guest state, it will fail if the
        wrapper/redirection function, or anything it calls, longjumps
        out past the wrapper, because then the magic return stub will
        not be run and so the shadow stack will not be popped.  So it
        will quickly fill up.  Fortunately none of this applies to
        {x86,amd64,ppc32}-linux; on those platforms, wrappers can
        longjump and recurse arbitrarily and everything should work
        fine.

      Note that copying the preamble verbatim may cause complications
      for your instrumenter if you shadow IR temporaries.  See big
      comment in MC_(instrument) in memcheck/mc_translate.c for
      details.
   */
   IRSB*(*instrument)(VgCallbackClosure* closure, 
                      IRSB*              sb_in, 
                      VexGuestLayout*    layout, 
                      VexGuestExtents*   vge, 
                      IRType             gWordTy, 
                      IRType             hWordTy),

   // Finish up, print out any results, etc.  `exitcode' is program's exit
   // code.  The shadow can be found with VG_(get_exit_status_shadow)().
   void  (*fini)(Int)
);

/* ------------------------------------------------------------------ */
/* Details */

/* Default value for avg_translations_sizeB (in bytes), indicating typical
   code expansion of about 6:1. */
#define VG_DEFAULT_TRANS_SIZEB   172

/* Information used in the startup message.  `name' also determines the
   string used for identifying suppressions in a suppression file as
   belonging to this tool.  `version' can be NULL, in which case (not
   surprisingly) no version info is printed; this mechanism is designed for
   tools distributed with Valgrind that share a version number with
   Valgrind.  Other tools not distributed as part of Valgrind should
   probably have their own version number.  */
extern void VG_(details_name)                  ( Char* name );
extern void VG_(details_version)               ( Char* version );
extern void VG_(details_description)           ( Char* description );
extern void VG_(details_copyright_author)      ( Char* copyright_author );

/* Average size of a translation, in bytes, so that the translation
   storage machinery can allocate memory appropriately.  Not critical,
   setting is optional. */
extern void VG_(details_avg_translation_sizeB) ( UInt size );

/* String printed if an `tl_assert' assertion fails or VG_(tool_panic)
   is called.  Should probably be an email address. */
extern void VG_(details_bug_reports_to)   ( Char* bug_reports_to );

/* ------------------------------------------------------------------ */
/* Needs */

/* Should __libc_freeres() be run?  Bugs in it can crash the tool. */
extern void VG_(needs_libc_freeres) ( void );

/* Want to have errors detected by Valgrind's core reported?  Includes:
   - pthread API errors (many;  eg. unlocking a non-locked mutex) 
     [currently disabled]
   - invalid file descriptors to syscalls like read() and write()
   - bad signal numbers passed to sigaction()
   - attempt to install signal handler for SIGKILL or SIGSTOP */
extern void VG_(needs_core_errors) ( void );

/* Booleans that indicate extra operations are defined;  if these are True,
   the corresponding template functions (given below) must be defined.  A
   lot like being a member of a type class. */

/* Want to report errors from tool?  This implies use of suppressions, too. */
extern void VG_(needs_tool_errors) (
   // Identify if two errors are equal, or close enough.  This function is
   // only called if e1 and e2 will have the same error kind.  `res' indicates
   // how close is "close enough".  `res' should be passed on as necessary,
   // eg. if the Error's `extra' part contains an ExeContext, `res' should be
   // passed to VG_(eq_ExeContext)() if the ExeContexts are considered.  Other
   // than that, probably don't worry about it unless you have lots of very
   // similar errors occurring.
   Bool (*eq_Error)(VgRes res, Error* e1, Error* e2),

   // We give tools a chance to have a look at errors
   // just before they are printed.  That is, before_pp_Error is 
   // called just before pp_Error itself.  This gives the tool a
   // chance to look at the just-about-to-be-printed error, so as to 
   // emit any arbitrary output if wants to, before the error itself
   // is printed.  This functionality was added to allow Helgrind to
   // print thread-announcement messages immediately before the 
   // errors that refer to them.
   void (*before_pp_Error)(Error* err),

   // Print error context.
   void (*pp_Error)(Error* err),

   // Should the core indicate which ThreadId each error comes from?
   Bool show_ThreadIDs_for_errors,

   // Should fill in any details that could be postponed until after the
   // decision whether to ignore the error (ie. details not affecting the
   // result of VG_(tdict).tool_eq_Error()).  This saves time when errors
   // are ignored.
   // Yuk.
   // Return value: must be the size of the `extra' part in bytes -- used by
   // the core to make a copy.
   UInt (*update_extra)(Error* err),

   // Return value indicates recognition.  If recognised, must set skind using
   // VG_(set_supp_kind)().
   Bool (*recognised_suppression)(Char* name, Supp* su),

   // Read any extra info for this suppression kind.  Most likely for filling
   // in the `extra' and `string' parts (with VG_(set_supp_{extra, string})())
   // of a suppression if necessary.  Should return False if a syntax error
   // occurred, True otherwise.  bufpp and nBufp are the same as for
   // VG_(get_line).
   Bool (*read_extra_suppression_info)(Int fd, Char** bufpp, SizeT* nBufp,
                                       Supp* su),

   // This should just check the kinds match and maybe some stuff in the
   // `string' and `extra' field if appropriate (using VG_(get_supp_*)() to
   // get the relevant suppression parts).
   Bool (*error_matches_suppression)(Error* err, Supp* su),

   // This should return the suppression name, for --gen-suppressions, or NULL
   // if that error type cannot be suppressed.  This is the inverse of
   // VG_(tdict).tool_recognised_suppression().
   Char* (*get_error_name)(Error* err),

   // This should print into buf[0..nBuf-1] any extra info for the
   // error, for --gen-suppressions, but not including any leading
   // spaces nor a trailing newline.  When called, buf[0 .. nBuf-1]
   // will be zero filled, and it is expected and checked that the
   // last element is still zero after the call.  In other words the
   // tool may not overrun the buffer, and this is checked for.  If
   // there is any info printed in the buffer, return True, otherwise
   // do nothing, and return False.  This function is the inverse of
   // VG_(tdict).tool_read_extra_suppression_info().
   Bool (*print_extra_suppression_info)(Error* err,
                                        /*OUT*/Char* buf, Int nBuf)
);

/* Is information kept by the tool about specific instructions or
   translations?  (Eg. for cachegrind there are cost-centres for every
   instruction, stored in a per-translation fashion.)  If so, the info
   may have to be discarded when translations are unloaded (eg. due to
   .so unloading, or otherwise at the discretion of m_transtab, eg
   when the table becomes too full) to avoid stale information being
   reused for new translations. */
extern void VG_(needs_superblock_discards) (
   // Discard any information that pertains to specific translations
   // or instructions within the address range given.  There are two
   // possible approaches.
   // - If info is being stored at a per-translation level, use orig_addr
   //   to identify which translation is being discarded.  Each translation
   //   will be discarded exactly once.
   //   This orig_addr will match the closure->nraddr which was passed to
   //   to instrument() (see extensive comments above) when this 
   //   translation was made.  Note that orig_addr won't necessarily be 
   //   the same as the first address in "extents".
   // - If info is being stored at a per-instruction level, you can get
   //   the address range(s) being discarded by stepping through "extents".
   //   Note that any single instruction may belong to more than one
   //   translation, and so could be covered by the "extents" of more than
   //   one call to this function.
   // Doing it the first way (as eg. Cachegrind does) is probably easier.
   void (*discard_superblock_info)(Addr64 orig_addr, VexGuestExtents extents)
);

/* Tool defines its own command line options? */
extern void VG_(needs_command_line_options) (
   // Return True if option was recognised, False if it wasn't (but also see
   // below).  Presumably sets some state to record the option as well.  
   //
   // Nb: tools can assume that the argv will never disappear.  So they can,
   // for example, store a pointer to a string within an option, rather than
   // having to make a copy.
   //
   // Options (and combinations of options) should be checked in this function
   // if possible rather than in post_clo_init(), and if they are bad then
   // VG_(fmsg_bad_option)() should be called.  This ensures that the
   // messaging is consistent with command line option errors from the core.
   Bool (*process_cmd_line_option)(Char* argv),

   // Print out command line usage for options for normal tool operation.
   void (*print_usage)(void),

   // Print out command line usage for options for debugging the tool.
   void (*print_debug_usage)(void)
);

/* Tool defines its own client requests? */
extern void VG_(needs_client_requests) (
   // If using client requests, the number of the first request should be equal
   // to VG_USERREQ_TOOL_BASE('X', 'Y'), where 'X' and 'Y' form a suitable two
   // character identification for the string.  The second and subsequent
   // requests should follow.
   //
   // This function should use the VG_IS_TOOL_USERREQ macro (in
   // include/valgrind.h) to first check if it's a request for this tool.  Then
   // should handle it if it's recognised (and return True), or return False if
   // not recognised.  arg_block[0] holds the request number, any further args
   // from the request are in arg_block[1..].  'ret' is for the return value...
   // it should probably be filled, if only with 0.
   Bool (*handle_client_request)(ThreadId tid, UWord* arg_block, UWord* ret)
);

/* Tool does stuff before and/or after system calls? */
// Nb: If either of the pre_ functions malloc() something to return, the
// corresponding post_ function had better free() it!
// Also, the args are the 'original args' -- that is, it may be
// that the syscall pre-wrapper will modify the args before the
// syscall happens.  So these args are the original, un-modified
// args.  Finally, nArgs merely indicates the length of args[..],
// it does not indicate how many of those values are actually
// relevant to the syscall.  args[0 .. nArgs-1] is guaranteed
// to be defined and to contain all the args for this syscall,
// possibly including some trailing zeroes.
extern void VG_(needs_syscall_wrapper) (
               void (* pre_syscall)(ThreadId tid, UInt syscallno,
                                    UWord* args, UInt nArgs),
               void (*post_syscall)(ThreadId tid, UInt syscallno,
                                    UWord* args, UInt nArgs, SysRes res)
);

/* Are tool-state sanity checks performed? */
// Can be useful for ensuring a tool's correctness.  cheap_sanity_check()
// is called very frequently;  expensive_sanity_check() is called less
// frequently and can be more involved.
extern void VG_(needs_sanity_checks) (
   Bool(*cheap_sanity_check)(void),
   Bool(*expensive_sanity_check)(void)
);

/* Do we need to see variable type and location information? */
extern void VG_(needs_var_info) ( void );

/* Does the tool replace malloc() and friends with its own versions?
   This has to be combined with the use of a vgpreload_<tool>.so module
   or it won't work.  See massif/Makefile.am for how to build it. */
// The 'p' prefix avoids GCC complaints about overshadowing global names.
extern void VG_(needs_malloc_replacement)(
   void* (*pmalloc)               ( ThreadId tid, SizeT n ),
   void* (*p__builtin_new)        ( ThreadId tid, SizeT n ),
   void* (*p__builtin_vec_new)    ( ThreadId tid, SizeT n ),
   void* (*pmemalign)             ( ThreadId tid, SizeT align, SizeT n ),
   void* (*pcalloc)               ( ThreadId tid, SizeT nmemb, SizeT size1 ),
   void  (*pfree)                 ( ThreadId tid, void* p ),
   void  (*p__builtin_delete)     ( ThreadId tid, void* p ),
   void  (*p__builtin_vec_delete) ( ThreadId tid, void* p ),
   void* (*prealloc)              ( ThreadId tid, void* p, SizeT new_size ),
   SizeT (*pmalloc_usable_size)   ( ThreadId tid, void* p), 
   SizeT client_malloc_redzone_szB
);

/* Can the tool do XML output?  This is a slight misnomer, because the tool
 * is not requesting the core to do anything, rather saying "I can handle
 * it". */
extern void VG_(needs_xml_output) ( void );

/* Does the tool want to have one final pass over the IR after tree
   building but before instruction selection?  If so specify the
   function here. */
extern void VG_(needs_final_IR_tidy_pass) ( IRSB*(*final_tidy)(IRSB*) );


/* ------------------------------------------------------------------ */
/* Core events to track */

/* Part of the core from which this call was made.  Useful for determining
   what kind of error message should be emitted. */
typedef
   enum { Vg_CoreStartup=1, Vg_CoreSignal, Vg_CoreSysCall,
          // This is for platforms where syscall args are passed on the
          // stack; although pre_mem_read is the callback that will be
          // called, such an arg should be treated (with respect to
          // presenting information to the user) as if it was passed in a
          // register, ie. like pre_reg_read.
          Vg_CoreSysCallArgInMem,  
          Vg_CoreTranslate, Vg_CoreClientReq
   } CorePart;

/* Events happening in core to track.  To be notified, pass a callback
   function to the appropriate function.  To ignore an event, don't do
   anything (the default is for events to be ignored).

   Note that most events aren't passed a ThreadId.  If the event is one called
   from generated code (eg. new_mem_stack_*), you can use
   VG_(get_running_tid)() to find it.  Otherwise, it has to be passed in,
   as in pre_mem_read, and so the event signature will require changing.

   Memory events (Nb: to track heap allocation/freeing, a tool must replace
   malloc() et al.  See above how to do this.)

   These ones occur at startup, upon some signals, and upon some syscalls.

   For new_mem_brk and new_mem_stack_signal, the supplied ThreadId
   indicates the thread for whom the new memory is being allocated.

   For new_mem_startup and new_mem_mmap, the di_handle argument is a
   handle which can be used to retrieve debug info associated with the
   mapping or allocation (because it is of a file that Valgrind has
   decided to read debug info from).  If the value is zero, there is
   no associated debug info.  If the value exceeds zero, it can be
   supplied as an argument to selected queries in m_debuginfo.
*/
void VG_(track_new_mem_startup)     (void(*f)(Addr a, SizeT len,
                                              Bool rr, Bool ww, Bool xx,
                                              ULong di_handle));
void VG_(track_new_mem_stack_signal)(void(*f)(Addr a, SizeT len, ThreadId tid));
void VG_(track_new_mem_brk)         (void(*f)(Addr a, SizeT len, ThreadId tid));
void VG_(track_new_mem_mmap)        (void(*f)(Addr a, SizeT len,
                                              Bool rr, Bool ww, Bool xx,
                                              ULong di_handle));

void VG_(track_copy_mem_remap)      (void(*f)(Addr from, Addr to, SizeT len));
void VG_(track_change_mem_mprotect) (void(*f)(Addr a, SizeT len,
                                              Bool rr, Bool ww, Bool xx));
void VG_(track_die_mem_stack_signal)(void(*f)(Addr a, SizeT len));
void VG_(track_die_mem_brk)         (void(*f)(Addr a, SizeT len));
void VG_(track_die_mem_munmap)      (void(*f)(Addr a, SizeT len));

/* These ones are called when SP changes.  A tool could track these itself
   (except for ban_mem_stack) but it's much easier to use the core's help.

   The specialised ones are called in preference to the general one, if they
   are defined.  These functions are called a lot if they are used, so
   specialising can optimise things significantly.  If any of the
   specialised cases are defined, the general case must be defined too.

   Nb: all the specialised ones must use the VG_REGPARM(n) attribute.

   For the _new functions, a tool may specify with with-ECU
   (ExeContext Unique) or without-ECU version for each size, but not
   both.  If the with-ECU version is supplied, then the core will
   arrange to pass, as the ecu argument, a 32-bit int which uniquely
   identifies the instruction moving the stack pointer down.  This
   32-bit value is as obtained from VG_(get_ECU_from_ExeContext).
   VG_(get_ExeContext_from_ECU) can then be used to retrieve the
   associated depth-1 ExeContext for the location.  All this
   complexity is provided to support origin tracking in Memcheck.
*/
void VG_(track_new_mem_stack_4_w_ECU)  (VG_REGPARM(2) void(*f)(Addr new_ESP, UInt ecu));
void VG_(track_new_mem_stack_8_w_ECU)  (VG_REGPARM(2) void(*f)(Addr new_ESP, UInt ecu));
void VG_(track_new_mem_stack_12_w_ECU) (VG_REGPARM(2) void(*f)(Addr new_ESP, UInt ecu));
void VG_(track_new_mem_stack_16_w_ECU) (VG_REGPARM(2) void(*f)(Addr new_ESP, UInt ecu));
void VG_(track_new_mem_stack_32_w_ECU) (VG_REGPARM(2) void(*f)(Addr new_ESP, UInt ecu));
void VG_(track_new_mem_stack_112_w_ECU)(VG_REGPARM(2) void(*f)(Addr new_ESP, UInt ecu));
void VG_(track_new_mem_stack_128_w_ECU)(VG_REGPARM(2) void(*f)(Addr new_ESP, UInt ecu));
void VG_(track_new_mem_stack_144_w_ECU)(VG_REGPARM(2) void(*f)(Addr new_ESP, UInt ecu));
void VG_(track_new_mem_stack_160_w_ECU)(VG_REGPARM(2) void(*f)(Addr new_ESP, UInt ecu));
void VG_(track_new_mem_stack_w_ECU)                  (void(*f)(Addr a, SizeT len,
                                                                       UInt ecu));

void VG_(track_new_mem_stack_4)  (VG_REGPARM(1) void(*f)(Addr new_ESP));
void VG_(track_new_mem_stack_8)  (VG_REGPARM(1) void(*f)(Addr new_ESP));
void VG_(track_new_mem_stack_12) (VG_REGPARM(1) void(*f)(Addr new_ESP));
void VG_(track_new_mem_stack_16) (VG_REGPARM(1) void(*f)(Addr new_ESP));
void VG_(track_new_mem_stack_32) (VG_REGPARM(1) void(*f)(Addr new_ESP));
void VG_(track_new_mem_stack_112)(VG_REGPARM(1) void(*f)(Addr new_ESP));
void VG_(track_new_mem_stack_128)(VG_REGPARM(1) void(*f)(Addr new_ESP));
void VG_(track_new_mem_stack_144)(VG_REGPARM(1) void(*f)(Addr new_ESP));
void VG_(track_new_mem_stack_160)(VG_REGPARM(1) void(*f)(Addr new_ESP));
void VG_(track_new_mem_stack)                  (void(*f)(Addr a, SizeT len));

void VG_(track_die_mem_stack_4)  (VG_REGPARM(1) void(*f)(Addr die_ESP));
void VG_(track_die_mem_stack_8)  (VG_REGPARM(1) void(*f)(Addr die_ESP));
void VG_(track_die_mem_stack_12) (VG_REGPARM(1) void(*f)(Addr die_ESP));
void VG_(track_die_mem_stack_16) (VG_REGPARM(1) void(*f)(Addr die_ESP));
void VG_(track_die_mem_stack_32) (VG_REGPARM(1) void(*f)(Addr die_ESP));
void VG_(track_die_mem_stack_112)(VG_REGPARM(1) void(*f)(Addr die_ESP));
void VG_(track_die_mem_stack_128)(VG_REGPARM(1) void(*f)(Addr die_ESP));
void VG_(track_die_mem_stack_144)(VG_REGPARM(1) void(*f)(Addr die_ESP));
void VG_(track_die_mem_stack_160)(VG_REGPARM(1) void(*f)(Addr die_ESP));
void VG_(track_die_mem_stack)                  (void(*f)(Addr a, SizeT len));

/* Used for redzone at end of thread stacks */
void VG_(track_ban_mem_stack)      (void(*f)(Addr a, SizeT len));

/* These ones occur around syscalls, signal handling, etc */
void VG_(track_pre_mem_read)       (void(*f)(CorePart part, ThreadId tid,
                                             Char* s, Addr a, SizeT size));
void VG_(track_pre_mem_read_asciiz)(void(*f)(CorePart part, ThreadId tid,
                                             Char* s, Addr a));
void VG_(track_pre_mem_write)      (void(*f)(CorePart part, ThreadId tid,
                                             Char* s, Addr a, SizeT size));
void VG_(track_post_mem_write)     (void(*f)(CorePart part, ThreadId tid,
                                             Addr a, SizeT size));

/* Register events.  Use VG_(set_shadow_state_area)() to set the shadow regs
   for these events.  */
void VG_(track_pre_reg_read)  (void(*f)(CorePart part, ThreadId tid,
                                        Char* s, PtrdiffT guest_state_offset,
                                        SizeT size));
void VG_(track_post_reg_write)(void(*f)(CorePart part, ThreadId tid,
                                        PtrdiffT guest_state_offset,
                                        SizeT size));

/* This one is called for malloc() et al if they are replaced by a tool. */
void VG_(track_post_reg_write_clientcall_return)(
      void(*f)(ThreadId tid, PtrdiffT guest_state_offset, SizeT size, Addr f));


/* Scheduler events (not exhaustive) */

/* Called when 'tid' starts or stops running client code blocks.
   Gives the total dispatched block count at that event.  Note, this
   is not the same as 'tid' holding the BigLock (the lock that ensures
   that only one thread runs at a time): a thread can hold the lock
   for other purposes (making translations, etc) yet not be running
   client blocks.  Obviously though, a thread must hold the lock in
   order to run client code blocks, so the times bracketed by
   'start_client_code'..'stop_client_code' are a subset of the times
   when thread 'tid' holds the cpu lock.
*/
void VG_(track_start_client_code)(
        void(*f)(ThreadId tid, ULong blocks_dispatched)
     );
void VG_(track_stop_client_code)(
        void(*f)(ThreadId tid, ULong blocks_dispatched)
     );


/* Thread events (not exhaustive)

   ll_create: low level thread creation.  Called before the new thread
   has run any instructions (or touched any memory).  In fact, called
   immediately before the new thread has come into existence; the new
   thread can be assumed to exist when notified by this call.

   ll_exit: low level thread exit.  Called after the exiting thread
   has run its last instruction.

   The _ll_ part makes it clear these events are not to do with
   pthread_create or pthread_exit/pthread_join (etc), which are a
   higher level abstraction synthesised by libpthread.  What you can
   be sure of from _ll_create/_ll_exit is the absolute limits of each
   thread's lifetime, and hence be assured that all memory references
   made by the thread fall inside the _ll_create/_ll_exit pair.  This
   is important for tools that need a 100% accurate account of which
   thread is responsible for every memory reference in the process.

   pthread_create/join/exit do not give this property.  Calls/returns
   to/from them happen arbitrarily far away from the relevant
   low-level thread create/quit event.  In general a few hundred
   instructions; hence a few hundred(ish) memory references could get
   misclassified each time.

   pre_thread_first_insn: is called when the thread is all set up and
   ready to go (stack in place, etc) but has not executed its first
   instruction yet.  Gives threading tools a chance to ask questions
   about the thread (eg, what is its initial client stack pointer)
   that are not easily answered at pre_thread_ll_create time.

   For a given thread, the call sequence is:
      ll_create (in the parent's context)
      first_insn (in the child's context)
      ll_exit (in the child's context)
*/
void VG_(track_pre_thread_ll_create) (void(*f)(ThreadId tid, ThreadId child));
void VG_(track_workq_task_start) (void(*f)(ThreadId tid, Addr workitem));
void VG_(track_pre_thread_first_insn)(void(*f)(ThreadId tid));
void VG_(track_pre_thread_ll_exit)   (void(*f)(ThreadId tid));


/* Signal events (not exhaustive)

   ... pre_send_signal, post_send_signal ...

   Called before a signal is delivered;  `alt_stack' indicates if it is
   delivered on an alternative stack.  */
void VG_(track_pre_deliver_signal) (void(*f)(ThreadId tid, Int sigNo,
                                             Bool alt_stack));
/* Called after a signal is delivered.  Nb: unfortunately, if the signal
   handler longjmps, this won't be called.  */
void VG_(track_post_deliver_signal)(void(*f)(ThreadId tid, Int sigNo));

#endif   // __PUB_TOOL_TOOLIFACE_H

/*--------------------------------------------------------------------*/
/*--- end                                                          ---*/
/*--------------------------------------------------------------------*/