blob: c0a3cb57d5a9902db87d78792e7a718277d1a788 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2016-2024 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.
*/
#include "analyzer.h"
#include "analyzer_multi.h"
#include "common/options.h"
#include "common/utils.h"
#include "common/directory_iterator.h"
#include "tracer/raw2trace_directory.h"
#include "tracer/raw2trace.h"
#include "reader/file_reader.h"
#ifdef HAS_ZLIB
# include "common/gzip_istream.h"
# include "reader/compressed_file_reader.h"
# ifdef HAS_ZIP
# include "zipfile_istream.h"
# include "zipfile_ostream.h"
# endif
#endif
#ifdef HAS_ZIP
# include "common/zipfile_istream.h"
#endif
#include "reader/ipc_reader.h"
#include "simulator/cache_simulator_create.h"
#include "simulator/tlb_simulator_create.h"
#include "tools/basic_counts_create.h"
#include "tools/filter/record_filter_create.h"
#include "tools/func_view_create.h"
#include "tools/histogram_create.h"
#include "tools/invariant_checker.h"
#include "tools/invariant_checker_create.h"
#include "tools/opcode_mix_create.h"
#include "tools/schedule_stats_create.h"
#include "tools/syscall_mix_create.h"
#include "tools/reuse_distance_create.h"
#include "tools/reuse_time_create.h"
#include "tools/view_create.h"
#include "tools/loader/external_config_file.h"
#include "tools/loader/external_tool_creator.h"
#include "tools/filter/record_filter_create.h"
namespace dynamorio {
namespace drmemtrace {
using ::dynamorio::droption::droption_parser_t;
using ::dynamorio::droption::DROPTION_SCOPE_ALL;
/****************************************************************
* Specializations for analyzer_multi_tmpl_t<memref_t, reader_t>,
* aka analyzer_multi_t.
*/
template <>
std::unique_ptr<reader_t>
analyzer_multi_t::create_ipc_reader(const char *name, int verbose)
{
return std::unique_ptr<reader_t>(new ipc_reader_t(name, verbose));
}
template <>
std::unique_ptr<reader_t>
analyzer_multi_t::create_ipc_reader_end()
{
return std::unique_ptr<reader_t>(new ipc_reader_t());
}
template <>
analysis_tool_t *
analyzer_multi_t::create_external_tool(const std::string &tool_name)
{
analysis_tool_t *tool = nullptr;
std::string tools_dir(op_dr_root.get_value());
tools_dir += std::string(DIRSEP) + "tools" + std::string(DIRSEP);
directory_iterator_t end;
directory_iterator_t iter(tools_dir);
if (!iter) {
return nullptr;
}
for (; iter != end; ++iter) {
if ((*iter).find(".drcachesim") != std::string::npos) {
std::string abs_path(tools_dir);
abs_path.append(*iter);
external_tool_config_file_t config(op_dr_root.get_value(), abs_path);
if (config.valid_ && config.tool_name_ == tool_name) {
external_tool_creator_t creator(config.creator_path_);
error_string_ = creator.error();
if (creator.error().empty()) {
DR_ASSERT(creator.get_tool_name() == tool_name.c_str());
tool = creator.create_tool();
loaders_.push_back(std::move(creator));
break;
}
}
}
}
return tool;
}
template <>
analysis_tool_t *
analyzer_multi_t::create_invariant_checker()
{
if (op_offline.get_value()) {
// TODO i#5538: Locate and open the schedule files and pass to the
// reader(s) for seeking. For now we only read them for this test.
// TODO i#5843: Share this code with scheduler_t or pass in for all
// tools from here for fast skipping in serial and per-cpu modes.
std::string tracedir =
raw2trace_directory_t::tracedir_from_rawdir(op_indir.get_value());
if (directory_iterator_t::is_directory(tracedir)) {
directory_iterator_t end;
directory_iterator_t iter(tracedir);
if (!iter) {
this->error_string_ = "Failed to list directory: " + iter.error_string();
return nullptr;
}
for (; iter != end; ++iter) {
const std::string fname = *iter;
const std::string fpath = tracedir + DIRSEP + fname;
if (starts_with(fname, DRMEMTRACE_SERIAL_SCHEDULE_FILENAME)) {
if (ends_with(fname, ".gz")) {
#ifdef HAS_ZLIB
this->serial_schedule_file_ =
std::unique_ptr<std::istream>(new gzip_istream_t(fpath));
#endif
} else {
this->serial_schedule_file_ = std::unique_ptr<std::istream>(
new std::ifstream(fpath, std::ifstream::binary));
}
if (this->serial_schedule_file_ && !*serial_schedule_file_) {
this->error_string_ =
"Failed to open serial schedule file " + fpath;
return nullptr;
}
} else if (fname == DRMEMTRACE_CPU_SCHEDULE_FILENAME) {
#ifdef HAS_ZIP
this->cpu_schedule_file_ =
std::unique_ptr<std::istream>(new zipfile_istream_t(fpath));
#endif
}
}
}
}
return new invariant_checker_t(op_offline.get_value(), op_verbose.get_value(),
op_test_mode_name.get_value(),
serial_schedule_file_.get(), cpu_schedule_file_.get(),
op_abort_on_invariant_error.get_value());
}
template <>
analysis_tool_t *
analyzer_multi_t::create_analysis_tool_from_options(const std::string &tool)
{
if (tool == CPU_CACHE || tool == CPU_CACHE_ALT || tool == CPU_CACHE_LEGACY) {
const std::string &config_file = op_config_file.get_value();
if (!config_file.empty()) {
return cache_simulator_create(config_file);
} else {
cache_simulator_knobs_t *knobs = get_cache_simulator_knobs();
return cache_simulator_create(*knobs);
}
} else if (tool == MISS_ANALYZER) {
cache_simulator_knobs_t *knobs = get_cache_simulator_knobs();
return cache_miss_analyzer_create(*knobs, op_miss_count_threshold.get_value(),
op_miss_frac_threshold.get_value(),
op_confidence_threshold.get_value());
} else if (tool == TLB || tool == TLB_LEGACY) {
tlb_simulator_knobs_t knobs;
knobs.num_cores = op_num_cores.get_value();
knobs.page_size = op_page_size.get_value();
knobs.TLB_L1I_entries = op_TLB_L1I_entries.get_value();
knobs.TLB_L1D_entries = op_TLB_L1D_entries.get_value();
knobs.TLB_L1I_assoc = op_TLB_L1I_assoc.get_value();
knobs.TLB_L1D_assoc = op_TLB_L1D_assoc.get_value();
knobs.TLB_L2_entries = op_TLB_L2_entries.get_value();
knobs.TLB_L2_assoc = op_TLB_L2_assoc.get_value();
knobs.TLB_replace_policy = op_TLB_replace_policy.get_value();
knobs.skip_refs = op_skip_refs.get_value();
knobs.warmup_refs = op_warmup_refs.get_value();
knobs.warmup_fraction = op_warmup_fraction.get_value();
knobs.sim_refs = op_sim_refs.get_value();
knobs.verbose = op_verbose.get_value();
knobs.cpu_scheduling = op_cpu_scheduling.get_value();
knobs.use_physical = op_use_physical.get_value();
return tlb_simulator_create(knobs);
} else if (tool == HISTOGRAM) {
return histogram_tool_create(op_line_size.get_value(), op_report_top.get_value(),
op_verbose.get_value());
} else if (tool == REUSE_DIST) {
reuse_distance_knobs_t knobs;
knobs.line_size = op_line_size.get_value();
knobs.report_histogram = op_reuse_distance_histogram.get_value();
knobs.distance_threshold = op_reuse_distance_threshold.get_value();
knobs.report_top = op_report_top.get_value();
knobs.skip_list_distance = op_reuse_skip_dist.get_value();
knobs.distance_limit = op_reuse_distance_limit.get_value();
knobs.verify_skip = op_reuse_verify_skip.get_value();
knobs.histogram_bin_multiplier = op_reuse_histogram_bin_multiplier.get_value();
if (knobs.histogram_bin_multiplier < 1.0) {
ERRMSG("Usage error: reuse_histogram_bin_multiplier must be >= 1.0\n");
return nullptr;
}
knobs.verbose = op_verbose.get_value();
return reuse_distance_tool_create(knobs);
} else if (tool == REUSE_TIME) {
return reuse_time_tool_create(op_line_size.get_value(), op_verbose.get_value());
} else if (tool == BASIC_COUNTS) {
return basic_counts_tool_create(op_verbose.get_value());
} else if (tool == OPCODE_MIX) {
std::string module_file_path = get_module_file_path();
if (module_file_path.empty() && op_indir.get_value().empty() &&
op_infile.get_value().empty() && !op_instr_encodings.get_value()) {
ERRMSG("Usage error: the opcode_mix tool requires offline traces, or "
"-instr_encodings for online traces.\n");
return nullptr;
}
return opcode_mix_tool_create(module_file_path, op_verbose.get_value(),
op_alt_module_dir.get_value());
} else if (tool == SYSCALL_MIX) {
return syscall_mix_tool_create(op_verbose.get_value());
} else if (tool == VIEW) {
std::string module_file_path = get_module_file_path();
// The module file is optional so we don't check for emptiness.
return view_tool_create(module_file_path, op_skip_refs.get_value(),
op_sim_refs.get_value(), op_view_syntax.get_value(),
op_verbose.get_value(), op_alt_module_dir.get_value());
} else if (tool == FUNC_VIEW) {
std::string funclist_file_path = get_aux_file_path(
op_funclist_file.get_value(), DRMEMTRACE_FUNCTION_LIST_FILENAME);
if (funclist_file_path.empty()) {
ERRMSG("Usage error: the func_view tool requires offline traces.\n");
return nullptr;
}
return func_view_tool_create(funclist_file_path, op_show_func_trace.get_value(),
op_verbose.get_value());
} else if (tool == INVARIANT_CHECKER) {
return create_invariant_checker();
} else if (tool == SCHEDULE_STATS) {
return schedule_stats_tool_create(op_schedule_stats_print_every.get_value(),
op_verbose.get_value());
} else {
auto ext_tool = create_external_tool(tool);
if (ext_tool == nullptr) {
ERRMSG("Usage error: unsupported analyzer type \"%s\". "
"Please choose " CPU_CACHE ", " MISS_ANALYZER ", " TLB ", " HISTOGRAM
", " REUSE_DIST ", " BASIC_COUNTS ", " OPCODE_MIX ", " SYSCALL_MIX
", " VIEW ", " FUNC_VIEW ", or some external analyzer.\n",
tool.c_str());
}
return ext_tool;
}
}
/******************************************************************************
* Specializations for analyzer_multi_tmpl_t<trace_entry_t, record_reader_t>, aka
* record_analyzer_multi_t.
*/
template <>
std::unique_ptr<record_reader_t>
record_analyzer_multi_t::create_ipc_reader(const char *name, int verbose)
{
error_string_ = "Online analysis is not supported for record_filter";
ERRMSG("%s\n", error_string_.c_str());
return std::unique_ptr<record_reader_t>();
}
template <>
std::unique_ptr<record_reader_t>
record_analyzer_multi_t::create_ipc_reader_end()
{
error_string_ = "Online analysis is not supported for record_filter";
ERRMSG("%s\n", error_string_.c_str());
return std::unique_ptr<record_reader_t>();
}
template <>
record_analysis_tool_t *
record_analyzer_multi_t::create_external_tool(const std::string &tool_name)
{
error_string_ = "External tools are not supported for record analysis";
ERRMSG("%s\n", error_string_.c_str());
return nullptr;
}
template <>
record_analysis_tool_t *
record_analyzer_multi_t::create_invariant_checker()
{
error_string_ = "Invariant checker is not supported for record analysis";
ERRMSG("%s\n", error_string_.c_str());
return nullptr;
}
template <>
record_analysis_tool_t *
record_analyzer_multi_t::create_analysis_tool_from_options(const std::string &tool)
{
if (tool == RECORD_FILTER) {
return record_filter_tool_create(
op_outdir.get_value(), op_filter_stop_timestamp.get_value(),
op_filter_cache_size.get_value(), op_filter_trace_types.get_value(),
op_filter_marker_types.get_value(), op_trim_before_timestamp.get_value(),
op_trim_after_timestamp.get_value(), op_encodings2regdeps.get_value(),
op_filter_func_ids.get_value(), op_verbose.get_value());
}
ERRMSG("Usage error: unsupported record analyzer type \"%s\". Only " RECORD_FILTER
" is supported.\n",
tool.c_str());
return nullptr;
}
/********************************************************************
* Other analyzer_multi_tmpl_t routines that do not need to be specialized.
*/
template <typename RecordType, typename ReaderType>
analyzer_multi_tmpl_t<RecordType, ReaderType>::analyzer_multi_tmpl_t()
{
this->worker_count_ = op_jobs.get_value();
this->skip_instrs_ = op_skip_instrs.get_value();
this->interval_microseconds_ = op_interval_microseconds.get_value();
this->interval_instr_count_ = op_interval_instr_count.get_value();
// Initial measurements show it's sometimes faster to keep the parallel model
// of using single-file readers but use them sequentially, as opposed to
// the every-file interleaving reader, but the user can specify -jobs 1, so
// we still keep the serial vs parallel split for 0.
if (this->worker_count_ == 0)
this->parallel_ = false;
if (!op_indir.get_value().empty() || !op_infile.get_value().empty())
op_offline.set_value(true); // Some tools check this on post-proc runs.
// XXX: add a "required" flag to droption to avoid needing this here
if (op_indir.get_value().empty() && op_infile.get_value().empty() &&
op_ipc_name.get_value().empty()) {
this->error_string_ =
"Usage error: -ipc_name or -indir or -infile is required\nUsage:\n" +
droption_parser_t::usage_short(DROPTION_SCOPE_ALL);
this->success_ = false;
return;
}
if (!op_indir.get_value().empty()) {
std::string tracedir =
raw2trace_directory_t::tracedir_from_rawdir(op_indir.get_value());
// We support the trace dir being empty if we haven't post-processed
// the raw files yet.
bool needs_processing = false;
if (!directory_iterator_t::is_directory(tracedir))
needs_processing = true;
else {
directory_iterator_t end;
directory_iterator_t iter(tracedir);
if (!iter) {
needs_processing = true;
} else {
int count = 0;
for (; iter != end; ++iter) {
if ((*iter) == "." || (*iter) == ".." ||
starts_with(*iter, DRMEMTRACE_SERIAL_SCHEDULE_FILENAME) ||
*iter == DRMEMTRACE_CPU_SCHEDULE_FILENAME)
continue;
++count;
// XXX: It would be nice to call file_reader_t::is_complete()
// but we don't have support for that for compressed files.
// Thus it's up to the user to delete incomplete processed files.
}
if (count == 0)
needs_processing = true;
}
}
if (needs_processing) {
raw2trace_directory_t dir(op_verbose.get_value());
std::string dir_err =
dir.initialize(op_indir.get_value(), "", op_trace_compress.get_value(),
op_syscall_template_file.get_value());
if (!dir_err.empty()) {
this->success_ = false;
this->error_string_ = "Directory setup failed: " + dir_err;
return;
}
raw2trace_t raw2trace(
dir.modfile_bytes_, dir.in_files_, dir.out_files_, dir.out_archives_,
dir.encoding_file_, dir.serial_schedule_file_, dir.cpu_schedule_file_,
nullptr, op_verbose.get_value(), op_jobs.get_value(),
op_alt_module_dir.get_value(), op_chunk_instr_count.get_value(),
dir.in_kfiles_map_, dir.kcoredir_, dir.kallsymsdir_,
std::move(dir.syscall_template_file_reader_));
std::string error = raw2trace.do_conversion();
if (!error.empty()) {
this->success_ = false;
this->error_string_ = "raw2trace failed: " + error;
}
}
}
// Create the tools after post-processing so we have the schedule files for
// test_mode.
if (!create_analysis_tools()) {
this->success_ = false;
this->error_string_ = "Failed to create analysis tool:" + this->error_string_;
return;
}
typename sched_type_t::scheduler_options_t sched_ops;
if (op_core_sharded.get_value() || op_core_serial.get_value()) {
if (op_core_serial.get_value()) {
this->parallel_ = false;
}
sched_ops = init_dynamic_schedule();
}
if (!op_indir.get_value().empty()) {
std::string tracedir =
raw2trace_directory_t::tracedir_from_rawdir(op_indir.get_value());
if (!this->init_scheduler(tracedir, op_only_thread.get_value(),
op_verbose.get_value(), std::move(sched_ops)))
this->success_ = false;
} else if (op_infile.get_value().empty()) {
// XXX i#3323: Add parallel analysis support for online tools.
this->parallel_ = false;
auto reader =
create_ipc_reader(op_ipc_name.get_value().c_str(), op_verbose.get_value());
if (!reader) {
this->error_string_ = "Failed to create IPC reader: " + this->error_string_;
this->success_ = false;
return;
}
auto end = create_ipc_reader_end();
// We do not want the scheduler's init() to block.
sched_ops.read_inputs_in_init = false;
if (!this->init_scheduler(std::move(reader), std::move(end),
op_verbose.get_value(), std::move(sched_ops))) {
this->success_ = false;
}
} else {
// Legacy file.
if (!this->init_scheduler(op_infile.get_value(),
INVALID_THREAD_ID /*all threads*/,
op_verbose.get_value(), std::move(sched_ops)))
this->success_ = false;
}
if (!init_analysis_tools()) {
this->success_ = false;
return;
}
// We can't call serial_trace_iter_->init() here as it blocks for ipc_reader_t.
}
template <typename RecordType, typename ReaderType>
analyzer_multi_tmpl_t<RecordType, ReaderType>::~analyzer_multi_tmpl_t()
{
#ifdef HAS_ZIP
if (!op_record_file.get_value().empty()) {
if (this->scheduler_.write_recorded_schedule() != sched_type_t::STATUS_SUCCESS) {
ERRMSG("Failed to write schedule to %s", op_record_file.get_value().c_str());
}
}
#endif
destroy_analysis_tools();
}
template <typename RecordType, typename ReaderType>
typename scheduler_tmpl_t<RecordType, ReaderType>::scheduler_options_t
analyzer_multi_tmpl_t<RecordType, ReaderType>::init_dynamic_schedule()
{
this->shard_type_ = SHARD_BY_CORE;
this->worker_count_ = op_num_cores.get_value();
typename sched_type_t::scheduler_options_t sched_ops(
sched_type_t::MAP_TO_ANY_OUTPUT,
op_sched_order_time.get_value() ? sched_type_t::DEPENDENCY_TIMESTAMPS
: sched_type_t::DEPENDENCY_IGNORE,
sched_type_t::SCHEDULER_DEFAULTS, op_verbose.get_value());
sched_ops.quantum_duration = op_sched_quantum.get_value();
if (op_sched_time.get_value())
sched_ops.quantum_unit = sched_type_t::QUANTUM_TIME;
sched_ops.syscall_switch_threshold = op_sched_syscall_switch_us.get_value();
sched_ops.blocking_switch_threshold = op_sched_blocking_switch_us.get_value();
sched_ops.block_time_scale = op_sched_block_scale.get_value();
sched_ops.block_time_max = op_sched_block_max_us.get_value();
sched_ops.randomize_next_input = op_sched_randomize.get_value();
sched_ops.honor_direct_switches = !op_sched_disable_direct_switches.get_value();
#ifdef HAS_ZIP
if (!op_record_file.get_value().empty()) {
record_schedule_zip_.reset(new zipfile_ostream_t(op_record_file.get_value()));
sched_ops.schedule_record_ostream = record_schedule_zip_.get();
} else if (!op_replay_file.get_value().empty()) {
replay_schedule_zip_.reset(new zipfile_istream_t(op_replay_file.get_value()));
sched_ops.schedule_replay_istream = replay_schedule_zip_.get();
sched_ops.mapping = sched_type_t::MAP_AS_PREVIOUSLY;
sched_ops.deps = sched_type_t::DEPENDENCY_TIMESTAMPS;
} else if (!op_cpu_schedule_file.get_value().empty()) {
cpu_schedule_zip_.reset(new zipfile_istream_t(op_cpu_schedule_file.get_value()));
sched_ops.mapping = sched_type_t::MAP_TO_RECORDED_OUTPUT;
sched_ops.deps = sched_type_t::DEPENDENCY_TIMESTAMPS;
sched_ops.replay_as_traced_istream = cpu_schedule_zip_.get();
}
#endif
sched_ops.kernel_switch_trace_path = op_sched_switch_file.get_value();
return sched_ops;
}
template <typename RecordType, typename ReaderType>
bool
analyzer_multi_tmpl_t<RecordType, ReaderType>::create_analysis_tools()
{
this->tools_ = new analysis_tool_tmpl_t<RecordType> *[this->max_num_tools_];
if (!op_tool.get_value().empty()) {
std::stringstream stream(op_tool.get_value());
std::string type;
while (std::getline(stream, type, ':')) {
if (this->num_tools_ >= this->max_num_tools_ - 1) {
this->error_string_ = "Only " + std::to_string(this->max_num_tools_ - 1) +
" simulators are allowed simultaneously";
return false;
}
auto tool = create_analysis_tool_from_options(type);
if (tool == NULL)
continue;
if (!*tool) {
std::string tool_error = tool->get_error_string();
if (tool_error.empty())
tool_error = "no error message provided.";
this->error_string_ = "Tool failed to initialize: " + tool_error;
delete tool;
return false;
}
this->tools_[this->num_tools_++] = tool;
}
}
if (op_test_mode.get_value()) {
// This will return nullptr for record_ instantiation; we just don't support
// -test_mode for record_.
this->tools_[this->num_tools_] = create_invariant_checker();
if (this->tools_[this->num_tools_] == NULL)
return false;
if (!*this->tools_[this->num_tools_]) {
this->error_string_ = this->tools_[this->num_tools_]->get_error_string();
delete this->tools_[this->num_tools_];
this->tools_[this->num_tools_] = NULL;
return false;
}
this->num_tools_++;
}
return (this->num_tools_ != 0);
}
template <typename RecordType, typename ReaderType>
bool
analyzer_multi_tmpl_t<RecordType, ReaderType>::init_analysis_tools()
{
// initialize_stream() is now called from analyzer_t::run().
return true;
}
template <typename RecordType, typename ReaderType>
void
analyzer_multi_tmpl_t<RecordType, ReaderType>::destroy_analysis_tools()
{
if (!this->success_)
return;
for (int i = 0; i < this->num_tools_; i++)
delete this->tools_[i];
delete[] this->tools_;
}
/* Get the path to an auxiliary file by examining
* 1. The corresponding command line option
* 2. The trace directory
* If a trace file is provided instead of a trace directory, it searches in the
* directory which contains the trace file.
*/
template <typename RecordType, typename ReaderType>
std::string
analyzer_multi_tmpl_t<RecordType, ReaderType>::get_aux_file_path(
std::string option_val, std::string default_filename)
{
std::string file_path;
if (!option_val.empty())
file_path = option_val;
else {
std::string trace_dir;
if (!op_indir.get_value().empty())
trace_dir = op_indir.get_value();
else {
if (op_infile.get_value().empty()) {
return "";
}
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);
}
if (raw2trace_directory_t::is_window_subdir(trace_dir)) {
// If we're operating on a specific window, point at the parent for the
// modfile.
trace_dir += std::string(DIRSEP) + "..";
}
file_path = trace_dir + std::string(DIRSEP) + default_filename;
/* Support the aux file in either raw/ or trace/. */
if (!std::ifstream(file_path.c_str()).good()) {
file_path = trace_dir + std::string(DIRSEP) + TRACE_SUBDIR +
std::string(DIRSEP) + default_filename;
}
if (!std::ifstream(file_path.c_str()).good()) {
file_path = trace_dir + std::string(DIRSEP) + OUTFILE_SUBDIR +
std::string(DIRSEP) + default_filename;
}
}
if (!std::ifstream(file_path.c_str()).good())
return "";
return file_path;
}
template <typename RecordType, typename ReaderType>
std::string
analyzer_multi_tmpl_t<RecordType, ReaderType>::get_module_file_path()
{
return get_aux_file_path(op_module_file.get_value(), DRMEMTRACE_MODULE_LIST_FILENAME);
}
/* Get the cache simulator knobs used by the cache simulator
* and the cache miss analyzer.
*/
template <typename RecordType, typename ReaderType>
cache_simulator_knobs_t *
analyzer_multi_tmpl_t<RecordType, ReaderType>::get_cache_simulator_knobs()
{
cache_simulator_knobs_t *knobs = new cache_simulator_knobs_t;
knobs->num_cores = op_num_cores.get_value();
knobs->line_size = op_line_size.get_value();
knobs->L1I_size = op_L1I_size.get_value();
knobs->L1D_size = op_L1D_size.get_value();
knobs->L1I_assoc = op_L1I_assoc.get_value();
knobs->L1D_assoc = op_L1D_assoc.get_value();
knobs->LL_size = op_LL_size.get_value();
knobs->LL_assoc = op_LL_assoc.get_value();
knobs->LL_miss_file = op_LL_miss_file.get_value();
knobs->model_coherence = op_coherence.get_value();
knobs->replace_policy = op_replace_policy.get_value();
knobs->data_prefetcher = op_data_prefetcher.get_value();
knobs->skip_refs = op_skip_refs.get_value();
knobs->warmup_refs = op_warmup_refs.get_value();
knobs->warmup_fraction = op_warmup_fraction.get_value();
knobs->sim_refs = op_sim_refs.get_value();
knobs->verbose = op_verbose.get_value();
knobs->cpu_scheduling = op_cpu_scheduling.get_value();
knobs->use_physical = op_use_physical.get_value();
return knobs;
}
template class analyzer_multi_tmpl_t<memref_t, reader_t>;
template class analyzer_multi_tmpl_t<trace_entry_t,
dynamorio::drmemtrace::record_reader_t>;
} // namespace drmemtrace
} // namespace dynamorio