Skip to content

Commit

Permalink
i#6662 public traces, part 6: invariant_checker (DynamoRIO#6861)
Browse files Browse the repository at this point in the history
OFFLINE_FILE_TYPE_ARCH_REGDEPS traces lack some markers and have
instructions with DR_ISA_REGDEPS encodings, which don't have all the
necessary information to perform all the invariant checks.
Here we disable or modify some checks for invariant errors to handle
OFFLINE_FILE_TYPE_ARCH_REGDEPS synthetic traces in the invariant_checker
tool.

Specifically, we disable:
- the check for maybe-blocking syscall markers always being preceded by
a regular syscall marker, as we don't have regular syscall markers in
OFFLINE_FILE_TYPE_ARCH_REGDEPS traces;
- the setting of branch targets, as they are not saved as first source
operand in DR_ISA_REGDEPS instructions;
- the check for "Missing read/write records", as we can't check for
predicated instructions in OFFLINE_FILE_TYPE_ARCH_REGDEPS traces and we
can't infer the precise number of load/stores performed by
DR_ISA_REGDEPS instructions to adjust the read/write counters
accordingly;

We modify the check for matching loads/stores performed by a
DR_ISA_REGDEPS fetched instruction with the subsequent read/write
records, by requiring that if a DR_ISA_REGDEPS instruction performs a
load/store (we use the instruction category to determine that), at least
one subsequent read/write record must be present.

Issue DynamoRIO#6662
  • Loading branch information
edeiana authored Jul 10, 2024
1 parent 8ba103c commit 92a44cc
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Estimation of pi is 3.142425985001098

Trace invariant checks passed

Output .* entries from .* entries.

Trace invariant checks passed

WARNING: invariant_checker is being run on an OFFLINE_FILE_TYPE_ARCH_REGDEPS trace.
Some invariant checks have been disabled.
130 changes: 105 additions & 25 deletions clients/drcachesim/tools/invariant_checker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,18 @@ invariant_checker_t::parallel_shard_init_stream(int shard_index, void *worker_da
auto per_shard = std::unique_ptr<per_shard_t>(new per_shard_t);
per_shard->stream = shard_stream;
void *res = reinterpret_cast<void *>(per_shard.get());
std::lock_guard<std::mutex> guard(shard_map_mutex_);
std::lock_guard<std::mutex> guard(init_mutex_);
per_shard->tid_ = shard_stream->get_tid();
shard_map_[shard_index] = std::move(per_shard);
// If we are dealing with an OFFLINE_FILE_TYPE_ARCH_REGDEPS trace, we need to set the
// dcontext ISA mode to the correct synthetic ISA (i.e., DR_ISA_REGDEPS).
uint64_t filetype = shard_stream->get_filetype();
if (TESTANY(OFFLINE_FILE_TYPE_ARCH_REGDEPS, filetype)) {
dr_isa_mode_t isa_mode = dr_get_isa_mode(drcontext_);
if (isa_mode != DR_ISA_REGDEPS) {
dr_set_isa_mode(drcontext_, DR_ISA_REGDEPS, nullptr);
}
}
return res;
}

Expand All @@ -174,7 +183,13 @@ invariant_checker_t::parallel_shard_exit(void *shard_data)
// that out we disable this error for Windows online.
IF_WINDOWS(|| !knob_offline_),
"Thread is missing exit");
if (!TESTANY(OFFLINE_FILE_TYPE_FILTERED | OFFLINE_FILE_TYPE_DFILTERED,
if (!TESTANY(OFFLINE_FILE_TYPE_FILTERED | OFFLINE_FILE_TYPE_DFILTERED |
// In OFFLINE_FILE_TYPE_ARCH_REGDEPS we can have leftover
// shard->expected_[read | write]_records_ as we don't decrease this
// counter when we encounter a TRACE_TYPE_[READ | WRITE]. We cannot
// decrease the counter because the precise number of reads and
// writes a DR_ISA_REGDEPS instruction performs cannot be determined.
OFFLINE_FILE_TYPE_ARCH_REGDEPS,
shard->file_type_)) {
report_if_false(shard, shard->expected_read_records_ == 0,
"Missing read records");
Expand Down Expand Up @@ -497,11 +512,15 @@ invariant_checker_t::parallel_shard_memref(void *shard_data, const memref_t &mem
// Re-assign the saved value to the shard state to allow this intervening
// maybe_blocking marker.
shard->prev_was_syscall_marker_ = prev_was_syscall_marker_saved;
report_if_false(shard,
shard->prev_entry_.marker.type == TRACE_TYPE_MARKER &&
shard->prev_entry_.marker.marker_type ==
TRACE_MARKER_TYPE_SYSCALL,
"Maybe-blocking marker not preceded by syscall marker");
report_if_false(
shard,
(shard->prev_entry_.marker.type == TRACE_TYPE_MARKER &&
shard->prev_entry_.marker.marker_type == TRACE_MARKER_TYPE_SYSCALL) ||
// In OFFLINE_FILE_TYPE_ARCH_REGDEPS traces we remove
// TRACE_MARKER_TYPE_SYSCALL markers, hence we disable this check for
// these traces.
TESTANY(OFFLINE_FILE_TYPE_ARCH_REGDEPS, shard->file_type_),
"Maybe-blocking marker not preceded by syscall marker");
}

// Invariant: each chunk's instruction count must be identical and equal to
Expand Down Expand Up @@ -771,11 +790,30 @@ invariant_checker_t::parallel_shard_memref(void *shard_data, const memref_t &mem
instr_writes_memory(noalloc_instr);
cur_instr_info.decoding.is_predicated =
instr_is_predicated(noalloc_instr);
cur_instr_info.decoding.num_memory_read_access =
instr_num_memory_read_access(noalloc_instr);
cur_instr_info.decoding.num_memory_write_access =
instr_num_memory_write_access(noalloc_instr);
if (type_is_instr_branch(memref.instr.type)) {
// DR_ISA_REGDEPS instructions don't have an opcode, hence we use
// their category to determine whether they perform at least one read
// or write.
if (instr_get_isa_mode(noalloc_instr) == DR_ISA_REGDEPS) {
cur_instr_info.decoding.num_memory_read_access =
TESTANY(DR_INSTR_CATEGORY_LOAD,
instr_get_category(noalloc_instr))
? 1
: 0;
cur_instr_info.decoding.num_memory_write_access =
TESTANY(DR_INSTR_CATEGORY_STORE,
instr_get_category(noalloc_instr))
? 1
: 0;
} else {
cur_instr_info.decoding.num_memory_read_access =
instr_num_memory_read_access(noalloc_instr);
cur_instr_info.decoding.num_memory_write_access =
instr_num_memory_write_access(noalloc_instr);
}
if (type_is_instr_branch(memref.instr.type) &&
// DR_ISA_REGDEPS instructions don't have a branch target saved as
// instr.src[0], so we cannot retrieve this information.
!TESTANY(OFFLINE_FILE_TYPE_ARCH_REGDEPS, shard->file_type_)) {
const opnd_t target = instr_get_target(noalloc_instr);
if (opnd_is_pc(target)) {
cur_instr_info.decoding.branch_target =
Expand All @@ -801,17 +839,28 @@ invariant_checker_t::parallel_shard_memref(void *shard_data, const memref_t &mem
// Verify the number of read/write records matches the last
// operand. Skip D-filtered traces which don't have every load or
// store records.
report_if_false(shard,
shard->expected_read_records_ == 0 ||
// Some prefetches did not have any corresponding
// memory access in system call trace templates
// collected on QEMU.
(shard->between_kernel_syscall_trace_markers_ &&
shard->prev_instr_.decoding.is_prefetch),
"Missing read records");
report_if_false(
shard,
(shard->expected_read_records_ == 0 ||
// Some prefetches did not have any corresponding
// memory access in system call trace templates
// collected on QEMU.
(shard->between_kernel_syscall_trace_markers_ &&
shard->prev_instr_.decoding.is_prefetch)) ||
// We cannot check for is_predicated in
// OFFLINE_FILE_TYPE_ARCH_REGDEPS traces (it's always false), so
// we disable this check.
TESTANY(OFFLINE_FILE_TYPE_ARCH_REGDEPS, shard->file_type_),
"Missing read records");
shard->expected_read_records_ = 0;
report_if_false(shard, shard->expected_write_records_ == 0,
"Missing write records");
report_if_false(
shard,
shard->expected_write_records_ == 0 ||
// We cannot check for is_predicated in
// OFFLINE_FILE_TYPE_ARCH_REGDEPS traces (it's always false), so
// we disable this check.
TESTANY(OFFLINE_FILE_TYPE_ARCH_REGDEPS, shard->file_type_),
"Missing write records");

if (!(shard->between_kernel_syscall_trace_markers_ &&
TESTANY(OFFLINE_FILE_TYPE_KERNEL_SYSCALL_INSTR_ONLY,
Expand Down Expand Up @@ -1150,7 +1199,16 @@ invariant_checker_t::parallel_shard_memref(void *shard_data, const memref_t &mem
shard->expected_read_records_ >
0 IF_X86(|| relax_expected_read_count_check_for_kernel(shard)),
"Too many read records");
if (shard->expected_read_records_ > 0) {
if (shard->expected_read_records_ > 0 &&
// We cannot decrease the shard->expected_read_records_ counter in
// OFFLINE_FILE_TYPE_ARCH_REGDEPS traces because we cannot determine
// the exact number of loads a DR_ISA_REGDEPS instruction performs.
// If a DR_ISA_REGDEPS instruction has a DR_INSTR_CATEGORY_LOAD among
// its categories, we simply set
// cur_instr_info.decoding.num_memory_read_access to 1. We rely on the
// next instruction to re-set shard->expected_read_records_
// accordingly.
!TESTANY(OFFLINE_FILE_TYPE_ARCH_REGDEPS, shard->file_type_)) {
shard->expected_read_records_--;
}
} else {
Expand All @@ -1160,7 +1218,16 @@ invariant_checker_t::parallel_shard_memref(void *shard_data, const memref_t &mem
shard->expected_write_records_ >
0 IF_X86(|| relax_expected_write_count_check_for_kernel(shard)),
"Too many write records");
if (shard->expected_write_records_ > 0) {
if (shard->expected_write_records_ > 0 &&
// We cannot decrease the shard->expected_write_records_ counter in
// OFFLINE_FILE_TYPE_ARCH_REGDEPS traces because we cannot determine
// the exact number of stores a DR_ISA_REGDEPS instruction performs.
// If a DR_ISA_REGDEPS instruction has a DR_INSTR_CATEGORY_STORE among
// its categories, we simply set
// cur_instr_info.decoding.num_memory_write_access to 1. We rely on
// the next instruction to re-set shard->expected_write_records_
// accordingly.
!TESTANY(OFFLINE_FILE_TYPE_ARCH_REGDEPS, shard->file_type_)) {
shard->expected_write_records_--;
}
}
Expand Down Expand Up @@ -1356,6 +1423,14 @@ invariant_checker_t::print_results()
// -no_abort_on_invariant_error even if some invariant errors were found.
std::cerr << "Found " << total_error_count << " invariant errors\n";
}
if (!shard_map_.empty()) {
uint64_t filetype = shard_map_.begin()->second->file_type_;
if (TESTANY(OFFLINE_FILE_TYPE_ARCH_REGDEPS, filetype)) {
std::cerr << "WARNING: invariant_checker is being run on an "
"OFFLINE_FILE_TYPE_ARCH_REGDEPS trace.\nSome invariant checks "
"have been disabled.\n";
}
}
return true;
}

Expand Down Expand Up @@ -1471,7 +1546,12 @@ invariant_checker_t::check_for_pc_discontinuity(
have_branch_target = true;
}
}
if (have_branch_target && branch_target != cur_pc) {
if (have_branch_target && branch_target != cur_pc &&
// We cannot determine the branch target in OFFLINE_FILE_TYPE_ARCH_REGDEPS
// traces because next_pc != cur_pc + instr.size and
// indirect_branch_target is not saved in inst.src[0].
// So, we skip this invariant error.
!TESTANY(OFFLINE_FILE_TYPE_ARCH_REGDEPS, shard->file_type_)) {
error_msg = "Branch does not go to the correct target";
}
} else if (cur_memref_info.decoding.has_valid_decoding &&
Expand Down
8 changes: 5 additions & 3 deletions clients/drcachesim/tools/invariant_checker.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,9 +272,11 @@ class invariant_checker_t : public analysis_tool_t {

void *drcontext_ = dr_standalone_init();
std::unordered_map<int, std::unique_ptr<per_shard_t>> shard_map_;
// This mutex is only needed in parallel_shard_init. In all other accesses to
// shard_map (process_memref, print_results) we are single-threaded.
std::mutex shard_map_mutex_;
// This mutex is only needed in parallel_shard_init to initialize shard_map_ with
// per_shard_t data and set dcontext_t.isa_mode, which is a global resource.
// In all other accesses to shard_map_ (process_memref, print_results) we are
// single-threaded.
std::mutex init_mutex_;

bool knob_offline_;
unsigned int knob_verbose_;
Expand Down
19 changes: 19 additions & 0 deletions suite/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4744,6 +4744,25 @@ if (BUILD_CLIENTS)
"basic_counts")
endif ()

if (X86 AND X64 AND UNIX AND NOT APPLE)
# Run the invariant_checker on an OFFLINE_FILE_TYPE_ARCH_REGDEPS trace.
set(testname "tool.invariant_checker_on_regdeps_trace")
torun_record_filter("${testname}" ${kernel_xfer_app}
"invariant_checker_on_regdeps_trace"
# Generate an OFFLINE_FILE_TYPE_ARCH_REGDEPS trace by running record_filter
# with -filter_encodings2regdeps to change instruction encodings,
# -filter_keep_func_ids 4294967498 (which is SYS_futex, associated to the only
# TRACE_MARKER_TYPE_FUNC_ markers we want to keep), and
# -filter_marker_types 19,25,27,28,30 (which correspond to
# TRACE_MARKER_TYPE_SYSCALL_IDX, TRACE_MARKER_TYPE_SYSCALL,
# TRACE_MARKER_TYPE_SYSCALL_TRACE_START, TRACE_MARKER_TYPE_SYSCALL_TRACE_END,
# TRACE_MARKER_TYPE_SYSCALL_FAILED).
"${drcachesim_path}@-simulator_type@record_filter@-filter_encodings2regdeps@-indir@${testname}.${kernel_xfer_app}.*.dir/trace@-outdir@${testname}.filtered.dir@-filter_marker_types@19,25,27,28,30@-filter_keep_func_ids@4294967498"
# We run the invariant_checker analyzer on the REGDEPS filtered trace.
# We expect no invariant errors.
"invariant_checker")
endif ()

if (UNIX) # Windows multi-thread tests are too slow.
set(testname "tool.record_filter_bycore_multi")
torun_record_filter("${testname}" pthreads.ptsig
Expand Down

0 comments on commit 92a44cc

Please sign in to comment.