diff --git a/api/docs/release.dox b/api/docs/release.dox
index 498e162497e0f3d1482e82f4102510567e82fc01..ffb7821513022e3d01fe62d7b035c52d800c0d0e 100644
--- a/api/docs/release.dox
+++ b/api/docs/release.dox
@@ -183,7 +183,11 @@ Further non-compatibility-affecting changes include:
    drmemtrace_get_modlist_path(), and a separate rawtrace library for
    post-processing customization with raw2trace_t::handle_custom_data(),
    raw2trace_t::do_module_parsing(), raw2trace_t::do_conversion(), and
-   raw2trace_directory_t.  A corresponding CMake function for finding the
+   raw2trace_directory_t.  The raw2trace library also includes an interface
+   for obtaining further instruction information than is stored in the
+   trace via raw2trace_t::do_module_parsing_and_mapping() and
+   raw2trace_t::find_mapped_trace_address().
+   A corresponding CMake function for finding the
    tracer customization header is use_DynamoRIO_drmemtrace_tracer().
  - Added a set_value() function to the \ref page_droption.
  - Added instrlist_get_auto_predicate() and instrlist_set_auto_predicate().
diff --git a/clients/drcachesim/CMakeLists.txt b/clients/drcachesim/CMakeLists.txt
index 2e1d26eea38fa2ab24f650f187e9252b7060da62..fb2a461706c523688d3d66c5273b20ebbd428a6f 100644
--- a/clients/drcachesim/CMakeLists.txt
+++ b/clients/drcachesim/CMakeLists.txt
@@ -1,5 +1,5 @@
 # **********************************************************
-# Copyright (c) 2015-2017 Google, Inc.    All rights reserved.
+# Copyright (c) 2015-2018 Google, Inc.    All rights reserved.
 # **********************************************************
 
 # Redistribution and use in source and binary forms, with or without
@@ -71,6 +71,8 @@ add_exported_library(drmemtrace_reuse_distance STATIC tools/reuse_distance.cpp)
 add_exported_library(drmemtrace_histogram STATIC tools/histogram.cpp)
 add_exported_library(drmemtrace_reuse_time STATIC tools/reuse_time.cpp)
 add_exported_library(drmemtrace_basic_counts STATIC tools/basic_counts.cpp)
+add_exported_library(drmemtrace_opcode_mix STATIC tools/opcode_mix.cpp)
+configure_DynamoRIO_standalone(drmemtrace_opcode_mix)
 
 # We combine the cache and TLB simulators as they share code already.
 add_exported_library(drmemtrace_simulator STATIC
@@ -116,7 +118,8 @@ add_executable(drcachesim ${drcachesim_srcs})
 configure_DynamoRIO_standalone(drcachesim)
 # Link in our tools:
 target_link_libraries(drcachesim drmemtrace_simulator drmemtrace_reuse_distance
-  drmemtrace_histogram drmemtrace_reuse_time drmemtrace_basic_counts drmemtrace_raw2trace)
+  drmemtrace_histogram drmemtrace_reuse_time drmemtrace_basic_counts
+  drmemtrace_opcode_mix drmemtrace_raw2trace)
 # To avoid dup symbol errors between drinjectlib and the drdecode brought in
 # by drfrontendlib we have to explicitly list drdecode up front:
 target_link_libraries(drcachesim drdecode drinjectlib drconfiglib drfrontendlib)
@@ -150,6 +153,7 @@ install_client_nonDR_header(drmemtrace tools/reuse_distance_create.h)
 install_client_nonDR_header(drmemtrace tools/histogram_create.h)
 install_client_nonDR_header(drmemtrace tools/reuse_time_create.h)
 install_client_nonDR_header(drmemtrace tools/basic_counts_create.h)
+install_client_nonDR_header(drmemtrace tools/opcode_mix_create.h)
 install_client_nonDR_header(drmemtrace simulator/cache_simulator_create.h)
 install_client_nonDR_header(drmemtrace simulator/tlb_simulator_create.h)
 install_client_nonDR_header(drmemtrace tracer/raw2trace.h)
@@ -250,6 +254,7 @@ restore_nonclient_flags(drmemtrace_reuse_distance)
 restore_nonclient_flags(drmemtrace_histogram)
 restore_nonclient_flags(drmemtrace_reuse_time)
 restore_nonclient_flags(drmemtrace_basic_counts)
+restore_nonclient_flags(drmemtrace_opcode_mix)
 restore_nonclient_flags(drmemtrace_analyzer)
 
 # We need to pass /EHsc and we pull in libcmtd into drcachesim from a dep lib.
@@ -283,13 +288,14 @@ add_win32_flags(drmemtrace_reuse_distance)
 add_win32_flags(drmemtrace_histogram)
 add_win32_flags(drmemtrace_reuse_time)
 add_win32_flags(drmemtrace_basic_counts)
+add_win32_flags(drmemtrace_opcode_mix)
 add_win32_flags(drmemtrace_analyzer)
 if (WIN32 AND DEBUG)
   get_target_property(sim_srcs drcachesim SOURCES)
   get_target_property(raw2trace_srcs drraw2trace SOURCES)
   # The client, and our standalone DR users, had /MT added so we need to override.
   # XXX: solve this by avoiding the /MT in the first place!
-  foreach (src ${client_and_sim_srcs} ${sim_srcs} ${raw2trace_srcs})
+  foreach (src ${client_and_sim_srcs} ${sim_srcs} ${raw2trace_srcs} tools/opcode_mix.cpp)
     get_property(cur SOURCE ${src} PROPERTY COMPILE_FLAGS)
     string(REPLACE "/MT " "" cur ${cur}) # Avoid override warning.
     set_source_files_properties(${src} COMPILE_FLAGS "${cur} /MTd")
diff --git a/clients/drcachesim/analysis_tool.h b/clients/drcachesim/analysis_tool.h
index 5425bf3649a5d735ea4e0375ca6336b8498b60a3..f2371d01c245898ae0d49a505d9c1f76c4893598 100644
--- a/clients/drcachesim/analysis_tool.h
+++ b/clients/drcachesim/analysis_tool.h
@@ -1,5 +1,5 @@
 /* **********************************************************
- * Copyright (c) 2016-2017 Google, Inc.  All rights reserved.
+ * Copyright (c) 2016-2018 Google, Inc.  All rights reserved.
  * **********************************************************/
 
 /*
@@ -69,9 +69,13 @@ class analysis_tool_t
     /**
      * The heart of an analysis tool, this routine operates on a single trace entry and
      * takes whatever actions the tool needs to perform its analysis.
+     * The return value indicates whether it was successful or there was an error.
      */
     virtual bool process_memref(const memref_t &memref) = 0;
-    /** This routine reports the results of the trace analysis. */
+    /**
+     * This routine reports the results of the trace analysis.
+     * The return value indicates whether it was successful or there was an error.
+     */
     virtual bool print_results() = 0;
  protected:
     bool success;
diff --git a/clients/drcachesim/common/options.cpp b/clients/drcachesim/common/options.cpp
index f49167dee790fcaf8e7101b489acbe2d3e978f3a..c17249d9a15586a852e8196f6e931e0317257a78 100644
--- a/clients/drcachesim/common/options.cpp
+++ b/clients/drcachesim/common/options.cpp
@@ -65,7 +65,14 @@ droption_t<std::string> op_indir
 droption_t<std::string> op_infile
 (DROPTION_SCOPE_ALL, "infile", "", "Offline trace file for input to the simulator",
  "Directs the simulator to use a trace file (not a raw data file from -offline: "
- "such a file neeeds to be converted via drposttrace or -indir first).");
+ "such a file neeeds to be converted via drraw2trace or -indir first).");
+
+droption_t<std::string> op_module_file
+(DROPTION_SCOPE_ALL, "module_file", "", "Path to modules.log for opcode_mix tool",
+ "The opcode_mix tool needs the modules.log file (generated by the offline "
+ "post-processing step in the raw/ subdirectory) in addition to the trace file. "
+ "If the file is named modules.log and is in the same directory as the trace file, "
+ "or a raw/ subdirectory below the trace file, this parameter can be omitted.");
 
 droption_t<unsigned int> op_num_cores
 (DROPTION_SCOPE_FRONTEND, "cores", 4, "Number of cores",
diff --git a/clients/drcachesim/common/options.h b/clients/drcachesim/common/options.h
index 9aa0dedf783ec09ec1258b1472af6d9f1f4edec2..054fde20a133b3b59ceaa25147c689c9ba1fdd8d 100644
--- a/clients/drcachesim/common/options.h
+++ b/clients/drcachesim/common/options.h
@@ -1,5 +1,5 @@
 /* **********************************************************
- * Copyright (c) 2015-2017 Google, Inc.  All rights reserved.
+ * Copyright (c) 2015-2018 Google, Inc.  All rights reserved.
  * **********************************************************/
 
 /*
@@ -47,6 +47,7 @@
 #define REUSE_DIST                              "reuse_distance"
 #define REUSE_TIME                              "reuse_time"
 #define BASIC_COUNTS                            "basic_counts"
+#define OPCODE_MIX                              "opcode_mix"
 
 #include <string>
 #include "droption.h"
@@ -56,6 +57,7 @@ extern droption_t<std::string> op_ipc_name;
 extern droption_t<std::string> op_outdir;
 extern droption_t<std::string> op_infile;
 extern droption_t<std::string> op_indir;
+extern droption_t<std::string> op_module_file;
 extern droption_t<unsigned int> op_num_cores;
 extern droption_t<unsigned int> op_line_size;
 extern droption_t<bytesize_t> op_L1I_size;
diff --git a/clients/drcachesim/common/utils.h b/clients/drcachesim/common/utils.h
index 668dd175906c2e51cb16e148d20eee0252461df1..274e0be0e054d5a4f8f4dd91928599082c3116e3 100644
--- a/clients/drcachesim/common/utils.h
+++ b/clients/drcachesim/common/utils.h
@@ -1,5 +1,5 @@
 /* **********************************************************
- * Copyright (c) 2015-2017 Google, Inc.  All rights reserved.
+ * Copyright (c) 2015-2018 Google, Inc.  All rights reserved.
  * **********************************************************/
 
 /*
@@ -86,10 +86,12 @@
 
 #ifdef WINDOWS
 # define DIRSEP "\\"
+# define ALT_DIRSEP "/"
 # define IF_WINDOWS(x) x
 # define IF_UNIX(x)
 #else
 # define DIRSEP "/"
+# define ALT_DIRSEP ""
 # define IF_WINDOWS(x)
 # define IF_UNIX(x) x
 #endif
diff --git a/clients/drcachesim/drcachesim.dox.in b/clients/drcachesim/drcachesim.dox.in
index 65209dff6d4fb5456807f80067d3f09053944cfa..c6c14889027e26af7f6789d587453c4fa1073801 100644
--- a/clients/drcachesim/drcachesim.dox.in
+++ b/clients/drcachesim/drcachesim.dox.in
@@ -338,7 +338,49 @@ subsequent iterations do not incur a fetch.  They are included in the trace
 as a different type of trace entry to support core simulators in addition
 to cache simulators.
 
-The top referenced lines are displayed by the \p histogram tool:
+The opcode_mix tool uses the non-fetched instruction information along with
+the preserved libraries and binaries from the traced execution to gather
+more information on each executed instruction than was stored in the trace.
+It only supports offline traces, and the \p modules.log file created during
+post-processing of the trace must be preserved.  The results are broken
+down by the opcodes used in DR's IR, where \p mov is split into a separate
+opcode for load and store but both have the same public string "mov":
+
+\code
+$ bin64/drrun -t drcachesim -offline -- suite/tests/bin/pi_estimator
+Estimation of pi is 3.142425985001098
+$ bin64/drrun -t drcachesim -simulator_type opcode_mix -indir drmemtrace.*.dir
+Opcode mix tool results:
+         267271 : total executed instructions
+          36432 :       mov
+          31075 :       mov
+          24715 :       add
+          22579 :      test
+          22539 :       cmp
+          12137 :       lea
+          11136 :       jnz
+          10568 :     movzx
+          10243 :        jz
+           9056 :       and
+           8064 :       jnz
+           7279 :        jz
+           5659 :      push
+           4528 :       sub
+           4357 :       pop
+           4001 :       shr
+           3427 :      jnbe
+           2634 :       mov
+           2469 :       shl
+           2344 :        jb
+           2291 :       ret
+           2178 :       xor
+           2164 :      call
+           2111 :   pcmpeqb
+           1472 :    movdqa
+...
+\endcode
+
+The top referenced cache lines are displayed by the \p histogram tool:
 
 \code
 $ bin64/drrun -t drcachesim -simulator_type histogram -- suite/tests/bin/pi_estimator
@@ -578,9 +620,9 @@ use_DynamoRIO_drmemtrace(mytool)
 The \p drmemtrace_analyzer library exported by the DynamoRIO package is the
 main library to link when building a new tool.  The tools described above
 are also exported as the libraries \p drmemtrace_basic_counts, \p
-drmemtrace_histogram, \p drmemtrace_reuse_distance, \p
+drmemtrace_opcode_mix, \p drmemtrace_histogram, \p drmemtrace_reuse_distance, \p
 drmemtrace_reuse_time, and \p drmemtrace_simulator and can be created using
-the basic_counts_tool_create(), histogram_tool_create(),
+the basic_counts_tool_create(), opcode_mix_tool_create(), histogram_tool_create(),
 reuse_distance_tool_create(), reuse_time_tool_create(),
 cache_simulator_create(), and tlb_simulator_create() functions.
 
diff --git a/clients/drcachesim/simulator/analyzer_interface.cpp b/clients/drcachesim/simulator/analyzer_interface.cpp
index def958072f3dda8d8f111a3e9545c83fc13dbf0b..f8ba958cccd8653cdb19133f5a452d4bce20d437 100644
--- a/clients/drcachesim/simulator/analyzer_interface.cpp
+++ b/clients/drcachesim/simulator/analyzer_interface.cpp
@@ -1,5 +1,5 @@
 /* **********************************************************
- * Copyright (c) 2017 Google, Inc.  All rights reserved.
+ * Copyright (c) 2017-2018 Google, Inc.  All rights reserved.
  * **********************************************************/
 
 /*
@@ -44,6 +44,9 @@
 #include "../tools/reuse_distance_create.h"
 #include "../tools/reuse_time_create.h"
 #include "../tools/basic_counts_create.h"
+#include "../tools/opcode_mix_create.h"
+#include "../tracer/raw2trace.h"
+#include <fstream>
 
 analysis_tool_t *
 drmemtrace_analysis_tool_create()
@@ -97,10 +100,38 @@ drmemtrace_analysis_tool_create()
                                       op_verbose.get_value());
     } else if (op_simulator_type.get_value() == BASIC_COUNTS) {
         return basic_counts_tool_create(op_verbose.get_value());
+    } else if (op_simulator_type.get_value() == OPCODE_MIX) {
+        // This tool needs the modules.log, which we assume is next to the trace file or
+        // in the raw/ subdir from raw2trace.
+        std::string module_file_path;
+        if (!op_module_file.get_value().empty())
+            module_file_path = op_module_file.get_value();
+        else {
+            std::string trace_dir;
+            if (!op_indir.get_value().empty())
+                trace_dir = op_indir.get_value();
+            else {
+                if (op_infile.get_value().empty()) {
+                    ERRMSG("Usage error: the opcode mix tool requires offline traces.\n");
+                    return nullptr;
+                }
+                size_t sep_index = op_infile.get_value().find_last_of(DIRSEP ALT_DIRSEP);
+                if (sep_index != std::string::npos)
+                    trace_dir = std::string(op_infile.get_value(), 0, sep_index);
+            }
+            module_file_path = trace_dir + std::string(DIRSEP) +
+                DRMEMTRACE_MODULE_LIST_FILENAME;
+            if (!std::ifstream(module_file_path.c_str()).good()) {
+                trace_dir += std::string(DIRSEP) + OUTFILE_SUBDIR;
+                module_file_path = trace_dir + std::string(DIRSEP) +
+                    DRMEMTRACE_MODULE_LIST_FILENAME;
+            }
+        }
+        return opcode_mix_tool_create(module_file_path, op_verbose.get_value());
     } else {
         ERRMSG("Usage error: unsupported analyzer type. "
                "Please choose " CPU_CACHE ", " TLB ", "
                HISTOGRAM ", " REUSE_DIST ", or " BASIC_COUNTS ".\n");
-        return NULL;
+        return nullptr;
     }
 }
diff --git a/clients/drcachesim/tests/offline-opcode_mix.templatex b/clients/drcachesim/tests/offline-opcode_mix.templatex
new file mode 100644
index 0000000000000000000000000000000000000000..676004cc6c0533b36eda9af3718f7d8fc5fc4cdb
--- /dev/null
+++ b/clients/drcachesim/tests/offline-opcode_mix.templatex
@@ -0,0 +1,8 @@
+Hello, world!
+Opcode mix tool results:
+    *[0-9]* : total executed instructions
+    *[0-9]* : [a-z ]*
+    *[0-9]* : [a-z ]*
+    *[0-9]* : [a-z ]*
+    *[0-9]* : [a-z ]*
+.*
diff --git a/clients/drcachesim/tools/opcode_mix.cpp b/clients/drcachesim/tools/opcode_mix.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cfd2a8a2bc938d27cd66714df2fb898e0f1b7a60
--- /dev/null
+++ b/clients/drcachesim/tools/opcode_mix.cpp
@@ -0,0 +1,130 @@
+/* **********************************************************
+ * Copyright (c) 2017-2018 Google, Inc.  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 Google, 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.
+ */
+
+/* This trace analyzer requires access to the modules.log file and the
+ * libraries and binary from the traced execution in order to obtain further
+ * information about each instruction than was stored in the trace.
+ * It does not support online use, only offline.
+ */
+
+#include "dr_api.h"
+#include "opcode_mix.h"
+#include "common/utils.h"
+#include "tracer/raw2trace.h"
+#include "tracer/raw2trace_directory.h"
+#include <algorithm>
+#include <iomanip>
+#include <iostream>
+#include <vector>
+
+const std::string opcode_mix_t::TOOL_NAME = "Opcode mix tool";
+
+analysis_tool_t *
+opcode_mix_tool_create(const std::string& module_file_path, unsigned int verbose)
+{
+    return new opcode_mix_t(module_file_path, verbose);
+}
+
+opcode_mix_t::opcode_mix_t(const std::string& module_file_path, unsigned int verbose) :
+    dcontext(nullptr), raw2trace(nullptr), knob_verbose(verbose), instr_count(0)
+{
+    if (module_file_path.empty()) {
+        success = false;
+        return;
+    }
+    dcontext = dr_standalone_init();
+    raw2trace_directory_t dir(module_file_path);
+    raw2trace = new raw2trace_t(dir.modfile_bytes, std::vector<std::istream*>(),
+                                nullptr, dcontext, verbose);
+    std::string error = raw2trace->do_module_parsing_and_mapping();
+    if (!error.empty()) {
+        success = false;
+        return;
+    }
+}
+
+opcode_mix_t::~opcode_mix_t()
+{
+    delete raw2trace;
+}
+
+bool
+opcode_mix_t::process_memref(const memref_t &memref)
+{
+  if (!type_is_instr(memref.instr.type) &&
+      memref.data.type != TRACE_TYPE_INSTR_NO_FETCH)
+      return true;
+  ++instr_count;
+  app_pc mapped_pc;
+  std::string err =
+      raw2trace->find_mapped_trace_address((app_pc)memref.instr.addr, &mapped_pc);
+  if (!err.empty())
+      return false;
+  int opcode;
+  auto cached_opcode = opcode_cache.find(mapped_pc);
+  if (cached_opcode != opcode_cache.end()) {
+      opcode = cached_opcode->second;
+  } else {
+      instr_t instr;
+      instr_init(dcontext, &instr);
+      app_pc next_pc = decode(dcontext, mapped_pc, &instr);
+      if (next_pc == NULL || !instr_valid(&instr))
+          return false;
+      opcode = instr_get_opcode(&instr);
+      opcode_cache[mapped_pc] = opcode;
+      instr_free(dcontext, &instr);
+  }
+  ++opcode_counts[opcode];
+  return true;
+}
+
+static bool
+cmp_val(const std::pair<int, int_least64_t> &l,
+        const std::pair<int, int_least64_t> &r)
+{
+    return (l.second > r.second);
+}
+
+bool
+opcode_mix_t::print_results()
+{
+    std::cerr << TOOL_NAME << " results:\n";
+    std::cerr << std::setw(15) << instr_count << " : total executed instructions\n";
+    std::vector<std::pair<int, int_least64_t>> sorted(opcode_counts.begin(),
+                                                      opcode_counts.end());
+    std::sort(sorted.begin(), sorted.end(), cmp_val);
+    for (const auto &keyvals : sorted) {
+        std::cerr << std::setw(15) << keyvals.second << " : "
+                  << std::setw(9) << decode_opcode_name(keyvals.first) << "\n";
+    }
+    return true;
+}
diff --git a/clients/drcachesim/tools/opcode_mix.h b/clients/drcachesim/tools/opcode_mix.h
new file mode 100644
index 0000000000000000000000000000000000000000..a569e957e9d739830c2db1fc039a0306fab26feb
--- /dev/null
+++ b/clients/drcachesim/tools/opcode_mix.h
@@ -0,0 +1,60 @@
+/* **********************************************************
+ * Copyright (c) 2018 Google, Inc.  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 Google, 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.
+ */
+
+#ifndef _OPCODE_MIX_H_
+#define _OPCODE_MIX_H_ 1
+
+#include <string>
+#include <unordered_map>
+
+#include "analysis_tool.h"
+#include "tracer/raw2trace.h"
+
+class opcode_mix_t : public analysis_tool_t
+{
+ public:
+    opcode_mix_t(const std::string& module_file_path, unsigned int verbose);
+    virtual ~opcode_mix_t();
+    virtual bool process_memref(const memref_t &memref);
+    virtual bool print_results();
+
+ protected:
+    void *dcontext;
+    raw2trace_t *raw2trace;
+    unsigned int knob_verbose;
+    int_least64_t instr_count;
+    std::unordered_map<int, int_least64_t> opcode_counts;
+    std::unordered_map<app_pc, int> opcode_cache;
+    static const std::string TOOL_NAME;
+};
+
+#endif /* _OPCODE_MIX_H_ */
diff --git a/clients/drcachesim/tools/opcode_mix_create.h b/clients/drcachesim/tools/opcode_mix_create.h
new file mode 100644
index 0000000000000000000000000000000000000000..d1085590b30cbcbbd28b3677668329bb22c111d9
--- /dev/null
+++ b/clients/drcachesim/tools/opcode_mix_create.h
@@ -0,0 +1,53 @@
+/* **********************************************************
+ * Copyright (c) 2018 Google, Inc.  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 Google, 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.
+ */
+
+/* basic-counts tool creation */
+
+#ifndef _OPCODE_MIX_CREATE_H_
+#define _OPCODE_MIX_CREATE_H_ 1
+
+#include "analysis_tool.h"
+
+/**
+ * @file drmemtrace/opcode_mix_create.h
+ * @brief DrMemtrace opcode mixture trace analysis tool creation.
+ */
+
+/**
+ * Creates an analysis tool which counts the number of instances of each opcode
+ * in the trace.  This tool needs access to the modules.log and original libraries
+ * and binaries from the traced execution.  It does not support online analysis.
+ */
+analysis_tool_t *
+opcode_mix_tool_create(const std::string& module_file_path, unsigned int verbose = 0);
+
+#endif /* _OPCODE_MIX_CREATE_H_ */
diff --git a/clients/drcachesim/tracer/raw2trace.cpp b/clients/drcachesim/tracer/raw2trace.cpp
index 064340d52f065fea3a26534bb56c0b24ce51b602..e9978063b800bece395f64deeb26e598f0a3ea0d 100644
--- a/clients/drcachesim/tracer/raw2trace.cpp
+++ b/clients/drcachesim/tracer/raw2trace.cpp
@@ -259,7 +259,7 @@ raw2trace_t::unmap_modules(void)
 {
     // drmodtrack_offline_exit requires the parameter to be non-null, but we
     // may not have even initialized the modhandle yet.
-    if (modhandle != NULL &&
+    if (modhandle != nullptr &&
         drmodtrack_offline_exit(modhandle) != DRCOVLIB_SUCCESS) {
           return "Failed to clean up module table data";
     }
@@ -274,6 +274,42 @@ raw2trace_t::unmap_modules(void)
     return "";
 }
 
+std::string
+raw2trace_t::do_module_parsing_and_mapping()
+{
+   std::string error = read_and_map_modules();
+   if (!error.empty())
+       return error;
+   return "";
+}
+
+std::string
+raw2trace_t::find_mapped_trace_address(app_pc trace_address, OUT app_pc *mapped_address)
+{
+    if (modhandle == nullptr || modlist.empty())
+        return "Failed to call do_module_parsing_and_mapping() first";
+    if (mapped_address == nullptr)
+        return "Invalid parameter";
+    // For simplicity we do a linear search, caching the prior hit.
+    if (trace_address >= last_orig_base &&
+        trace_address < last_orig_base + last_map_size) {
+        *mapped_address = trace_address - last_orig_base + last_map_base;
+        return "";
+    }
+    for (std::vector<module_t>::iterator mvi = modvec.begin();
+         mvi != modvec.end(); ++mvi) {
+        if (trace_address >= mvi->orig_base &&
+            trace_address < mvi->orig_base + mvi->map_size) {
+            *mapped_address = trace_address - mvi->orig_base + mvi->map_base;
+            last_orig_base = mvi->orig_base;
+            last_map_size = mvi->map_size;
+            last_map_base = mvi->map_base;
+            return "";
+        }
+    }
+    return "Trace address not found";
+}
+
 /***************************************************************************
  * Disassembly to fill in instr and memref entries
  */
@@ -771,7 +807,8 @@ raw2trace_t::raw2trace_t(const char *module_map_in,
     : modmap(module_map_in), modhandle(NULL), thread_files(thread_files_in),
       out_file(out_file_in), dcontext(dcontext_in),
       prev_instr_was_rep_string(false), instrs_are_separate(false),
-      verbosity(verbosity_in), user_process(nullptr), user_process_data(nullptr)
+      verbosity(verbosity_in), user_process(nullptr), user_process_data(nullptr),
+      last_orig_base(nullptr), last_map_size(0), last_map_base(nullptr)
 {
     if (dcontext == NULL) {
         dcontext = dr_standalone_init();
diff --git a/clients/drcachesim/tracer/raw2trace.h b/clients/drcachesim/tracer/raw2trace.h
index cb2342e6c8a634ed14103262ff5c3671eca74cc2..ee9812111982f265fa9b934b0a76c2f042d533d8 100644
--- a/clients/drcachesim/tracer/raw2trace.h
+++ b/clients/drcachesim/tracer/raw2trace.h
@@ -1,5 +1,5 @@
 /* **********************************************************
- * Copyright (c) 2016-2017 Google, Inc.  All rights reserved.
+ * Copyright (c) 2016-2018 Google, Inc.  All rights reserved.
  * **********************************************************/
 
 /*
@@ -119,6 +119,31 @@ public:
      */
     std::string do_module_parsing();
 
+    /**
+     * This interface is meant to be used with a final trace rather than a raw
+     * trace, using the module log file saved from the raw2trace conversion.
+     * This routine first calls do_module_parsing() and then maps each module into
+     * the current address space, allowing the user to augment the instruction
+     * information in the trace with additional information by decoding the
+     * instruction bytes.  The routine find_mapped_trace_address() should be used
+     * to convert from memref_t.instr.addr to the corresponding mapped address in
+     * the current process.
+     * Returns a non-empty error message on failure.
+     */
+    std::string do_module_parsing_and_mapping();
+
+    /**
+     * This interface is meant to be used with a final trace rather than a raw
+     * trace, using the module log file saved from the raw2trace conversion.
+     * When do_module_parsing_and_mapping() has been called, this routine can be used
+     * to convert an instruction program counter in a trace into an address in the
+     * current process where the instruction bytes for that instruction are mapped,
+     * allowing decoding for obtaining further information than is stored in the trace.
+     * Returns a non-empty error message on failure.
+     */
+    std::string find_mapped_trace_address(app_pc trace_address,
+                                          OUT app_pc *mapped_address);
+
     /**
      * Performs the conversion from raw data to finished trace files.
      * Returns a non-empty error message on failure.
@@ -178,6 +203,9 @@ private:
     std::vector<drmodtrack_info_t> modlist;
     std::string (*user_process)(drmodtrack_info_t *info, void *data, void *user_data);
     void *user_process_data;
+    app_pc last_orig_base;
+    size_t last_map_size;
+    byte *last_map_base;
 
     // Custom module fields that use drmodtrack are global.
     static const char * (*user_parse)(const char *src, OUT void **data);
diff --git a/clients/drcachesim/tracer/raw2trace_directory.cpp b/clients/drcachesim/tracer/raw2trace_directory.cpp
index ee6c1bc8f2cdb4b12564f8778d713cc755b9e6d0..8581a7e5a382cd7b122675c5aaf4f7c07b33a41b 100644
--- a/clients/drcachesim/tracer/raw2trace_directory.cpp
+++ b/clients/drcachesim/tracer/raw2trace_directory.cpp
@@ -1,5 +1,5 @@
 /* **********************************************************
- * Copyright (c) 2017 Google, Inc.  All rights reserved.
+ * Copyright (c) 2017-2018 Google, Inc.  All rights reserved.
  * **********************************************************/
 
 /*
@@ -144,16 +144,9 @@ raw2trace_directory_t::open_thread_log_file(const char *basename)
     VPRINT(1, "Opened thread log file %s\n", path);
 }
 
-raw2trace_directory_t::raw2trace_directory_t(const std::string &indir_in,
-                                             const std::string &outname_in,
-                                             unsigned int verbosity_in)
-    : indir(indir_in), outname(outname_in), verbosity(verbosity_in)
+void
+raw2trace_directory_t::read_module_file(const std::string &modfilename)
 {
-    // Support passing both base dir and raw/ subdir.
-    if (indir.find(OUTFILE_SUBDIR) == std::string::npos)
-        indir += std::string(DIRSEP) + OUTFILE_SUBDIR;
-    std::string modfilename = indir + std::string(DIRSEP) +
-        DRMEMTRACE_MODULE_LIST_FILENAME;
     modfile = dr_open_file(modfilename.c_str(), DR_FILE_READ);
     if (modfile == INVALID_FILE)
         FATAL_ERROR("Failed to open module file %s", modfilename.c_str());
@@ -164,6 +157,20 @@ raw2trace_directory_t::raw2trace_directory_t(const std::string &indir_in,
     modfile_bytes = new char[modfile_size_];
     if (dr_read_file(modfile, modfile_bytes, modfile_size_) < (ssize_t)modfile_size_)
         FATAL_ERROR("Didn't read whole module file %s", modfilename.c_str());
+}
+
+raw2trace_directory_t::raw2trace_directory_t(const std::string &indir_in,
+                                             const std::string &outname_in,
+                                             unsigned int verbosity_in)
+    : indir(indir_in), outname(outname_in), verbosity(verbosity_in)
+{
+    // Support passing both base dir and raw/ subdir.
+    if (indir.find(OUTFILE_SUBDIR) == std::string::npos) {
+        indir += std::string(DIRSEP) + OUTFILE_SUBDIR;
+    }
+    std::string modfilename = indir + std::string(DIRSEP) +
+        DRMEMTRACE_MODULE_LIST_FILENAME;
+    read_module_file(modfilename);
 
     out_file.open(outname.c_str(), std::ofstream::binary);
     if (!out_file)
@@ -173,6 +180,13 @@ raw2trace_directory_t::raw2trace_directory_t(const std::string &indir_in,
     open_thread_files();
 }
 
+raw2trace_directory_t::raw2trace_directory_t(const std::string &module_file_path,
+                                             unsigned int verbosity_in)
+    : indir(""), outname(""), verbosity(verbosity_in)
+{
+    read_module_file(module_file_path);
+}
+
 raw2trace_directory_t::~raw2trace_directory_t()
 {
     delete[] modfile_bytes;
diff --git a/clients/drcachesim/tracer/raw2trace_directory.h b/clients/drcachesim/tracer/raw2trace_directory.h
index 76e47e14faeb2ec2268a34f730fa6a9552def3b3..f1b0bba57f82bea1cbf78bad62620bf3c77d93af 100644
--- a/clients/drcachesim/tracer/raw2trace_directory.h
+++ b/clients/drcachesim/tracer/raw2trace_directory.h
@@ -1,5 +1,5 @@
 /* **********************************************************
- * Copyright (c) 2017 Google, Inc.  All rights reserved.
+ * Copyright (c) 2017-2018 Google, Inc.  All rights reserved.
  * **********************************************************/
 
 /*
@@ -43,6 +43,10 @@ class raw2trace_directory_t {
 public:
     raw2trace_directory_t(const std::string &indir, const std::string &outname,
                           unsigned int verbosity = 0);
+    // This version is for raw2trace_t::do_module_parsing() or
+    // raw2trace_t::do_module_parsing_and_mapping().
+    raw2trace_directory_t(const std::string &module_file_path,
+                          unsigned int verbosity = 0);
     ~raw2trace_directory_t();
 
     char *modfile_bytes;
@@ -50,6 +54,7 @@ public:
     std::ofstream out_file;
 
 private:
+    void read_module_file(const std::string &modfilename);
     void open_thread_files();
     void open_thread_log_file(const char *basename);
     file_t modfile;
diff --git a/suite/tests/CMakeLists.txt b/suite/tests/CMakeLists.txt
index 8948d3bfa9336e1d67aecdf3b653a55acbe738bc..52a04d0ca2801bedb9929729beb5cabd20604168 100644
--- a/suite/tests/CMakeLists.txt
+++ b/suite/tests/CMakeLists.txt
@@ -2590,6 +2590,12 @@ if (CLIENT_INTERFACE)
         unset(tool.drcacheoff.basic_counts_rawtemp)
       endif ()
 
+      torunonly_drcacheoff(opcode_mix ${ci_shared_app} ""
+        "@-simulator_type@opcode_mix" "")
+      # We're using the same app so we serialize to avoid racing trace dirs:
+      set(tool.drcacheoff.opcode_mix_depends tool.drcacheoff.simple)
+      set(tool.drcacheoff.opcode_mix_depends tool.drcacheoff.filter)
+
       # FIXME i#2007: fails to link on A64
       # XXX i#1551: startstop API is NYI on ARM
       # XXX i#1997: dynamorio_static is not supported on Mac yet