/*
  This file is a benchmark for Valgrind instrumentation performance.

   Copyright (C) 2002-2012 Nicholas Nethercote
      njn@valgrind.org
   Copyright (C) 2013 Google Inc
      opensource@google.com

   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_tool_basics.h"
#include "pub_tool_debuginfo.h"
#include "pub_tool_libcassert.h"
#include "pub_tool_libcbase.h"
#include "pub_tool_libcprint.h"
#include "pub_tool_machine.h"
#include "pub_tool_tooliface.h"

int num_reads = 0,
    num_writes = 0;

static void handle_read_instruction(void) {
  num_reads++;
}

static void handle_write_instruction(void) {
  num_writes++;
}

static void vt_fini(Int exitcode) {
  VG_(printf)("Number of\n   reads: %d\n  writes: %d\n", num_reads, num_writes);
}

static void vt_post_clo_init(void) { }

static void add_hook(IRSB* bbOut, void (*fn)(void)) {
  IRDirty *di = unsafeIRDirty_0_N(0, "user_hook",
                                  VG_(fnptr_to_fnentry)(fn),
                                  mkIRExprVec_0());
  addStmtToIRSB(bbOut, IRStmt_Dirty(di));
}

static void maybe_instrument_statement(IRStmt* st, IRSB* bbIn, IRSB* bbOut,
                                       IRType hWordTy) {
  switch (st->tag) {
    case Ist_NoOp:
    case Ist_AbiHint:
    case Ist_Put:
    case Ist_PutI:
    case Ist_Exit:
    case Ist_IMark:
      /* Can't reference memory. */
      break;

    case Ist_LLSC:
    case Ist_MBE:
      /* TODO: consider? */
      break;

    case Ist_CAS:
      /* TODO: consider? */
      break;

    case Ist_WrTmp: {
      // Load
      IRExpr* data = st->Ist.WrTmp.data;
      if (data->tag == Iex_Load)
        add_hook(bbOut, handle_read_instruction);
      break;
    }

    case Ist_Store: {
      // Store
      add_hook(bbOut, handle_write_instruction);
      break;
    }

    /* TODO: Ist_Dirty? */

    default:
      ppIRStmt(st);
      tl_assert(0);
  }

}

static IRSB* vt_instrument(VgCallbackClosure* closure,
                           IRSB* bbIn,
                           VexGuestLayout* layout,
                           VexGuestExtents* vge,
                           IRType gWordTy, IRType hWordTy) {
#if 0
  return bbIn;
#else
# define BUFFSIZE 1024
  char objname[BUFFSIZE] = "XXX";
  VG_(get_objname)(closure->readdr, objname, BUFFSIZE);

  if (VG_(strstr)(objname, "test.so") == NULL &&
      VG_(strstr)(objname, "fontconfig.so") == NULL)
    return bbIn;

  IRSB* bbOut = deepCopyIRSBExceptStmts(bbIn);
  int i = 0;
  while (i < bbIn->stmts_used) {
    IRStmt* st = bbIn->stmts[i];
    maybe_instrument_statement(st, bbIn, bbOut, hWordTy);
    addStmtToIRSB(bbOut, st);
    i++;
  }

  return bbOut;
#endif
}

static void vt_pre_clo_init(void) {
  VG_(details_name)            ("vgtool");
  VG_(details_version)         (NULL);
  VG_(details_description)     ("a Valgrind benchmark");
  VG_(details_copyright_author)("TODO");
  VG_(details_bug_reports_to)  ((Char*)"nobody@nowhere.com");

  VG_(details_avg_translation_sizeB)( 640 /* TODO */ );

  VG_(basic_tool_funcs)(vt_post_clo_init, vt_instrument, vt_fini);
}

VG_DETERMINE_INTERFACE_VERSION(vt_pre_clo_init)