--trace_functions and --trace_function_coverage

This commit is contained in:
Ben Vanik
2015-05-05 22:44:36 -07:00
parent ade5388728
commit 94c62b91d0
24 changed files with 365 additions and 82 deletions

View File

@@ -41,7 +41,7 @@ class Assembler {
virtual bool Assemble(FunctionInfo* symbol_info, hir::HIRBuilder* builder,
uint32_t debug_info_flags,
std::unique_ptr<DebugInfo> debug_info,
uint32_t trace_flags, Function** out_function) = 0;
Function** out_function) = 0;
protected:
Backend* backend_;

View File

@@ -60,22 +60,22 @@ void X64Assembler::Reset() {
bool X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder,
uint32_t debug_info_flags,
std::unique_ptr<DebugInfo> debug_info,
uint32_t trace_flags, Function** out_function) {
Function** out_function) {
SCOPE_profile_cpu_f("cpu");
// Reset when we leave.
xe::make_reset_scope(this);
// Lower HIR -> x64.
void* machine_code = 0;
void* machine_code = nullptr;
size_t code_size = 0;
if (!emitter_->Emit(builder, debug_info_flags, debug_info.get(), trace_flags,
machine_code, code_size)) {
if (!emitter_->Emit(builder, debug_info_flags, debug_info.get(), machine_code,
code_size)) {
return false;
}
// Stash generated machine code.
if (debug_info_flags & DebugInfoFlags::DEBUG_INFO_MACHINE_CODE_DISASM) {
if (debug_info_flags & DebugInfoFlags::kDebugInfoDisasmMachineCode) {
DumpMachineCode(debug_info.get(), machine_code, code_size, &string_buffer_);
debug_info->set_machine_code_disasm(string_buffer_.ToString());
string_buffer_.Reset();

View File

@@ -35,7 +35,7 @@ class X64Assembler : public Assembler {
bool Assemble(FunctionInfo* symbol_info, hir::HIRBuilder* builder,
uint32_t debug_info_flags,
std::unique_ptr<DebugInfo> debug_info, uint32_t trace_flags,
std::unique_ptr<DebugInfo> debug_info,
Function** out_function) override;
private:

View File

@@ -13,6 +13,7 @@
#include "xenia/base/atomic.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/memory.h"
#include "xenia/base/vec128.h"
#include "xenia/cpu/backend/x64/x64_backend.h"
#include "xenia/cpu/backend/x64/x64_code_cache.h"
@@ -65,24 +66,25 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator)
code_cache_(backend->code_cache()),
allocator_(allocator),
current_instr_(0),
debug_info_(nullptr),
debug_info_flags_(0),
source_map_count_(0),
stack_size_(0),
trace_flags_(0),
cpu_() {}
stack_size_(0) {}
X64Emitter::~X64Emitter() = default;
bool X64Emitter::Emit(HIRBuilder* builder, uint32_t debug_info_flags,
DebugInfo* debug_info, uint32_t trace_flags,
void*& out_code_address, size_t& out_code_size) {
DebugInfo* debug_info, void*& out_code_address,
size_t& out_code_size) {
SCOPE_profile_cpu_f("cpu");
// Reset.
if (debug_info_flags & DEBUG_INFO_SOURCE_MAP) {
debug_info_ = debug_info;
debug_info_flags_ = debug_info_flags;
if (debug_info_flags_ & DebugInfoFlags::kDebugInfoSourceMap) {
source_map_count_ = 0;
source_map_arena_.Reset();
}
trace_flags_ = trace_flags;
// Fill the generator with code.
size_t stack_size = 0;
@@ -95,7 +97,7 @@ bool X64Emitter::Emit(HIRBuilder* builder, uint32_t debug_info_flags,
out_code_address = Emplace(stack_size);
// Stash source map.
if (debug_info_flags & DEBUG_INFO_SOURCE_MAP) {
if (debug_info_flags_ & DebugInfoFlags::kDebugInfoSourceMap) {
debug_info->InitializeSourceMap(
source_map_count_, (SourceMapEntry*)source_map_arena_.CloneContents());
}
@@ -145,19 +147,46 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
// IMPORTANT: any changes to the prolog must be kept in sync with
// X64CodeCache, which dynamically generates exception information.
// Adding or changing anything here must be matched!
const bool emit_prolog = true;
const size_t stack_size = StackLayout::GUEST_STACK_SIZE + stack_offset;
assert_true((stack_size + 8) % 16 == 0);
out_stack_size = stack_size;
stack_size_ = stack_size;
if (emit_prolog) {
sub(rsp, (uint32_t)stack_size);
mov(qword[rsp + StackLayout::GUEST_RCX_HOME], rcx);
mov(qword[rsp + StackLayout::GUEST_RET_ADDR], rdx);
mov(qword[rsp + StackLayout::GUEST_CALL_RET_ADDR], 0);
mov(rdx, qword[rcx + 8]); // membase
sub(rsp, (uint32_t)stack_size);
mov(qword[rsp + StackLayout::GUEST_RCX_HOME], rcx);
mov(qword[rsp + StackLayout::GUEST_RET_ADDR], rdx);
mov(qword[rsp + StackLayout::GUEST_CALL_RET_ADDR], 0);
// Safe now to do some tracing.
if (debug_info_flags_ & DebugInfoFlags::kDebugInfoTraceFunctions) {
// We require 32-bit addresses.
assert_true(uint64_t(debug_info_->trace_data().header()) < UINT_MAX);
auto trace_header = debug_info_->trace_data().header();
// Call count.
lock();
inc(qword[low_address(&trace_header->function_call_count)]);
// Get call history slot.
static_assert(debug::FunctionTraceData::kFunctionCallerHistoryCount == 4,
"bitmask depends on count");
mov(rax, qword[low_address(&trace_header->function_call_count)]);
and(rax, B00000011);
// Record call history value into slot (guest addr in RDX).
mov(dword[RegExp(uint32_t(uint64_t(
low_address(&trace_header->function_caller_history)))) +
rax * 4],
edx);
// Calling thread. Load ax with thread ID.
EmitGetCurrentThreadId();
lock();
bts(qword[low_address(&trace_header->function_thread_use)], rax);
}
// Load membase.
mov(rdx, qword[rcx + 8]);
// Body.
auto block = builder->first_block();
while (block) {
@@ -187,10 +216,8 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
// Function epilog.
L("epilog");
EmitTraceUserCallReturn();
if (emit_prolog) {
mov(rcx, qword[rsp + StackLayout::GUEST_RCX_HOME]);
add(rsp, (uint32_t)stack_size);
}
mov(rcx, qword[rsp + StackLayout::GUEST_RCX_HOME]);
add(rsp, (uint32_t)stack_size);
ret();
#if XE_DEBUG
@@ -210,6 +237,23 @@ void X64Emitter::MarkSourceOffset(const Instr* i) {
entry->hir_offset = uint32_t(i->block->ordinal << 16) | i->ordinal;
entry->code_offset = static_cast<uint32_t>(getSize());
source_map_count_++;
#if XE_DEBUG
nop();
nop();
mov(eax, entry->source_offset);
nop();
nop();
#endif // XE_DEBUG
if (debug_info_flags_ & DebugInfoFlags::kDebugInfoTraceFunctionCoverage) {
auto trace_data = debug_info_->trace_data();
uint32_t instruction_index =
(entry->source_offset - trace_data.start_address()) / 4;
lock();
inc(qword[low_address(trace_data.instruction_execute_counts() +
instruction_index * 8)]);
}
}
void X64Emitter::EmitGetCurrentThreadId() {
@@ -234,6 +278,7 @@ uint64_t TrapDebugPrint(void* raw_context, uint64_t address) {
XELOGD("(DebugPrint) %s", str);
return 0;
}
void X64Emitter::Trap(uint16_t trap_type) {
switch (trap_type) {
case 20:

View File

@@ -15,6 +15,7 @@
#include "xenia/base/arena.h"
#include "xenia/cpu/hir/value.h"
#include "xenia/debug/trace_data.h"
namespace xe {
namespace cpu {
@@ -106,8 +107,8 @@ class X64Emitter : public Xbyak::CodeGenerator {
const Xbyak::util::Cpu* cpu() const { return &cpu_; }
bool Emit(hir::HIRBuilder* builder, uint32_t debug_info_flags,
DebugInfo* debug_info, uint32_t trace_flags,
void*& out_code_address, size_t& out_code_size);
DebugInfo* debug_info, void*& out_code_address,
size_t& out_code_size);
public:
// Reserved: rsp
@@ -176,6 +177,8 @@ class X64Emitter : public Xbyak::CodeGenerator {
void LoadConstantXmm(Xbyak::Xmm dest, const vec128_t& v);
Xbyak::Address StashXmm(int index, const Xbyak::Xmm& r);
DebugInfo* debug_info() const { return debug_info_; }
size_t stack_size() const { return stack_size_; }
protected:
@@ -193,13 +196,13 @@ class X64Emitter : public Xbyak::CodeGenerator {
hir::Instr* current_instr_;
DebugInfo* debug_info_;
uint32_t debug_info_flags_;
size_t source_map_count_;
Arena source_map_arena_;
size_t stack_size_;
uint32_t trace_flags_;
static const uint32_t gpr_reg_map_[GPR_COUNT];
static const uint32_t xmm_reg_map_[XMM_COUNT];
};

View File

@@ -95,13 +95,6 @@ EMITTER_OPCODE_TABLE(
// ============================================================================
EMITTER(SOURCE_OFFSET, MATCH(I<OPCODE_SOURCE_OFFSET, VoidOp, OffsetOp>)) {
static void Emit(X64Emitter& e, const EmitArgType& i) {
#if XE_DEBUG
e.nop();
e.nop();
e.mov(e.eax, (uint32_t)i.src1.value);
e.nop();
e.nop();
#endif // XE_DEBUG
e.MarkSourceOffset(i.instr);
}
};