<commit_before>/* ******************************************************************************
 * Copyright (c) 2016 <NAME>. All rights reserved.
 * ******************************************************************************/

/*
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of VMware, Inc. nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

#include <stddef.h>   // for offsetof
#include <stdint.h>   // for uint*_t
#include <inttypes.h> // for PRIuPTR
#include "dr_api.h"
#include "drmgr.h"
#include "drutil.h"
#include "utils.h"
#include "base64.h"

#ifdef UNIX
# if defined(MACOS) || defined(ANDROID)
#  include <sys/syscall.h>
# else
#  include <syscall.h>
# endif
#endif

typedef struct _mem_ref_t {
    ushort size;
    ushort type;
    app_pc addr;
} mem_ref_t;

#define MAX_NUM_MEM_REFS 4096
#define MEM_BUF_SIZE (sizeof(mem_ref_t) * MAX_NUM_MEM_REFS)

/* thread private log file and counter */
typedef struct {
    byte      *seg_base;
    mem_ref_t *buf_base;
    file_t     log;
    app_pc     stk_base;
    app_pc     stk_ceil;
} per_thread_t;

static client_id_t client_id;

/* Allocated TLS slot offsets */
enum {
    MEMTRACE_TLS_OFFS_BUF_PTR,
    MEMTRACE_TLS_COUNT, /* total number of TLS slots allocated */
};
static reg_id_t tls_seg;
static uint     tls_offs;
static int      tls_idx;

/* The system call number of SYS_write/NtWriteFile */
static int      write_sysnum;
#define TLS_SLOT(tls_base, enum_val) (void **)((byte *)(tls_base)+tls_offs+(enum_val))
#define BUF_PTR(tls_base) *(mem_ref_t **)TLS_SLOT(tls_base, MEMTRACE_TLS_OFFS_BUF_PTR)

#define MINSERT instrlist_meta_preinsert

static uint64_t
dereference_pointer(app_pc pc, ushort size)
{
    switch (size) {
    case 1:  return *(uint8_t *)  pc;
    case 2:  return *(uint16_t *) pc;
    case 4:  return *(uint32_t *) pc;
    case 8:  return *(uint64_t *) pc;
    default: return 0;
    }
}

static void
memtrace(app_pc pc, bool pre_call)
{
    per_thread_t *data;
    mem_ref_t *mem_ref, *buf_ptr;

    /* get stack pointer */
    void *drcontext = dr_get_current_drcontext();
    dr_mcontext_t mcontext = {sizeof(mcontext), DR_MC_CONTROL/*only xsp*/,};
    dr_get_mcontext(drcontext, &mcontext);
    app_pc stk_ptr = (app_pc) mcontext.xsp;

    /* We preinsert on calls, so we have to manually decrement stk_ptr. */
    if (pre_call) stk_ptr -= sizeof(app_pc);

    data    = drmgr_get_tls_field(drcontext, tls_idx);
    buf_ptr = BUF_PTR(data->seg_base);
    mem_ref = (mem_ref_t *) data->buf_base;

    /* get the stack base, or a good estimate */
    if (data->stk_base == 0) {
        size_t sz;
        bool ok = dr_query_memory(stk_ptr, &data->stk_base, &sz, NULL);
        DR_ASSERT(ok);

        /* stack starts at top of memory */
        dr_fprintf(data->log, "stk_base:%"PRIuPTR" stk_ceil:%"PRIuPTR"\n",
                data->stk_base + sz, data->stk_base);
        data->stk_ceil = data->stk_base;
        data->stk_base += sz;
    }

    for (; mem_ref < buf_ptr; mem_ref++) {
        /* filter by whether write occurs on the stack or not */
        if (mem_ref->addr <= data->stk_base && mem_ref->addr >= data->stk_ceil) {
            /* on a call instruction, the written memory is just pc */
            app_pc wmem = pre_call ? pc
                : (app_pc) dereference_pointer(mem_ref->addr, mem_ref->size);
            dr_fprintf(data->log, "addr:%"PRIuPTR
                                  " size:%d"
                                  " sptr:%"PRIuPTR
                                  " type:%s"
                                  " wmem:%"PRIuPTR"\n",
                        mem_ref->addr, mem_ref->size, stk_ptr,
                        decode_opcode_name(mem_ref->type), wmem);
        }
    }
    BUF_PTR(data->seg_base) = data->buf_base;
}

/* clean call to flush the buffer */
static void post_mov(void)      { memtrace(0, false); }
static void pre_call(app_pc pc) { memtrace(pc, true); }

static void
insert_load_buf_ptr(void *drcontext, instrlist_t *ilist, instr_t *where,
                    reg_id_t reg_ptr)
{
    dr_insert_read_raw_tls(drcontext, ilist, where, tls_seg,
                           tls_offs + MEMTRACE_TLS_OFFS_BUF_PTR, reg_ptr);
}

static void
insert_update_buf_ptr(void *drcontext, instrlist_t *ilist, instr_t *where,
                      reg_id_t reg_ptr, int adjust)
{
    MINSERT(ilist, where,
            XINST_CREATE_add(drcontext,
                             opnd_create_reg(reg_ptr),
                             OPND_CREATE_INT16(adjust)));
    dr_insert_write_raw_tls(drcontext, ilist, where, tls_seg,
                            tls_offs + MEMTRACE_TLS_OFFS_BUF_PTR, reg_ptr);
}

static void
insert_save_size(void *drcontext, instrlist_t *ilist, instr_t *where,
                 reg_id_t base, reg_id_t scratch, ushort size)
{
    scratch = reg_resize_to_opsz(scratch, OPSZ_2);
    MINSERT(ilist, where,
            XINST_CREATE_load_int(drcontext,
                                  opnd_create_reg(scratch),
                                  OPND_CREATE_INT16(size)));
    MINSERT(ilist, where,
            XINST_CREATE_store_2bytes(drcontext,
                                      OPND_CREATE_MEM16(base,
                                                        offsetof(mem_ref_t, size)),
                                      opnd_create_reg(scratch)));
}

static void
insert_save_type(void *drcontext, instrlist_t *ilist, instr_t *where,
                 reg_id_t base, reg_id_t scratch, ushort type)
{
    scratch = reg_resize_to_opsz(scratch, OPSZ_2);
    MINSERT(ilist, where,
            XINST_CREATE_load_int(drcontext,
                                  opnd_create_reg(scratch),
                                  OPND_CREATE_INT16(type)));
    MINSERT(ilist, where,
            XINST_CREATE_store_2bytes(drcontext,
                                      OPND_CREATE_MEM16(base,
                                                        offsetof(mem_ref_t, type)),
                                      opnd_create_reg(scratch)));
}

static void
insert_save_addr(void *drcontext, instrlist_t *ilist, instr_t *where,
                 opnd_t ref, reg_id_t reg_ptr, reg_id_t reg_addr)
{
    bool ok;
    /* we use reg_ptr as scratch to get addr */
    ok = drutil_insert_get_mem_addr(drcontext, ilist, where, ref, reg_addr, reg_ptr);
    DR_ASSERT(ok);

    /* write to offset */
    insert_load_buf_ptr(drcontext, ilist, where, reg_ptr);
    MINSERT(ilist, where,
            XINST_CREATE_store(drcontext,
                               OPND_CREATE_MEMPTR(reg_ptr,
                                                  offsetof(mem_ref_t, addr)),
                               opnd_create_reg(reg_addr)));
}

/* insert inline code to add a memory reference info entry into the buffer */
static void
instrument_mem(void *drcontext, instrlist_t *ilist, instr_t *where, opnd_t ref)
{
    reg_id_t reg_ptr = IF_X86_ELSE(DR_REG_XCX, DR_REG_R1);
    reg_id_t reg_tmp = IF_X86_ELSE(DR_REG_XBX, DR_REG_R2);
    ushort slot_ptr  = SPILL_SLOT_2;
    ushort slot_tmp  = SPILL_SLOT_3;
    ushort size = drutil_opnd_mem_size_in_bytes(ref, where);
    ushort type = instr_get_opcode(where);

    /* we need two scratch registers */
    dr_save_reg(drcontext, ilist, where, reg_ptr, slot_ptr);
    dr_save_reg(drcontext, ilist, where, reg_tmp, slot_tmp);

    /* save_addr should be called first as reg_ptr or reg_tmp maybe used in ref */
    insert_save_addr(drcontext, ilist, where, ref, reg_ptr, reg_tmp);
    insert_save_size(drcontext, ilist, where, reg_ptr, reg_tmp, size);
    insert_save_type(drcontext, ilist, where, reg_ptr, reg_tmp, type);
    insert_update_buf_ptr(drcontext, ilist, where, reg_ptr, sizeof(mem_ref_t));

    /* restore scratch registers */
    dr_restore_reg(drcontext, ilist, where, reg_ptr, slot_ptr);
    dr_restore_reg(drcontext, ilist, where, reg_tmp, slot_tmp);
}

/* For each memory reference app instr, we insert inline code to fill the buffer
 * with an instruction entry and memory reference entries.
 */
static dr_emit_flags_t
event_app_instruction(void *drcontext, void *tag, instrlist_t *bb,
                      instr_t *instr, bool for_trace,
                      bool translating, void *user_data)
{
    int i;

    /* TODO: idea! change all call's to push then jmp */
    if (!instr_is_app(instr))
        return DR_EMIT_DEFAULT;
    if (!instr_writes_memory(instr))
        return DR_EMIT_DEFAULT;

    /* insert code to add an entry for each memory write opnd */
    for (i = 0; i < instr_num_dsts(instr); i++) {
        if (opnd_is_memory_reference(instr_get_dst(instr, i)))
            instrument_mem(drcontext, bb, instr, instr_get_dst(instr, i));
    }

    bool should_insert_clean_call =
        /* XXX i#1702: it is ok to skip a few clean calls on predicated instructions,
         * since the buffer will be dumped later by other clean calls.
         */
        IF_X86_ELSE(true, !instr_is_predicated(instr))
        /* FIXME i#1698: there are constraints for code between ldrex/strex pairs,
         * so we minimize the instrumentation in between by skipping the clean call.
         * However, there is still a chance that the instrumentation code may clear the
         * exclusive monitor state.
         */
        IF_ARM(&& !instr_is_exclusive_store(instr));
    if (!should_insert_clean_call)
        return DR_EMIT_DEFAULT;

    /* We dump the writes to stdout. In the case of a call instruction, we pre-insert
     * instrumentation so writes appear in order. Otherwise, we post-insert for ease
     * of getting the written value.
     */
    if (!instr_is_call(instr)) {
        dr_insert_clean_call(drcontext, bb, instr_get_next(instr),
                             (void *) post_mov, false, 0);
    } else {
        /* for call instruction, this is xip to be pushed onto the stack */
        opnd_t pc = IF_X86_ELSE(OPND_CREATE_INTPTR,OPND_CREATE_INT)(
                (app_pc) decode_next_pc(drcontext, (byte *) instr_get_app_pc(instr)));
        dr_insert_clean_call(drcontext, bb, instr, (void *) pre_call, false, 1, pc);
    }

    return DR_EMIT_DEFAULT;
}

/* We transform string loops into regular loops so we can more easily
 * monitor every memory reference they make.
 */
static dr_emit_flags_t
event_bb_app2app(void *drcontext, void *tag, instrlist_t *bb,
                 bool for_trace, bool translating)
{
    if (!drutil_expand_rep_string(drcontext, bb)) {
        DR_ASSERT(false);
        /* in release build, carry on: we'll just miss per-iter refs */
    }
    return DR_EMIT_DEFAULT;
}<commit_msg>More refactoring<commit_after>/* ******************************************************************************
 * Copyright (c) 2016 <NAME>. All rights reserved.
 * ******************************************************************************/

/*
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of VMware, Inc. nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

#include <stddef.h>   // for offsetof
#include <stdint.h>   // for uint*_t
#include <inttypes.h> // for PRIuPTR
#include "dr_api.h"
#include "drmgr.h"
#include "drutil.h"
#include "utils.h"
#include "base64.h"

#ifdef UNIX
# if defined(MACOS) || defined(ANDROID)
#  include <sys/syscall.h>
# else
#  include <syscall.h>
# endif
#endif

typedef struct _mem_ref_t {
    ushort size;
    ushort type;
    app_pc addr;
} mem_ref_t;

#define MAX_NUM_MEM_REFS 4096
#define MEM_BUF_SIZE (sizeof(mem_ref_t) * MAX_NUM_MEM_REFS)

/* thread private log file and counter */
typedef struct {
    byte      *seg_base;
    mem_ref_t *buf_base;
    file_t     log;
    app_pc     stk_base;
    app_pc     stk_ceil;
} per_thread_t;

static client_id_t client_id;

/* Allocated TLS slot offsets */
enum {
    MEMTRACE_TLS_OFFS_BUF_PTR,
    MEMTRACE_TLS_COUNT, /* total number of TLS slots allocated */
};
static reg_id_t tls_seg;
static uint     tls_offs;
static int      tls_idx;

/* The system call number of SYS_write/NtWriteFile */
static int      write_sysnum;
#define TLS_SLOT(tls_base, enum_val) (void **)((byte *)(tls_base)+tls_offs+(enum_val))
#define BUF_PTR(tls_base) *(mem_ref_t **)TLS_SLOT(tls_base, MEMTRACE_TLS_OFFS_BUF_PTR)

#define MINSERT instrlist_meta_preinsert

static uint64_t
dereference_pointer(app_pc pc, ushort size)
{
    switch (size) {
    case 1:  return *(uint8_t *)  pc;
    case 2:  return *(uint16_t *) pc;
    case 4:  return *(uint32_t *) pc;
    case 8:  return *(uint64_t *) pc;
    default: return 0;
    }
}

static void
get_stack_bounds(app_pc *base, app_pc *ceil, app_pc sptr)
{
    size_t sz;
    bool ok = dr_query_memory(sptr, ceil, &sz, NULL);
    DR_ASSERT(ok);
    /* stack starts at top of memory */
    *base -= sz;
}

static void
memtrace(app_pc pc, bool pre_call)
{
    /* get stack pointer */
    void *drcontext = dr_get_current_drcontext();
    dr_mcontext_t mcontext = {sizeof(mcontext), DR_MC_CONTROL/*only xsp*/,};
    dr_get_mcontext(drcontext, &mcontext);
    app_pc stk_ptr = (app_pc) mcontext.xsp;

    /* We preinsert on calls, so we have to manually decrement stk_ptr. */
    if (pre_call) stk_ptr -= sizeof(app_pc);

    per_thread_t *data = drmgr_get_tls_field(drcontext, tls_idx);
    mem_ref_t *buf_ptr = BUF_PTR(data->seg_base),
              *mem_ref = (mem_ref_t *) data->buf_base;

    /* get the stack base, or a good estimate */
    if (data->stk_base == 0) {
        get_stack_bounds(&data->stk_base, &data->stk_ceil, stk_ptr);
        dr_fprintf(data->log, "stk_base:%"PRIuPTR" stk_ceil:%"PRIuPTR"\n",
                data->stk_base, data->stk_ceil);
    }

    for (; mem_ref < buf_ptr; mem_ref++) {
        /* filter by whether write occurs on the stack or not */
        if (mem_ref->addr <= data->stk_base && mem_ref->addr >= data->stk_ceil) {
            /* on a call instruction, the written memory is just pc */
            app_pc wmem = pre_call ? pc
                : (app_pc) dereference_pointer(mem_ref->addr, mem_ref->size);
            dr_fprintf(data->log, "addr:%"PRIuPTR
                                  " size:%d"
                                  " sptr:%"PRIuPTR
                                  " type:%s"
                                  " wmem:%"PRIuPTR"\n",
                        mem_ref->addr, mem_ref->size, stk_ptr,
                        decode_opcode_name(mem_ref->type), wmem);
        }
    }
    BUF_PTR(data->seg_base) = data->buf_base;
}

/* clean call to flush the buffer */
static void post_mov(void)      { memtrace(0, false); }
static void pre_call(app_pc pc) { memtrace(pc, true); }

static void
insert_load_buf_ptr(void *drcontext, instrlist_t *ilist, instr_t *where,
                    reg_id_t reg_ptr)
{
    dr_insert_read_raw_tls(drcontext, ilist, where, tls_seg,
                           tls_offs + MEMTRACE_TLS_OFFS_BUF_PTR, reg_ptr);
}

static void
insert_update_buf_ptr(void *drcontext, instrlist_t *ilist, instr_t *where,
                      reg_id_t reg_ptr, int adjust)
{
    MINSERT(ilist, where,
            XINST_CREATE_add(drcontext,
                             opnd_create_reg(reg_ptr),
                             OPND_CREATE_INT16(adjust)));
    dr_insert_write_raw_tls(drcontext, ilist, where, tls_seg,
                            tls_offs + MEMTRACE_TLS_OFFS_BUF_PTR, reg_ptr);
}

static void
insert_save_size(void *drcontext, instrlist_t *ilist, instr_t *where,
                 reg_id_t base, reg_id_t scratch, ushort size)
{
    scratch = reg_resize_to_opsz(scratch, OPSZ_2);
    MINSERT(ilist, where,
            XINST_CREATE_load_int(drcontext,
                                  opnd_create_reg(scratch),
                                  OPND_CREATE_INT16(size)));
    MINSERT(ilist, where,
            XINST_CREATE_store_2bytes(drcontext,
                                      OPND_CREATE_MEM16(base,
                                                        offsetof(mem_ref_t, size)),
                                      opnd_create_reg(scratch)));
}

static void
insert_save_type(void *drcontext, instrlist_t *ilist, instr_t *where,
                 reg_id_t base, reg_id_t scratch, ushort type)
{
    scratch = reg_resize_to_opsz(scratch, OPSZ_2);
    MINSERT(ilist, where,
            XINST_CREATE_load_int(drcontext,
                                  opnd_create_reg(scratch),
                                  OPND_CREATE_INT16(type)));
    MINSERT(ilist, where,
            XINST_CREATE_store_2bytes(drcontext,
                                      OPND_CREATE_MEM16(base,
                                                        offsetof(mem_ref_t, type)),
                                      opnd_create_reg(scratch)));
}

static void
insert_save_addr(void *drcontext, instrlist_t *ilist, instr_t *where,
                 opnd_t ref, reg_id_t reg_ptr, reg_id_t reg_addr)
{
    bool ok;
    /* we use reg_ptr as scratch to get addr */
    ok = drutil_insert_get_mem_addr(drcontext, ilist, where, ref, reg_addr, reg_ptr);
    DR_ASSERT(ok);

    /* write to offset */
    insert_load_buf_ptr(drcontext, ilist, where, reg_ptr);
    MINSERT(ilist, where,
            XINST_CREATE_store(drcontext,
                               OPND_CREATE_MEMPTR(reg_ptr,
                                                  offsetof(mem_ref_t, addr)),
                               opnd_create_reg(reg_addr)));
}

/* insert inline code to add a memory reference info entry into the buffer */
static void
instrument_mem(void *drcontext, instrlist_t *ilist, instr_t *where, opnd_t ref)
{
    reg_id_t reg_ptr = IF_X86_ELSE(DR_REG_XCX, DR_REG_R1);
    reg_id_t reg_tmp = IF_X86_ELSE(DR_REG_XBX, DR_REG_R2);
    ushort slot_ptr  = SPILL_SLOT_2;
    ushort slot_tmp  = SPILL_SLOT_3;
    ushort size = drutil_opnd_mem_size_in_bytes(ref, where);
    ushort type = instr_get_opcode(where);

    /* we need two scratch registers */
    dr_save_reg(drcontext, ilist, where, reg_ptr, slot_ptr);
    dr_save_reg(drcontext, ilist, where, reg_tmp, slot_tmp);

    /* save_addr should be called first as reg_ptr or reg_tmp maybe used in ref */
    insert_save_addr(drcontext, ilist, where, ref, reg_ptr, reg_tmp);
    insert_save_size(drcontext, ilist, where, reg_ptr, reg_tmp, size);
    insert_save_type(drcontext, ilist, where, reg_ptr, reg_tmp, type);
    insert_update_buf_ptr(drcontext, ilist, where, reg_ptr, sizeof(mem_ref_t));

    /* restore scratch registers */
    dr_restore_reg(drcontext, ilist, where, reg_ptr, slot_ptr);
    dr_restore_reg(drcontext, ilist, where, reg_tmp, slot_tmp);
}

/* For each memory reference app instr, we insert inline code to fill the buffer
 * with an instruction entry and memory reference entries.
 */
static dr_emit_flags_t
event_app_instruction(void *drcontext, void *tag, instrlist_t *bb,
                      instr_t *instr, bool for_trace,
                      bool translating, void *user_data)
{
    int i;

    /* TODO: idea! change all call's to push then jmp */
    if (!instr_is_app(instr))
        return DR_EMIT_DEFAULT;
    if (!instr_writes_memory(instr))
        return DR_EMIT_DEFAULT;

    /* insert code to add an entry for each memory write opnd */
    for (i = 0; i < instr_num_dsts(instr); i++) {
        if (opnd_is_memory_reference(instr_get_dst(instr, i)))
            instrument_mem(drcontext, bb, instr, instr_get_dst(instr, i));
    }

    bool should_insert_clean_call =
        /* XXX i#1702: it is ok to skip a few clean calls on predicated instructions,
         * since the buffer will be dumped later by other clean calls.
         */
        IF_X86_ELSE(true, !instr_is_predicated(instr))
        /* FIXME i#1698: there are constraints for code between ldrex/strex pairs,
         * so we minimize the instrumentation in between by skipping the clean call.
         * However, there is still a chance that the instrumentation code may clear the
         * exclusive monitor state.
         */
        IF_ARM(&& !instr_is_exclusive_store(instr));
    if (!should_insert_clean_call)
        return DR_EMIT_DEFAULT;

    /* We dump the writes to stdout. In the case of a call instruction, we pre-insert
     * instrumentation so writes appear in order. Otherwise, we post-insert for ease
     * of getting the written value.
     */
    if (!instr_is_call(instr)) {
        instr_t *next = instr_get_next(instr);
        dr_insert_clean_call(drcontext, bb, next, (void *) post_mov, false, 0);
    } else {
        /* for call instruction, this is xip to be pushed onto the stack */
        byte * curr_pc = instr_get_app_pc(instr);
        app_pc next_pc = decode_next_pc(drcontext, curr_pc);
        opnd_t pc = IF_X86_ELSE(OPND_CREATE_INTPTR,OPND_CREATE_INT)(next_pc);
        dr_insert_clean_call(drcontext, bb, instr, (void *) pre_call, false, 1, pc);
    }

    return DR_EMIT_DEFAULT;
}

/* We transform string loops into regular loops so we can more easily
 * monitor every memory reference they make.
 */
static dr_emit_flags_t
event_bb_app2app(void *drcontext, void *tag, instrlist_t *bb,
                 bool for_trace, bool translating)
{
    if (!drutil_expand_rep_string(drcontext, bb)) {
        DR_ASSERT(false);
        /* in release build, carry on: we'll just miss per-iter refs */
    }
    return DR_EMIT_DEFAULT;
}
{'fraction_non_alphanumeric': 0.10167078430551485, 'fraction_numerical': 0.005359909551526318, 'mean_word_length': 2.6809494451294698, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 59, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 36, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3077235', 'n_tokens_mistral': 7796, 'n_tokens_neox': 7472, 'n_words': 2102}