diff --git a/.gitignore b/.gitignore index e41172231..57cc0de77 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ tmtags npm-debug.log private/ +*.trace # ============================================================================== # Build system output @@ -70,3 +71,8 @@ build-out/ build-gen/ build-bin/ build-test/ + +# ============================================================================== +# Local-only paths +# ============================================================================== +attic/ diff --git a/src/alloy/backend/assembler.h b/src/alloy/backend/assembler.h index 161142389..5e721dbd9 100644 --- a/src/alloy/backend/assembler.h +++ b/src/alloy/backend/assembler.h @@ -43,6 +43,7 @@ class Assembler { virtual int Assemble(runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder, uint32_t debug_info_flags, std::unique_ptr debug_info, + uint32_t trace_flags, runtime::Function** out_function) = 0; protected: diff --git a/src/alloy/backend/ivm/ivm_assembler.cc b/src/alloy/backend/ivm/ivm_assembler.cc index 53d78e336..7585b7457 100644 --- a/src/alloy/backend/ivm/ivm_assembler.cc +++ b/src/alloy/backend/ivm/ivm_assembler.cc @@ -50,7 +50,7 @@ void IVMAssembler::Reset() { int IVMAssembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder, uint32_t debug_info_flags, std::unique_ptr debug_info, - Function** out_function) { + uint32_t trace_flags, Function** out_function) { SCOPE_profile_cpu_f("alloy"); // Reset when we leave. diff --git a/src/alloy/backend/ivm/ivm_assembler.h b/src/alloy/backend/ivm/ivm_assembler.h index eda2e864c..4fe079b37 100644 --- a/src/alloy/backend/ivm/ivm_assembler.h +++ b/src/alloy/backend/ivm/ivm_assembler.h @@ -31,7 +31,7 @@ class IVMAssembler : public Assembler { int Assemble(runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder, uint32_t debug_info_flags, std::unique_ptr debug_info, - runtime::Function** out_function) override; + uint32_t trace_flags, runtime::Function** out_function) override; private: Arena intcode_arena_; diff --git a/src/alloy/backend/ivm/ivm_intcode.cc b/src/alloy/backend/ivm/ivm_intcode.cc index f82ae39e7..b9122604d 100644 --- a/src/alloy/backend/ivm/ivm_intcode.cc +++ b/src/alloy/backend/ivm/ivm_intcode.cc @@ -11,11 +11,12 @@ #include -#include #include #include #include #include +#include +#include // TODO(benvanik): make a compile time flag? //#define DYNAMIC_REGISTER_ACCESS_CHECK(address) false @@ -246,6 +247,22 @@ int Translate_SOURCE_OFFSET(TranslationContext& ctx, Instr* i) { 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) { DFLUSH(); __debugbreak(); @@ -4174,33 +4191,33 @@ int Translate_ATOMIC_EXCHANGE(TranslationContext& ctx, Instr* i) { typedef int (*TranslateFn)(TranslationContext& ctx, Instr* i); static const TranslateFn dispatch_table[] = { Translate_COMMENT, Translate_NOP, - Translate_SOURCE_OFFSET, Translate_DEBUG_BREAK, - Translate_DEBUG_BREAK_TRUE, Translate_TRAP, - Translate_TRAP_TRUE, Translate_CALL, - Translate_CALL_TRUE, Translate_CALL_INDIRECT, - Translate_CALL_INDIRECT_TRUE, Translate_CALL_EXTERN, - Translate_RETURN, Translate_RETURN_TRUE, - Translate_SET_RETURN_ADDRESS, Translate_BRANCH, - Translate_BRANCH_TRUE, Translate_BRANCH_FALSE, - Translate_ASSIGN, Translate_CAST, - Translate_ZERO_EXTEND, Translate_SIGN_EXTEND, - Translate_TRUNCATE, Translate_CONVERT, - Translate_ROUND, Translate_VECTOR_CONVERT_I2F, - Translate_VECTOR_CONVERT_F2I, Translate_LOAD_VECTOR_SHL, - Translate_LOAD_VECTOR_SHR, Translate_LOAD_CLOCK, - Translate_LOAD_LOCAL, Translate_STORE_LOCAL, - Translate_LOAD_CONTEXT, Translate_STORE_CONTEXT, - Translate_LOAD, Translate_STORE, - Translate_PREFETCH, Translate_MAX, - Translate_VECTOR_MAX, Translate_MIN, - Translate_VECTOR_MIN, Translate_SELECT, - Translate_IS_TRUE, Translate_IS_FALSE, - Translate_COMPARE_EQ, Translate_COMPARE_NE, - Translate_COMPARE_SLT, Translate_COMPARE_SLE, - Translate_COMPARE_SGT, Translate_COMPARE_SGE, - Translate_COMPARE_ULT, Translate_COMPARE_ULE, - Translate_COMPARE_UGT, Translate_COMPARE_UGE, - Translate_DID_CARRY, + Translate_SOURCE_OFFSET, Translate_TRACE_SOURCE, + Translate_DEBUG_BREAK, Translate_DEBUG_BREAK_TRUE, + Translate_TRAP, Translate_TRAP_TRUE, + Translate_CALL, Translate_CALL_TRUE, + Translate_CALL_INDIRECT, Translate_CALL_INDIRECT_TRUE, + Translate_CALL_EXTERN, Translate_RETURN, + Translate_RETURN_TRUE, Translate_SET_RETURN_ADDRESS, + Translate_BRANCH, Translate_BRANCH_TRUE, + Translate_BRANCH_FALSE, Translate_ASSIGN, + Translate_CAST, Translate_ZERO_EXTEND, + Translate_SIGN_EXTEND, Translate_TRUNCATE, + Translate_CONVERT, Translate_ROUND, + Translate_VECTOR_CONVERT_I2F, Translate_VECTOR_CONVERT_F2I, + Translate_LOAD_VECTOR_SHL, Translate_LOAD_VECTOR_SHR, + Translate_LOAD_CLOCK, Translate_LOAD_LOCAL, + Translate_STORE_LOCAL, Translate_LOAD_CONTEXT, + Translate_STORE_CONTEXT, Translate_LOAD, + Translate_STORE, Translate_PREFETCH, + Translate_MAX, Translate_VECTOR_MAX, + Translate_MIN, Translate_VECTOR_MIN, + Translate_SELECT, Translate_IS_TRUE, + Translate_IS_FALSE, Translate_COMPARE_EQ, + Translate_COMPARE_NE, Translate_COMPARE_SLT, + Translate_COMPARE_SLE, Translate_COMPARE_SGT, + Translate_COMPARE_SGE, Translate_COMPARE_ULT, + Translate_COMPARE_ULE, Translate_COMPARE_UGT, + Translate_COMPARE_UGE, Translate_DID_CARRY, TranslateInvalid, // Translate_DID_OVERFLOW, Translate_DID_SATURATE, Translate_VECTOR_COMPARE_EQ, Translate_VECTOR_COMPARE_SGT, Translate_VECTOR_COMPARE_SGE, diff --git a/src/alloy/backend/ivm/sources.gypi b/src/alloy/backend/ivm/sources.gypi index c13431404..20306f9fb 100644 --- a/src/alloy/backend/ivm/sources.gypi +++ b/src/alloy/backend/ivm/sources.gypi @@ -8,6 +8,7 @@ 'ivm_backend.cc', 'ivm_backend.h', 'ivm_function.cc', + 'ivm_function.h', 'ivm_stack.cc', 'ivm_stack.h', ], diff --git a/src/alloy/backend/x64/x64_assembler.cc b/src/alloy/backend/x64/x64_assembler.cc index 1fd0e2223..34b8928b2 100644 --- a/src/alloy/backend/x64/x64_assembler.cc +++ b/src/alloy/backend/x64/x64_assembler.cc @@ -58,7 +58,7 @@ void X64Assembler::Reset() { int X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder, uint32_t debug_info_flags, std::unique_ptr debug_info, - Function** out_function) { + uint32_t trace_flags, Function** out_function) { SCOPE_profile_cpu_f("alloy"); // Reset when we leave. @@ -68,7 +68,7 @@ int X64Assembler::Assemble(FunctionInfo* symbol_info, HIRBuilder* builder, void* machine_code = 0; size_t code_size = 0; int result = emitter_->Emit(builder, debug_info_flags, debug_info.get(), - machine_code, code_size); + trace_flags, machine_code, code_size); if (result) { return result; } diff --git a/src/alloy/backend/x64/x64_assembler.h b/src/alloy/backend/x64/x64_assembler.h index 9959a1135..a1b81e198 100644 --- a/src/alloy/backend/x64/x64_assembler.h +++ b/src/alloy/backend/x64/x64_assembler.h @@ -37,7 +37,7 @@ class X64Assembler : public Assembler { int Assemble(runtime::FunctionInfo* symbol_info, hir::HIRBuilder* builder, uint32_t debug_info_flags, std::unique_ptr debug_info, - runtime::Function** out_function) override; + uint32_t trace_flags, runtime::Function** out_function) override; private: void DumpMachineCode(runtime::DebugInfo* debug_info, void* machine_code, diff --git a/src/alloy/backend/x64/x64_emitter.cc b/src/alloy/backend/x64/x64_emitter.cc index 2661c54d1..bf583829d 100644 --- a/src/alloy/backend/x64/x64_emitter.cc +++ b/src/alloy/backend/x64/x64_emitter.cc @@ -19,6 +19,7 @@ #include #include #include +#include namespace alloy { namespace backend { @@ -66,8 +67,8 @@ X64Emitter::~X64Emitter() {} int X64Emitter::Initialize() { return 0; } int X64Emitter::Emit(HIRBuilder* builder, uint32_t debug_info_flags, - runtime::DebugInfo* debug_info, void*& out_code_address, - size_t& out_code_size) { + runtime::DebugInfo* debug_info, uint32_t trace_flags, + void*& out_code_address, size_t& out_code_size) { SCOPE_profile_cpu_f("alloy"); // Reset. @@ -75,6 +76,7 @@ int X64Emitter::Emit(HIRBuilder* builder, uint32_t debug_info_flags, source_map_count_ = 0; source_map_arena_.Reset(); } + trace_flags_ = trace_flags; // Fill the generator with code. 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 } + uint64_t trace_base = runtime_->memory()->trace_base(); + if (trace_base && trace_flags_ & TRACE_USER_CALLS) { + mov(rax, trace_base); + mov(r8d, static_cast(sizeof(xdb::protocol::UserCallEvent))); + lock(); + xadd(qword[rax], r8); + mov(rax, static_cast(xdb::protocol::EventType::USER_CALL) | + (static_cast(0) << 8) | (0ull << 32)); + mov(qword[r8], rax); + EmitGetCurrentThreadId(); + mov(word[r8 + 2], ax); + } + // Body. auto block = builder->first_block(); while (block) { @@ -165,6 +180,16 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) { const Instr* instr = block->instr_head; while (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)) { // No sequence found! assert_always(); @@ -179,6 +204,7 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) { // Function epilog. L("epilog"); + EmitTraceUserCallReturn(); if (emit_prolog) { mov(rcx, qword[rsp + StackLayout::GUEST_RCX_HOME]); add(rsp, (uint32_t)stack_size); @@ -204,6 +230,129 @@ void X64Emitter::MarkSourceOffset(const Instr* i) { 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(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(event_type) | + (static_cast(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(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(sizeof(xdb::protocol::UserCallReturnEvent))); + lock(); + xadd(qword[rax], r8); + mov(rax, static_cast(xdb::protocol::EventType::USER_CALL_RETURN) | + (static_cast(0) << 8) | (0ull << 32)); + mov(qword[r8], rax); + EmitGetCurrentThreadId(); + mov(word[r8 + 2], ax); + mov(rax, rdx); + ReloadEDX(); +} + void X64Emitter::DebugBreak() { // TODO(benvanik): notify debugger. db(0xCC); @@ -258,7 +407,7 @@ uint64_t ResolveFunctionSymbol(void* raw_context, uint64_t symbol_info_ptr) { #else uint64_t return_address = reinterpret_cast(__builtin_return_address(0)); -#endif // XE_WIN32_LIKE +#endif // XE_LIKE_WIN32 #pragma pack(push, 1) struct Asm { uint16_t mov_rax; @@ -301,6 +450,9 @@ void X64Emitter::Call(const hir::Instr* instr, // Actually jump/call to rax. if (instr->flags & CALL_TAIL) { + // Since we skip the prolog we need to mark the return here. + EmitTraceUserCallReturn(); + // Pass the callers return address over. mov(rdx, qword[rsp + StackLayout::GUEST_RET_ADDR]); @@ -338,7 +490,7 @@ uint64_t ResolveFunctionAddress(void* raw_context, uint64_t target_address) { #else uint64_t return_address = reinterpret_cast(__builtin_return_address(0)); -#endif // XE_WIN32_LIKE +#endif // XE_LIKE_WIN32 #pragma pack(push, 1) struct Asm { uint16_t cmp_rdx; @@ -426,6 +578,9 @@ void X64Emitter::CallIndirect(const hir::Instr* instr, const Reg64& reg) { // Actually jump/call to rax. L(skip_resolve); if (instr->flags & CALL_TAIL) { + // Since we skip the prolog we need to mark the return here. + EmitTraceUserCallReturn(); + // Pass the callers return address over. 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, const FunctionInfo* symbol_info) { 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(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(xdb::protocol::EventType::KERNEL_CALL) | + (static_cast(0) << 8) | (module_id << 16) | + (ordinal)); + mov(qword[r8], rax); + EmitGetCurrentThreadId(); + mov(word[r8 + 2], ax); + } + if (!symbol_info->extern_handler()) { CallNative(UndefinedCallExtern, reinterpret_cast(symbol_info)); } else { @@ -466,6 +639,21 @@ void X64Emitter::CallExtern(const hir::Instr* instr, ReloadEDX(); // rax = host return } + if (trace_base && trace_flags_ & TRACE_EXTERN_CALLS) { + mov(rax, trace_base); + mov(r8d, + static_cast(sizeof(xdb::protocol::KernelCallReturnEvent))); + lock(); + xadd(qword[rax], r8); + uint32_t module_id = 0; + uint32_t ordinal = 0; + mov(rax, + static_cast(xdb::protocol::EventType::KERNEL_CALL_RETURN) | + (static_cast(0) << 8) | (0)); + mov(qword[r8], rax); + EmitGetCurrentThreadId(); + mov(word[r8 + 2], ax); + } } void X64Emitter::CallNative(void* fn) { diff --git a/src/alloy/backend/x64/x64_emitter.h b/src/alloy/backend/x64/x64_emitter.h index 05131d55a..86ffba7db 100644 --- a/src/alloy/backend/x64/x64_emitter.h +++ b/src/alloy/backend/x64/x64_emitter.h @@ -84,8 +84,8 @@ class X64Emitter : public Xbyak::CodeGenerator { int Initialize(); int Emit(hir::HIRBuilder* builder, uint32_t debug_info_flags, - runtime::DebugInfo* debug_info, void*& out_code_address, - size_t& out_code_size); + runtime::DebugInfo* debug_info, uint32_t trace_flags, + void*& out_code_address, size_t& out_code_size); public: // Reserved: rsp @@ -163,6 +163,10 @@ class X64Emitter : public Xbyak::CodeGenerator { protected: void* Emplace(size_t 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: runtime::Runtime* runtime_; @@ -177,6 +181,8 @@ class X64Emitter : public Xbyak::CodeGenerator { size_t stack_size_; + uint32_t trace_flags_; + static const uint32_t gpr_reg_map_[GPR_COUNT]; static const uint32_t xmm_reg_map_[XMM_COUNT]; }; diff --git a/src/alloy/frontend/context_info.cc b/src/alloy/frontend/context_info.cc index 126b77448..f14109124 100644 --- a/src/alloy/frontend/context_info.cc +++ b/src/alloy/frontend/context_info.cc @@ -12,8 +12,11 @@ namespace alloy { namespace frontend { -ContextInfo::ContextInfo(size_t size, uintptr_t thread_state_offset) - : size_(size), thread_state_offset_(thread_state_offset) {} +ContextInfo::ContextInfo(size_t size, uintptr_t thread_state_offset, + uintptr_t thread_id_offset) + : size_(size), + thread_state_offset_(thread_state_offset), + thread_id_offset_(thread_id_offset) {} ContextInfo::~ContextInfo() {} diff --git a/src/alloy/frontend/context_info.h b/src/alloy/frontend/context_info.h index 47cc8ae06..f9bf530d4 100644 --- a/src/alloy/frontend/context_info.h +++ b/src/alloy/frontend/context_info.h @@ -17,16 +17,19 @@ namespace frontend { class ContextInfo { 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(); size_t size() const { return size_; } uintptr_t thread_state_offset() const { return thread_state_offset_; } + uintptr_t thread_id_offset() const { return thread_id_offset_; } private: size_t size_; uintptr_t thread_state_offset_; + uintptr_t thread_id_offset_; }; } // namespace frontend diff --git a/src/alloy/frontend/frontend.h b/src/alloy/frontend/frontend.h index ec43bee3d..7555324b3 100644 --- a/src/alloy/frontend/frontend.h +++ b/src/alloy/frontend/frontend.h @@ -40,7 +40,7 @@ class Frontend { virtual int DeclareFunction(runtime::FunctionInfo* symbol_info) = 0; 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; protected: diff --git a/src/alloy/frontend/ppc/ppc_context.h b/src/alloy/frontend/ppc/ppc_context.h index 6833f46cd..b9858323a 100644 --- a/src/alloy/frontend/ppc/ppc_context.h +++ b/src/alloy/frontend/ppc/ppc_context.h @@ -13,55 +13,28 @@ #include #include - namespace alloy { namespace runtime { class Runtime; class ThreadState; } } - namespace alloy { namespace frontend { namespace ppc { - using vec128_t = alloy::vec128_t; - -typedef union { - uint32_t value; - struct { - uint8_t lt :1; // Negative (LT) - result is negative - uint8_t gt :1; // Positive (GT) - result is positive (and not zero) - uint8_t eq :1; // Zero (EQ) - result is zero or a stwcx/stdcx completed successfully - uint8_t so :1; // Summary Overflow (SO) - copy of XER[SO] - } cr0; - struct { - uint8_t fx :1; // FP exception summary - copy of FPSCR[FX] - 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; - +// Map: +// 0-31: GPR +// 32-63: FPR +// 64: LR +// 65: CTR +// 66: XER +// 67: FPSCR +// 68: VSCR +// 69-76: CR0-7 +// 100: invalid +// 128-256: VR #pragma pack(push, 4) typedef struct XECACHEALIGN64 PPCContext_s { @@ -194,12 +167,15 @@ typedef struct XECACHEALIGN64 PPCContext_s { // fpscr.value = (fpscr.value & ~0x000F8000) | v; // } + // Thread ID assigned to this context. + uint32_t thread_id; + // 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 // current runtime and its data. - runtime::Runtime* runtime; + runtime::Runtime* runtime; void SetRegFromString(const char* name, const char* value); bool CompareRegWithString(const char* name, const char* value, @@ -207,10 +183,8 @@ typedef struct XECACHEALIGN64 PPCContext_s { } PPCContext; #pragma pack(pop) - } // namespace ppc } // namespace frontend } // namespace alloy - #endif // ALLOY_FRONTEND_PPC_PPC_CONTEXT_H_ diff --git a/src/alloy/frontend/ppc/ppc_emit_fpu.cc b/src/alloy/frontend/ppc/ppc_emit_fpu.cc index c651c6d6e..8a332bf68 100644 --- a/src/alloy/frontend/ppc/ppc_emit_fpu.cc +++ b/src/alloy/frontend/ppc/ppc_emit_fpu.cc @@ -297,8 +297,8 @@ XEEMITTER(fnmsubsx, 0xEC00003C, A)(PPCHIRBuilder& f, InstrData& i) { XEEMITTER(fcfidx, 0xFC00069C, X)(PPCHIRBuilder& f, InstrData& i) { // frD <- signed_int64_to_double( frB ) - Value* v = f.Convert(f.Cast(f.LoadFPR(i.A.FRB), INT64_TYPE), FLOAT64_TYPE); - f.StoreFPR(i.A.FRT, v); + Value* v = f.Convert(f.Cast(f.LoadFPR(i.X.RB), INT64_TYPE), FLOAT64_TYPE); + f.StoreFPR(i.X.RT, v); // f.UpdateFPRF(v); if (i.A.Rc) { // 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); f.StoreFPR(i.X.RT, v); // f.UpdateFPRF(v); - if (i.A.Rc) { + if (i.X.Rc) { // e.update_cr_with_cond(1, v); XEINSTRNOTIMPLEMENTED(); return 1; @@ -409,7 +409,8 @@ XEEMITTER(mffsx, 0xFC00048E, X)(PPCHIRBuilder& f, InstrData& i) { XEINSTRNOTIMPLEMENTED(); 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; } diff --git a/src/alloy/frontend/ppc/ppc_frontend.cc b/src/alloy/frontend/ppc/ppc_frontend.cc index 33451e21f..ce14ddfe3 100644 --- a/src/alloy/frontend/ppc/ppc_frontend.cc +++ b/src/alloy/frontend/ppc/ppc_frontend.cc @@ -47,7 +47,8 @@ PPCFrontend::PPCFrontend(Runtime* runtime) : Frontend(runtime) { InitializeIfNeeded(); std::unique_ptr 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. context_info_ = std::move(context_info); } @@ -77,10 +78,11 @@ int PPCFrontend::DeclareFunction(FunctionInfo* symbol_info) { int PPCFrontend::DefineFunction(FunctionInfo* symbol_info, uint32_t debug_info_flags, + uint32_t trace_flags, Function** out_function) { PPCTranslator* translator = translator_pool_.Allocate(this); 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); return result; } diff --git a/src/alloy/frontend/ppc/ppc_frontend.h b/src/alloy/frontend/ppc/ppc_frontend.h index 76e37f4a9..50608f934 100644 --- a/src/alloy/frontend/ppc/ppc_frontend.h +++ b/src/alloy/frontend/ppc/ppc_frontend.h @@ -24,14 +24,14 @@ class PPCTranslator; class PPCFrontend : public Frontend { public: PPCFrontend(runtime::Runtime* runtime); - virtual ~PPCFrontend(); + ~PPCFrontend() override; - virtual int Initialize(); + int Initialize() override; - virtual int DeclareFunction(runtime::FunctionInfo* symbol_info); - virtual int DefineFunction(runtime::FunctionInfo* symbol_info, - uint32_t debug_info_flags, - runtime::Function** out_function); + int DeclareFunction(runtime::FunctionInfo* symbol_info) override; + int DefineFunction(runtime::FunctionInfo* symbol_info, + uint32_t debug_info_flags, uint32_t trace_flags, + runtime::Function** out_function) override; private: TypePool translator_pool_; diff --git a/src/alloy/frontend/ppc/ppc_hir_builder.cc b/src/alloy/frontend/ppc/ppc_hir_builder.cc index 3d5cf5f41..660ac1107 100644 --- a/src/alloy/frontend/ppc/ppc_hir_builder.cc +++ b/src/alloy/frontend/ppc/ppc_hir_builder.cc @@ -43,7 +43,7 @@ void PPCHIRBuilder::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"); Memory* memory = frontend_->memory(); @@ -53,7 +53,7 @@ int PPCHIRBuilder::Emit(FunctionInfo* symbol_info, bool with_debug_info) { start_address_ = symbol_info->address(); 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_) { Comment("%s fn %.8X-%.8X %s", symbol_info->module()->name().c_str(), 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(p + address); // TODO(benvanik): find a way to avoid using the opcode tables. i.type = GetInstrType(i.code); + trace_info_.dest_count = 0; // 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 @@ -136,6 +137,30 @@ int PPCHIRBuilder::Emit(FunctionInfo* symbol_info, bool with_debug_info) { // DebugBreak(); // 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(); @@ -205,6 +230,10 @@ Value* PPCHIRBuilder::LoadLR() { void PPCHIRBuilder::StoreLR(Value* value) { assert_true(value->type == INT64_TYPE); 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() { @@ -214,6 +243,10 @@ Value* PPCHIRBuilder::LoadCTR() { void PPCHIRBuilder::StoreCTR(Value* value) { assert_true(value->type == INT64_TYPE); 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) { @@ -232,6 +265,8 @@ void PPCHIRBuilder::StoreCR(uint32_t n, Value* value) { void PPCHIRBuilder::StoreCRField(uint32_t n, uint32_t bit, Value* value) { StoreContext(offsetof(PPCContext, cr0) + (4 * n) + bit, value); + + // TODO(benvanik): trace CR. } 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); // StoreContext(offsetof(PPCContext, cr) + (4 * n) + 3, so); + + // TOOD(benvanik): trace CR. } void PPCHIRBuilder::UpdateCR6(Value* src_value) { @@ -265,6 +302,8 @@ void PPCHIRBuilder::UpdateCR6(Value* src_value) { StoreContext(offsetof(PPCContext, cr6.cr6_all_equal), IsFalse(Not(src_value))); StoreContext(offsetof(PPCContext, cr6.cr6_none_equal), IsFalse(src_value)); + + // TOOD(benvanik): trace CR. } Value* PPCHIRBuilder::LoadFPSCR() { @@ -274,6 +313,10 @@ Value* PPCHIRBuilder::LoadFPSCR() { void PPCHIRBuilder::StoreFPSCR(Value* value) { assert_true(value->type == INT64_TYPE); 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() { @@ -290,6 +333,10 @@ Value* PPCHIRBuilder::LoadCA() { void PPCHIRBuilder::StoreCA(Value* value) { assert_true(value->type == INT8_TYPE); 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() { @@ -299,6 +346,10 @@ Value* PPCHIRBuilder::LoadSAT() { void PPCHIRBuilder::StoreSAT(Value* value) { value = Truncate(value, INT8_TYPE); 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) { @@ -308,6 +359,10 @@ Value* PPCHIRBuilder::LoadGPR(uint32_t reg) { void PPCHIRBuilder::StoreGPR(uint32_t reg, Value* value) { assert_true(value->type == INT64_TYPE); 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) { @@ -317,6 +372,10 @@ Value* PPCHIRBuilder::LoadFPR(uint32_t reg) { void PPCHIRBuilder::StoreFPR(uint32_t reg, Value* value) { assert_true(value->type == FLOAT64_TYPE); 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) { @@ -326,6 +385,10 @@ Value* PPCHIRBuilder::LoadVR(uint32_t reg) { void PPCHIRBuilder::StoreVR(uint32_t reg, Value* value) { assert_true(value->type == VEC128_TYPE); 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, diff --git a/src/alloy/frontend/ppc/ppc_hir_builder.h b/src/alloy/frontend/ppc/ppc_hir_builder.h index 67aeb350b..afbece4e3 100644 --- a/src/alloy/frontend/ppc/ppc_hir_builder.h +++ b/src/alloy/frontend/ppc/ppc_hir_builder.h @@ -33,7 +33,15 @@ class PPCHIRBuilder : public hir::HIRBuilder { 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* LookupFunction(uint64_t address); @@ -89,6 +97,15 @@ class PPCHIRBuilder : public hir::HIRBuilder { uint64_t instr_count_; Instr** instr_offset_list_; Label** label_list_; + + // Reset each instruction. + struct { + uint32_t dest_count; + struct { + uint8_t reg; + Value* value; + } dests[4]; + } trace_info_; }; } // namespace ppc diff --git a/src/alloy/frontend/ppc/ppc_translator.cc b/src/alloy/frontend/ppc/ppc_translator.cc index e1720f523..a6059459d 100644 --- a/src/alloy/frontend/ppc/ppc_translator.cc +++ b/src/alloy/frontend/ppc/ppc_translator.cc @@ -85,7 +85,7 @@ PPCTranslator::PPCTranslator(PPCFrontend* frontend) : frontend_(frontend) { PPCTranslator::~PPCTranslator() = default; int PPCTranslator::Translate(FunctionInfo* symbol_info, - uint32_t debug_info_flags, + uint32_t debug_info_flags, uint32_t trace_flags, Function** out_function) { SCOPE_profile_cpu_f("alloy"); @@ -124,7 +124,16 @@ int PPCTranslator::Translate(FunctionInfo* symbol_info, } // 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) { return result; } @@ -150,8 +159,9 @@ int PPCTranslator::Translate(FunctionInfo* symbol_info, } // Assemble to backend machine code. - result = assembler_->Assemble(symbol_info, builder_.get(), debug_info_flags, - std::move(debug_info), out_function); + result = + assembler_->Assemble(symbol_info, builder_.get(), debug_info_flags, + std::move(debug_info), trace_flags, out_function); if (result) { return result; } diff --git a/src/alloy/frontend/ppc/ppc_translator.h b/src/alloy/frontend/ppc/ppc_translator.h index 5b359f836..4c95e94b4 100644 --- a/src/alloy/frontend/ppc/ppc_translator.h +++ b/src/alloy/frontend/ppc/ppc_translator.h @@ -32,7 +32,7 @@ class PPCTranslator { ~PPCTranslator(); int Translate(runtime::FunctionInfo* symbol_info, uint32_t debug_info_flags, - runtime::Function** out_function); + uint32_t trace_flags, runtime::Function** out_function); private: void DumpSource(runtime::FunctionInfo* symbol_info, diff --git a/src/alloy/hir/hir_builder.cc b/src/alloy/hir/hir_builder.cc index 87d9f75f9..ac6eec173 100644 --- a/src/alloy/hir/hir_builder.cc +++ b/src/alloy/hir/hir_builder.cc @@ -649,6 +649,28 @@ void HIRBuilder::SourceOffset(uint64_t offset) { 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() { Instr* i = AppendInstr(OPCODE_DEBUG_BREAK_info, 0); i->src1.value = i->src2.value = i->src3.value = NULL; @@ -1743,11 +1765,13 @@ Value* HIRBuilder::RotateLeft(Value* value1, Value* value2) { 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(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_src2(value2); i->src3.value = NULL; diff --git a/src/alloy/hir/hir_builder.h b/src/alloy/hir/hir_builder.h index a6ec2ef4d..ae0b23e55 100644 --- a/src/alloy/hir/hir_builder.h +++ b/src/alloy/hir/hir_builder.h @@ -71,6 +71,10 @@ class HIRBuilder { void Nop(); 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 void DebugBreak(); diff --git a/src/alloy/hir/opcodes.h b/src/alloy/hir/opcodes.h index ca63f69eb..cfca101a0 100644 --- a/src/alloy/hir/opcodes.h +++ b/src/alloy/hir/opcodes.h @@ -77,6 +77,7 @@ enum Opcode { OPCODE_COMMENT, OPCODE_NOP, OPCODE_SOURCE_OFFSET, + OPCODE_TRACE_SOURCE, OPCODE_DEBUG_BREAK, OPCODE_DEBUG_BREAK_TRUE, 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_V = (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_V = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_V << 3), OPCODE_SIG_X_V_L = diff --git a/src/alloy/hir/opcodes.inl b/src/alloy/hir/opcodes.inl index 77c51bb5a..3baf405c8 100644 --- a/src/alloy/hir/opcodes.inl +++ b/src/alloy/hir/opcodes.inl @@ -26,6 +26,12 @@ DEFINE_OPCODE( OPCODE_SIG_X_O, 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( OPCODE_DEBUG_BREAK, "debug_break", diff --git a/src/alloy/memory.cc b/src/alloy/memory.cc index b744a24b9..abc0a8635 100644 --- a/src/alloy/memory.cc +++ b/src/alloy/memory.cc @@ -13,7 +13,7 @@ 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(); } diff --git a/src/alloy/memory.h b/src/alloy/memory.h index 0a1f861ad..5fe4069a4 100644 --- a/src/alloy/memory.h +++ b/src/alloy/memory.h @@ -33,6 +33,9 @@ class Memory { 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(); void Zero(uint64_t address, size_t size); @@ -65,6 +68,7 @@ class Memory { size_t system_page_size_; uint8_t* membase_; uint32_t reserve_address_; + uint64_t trace_base_; }; } // namespace alloy diff --git a/src/alloy/runtime/debug_info.h b/src/alloy/runtime/debug_info.h index 8ed6b0b34..fa4ee4f3a 100644 --- a/src/alloy/runtime/debug_info.h +++ b/src/alloy/runtime/debug_info.h @@ -26,6 +26,15 @@ enum DebugInfoFlags { 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 { uint64_t source_offset; // Original source address/offset. uint64_t hir_offset; // Block ordinal (16b) | Instr ordinal (16b) diff --git a/src/alloy/runtime/function.cc b/src/alloy/runtime/function.cc index 06c0a68c2..6f7f2762a 100644 --- a/src/alloy/runtime/function.cc +++ b/src/alloy/runtime/function.cc @@ -12,6 +12,7 @@ #include #include #include +#include namespace alloy { namespace runtime { @@ -73,8 +74,18 @@ int Function::Call(ThreadState* thread_state, uint64_t return_address) { int result = 0; + uint64_t trace_base = thread_state->memory()->trace_base(); if (symbol_info_->behavior() == FunctionInfo::BEHAVIOR_EXTERN) { 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) { handler(thread_state->raw_context(), symbol_info_->extern_arg0(), symbol_info_->extern_arg1()); @@ -83,8 +94,28 @@ int Function::Call(ThreadState* thread_state, uint64_t return_address) { symbol_info_->name().c_str()); 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 { + 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(symbol_info_->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) { diff --git a/src/alloy/runtime/runtime.cc b/src/alloy/runtime/runtime.cc index 8f9ebd832..9189cc299 100644 --- a/src/alloy/runtime/runtime.cc +++ b/src/alloy/runtime/runtime.cc @@ -13,6 +13,7 @@ #include #include +#include // TODO(benvanik): based on compiler support #include @@ -26,7 +27,11 @@ namespace runtime { using alloy::backend::Backend; 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() { { @@ -224,14 +229,24 @@ int Runtime::DemandFunction(FunctionInfo* symbol_info, if (symbol_status == SymbolInfo::STATUS_NEW) { // Symbol is undefined, so define now. Function* function = nullptr; - int result = - frontend_->DefineFunction(symbol_info, DEBUG_INFO_DEFAULT, &function); + int result = frontend_->DefineFunction(symbol_info, debug_info_flags_, + trace_flags_, &function); if (result) { symbol_info->set_status(SymbolInfo::STATUS_FAILED); return result; } 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(symbol_info->address()); + ev->length = + static_cast(symbol_info->end_address() - ev->address); + } + // Before we give the symbol back to the rest, let the debugger know. debugger_->OnFunctionDefined(symbol_info, function); diff --git a/src/alloy/runtime/runtime.h b/src/alloy/runtime/runtime.h index 2fd5d1498..a9fe9c374 100644 --- a/src/alloy/runtime/runtime.h +++ b/src/alloy/runtime/runtime.h @@ -29,7 +29,8 @@ namespace runtime { class Runtime { public: - explicit Runtime(Memory* memory); + explicit Runtime(Memory* memory, uint32_t debug_info_flags = 0, + uint32_t trace_flags = 0); virtual ~Runtime(); Memory* memory() const { return memory_; } @@ -60,6 +61,9 @@ class Runtime { protected: Memory* memory_; + uint32_t debug_info_flags_; + uint32_t trace_flags_; + std::unique_ptr debugger_; std::unique_ptr frontend_; diff --git a/src/poly/atomic.h b/src/poly/atomic.h index 721db37dc..4d8473e23 100644 --- a/src/poly/atomic.h +++ b/src/poly/atomic.h @@ -40,6 +40,11 @@ inline int64_t atomic_exchange(int64_t new_value, volatile int64_t* 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, volatile int32_t* value) { return OSAtomicCompareAndSwap32Barrier( @@ -69,6 +74,15 @@ inline int64_t atomic_exchange(int64_t new_value, volatile int64_t* value) { new_value); } +inline int32_t atomic_exchange_add(int32_t amount, volatile int32_t* value) { + return InterlockedExchangeAdd(reinterpret_cast(value), + amount); +} +inline int64_t atomic_exchange_add(int64_t amount, volatile int64_t* value) { + return InterlockedExchangeAdd64(reinterpret_cast(value), + amount); +} + inline bool atomic_cas(int32_t old_value, int32_t new_value, volatile int32_t* value) { return InterlockedCompareExchange(reinterpret_cast(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); } +//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, volatile int32_t* value) { 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(value))); } +inline uint32_t atomic_exchange_add(uint32_t amount, volatile uint32_t* value) { + return static_cast( + atomic_exchange_add(static_cast(amount), + reinterpret_cast(value))); +} +inline uint64_t atomic_exchange_add(uint64_t amount, volatile uint64_t* value) { + return static_cast( + atomic_exchange_add(static_cast(amount), + reinterpret_cast(value))); +} + inline bool atomic_cas(uint32_t old_value, uint32_t new_value, volatile uint32_t* value) { return atomic_cas(static_cast(old_value), diff --git a/src/xdb/protocol.h b/src/xdb/protocol.h new file mode 100644 index 000000000..51f9628da --- /dev/null +++ b/src/xdb/protocol.h @@ -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 + +#include +#include + +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 +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(trace_base)); + return reinterpret_cast(ptr); + } +}; + +struct SetupEvent : public Event { + EventType type; + uint64_t membase; +}; + +struct ProcessStartEvent : public Event { + EventType type; +}; + +struct ProcessExitEvent : public Event { + EventType type; +}; + +struct ModuleInfo { + uint32_t module_id; +}; + +struct ModuleLoadEvent : public Event { + EventType type; + uint32_t module_id; + ModuleInfo module_info; +}; + +struct ModuleUnloadEvent : public Event { + 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 { + EventType type; + uint16_t thread_id; + Registers registers; +}; + +struct ThreadInfoEvent : public Event { + EventType type; + uint16_t thread_id; +}; + +struct ThreadExitEvent : public Event { + EventType type; + uint16_t thread_id; +}; + +struct FunctionCompiledEvent : public Event { + EventType type; + uint8_t _reserved; + uint16_t flags; // RECOMPILED? user/extern? etc + uint32_t address; + uint32_t length; + // timing? +}; + +struct OutputStringEvent : public Event { + EventType type; + uint16_t thread_id; + // ? +}; + +struct KernelCallEvent : public Event { + EventType type; + uint8_t _reserved; + uint16_t thread_id; + uint16_t module_id; + uint16_t ordinal; +}; +struct KernelCallReturnEvent : public Event { + EventType type; + uint8_t _reserved; + uint16_t thread_id; +}; + +struct UserCallEvent : public Event { + EventType type; + uint8_t call_type; // TAIL? + uint16_t thread_id; + uint32_t address; +}; +struct UserCallReturnEvent : public Event { + EventType type; + uint8_t _reserved; + uint16_t thread_id; +}; + +struct InstrEvent : public Event { + EventType type; + uint8_t _reserved; + uint16_t thread_id; + uint32_t address; +}; +struct InstrEventR8 : public Event { + EventType type; + uint8_t dest_reg; + uint16_t thread_id; + uint32_t address; + uint64_t dest_value; +}; +struct InstrEventR8R8 : public Event { + 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 { + 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 { + EventType type; + uint8_t dest_reg; + uint16_t thread_id; + uint32_t address; + vec128_t dest_value; +}; +struct InstrEventR16R8 : public Event { + 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 { + 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_ diff --git a/src/xdb/sources.gypi b/src/xdb/sources.gypi index f5ffe8297..4cf583f01 100644 --- a/src/xdb/sources.gypi +++ b/src/xdb/sources.gypi @@ -1,6 +1,7 @@ # Copyright 2014 Ben Vanik. All Rights Reserved. { 'sources': [ + 'protocol.h', 'xdb.cc', 'xdb.h', ], diff --git a/src/xdb/xdb.cc b/src/xdb/xdb.cc index f6d955728..5a89bf8c7 100644 --- a/src/xdb/xdb.cc +++ b/src/xdb/xdb.cc @@ -9,5 +9,8 @@ #include -void test() { -} +namespace xdb { + +void test() {} + +} // namespace xdb diff --git a/src/xenia/cpu/cpu-private.h b/src/xenia/cpu/cpu-private.h index 3272d2ece..0c2c33313 100644 --- a/src/xenia/cpu/cpu-private.h +++ b/src/xenia/cpu/cpu-private.h @@ -12,18 +12,14 @@ #include - +DECLARE_bool(trace_function_generation); +DECLARE_bool(trace_kernel_calls); +DECLARE_bool(trace_user_calls); DECLARE_bool(trace_instructions); 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(dump_path); DECLARE_bool(dump_module_map); - #endif // XENIA_CPU_PRIVATE_H_ diff --git a/src/xenia/cpu/cpu.cc b/src/xenia/cpu/cpu.cc index 389aeee18..61ed9ac61 100644 --- a/src/xenia/cpu/cpu.cc +++ b/src/xenia/cpu/cpu.cc @@ -9,32 +9,22 @@ #include - -// 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: -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 " "database."); - // Dumping: 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, - "Writes the module bitcode both before and after optimizations."); -DEFINE_bool(dump_module_map, true, - "Dumps the module symbol database."); + "Writes the module bitcode both before and after optimizations."); +DEFINE_bool(dump_module_map, true, "Dumps the module symbol database."); diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index 21512acbb..f94ecf588 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -70,7 +71,26 @@ Processor::~Processor() { int Processor::Setup() { 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_) { return 1; } diff --git a/src/xenia/cpu/xenon_runtime.cc b/src/xenia/cpu/xenon_runtime.cc index 46fe20d5f..1ae38b8c9 100644 --- a/src/xenia/cpu/xenon_runtime.cc +++ b/src/xenia/cpu/xenon_runtime.cc @@ -19,12 +19,11 @@ using namespace alloy::runtime; using namespace xe; using namespace xe::cpu; - -XenonRuntime::XenonRuntime( - alloy::Memory* memory, ExportResolver* export_resolver) : - Runtime(memory), - export_resolver_(export_resolver) { -} +XenonRuntime::XenonRuntime(alloy::Memory* memory, + ExportResolver* export_resolver, + uint32_t debug_info_flags, uint32_t trace_flags) + : Runtime(memory, debug_info_flags, trace_flags), + export_resolver_(export_resolver) {} XenonRuntime::~XenonRuntime() = default; diff --git a/src/xenia/cpu/xenon_runtime.h b/src/xenia/cpu/xenon_runtime.h index 3b6c219b7..de90e167d 100644 --- a/src/xenia/cpu/xenon_runtime.h +++ b/src/xenia/cpu/xenon_runtime.h @@ -17,29 +17,26 @@ XEDECLARECLASS1(xe, ExportResolver); - namespace xe { namespace cpu { class XenonThreadState; - class XenonRuntime : public alloy::runtime::Runtime { -public: - XenonRuntime(Memory* memory, ExportResolver* export_resolver); + public: + XenonRuntime(Memory* memory, ExportResolver* export_resolver, + uint32_t debug_info_flags, uint32_t trace_flags); virtual ~XenonRuntime(); ExportResolver* export_resolver() const { return export_resolver_; } virtual int Initialize(std::unique_ptr backend = 0); -private: + private: ExportResolver* export_resolver_; }; - } // namespace cpu } // namespace xe - #endif // XENIA_CPU_XENON_RUNTIME_H_ diff --git a/src/xenia/cpu/xenon_thread_state.cc b/src/xenia/cpu/xenon_thread_state.cc index a3403f0d2..0a4190573 100644 --- a/src/xenia/cpu/xenon_thread_state.cc +++ b/src/xenia/cpu/xenon_thread_state.cc @@ -9,6 +9,7 @@ #include +#include #include using namespace alloy; @@ -36,6 +37,7 @@ XenonThreadState::XenonThreadState(XenonRuntime* runtime, uint32_t thread_id, context_->membase = memory_->membase(); context_->runtime = runtime; context_->thread_state = this; + context_->thread_id = thread_id_; // Set initial registers. context_->r[1] = stack_address_ + stack_size; @@ -56,3 +58,28 @@ XenonThreadState::~XenonThreadState() { xe_free_aligned(context_); 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)); +} diff --git a/src/xenia/cpu/xenon_thread_state.h b/src/xenia/cpu/xenon_thread_state.h index 0bda965fa..409d85a97 100644 --- a/src/xenia/cpu/xenon_thread_state.h +++ b/src/xenia/cpu/xenon_thread_state.h @@ -14,6 +14,11 @@ #include #include +namespace xdb { +namespace protocol { +struct Registers; +} // namespace protocol +} // namespace xdb namespace xe { namespace cpu { @@ -34,6 +39,8 @@ public: uint64_t thread_state_address() const { return thread_state_address_; } PPCContext* context() const { return context_; } + void WriteRegisters(xdb::protocol::Registers* registers); + private: uint64_t stack_address_; size_t stack_size_; diff --git a/src/xenia/debug_agent.cc b/src/xenia/debug_agent.cc new file mode 100644 index 000000000..3b2c63c82 --- /dev/null +++ b/src/xenia/debug_agent.cc @@ -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 + +#include + +#include + +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> 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(capacity >> 32), + static_cast(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(trace_base_, trace_base() + 8); + + return 0; +} + +} // namespace xe diff --git a/src/xenia/debug_agent.h b/src/xenia/debug_agent.h new file mode 100644 index 000000000..33a970a3d --- /dev/null +++ b/src/xenia/debug_agent.h @@ -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 + +#include + +namespace xe { + +class Emulator; + +class DebugAgent { + public: + DebugAgent(Emulator* emulator); + ~DebugAgent(); + + uint64_t trace_base() const { + return reinterpret_cast(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_ diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 1b3dbbcac..5cbc64675 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -9,6 +9,7 @@ #include +#include #include #include #include @@ -44,10 +45,17 @@ Emulator::Emulator(const xechar_t* command_line) : Emulator::~Emulator() { // 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_) { main_window_->Close(); } + debug_agent_.reset(); + delete xam_; delete xboxkrnl_; delete kernel_state_; @@ -70,11 +78,16 @@ Emulator::~Emulator() { X_STATUS Emulator::Setup() { 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. memory_ = new XenonMemory(); XEEXPECTNOTNULL(memory_); result = memory_->Initialize(); XEEXPECTZERO(result); + memory_->set_trace_base(debug_agent_->trace_base()); // Shared export resolver used to attach and query for HLE exports. export_resolver_ = new ExportResolver(); @@ -144,6 +157,11 @@ X_STATUS Emulator::LaunchXexFile(const xechar_t* path) { // and then get that symlinked to game:\, so // -> 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; // 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) { 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. result_code = file_system_->RegisterDiscImageDevice( "\\Device\\Cdrom0", path); @@ -216,6 +239,11 @@ X_STATUS Emulator::LaunchDiscImage(const xechar_t* path) { X_STATUS Emulator::LaunchSTFSTitle(const xechar_t* path) { 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. // Register the disc image in the virtual filesystem. diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index 3548e5d2c..f3120c8d5 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -19,7 +20,6 @@ XEDECLARECLASS1(xe, ExportResolver); XEDECLARECLASS2(xe, apu, AudioSystem); XEDECLARECLASS2(xe, cpu, Processor); -XEDECLARECLASS2(xe, debug, DebugServer); XEDECLARECLASS2(xe, gpu, GraphicsSystem); XEDECLARECLASS2(xe, hid, InputSystem); XEDECLARECLASS2(xe, kernel, KernelState); @@ -31,7 +31,6 @@ XEDECLARECLASS2(xe, ui, Window); namespace xe { - class Emulator { public: Emulator(const xechar_t* command_line); @@ -44,7 +43,7 @@ public: 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_; } apu::AudioSystem* audio_system() const { return audio_system_; } @@ -71,7 +70,7 @@ private: cpu::XenonMemory* memory_; - debug::DebugServer* debug_server_; + std::unique_ptr debug_agent_; cpu::Processor* processor_; apu::AudioSystem* audio_system_; diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index b806253b3..f885e03fd 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -73,6 +73,10 @@ KernelState* KernelState::shared() { return shared_kernel_state_; } +void KernelState::RegisterModule(XModule* module) {} + +void KernelState::UnregisterModule(XModule* module) {} + XModule* KernelState::GetModule(const char* name) { if (!name) { // NULL name = self. diff --git a/src/xenia/kernel/kernel_state.h b/src/xenia/kernel/kernel_state.h index 77173a8f2..55033aef6 100644 --- a/src/xenia/kernel/kernel_state.h +++ b/src/xenia/kernel/kernel_state.h @@ -56,6 +56,8 @@ public: ObjectTable* object_table() const { return object_table_; } + void RegisterModule(XModule* module); + void UnregisterModule(XModule* module); XModule* GetModule(const char* name); XUserModule* GetExecutableModule(); void SetExecutableModule(XUserModule* module); diff --git a/src/xenia/kernel/objects/xkernel_module.cc b/src/xenia/kernel/objects/xkernel_module.cc index 506cc0b8e..d5419f2fc 100644 --- a/src/xenia/kernel/objects/xkernel_module.cc +++ b/src/xenia/kernel/objects/xkernel_module.cc @@ -24,6 +24,8 @@ XKernelModule::XKernelModule(KernelState* kernel_state, const char* path) : emulator_ = kernel_state->emulator(); memory_ = emulator_->memory(); export_resolver_ = kernel_state->emulator()->export_resolver(); + + OnLoad(); } XKernelModule::~XKernelModule() { diff --git a/src/xenia/kernel/objects/xmodule.cc b/src/xenia/kernel/objects/xmodule.cc index c6efc9361..462d76ac4 100644 --- a/src/xenia/kernel/objects/xmodule.cc +++ b/src/xenia/kernel/objects/xmodule.cc @@ -9,6 +9,8 @@ #include +#include + using namespace xe; using namespace xe::cpu; @@ -32,6 +34,23 @@ XModule::XModule(KernelState* kernel_state, const char* path) : } 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( diff --git a/src/xenia/kernel/objects/xmodule.h b/src/xenia/kernel/objects/xmodule.h index c19a4961f..9537adbcd 100644 --- a/src/xenia/kernel/objects/xmodule.h +++ b/src/xenia/kernel/objects/xmodule.h @@ -33,6 +33,8 @@ public: uint32_t* out_section_data, uint32_t* out_section_size); protected: + void OnLoad(); + char name_[256]; char path_[poly::max_path]; }; diff --git a/src/xenia/kernel/objects/xthread.cc b/src/xenia/kernel/objects/xthread.cc index 0b6b93d06..7c3f6b0be 100644 --- a/src/xenia/kernel/objects/xthread.cc +++ b/src/xenia/kernel/objects/xthread.cc @@ -11,6 +11,7 @@ #include +#include #include #include #include @@ -70,6 +71,12 @@ XThread::XThread(KernelState* kernel_state, } 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. kernel_state_->UnregisterThread(this); @@ -254,6 +261,13 @@ X_STATUS XThread::Create() { 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(); return X_STATUS_SUCCESS; } diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index 2008aa94a..7d566ce7c 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -122,6 +122,8 @@ X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) { return X_STATUS_UNSUCCESSFUL; } + OnLoad(); + return X_STATUS_SUCCESS; } diff --git a/src/xenia/sources.gypi b/src/xenia/sources.gypi index 568c231ec..db918c4e9 100644 --- a/src/xenia/sources.gypi +++ b/src/xenia/sources.gypi @@ -4,6 +4,8 @@ 'common.h', 'config.h', 'core.h', + 'debug_agent.cc', + 'debug_agent.h', 'emulator.cc', 'emulator.h', 'export_resolver.cc',