summaryrefslogtreecommitdiff
path: root/mmt
diff options
context:
space:
mode:
Diffstat (limited to 'mmt')
-rw-r--r--mmt/mmt_main.c385
1 files changed, 385 insertions, 0 deletions
diff --git a/mmt/mmt_main.c b/mmt/mmt_main.c
new file mode 100644
index 0000000..32bfb7a
--- /dev/null
+++ b/mmt/mmt_main.c
@@ -0,0 +1,385 @@
+/*
+ * vim:sw=3 ts=3 sts=3 noexpandtab
+ */
+
+/*--------------------------------------------------------------------*/
+/*--- mmaptrace: The mmaptracer tool. mmt_main.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ Copyright (C) 2006 Dave Airlie
+
+ 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_libcprint.h"
+#include "pub_tool_libcassert.h"
+#include "pub_tool_tooliface.h"
+#include "pub_tool_debuginfo.h"
+#include "pub_tool_libcbase.h"
+#include "pub_tool_options.h"
+#include "pub_tool_machine.h" // VG_(fnptr_to_fnentry)
+
+#define MAX_ITEMS 100
+
+static struct mmt_memmap {
+ Bool initialized;
+ Addr offset;
+ Addr addr;
+ SizeT len;
+} mmt_gpu_reg[MAX_ITEMS];
+
+static UInt current_item;
+
+static struct mmt_op {
+ Addr offset;
+ SizeT size;
+ UInt value;
+ ULong reps;
+ UInt store;
+} last_op;
+
+
+/* Command line options */
+UInt mmt_clo_offset = (UInt) -1;
+
+static struct mmt_memmap *find_mmap(Addr addr)
+{
+ struct mmt_memmap *ptr = NULL;
+ UInt i;
+
+ for (i = 0; i < MAX_ITEMS; i++) {
+ ptr = &mmt_gpu_reg[i];
+
+ if (!ptr->initialized) {
+ /* since we cannot un-init entries in the array,
+ * we can return once we hit the first uninitialized
+ * item -- there can't be initialized items following it.
+ */
+ return NULL;
+ }
+
+ if ((addr > ptr->addr) && (addr < ptr->addr + ptr->len))
+ return ptr;
+ }
+
+ return NULL;
+}
+
+static void
+print_op(void)
+{
+ int i;
+
+ if (last_op.reps > 5) {
+ VG_(printf)(" ... repeated %lu times total\n", last_op.reps);
+ }
+ else {
+ for (i = 0; i < last_op.reps; i++) {
+ VG_(printf)("%s: %p, %d, %p\n", last_op.store ? "store" : "load",
+ last_op.offset, last_op.size, last_op.value);
+ }
+ }
+}
+
+static VG_REGPARM(2) void
+trace_store(Addr addr, SizeT size, UInt value)
+{
+ struct mmt_memmap *ptr;
+
+ ptr = find_mmap(addr);
+ if (!ptr)
+ return;
+
+ if (last_op.offset != (addr - ptr->addr) || last_op.size != size ||
+ last_op.value != value || !last_op.store) {
+ print_op();
+
+ last_op.offset = addr - ptr->addr;
+ last_op.size = size;
+ last_op.value = value;
+ last_op.store = 1;
+ last_op.reps = 1;
+ }
+ else {
+ last_op.reps++;
+
+ if (last_op.reps % 1000 == 0)
+ VG_(printf)(" ... repeating: %d times\n", last_op.reps);
+ if (last_op.reps == ~0UL)
+ VG_(printf)(" ... overflow!\n");
+ }
+}
+
+static VG_REGPARM(2) void
+trace_load(Addr addr, SizeT size)
+{
+ struct mmt_memmap *ptr;
+ UInt value;
+
+ ptr = find_mmap(addr);
+ if (!ptr)
+ return;
+
+ switch (size) {
+ case 1:
+ value = *(UChar*) addr;
+ break;
+ case 2:
+ value = *(UShort*) addr;
+ break;
+ case 4:
+ value = *(UInt*) addr;
+ break;
+ case 8:
+ value = *(ULong*) addr;
+ default:
+ return;
+ }
+
+ if (last_op.offset != (addr - ptr->addr) || last_op.size != size ||
+ last_op.value != value || last_op.store) {
+ print_op();
+
+ last_op.offset = addr - ptr->addr;
+ last_op.size = size;
+ last_op.value = value;
+ last_op.store = 0;
+ last_op.reps = 1;
+ }
+ else {
+ last_op.reps++;
+
+ if (last_op.reps % 1000 == 0)
+ VG_(printf)(" ... repeating: %d times\n", last_op.reps);
+ if (last_op.reps == ~0UL)
+ VG_(printf)(" ... overflow!\n");
+ }
+}
+
+static void
+handle_load(IRSB* bb, IRExpr* addr, Int size)
+{
+ IRExpr** argv = mkIRExprVec_2(addr, mkIRExpr_HWord(size));
+ IRDirty* di = unsafeIRDirty_0_N(2,
+ "trace_load",
+ VG_(fnptr_to_fnentry)(trace_load),
+ argv);
+ addStmtToIRSB(bb, IRStmt_Dirty(di));
+}
+
+static void
+handle_store(IRSB*bb, IRExpr* addr, Int size, IRExpr* data)
+{
+ IRExpr** argv = mkIRExprVec_3(addr, mkIRExpr_HWord(size), data);
+ IRDirty* di = unsafeIRDirty_0_N(2,
+ "trace_store",
+ VG_(fnptr_to_fnentry)(trace_store),
+ argv);
+ addStmtToIRSB(bb, IRStmt_Dirty(di));
+}
+
+static IRSB*
+mmt_instrument(VgCallbackClosure* closure,
+ IRSB* bbIn,
+ VexGuestLayout* layout,
+ VexGuestExtents* vge,
+ IRType gWordTy, IRType hWordTy)
+{
+ IRSB* bbOut;
+ int i = 0;
+
+ if (gWordTy != hWordTy) {
+ /* We don't currently support this case. */
+ VG_(tool_panic)("host/guest word size mismatch");
+ }
+ if (gWordTy != Ity_I32 && gWordTy != Ity_I64) {
+ VG_(tool_panic)("word size must be 32 or 64 bits");
+ }
+
+ /* Set up BB */
+ bbOut = deepCopyIRSBExceptStmts(bbIn);
+
+ /* Copy verbatim any IR preamble preceding the first IMark */
+ while (i < bbIn->stmts_used && bbIn->stmts[i]->tag != Ist_IMark) {
+ addStmtToIRSB(bbOut, bbIn->stmts[i]);
+ i++;
+ }
+
+ for (; i < bbIn->stmts_used; i++) {
+ IRStmt* st = bbIn->stmts[i];
+ IRExpr* data_expr;
+ IRType arg_ty;
+ IRTemp t = IRTemp_INVALID;
+ IRStmt* cast = NULL;
+
+ if (!st)
+ continue;
+
+ if (st->tag == Ist_Store) {
+ arg_ty = typeOfIRExpr(bbIn->tyenv, st->Ist.Store.data);
+
+ data_expr = st->Ist.Store.data;
+
+ /* convert data_expr into a 64 bit value */
+ switch (arg_ty) {
+ case Ity_I8:
+ if (gWordTy == Ity_I32) {
+ t = newIRTemp(bbOut->tyenv, Ity_I32);
+ cast = IRStmt_WrTmp(t, IRExpr_Unop(Iop_8Uto32, data_expr));
+ }
+ else {
+ t = newIRTemp(bbOut->tyenv, Ity_I64);
+ cast = IRStmt_WrTmp(t, IRExpr_Unop(Iop_8Uto64, data_expr));
+ }
+ break;
+ case Ity_I16:
+ if (gWordTy == Ity_I32) {
+ t = newIRTemp(bbOut->tyenv, Ity_I32);
+ cast = IRStmt_WrTmp(t, IRExpr_Unop(Iop_16Uto32, data_expr));
+ }
+ else {
+ t = newIRTemp(bbOut->tyenv, Ity_I64);
+ cast = IRStmt_WrTmp(t, IRExpr_Unop(Iop_16Uto64, data_expr));
+ }
+ break;
+ case Ity_I32:
+ if (gWordTy == Ity_I64) {
+ t = newIRTemp(bbOut->tyenv, Ity_I64);
+ cast = IRStmt_WrTmp(t, IRExpr_Unop(Iop_32Uto64, data_expr));
+ }
+ break;
+ case Ity_I64:
+ if (gWordTy == Ity_I64) {
+ break;
+ }
+ default:
+ /* since we only support stores up to the guest word size, we
+ * will never have to evaluate the data_expr that this case
+ * generates.
+ */
+ if (gWordTy == Ity_I32) {
+ t = newIRTemp(bbOut->tyenv, Ity_I32);
+ cast = IRStmt_WrTmp(t, IRExpr_Const (IRConst_U32 (0)));
+ }
+ else {
+ t = newIRTemp(bbOut->tyenv, Ity_I64);
+ cast = IRStmt_WrTmp(t, IRExpr_Const (IRConst_U64 (0)));
+ }
+ break;
+ }
+
+ if (cast) {
+ addStmtToIRSB(bbOut, cast);
+ data_expr = IRExpr_RdTmp(t);
+ }
+
+ handle_store(bbOut, st->Ist.Store.addr,
+ sizeofIRType(arg_ty), data_expr);
+ } else if (st->tag == Ist_WrTmp) {
+ data_expr = st->Ist.WrTmp.data;
+
+ if (data_expr->tag == Iex_Load) {
+ handle_load(bbOut, data_expr->Iex.Load.addr,
+ sizeofIRType (data_expr->Iex.Load.ty));
+ }
+ }
+
+ addStmtToIRSB(bbOut, st);
+ }
+
+ return bbOut;
+}
+
+static Bool
+mmt_process_cmd_line_option(Char* arg)
+{
+ /* FIXME: make this argument mandatory */
+ VG_BHEX_CLO(arg, "--offset", mmt_clo_offset, 0, 0xffffffff)
+ else
+ return False;
+
+ VG_(message)(Vg_UserMsg, "set offset to %lx\n", mmt_clo_offset);
+
+ return True;
+}
+
+static void
+mmt_print_usage(void)
+{
+ VG_(printf)(" --offset=<number> (number must be in decimal!)\n");
+}
+
+static void
+mmt_print_debug_usage(void)
+{
+}
+
+static void
+mmt_new_mem_mmap(Addr a, SizeT len, Off64T offset, Bool rr, Bool ww, Bool xx)
+{
+ if (offset != mmt_clo_offset)
+ return;
+
+ mmt_gpu_reg[current_item].offset = offset;
+ mmt_gpu_reg[current_item].addr = a;
+ mmt_gpu_reg[current_item].len = len;
+ mmt_gpu_reg[current_item].initialized = True;
+ current_item++;
+
+ VG_(message)(Vg_UserMsg, "got new mmap for %p, len %d, offset %p\n", a, len, offset);
+}
+
+static void
+mmt_fini(Int exitcode)
+{
+}
+
+static void mmt_post_clo_init(void)
+{
+}
+
+static void
+mmt_pre_clo_init(void)
+{
+ VG_(details_name) ("mmaptrace");
+ VG_(details_version) (NULL);
+ VG_(details_description) ("an MMAP tracer");
+ VG_(details_copyright_author)(
+ "Copyright (C) 2006, and GNU GPL'd, by Dave Airlie.");
+ VG_(details_bug_reports_to) (VG_BUGS_TO);
+
+ VG_(basic_tool_funcs) (mmt_post_clo_init,
+ mmt_instrument,
+ mmt_fini);
+
+ VG_(needs_command_line_options)(mmt_process_cmd_line_option,
+ mmt_print_usage,
+ mmt_print_debug_usage);
+
+ VG_(track_new_mem_mmap) (mmt_new_mem_mmap);
+
+ last_op.offset = 0;
+ last_op.size = 0;
+ last_op.value = 0;
+ last_op.store = 0;
+ last_op.reps = 0;
+}
+
+VG_DETERMINE_INTERFACE_VERSION(mmt_pre_clo_init)