blob: e1a8bb7a481571b44892a8b5db56382d1512094a [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2022-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.
*/
/* Standalone record filter tool launcher for file traces. */
#ifdef WINDOWS
# define NOMINMAX // Avoid windows.h messing up std::max.
# define UNICODE // For Windows headers.
# define _UNICODE // For C headers.
#endif
#include "analyzer.h"
#include "droption.h"
#include "dr_frontend.h"
#include "tools/filter/record_filter_create.h"
#include "tests/test_helpers.h"
#include <limits>
using ::dynamorio::drmemtrace::disable_popups;
using ::dynamorio::drmemtrace::record_analysis_tool_t;
using ::dynamorio::drmemtrace::record_analyzer_t;
using ::dynamorio::drmemtrace::trace_marker_type_t;
using ::dynamorio::drmemtrace::trace_type_t;
using ::dynamorio::droption::droption_parser_t;
using ::dynamorio::droption::DROPTION_SCOPE_ALL;
using ::dynamorio::droption::DROPTION_SCOPE_FRONTEND;
using ::dynamorio::droption::droption_t;
namespace {
#define FATAL_ERROR(msg, ...) \
do { \
fprintf(stderr, "ERROR: " msg "\n", ##__VA_ARGS__); \
fflush(stderr); \
exit(1); \
} while (0)
static droption_t<std::string>
op_trace_dir(DROPTION_SCOPE_FRONTEND, "trace_dir", "",
"[Required] Trace input directory",
"Specifies the directory containing the trace files to be filtered.");
static droption_t<std::string>
op_output_dir(DROPTION_SCOPE_FRONTEND, "output_dir", "",
"[Required] Output directory for the filtered trace",
"Specifies the directory where the filtered trace will be written.");
static droption_t<unsigned int> op_verbose(DROPTION_SCOPE_ALL, "verbose", 0, 0, 64,
"Verbosity level",
"Verbosity level for notifications.");
static droption_t<uint64_t>
// Wrap max in parens to work around Visual Studio compiler issues with the
// max macro (even despite NOMINMAX defined above).
op_stop_timestamp(DROPTION_SCOPE_ALL, "stop_timestamp", 0, 0,
(std::numeric_limits<uint64_t>::max)(),
"Timestamp (in us) in the trace when to stop filtering.",
"Record filtering will be disabled (everything will be output) "
"when the tool sees a TRACE_MARKER_TYPE_TIMESTAMP marker with "
"timestamp greater than the specified value.");
static droption_t<int> op_cache_filter_size(
DROPTION_SCOPE_FRONTEND, "cache_filter_size", 0,
"Enable data cache filter with given size (in bytes).",
"Enable data cache filter with given size (in bytes), with 64 byte "
"line size and a direct mapped LRU cache.");
static droption_t<std::string>
op_remove_trace_types(DROPTION_SCOPE_FRONTEND, "remove_trace_types", "",
"Comma-separated integers for trace types to remove.",
"Comma-separated integers for trace types to remove. "
"See trace_type_t for the list of trace entry types.");
static droption_t<std::string>
op_remove_marker_types(DROPTION_SCOPE_FRONTEND, "remove_marker_types", "",
"Comma-separated integers for marker types to remove.",
"Comma-separated integers for marker types to remove. "
"See trace_marker_type_t for the list of marker types.");
static droption_t<uint64_t> op_trim_before_timestamp(
DROPTION_SCOPE_ALL, "trim_before_timestamp", 0, 0,
(std::numeric_limits<uint64_t>::max)(),
"Trim records until this timestamp (in us) in the trace.",
"Removes all records (after headers) before the first TRACE_MARKER_TYPE_TIMESTAMP "
"marker in the trace with timestamp greater than or equal to the specified value.");
static droption_t<uint64_t> op_trim_after_timestamp(
DROPTION_SCOPE_ALL, "trim_after_timestamp", (std::numeric_limits<uint64_t>::max)(), 0,
(std::numeric_limits<uint64_t>::max)(),
"Trim records after this timestamp (in us) in the trace.",
"Removes all records from the first TRACE_MARKER_TYPE_TIMESTAMP marker with "
"timestamp larger than the specified value.");
/* XXX i#6369: we should partition our options by tool. This one should belong to the
* record_filter partition. For now we add the filter_ prefix to options that should be
* used in conjunction with record_filter.
*/
droption_t<bool> op_encodings2regdeps(
DROPTION_SCOPE_FRONTEND, "filter_encodings2regdeps", false,
"Enable converting the encoding of instructions to synthetic ISA DR_ISA_REGDEPS.",
"This option is for -tool record_filter. When present, it converts "
"the encoding of instructions from a real ISA to the DR_ISA_REGDEPS synthetic ISA.");
droption_t<std::string>
op_filter_func_ids(DROPTION_SCOPE_FRONTEND, "filter_keep_func_ids", "",
"Comma-separated integers of function IDs to keep.",
"This option is for -tool record_filter. It preserves "
"TRACE_MARKER_TYPE_FUNC_[ID | ARG | RETVAL | RETADDR] "
"markers for the listed function IDs and removed those "
"belonging to unlisted function IDs.");
} // namespace
int
_tmain(int argc, const TCHAR *targv[])
{
disable_popups();
#if defined(WINDOWS) && !defined(_UNICODE)
# error _UNICODE must be defined
#endif
char **argv;
drfront_status_t sc = drfront_convert_args(targv, &argv, argc);
if (sc != DRFRONT_SUCCESS)
FATAL_ERROR("Failed to process args: %d", sc);
std::string parse_err;
if (!droption_parser_t::parse_argv(DROPTION_SCOPE_FRONTEND, argc, (const char **)argv,
&parse_err, NULL) ||
op_trace_dir.get_value().empty() || op_output_dir.get_value().empty()) {
FATAL_ERROR("Usage error: %s\nUsage:\n%s", parse_err.c_str(),
droption_parser_t::usage_short(DROPTION_SCOPE_ALL).c_str());
}
auto record_filter = std::unique_ptr<record_analysis_tool_t>(
dynamorio::drmemtrace::record_filter_tool_create(
op_output_dir.get_value(), op_stop_timestamp.get_value(),
op_cache_filter_size.get_value(), op_remove_trace_types.get_value(),
op_remove_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()));
std::vector<record_analysis_tool_t *> tools;
tools.push_back(record_filter.get());
record_analyzer_t record_analyzer(
op_trace_dir.get_value(), &tools[0], (int)tools.size(), /*worker_count=*/0,
/*skip_instrs=*/0, /*interval_microseconds=*/0, op_verbose.get_value());
if (!record_analyzer) {
FATAL_ERROR("Failed to initialize trace filter: %s",
record_analyzer.get_error_string().c_str());
}
if (!record_analyzer.run()) {
FATAL_ERROR("Failed to run trace filter: %s",
record_analyzer.get_error_string().c_str());
}
if (!record_analyzer.print_stats()) {
FATAL_ERROR("Failed to print stats: %s",
record_analyzer.get_error_string().c_str());
}
fprintf(stderr, "Done!\n");
return 0;
}