blob: 2d7391dcee6d087aafd29bb9d4e888c4833fd18c [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2017-2023 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 "view.h"
#include <stdint.h>
#include <iomanip>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include "analysis_tool.h"
#include "dr_api.h"
#include "memref.h"
#include "memtrace_stream.h"
#include "raw2trace.h"
#include "raw2trace_directory.h"
#include "trace_entry.h"
#include "utils.h"
namespace dynamorio {
namespace drmemtrace {
const std::string view_t::TOOL_NAME = "View tool";
analysis_tool_t *
view_tool_create(const std::string &module_file_path, uint64_t skip_refs,
uint64_t sim_refs, const std::string &syntax, unsigned int verbose,
const std::string &alt_module_dir)
{
return new view_t(module_file_path, skip_refs, sim_refs, syntax, verbose,
alt_module_dir);
}
view_t::view_t(const std::string &module_file_path, uint64_t skip_refs, uint64_t sim_refs,
const std::string &syntax, unsigned int verbose,
const std::string &alt_module_dir)
: module_file_path_(module_file_path)
, knob_verbose_(verbose)
, trace_version_(-1)
, knob_skip_refs_(skip_refs)
, skip_refs_left_(knob_skip_refs_)
, knob_sim_refs_(sim_refs)
, sim_refs_left_(knob_sim_refs_)
, knob_syntax_(syntax)
, knob_alt_module_dir_(alt_module_dir)
, num_disasm_instrs_(0)
, prev_tid_(-1)
, filetype_(-1)
, timestamp_(0)
, has_modules_(true)
{
}
std::string
view_t::initialize_stream(memtrace_stream_t *serial_stream)
{
serial_stream_ = serial_stream;
print_header();
dcontext_.dcontext = dr_standalone_init();
if (module_file_path_.empty()) {
has_modules_ = false;
} else {
std::string error = directory_.initialize_module_file(module_file_path_);
if (!error.empty())
has_modules_ = false;
}
if (!has_modules_) {
// Continue but omit disassembly to support cases where binaries are
// not available and OFFLINE_FILE_TYPE_ENCODINGS is not present.
return "";
}
// Legacy trace support where binaries are needed.
// We do not support non-module code for such traces.
module_mapper_ =
module_mapper_t::create(directory_.modfile_bytes_, nullptr, nullptr, nullptr,
nullptr, knob_verbose_, knob_alt_module_dir_);
module_mapper_->get_loaded_modules();
std::string error = module_mapper_->get_last_error();
if (!error.empty())
return "Failed to load binaries: " + error;
dr_disasm_flags_t flags = IF_X86_ELSE(
DR_DISASM_ATT,
IF_AARCH64_ELSE(DR_DISASM_DR, IF_RISCV64_ELSE(DR_DISASM_RISCV, DR_DISASM_ARM)));
if (knob_syntax_ == "intel") {
flags = DR_DISASM_INTEL;
} else if (knob_syntax_ == "dr") {
flags = DR_DISASM_DR;
} else if (knob_syntax_ == "arm") {
flags = DR_DISASM_ARM;
} else if (knob_syntax_ == "riscv") {
flags = DR_DISASM_RISCV;
}
disassemble_set_syntax(flags);
return "";
}
bool
view_t::parallel_shard_supported()
{
return false;
}
void *
view_t::parallel_shard_init_stream(int shard_index, void *worker_data,
memtrace_stream_t *shard_stream)
{
return shard_stream;
}
bool
view_t::parallel_shard_exit(void *shard_data)
{
return true;
}
std::string
view_t::parallel_shard_error(void *shard_data)
{
// Our parallel operation ignores all but one thread, so we need just
// the one global error string.
return error_string_;
}
bool
view_t::should_skip(memtrace_stream_t *memstream, const memref_t &memref)
{
if (skip_refs_left_ > 0) {
skip_refs_left_--;
// I considered printing the version and filetype even when skipped but
// it adds more confusion from the memref counting than it removes.
// A user can do two views, one without a skip, to see the headers.
return true;
}
if (knob_sim_refs_ > 0) {
if (sim_refs_left_ == 0)
return true;
sim_refs_left_--;
if (sim_refs_left_ == 0 && timestamp_ > 0) {
// Print this timestamp right before the final record.
print_prefix(memstream, memref, timestamp_record_ord_);
std::cerr << "<marker: timestamp " << timestamp_ << ">\n";
timestamp_ = 0;
}
}
return false;
}
bool
view_t::process_memref(const memref_t &memref)
{
return parallel_shard_memref(serial_stream_, memref);
}
bool
view_t::parallel_shard_memref(void *shard_data, const memref_t &memref)
{
memtrace_stream_t *memstream = reinterpret_cast<memtrace_stream_t *>(shard_data);
// Even for -skip_refs we need to process the up-front version and type.
if (memref.marker.type == TRACE_TYPE_MARKER) {
switch (memref.marker.marker_type) {
case TRACE_MARKER_TYPE_VERSION:
// We delay printing until we know the tid.
if (trace_version_ == -1) {
trace_version_ = static_cast<int>(memref.marker.marker_value);
} else if (trace_version_ != static_cast<int>(memref.marker.marker_value)) {
error_string_ = std::string("Version mismatch across files");
return false;
}
version_record_ord_ = memstream->get_record_ordinal();
return true; // Do not count toward -sim_refs yet b/c we don't have tid.
case TRACE_MARKER_TYPE_FILETYPE:
// We delay printing until we know the tid.
if (filetype_ == -1) {
filetype_ = static_cast<intptr_t>(memref.marker.marker_value);
} else if (filetype_ != static_cast<intptr_t>(memref.marker.marker_value)) {
error_string_ = std::string("Filetype mismatch across files");
return false;
}
filetype_record_ord_ = memstream->get_record_ordinal();
if (TESTANY(OFFLINE_FILE_TYPE_ARCH_ALL, memref.marker.marker_value) &&
!TESTANY(build_target_arch_type(), memref.marker.marker_value)) {
error_string_ = std::string("Architecture mismatch: trace recorded on ") +
trace_arch_string(static_cast<offline_file_type_t>(
memref.marker.marker_value)) +
" but tool built for " + trace_arch_string(build_target_arch_type());
return false;
}
return true; // Do not count toward -sim_refs yet b/c we don't have tid.
case TRACE_MARKER_TYPE_TIMESTAMP:
// Delay to see whether this is a new window. We assume a timestamp
// is always followed by another marker (cpu or window).
// We can't easily reorder and place window markers before timestamps
// since memref iterators use the timestamps to order buffer units.
timestamp_ = memref.marker.marker_value;
timestamp_record_ord_ = memstream->get_record_ordinal();
if (should_skip(memstream, memref))
timestamp_ = 0;
return true;
default: break;
}
}
// We delay the initial markers until we know the tid.
// There are always at least 2 markers (timestamp+cpu) immediately after the
// first two, and on newer versions there is a 3rd (line size).
if (memref.marker.type == TRACE_TYPE_MARKER && memref.marker.tid != 0 &&
printed_header_.find(memref.marker.tid) == printed_header_.end()) {
printed_header_.insert(memref.marker.tid);
if (trace_version_ != -1) { // Old versions may not have a version marker.
if (!should_skip(memstream, memref)) {
print_prefix(memstream, memref, version_record_ord_);
std::cerr << "<marker: version " << trace_version_ << ">\n";
}
}
if (filetype_ != -1) { // Handle old/malformed versions.
if (!should_skip(memstream, memref)) {
print_prefix(memstream, memref, filetype_record_ord_);
std::cerr << "<marker: filetype 0x" << std::hex << filetype_ << std::dec
<< ">\n";
}
}
}
if (should_skip(memstream, memref))
return true;
if (memref.marker.type == TRACE_TYPE_MARKER) {
if (memref.marker.marker_type == TRACE_MARKER_TYPE_WINDOW_ID) {
// Needs special handling to get the horizontal line before the timestamp.
if (last_window_[memref.marker.tid] != memref.marker.marker_value) {
std::cerr
<< "------------------------------------------------------------\n";
print_prefix(memstream, memref,
-1); // Already incremented for timestamp above.
}
if (timestamp_ > 0) {
std::cerr << "<marker: timestamp " << timestamp_ << ">\n";
timestamp_ = 0;
print_prefix(memstream, memref);
}
std::cerr << "<marker: window " << memref.marker.marker_value << ">\n";
last_window_[memref.marker.tid] = memref.marker.marker_value;
}
if (timestamp_ > 0) {
print_prefix(memstream, memref, timestamp_record_ord_);
std::cerr << "<marker: timestamp " << timestamp_ << ">\n";
timestamp_ = 0;
}
}
if (memref.instr.tid != 0) {
print_prefix(memstream, memref);
}
if (memref.marker.type == TRACE_TYPE_MARKER) {
switch (memref.marker.marker_type) {
case TRACE_MARKER_TYPE_VERSION:
// Handled above.
break;
case TRACE_MARKER_TYPE_FILETYPE:
// Handled above.
break;
case TRACE_MARKER_TYPE_TIMESTAMP:
// Handled above.
break;
case TRACE_MARKER_TYPE_CPU_ID:
// We include the thread ID here under the assumption that we will always
// see a cpuid marker on a thread switch. To avoid that assumption
// we would want to track the prior tid and print out a thread switch
// message whenever it changes.
std::cerr << "<marker: tid " << memref.marker.tid << " on core "
<< memref.marker.marker_value << ">\n";
break;
case TRACE_MARKER_TYPE_KERNEL_EVENT:
if (trace_version_ <= TRACE_ENTRY_VERSION_NO_KERNEL_PC) {
// Legacy traces just have the module offset.
std::cerr << "<marker: kernel xfer from module offset +0x" << std::hex
<< memref.marker.marker_value << std::dec << " to handler>\n";
} else {
std::cerr << "<marker: kernel xfer from 0x" << std::hex
<< memref.marker.marker_value << std::dec << " to handler>\n";
}
break;
case TRACE_MARKER_TYPE_RSEQ_ABORT:
std::cerr << "<marker: rseq abort from 0x" << std::hex
<< memref.marker.marker_value << std::dec << " to handler>\n";
break;
case TRACE_MARKER_TYPE_RSEQ_ENTRY:
std::cerr << "<marker: rseq entry with end at 0x" << std::hex
<< memref.marker.marker_value << std::dec << ">\n";
break;
case TRACE_MARKER_TYPE_KERNEL_XFER:
if (trace_version_ <= TRACE_ENTRY_VERSION_NO_KERNEL_PC) {
// Legacy traces just have the module offset.
std::cerr << "<marker: syscall xfer from module offset +0x" << std::hex
<< memref.marker.marker_value << std::dec << ">\n";
} else {
std::cerr << "<marker: syscall xfer from 0x" << std::hex
<< memref.marker.marker_value << std::dec << ">\n";
}
break;
case TRACE_MARKER_TYPE_INSTRUCTION_COUNT:
std::cerr << "<marker: instruction count " << memref.marker.marker_value
<< ">\n";
break;
case TRACE_MARKER_TYPE_CACHE_LINE_SIZE:
std::cerr << "<marker: cache line size " << memref.marker.marker_value
<< ">\n";
break;
case TRACE_MARKER_TYPE_PAGE_SIZE:
std::cerr << "<marker: page size " << memref.marker.marker_value << ">\n";
break;
case TRACE_MARKER_TYPE_CHUNK_INSTR_COUNT:
std::cerr << "<marker: chunk instruction count " << memref.marker.marker_value
<< ">\n";
break;
case TRACE_MARKER_TYPE_CHUNK_FOOTER:
std::cerr << "<marker: chunk footer #" << memref.marker.marker_value << ">\n";
break;
case TRACE_MARKER_TYPE_FILTER_ENDPOINT:
std::cerr << "<marker: filter endpoint>\n";
break;
case TRACE_MARKER_TYPE_PHYSICAL_ADDRESS:
std::cerr << "<marker: physical address for following virtual: 0x" << std::hex
<< memref.marker.marker_value << std::dec << ">\n";
break;
case TRACE_MARKER_TYPE_VIRTUAL_ADDRESS:
std::cerr << "<marker: virtual address for prior physical: 0x" << std::hex
<< memref.marker.marker_value << std::dec << ">\n";
break;
case TRACE_MARKER_TYPE_PHYSICAL_ADDRESS_NOT_AVAILABLE:
std::cerr << "<marker: physical address not available for 0x" << std::hex
<< memref.marker.marker_value << std::dec << ">\n";
break;
case TRACE_MARKER_TYPE_FUNC_ID:
if (memref.marker.marker_value >=
static_cast<intptr_t>(func_trace_t::TRACE_FUNC_ID_SYSCALL_BASE)) {
std::cerr << "<marker: function==syscall #"
<< (memref.marker.marker_value -
static_cast<uintptr_t>(
func_trace_t::TRACE_FUNC_ID_SYSCALL_BASE))
<< ">\n";
} else {
std::cerr << "<marker: function #" << memref.marker.marker_value << ">\n";
}
break;
case TRACE_MARKER_TYPE_FUNC_RETADDR:
std::cerr << "<marker: function return address 0x" << std::hex
<< memref.marker.marker_value << std::dec << ">\n";
break;
case TRACE_MARKER_TYPE_FUNC_ARG:
std::cerr << "<marker: function argument 0x" << std::hex
<< memref.marker.marker_value << std::dec << ">\n";
break;
case TRACE_MARKER_TYPE_FUNC_RETVAL:
std::cerr << "<marker: function return value 0x" << std::hex
<< memref.marker.marker_value << std::dec << ">\n";
break;
case TRACE_MARKER_TYPE_SYSCALL_FAILED:
std::cerr << "<marker: system call failed: " << memref.marker.marker_value
<< ">\n";
break;
case TRACE_MARKER_TYPE_RECORD_ORDINAL:
std::cerr << "<marker: record ordinal 0x" << std::hex
<< memref.marker.marker_value << std::dec << ">\n";
break;
case TRACE_MARKER_TYPE_SYSCALL:
std::cerr << "<marker: system call " << memref.marker.marker_value << ">\n";
break;
case TRACE_MARKER_TYPE_MAYBE_BLOCKING_SYSCALL:
std::cerr << "<marker: maybe-blocking system call>\n";
break;
case TRACE_MARKER_TYPE_DIRECT_THREAD_SWITCH:
std::cerr << "<marker: direct switch to thread " << memref.marker.marker_value
<< ">\n";
break;
case TRACE_MARKER_TYPE_WINDOW_ID:
// Handled above.
break;
case TRACE_MARKER_TYPE_SYSCALL_TRACE_START:
std::cerr << "<marker: trace start for system call number "
<< memref.marker.marker_value << ">\n";
break;
case TRACE_MARKER_TYPE_SYSCALL_TRACE_END:
std::cerr << "<marker: trace end for system call number "
<< memref.marker.marker_value << ">\n";
break;
case TRACE_MARKER_TYPE_CONTEXT_SWITCH_START:
std::cerr << "<marker: trace start for context switch type "
<< memref.marker.marker_value << ">\n";
break;
case TRACE_MARKER_TYPE_CONTEXT_SWITCH_END:
std::cerr << "<marker: trace end for context switch type "
<< memref.marker.marker_value << ">\n";
break;
case TRACE_MARKER_TYPE_BRANCH_TARGET:
// These are not expected to be visible (since the reader adds them
// to memref.instr.indirect_branch_target) but we handle nonetheless.
std::cerr << "<marker: indirect branch target 0x" << std::hex
<< memref.marker.marker_value << std::dec << ">\n";
break;
case TRACE_MARKER_TYPE_CORE_WAIT:
std::cerr << "<marker: wait for another core>\n";
break;
case TRACE_MARKER_TYPE_CORE_IDLE: std::cerr << "<marker: core is idle>\n"; break;
case TRACE_MARKER_TYPE_VECTOR_LENGTH:
std::cerr << "<marker: vector length " << memref.marker.marker_value
<< " bytes>\n";
break;
default:
std::cerr << "<marker: type " << memref.marker.marker_type << "; value "
<< memref.marker.marker_value << ">\n";
break;
}
return true;
}
static constexpr int name_width = 12;
if (!type_is_instr(memref.instr.type) &&
memref.data.type != TRACE_TYPE_INSTR_NO_FETCH) {
std::string name; // Shared output for address-containing types.
switch (memref.data.type) {
default: std::cerr << "<entry type " << memref.data.type << ">\n"; return true;
case TRACE_TYPE_THREAD_EXIT:
std::cerr << "<thread " << memref.data.tid << " exited>\n";
return true;
// The rest are address-containing types.
case TRACE_TYPE_READ: name = "read"; break;
case TRACE_TYPE_WRITE: name = "write"; break;
case TRACE_TYPE_INSTR_FLUSH: name = "iflush"; break;
case TRACE_TYPE_DATA_FLUSH: name = "dflush"; break;
case TRACE_TYPE_PREFETCH: name = "pref"; break;
case TRACE_TYPE_PREFETCH_READ_L1: name = "pref-r-L1"; break;
case TRACE_TYPE_PREFETCH_READ_L2: name = "pref-r-L2"; break;
case TRACE_TYPE_PREFETCH_READ_L3: name = "pref-r-L3"; break;
case TRACE_TYPE_PREFETCHNTA: name = "pref-NTA"; break;
case TRACE_TYPE_PREFETCH_READ: name = "pref-r"; break;
case TRACE_TYPE_PREFETCH_WRITE: name = "pref-w"; break;
case TRACE_TYPE_PREFETCH_INSTR: name = "pref-i"; break;
case TRACE_TYPE_PREFETCH_READ_L1_NT: name = "pref-r-L1-NT"; break;
case TRACE_TYPE_PREFETCH_READ_L2_NT: name = "pref-r-L2-NT"; break;
case TRACE_TYPE_PREFETCH_READ_L3_NT: name = "pref-r-L3-NT"; break;
case TRACE_TYPE_PREFETCH_INSTR_L1: name = "pref-i-L1"; break;
case TRACE_TYPE_PREFETCH_INSTR_L1_NT: name = "pref-i-L1-NT"; break;
case TRACE_TYPE_PREFETCH_INSTR_L2: name = "pref-i-L2"; break;
case TRACE_TYPE_PREFETCH_INSTR_L2_NT: name = "pref-i-L2-NT"; break;
case TRACE_TYPE_PREFETCH_INSTR_L3: name = "pref-i-L3"; break;
case TRACE_TYPE_PREFETCH_INSTR_L3_NT: name = "pref-i-L3-NT"; break;
case TRACE_TYPE_PREFETCH_WRITE_L1: name = "pref-w-L1"; break;
case TRACE_TYPE_PREFETCH_WRITE_L1_NT: name = "pref-w-L1-NT"; break;
case TRACE_TYPE_PREFETCH_WRITE_L2: name = "pref-w-L2"; break;
case TRACE_TYPE_PREFETCH_WRITE_L2_NT: name = "pref-w-L2-NT"; break;
case TRACE_TYPE_PREFETCH_WRITE_L3: name = "pref-w-L3"; break;
case TRACE_TYPE_PREFETCH_WRITE_L3_NT: name = "pref-w-L3-NT"; break;
case TRACE_TYPE_HARDWARE_PREFETCH: name = "pref-HW"; break;
}
std::cerr << std::left << std::setw(name_width) << name << std::right
<< std::setw(2) << memref.data.size << " byte(s) @ 0x" << std::hex
<< std::setfill('0') << std::setw(sizeof(void *) * 2)
<< memref.data.addr << " by PC 0x" << std::setw(sizeof(void *) * 2)
<< memref.data.pc << std::dec << std::setfill(' ') << "\n";
return true;
}
std::cerr << std::left << std::setw(name_width) << "ifetch" << std::right
<< std::setw(2) << memref.instr.size << " byte(s) @ 0x" << std::hex
<< std::setfill('0') << std::setw(sizeof(void *) * 2) << memref.instr.addr
<< std::dec << std::setfill(' ');
if (!TESTANY(OFFLINE_FILE_TYPE_ENCODINGS, filetype_) && !has_modules_) {
// We can't disassemble so we provide what info the trace itself contains.
// XXX i#5486: We may want to store the taken target for conditional
// branches; if added, we can print it here.
// XXX: It may avoid initial confusion over the record-oriented output
// to indicate whether an instruction accesses memory, but that requires
// delayed printing.
std::cerr << " ";
switch (memref.instr.type) {
case TRACE_TYPE_INSTR: std::cerr << "non-branch\n"; break;
case TRACE_TYPE_INSTR_DIRECT_JUMP: std::cerr << "jump\n"; break;
case TRACE_TYPE_INSTR_INDIRECT_JUMP: std::cerr << "indirect jump\n"; break;
case TRACE_TYPE_INSTR_CONDITIONAL_JUMP: std::cerr << "conditional jump\n"; break;
case TRACE_TYPE_INSTR_TAKEN_JUMP: std::cerr << "taken conditional jump\n"; break;
case TRACE_TYPE_INSTR_UNTAKEN_JUMP:
std::cerr << "untaken conditional jump\n";
break;
case TRACE_TYPE_INSTR_DIRECT_CALL: std::cerr << "call\n"; break;
case TRACE_TYPE_INSTR_INDIRECT_CALL: std::cerr << "indirect call\n"; break;
case TRACE_TYPE_INSTR_RETURN: std::cerr << "return\n"; break;
case TRACE_TYPE_INSTR_NO_FETCH: std::cerr << "non-fetched instruction\n"; break;
case TRACE_TYPE_INSTR_SYSENTER: std::cerr << "sysenter\n"; break;
default: error_string_ = "Unknown instruction type\n"; return false;
}
++num_disasm_instrs_;
return true;
}
app_pc decode_pc;
const app_pc orig_pc = (app_pc)memref.instr.addr;
if (TESTANY(OFFLINE_FILE_TYPE_ENCODINGS, filetype_)) {
// The trace has instruction encodings inside it.
decode_pc = const_cast<app_pc>(memref.instr.encoding);
if (memref.instr.encoding_is_new) {
// The code may have changed: invalidate the cache.
disasm_cache_.erase(orig_pc);
}
} else {
// Legacy trace support where we need the binaries.
decode_pc = module_mapper_->find_mapped_trace_address(orig_pc);
if (!module_mapper_->get_last_error().empty()) {
error_string_ = "Failed to find mapped address for " +
to_hex_string(memref.instr.addr) + ": " +
module_mapper_->get_last_error();
return false;
}
}
std::string disasm;
auto cached_disasm = disasm_cache_.find(orig_pc);
if (cached_disasm != disasm_cache_.end()) {
disasm = cached_disasm->second;
} else {
// MAX_INSTR_DIS_SZ is set to 196 in core/ir/disassemble.h but is not
// exported so we just use the same value here.
char buf[196];
byte *next_pc = disassemble_to_buffer(
dcontext_.dcontext, decode_pc, orig_pc, /*show_pc=*/false,
/*show_bytes=*/true, buf, BUFFER_SIZE_ELEMENTS(buf),
/*printed=*/nullptr);
if (next_pc == nullptr) {
error_string_ = "Failed to disassemble " + to_hex_string(memref.instr.addr);
return false;
}
disasm = buf;
disasm_cache_.insert({ orig_pc, disasm });
}
// Add branch decoration, which varies and so can't be cached purely by PC.
auto newline = disasm.find('\n');
if (memref.instr.type == TRACE_TYPE_INSTR_TAKEN_JUMP)
disasm.insert(newline, " (taken)");
else if (memref.instr.type == TRACE_TYPE_INSTR_UNTAKEN_JUMP)
disasm.insert(newline, " (untaken)");
else if (trace_version_ >= TRACE_ENTRY_VERSION_BRANCH_INFO &&
type_is_instr_branch(memref.instr.type) &&
!type_is_instr_direct_branch(memref.instr.type)) {
std::stringstream str;
str << " (target 0x" << std::hex << memref.instr.indirect_branch_target << ")";
disasm.insert(newline, str.str());
}
// Put our prefix on raw byte spillover, and skip the other columns.
newline = disasm.find('\n');
if (newline != std::string::npos && newline < disasm.size() - 1) {
std::stringstream prefix;
print_prefix(memstream, memref, -1, prefix);
std::string skip_name(name_width, ' ');
disasm.insert(newline + 1,
prefix.str() + skip_name + " ");
}
std::cerr << disasm;
++num_disasm_instrs_;
return true;
}
bool
view_t::print_results()
{
std::cerr << TOOL_NAME << " results:\n";
std::cerr << std::setw(15) << num_disasm_instrs_ << " : total instructions\n";
return true;
}
} // namespace drmemtrace
} // namespace dynamorio