diff --git a/clients/drcachesim/analyzer_multi.cpp b/clients/drcachesim/analyzer_multi.cpp index 8e3cd28db18..a4650803e53 100644 --- a/clients/drcachesim/analyzer_multi.cpp +++ b/clients/drcachesim/analyzer_multi.cpp @@ -72,6 +72,26 @@ namespace drmemtrace { using ::dynamorio::droption::droption_parser_t; using ::dynamorio::droption::DROPTION_SCOPE_ALL; +/**************************************************************** + * Specializations for analyzer_multi_tmpl_t, + * aka analyzer_multi_t. + */ + +template <> +std::unique_ptr +analyzer_multi_t::create_ipc_reader(const char *name, int verbose) +{ + return std::unique_ptr(new ipc_reader_t(name, verbose)); +} + +template <> +std::unique_ptr +analyzer_multi_t::create_ipc_reader_end() +{ + return std::unique_ptr(new ipc_reader_t()); +} + +template <> analysis_tool_t * analyzer_multi_t::create_external_tool(const std::string &tool_name) { @@ -106,26 +126,235 @@ analyzer_multi_t::create_external_tool(const std::string &tool_name) return tool; } -analyzer_multi_t::analyzer_multi_t() +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(new gzip_istream_t(fpath)); +#endif + } else { + this->serial_schedule_file_ = std::unique_ptr( + 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(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()); +} + +template <> +analysis_tool_t * +analyzer_multi_t::create_analysis_tool_from_options(const std::string &simulator_type) +{ + if (simulator_type == CPU_CACHE) { + 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 (simulator_type == 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 (simulator_type == TLB) { + 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 (simulator_type == HISTOGRAM) { + return histogram_tool_create(op_line_size.get_value(), op_report_top.get_value(), + op_verbose.get_value()); + } else if (simulator_type == 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 (simulator_type == REUSE_TIME) { + return reuse_time_tool_create(op_line_size.get_value(), op_verbose.get_value()); + } else if (simulator_type == BASIC_COUNTS) { + return basic_counts_tool_create(op_verbose.get_value()); + } else if (simulator_type == 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 (simulator_type == SYSCALL_MIX) { + return syscall_mix_tool_create(op_verbose.get_value()); + } else if (simulator_type == 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 (simulator_type == 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 (simulator_type == INVARIANT_CHECKER) { + return create_invariant_checker(); + } else if (simulator_type == SCHEDULE_STATS) { + return schedule_stats_tool_create(op_schedule_stats_print_every.get_value(), + op_verbose.get_value()); + } else { + auto tool = create_external_tool(simulator_type); + if (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", + simulator_type.c_str()); + } + return tool; + } +} + +/****************************************************************************** + * Specializations for analyzer_multi_tmpl_t, aka + * record_analyzer_multi_t. + */ + +template <> +std::unique_ptr +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(); +} + +template <> +std::unique_ptr +record_analyzer_multi_t::create_ipc_reader_end() { - worker_count_ = op_jobs.get_value(); - skip_instrs_ = op_skip_instrs.get_value(); - interval_microseconds_ = op_interval_microseconds.get_value(); + error_string_ = "Online analysis is not supported for record_filter"; + ERRMSG("%s\n", error_string_.c_str()); + return std::unique_ptr(); +} + +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 &simulator_type) +{ + // TODO i#6635: create the record_filter tool if requested. + return nullptr; +} + +/******************************************************************** + * Other analyzer_multi_tmpl_t routines that do not need to be specialized. + */ + +template +analyzer_multi_tmpl_t::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(); // 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 (worker_count_ == 0) - parallel_ = false; + 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()) { - error_string_ = + this->error_string_ = "Usage error: -ipc_name or -indir or -infile is required\nUsage:\n" + droption_parser_t::usage_short(DROPTION_SCOPE_ALL); - success_ = false; + this->success_ = false; return; } if (!op_indir.get_value().empty()) { @@ -163,8 +392,8 @@ analyzer_multi_t::analyzer_multi_t() dir.initialize(op_indir.get_value(), "", op_trace_compress.get_value(), op_syscall_template_file.get_value()); if (!dir_err.empty()) { - success_ = false; - error_string_ = "Directory setup failed: " + dir_err; + this->success_ = false; + this->error_string_ = "Directory setup failed: " + dir_err; return; } raw2trace_t raw2trace( @@ -176,23 +405,23 @@ analyzer_multi_t::analyzer_multi_t() std::move(dir.syscall_template_file_reader_)); std::string error = raw2trace.do_conversion(); if (!error.empty()) { - success_ = false; - error_string_ = "raw2trace failed: " + error; + 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()) { - success_ = false; - error_string_ = "Failed to create analysis tool:" + error_string_; + this->success_ = false; + this->error_string_ = "Failed to create analysis tool:" + this->error_string_; return; } - scheduler_t::scheduler_options_t sched_ops; + 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()) { - parallel_ = false; + this->parallel_ = false; } sched_ops = init_dynamic_schedule(); } @@ -200,38 +429,45 @@ analyzer_multi_t::analyzer_multi_t() if (!op_indir.get_value().empty()) { std::string tracedir = raw2trace_directory_t::tracedir_from_rawdir(op_indir.get_value()); - if (!init_scheduler(tracedir, op_only_thread.get_value(), op_verbose.get_value(), - std::move(sched_ops))) - success_ = false; + 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. - parallel_ = false; - auto reader = std::unique_ptr( - new ipc_reader_t(op_ipc_name.get_value().c_str(), op_verbose.get_value())); - auto end = std::unique_ptr(new ipc_reader_t()); - if (!init_scheduler(std::move(reader), std::move(end), op_verbose.get_value(), - std::move(sched_ops))) { - success_ = false; + 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(); + 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 (!init_scheduler(op_infile.get_value(), INVALID_THREAD_ID /*all threads*/, - op_verbose.get_value(), std::move(sched_ops))) - success_ = false; + 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()) { - success_ = false; + this->success_ = false; return; } // We can't call serial_trace_iter_->init() here as it blocks for ipc_reader_t. } -analyzer_multi_t::~analyzer_multi_t() +template +analyzer_multi_tmpl_t::~analyzer_multi_tmpl_t() { #ifdef HAS_ZIP if (!op_record_file.get_value().empty()) { - if (scheduler_.write_recorded_schedule() != scheduler_t::STATUS_SUCCESS) { + 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()); } } @@ -239,19 +475,20 @@ analyzer_multi_t::~analyzer_multi_t() destroy_analysis_tools(); } -scheduler_t::scheduler_options_t -analyzer_multi_t::init_dynamic_schedule() +template +typename scheduler_tmpl_t::scheduler_options_t +analyzer_multi_tmpl_t::init_dynamic_schedule() { - shard_type_ = SHARD_BY_CORE; - worker_count_ = op_num_cores.get_value(); - scheduler_t::scheduler_options_t sched_ops( - scheduler_t::MAP_TO_ANY_OUTPUT, - op_sched_order_time.get_value() ? scheduler_t::DEPENDENCY_TIMESTAMPS - : scheduler_t::DEPENDENCY_IGNORE, - scheduler_t::SCHEDULER_DEFAULTS, op_verbose.get_value()); + 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 = scheduler_t::QUANTUM_TIME; + 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(); @@ -264,12 +501,12 @@ analyzer_multi_t::init_dynamic_schedule() } 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 = scheduler_t::MAP_AS_PREVIOUSLY; - sched_ops.deps = scheduler_t::DEPENDENCY_TIMESTAMPS; + 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 = scheduler_t::MAP_TO_RECORDED_OUTPUT; - sched_ops.deps = scheduler_t::DEPENDENCY_TIMESTAMPS; + 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 @@ -277,16 +514,17 @@ analyzer_multi_t::init_dynamic_schedule() return sched_ops; } +template bool -analyzer_multi_t::create_analysis_tools() +analyzer_multi_tmpl_t::create_analysis_tools() { - tools_ = new analysis_tool_t *[max_num_tools_]; + this->tools_ = new analysis_tool_tmpl_t *[this->max_num_tools_]; if (!op_simulator_type.get_value().empty()) { std::stringstream stream(op_simulator_type.get_value()); std::string type; while (std::getline(stream, type, ':')) { - if (num_tools_ >= max_num_tools_ - 1) { - error_string_ = "Only " + std::to_string(max_num_tools_ - 1) + + 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; } @@ -297,196 +535,49 @@ analyzer_multi_t::create_analysis_tools() std::string tool_error = tool->get_error_string(); if (tool_error.empty()) tool_error = "no error message provided."; - error_string_ = "Tool failed to initialize: " + tool_error; + this->error_string_ = "Tool failed to initialize: " + tool_error; delete tool; return false; } - tools_[num_tools_++] = tool; + this->tools_[this->num_tools_++] = tool; } } if (op_test_mode.get_value()) { - tools_[num_tools_] = create_invariant_checker(); - if (tools_[num_tools_] == NULL) + // 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 (!*tools_[num_tools_]) { - error_string_ = tools_[num_tools_]->get_error_string(); - delete tools_[num_tools_]; - tools_[num_tools_] = NULL; + 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; } - num_tools_++; + this->num_tools_++; } - return (num_tools_ != 0); + return (this->num_tools_ != 0); } +template bool -analyzer_multi_t::init_analysis_tools() +analyzer_multi_tmpl_t::init_analysis_tools() { // initialize_stream() is now called from analyzer_t::run(). return true; } +template void -analyzer_multi_t::destroy_analysis_tools() +analyzer_multi_tmpl_t::destroy_analysis_tools() { - if (!success_) + if (!this->success_) return; - for (int i = 0; i < num_tools_; i++) - delete tools_[i]; - delete[] tools_; -} - -analysis_tool_t * -analyzer_multi_t::create_analysis_tool_from_options(const std::string &simulator_type) -{ - if (simulator_type == CPU_CACHE) { - 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 (simulator_type == 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 (simulator_type == TLB) { - 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 (simulator_type == HISTOGRAM) { - return histogram_tool_create(op_line_size.get_value(), op_report_top.get_value(), - op_verbose.get_value()); - } else if (simulator_type == 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 (simulator_type == REUSE_TIME) { - return reuse_time_tool_create(op_line_size.get_value(), op_verbose.get_value()); - } else if (simulator_type == BASIC_COUNTS) { - return basic_counts_tool_create(op_verbose.get_value()); - } else if (simulator_type == 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 (simulator_type == SYSCALL_MIX) { - return syscall_mix_tool_create(op_verbose.get_value()); - } else if (simulator_type == 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 (simulator_type == 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 (simulator_type == INVARIANT_CHECKER) { - return create_invariant_checker(); - } else if (simulator_type == SCHEDULE_STATS) { - return schedule_stats_tool_create(op_schedule_stats_print_every.get_value(), - op_verbose.get_value()); - } else { - auto tool = create_external_tool(simulator_type); - if (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", - simulator_type.c_str()); - } - return tool; - } -} - -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) { - 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 - serial_schedule_file_ = - std::unique_ptr(new gzip_istream_t(fpath)); -#endif - } else { - serial_schedule_file_ = std::unique_ptr( - new std::ifstream(fpath, std::ifstream::binary)); - } - if (serial_schedule_file_ && !*serial_schedule_file_) { - error_string_ = "Failed to open serial schedule file " + fpath; - return nullptr; - } - } else if (fname == DRMEMTRACE_CPU_SCHEDULE_FILENAME) { -#ifdef HAS_ZIP - cpu_schedule_file_ = - std::unique_ptr(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()); + 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 @@ -495,8 +586,10 @@ analyzer_multi_t::create_invariant_checker() * If a trace file is provided instead of a trace directory, it searches in the * directory which contains the trace file. */ +template std::string -analyzer_multi_t::get_aux_file_path(std::string option_val, std::string default_filename) +analyzer_multi_tmpl_t::get_aux_file_path( + std::string option_val, std::string default_filename) { std::string file_path; if (!option_val.empty()) @@ -534,8 +627,9 @@ analyzer_multi_t::get_aux_file_path(std::string option_val, std::string default_ return file_path; } +template std::string -analyzer_multi_t::get_module_file_path() +analyzer_multi_tmpl_t::get_module_file_path() { return get_aux_file_path(op_module_file.get_value(), DRMEMTRACE_MODULE_LIST_FILENAME); } @@ -543,8 +637,9 @@ analyzer_multi_t::get_module_file_path() /* Get the cache simulator knobs used by the cache simulator * and the cache miss analyzer. */ +template cache_simulator_knobs_t * -analyzer_multi_t::get_cache_simulator_knobs() +analyzer_multi_tmpl_t::get_cache_simulator_knobs() { cache_simulator_knobs_t *knobs = new cache_simulator_knobs_t; knobs->num_cores = op_num_cores.get_value(); @@ -569,5 +664,9 @@ analyzer_multi_t::get_cache_simulator_knobs() return knobs; } +template class analyzer_multi_tmpl_t; +template class analyzer_multi_tmpl_t; + } // namespace drmemtrace } // namespace dynamorio diff --git a/clients/drcachesim/analyzer_multi.h b/clients/drcachesim/analyzer_multi.h index 5d8e5068c42..4699e09c4ea 100644 --- a/clients/drcachesim/analyzer_multi.h +++ b/clients/drcachesim/analyzer_multi.h @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2016-2023 Google, Inc. All rights reserved. + * Copyright (c) 2016-2024 Google, Inc. All rights reserved. * **********************************************************/ /* @@ -45,15 +45,18 @@ namespace dynamorio { namespace drmemtrace { -class analyzer_multi_t : public analyzer_t { +template +class analyzer_multi_tmpl_t : public analyzer_tmpl_t { public: // Usage: errors encountered during the constructor will set a flag that should // be queried via operator!. - analyzer_multi_t(); - virtual ~analyzer_multi_t(); + analyzer_multi_tmpl_t(); + virtual ~analyzer_multi_tmpl_t(); protected: - scheduler_t::scheduler_options_t + typedef scheduler_tmpl_t sched_type_t; + + typename scheduler_tmpl_t::scheduler_options_t init_dynamic_schedule(); bool create_analysis_tools(); @@ -62,13 +65,19 @@ class analyzer_multi_t : public analyzer_t { void destroy_analysis_tools(); - analysis_tool_t * + std::unique_ptr + create_ipc_reader(const char *name, int verbose); + + std::unique_ptr + create_ipc_reader_end(); + + analysis_tool_tmpl_t * create_analysis_tool_from_options(const std::string &type); - analysis_tool_t * + analysis_tool_tmpl_t * create_external_tool(const std::string &id); - analysis_tool_t * + analysis_tool_tmpl_t * create_invariant_checker(); std::string @@ -96,6 +105,11 @@ class analyzer_multi_t : public analyzer_t { static const int max_num_tools_ = 8; }; +typedef analyzer_multi_tmpl_t analyzer_multi_t; + +typedef analyzer_multi_tmpl_t + record_analyzer_multi_t; + } // namespace drmemtrace } // namespace dynamorio