Mostly complete tracing. Probably a lot of bugs.
This commit is contained in:
parent
cebf595958
commit
c275562594
|
@ -49,6 +49,7 @@ tmtags
|
||||||
|
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
private/
|
private/
|
||||||
|
*.trace
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# Build system output
|
# Build system output
|
||||||
|
@ -70,3 +71,8 @@ build-out/
|
||||||
build-gen/
|
build-gen/
|
||||||
build-bin/
|
build-bin/
|
||||||
build-test/
|
build-test/
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# Local-only paths
|
||||||
|
# ==============================================================================
|
||||||
|
attic/
|
||||||
|
|
|
@ -43,6 +43,7 @@ class Assembler {
|
||||||
virtual int Assemble(runtime::FunctionInfo* symbol_info,
|
virtual int Assemble(runtime::FunctionInfo* symbol_info,
|
||||||
hir::HIRBuilder* builder, uint32_t debug_info_flags,
|
hir::HIRBuilder* builder, uint32_t debug_info_flags,
|
||||||
std::unique_ptr<runtime::DebugInfo> debug_info,
|
std::unique_ptr<runtime::DebugInfo> debug_info,
|
||||||
|
uint32_t trace_flags,
|
||||||
runtime::Function** out_function) = 0;
|
runtime::Function** out_function) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -50,7 +50,7 @@ void IVMAssembler::Reset() {
|
||||||
int IVMAssembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder,
|
int IVMAssembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder,
|
||||||
uint32_t debug_info_flags,
|
uint32_t debug_info_flags,
|
||||||
std::unique_ptr<DebugInfo> debug_info,
|
std::unique_ptr<DebugInfo> debug_info,
|
||||||
Function** out_function) {
|
uint32_t trace_flags, Function** out_function) {
|
||||||
SCOPE_profile_cpu_f("alloy");
|
SCOPE_profile_cpu_f("alloy");
|
||||||
|
|
||||||
// Reset when we leave.
|
// Reset when we leave.
|
||||||
|
|
|
@ -31,7 +31,7 @@ class IVMAssembler : public Assembler {
|
||||||
int Assemble(runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder,
|
int Assemble(runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder,
|
||||||
uint32_t debug_info_flags,
|
uint32_t debug_info_flags,
|
||||||
std::unique_ptr<runtime::DebugInfo> debug_info,
|
std::unique_ptr<runtime::DebugInfo> debug_info,
|
||||||
runtime::Function** out_function) override;
|
uint32_t trace_flags, runtime::Function** out_function) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Arena intcode_arena_;
|
Arena intcode_arena_;
|
||||||
|
|
|
@ -11,11 +11,12 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <poly/poly.h>
|
|
||||||
#include <alloy/hir/label.h>
|
#include <alloy/hir/label.h>
|
||||||
#include <alloy/runtime/runtime.h>
|
#include <alloy/runtime/runtime.h>
|
||||||
#include <alloy/runtime/symbol_info.h>
|
#include <alloy/runtime/symbol_info.h>
|
||||||
#include <alloy/runtime/thread_state.h>
|
#include <alloy/runtime/thread_state.h>
|
||||||
|
#include <poly/poly.h>
|
||||||
|
#include <xdb/protocol.h>
|
||||||
|
|
||||||
// TODO(benvanik): make a compile time flag?
|
// TODO(benvanik): make a compile time flag?
|
||||||
//#define DYNAMIC_REGISTER_ACCESS_CHECK(address) false
|
//#define DYNAMIC_REGISTER_ACCESS_CHECK(address) false
|
||||||
|
@ -246,6 +247,22 @@ int Translate_SOURCE_OFFSET(TranslationContext& ctx, Instr* i) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(benvanik): dispatch of register forms.
|
||||||
|
uint32_t IntCode_TRACE_SOURCE(IntCodeState& ics, const IntCode* i) {
|
||||||
|
// TODO(benvanik): append to active trace writer.
|
||||||
|
uint64_t trace_base = ics.thread_state->memory()->trace_base();
|
||||||
|
if (trace_base) {
|
||||||
|
auto ev = xdb::protocol::InstrEvent::Append(trace_base);
|
||||||
|
ev->type = xdb::protocol::EventType::INSTR;
|
||||||
|
ev->thread_id = ics.thread_state->thread_id();
|
||||||
|
ev->address = ics.rf[i->src1_reg].i32;
|
||||||
|
}
|
||||||
|
return IA_NEXT;
|
||||||
|
}
|
||||||
|
int Translate_TRACE_SOURCE(TranslationContext& ctx, Instr* i) {
|
||||||
|
return DispatchToC(ctx, i, IntCode_TRACE_SOURCE);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t IntCode_DEBUG_BREAK(IntCodeState& ics, const IntCode* i) {
|
uint32_t IntCode_DEBUG_BREAK(IntCodeState& ics, const IntCode* i) {
|
||||||
DFLUSH();
|
DFLUSH();
|
||||||
__debugbreak();
|
__debugbreak();
|
||||||
|
@ -4174,33 +4191,33 @@ int Translate_ATOMIC_EXCHANGE(TranslationContext& ctx, Instr* i) {
|
||||||
typedef int (*TranslateFn)(TranslationContext& ctx, Instr* i);
|
typedef int (*TranslateFn)(TranslationContext& ctx, Instr* i);
|
||||||
static const TranslateFn dispatch_table[] = {
|
static const TranslateFn dispatch_table[] = {
|
||||||
Translate_COMMENT, Translate_NOP,
|
Translate_COMMENT, Translate_NOP,
|
||||||
Translate_SOURCE_OFFSET, Translate_DEBUG_BREAK,
|
Translate_SOURCE_OFFSET, Translate_TRACE_SOURCE,
|
||||||
Translate_DEBUG_BREAK_TRUE, Translate_TRAP,
|
Translate_DEBUG_BREAK, Translate_DEBUG_BREAK_TRUE,
|
||||||
Translate_TRAP_TRUE, Translate_CALL,
|
Translate_TRAP, Translate_TRAP_TRUE,
|
||||||
Translate_CALL_TRUE, Translate_CALL_INDIRECT,
|
Translate_CALL, Translate_CALL_TRUE,
|
||||||
Translate_CALL_INDIRECT_TRUE, Translate_CALL_EXTERN,
|
Translate_CALL_INDIRECT, Translate_CALL_INDIRECT_TRUE,
|
||||||
Translate_RETURN, Translate_RETURN_TRUE,
|
Translate_CALL_EXTERN, Translate_RETURN,
|
||||||
Translate_SET_RETURN_ADDRESS, Translate_BRANCH,
|
Translate_RETURN_TRUE, Translate_SET_RETURN_ADDRESS,
|
||||||
Translate_BRANCH_TRUE, Translate_BRANCH_FALSE,
|
Translate_BRANCH, Translate_BRANCH_TRUE,
|
||||||
Translate_ASSIGN, Translate_CAST,
|
Translate_BRANCH_FALSE, Translate_ASSIGN,
|
||||||
Translate_ZERO_EXTEND, Translate_SIGN_EXTEND,
|
Translate_CAST, Translate_ZERO_EXTEND,
|
||||||
Translate_TRUNCATE, Translate_CONVERT,
|
Translate_SIGN_EXTEND, Translate_TRUNCATE,
|
||||||
Translate_ROUND, Translate_VECTOR_CONVERT_I2F,
|
Translate_CONVERT, Translate_ROUND,
|
||||||
Translate_VECTOR_CONVERT_F2I, Translate_LOAD_VECTOR_SHL,
|
Translate_VECTOR_CONVERT_I2F, Translate_VECTOR_CONVERT_F2I,
|
||||||
Translate_LOAD_VECTOR_SHR, Translate_LOAD_CLOCK,
|
Translate_LOAD_VECTOR_SHL, Translate_LOAD_VECTOR_SHR,
|
||||||
Translate_LOAD_LOCAL, Translate_STORE_LOCAL,
|
Translate_LOAD_CLOCK, Translate_LOAD_LOCAL,
|
||||||
Translate_LOAD_CONTEXT, Translate_STORE_CONTEXT,
|
Translate_STORE_LOCAL, Translate_LOAD_CONTEXT,
|
||||||
Translate_LOAD, Translate_STORE,
|
Translate_STORE_CONTEXT, Translate_LOAD,
|
||||||
Translate_PREFETCH, Translate_MAX,
|
Translate_STORE, Translate_PREFETCH,
|
||||||
Translate_VECTOR_MAX, Translate_MIN,
|
Translate_MAX, Translate_VECTOR_MAX,
|
||||||
Translate_VECTOR_MIN, Translate_SELECT,
|
Translate_MIN, Translate_VECTOR_MIN,
|
||||||
Translate_IS_TRUE, Translate_IS_FALSE,
|
Translate_SELECT, Translate_IS_TRUE,
|
||||||
Translate_COMPARE_EQ, Translate_COMPARE_NE,
|
Translate_IS_FALSE, Translate_COMPARE_EQ,
|
||||||
Translate_COMPARE_SLT, Translate_COMPARE_SLE,
|
Translate_COMPARE_NE, Translate_COMPARE_SLT,
|
||||||
Translate_COMPARE_SGT, Translate_COMPARE_SGE,
|
Translate_COMPARE_SLE, Translate_COMPARE_SGT,
|
||||||
Translate_COMPARE_ULT, Translate_COMPARE_ULE,
|
Translate_COMPARE_SGE, Translate_COMPARE_ULT,
|
||||||
Translate_COMPARE_UGT, Translate_COMPARE_UGE,
|
Translate_COMPARE_ULE, Translate_COMPARE_UGT,
|
||||||
Translate_DID_CARRY,
|
Translate_COMPARE_UGE, Translate_DID_CARRY,
|
||||||
TranslateInvalid, // Translate_DID_OVERFLOW,
|
TranslateInvalid, // Translate_DID_OVERFLOW,
|
||||||
Translate_DID_SATURATE, Translate_VECTOR_COMPARE_EQ,
|
Translate_DID_SATURATE, Translate_VECTOR_COMPARE_EQ,
|
||||||
Translate_VECTOR_COMPARE_SGT, Translate_VECTOR_COMPARE_SGE,
|
Translate_VECTOR_COMPARE_SGT, Translate_VECTOR_COMPARE_SGE,
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
'ivm_backend.cc',
|
'ivm_backend.cc',
|
||||||
'ivm_backend.h',
|
'ivm_backend.h',
|
||||||
'ivm_function.cc',
|
'ivm_function.cc',
|
||||||
|
'ivm_function.h',
|
||||||
'ivm_stack.cc',
|
'ivm_stack.cc',
|
||||||
'ivm_stack.h',
|
'ivm_stack.h',
|
||||||
],
|
],
|
||||||
|
|
|
@ -58,7 +58,7 @@ void X64Assembler::Reset() {
|
||||||
int X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder,
|
int X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder,
|
||||||
uint32_t debug_info_flags,
|
uint32_t debug_info_flags,
|
||||||
std::unique_ptr<DebugInfo> debug_info,
|
std::unique_ptr<DebugInfo> debug_info,
|
||||||
Function** out_function) {
|
uint32_t trace_flags, Function** out_function) {
|
||||||
SCOPE_profile_cpu_f("alloy");
|
SCOPE_profile_cpu_f("alloy");
|
||||||
|
|
||||||
// Reset when we leave.
|
// Reset when we leave.
|
||||||
|
@ -68,7 +68,7 @@ int X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder,
|
||||||
void* machine_code = 0;
|
void* machine_code = 0;
|
||||||
size_t code_size = 0;
|
size_t code_size = 0;
|
||||||
int result = emitter_->Emit(builder, debug_info_flags, debug_info.get(),
|
int result = emitter_->Emit(builder, debug_info_flags, debug_info.get(),
|
||||||
machine_code, code_size);
|
trace_flags, machine_code, code_size);
|
||||||
if (result) {
|
if (result) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ class X64Assembler : public Assembler {
|
||||||
int Assemble(runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder,
|
int Assemble(runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder,
|
||||||
uint32_t debug_info_flags,
|
uint32_t debug_info_flags,
|
||||||
std::unique_ptr<runtime::DebugInfo> debug_info,
|
std::unique_ptr<runtime::DebugInfo> debug_info,
|
||||||
runtime::Function** out_function) override;
|
uint32_t trace_flags, runtime::Function** out_function) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DumpMachineCode(runtime::DebugInfo* debug_info, void* machine_code,
|
void DumpMachineCode(runtime::DebugInfo* debug_info, void* machine_code,
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <alloy/runtime/runtime.h>
|
#include <alloy/runtime/runtime.h>
|
||||||
#include <alloy/runtime/symbol_info.h>
|
#include <alloy/runtime/symbol_info.h>
|
||||||
#include <alloy/runtime/thread_state.h>
|
#include <alloy/runtime/thread_state.h>
|
||||||
|
#include <xdb/protocol.h>
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
|
@ -66,8 +67,8 @@ X64Emitter::~X64Emitter() {}
|
||||||
int X64Emitter::Initialize() { return 0; }
|
int X64Emitter::Initialize() { return 0; }
|
||||||
|
|
||||||
int X64Emitter::Emit(HIRBuilder* builder, uint32_t debug_info_flags,
|
int X64Emitter::Emit(HIRBuilder* builder, uint32_t debug_info_flags,
|
||||||
runtime::DebugInfo* debug_info, void*& out_code_address,
|
runtime::DebugInfo* debug_info, uint32_t trace_flags,
|
||||||
size_t& out_code_size) {
|
void*& out_code_address, size_t& out_code_size) {
|
||||||
SCOPE_profile_cpu_f("alloy");
|
SCOPE_profile_cpu_f("alloy");
|
||||||
|
|
||||||
// Reset.
|
// Reset.
|
||||||
|
@ -75,6 +76,7 @@ int X64Emitter::Emit(HIRBuilder* builder, uint32_t debug_info_flags,
|
||||||
source_map_count_ = 0;
|
source_map_count_ = 0;
|
||||||
source_map_arena_.Reset();
|
source_map_arena_.Reset();
|
||||||
}
|
}
|
||||||
|
trace_flags_ = trace_flags;
|
||||||
|
|
||||||
// Fill the generator with code.
|
// Fill the generator with code.
|
||||||
size_t stack_size = 0;
|
size_t stack_size = 0;
|
||||||
|
@ -151,6 +153,19 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
|
||||||
mov(rdx, qword[rcx + 8]); // membase
|
mov(rdx, qword[rcx + 8]); // membase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t trace_base = runtime_->memory()->trace_base();
|
||||||
|
if (trace_base && trace_flags_ & TRACE_USER_CALLS) {
|
||||||
|
mov(rax, trace_base);
|
||||||
|
mov(r8d, static_cast<uint32_t>(sizeof(xdb::protocol::UserCallEvent)));
|
||||||
|
lock();
|
||||||
|
xadd(qword[rax], r8);
|
||||||
|
mov(rax, static_cast<uint64_t>(xdb::protocol::EventType::USER_CALL) |
|
||||||
|
(static_cast<uint64_t>(0) << 8) | (0ull << 32));
|
||||||
|
mov(qword[r8], rax);
|
||||||
|
EmitGetCurrentThreadId();
|
||||||
|
mov(word[r8 + 2], ax);
|
||||||
|
}
|
||||||
|
|
||||||
// Body.
|
// Body.
|
||||||
auto block = builder->first_block();
|
auto block = builder->first_block();
|
||||||
while (block) {
|
while (block) {
|
||||||
|
@ -165,6 +180,16 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
|
||||||
const Instr* instr = block->instr_head;
|
const Instr* instr = block->instr_head;
|
||||||
while (instr) {
|
while (instr) {
|
||||||
const Instr* new_tail = instr;
|
const Instr* new_tail = instr;
|
||||||
|
|
||||||
|
// Special handling of TRACE_SOURCE.
|
||||||
|
if (instr->opcode == &OPCODE_TRACE_SOURCE_info) {
|
||||||
|
if (trace_flags_ & TRACE_SOURCE) {
|
||||||
|
EmitTraceSource(instr);
|
||||||
|
}
|
||||||
|
instr = instr->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!SelectSequence(*this, instr, &new_tail)) {
|
if (!SelectSequence(*this, instr, &new_tail)) {
|
||||||
// No sequence found!
|
// No sequence found!
|
||||||
assert_always();
|
assert_always();
|
||||||
|
@ -179,6 +204,7 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
|
||||||
|
|
||||||
// Function epilog.
|
// Function epilog.
|
||||||
L("epilog");
|
L("epilog");
|
||||||
|
EmitTraceUserCallReturn();
|
||||||
if (emit_prolog) {
|
if (emit_prolog) {
|
||||||
mov(rcx, qword[rsp + StackLayout::GUEST_RCX_HOME]);
|
mov(rcx, qword[rsp + StackLayout::GUEST_RCX_HOME]);
|
||||||
add(rsp, (uint32_t)stack_size);
|
add(rsp, (uint32_t)stack_size);
|
||||||
|
@ -204,6 +230,129 @@ void X64Emitter::MarkSourceOffset(const Instr* i) {
|
||||||
source_map_count_++;
|
source_map_count_++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void X64Emitter::EmitTraceSource(const Instr* instr) {
|
||||||
|
uint64_t trace_base = runtime_->memory()->trace_base();
|
||||||
|
if (!trace_base || !(trace_flags_ & TRACE_SOURCE)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(benvanik): make this a function call to some append fn.
|
||||||
|
|
||||||
|
uint8_t dest_reg_0 = instr->flags & 0xFF;
|
||||||
|
uint8_t dest_reg_1 = instr->flags >> 8;
|
||||||
|
|
||||||
|
xdb::protocol::EventType event_type;
|
||||||
|
size_t event_size;
|
||||||
|
if (dest_reg_0 == 100) {
|
||||||
|
event_type = xdb::protocol::EventType::INSTR;
|
||||||
|
event_size = sizeof(xdb::protocol::InstrEvent);
|
||||||
|
} else if (dest_reg_1 == 100) {
|
||||||
|
if (dest_reg_0 & (1 << 7)) {
|
||||||
|
event_type = xdb::protocol::EventType::INSTR_R16;
|
||||||
|
event_size = sizeof(xdb::protocol::InstrEventR16);
|
||||||
|
} else {
|
||||||
|
event_type = xdb::protocol::EventType::INSTR_R8;
|
||||||
|
event_size = sizeof(xdb::protocol::InstrEventR8);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (dest_reg_0 & (1 << 7) && dest_reg_1 & (1 << 7)) {
|
||||||
|
event_type = xdb::protocol::EventType::INSTR_R16_R16;
|
||||||
|
event_size = sizeof(xdb::protocol::InstrEventR16R16);
|
||||||
|
} else if (dest_reg_0 & (1 << 7) && !(dest_reg_1 & (1 << 7))) {
|
||||||
|
event_type = xdb::protocol::EventType::INSTR_R16_R8;
|
||||||
|
event_size = sizeof(xdb::protocol::InstrEventR16R8);
|
||||||
|
} else if (!(dest_reg_0 & (1 << 7)) && dest_reg_1 & (1 << 7)) {
|
||||||
|
event_type = xdb::protocol::EventType::INSTR_R8_R16;
|
||||||
|
event_size = sizeof(xdb::protocol::InstrEventR8R16);
|
||||||
|
} else if (!(dest_reg_0 & (1 << 7)) && !(dest_reg_1 & (1 << 7))) {
|
||||||
|
event_type = xdb::protocol::EventType::INSTR_R8_R8;
|
||||||
|
event_size = sizeof(xdb::protocol::InstrEventR8R8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mov(rax, trace_base);
|
||||||
|
mov(r8d, static_cast<uint32_t>(event_size));
|
||||||
|
lock();
|
||||||
|
xadd(qword[rax], r8);
|
||||||
|
// r8 is now the pointer where we can write our event.
|
||||||
|
|
||||||
|
// Write the header, which is the same for everything (pretty much).
|
||||||
|
// Some event types ignore the dest reg, and that's fine.
|
||||||
|
uint64_t qword_0 = static_cast<uint64_t>(event_type) |
|
||||||
|
(static_cast<uint64_t>(dest_reg_0) << 8) |
|
||||||
|
(instr->src1.offset << 32);
|
||||||
|
mov(rax, qword_0);
|
||||||
|
mov(qword[r8], rax);
|
||||||
|
|
||||||
|
// Write thread ID.
|
||||||
|
EmitGetCurrentThreadId();
|
||||||
|
mov(word[r8 + 2], ax);
|
||||||
|
|
||||||
|
switch (event_type) {
|
||||||
|
case xdb::protocol::EventType::INSTR:
|
||||||
|
break;
|
||||||
|
case xdb::protocol::EventType::INSTR_R8:
|
||||||
|
case xdb::protocol::EventType::INSTR_R16:
|
||||||
|
if (dest_reg_0 & (1 << 7)) {
|
||||||
|
EmitTraceSourceAppendValue(instr->src2.value, 8);
|
||||||
|
} else {
|
||||||
|
EmitTraceSourceAppendValue(instr->src2.value, 8);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case xdb::protocol::EventType::INSTR_R8_R8:
|
||||||
|
case xdb::protocol::EventType::INSTR_R8_R16:
|
||||||
|
case xdb::protocol::EventType::INSTR_R16_R8:
|
||||||
|
case xdb::protocol::EventType::INSTR_R16_R16:
|
||||||
|
mov(word[r8 + 8], dest_reg_0 | static_cast<uint16_t>(dest_reg_1 << 8));
|
||||||
|
size_t offset = 8;
|
||||||
|
if (dest_reg_0 & (1 << 7)) {
|
||||||
|
EmitTraceSourceAppendValue(instr->src2.value, offset);
|
||||||
|
offset += 16;
|
||||||
|
} else {
|
||||||
|
EmitTraceSourceAppendValue(instr->src2.value, offset);
|
||||||
|
offset += 8;
|
||||||
|
}
|
||||||
|
if (dest_reg_1 & (1 << 7)) {
|
||||||
|
EmitTraceSourceAppendValue(instr->src3.value, offset);
|
||||||
|
offset += 16;
|
||||||
|
} else {
|
||||||
|
EmitTraceSourceAppendValue(instr->src3.value, offset);
|
||||||
|
offset += 8;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void X64Emitter::EmitTraceSourceAppendValue(const Value* value,
|
||||||
|
size_t r8_offset) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void X64Emitter::EmitGetCurrentThreadId() {
|
||||||
|
// rcx must point to context. We could fetch from the stack if needed.
|
||||||
|
mov(ax,
|
||||||
|
word[rcx + runtime_->frontend()->context_info()->thread_id_offset()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void X64Emitter::EmitTraceUserCallReturn() {
|
||||||
|
auto trace_base = runtime_->memory()->trace_base();
|
||||||
|
if (!trace_base || !(trace_flags_ & TRACE_USER_CALLS)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mov(rdx, rax);
|
||||||
|
mov(rax, trace_base);
|
||||||
|
mov(r8d, static_cast<uint32_t>(sizeof(xdb::protocol::UserCallReturnEvent)));
|
||||||
|
lock();
|
||||||
|
xadd(qword[rax], r8);
|
||||||
|
mov(rax, static_cast<uint64_t>(xdb::protocol::EventType::USER_CALL_RETURN) |
|
||||||
|
(static_cast<uint64_t>(0) << 8) | (0ull << 32));
|
||||||
|
mov(qword[r8], rax);
|
||||||
|
EmitGetCurrentThreadId();
|
||||||
|
mov(word[r8 + 2], ax);
|
||||||
|
mov(rax, rdx);
|
||||||
|
ReloadEDX();
|
||||||
|
}
|
||||||
|
|
||||||
void X64Emitter::DebugBreak() {
|
void X64Emitter::DebugBreak() {
|
||||||
// TODO(benvanik): notify debugger.
|
// TODO(benvanik): notify debugger.
|
||||||
db(0xCC);
|
db(0xCC);
|
||||||
|
@ -258,7 +407,7 @@ uint64_t ResolveFunctionSymbol(void* raw_context, uint64_t symbol_info_ptr) {
|
||||||
#else
|
#else
|
||||||
uint64_t return_address =
|
uint64_t return_address =
|
||||||
reinterpret_cast<uint64_t>(__builtin_return_address(0));
|
reinterpret_cast<uint64_t>(__builtin_return_address(0));
|
||||||
#endif // XE_WIN32_LIKE
|
#endif // XE_LIKE_WIN32
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
struct Asm {
|
struct Asm {
|
||||||
uint16_t mov_rax;
|
uint16_t mov_rax;
|
||||||
|
@ -301,6 +450,9 @@ void X64Emitter::Call(const hir::Instr* instr,
|
||||||
|
|
||||||
// Actually jump/call to rax.
|
// Actually jump/call to rax.
|
||||||
if (instr->flags & CALL_TAIL) {
|
if (instr->flags & CALL_TAIL) {
|
||||||
|
// Since we skip the prolog we need to mark the return here.
|
||||||
|
EmitTraceUserCallReturn();
|
||||||
|
|
||||||
// Pass the callers return address over.
|
// Pass the callers return address over.
|
||||||
mov(rdx, qword[rsp + StackLayout::GUEST_RET_ADDR]);
|
mov(rdx, qword[rsp + StackLayout::GUEST_RET_ADDR]);
|
||||||
|
|
||||||
|
@ -338,7 +490,7 @@ uint64_t ResolveFunctionAddress(void* raw_context, uint64_t target_address) {
|
||||||
#else
|
#else
|
||||||
uint64_t return_address =
|
uint64_t return_address =
|
||||||
reinterpret_cast<uint64_t>(__builtin_return_address(0));
|
reinterpret_cast<uint64_t>(__builtin_return_address(0));
|
||||||
#endif // XE_WIN32_LIKE
|
#endif // XE_LIKE_WIN32
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
struct Asm {
|
struct Asm {
|
||||||
uint16_t cmp_rdx;
|
uint16_t cmp_rdx;
|
||||||
|
@ -426,6 +578,9 @@ void X64Emitter::CallIndirect(const hir::Instr* instr, const Reg64& reg) {
|
||||||
// Actually jump/call to rax.
|
// Actually jump/call to rax.
|
||||||
L(skip_resolve);
|
L(skip_resolve);
|
||||||
if (instr->flags & CALL_TAIL) {
|
if (instr->flags & CALL_TAIL) {
|
||||||
|
// Since we skip the prolog we need to mark the return here.
|
||||||
|
EmitTraceUserCallReturn();
|
||||||
|
|
||||||
// Pass the callers return address over.
|
// Pass the callers return address over.
|
||||||
mov(rdx, qword[rsp + StackLayout::GUEST_RET_ADDR]);
|
mov(rdx, qword[rsp + StackLayout::GUEST_RET_ADDR]);
|
||||||
|
|
||||||
|
@ -449,6 +604,24 @@ uint64_t UndefinedCallExtern(void* raw_context, uint64_t symbol_info_ptr) {
|
||||||
void X64Emitter::CallExtern(const hir::Instr* instr,
|
void X64Emitter::CallExtern(const hir::Instr* instr,
|
||||||
const FunctionInfo* symbol_info) {
|
const FunctionInfo* symbol_info) {
|
||||||
assert_true(symbol_info->behavior() == FunctionInfo::BEHAVIOR_EXTERN);
|
assert_true(symbol_info->behavior() == FunctionInfo::BEHAVIOR_EXTERN);
|
||||||
|
|
||||||
|
uint64_t trace_base = runtime_->memory()->trace_base();
|
||||||
|
if (trace_base & trace_flags_ & TRACE_EXTERN_CALLS) {
|
||||||
|
mov(rax, trace_base);
|
||||||
|
mov(r8d, static_cast<uint32_t>(sizeof(xdb::protocol::KernelCallEvent)));
|
||||||
|
lock();
|
||||||
|
xadd(qword[rax], r8);
|
||||||
|
// TODO(benvanik): get module/ordinal.
|
||||||
|
uint32_t module_id = 0;
|
||||||
|
uint32_t ordinal = 0;
|
||||||
|
mov(rax, static_cast<uint64_t>(xdb::protocol::EventType::KERNEL_CALL) |
|
||||||
|
(static_cast<uint64_t>(0) << 8) | (module_id << 16) |
|
||||||
|
(ordinal));
|
||||||
|
mov(qword[r8], rax);
|
||||||
|
EmitGetCurrentThreadId();
|
||||||
|
mov(word[r8 + 2], ax);
|
||||||
|
}
|
||||||
|
|
||||||
if (!symbol_info->extern_handler()) {
|
if (!symbol_info->extern_handler()) {
|
||||||
CallNative(UndefinedCallExtern, reinterpret_cast<uint64_t>(symbol_info));
|
CallNative(UndefinedCallExtern, reinterpret_cast<uint64_t>(symbol_info));
|
||||||
} else {
|
} else {
|
||||||
|
@ -466,6 +639,21 @@ void X64Emitter::CallExtern(const hir::Instr* instr,
|
||||||
ReloadEDX();
|
ReloadEDX();
|
||||||
// rax = host return
|
// rax = host return
|
||||||
}
|
}
|
||||||
|
if (trace_base && trace_flags_ & TRACE_EXTERN_CALLS) {
|
||||||
|
mov(rax, trace_base);
|
||||||
|
mov(r8d,
|
||||||
|
static_cast<uint32_t>(sizeof(xdb::protocol::KernelCallReturnEvent)));
|
||||||
|
lock();
|
||||||
|
xadd(qword[rax], r8);
|
||||||
|
uint32_t module_id = 0;
|
||||||
|
uint32_t ordinal = 0;
|
||||||
|
mov(rax,
|
||||||
|
static_cast<uint64_t>(xdb::protocol::EventType::KERNEL_CALL_RETURN) |
|
||||||
|
(static_cast<uint64_t>(0) << 8) | (0));
|
||||||
|
mov(qword[r8], rax);
|
||||||
|
EmitGetCurrentThreadId();
|
||||||
|
mov(word[r8 + 2], ax);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void X64Emitter::CallNative(void* fn) {
|
void X64Emitter::CallNative(void* fn) {
|
||||||
|
|
|
@ -84,8 +84,8 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||||
int Initialize();
|
int Initialize();
|
||||||
|
|
||||||
int Emit(hir::HIRBuilder* builder, uint32_t debug_info_flags,
|
int Emit(hir::HIRBuilder* builder, uint32_t debug_info_flags,
|
||||||
runtime::DebugInfo* debug_info, void*& out_code_address,
|
runtime::DebugInfo* debug_info, uint32_t trace_flags,
|
||||||
size_t& out_code_size);
|
void*& out_code_address, size_t& out_code_size);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Reserved: rsp
|
// Reserved: rsp
|
||||||
|
@ -163,6 +163,10 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||||
protected:
|
protected:
|
||||||
void* Emplace(size_t stack_size);
|
void* Emplace(size_t stack_size);
|
||||||
int Emit(hir::HIRBuilder* builder, size_t& out_stack_size);
|
int Emit(hir::HIRBuilder* builder, size_t& out_stack_size);
|
||||||
|
void EmitTraceSource(const hir::Instr* instr);
|
||||||
|
void EmitTraceSourceAppendValue(const hir::Value* value, size_t r8_offset);
|
||||||
|
void EmitGetCurrentThreadId();
|
||||||
|
void EmitTraceUserCallReturn();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
runtime::Runtime* runtime_;
|
runtime::Runtime* runtime_;
|
||||||
|
@ -177,6 +181,8 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||||
|
|
||||||
size_t stack_size_;
|
size_t stack_size_;
|
||||||
|
|
||||||
|
uint32_t trace_flags_;
|
||||||
|
|
||||||
static const uint32_t gpr_reg_map_[GPR_COUNT];
|
static const uint32_t gpr_reg_map_[GPR_COUNT];
|
||||||
static const uint32_t xmm_reg_map_[XMM_COUNT];
|
static const uint32_t xmm_reg_map_[XMM_COUNT];
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,8 +12,11 @@
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace frontend {
|
namespace frontend {
|
||||||
|
|
||||||
ContextInfo::ContextInfo(size_t size, uintptr_t thread_state_offset)
|
ContextInfo::ContextInfo(size_t size, uintptr_t thread_state_offset,
|
||||||
: size_(size), thread_state_offset_(thread_state_offset) {}
|
uintptr_t thread_id_offset)
|
||||||
|
: size_(size),
|
||||||
|
thread_state_offset_(thread_state_offset),
|
||||||
|
thread_id_offset_(thread_id_offset) {}
|
||||||
|
|
||||||
ContextInfo::~ContextInfo() {}
|
ContextInfo::~ContextInfo() {}
|
||||||
|
|
||||||
|
|
|
@ -17,16 +17,19 @@ namespace frontend {
|
||||||
|
|
||||||
class ContextInfo {
|
class ContextInfo {
|
||||||
public:
|
public:
|
||||||
ContextInfo(size_t size, uintptr_t thread_state_offset);
|
ContextInfo(size_t size, uintptr_t thread_state_offset,
|
||||||
|
uintptr_t thread_id_offset);
|
||||||
~ContextInfo();
|
~ContextInfo();
|
||||||
|
|
||||||
size_t size() const { return size_; }
|
size_t size() const { return size_; }
|
||||||
|
|
||||||
uintptr_t thread_state_offset() const { return thread_state_offset_; }
|
uintptr_t thread_state_offset() const { return thread_state_offset_; }
|
||||||
|
uintptr_t thread_id_offset() const { return thread_id_offset_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t size_;
|
size_t size_;
|
||||||
uintptr_t thread_state_offset_;
|
uintptr_t thread_state_offset_;
|
||||||
|
uintptr_t thread_id_offset_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace frontend
|
} // namespace frontend
|
||||||
|
|
|
@ -40,7 +40,7 @@ class Frontend {
|
||||||
|
|
||||||
virtual int DeclareFunction(runtime::FunctionInfo* symbol_info) = 0;
|
virtual int DeclareFunction(runtime::FunctionInfo* symbol_info) = 0;
|
||||||
virtual int DefineFunction(runtime::FunctionInfo* symbol_info,
|
virtual int DefineFunction(runtime::FunctionInfo* symbol_info,
|
||||||
uint32_t debug_info_flags,
|
uint32_t debug_info_flags, uint32_t trace_flags,
|
||||||
runtime::Function** out_function) = 0;
|
runtime::Function** out_function) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -13,55 +13,28 @@
|
||||||
#include <alloy/core.h>
|
#include <alloy/core.h>
|
||||||
#include <alloy/vec128.h>
|
#include <alloy/vec128.h>
|
||||||
|
|
||||||
|
|
||||||
namespace alloy { namespace runtime {
|
namespace alloy { namespace runtime {
|
||||||
class Runtime;
|
class Runtime;
|
||||||
class ThreadState;
|
class ThreadState;
|
||||||
} }
|
} }
|
||||||
|
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace frontend {
|
namespace frontend {
|
||||||
namespace ppc {
|
namespace ppc {
|
||||||
|
|
||||||
|
|
||||||
using vec128_t = alloy::vec128_t;
|
using vec128_t = alloy::vec128_t;
|
||||||
|
|
||||||
|
// Map:
|
||||||
typedef union {
|
// 0-31: GPR
|
||||||
uint32_t value;
|
// 32-63: FPR
|
||||||
struct {
|
// 64: LR
|
||||||
uint8_t lt :1; // Negative (LT) - result is negative
|
// 65: CTR
|
||||||
uint8_t gt :1; // Positive (GT) - result is positive (and not zero)
|
// 66: XER
|
||||||
uint8_t eq :1; // Zero (EQ) - result is zero or a stwcx/stdcx completed successfully
|
// 67: FPSCR
|
||||||
uint8_t so :1; // Summary Overflow (SO) - copy of XER[SO]
|
// 68: VSCR
|
||||||
} cr0;
|
// 69-76: CR0-7
|
||||||
struct {
|
// 100: invalid
|
||||||
uint8_t fx :1; // FP exception summary - copy of FPSCR[FX]
|
// 128-256: VR
|
||||||
uint8_t fex :1; // FP enabled exception summary - copy of FPSCR[FEX]
|
|
||||||
uint8_t vx :1; // FP invalid operation exception summary - copy of FPSCR[VX]
|
|
||||||
uint8_t ox :1; // FP overflow exception - copy of FPSCR[OX]
|
|
||||||
} cr1;
|
|
||||||
struct {
|
|
||||||
uint8_t value :4;
|
|
||||||
} cr2;
|
|
||||||
struct {
|
|
||||||
uint8_t value :4;
|
|
||||||
} cr3;
|
|
||||||
struct {
|
|
||||||
uint8_t value :4;
|
|
||||||
} cr4;
|
|
||||||
struct {
|
|
||||||
uint8_t value :4;
|
|
||||||
} cr5;
|
|
||||||
struct {
|
|
||||||
uint8_t value :4;
|
|
||||||
} cr6;
|
|
||||||
struct {
|
|
||||||
uint8_t value :4;
|
|
||||||
} cr7;
|
|
||||||
} PPCCR;
|
|
||||||
|
|
||||||
|
|
||||||
#pragma pack(push, 4)
|
#pragma pack(push, 4)
|
||||||
typedef struct XECACHEALIGN64 PPCContext_s {
|
typedef struct XECACHEALIGN64 PPCContext_s {
|
||||||
|
@ -194,12 +167,15 @@ typedef struct XECACHEALIGN64 PPCContext_s {
|
||||||
// fpscr.value = (fpscr.value & ~0x000F8000) | v;
|
// fpscr.value = (fpscr.value & ~0x000F8000) | v;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// Thread ID assigned to this context.
|
||||||
|
uint32_t thread_id;
|
||||||
|
|
||||||
// Reserve address for load acquire/store release. Shared.
|
// Reserve address for load acquire/store release. Shared.
|
||||||
uint32_t* reserve_address;
|
uint32_t* reserve_address;
|
||||||
|
|
||||||
// Runtime-specific data pointer. Used on callbacks to get access to the
|
// Runtime-specific data pointer. Used on callbacks to get access to the
|
||||||
// current runtime and its data.
|
// current runtime and its data.
|
||||||
runtime::Runtime* runtime;
|
runtime::Runtime* runtime;
|
||||||
|
|
||||||
void SetRegFromString(const char* name, const char* value);
|
void SetRegFromString(const char* name, const char* value);
|
||||||
bool CompareRegWithString(const char* name, const char* value,
|
bool CompareRegWithString(const char* name, const char* value,
|
||||||
|
@ -207,10 +183,8 @@ typedef struct XECACHEALIGN64 PPCContext_s {
|
||||||
} PPCContext;
|
} PPCContext;
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
|
||||||
} // namespace ppc
|
} // namespace ppc
|
||||||
} // namespace frontend
|
} // namespace frontend
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
||||||
|
|
||||||
#endif // ALLOY_FRONTEND_PPC_PPC_CONTEXT_H_
|
#endif // ALLOY_FRONTEND_PPC_PPC_CONTEXT_H_
|
||||||
|
|
|
@ -297,8 +297,8 @@ XEEMITTER(fnmsubsx, 0xEC00003C, A)(PPCHIRBuilder& f, InstrData& i) {
|
||||||
|
|
||||||
XEEMITTER(fcfidx, 0xFC00069C, X)(PPCHIRBuilder& f, InstrData& i) {
|
XEEMITTER(fcfidx, 0xFC00069C, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||||
// frD <- signed_int64_to_double( frB )
|
// frD <- signed_int64_to_double( frB )
|
||||||
Value* v = f.Convert(f.Cast(f.LoadFPR(i.A.FRB), INT64_TYPE), FLOAT64_TYPE);
|
Value* v = f.Convert(f.Cast(f.LoadFPR(i.X.RB), INT64_TYPE), FLOAT64_TYPE);
|
||||||
f.StoreFPR(i.A.FRT, v);
|
f.StoreFPR(i.X.RT, v);
|
||||||
// f.UpdateFPRF(v);
|
// f.UpdateFPRF(v);
|
||||||
if (i.A.Rc) {
|
if (i.A.Rc) {
|
||||||
// e.update_cr_with_cond(1, v);
|
// e.update_cr_with_cond(1, v);
|
||||||
|
@ -337,7 +337,7 @@ XEEMITTER(fctiwx, 0xFC00001C, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||||
v = f.Cast(f.ZeroExtend(v, INT64_TYPE), FLOAT64_TYPE);
|
v = f.Cast(f.ZeroExtend(v, INT64_TYPE), FLOAT64_TYPE);
|
||||||
f.StoreFPR(i.X.RT, v);
|
f.StoreFPR(i.X.RT, v);
|
||||||
// f.UpdateFPRF(v);
|
// f.UpdateFPRF(v);
|
||||||
if (i.A.Rc) {
|
if (i.X.Rc) {
|
||||||
// e.update_cr_with_cond(1, v);
|
// e.update_cr_with_cond(1, v);
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -409,7 +409,8 @@ XEEMITTER(mffsx, 0xFC00048E, X)(PPCHIRBuilder& f, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
f.StoreFPR(i.X.RT, f.Cast(f.LoadFPSCR(), FLOAT64_TYPE));
|
Value* v = f.Cast(f.LoadFPSCR(), FLOAT64_TYPE);
|
||||||
|
f.StoreFPR(i.X.RT, v);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,8 @@ PPCFrontend::PPCFrontend(Runtime* runtime) : Frontend(runtime) {
|
||||||
InitializeIfNeeded();
|
InitializeIfNeeded();
|
||||||
|
|
||||||
std::unique_ptr<ContextInfo> context_info(
|
std::unique_ptr<ContextInfo> context_info(
|
||||||
new ContextInfo(sizeof(PPCContext), offsetof(PPCContext, thread_state)));
|
new ContextInfo(sizeof(PPCContext), offsetof(PPCContext, thread_state),
|
||||||
|
offsetof(PPCContext, thread_id)));
|
||||||
// Add fields/etc.
|
// Add fields/etc.
|
||||||
context_info_ = std::move(context_info);
|
context_info_ = std::move(context_info);
|
||||||
}
|
}
|
||||||
|
@ -77,10 +78,11 @@ int PPCFrontend::DeclareFunction(FunctionInfo* symbol_info) {
|
||||||
|
|
||||||
int PPCFrontend::DefineFunction(FunctionInfo* symbol_info,
|
int PPCFrontend::DefineFunction(FunctionInfo* symbol_info,
|
||||||
uint32_t debug_info_flags,
|
uint32_t debug_info_flags,
|
||||||
|
uint32_t trace_flags,
|
||||||
Function** out_function) {
|
Function** out_function) {
|
||||||
PPCTranslator* translator = translator_pool_.Allocate(this);
|
PPCTranslator* translator = translator_pool_.Allocate(this);
|
||||||
int result =
|
int result =
|
||||||
translator->Translate(symbol_info, debug_info_flags, out_function);
|
translator->Translate(symbol_info, debug_info_flags, trace_flags, out_function);
|
||||||
translator_pool_.Release(translator);
|
translator_pool_.Release(translator);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,14 +24,14 @@ class PPCTranslator;
|
||||||
class PPCFrontend : public Frontend {
|
class PPCFrontend : public Frontend {
|
||||||
public:
|
public:
|
||||||
PPCFrontend(runtime::Runtime* runtime);
|
PPCFrontend(runtime::Runtime* runtime);
|
||||||
virtual ~PPCFrontend();
|
~PPCFrontend() override;
|
||||||
|
|
||||||
virtual int Initialize();
|
int Initialize() override;
|
||||||
|
|
||||||
virtual int DeclareFunction(runtime::FunctionInfo* symbol_info);
|
int DeclareFunction(runtime::FunctionInfo* symbol_info) override;
|
||||||
virtual int DefineFunction(runtime::FunctionInfo* symbol_info,
|
int DefineFunction(runtime::FunctionInfo* symbol_info,
|
||||||
uint32_t debug_info_flags,
|
uint32_t debug_info_flags, uint32_t trace_flags,
|
||||||
runtime::Function** out_function);
|
runtime::Function** out_function) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TypePool<PPCTranslator, PPCFrontend*> translator_pool_;
|
TypePool<PPCTranslator, PPCFrontend*> translator_pool_;
|
||||||
|
|
|
@ -43,7 +43,7 @@ void PPCHIRBuilder::Reset() {
|
||||||
HIRBuilder::Reset();
|
HIRBuilder::Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
int PPCHIRBuilder::Emit(FunctionInfo* symbol_info, bool with_debug_info) {
|
int PPCHIRBuilder::Emit(FunctionInfo* symbol_info, uint32_t flags) {
|
||||||
SCOPE_profile_cpu_f("alloy");
|
SCOPE_profile_cpu_f("alloy");
|
||||||
|
|
||||||
Memory* memory = frontend_->memory();
|
Memory* memory = frontend_->memory();
|
||||||
|
@ -53,7 +53,7 @@ int PPCHIRBuilder::Emit(FunctionInfo* symbol_info, bool with_debug_info) {
|
||||||
start_address_ = symbol_info->address();
|
start_address_ = symbol_info->address();
|
||||||
instr_count_ = (symbol_info->end_address() - symbol_info->address()) / 4 + 1;
|
instr_count_ = (symbol_info->end_address() - symbol_info->address()) / 4 + 1;
|
||||||
|
|
||||||
with_debug_info_ = with_debug_info;
|
with_debug_info_ = (flags & EMIT_DEBUG_COMMENTS) == EMIT_DEBUG_COMMENTS;
|
||||||
if (with_debug_info_) {
|
if (with_debug_info_) {
|
||||||
Comment("%s fn %.8X-%.8X %s", symbol_info->module()->name().c_str(),
|
Comment("%s fn %.8X-%.8X %s", symbol_info->module()->name().c_str(),
|
||||||
symbol_info->address(), symbol_info->end_address(),
|
symbol_info->address(), symbol_info->end_address(),
|
||||||
|
@ -84,6 +84,7 @@ int PPCHIRBuilder::Emit(FunctionInfo* symbol_info, bool with_debug_info) {
|
||||||
i.code = poly::load_and_swap<uint32_t>(p + address);
|
i.code = poly::load_and_swap<uint32_t>(p + address);
|
||||||
// TODO(benvanik): find a way to avoid using the opcode tables.
|
// TODO(benvanik): find a way to avoid using the opcode tables.
|
||||||
i.type = GetInstrType(i.code);
|
i.type = GetInstrType(i.code);
|
||||||
|
trace_info_.dest_count = 0;
|
||||||
|
|
||||||
// Mark label, if we were assigned one earlier on in the walk.
|
// Mark label, if we were assigned one earlier on in the walk.
|
||||||
// We may still get a label, but it'll be inserted by LookupLabel
|
// We may still get a label, but it'll be inserted by LookupLabel
|
||||||
|
@ -136,6 +137,30 @@ int PPCHIRBuilder::Emit(FunctionInfo* symbol_info, bool with_debug_info) {
|
||||||
// DebugBreak();
|
// DebugBreak();
|
||||||
// TraceInvalidInstruction(i);
|
// TraceInvalidInstruction(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flags & EMIT_TRACE_SOURCE) {
|
||||||
|
if (flags & EMIT_TRACE_SOURCE_VALUES) {
|
||||||
|
switch (trace_info_.dest_count) {
|
||||||
|
case 0:
|
||||||
|
TraceSource(i.address);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
TraceSource(i.address, trace_info_.dests[0].reg,
|
||||||
|
trace_info_.dests[0].value);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
TraceSource(i.address, trace_info_.dests[0].reg,
|
||||||
|
trace_info_.dests[0].value, trace_info_.dests[1].reg,
|
||||||
|
trace_info_.dests[1].value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert_unhandled_case(trace_info_.dest_count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TraceSource(i.address);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Finalize();
|
return Finalize();
|
||||||
|
@ -205,6 +230,10 @@ Value* PPCHIRBuilder::LoadLR() {
|
||||||
void PPCHIRBuilder::StoreLR(Value* value) {
|
void PPCHIRBuilder::StoreLR(Value* value) {
|
||||||
assert_true(value->type == INT64_TYPE);
|
assert_true(value->type == INT64_TYPE);
|
||||||
StoreContext(offsetof(PPCContext, lr), value);
|
StoreContext(offsetof(PPCContext, lr), value);
|
||||||
|
|
||||||
|
auto& trace_reg = trace_info_.dests[trace_info_.dest_count++];
|
||||||
|
trace_reg.reg = 64;
|
||||||
|
trace_reg.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* PPCHIRBuilder::LoadCTR() {
|
Value* PPCHIRBuilder::LoadCTR() {
|
||||||
|
@ -214,6 +243,10 @@ Value* PPCHIRBuilder::LoadCTR() {
|
||||||
void PPCHIRBuilder::StoreCTR(Value* value) {
|
void PPCHIRBuilder::StoreCTR(Value* value) {
|
||||||
assert_true(value->type == INT64_TYPE);
|
assert_true(value->type == INT64_TYPE);
|
||||||
StoreContext(offsetof(PPCContext, ctr), value);
|
StoreContext(offsetof(PPCContext, ctr), value);
|
||||||
|
|
||||||
|
auto& trace_reg = trace_info_.dests[trace_info_.dest_count++];
|
||||||
|
trace_reg.reg = 65;
|
||||||
|
trace_reg.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* PPCHIRBuilder::LoadCR(uint32_t n) {
|
Value* PPCHIRBuilder::LoadCR(uint32_t n) {
|
||||||
|
@ -232,6 +265,8 @@ void PPCHIRBuilder::StoreCR(uint32_t n, Value* value) {
|
||||||
|
|
||||||
void PPCHIRBuilder::StoreCRField(uint32_t n, uint32_t bit, Value* value) {
|
void PPCHIRBuilder::StoreCRField(uint32_t n, uint32_t bit, Value* value) {
|
||||||
StoreContext(offsetof(PPCContext, cr0) + (4 * n) + bit, value);
|
StoreContext(offsetof(PPCContext, cr0) + (4 * n) + bit, value);
|
||||||
|
|
||||||
|
// TODO(benvanik): trace CR.
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPCHIRBuilder::UpdateCR(uint32_t n, Value* lhs, bool is_signed) {
|
void PPCHIRBuilder::UpdateCR(uint32_t n, Value* lhs, bool is_signed) {
|
||||||
|
@ -256,6 +291,8 @@ void PPCHIRBuilder::UpdateCR(uint32_t n, Value* lhs, Value* rhs,
|
||||||
|
|
||||||
// Value* so = AllocValue(UINT8_TYPE);
|
// Value* so = AllocValue(UINT8_TYPE);
|
||||||
// StoreContext(offsetof(PPCContext, cr) + (4 * n) + 3, so);
|
// StoreContext(offsetof(PPCContext, cr) + (4 * n) + 3, so);
|
||||||
|
|
||||||
|
// TOOD(benvanik): trace CR.
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPCHIRBuilder::UpdateCR6(Value* src_value) {
|
void PPCHIRBuilder::UpdateCR6(Value* src_value) {
|
||||||
|
@ -265,6 +302,8 @@ void PPCHIRBuilder::UpdateCR6(Value* src_value) {
|
||||||
StoreContext(offsetof(PPCContext, cr6.cr6_all_equal),
|
StoreContext(offsetof(PPCContext, cr6.cr6_all_equal),
|
||||||
IsFalse(Not(src_value)));
|
IsFalse(Not(src_value)));
|
||||||
StoreContext(offsetof(PPCContext, cr6.cr6_none_equal), IsFalse(src_value));
|
StoreContext(offsetof(PPCContext, cr6.cr6_none_equal), IsFalse(src_value));
|
||||||
|
|
||||||
|
// TOOD(benvanik): trace CR.
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* PPCHIRBuilder::LoadFPSCR() {
|
Value* PPCHIRBuilder::LoadFPSCR() {
|
||||||
|
@ -274,6 +313,10 @@ Value* PPCHIRBuilder::LoadFPSCR() {
|
||||||
void PPCHIRBuilder::StoreFPSCR(Value* value) {
|
void PPCHIRBuilder::StoreFPSCR(Value* value) {
|
||||||
assert_true(value->type == INT64_TYPE);
|
assert_true(value->type == INT64_TYPE);
|
||||||
StoreContext(offsetof(PPCContext, fpscr), value);
|
StoreContext(offsetof(PPCContext, fpscr), value);
|
||||||
|
|
||||||
|
auto& trace_reg = trace_info_.dests[trace_info_.dest_count++];
|
||||||
|
trace_reg.reg = 67;
|
||||||
|
trace_reg.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* PPCHIRBuilder::LoadXER() {
|
Value* PPCHIRBuilder::LoadXER() {
|
||||||
|
@ -290,6 +333,10 @@ Value* PPCHIRBuilder::LoadCA() {
|
||||||
void PPCHIRBuilder::StoreCA(Value* value) {
|
void PPCHIRBuilder::StoreCA(Value* value) {
|
||||||
assert_true(value->type == INT8_TYPE);
|
assert_true(value->type == INT8_TYPE);
|
||||||
StoreContext(offsetof(PPCContext, xer_ca), value);
|
StoreContext(offsetof(PPCContext, xer_ca), value);
|
||||||
|
|
||||||
|
auto& trace_reg = trace_info_.dests[trace_info_.dest_count++];
|
||||||
|
trace_reg.reg = 66;
|
||||||
|
trace_reg.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* PPCHIRBuilder::LoadSAT() {
|
Value* PPCHIRBuilder::LoadSAT() {
|
||||||
|
@ -299,6 +346,10 @@ Value* PPCHIRBuilder::LoadSAT() {
|
||||||
void PPCHIRBuilder::StoreSAT(Value* value) {
|
void PPCHIRBuilder::StoreSAT(Value* value) {
|
||||||
value = Truncate(value, INT8_TYPE);
|
value = Truncate(value, INT8_TYPE);
|
||||||
StoreContext(offsetof(PPCContext, vscr_sat), value);
|
StoreContext(offsetof(PPCContext, vscr_sat), value);
|
||||||
|
|
||||||
|
auto& trace_reg = trace_info_.dests[trace_info_.dest_count++];
|
||||||
|
trace_reg.reg = 44;
|
||||||
|
trace_reg.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* PPCHIRBuilder::LoadGPR(uint32_t reg) {
|
Value* PPCHIRBuilder::LoadGPR(uint32_t reg) {
|
||||||
|
@ -308,6 +359,10 @@ Value* PPCHIRBuilder::LoadGPR(uint32_t reg) {
|
||||||
void PPCHIRBuilder::StoreGPR(uint32_t reg, Value* value) {
|
void PPCHIRBuilder::StoreGPR(uint32_t reg, Value* value) {
|
||||||
assert_true(value->type == INT64_TYPE);
|
assert_true(value->type == INT64_TYPE);
|
||||||
StoreContext(offsetof(PPCContext, r) + reg * 8, value);
|
StoreContext(offsetof(PPCContext, r) + reg * 8, value);
|
||||||
|
|
||||||
|
auto& trace_reg = trace_info_.dests[trace_info_.dest_count++];
|
||||||
|
trace_reg.reg = reg;
|
||||||
|
trace_reg.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* PPCHIRBuilder::LoadFPR(uint32_t reg) {
|
Value* PPCHIRBuilder::LoadFPR(uint32_t reg) {
|
||||||
|
@ -317,6 +372,10 @@ Value* PPCHIRBuilder::LoadFPR(uint32_t reg) {
|
||||||
void PPCHIRBuilder::StoreFPR(uint32_t reg, Value* value) {
|
void PPCHIRBuilder::StoreFPR(uint32_t reg, Value* value) {
|
||||||
assert_true(value->type == FLOAT64_TYPE);
|
assert_true(value->type == FLOAT64_TYPE);
|
||||||
StoreContext(offsetof(PPCContext, f) + reg * 8, value);
|
StoreContext(offsetof(PPCContext, f) + reg * 8, value);
|
||||||
|
|
||||||
|
auto& trace_reg = trace_info_.dests[trace_info_.dest_count++];
|
||||||
|
trace_reg.reg = reg + 32;
|
||||||
|
trace_reg.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* PPCHIRBuilder::LoadVR(uint32_t reg) {
|
Value* PPCHIRBuilder::LoadVR(uint32_t reg) {
|
||||||
|
@ -326,6 +385,10 @@ Value* PPCHIRBuilder::LoadVR(uint32_t reg) {
|
||||||
void PPCHIRBuilder::StoreVR(uint32_t reg, Value* value) {
|
void PPCHIRBuilder::StoreVR(uint32_t reg, Value* value) {
|
||||||
assert_true(value->type == VEC128_TYPE);
|
assert_true(value->type == VEC128_TYPE);
|
||||||
StoreContext(offsetof(PPCContext, v) + reg * 16, value);
|
StoreContext(offsetof(PPCContext, v) + reg * 16, value);
|
||||||
|
|
||||||
|
auto& trace_reg = trace_info_.dests[trace_info_.dest_count++];
|
||||||
|
trace_reg.reg = 128 + reg;
|
||||||
|
trace_reg.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* PPCHIRBuilder::LoadAcquire(Value* address, TypeName type,
|
Value* PPCHIRBuilder::LoadAcquire(Value* address, TypeName type,
|
||||||
|
|
|
@ -33,7 +33,15 @@ class PPCHIRBuilder : public hir::HIRBuilder {
|
||||||
|
|
||||||
virtual void Reset();
|
virtual void Reset();
|
||||||
|
|
||||||
int Emit(runtime::FunctionInfo* symbol_info, bool with_debug_info);
|
enum EmitFlags {
|
||||||
|
// Emit comment nodes.
|
||||||
|
EMIT_DEBUG_COMMENTS = 1 << 0,
|
||||||
|
// Emit TraceSource nodes.
|
||||||
|
EMIT_TRACE_SOURCE = 1 << 1,
|
||||||
|
// Emit TraceSource nodes with the resulting values of the operations.
|
||||||
|
EMIT_TRACE_SOURCE_VALUES = EMIT_TRACE_SOURCE | (1 << 2),
|
||||||
|
};
|
||||||
|
int Emit(runtime::FunctionInfo* symbol_info, uint32_t flags);
|
||||||
|
|
||||||
runtime::FunctionInfo* symbol_info() const { return symbol_info_; }
|
runtime::FunctionInfo* symbol_info() const { return symbol_info_; }
|
||||||
runtime::FunctionInfo* LookupFunction(uint64_t address);
|
runtime::FunctionInfo* LookupFunction(uint64_t address);
|
||||||
|
@ -89,6 +97,15 @@ class PPCHIRBuilder : public hir::HIRBuilder {
|
||||||
uint64_t instr_count_;
|
uint64_t instr_count_;
|
||||||
Instr** instr_offset_list_;
|
Instr** instr_offset_list_;
|
||||||
Label** label_list_;
|
Label** label_list_;
|
||||||
|
|
||||||
|
// Reset each instruction.
|
||||||
|
struct {
|
||||||
|
uint32_t dest_count;
|
||||||
|
struct {
|
||||||
|
uint8_t reg;
|
||||||
|
Value* value;
|
||||||
|
} dests[4];
|
||||||
|
} trace_info_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ppc
|
} // namespace ppc
|
||||||
|
|
|
@ -85,7 +85,7 @@ PPCTranslator::PPCTranslator(PPCFrontend* frontend) : frontend_(frontend) {
|
||||||
PPCTranslator::~PPCTranslator() = default;
|
PPCTranslator::~PPCTranslator() = default;
|
||||||
|
|
||||||
int PPCTranslator::Translate(FunctionInfo* symbol_info,
|
int PPCTranslator::Translate(FunctionInfo* symbol_info,
|
||||||
uint32_t debug_info_flags,
|
uint32_t debug_info_flags, uint32_t trace_flags,
|
||||||
Function** out_function) {
|
Function** out_function) {
|
||||||
SCOPE_profile_cpu_f("alloy");
|
SCOPE_profile_cpu_f("alloy");
|
||||||
|
|
||||||
|
@ -124,7 +124,16 @@ int PPCTranslator::Translate(FunctionInfo* symbol_info,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit function.
|
// Emit function.
|
||||||
int result = builder_->Emit(symbol_info, debug_info != nullptr);
|
uint32_t emit_flags = 0;
|
||||||
|
if (debug_info) {
|
||||||
|
emit_flags |= PPCHIRBuilder::EMIT_DEBUG_COMMENTS;
|
||||||
|
}
|
||||||
|
if (trace_flags & TRACE_SOURCE_VALUES) {
|
||||||
|
emit_flags |= PPCHIRBuilder::EMIT_TRACE_SOURCE_VALUES;
|
||||||
|
} else if (trace_flags & TRACE_SOURCE) {
|
||||||
|
emit_flags |= PPCHIRBuilder::EMIT_TRACE_SOURCE;
|
||||||
|
}
|
||||||
|
int result = builder_->Emit(symbol_info, emit_flags);
|
||||||
if (result) {
|
if (result) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -150,8 +159,9 @@ int PPCTranslator::Translate(FunctionInfo* symbol_info,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assemble to backend machine code.
|
// Assemble to backend machine code.
|
||||||
result = assembler_->Assemble(symbol_info, builder_.get(), debug_info_flags,
|
result =
|
||||||
std::move(debug_info), out_function);
|
assembler_->Assemble(symbol_info, builder_.get(), debug_info_flags,
|
||||||
|
std::move(debug_info), trace_flags, out_function);
|
||||||
if (result) {
|
if (result) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ class PPCTranslator {
|
||||||
~PPCTranslator();
|
~PPCTranslator();
|
||||||
|
|
||||||
int Translate(runtime::FunctionInfo* symbol_info, uint32_t debug_info_flags,
|
int Translate(runtime::FunctionInfo* symbol_info, uint32_t debug_info_flags,
|
||||||
runtime::Function** out_function);
|
uint32_t trace_flags, runtime::Function** out_function);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DumpSource(runtime::FunctionInfo* symbol_info,
|
void DumpSource(runtime::FunctionInfo* symbol_info,
|
||||||
|
|
|
@ -649,6 +649,28 @@ void HIRBuilder::SourceOffset(uint64_t offset) {
|
||||||
i->src2.value = i->src3.value = NULL;
|
i->src2.value = i->src3.value = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HIRBuilder::TraceSource(uint64_t offset) {
|
||||||
|
Instr* i = AppendInstr(OPCODE_TRACE_SOURCE_info, 100 | (100 << 8));
|
||||||
|
i->src1.offset = offset;
|
||||||
|
i->set_src2(LoadZero(INT64_TYPE));
|
||||||
|
i->set_src3(LoadZero(INT64_TYPE));
|
||||||
|
}
|
||||||
|
|
||||||
|
void HIRBuilder::TraceSource(uint64_t offset, uint8_t index, Value* value) {
|
||||||
|
Instr* i = AppendInstr(OPCODE_TRACE_SOURCE_info, index | (100 << 8));
|
||||||
|
i->src1.offset = offset;
|
||||||
|
i->set_src2(value);
|
||||||
|
i->set_src3(LoadZero(INT64_TYPE));
|
||||||
|
}
|
||||||
|
|
||||||
|
void HIRBuilder::TraceSource(uint64_t offset, uint8_t index_0, Value* value_0,
|
||||||
|
uint8_t index_1, Value* value_1) {
|
||||||
|
Instr* i = AppendInstr(OPCODE_TRACE_SOURCE_info, index_0 | (index_1 << 8));
|
||||||
|
i->src1.offset = offset;
|
||||||
|
i->set_src2(value_0);
|
||||||
|
i->set_src3(value_1);
|
||||||
|
}
|
||||||
|
|
||||||
void HIRBuilder::DebugBreak() {
|
void HIRBuilder::DebugBreak() {
|
||||||
Instr* i = AppendInstr(OPCODE_DEBUG_BREAK_info, 0);
|
Instr* i = AppendInstr(OPCODE_DEBUG_BREAK_info, 0);
|
||||||
i->src1.value = i->src2.value = i->src3.value = NULL;
|
i->src1.value = i->src2.value = i->src3.value = NULL;
|
||||||
|
@ -1743,11 +1765,13 @@ Value* HIRBuilder::RotateLeft(Value* value1, Value* value2) {
|
||||||
return i->dest;
|
return i->dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* HIRBuilder::VectorRotateLeft(Value* value1, Value* value2, TypeName part_type) {
|
Value* HIRBuilder::VectorRotateLeft(Value* value1, Value* value2,
|
||||||
|
TypeName part_type) {
|
||||||
ASSERT_VECTOR_TYPE(value1);
|
ASSERT_VECTOR_TYPE(value1);
|
||||||
ASSERT_VECTOR_TYPE(value2);
|
ASSERT_VECTOR_TYPE(value2);
|
||||||
|
|
||||||
Instr* i = AppendInstr(OPCODE_VECTOR_ROTATE_LEFT_info, part_type, AllocValue(value1->type));
|
Instr* i = AppendInstr(OPCODE_VECTOR_ROTATE_LEFT_info, part_type,
|
||||||
|
AllocValue(value1->type));
|
||||||
i->set_src1(value1);
|
i->set_src1(value1);
|
||||||
i->set_src2(value2);
|
i->set_src2(value2);
|
||||||
i->src3.value = NULL;
|
i->src3.value = NULL;
|
||||||
|
|
|
@ -71,6 +71,10 @@ class HIRBuilder {
|
||||||
void Nop();
|
void Nop();
|
||||||
|
|
||||||
void SourceOffset(uint64_t offset);
|
void SourceOffset(uint64_t offset);
|
||||||
|
void TraceSource(uint64_t offset);
|
||||||
|
void TraceSource(uint64_t offset, uint8_t index, Value* value);
|
||||||
|
void TraceSource(uint64_t offset, uint8_t index_0, Value* value_0,
|
||||||
|
uint8_t index_1, Value* value_1);
|
||||||
|
|
||||||
// trace info/etc
|
// trace info/etc
|
||||||
void DebugBreak();
|
void DebugBreak();
|
||||||
|
|
|
@ -77,6 +77,7 @@ enum Opcode {
|
||||||
OPCODE_COMMENT,
|
OPCODE_COMMENT,
|
||||||
OPCODE_NOP,
|
OPCODE_NOP,
|
||||||
OPCODE_SOURCE_OFFSET,
|
OPCODE_SOURCE_OFFSET,
|
||||||
|
OPCODE_TRACE_SOURCE,
|
||||||
OPCODE_DEBUG_BREAK,
|
OPCODE_DEBUG_BREAK,
|
||||||
OPCODE_DEBUG_BREAK_TRUE,
|
OPCODE_DEBUG_BREAK_TRUE,
|
||||||
OPCODE_TRAP,
|
OPCODE_TRAP,
|
||||||
|
@ -207,6 +208,8 @@ enum OpcodeSignature {
|
||||||
OPCODE_SIG_X_O = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_O << 3),
|
OPCODE_SIG_X_O = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_O << 3),
|
||||||
OPCODE_SIG_X_O_V =
|
OPCODE_SIG_X_O_V =
|
||||||
(OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_O << 3) | (OPCODE_SIG_TYPE_V << 6),
|
(OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_O << 3) | (OPCODE_SIG_TYPE_V << 6),
|
||||||
|
OPCODE_SIG_X_O_V_V = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_O << 3) |
|
||||||
|
(OPCODE_SIG_TYPE_V << 6) | (OPCODE_SIG_TYPE_V << 9),
|
||||||
OPCODE_SIG_X_S = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_S << 3),
|
OPCODE_SIG_X_S = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_S << 3),
|
||||||
OPCODE_SIG_X_V = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_V << 3),
|
OPCODE_SIG_X_V = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_V << 3),
|
||||||
OPCODE_SIG_X_V_L =
|
OPCODE_SIG_X_V_L =
|
||||||
|
|
|
@ -26,6 +26,12 @@ DEFINE_OPCODE(
|
||||||
OPCODE_SIG_X_O,
|
OPCODE_SIG_X_O,
|
||||||
OPCODE_FLAG_IGNORE | OPCODE_FLAG_HIDE)
|
OPCODE_FLAG_IGNORE | OPCODE_FLAG_HIDE)
|
||||||
|
|
||||||
|
DEFINE_OPCODE(
|
||||||
|
OPCODE_TRACE_SOURCE,
|
||||||
|
"trace_source",
|
||||||
|
OPCODE_SIG_X_O_V_V,
|
||||||
|
OPCODE_FLAG_IGNORE | OPCODE_FLAG_HIDE)
|
||||||
|
|
||||||
DEFINE_OPCODE(
|
DEFINE_OPCODE(
|
||||||
OPCODE_DEBUG_BREAK,
|
OPCODE_DEBUG_BREAK,
|
||||||
"debug_break",
|
"debug_break",
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
|
|
||||||
Memory::Memory() : membase_(nullptr), reserve_address_(0) {
|
Memory::Memory() : membase_(nullptr), reserve_address_(0), trace_base_(0) {
|
||||||
system_page_size_ = poly::page_size();
|
system_page_size_ = poly::page_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,9 @@ class Memory {
|
||||||
|
|
||||||
virtual uint64_t page_table() const = 0;
|
virtual uint64_t page_table() const = 0;
|
||||||
|
|
||||||
|
uint64_t trace_base() const { return trace_base_; }
|
||||||
|
void set_trace_base(uint64_t value) { trace_base_ = value; }
|
||||||
|
|
||||||
virtual int Initialize();
|
virtual int Initialize();
|
||||||
|
|
||||||
void Zero(uint64_t address, size_t size);
|
void Zero(uint64_t address, size_t size);
|
||||||
|
@ -65,6 +68,7 @@ class Memory {
|
||||||
size_t system_page_size_;
|
size_t system_page_size_;
|
||||||
uint8_t* membase_;
|
uint8_t* membase_;
|
||||||
uint32_t reserve_address_;
|
uint32_t reserve_address_;
|
||||||
|
uint64_t trace_base_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace alloy
|
} // namespace alloy
|
||||||
|
|
|
@ -26,6 +26,15 @@ enum DebugInfoFlags {
|
||||||
DEBUG_INFO_ALL_DISASM = 0xFFFF,
|
DEBUG_INFO_ALL_DISASM = 0xFFFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum TraceFlags {
|
||||||
|
TRACE_NONE = 0,
|
||||||
|
TRACE_EXTERN_CALLS = (1 << 0),
|
||||||
|
TRACE_USER_CALLS = (1 << 1),
|
||||||
|
TRACE_SOURCE = (1 << 2),
|
||||||
|
TRACE_SOURCE_VALUES = (1 << 3),
|
||||||
|
TRACE_FUNCTION_GENERATION = (1 << 4),
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct SourceMapEntry_s {
|
typedef struct SourceMapEntry_s {
|
||||||
uint64_t source_offset; // Original source address/offset.
|
uint64_t source_offset; // Original source address/offset.
|
||||||
uint64_t hir_offset; // Block ordinal (16b) | Instr ordinal (16b)
|
uint64_t hir_offset; // Block ordinal (16b) | Instr ordinal (16b)
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <alloy/runtime/debugger.h>
|
#include <alloy/runtime/debugger.h>
|
||||||
#include <alloy/runtime/symbol_info.h>
|
#include <alloy/runtime/symbol_info.h>
|
||||||
#include <alloy/runtime/thread_state.h>
|
#include <alloy/runtime/thread_state.h>
|
||||||
|
#include <xdb/protocol.h>
|
||||||
|
|
||||||
namespace alloy {
|
namespace alloy {
|
||||||
namespace runtime {
|
namespace runtime {
|
||||||
|
@ -73,8 +74,18 @@ int Function::Call(ThreadState* thread_state, uint64_t return_address) {
|
||||||
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
|
uint64_t trace_base = thread_state->memory()->trace_base();
|
||||||
if (symbol_info_->behavior() == FunctionInfo::BEHAVIOR_EXTERN) {
|
if (symbol_info_->behavior() == FunctionInfo::BEHAVIOR_EXTERN) {
|
||||||
auto handler = symbol_info_->extern_handler();
|
auto handler = symbol_info_->extern_handler();
|
||||||
|
|
||||||
|
if (trace_base && true) {
|
||||||
|
auto ev = xdb::protocol::KernelCallEvent::Append(trace_base);
|
||||||
|
ev->type = xdb::protocol::EventType::KERNEL_CALL;
|
||||||
|
ev->thread_id = thread_state->thread_id();
|
||||||
|
ev->module_id = 0;
|
||||||
|
ev->ordinal = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (handler) {
|
if (handler) {
|
||||||
handler(thread_state->raw_context(), symbol_info_->extern_arg0(),
|
handler(thread_state->raw_context(), symbol_info_->extern_arg0(),
|
||||||
symbol_info_->extern_arg1());
|
symbol_info_->extern_arg1());
|
||||||
|
@ -83,8 +94,28 @@ int Function::Call(ThreadState* thread_state, uint64_t return_address) {
|
||||||
symbol_info_->name().c_str());
|
symbol_info_->name().c_str());
|
||||||
result = 1;
|
result = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (trace_base && true) {
|
||||||
|
auto ev = xdb::protocol::KernelCallReturnEvent::Append(trace_base);
|
||||||
|
ev->type = xdb::protocol::EventType::KERNEL_CALL_RETURN;
|
||||||
|
ev->thread_id = thread_state->thread_id();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (trace_base && true) {
|
||||||
|
auto ev = xdb::protocol::UserCallEvent::Append(trace_base);
|
||||||
|
ev->type = xdb::protocol::EventType::USER_CALL;
|
||||||
|
ev->call_type = 0; // ?
|
||||||
|
ev->thread_id = thread_state->thread_id();
|
||||||
|
ev->address = static_cast<uint32_t>(symbol_info_->address());
|
||||||
|
}
|
||||||
|
|
||||||
CallImpl(thread_state, return_address);
|
CallImpl(thread_state, return_address);
|
||||||
|
|
||||||
|
if (trace_base && true) {
|
||||||
|
auto ev = xdb::protocol::UserCallReturnEvent::Append(trace_base);
|
||||||
|
ev->type = xdb::protocol::EventType::USER_CALL_RETURN;
|
||||||
|
ev->thread_id = thread_state->thread_id();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (original_thread_state != thread_state) {
|
if (original_thread_state != thread_state) {
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include <alloy/runtime/module.h>
|
#include <alloy/runtime/module.h>
|
||||||
#include <poly/poly.h>
|
#include <poly/poly.h>
|
||||||
|
#include <xdb/protocol.h>
|
||||||
|
|
||||||
// TODO(benvanik): based on compiler support
|
// TODO(benvanik): based on compiler support
|
||||||
#include <alloy/backend/ivm/ivm_backend.h>
|
#include <alloy/backend/ivm/ivm_backend.h>
|
||||||
|
@ -26,7 +27,11 @@ namespace runtime {
|
||||||
using alloy::backend::Backend;
|
using alloy::backend::Backend;
|
||||||
using alloy::frontend::Frontend;
|
using alloy::frontend::Frontend;
|
||||||
|
|
||||||
Runtime::Runtime(Memory* memory) : memory_(memory) {}
|
Runtime::Runtime(Memory* memory, uint32_t debug_info_flags,
|
||||||
|
uint32_t trace_flags)
|
||||||
|
: memory_(memory),
|
||||||
|
debug_info_flags_(debug_info_flags),
|
||||||
|
trace_flags_(trace_flags) {}
|
||||||
|
|
||||||
Runtime::~Runtime() {
|
Runtime::~Runtime() {
|
||||||
{
|
{
|
||||||
|
@ -224,14 +229,24 @@ int Runtime::DemandFunction(FunctionInfo* symbol_info,
|
||||||
if (symbol_status == SymbolInfo::STATUS_NEW) {
|
if (symbol_status == SymbolInfo::STATUS_NEW) {
|
||||||
// Symbol is undefined, so define now.
|
// Symbol is undefined, so define now.
|
||||||
Function* function = nullptr;
|
Function* function = nullptr;
|
||||||
int result =
|
int result = frontend_->DefineFunction(symbol_info, debug_info_flags_,
|
||||||
frontend_->DefineFunction(symbol_info, DEBUG_INFO_DEFAULT, &function);
|
trace_flags_, &function);
|
||||||
if (result) {
|
if (result) {
|
||||||
symbol_info->set_status(SymbolInfo::STATUS_FAILED);
|
symbol_info->set_status(SymbolInfo::STATUS_FAILED);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
symbol_info->set_function(function);
|
symbol_info->set_function(function);
|
||||||
|
|
||||||
|
auto trace_base = memory()->trace_base();
|
||||||
|
if (trace_base && trace_flags_ & TRACE_FUNCTION_GENERATION) {
|
||||||
|
auto ev = xdb::protocol::FunctionCompiledEvent::Append(trace_base);
|
||||||
|
ev->type = xdb::protocol::EventType::FUNCTION_COMPILED;
|
||||||
|
ev->flags = 0;
|
||||||
|
ev->address = static_cast<uint32_t>(symbol_info->address());
|
||||||
|
ev->length =
|
||||||
|
static_cast<uint32_t>(symbol_info->end_address() - ev->address);
|
||||||
|
}
|
||||||
|
|
||||||
// Before we give the symbol back to the rest, let the debugger know.
|
// Before we give the symbol back to the rest, let the debugger know.
|
||||||
debugger_->OnFunctionDefined(symbol_info, function);
|
debugger_->OnFunctionDefined(symbol_info, function);
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,8 @@ namespace runtime {
|
||||||
|
|
||||||
class Runtime {
|
class Runtime {
|
||||||
public:
|
public:
|
||||||
explicit Runtime(Memory* memory);
|
explicit Runtime(Memory* memory, uint32_t debug_info_flags = 0,
|
||||||
|
uint32_t trace_flags = 0);
|
||||||
virtual ~Runtime();
|
virtual ~Runtime();
|
||||||
|
|
||||||
Memory* memory() const { return memory_; }
|
Memory* memory() const { return memory_; }
|
||||||
|
@ -60,6 +61,9 @@ class Runtime {
|
||||||
protected:
|
protected:
|
||||||
Memory* memory_;
|
Memory* memory_;
|
||||||
|
|
||||||
|
uint32_t debug_info_flags_;
|
||||||
|
uint32_t trace_flags_;
|
||||||
|
|
||||||
std::unique_ptr<Debugger> debugger_;
|
std::unique_ptr<Debugger> debugger_;
|
||||||
|
|
||||||
std::unique_ptr<frontend::Frontend> frontend_;
|
std::unique_ptr<frontend::Frontend> frontend_;
|
||||||
|
|
|
@ -40,6 +40,11 @@ inline int64_t atomic_exchange(int64_t new_value, volatile int64_t* value) {
|
||||||
return OSAtomicCompareAndSwap64Barrier(*value, new_value, value);
|
return OSAtomicCompareAndSwap64Barrier(*value, new_value, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//inline int32_t atomic_exchange_add(int32_t amount, volatile int32_t* value) {
|
||||||
|
//}
|
||||||
|
//inline int64_t atomic_exchange_add(int64_t amount, volatile int64_t* value) {
|
||||||
|
//}
|
||||||
|
|
||||||
inline bool atomic_cas(int32_t old_value, int32_t new_value,
|
inline bool atomic_cas(int32_t old_value, int32_t new_value,
|
||||||
volatile int32_t* value) {
|
volatile int32_t* value) {
|
||||||
return OSAtomicCompareAndSwap32Barrier(
|
return OSAtomicCompareAndSwap32Barrier(
|
||||||
|
@ -69,6 +74,15 @@ inline int64_t atomic_exchange(int64_t new_value, volatile int64_t* value) {
|
||||||
new_value);
|
new_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline int32_t atomic_exchange_add(int32_t amount, volatile int32_t* value) {
|
||||||
|
return InterlockedExchangeAdd(reinterpret_cast<volatile LONG*>(value),
|
||||||
|
amount);
|
||||||
|
}
|
||||||
|
inline int64_t atomic_exchange_add(int64_t amount, volatile int64_t* value) {
|
||||||
|
return InterlockedExchangeAdd64(reinterpret_cast<volatile LONGLONG*>(value),
|
||||||
|
amount);
|
||||||
|
}
|
||||||
|
|
||||||
inline bool atomic_cas(int32_t old_value, int32_t new_value,
|
inline bool atomic_cas(int32_t old_value, int32_t new_value,
|
||||||
volatile int32_t* value) {
|
volatile int32_t* value) {
|
||||||
return InterlockedCompareExchange(reinterpret_cast<volatile LONG*>(value),
|
return InterlockedCompareExchange(reinterpret_cast<volatile LONG*>(value),
|
||||||
|
@ -96,6 +110,11 @@ inline int64_t atomic_exchange(int64_t new_value, volatile int64_t* value) {
|
||||||
return __sync_val_compare_and_swap(*value, value, new_value);
|
return __sync_val_compare_and_swap(*value, value, new_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//inline int32_t atomic_exchange_add(int32_t amount, volatile int32_t* value) {
|
||||||
|
//}
|
||||||
|
//inline int64_t atomic_exchange_add(int64_t amount, volatile int64_t* value) {
|
||||||
|
//}
|
||||||
|
|
||||||
inline bool atomic_cas(int32_t old_value, int32_t new_value,
|
inline bool atomic_cas(int32_t old_value, int32_t new_value,
|
||||||
volatile int32_t* value) {
|
volatile int32_t* value) {
|
||||||
return __sync_bool_compare_and_swap(
|
return __sync_bool_compare_and_swap(
|
||||||
|
@ -133,6 +152,17 @@ inline uint64_t atomic_exchange(uint64_t new_value, volatile uint64_t* value) {
|
||||||
reinterpret_cast<volatile int64_t*>(value)));
|
reinterpret_cast<volatile int64_t*>(value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline uint32_t atomic_exchange_add(uint32_t amount, volatile uint32_t* value) {
|
||||||
|
return static_cast<uint32_t>(
|
||||||
|
atomic_exchange_add(static_cast<int32_t>(amount),
|
||||||
|
reinterpret_cast<volatile int32_t*>(value)));
|
||||||
|
}
|
||||||
|
inline uint64_t atomic_exchange_add(uint64_t amount, volatile uint64_t* value) {
|
||||||
|
return static_cast<uint64_t>(
|
||||||
|
atomic_exchange_add(static_cast<int64_t>(amount),
|
||||||
|
reinterpret_cast<volatile int64_t*>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
inline bool atomic_cas(uint32_t old_value, uint32_t new_value,
|
inline bool atomic_cas(uint32_t old_value, uint32_t new_value,
|
||||||
volatile uint32_t* value) {
|
volatile uint32_t* value) {
|
||||||
return atomic_cas(static_cast<int32_t>(old_value),
|
return atomic_cas(static_cast<int32_t>(old_value),
|
||||||
|
|
|
@ -0,0 +1,232 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XDB_PROTOCOL_H_
|
||||||
|
#define XDB_PROTOCOL_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <alloy/vec128.h>
|
||||||
|
#include <poly/atomic.h>
|
||||||
|
|
||||||
|
namespace xdb {
|
||||||
|
namespace protocol {
|
||||||
|
|
||||||
|
using vec128_t = alloy::vec128_t;
|
||||||
|
|
||||||
|
#pragma pack(push, 4)
|
||||||
|
|
||||||
|
enum class EventType : uint8_t {
|
||||||
|
SETUP,
|
||||||
|
|
||||||
|
PROCESS_START,
|
||||||
|
PROCESS_EXIT,
|
||||||
|
MODULE_LOAD,
|
||||||
|
MODULE_UNLOAD,
|
||||||
|
THREAD_CREATE,
|
||||||
|
THREAD_INFO,
|
||||||
|
THREAD_EXIT,
|
||||||
|
|
||||||
|
FUNCTION_COMPILED,
|
||||||
|
|
||||||
|
OUTPUT_STRING,
|
||||||
|
|
||||||
|
KERNEL_CALL,
|
||||||
|
KERNEL_CALL_RETURN,
|
||||||
|
USER_CALL,
|
||||||
|
USER_CALL_RETURN,
|
||||||
|
|
||||||
|
INSTR,
|
||||||
|
INSTR_R8,
|
||||||
|
INSTR_R8_R8,
|
||||||
|
INSTR_R8_R16,
|
||||||
|
INSTR_R16,
|
||||||
|
INSTR_R16_R8,
|
||||||
|
INSTR_R16_R16,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Event {
|
||||||
|
static T* Append(uint64_t trace_base) {
|
||||||
|
if (!trace_base) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
uint64_t ptr = poly::atomic_exchange_add(
|
||||||
|
sizeof(T), reinterpret_cast<uint64_t*>(trace_base));
|
||||||
|
return reinterpret_cast<T*>(ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SetupEvent : public Event<SetupEvent> {
|
||||||
|
EventType type;
|
||||||
|
uint64_t membase;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProcessStartEvent : public Event<ProcessStartEvent> {
|
||||||
|
EventType type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProcessExitEvent : public Event<ProcessExitEvent> {
|
||||||
|
EventType type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ModuleInfo {
|
||||||
|
uint32_t module_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ModuleLoadEvent : public Event<ModuleLoadEvent> {
|
||||||
|
EventType type;
|
||||||
|
uint32_t module_id;
|
||||||
|
ModuleInfo module_info;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ModuleUnloadEvent : public Event<ModuleUnloadEvent> {
|
||||||
|
EventType type;
|
||||||
|
uint32_t module_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Registers {
|
||||||
|
uint64_t lr;
|
||||||
|
uint64_t ctr;
|
||||||
|
uint32_t xer;
|
||||||
|
uint32_t cr[8];
|
||||||
|
uint32_t fpscr;
|
||||||
|
uint32_t vscr;
|
||||||
|
uint64_t gpr[32];
|
||||||
|
double fpr[32];
|
||||||
|
alloy::vec128_t vr[128];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ThreadCreateEvent : public Event<ThreadCreateEvent> {
|
||||||
|
EventType type;
|
||||||
|
uint16_t thread_id;
|
||||||
|
Registers registers;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ThreadInfoEvent : public Event<ThreadInfoEvent> {
|
||||||
|
EventType type;
|
||||||
|
uint16_t thread_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ThreadExitEvent : public Event<ThreadExitEvent> {
|
||||||
|
EventType type;
|
||||||
|
uint16_t thread_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FunctionCompiledEvent : public Event<FunctionCompiledEvent> {
|
||||||
|
EventType type;
|
||||||
|
uint8_t _reserved;
|
||||||
|
uint16_t flags; // RECOMPILED? user/extern? etc
|
||||||
|
uint32_t address;
|
||||||
|
uint32_t length;
|
||||||
|
// timing?
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OutputStringEvent : public Event<OutputStringEvent> {
|
||||||
|
EventType type;
|
||||||
|
uint16_t thread_id;
|
||||||
|
// ?
|
||||||
|
};
|
||||||
|
|
||||||
|
struct KernelCallEvent : public Event<KernelCallEvent> {
|
||||||
|
EventType type;
|
||||||
|
uint8_t _reserved;
|
||||||
|
uint16_t thread_id;
|
||||||
|
uint16_t module_id;
|
||||||
|
uint16_t ordinal;
|
||||||
|
};
|
||||||
|
struct KernelCallReturnEvent : public Event<KernelCallReturnEvent> {
|
||||||
|
EventType type;
|
||||||
|
uint8_t _reserved;
|
||||||
|
uint16_t thread_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UserCallEvent : public Event<UserCallEvent> {
|
||||||
|
EventType type;
|
||||||
|
uint8_t call_type; // TAIL?
|
||||||
|
uint16_t thread_id;
|
||||||
|
uint32_t address;
|
||||||
|
};
|
||||||
|
struct UserCallReturnEvent : public Event<UserCallReturnEvent> {
|
||||||
|
EventType type;
|
||||||
|
uint8_t _reserved;
|
||||||
|
uint16_t thread_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InstrEvent : public Event<InstrEvent> {
|
||||||
|
EventType type;
|
||||||
|
uint8_t _reserved;
|
||||||
|
uint16_t thread_id;
|
||||||
|
uint32_t address;
|
||||||
|
};
|
||||||
|
struct InstrEventR8 : public Event<InstrEventR8> {
|
||||||
|
EventType type;
|
||||||
|
uint8_t dest_reg;
|
||||||
|
uint16_t thread_id;
|
||||||
|
uint32_t address;
|
||||||
|
uint64_t dest_value;
|
||||||
|
};
|
||||||
|
struct InstrEventR8R8 : public Event<InstrEventR8R8> {
|
||||||
|
EventType type;
|
||||||
|
uint8_t _reserved0;
|
||||||
|
uint16_t thread_id;
|
||||||
|
uint32_t address;
|
||||||
|
uint8_t dest_reg_0;
|
||||||
|
uint8_t dest_reg_1;
|
||||||
|
uint16_t _reserved1;
|
||||||
|
uint64_t dest_value_0;
|
||||||
|
uint64_t dest_value_1;
|
||||||
|
};
|
||||||
|
struct InstrEventR8R16 : public Event<InstrEventR8R16> {
|
||||||
|
EventType type;
|
||||||
|
uint8_t _reserved0;
|
||||||
|
uint16_t thread_id;
|
||||||
|
uint32_t address;
|
||||||
|
uint8_t dest_reg_0;
|
||||||
|
uint8_t dest_reg_1;
|
||||||
|
uint16_t _reserved1;
|
||||||
|
uint64_t dest_value_0;
|
||||||
|
vec128_t dest_value_1;
|
||||||
|
};
|
||||||
|
struct InstrEventR16 : public Event<InstrEventR16> {
|
||||||
|
EventType type;
|
||||||
|
uint8_t dest_reg;
|
||||||
|
uint16_t thread_id;
|
||||||
|
uint32_t address;
|
||||||
|
vec128_t dest_value;
|
||||||
|
};
|
||||||
|
struct InstrEventR16R8 : public Event<InstrEventR16R8> {
|
||||||
|
EventType type;
|
||||||
|
uint8_t _reserved0;
|
||||||
|
uint16_t thread_id;
|
||||||
|
uint32_t address;
|
||||||
|
uint8_t dest_reg_0;
|
||||||
|
uint8_t dest_reg_1;
|
||||||
|
uint16_t _reserved1;
|
||||||
|
vec128_t dest_value_0;
|
||||||
|
uint64_t dest_value_1;
|
||||||
|
};
|
||||||
|
struct InstrEventR16R16 : public Event<InstrEventR16R16> {
|
||||||
|
EventType type;
|
||||||
|
uint8_t _reserved0;
|
||||||
|
uint16_t thread_id;
|
||||||
|
uint32_t address;
|
||||||
|
uint8_t dest_reg_0;
|
||||||
|
uint8_t dest_reg_1;
|
||||||
|
uint16_t _reserved1;
|
||||||
|
vec128_t dest_value_0;
|
||||||
|
vec128_t dest_value_1;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
} // namespace protocol
|
||||||
|
} // namespace xdb
|
||||||
|
|
||||||
|
#endif // XDB_PROTOCOL_H_
|
|
@ -1,6 +1,7 @@
|
||||||
# Copyright 2014 Ben Vanik. All Rights Reserved.
|
# Copyright 2014 Ben Vanik. All Rights Reserved.
|
||||||
{
|
{
|
||||||
'sources': [
|
'sources': [
|
||||||
|
'protocol.h',
|
||||||
'xdb.cc',
|
'xdb.cc',
|
||||||
'xdb.h',
|
'xdb.h',
|
||||||
],
|
],
|
||||||
|
|
|
@ -9,5 +9,8 @@
|
||||||
|
|
||||||
#include <xdb/xdb.h>
|
#include <xdb/xdb.h>
|
||||||
|
|
||||||
void test() {
|
namespace xdb {
|
||||||
}
|
|
||||||
|
void test() {}
|
||||||
|
|
||||||
|
} // namespace xdb
|
||||||
|
|
|
@ -12,18 +12,14 @@
|
||||||
|
|
||||||
#include <gflags/gflags.h>
|
#include <gflags/gflags.h>
|
||||||
|
|
||||||
|
DECLARE_bool(trace_function_generation);
|
||||||
|
DECLARE_bool(trace_kernel_calls);
|
||||||
|
DECLARE_bool(trace_user_calls);
|
||||||
DECLARE_bool(trace_instructions);
|
DECLARE_bool(trace_instructions);
|
||||||
DECLARE_bool(trace_registers);
|
DECLARE_bool(trace_registers);
|
||||||
DECLARE_bool(trace_branches);
|
|
||||||
DECLARE_bool(trace_user_calls);
|
|
||||||
DECLARE_bool(trace_kernel_calls);
|
|
||||||
DECLARE_uint64(trace_thread_mask);
|
|
||||||
|
|
||||||
DECLARE_string(load_module_map);
|
DECLARE_string(load_module_map);
|
||||||
|
|
||||||
DECLARE_string(dump_path);
|
DECLARE_string(dump_path);
|
||||||
DECLARE_bool(dump_module_map);
|
DECLARE_bool(dump_module_map);
|
||||||
|
|
||||||
|
|
||||||
#endif // XENIA_CPU_PRIVATE_H_
|
#endif // XENIA_CPU_PRIVATE_H_
|
||||||
|
|
|
@ -9,32 +9,22 @@
|
||||||
|
|
||||||
#include <xenia/cpu/cpu-private.h>
|
#include <xenia/cpu/cpu-private.h>
|
||||||
|
|
||||||
|
|
||||||
// Tracing:
|
|
||||||
DEFINE_bool(trace_instructions, false,
|
|
||||||
"Trace all instructions.");
|
|
||||||
DEFINE_bool(trace_registers, false,
|
|
||||||
"Trace register values when tracing instructions.");
|
|
||||||
DEFINE_bool(trace_branches, false,
|
|
||||||
"Trace all branches.");
|
|
||||||
DEFINE_bool(trace_user_calls, false,
|
|
||||||
"Trace all user function calls.");
|
|
||||||
DEFINE_bool(trace_kernel_calls, false,
|
|
||||||
"Trace all kernel function calls.");
|
|
||||||
DEFINE_uint64(trace_thread_mask, -1,
|
|
||||||
"Trace threads with IDs in the mask, or -1 for all.");
|
|
||||||
|
|
||||||
|
|
||||||
// Debugging:
|
// Debugging:
|
||||||
DEFINE_string(load_module_map, "",
|
DEFINE_bool(trace_function_generation, false,
|
||||||
|
"Trace function generation/JITing.");
|
||||||
|
DEFINE_bool(trace_kernel_calls, false, "Trace all kernel function calls.");
|
||||||
|
DEFINE_bool(trace_user_calls, false, "Trace all user function calls.");
|
||||||
|
DEFINE_bool(trace_instructions, false, "Trace all instructions.");
|
||||||
|
DEFINE_bool(trace_registers, false,
|
||||||
|
"Trace register values when tracing instructions.");
|
||||||
|
DEFINE_string(
|
||||||
|
load_module_map, "",
|
||||||
"Loads a .map for symbol names and to diff with the generated symbol "
|
"Loads a .map for symbol names and to diff with the generated symbol "
|
||||||
"database.");
|
"database.");
|
||||||
|
|
||||||
|
|
||||||
// Dumping:
|
// Dumping:
|
||||||
DEFINE_string(dump_path, "build/",
|
DEFINE_string(dump_path, "build/",
|
||||||
"Directory that dump files are placed into.");
|
"Directory that dump files are placed into.");
|
||||||
DEFINE_bool(dump_module_bitcode, true,
|
DEFINE_bool(dump_module_bitcode, true,
|
||||||
"Writes the module bitcode both before and after optimizations.");
|
"Writes the module bitcode both before and after optimizations.");
|
||||||
DEFINE_bool(dump_module_map, true,
|
DEFINE_bool(dump_module_map, true, "Dumps the module symbol database.");
|
||||||
"Dumps the module symbol database.");
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <xenia/emulator.h>
|
#include <xenia/emulator.h>
|
||||||
#include <xenia/export_resolver.h>
|
#include <xenia/export_resolver.h>
|
||||||
|
#include <xenia/cpu/cpu-private.h>
|
||||||
#include <xenia/cpu/xenon_memory.h>
|
#include <xenia/cpu/xenon_memory.h>
|
||||||
#include <xenia/cpu/xenon_runtime.h>
|
#include <xenia/cpu/xenon_runtime.h>
|
||||||
#include <xenia/cpu/xex_module.h>
|
#include <xenia/cpu/xex_module.h>
|
||||||
|
@ -70,7 +71,26 @@ Processor::~Processor() {
|
||||||
int Processor::Setup() {
|
int Processor::Setup() {
|
||||||
assert_null(runtime_);
|
assert_null(runtime_);
|
||||||
|
|
||||||
runtime_ = new XenonRuntime(memory_, export_resolver_);
|
uint32_t debug_info_flags = DEBUG_INFO_DEFAULT;
|
||||||
|
uint32_t trace_flags = 0;
|
||||||
|
if (FLAGS_trace_function_generation) {
|
||||||
|
trace_flags |= TRACE_FUNCTION_GENERATION;
|
||||||
|
}
|
||||||
|
if (FLAGS_trace_kernel_calls) {
|
||||||
|
trace_flags |= TRACE_EXTERN_CALLS;
|
||||||
|
}
|
||||||
|
if (FLAGS_trace_user_calls) {
|
||||||
|
trace_flags |= TRACE_USER_CALLS;
|
||||||
|
}
|
||||||
|
if (FLAGS_trace_instructions) {
|
||||||
|
trace_flags |= TRACE_SOURCE;
|
||||||
|
}
|
||||||
|
if (FLAGS_trace_registers) {
|
||||||
|
trace_flags |= TRACE_SOURCE_VALUES;
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime_ = new XenonRuntime(memory_, export_resolver_, debug_info_flags,
|
||||||
|
trace_flags);
|
||||||
if (!runtime_) {
|
if (!runtime_) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,12 +19,11 @@ using namespace alloy::runtime;
|
||||||
using namespace xe;
|
using namespace xe;
|
||||||
using namespace xe::cpu;
|
using namespace xe::cpu;
|
||||||
|
|
||||||
|
XenonRuntime::XenonRuntime(alloy::Memory* memory,
|
||||||
XenonRuntime::XenonRuntime(
|
ExportResolver* export_resolver,
|
||||||
alloy::Memory* memory, ExportResolver* export_resolver) :
|
uint32_t debug_info_flags, uint32_t trace_flags)
|
||||||
Runtime(memory),
|
: Runtime(memory, debug_info_flags, trace_flags),
|
||||||
export_resolver_(export_resolver) {
|
export_resolver_(export_resolver) {}
|
||||||
}
|
|
||||||
|
|
||||||
XenonRuntime::~XenonRuntime() = default;
|
XenonRuntime::~XenonRuntime() = default;
|
||||||
|
|
||||||
|
|
|
@ -17,29 +17,26 @@
|
||||||
|
|
||||||
XEDECLARECLASS1(xe, ExportResolver);
|
XEDECLARECLASS1(xe, ExportResolver);
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
|
||||||
class XenonThreadState;
|
class XenonThreadState;
|
||||||
|
|
||||||
|
|
||||||
class XenonRuntime : public alloy::runtime::Runtime {
|
class XenonRuntime : public alloy::runtime::Runtime {
|
||||||
public:
|
public:
|
||||||
XenonRuntime(Memory* memory, ExportResolver* export_resolver);
|
XenonRuntime(Memory* memory, ExportResolver* export_resolver,
|
||||||
|
uint32_t debug_info_flags, uint32_t trace_flags);
|
||||||
virtual ~XenonRuntime();
|
virtual ~XenonRuntime();
|
||||||
|
|
||||||
ExportResolver* export_resolver() const { return export_resolver_; }
|
ExportResolver* export_resolver() const { return export_resolver_; }
|
||||||
|
|
||||||
virtual int Initialize(std::unique_ptr<alloy::backend::Backend> backend = 0);
|
virtual int Initialize(std::unique_ptr<alloy::backend::Backend> backend = 0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ExportResolver* export_resolver_;
|
ExportResolver* export_resolver_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace cpu
|
} // namespace cpu
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
|
|
||||||
#endif // XENIA_CPU_XENON_RUNTIME_H_
|
#endif // XENIA_CPU_XENON_RUNTIME_H_
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include <xenia/cpu/xenon_thread_state.h>
|
#include <xenia/cpu/xenon_thread_state.h>
|
||||||
|
|
||||||
|
#include <xdb/protocol.h>
|
||||||
#include <xenia/cpu/xenon_runtime.h>
|
#include <xenia/cpu/xenon_runtime.h>
|
||||||
|
|
||||||
using namespace alloy;
|
using namespace alloy;
|
||||||
|
@ -36,6 +37,7 @@ XenonThreadState::XenonThreadState(XenonRuntime* runtime, uint32_t thread_id,
|
||||||
context_->membase = memory_->membase();
|
context_->membase = memory_->membase();
|
||||||
context_->runtime = runtime;
|
context_->runtime = runtime;
|
||||||
context_->thread_state = this;
|
context_->thread_state = this;
|
||||||
|
context_->thread_id = thread_id_;
|
||||||
|
|
||||||
// Set initial registers.
|
// Set initial registers.
|
||||||
context_->r[1] = stack_address_ + stack_size;
|
context_->r[1] = stack_address_ + stack_size;
|
||||||
|
@ -56,3 +58,28 @@ XenonThreadState::~XenonThreadState() {
|
||||||
xe_free_aligned(context_);
|
xe_free_aligned(context_);
|
||||||
memory_->HeapFree(stack_address_, stack_size_);
|
memory_->HeapFree(stack_address_, stack_size_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void XenonThreadState::WriteRegisters(xdb::protocol::Registers* registers) {
|
||||||
|
registers->lr = context_->lr;
|
||||||
|
registers->ctr = context_->ctr;
|
||||||
|
registers->xer = 0xFEFEFEFE;
|
||||||
|
registers->cr[0] = context_->cr0.value;
|
||||||
|
registers->cr[1] = context_->cr1.value;
|
||||||
|
registers->cr[2] = context_->cr2.value;
|
||||||
|
registers->cr[3] = context_->cr3.value;
|
||||||
|
registers->cr[4] = context_->cr4.value;
|
||||||
|
registers->cr[5] = context_->cr5.value;
|
||||||
|
registers->cr[6] = context_->cr6.value;
|
||||||
|
registers->cr[7] = context_->cr7.value;
|
||||||
|
registers->fpscr = context_->fpscr.value;
|
||||||
|
registers->vscr = context_->vscr_sat;
|
||||||
|
static_assert(sizeof(registers->gpr) == sizeof(context_->r),
|
||||||
|
"structs must match");
|
||||||
|
static_assert(sizeof(registers->fpr) == sizeof(context_->f),
|
||||||
|
"structs must match");
|
||||||
|
static_assert(sizeof(registers->vr) == sizeof(context_->v),
|
||||||
|
"structs must match");
|
||||||
|
memcpy(registers->gpr, context_->r, sizeof(context_->r));
|
||||||
|
memcpy(registers->fpr, context_->f, sizeof(context_->f));
|
||||||
|
memcpy(registers->vr, context_->v, sizeof(context_->v));
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,11 @@
|
||||||
#include <alloy/runtime/thread_state.h>
|
#include <alloy/runtime/thread_state.h>
|
||||||
#include <xenia/core.h>
|
#include <xenia/core.h>
|
||||||
|
|
||||||
|
namespace xdb {
|
||||||
|
namespace protocol {
|
||||||
|
struct Registers;
|
||||||
|
} // namespace protocol
|
||||||
|
} // namespace xdb
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
@ -34,6 +39,8 @@ public:
|
||||||
uint64_t thread_state_address() const { return thread_state_address_; }
|
uint64_t thread_state_address() const { return thread_state_address_; }
|
||||||
PPCContext* context() const { return context_; }
|
PPCContext* context() const { return context_; }
|
||||||
|
|
||||||
|
void WriteRegisters(xdb::protocol::Registers* registers);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t stack_address_;
|
uint64_t stack_address_;
|
||||||
size_t stack_size_;
|
size_t stack_size_;
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <xenia/debug_agent.h>
|
||||||
|
|
||||||
|
#include <codecvt>
|
||||||
|
|
||||||
|
#include <gflags/gflags.h>
|
||||||
|
|
||||||
|
DEFINE_string(trace_file, "", "Trace to the given file.");
|
||||||
|
DEFINE_uint64(trace_capacity, 0x40000000, "Trace file capacity to allocate.");
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
|
||||||
|
DebugAgent::DebugAgent(Emulator* emulator)
|
||||||
|
: emulator_(emulator),
|
||||||
|
file_(nullptr),
|
||||||
|
file_mapping_(nullptr),
|
||||||
|
trace_base_(0) {}
|
||||||
|
|
||||||
|
DebugAgent::~DebugAgent() {
|
||||||
|
if (trace_base_) {
|
||||||
|
UnmapViewOfFile(trace_base_);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(file_mapping_);
|
||||||
|
CloseHandle(file_);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DebugAgent::Initialize() {
|
||||||
|
if (!FLAGS_trace_file.empty()) {
|
||||||
|
if (SetupTracing(FLAGS_trace_file, FLAGS_trace_capacity)) {
|
||||||
|
XELOGE("Failed to setup tracing - out of disk space?");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DebugAgent::SetupTracing(const std::string& trace_file, uint64_t capacity) {
|
||||||
|
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> cvt;
|
||||||
|
auto file_path = cvt.from_bytes(trace_file);
|
||||||
|
file_ = CreateFile(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS,
|
||||||
|
FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY,
|
||||||
|
nullptr);
|
||||||
|
if (!file_) {
|
||||||
|
XELOGE("Could not open trace file for writing");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_mapping_ = CreateFileMapping(
|
||||||
|
file_, nullptr, PAGE_READWRITE, static_cast<uint32_t>(capacity >> 32),
|
||||||
|
static_cast<uint32_t>(capacity), L"Local\\xenia_xdb_trace");
|
||||||
|
if (!file_mapping_) {
|
||||||
|
XELOGE("Could not create trace file mapping - out of disk space?");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_base_ = MapViewOfFile(file_mapping_, FILE_MAP_WRITE, 0, 0, 0);
|
||||||
|
if (!trace_base_) {
|
||||||
|
XELOGE("Could not map view of trace file - out of disk space?");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store initial trace base.
|
||||||
|
poly::store<uint64_t>(trace_base_, trace_base() + 8);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xe
|
|
@ -0,0 +1,44 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_DEBUG_AGENT_H_
|
||||||
|
#define XENIA_DEBUG_AGENT_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <xenia/core.h>
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
|
||||||
|
class Emulator;
|
||||||
|
|
||||||
|
class DebugAgent {
|
||||||
|
public:
|
||||||
|
DebugAgent(Emulator* emulator);
|
||||||
|
~DebugAgent();
|
||||||
|
|
||||||
|
uint64_t trace_base() const {
|
||||||
|
return reinterpret_cast<uint64_t>(trace_base_);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Initialize();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int SetupTracing(const std::string& trace_file, uint64_t capacity);
|
||||||
|
|
||||||
|
Emulator* emulator_;
|
||||||
|
|
||||||
|
HANDLE file_;
|
||||||
|
HANDLE file_mapping_;
|
||||||
|
void* trace_base_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
#endif // XENIA_DEBUG_AGENT_H_
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include <xenia/emulator.h>
|
#include <xenia/emulator.h>
|
||||||
|
|
||||||
|
#include <xdb/protocol.h>
|
||||||
#include <xenia/apu/apu.h>
|
#include <xenia/apu/apu.h>
|
||||||
#include <xenia/cpu/cpu.h>
|
#include <xenia/cpu/cpu.h>
|
||||||
#include <xenia/cpu/xenon_memory.h>
|
#include <xenia/cpu/xenon_memory.h>
|
||||||
|
@ -44,10 +45,17 @@ Emulator::Emulator(const xechar_t* command_line) :
|
||||||
Emulator::~Emulator() {
|
Emulator::~Emulator() {
|
||||||
// Note that we delete things in the reverse order they were initialized.
|
// Note that we delete things in the reverse order they were initialized.
|
||||||
|
|
||||||
|
auto ev = xdb::protocol::ProcessExitEvent::Append(memory()->trace_base());
|
||||||
|
if (ev) {
|
||||||
|
ev->type = xdb::protocol::EventType::PROCESS_EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
if (main_window_) {
|
if (main_window_) {
|
||||||
main_window_->Close();
|
main_window_->Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug_agent_.reset();
|
||||||
|
|
||||||
delete xam_;
|
delete xam_;
|
||||||
delete xboxkrnl_;
|
delete xboxkrnl_;
|
||||||
delete kernel_state_;
|
delete kernel_state_;
|
||||||
|
@ -70,11 +78,16 @@ Emulator::~Emulator() {
|
||||||
X_STATUS Emulator::Setup() {
|
X_STATUS Emulator::Setup() {
|
||||||
X_STATUS result = X_STATUS_UNSUCCESSFUL;
|
X_STATUS result = X_STATUS_UNSUCCESSFUL;
|
||||||
|
|
||||||
|
debug_agent_.reset(new DebugAgent(this));
|
||||||
|
result = debug_agent_->Initialize();
|
||||||
|
XEEXPECTZERO(result);
|
||||||
|
|
||||||
// Create memory system first, as it is required for other systems.
|
// Create memory system first, as it is required for other systems.
|
||||||
memory_ = new XenonMemory();
|
memory_ = new XenonMemory();
|
||||||
XEEXPECTNOTNULL(memory_);
|
XEEXPECTNOTNULL(memory_);
|
||||||
result = memory_->Initialize();
|
result = memory_->Initialize();
|
||||||
XEEXPECTZERO(result);
|
XEEXPECTZERO(result);
|
||||||
|
memory_->set_trace_base(debug_agent_->trace_base());
|
||||||
|
|
||||||
// Shared export resolver used to attach and query for HLE exports.
|
// Shared export resolver used to attach and query for HLE exports.
|
||||||
export_resolver_ = new ExportResolver();
|
export_resolver_ = new ExportResolver();
|
||||||
|
@ -144,6 +157,11 @@ X_STATUS Emulator::LaunchXexFile(const xechar_t* path) {
|
||||||
// and then get that symlinked to game:\, so
|
// and then get that symlinked to game:\, so
|
||||||
// -> game:\foo.xex
|
// -> game:\foo.xex
|
||||||
|
|
||||||
|
auto ev = xdb::protocol::ProcessStartEvent::Append(memory()->trace_base());
|
||||||
|
if (ev) {
|
||||||
|
ev->type = xdb::protocol::EventType::PROCESS_START;
|
||||||
|
}
|
||||||
|
|
||||||
int result_code = 0;
|
int result_code = 0;
|
||||||
|
|
||||||
// Get just the filename (foo.xex).
|
// Get just the filename (foo.xex).
|
||||||
|
@ -193,6 +211,11 @@ X_STATUS Emulator::LaunchXexFile(const xechar_t* path) {
|
||||||
X_STATUS Emulator::LaunchDiscImage(const xechar_t* path) {
|
X_STATUS Emulator::LaunchDiscImage(const xechar_t* path) {
|
||||||
int result_code = 0;
|
int result_code = 0;
|
||||||
|
|
||||||
|
auto ev = xdb::protocol::ProcessStartEvent::Append(memory()->trace_base());
|
||||||
|
if (ev) {
|
||||||
|
ev->type = xdb::protocol::EventType::PROCESS_START;
|
||||||
|
}
|
||||||
|
|
||||||
// Register the disc image in the virtual filesystem.
|
// Register the disc image in the virtual filesystem.
|
||||||
result_code = file_system_->RegisterDiscImageDevice(
|
result_code = file_system_->RegisterDiscImageDevice(
|
||||||
"\\Device\\Cdrom0", path);
|
"\\Device\\Cdrom0", path);
|
||||||
|
@ -216,6 +239,11 @@ X_STATUS Emulator::LaunchDiscImage(const xechar_t* path) {
|
||||||
X_STATUS Emulator::LaunchSTFSTitle(const xechar_t* path) {
|
X_STATUS Emulator::LaunchSTFSTitle(const xechar_t* path) {
|
||||||
int result_code = 0;
|
int result_code = 0;
|
||||||
|
|
||||||
|
auto ev = xdb::protocol::ProcessStartEvent::Append(memory()->trace_base());
|
||||||
|
if (ev) {
|
||||||
|
ev->type = xdb::protocol::EventType::PROCESS_START;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(benvanik): figure out paths.
|
// TODO(benvanik): figure out paths.
|
||||||
|
|
||||||
// Register the disc image in the virtual filesystem.
|
// Register the disc image in the virtual filesystem.
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <xenia/common.h>
|
#include <xenia/common.h>
|
||||||
#include <xenia/core.h>
|
#include <xenia/core.h>
|
||||||
|
#include <xenia/debug_agent.h>
|
||||||
#include <xenia/xbox.h>
|
#include <xenia/xbox.h>
|
||||||
#include <xenia/cpu/xenon_memory.h>
|
#include <xenia/cpu/xenon_memory.h>
|
||||||
|
|
||||||
|
@ -19,7 +20,6 @@
|
||||||
XEDECLARECLASS1(xe, ExportResolver);
|
XEDECLARECLASS1(xe, ExportResolver);
|
||||||
XEDECLARECLASS2(xe, apu, AudioSystem);
|
XEDECLARECLASS2(xe, apu, AudioSystem);
|
||||||
XEDECLARECLASS2(xe, cpu, Processor);
|
XEDECLARECLASS2(xe, cpu, Processor);
|
||||||
XEDECLARECLASS2(xe, debug, DebugServer);
|
|
||||||
XEDECLARECLASS2(xe, gpu, GraphicsSystem);
|
XEDECLARECLASS2(xe, gpu, GraphicsSystem);
|
||||||
XEDECLARECLASS2(xe, hid, InputSystem);
|
XEDECLARECLASS2(xe, hid, InputSystem);
|
||||||
XEDECLARECLASS2(xe, kernel, KernelState);
|
XEDECLARECLASS2(xe, kernel, KernelState);
|
||||||
|
@ -31,7 +31,6 @@ XEDECLARECLASS2(xe, ui, Window);
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
|
||||||
|
|
||||||
class Emulator {
|
class Emulator {
|
||||||
public:
|
public:
|
||||||
Emulator(const xechar_t* command_line);
|
Emulator(const xechar_t* command_line);
|
||||||
|
@ -44,7 +43,7 @@ public:
|
||||||
|
|
||||||
cpu::XenonMemory* memory() const { return memory_; }
|
cpu::XenonMemory* memory() const { return memory_; }
|
||||||
|
|
||||||
debug::DebugServer* debug_server() const { return debug_server_; }
|
DebugAgent* debug_agent() const { return debug_agent_.get(); }
|
||||||
|
|
||||||
cpu::Processor* processor() const { return processor_; }
|
cpu::Processor* processor() const { return processor_; }
|
||||||
apu::AudioSystem* audio_system() const { return audio_system_; }
|
apu::AudioSystem* audio_system() const { return audio_system_; }
|
||||||
|
@ -71,7 +70,7 @@ private:
|
||||||
|
|
||||||
cpu::XenonMemory* memory_;
|
cpu::XenonMemory* memory_;
|
||||||
|
|
||||||
debug::DebugServer* debug_server_;
|
std::unique_ptr<DebugAgent> debug_agent_;
|
||||||
|
|
||||||
cpu::Processor* processor_;
|
cpu::Processor* processor_;
|
||||||
apu::AudioSystem* audio_system_;
|
apu::AudioSystem* audio_system_;
|
||||||
|
|
|
@ -73,6 +73,10 @@ KernelState* KernelState::shared() {
|
||||||
return shared_kernel_state_;
|
return shared_kernel_state_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KernelState::RegisterModule(XModule* module) {}
|
||||||
|
|
||||||
|
void KernelState::UnregisterModule(XModule* module) {}
|
||||||
|
|
||||||
XModule* KernelState::GetModule(const char* name) {
|
XModule* KernelState::GetModule(const char* name) {
|
||||||
if (!name) {
|
if (!name) {
|
||||||
// NULL name = self.
|
// NULL name = self.
|
||||||
|
|
|
@ -56,6 +56,8 @@ public:
|
||||||
|
|
||||||
ObjectTable* object_table() const { return object_table_; }
|
ObjectTable* object_table() const { return object_table_; }
|
||||||
|
|
||||||
|
void RegisterModule(XModule* module);
|
||||||
|
void UnregisterModule(XModule* module);
|
||||||
XModule* GetModule(const char* name);
|
XModule* GetModule(const char* name);
|
||||||
XUserModule* GetExecutableModule();
|
XUserModule* GetExecutableModule();
|
||||||
void SetExecutableModule(XUserModule* module);
|
void SetExecutableModule(XUserModule* module);
|
||||||
|
|
|
@ -24,6 +24,8 @@ XKernelModule::XKernelModule(KernelState* kernel_state, const char* path) :
|
||||||
emulator_ = kernel_state->emulator();
|
emulator_ = kernel_state->emulator();
|
||||||
memory_ = emulator_->memory();
|
memory_ = emulator_->memory();
|
||||||
export_resolver_ = kernel_state->emulator()->export_resolver();
|
export_resolver_ = kernel_state->emulator()->export_resolver();
|
||||||
|
|
||||||
|
OnLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
XKernelModule::~XKernelModule() {
|
XKernelModule::~XKernelModule() {
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
#include <xenia/kernel/objects/xmodule.h>
|
#include <xenia/kernel/objects/xmodule.h>
|
||||||
|
|
||||||
|
#include <xdb/protocol.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace xe;
|
using namespace xe;
|
||||||
using namespace xe::cpu;
|
using namespace xe::cpu;
|
||||||
|
@ -32,6 +34,23 @@ XModule::XModule(KernelState* kernel_state, const char* path) :
|
||||||
}
|
}
|
||||||
|
|
||||||
XModule::~XModule() {
|
XModule::~XModule() {
|
||||||
|
auto ev = xdb::protocol::ModuleUnloadEvent::Append(memory()->trace_base());
|
||||||
|
if (ev) {
|
||||||
|
ev->type = xdb::protocol::EventType::MODULE_UNLOAD;
|
||||||
|
ev->module_id = handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel_state_->UnregisterModule(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void XModule::OnLoad() {
|
||||||
|
auto ev = xdb::protocol::ModuleLoadEvent::Append(memory()->trace_base());
|
||||||
|
if (ev) {
|
||||||
|
ev->type = xdb::protocol::EventType::MODULE_LOAD;
|
||||||
|
ev->module_id = handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel_state_->RegisterModule(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS XModule::GetSection(
|
X_STATUS XModule::GetSection(
|
||||||
|
|
|
@ -33,6 +33,8 @@ public:
|
||||||
uint32_t* out_section_data, uint32_t* out_section_size);
|
uint32_t* out_section_data, uint32_t* out_section_size);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void OnLoad();
|
||||||
|
|
||||||
char name_[256];
|
char name_[256];
|
||||||
char path_[poly::max_path];
|
char path_[poly::max_path];
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <poly/math.h>
|
#include <poly/math.h>
|
||||||
|
|
||||||
|
#include <xdb/protocol.h>
|
||||||
#include <xenia/cpu/cpu.h>
|
#include <xenia/cpu/cpu.h>
|
||||||
#include <xenia/kernel/native_list.h>
|
#include <xenia/kernel/native_list.h>
|
||||||
#include <xenia/kernel/xboxkrnl_threading.h>
|
#include <xenia/kernel/xboxkrnl_threading.h>
|
||||||
|
@ -70,6 +71,12 @@ XThread::XThread(KernelState* kernel_state,
|
||||||
}
|
}
|
||||||
|
|
||||||
XThread::~XThread() {
|
XThread::~XThread() {
|
||||||
|
auto ev = xdb::protocol::ThreadExitEvent::Append(memory()->trace_base());
|
||||||
|
if (ev) {
|
||||||
|
ev->type = xdb::protocol::EventType::THREAD_EXIT;
|
||||||
|
ev->thread_id = handle();
|
||||||
|
}
|
||||||
|
|
||||||
// Unregister first to prevent lookups while deleting.
|
// Unregister first to prevent lookups while deleting.
|
||||||
kernel_state_->UnregisterThread(this);
|
kernel_state_->UnregisterThread(this);
|
||||||
|
|
||||||
|
@ -254,6 +261,13 @@ X_STATUS XThread::Create() {
|
||||||
SetAffinity(proc_mask);
|
SetAffinity(proc_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto ev = xdb::protocol::ThreadCreateEvent::Append(memory()->trace_base());
|
||||||
|
if (ev) {
|
||||||
|
ev->type = xdb::protocol::EventType::THREAD_CREATE;
|
||||||
|
ev->thread_id = handle();
|
||||||
|
thread_state_->WriteRegisters(&ev->registers);
|
||||||
|
}
|
||||||
|
|
||||||
module->Release();
|
module->Release();
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,6 +122,8 @@ X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) {
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OnLoad();
|
||||||
|
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
'common.h',
|
'common.h',
|
||||||
'config.h',
|
'config.h',
|
||||||
'core.h',
|
'core.h',
|
||||||
|
'debug_agent.cc',
|
||||||
|
'debug_agent.h',
|
||||||
'emulator.cc',
|
'emulator.cc',
|
||||||
'emulator.h',
|
'emulator.h',
|
||||||
'export_resolver.cc',
|
'export_resolver.cc',
|
||||||
|
|
Loading…
Reference in New Issue