/*--------------------------------------------------------------------*/
/*--- Stuff relating to tool data structures.                      ---*/
/*---                                                m_tooliface.c ---*/
/*--------------------------------------------------------------------*/

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

   Copyright (C) 2000-2015 Nicholas Nethercote
      njn@valgrind.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.
*/

#include "pub_core_basics.h"
#include "pub_core_tooliface.h"
#include "pub_core_transtab.h"     /* VG_(ok_to_discard_translations) */

// The core/tool dictionary of functions (initially zeroed, as we want it)
VgToolInterface VG_(tdict);

/*--------------------------------------------------------------------*/
/* Setting basic functions */

void VG_(basic_tool_funcs)(
   void(*post_clo_init)(void),
   IRSB*(*instrument)(VgCallbackClosure*, IRSB*, 
                      const VexGuestLayout*, const VexGuestExtents*,
                      const VexArchInfo*, IRType, IRType),
   void(*fini)(Int)
)
{
   VG_(tdict).tool_post_clo_init = post_clo_init;
   VG_(tdict).tool_instrument    = instrument;
   VG_(tdict).tool_fini          = fini;
}


/*--------------------------------------------------------------------*/
/* Setting details */

/* Init with default values. */
VgDetails VG_(details) = {
   .name                  = NULL,
   .version               = NULL,
   .description           = NULL,
   .copyright_author      = NULL,
   .bug_reports_to        = NULL,
   .avg_translation_sizeB = VG_DEFAULT_TRANS_SIZEB,
};

/* Use macro because they're so repetitive */
#define DETAILS(type, detail)                       \
   extern void VG_(details_##detail)(type detail)   \
   {                                                \
      VG_(details).detail = detail;                 \
   }

DETAILS(const HChar*, name)
DETAILS(const HChar*, version)
DETAILS(const HChar*, description)
DETAILS(const HChar*, copyright_author)
DETAILS(const HChar*, bug_reports_to)
DETAILS(UInt,  avg_translation_sizeB)


/*--------------------------------------------------------------------*/
/* Setting needs */

VgNeeds VG_(needs) = {
   .core_errors          = False,
   .tool_errors          = False,
   .libc_freeres         = False,
   .superblock_discards  = False,
   .command_line_options = False,
   .client_requests      = False,
   .syscall_wrapper      = False,
   .sanity_checks        = False,
   .print_stats          = False,
   .info_location        = False,
   .var_info	         = False,
   .malloc_replacement   = False,
   .xml_output           = False,
   .final_IR_tidy_pass   = False
};

/* static */
Bool VG_(sanity_check_needs)(const HChar** failmsg)
{
   Bool any_new_mem_stack_N, any_new_mem_stack_N_w_ECU;
   Bool any_new_mem_stack_w_conflicting_otags;
   Bool any_die_mem_stack_N;

#define CHECK_NOT(var, value)                                  \
   if ((var)==(value)) {                                       \
      *failmsg = "Tool error: '" #var "' not initialised\n";   \
      return False;                                            \
   }
   
   /* Ones that must be set */
   CHECK_NOT(VG_(details).name,             NULL);
   /* Nb: .version can be NULL */
   CHECK_NOT(VG_(details).description,      NULL);
   CHECK_NOT(VG_(details).copyright_author, NULL);
   CHECK_NOT(VG_(details).bug_reports_to,   NULL);

   /* Check that new_mem_stack is defined if any new_mem_stack_N
      are. */
   any_new_mem_stack_N 
      = VG_(tdict).track_new_mem_stack_4   ||
        VG_(tdict).track_new_mem_stack_8   ||
        VG_(tdict).track_new_mem_stack_12  ||
        VG_(tdict).track_new_mem_stack_16  ||
        VG_(tdict).track_new_mem_stack_32  ||
        VG_(tdict).track_new_mem_stack_112 ||
        VG_(tdict).track_new_mem_stack_128 ||
        VG_(tdict).track_new_mem_stack_144 ||
        VG_(tdict).track_new_mem_stack_160;

   if (any_new_mem_stack_N && ! VG_(tdict).track_new_mem_stack) {
      *failmsg = "Tool error: one of the specialised 'new_mem_stack_N'\n"
                 "   events tracked, but not the generic 'new_mem_stack' one.\n"
                 "   'new_mem_stack' should be defined\n";
      return False;
   }

   /* Check that new_mem_stack_w_ECU is defined if any
      new_mem_stack_N_w_ECU are. */
   any_new_mem_stack_N_w_ECU
      = VG_(tdict).track_new_mem_stack_4_w_ECU   ||
        VG_(tdict).track_new_mem_stack_8_w_ECU   ||
        VG_(tdict).track_new_mem_stack_12_w_ECU  ||
        VG_(tdict).track_new_mem_stack_16_w_ECU  ||
        VG_(tdict).track_new_mem_stack_32_w_ECU  ||
        VG_(tdict).track_new_mem_stack_112_w_ECU ||
        VG_(tdict).track_new_mem_stack_128_w_ECU ||
        VG_(tdict).track_new_mem_stack_144_w_ECU ||
        VG_(tdict).track_new_mem_stack_160_w_ECU;

   if (any_new_mem_stack_N_w_ECU && ! VG_(tdict).track_new_mem_stack_w_ECU) {
      *failmsg = "Tool error: one of the specialised 'new_mem_stack_N_w_ECU'\n"
                 "   events tracked, but not the generic 'new_mem_stack_w_ECU' one.\n"
                 "   'new_mem_stack_w_ECU' should be defined\n";
      return False;
   }

   /* Check that in no cases are both with- and without-otag versions of the
      same new_mem_stack_ function defined. */
   any_new_mem_stack_w_conflicting_otags
      = (VG_(tdict).track_new_mem_stack_4   && VG_(tdict).track_new_mem_stack_4_w_ECU)   ||
        (VG_(tdict).track_new_mem_stack_8   && VG_(tdict).track_new_mem_stack_8_w_ECU)   ||
        (VG_(tdict).track_new_mem_stack_12  && VG_(tdict).track_new_mem_stack_12_w_ECU)  ||
        (VG_(tdict).track_new_mem_stack_16  && VG_(tdict).track_new_mem_stack_16_w_ECU)  ||
        (VG_(tdict).track_new_mem_stack_32  && VG_(tdict).track_new_mem_stack_32_w_ECU)  ||
        (VG_(tdict).track_new_mem_stack_112 && VG_(tdict).track_new_mem_stack_112_w_ECU) ||
        (VG_(tdict).track_new_mem_stack_128 && VG_(tdict).track_new_mem_stack_128_w_ECU) ||
        (VG_(tdict).track_new_mem_stack_144 && VG_(tdict).track_new_mem_stack_144_w_ECU) ||
        (VG_(tdict).track_new_mem_stack_160 && VG_(tdict).track_new_mem_stack_160_w_ECU) ||
        (VG_(tdict).track_new_mem_stack     && VG_(tdict).track_new_mem_stack_w_ECU);

   if (any_new_mem_stack_w_conflicting_otags) {
      *failmsg = "Tool error: tool supplies both a 'new_mem_stack_N' and a\n"
                 "   'new_mem_stack_N_w_ECU' function for some N (or none),\n"
                 "   but you can only have one or the other (not both)\n";
      return False;
   }

   /* Check that die_mem_stack is defined if any die_mem_stack_N
      are. */
   any_die_mem_stack_N
      = VG_(tdict).track_die_mem_stack_4   ||
        VG_(tdict).track_die_mem_stack_8   ||
        VG_(tdict).track_die_mem_stack_12  ||
        VG_(tdict).track_die_mem_stack_16  ||
        VG_(tdict).track_die_mem_stack_32  ||
        VG_(tdict).track_die_mem_stack_112 ||
        VG_(tdict).track_die_mem_stack_128 ||
        VG_(tdict).track_die_mem_stack_144 ||
        VG_(tdict).track_die_mem_stack_160;

    if (any_die_mem_stack_N && ! VG_(tdict).track_die_mem_stack) {
      *failmsg = "Tool error: one of the specialised 'die_mem_stack_N'\n"
                 "   events tracked, but not the generic 'die_mem_stack' one.\n"
                 "   'die_mem_stack' should be defined\n";
      return False;
   }

   return True;

#undef CHECK_NOT
}

/* Use macro because they're so repetitive */
#define NEEDS(need)  \
   extern void VG_(needs_##need)(void) \
   {                                   \
      VG_(needs).need = True;          \
   }

// These ones don't require any tool-supplied functions
NEEDS(libc_freeres)
NEEDS(core_errors)
NEEDS(var_info)

void VG_(needs_superblock_discards)(
   void (*discard)(Addr, VexGuestExtents)
)
{
   VG_(needs).superblock_discards = True;
   VG_(tdict).tool_discard_superblock_info = discard;
}

void VG_(needs_tool_errors)(
   Bool (*eq)         (VgRes, const Error*, const Error*),
   void (*before_pp)  (const Error*),
   void (*pp)         (const Error*),
   Bool show_TIDs,
   UInt (*update)     (const Error*),
   Bool (*recog)      (const HChar*, Supp*),
   Bool (*read_extra) (Int, HChar**, SizeT*, Int*, Supp*),
   Bool (*matches)    (const Error*, const Supp*),
   const HChar* (*name) (const Error*),
   SizeT (*get_xtra_si)(const Error*,/*OUT*/HChar*,Int),
   SizeT (*print_xtra_su)(const Supp*,/*OUT*/HChar*,Int),
   void (*update_xtra_su)(const Error*, const Supp*)
)
{
   VG_(needs).tool_errors = True;
   VG_(tdict).tool_eq_Error                     = eq;
   VG_(tdict).tool_before_pp_Error              = before_pp;
   VG_(tdict).tool_pp_Error                     = pp;
   VG_(tdict).tool_show_ThreadIDs_for_errors    = show_TIDs;
   VG_(tdict).tool_update_extra                 = update;
   VG_(tdict).tool_recognised_suppression       = recog;
   VG_(tdict).tool_read_extra_suppression_info  = read_extra;
   VG_(tdict).tool_error_matches_suppression    = matches;
   VG_(tdict).tool_get_error_name               = name;
   VG_(tdict).tool_get_extra_suppression_info   = get_xtra_si;
   VG_(tdict).tool_print_extra_suppression_use  = print_xtra_su;
   VG_(tdict).tool_update_extra_suppression_use = update_xtra_su;
}

void VG_(needs_command_line_options)(
   Bool (*process)(const HChar*),
   void (*usage)(void),
   void (*debug_usage)(void)
)
{
   VG_(needs).command_line_options = True;
   VG_(tdict).tool_process_cmd_line_option = process;
   VG_(tdict).tool_print_usage             = usage;
   VG_(tdict).tool_print_debug_usage       = debug_usage;
}

/* The tool's function for handling client requests. */
static Bool (*tool_handle_client_request_func)(ThreadId, UWord *, UWord *);

static Bool wrap_tool_handle_client_request(ThreadId tid, UWord *arg1,
                                            UWord *arg2)
{
   Bool ret;
   VG_(ok_to_discard_translations) = True;
   ret = tool_handle_client_request_func(tid, arg1, arg2);
   VG_(ok_to_discard_translations) = False;
   return ret;
}

void VG_(needs_client_requests)(
   Bool (*handle)(ThreadId, UWord*, UWord*)
)
{
   VG_(needs).client_requests = True;
   tool_handle_client_request_func = handle;   /* Stash away */
   /* Register the wrapper function */
   VG_(tdict).tool_handle_client_request = wrap_tool_handle_client_request;
}

void VG_(needs_syscall_wrapper)(
   void(*pre) (ThreadId, UInt, UWord*, UInt),
   void(*post)(ThreadId, UInt, UWord*, UInt, SysRes res)
)
{
   VG_(needs).syscall_wrapper = True;
   VG_(tdict).tool_pre_syscall  = pre;
   VG_(tdict).tool_post_syscall = post;
}

void VG_(needs_sanity_checks)(
   Bool(*cheap)(void),
   Bool(*expen)(void)
)
{
   VG_(needs).sanity_checks = True;
   VG_(tdict).tool_cheap_sanity_check     = cheap;
   VG_(tdict).tool_expensive_sanity_check = expen;
}

void VG_(needs_print_stats) (
   void (*print_stats)(void)
)
{
   VG_(needs).print_stats = True;
   VG_(tdict).tool_print_stats = print_stats;
}

void VG_(needs_info_location) (
   void (*info_location)(Addr)
)
{
   VG_(needs).info_location = True;
   VG_(tdict).tool_info_location = info_location;
}

void VG_(needs_malloc_replacement)(
   void* (*malloc)               ( ThreadId, SizeT ),
   void* (*__builtin_new)        ( ThreadId, SizeT ),
   void* (*__builtin_vec_new)    ( ThreadId, SizeT ),
   void* (*memalign)             ( ThreadId, SizeT, SizeT ),
   void* (*calloc)               ( ThreadId, SizeT, SizeT ),
   void  (*free)                 ( ThreadId, void* ),
   void  (*__builtin_delete)     ( ThreadId, void* ),
   void  (*__builtin_vec_delete) ( ThreadId, void* ),
   void* (*realloc)              ( ThreadId, void*, SizeT ),
   SizeT (*malloc_usable_size)   ( ThreadId, void* ), 
   SizeT client_malloc_redzone_szB
)
{
   VG_(needs).malloc_replacement        = True;
   VG_(tdict).tool_malloc               = malloc;
   VG_(tdict).tool___builtin_new        = __builtin_new;
   VG_(tdict).tool___builtin_vec_new    = __builtin_vec_new;
   VG_(tdict).tool_memalign             = memalign;
   VG_(tdict).tool_calloc               = calloc;
   VG_(tdict).tool_free                 = free;
   VG_(tdict).tool___builtin_delete     = __builtin_delete;
   VG_(tdict).tool___builtin_vec_delete = __builtin_vec_delete;
   VG_(tdict).tool_realloc              = realloc;
   VG_(tdict).tool_malloc_usable_size   = malloc_usable_size;
   VG_(tdict).tool_client_redzone_szB   = client_malloc_redzone_szB;
}

void VG_(needs_xml_output)( void )
{
   VG_(needs).xml_output = True;
}

void VG_(needs_final_IR_tidy_pass)( 
   IRSB*(*final_tidy)(IRSB*)
)
{
   VG_(needs).final_IR_tidy_pass = True;
   VG_(tdict).tool_final_IR_tidy_pass = final_tidy;
}

/*--------------------------------------------------------------------*/
/* Tracked events.  Digit 'n' on DEFn is the REGPARMness. */

#define DEF0(fn, args...) \
void VG_(fn)(void(*f)(args)) { \
   VG_(tdict).fn = f; \
}

#define DEF1(fn, args...) \
void VG_(fn)(VG_REGPARM(1) void(*f)(args)) { \
   VG_(tdict).fn = f; \
}

#define DEF2(fn, args...) \
void VG_(fn)(VG_REGPARM(2) void(*f)(args)) { \
   VG_(tdict).fn = f; \
}

DEF0(track_new_mem_startup,       Addr, SizeT, Bool, Bool, Bool, ULong)
DEF0(track_new_mem_stack_signal,  Addr, SizeT, UInt)
DEF0(track_new_mem_brk,           Addr, SizeT, UInt)
DEF0(track_new_mem_mmap,          Addr, SizeT, Bool, Bool, Bool, ULong)

DEF0(track_copy_mem_remap,        Addr, Addr, SizeT)
DEF0(track_change_mem_mprotect,   Addr, SizeT, Bool, Bool, Bool)
DEF0(track_die_mem_stack_signal,  Addr, SizeT)
DEF0(track_die_mem_brk,           Addr, SizeT)
DEF0(track_die_mem_munmap,        Addr, SizeT)

DEF2(track_new_mem_stack_4_w_ECU,    Addr, UInt)
DEF2(track_new_mem_stack_8_w_ECU,    Addr, UInt)
DEF2(track_new_mem_stack_12_w_ECU,   Addr, UInt)
DEF2(track_new_mem_stack_16_w_ECU,   Addr, UInt)
DEF2(track_new_mem_stack_32_w_ECU,   Addr, UInt)
DEF2(track_new_mem_stack_112_w_ECU,  Addr, UInt)
DEF2(track_new_mem_stack_128_w_ECU,  Addr, UInt)
DEF2(track_new_mem_stack_144_w_ECU,  Addr, UInt)
DEF2(track_new_mem_stack_160_w_ECU,  Addr, UInt)
DEF0(track_new_mem_stack_w_ECU,      Addr, SizeT, UInt)

DEF1(track_new_mem_stack_4,       Addr)
DEF1(track_new_mem_stack_8,       Addr)
DEF1(track_new_mem_stack_12,      Addr)
DEF1(track_new_mem_stack_16,      Addr)
DEF1(track_new_mem_stack_32,      Addr)
DEF1(track_new_mem_stack_112,     Addr)
DEF1(track_new_mem_stack_128,     Addr)
DEF1(track_new_mem_stack_144,     Addr)
DEF1(track_new_mem_stack_160,     Addr)
DEF0(track_new_mem_stack,         Addr, SizeT)

DEF1(track_die_mem_stack_4,       Addr)
DEF1(track_die_mem_stack_8,       Addr)
DEF1(track_die_mem_stack_12,      Addr)
DEF1(track_die_mem_stack_16,      Addr)
DEF1(track_die_mem_stack_32,      Addr)
DEF1(track_die_mem_stack_112,     Addr)
DEF1(track_die_mem_stack_128,     Addr)
DEF1(track_die_mem_stack_144,     Addr)
DEF1(track_die_mem_stack_160,     Addr)
DEF0(track_die_mem_stack,         Addr, SizeT)

DEF0(track_ban_mem_stack,         Addr, SizeT)

DEF0(track_pre_mem_read,          CorePart, ThreadId, const HChar*, Addr, SizeT)
DEF0(track_pre_mem_read_asciiz,   CorePart, ThreadId, const HChar*, Addr)
DEF0(track_pre_mem_write,         CorePart, ThreadId, const HChar*, Addr, SizeT)
DEF0(track_post_mem_write,        CorePart, ThreadId, Addr, SizeT)

DEF0(track_pre_reg_read,          CorePart, ThreadId, const HChar*, PtrdiffT, SizeT)
DEF0(track_post_reg_write,        CorePart, ThreadId,               PtrdiffT, SizeT)

DEF0(track_copy_mem_to_reg,       CorePart, ThreadId, Addr, PtrdiffT, SizeT)
DEF0(track_copy_reg_to_mem,       CorePart, ThreadId, PtrdiffT, Addr, SizeT)

DEF0(track_post_reg_write_clientcall_return, ThreadId, PtrdiffT, SizeT, Addr)

DEF0(track_start_client_code,     ThreadId, ULong)
DEF0(track_stop_client_code,      ThreadId, ULong)

DEF0(track_pre_thread_ll_create,  ThreadId, ThreadId)
DEF0(track_pre_thread_first_insn, ThreadId)
DEF0(track_pre_thread_ll_exit,    ThreadId)

DEF0(track_pre_deliver_signal,    ThreadId, Int sigNo, Bool)
DEF0(track_post_deliver_signal,   ThreadId, Int sigNo)

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