Capturing guest/host context and showing registers in debugger.

This commit is contained in:
Ben Vanik
2015-08-29 08:08:54 -07:00
parent ab04175aad
commit 3c50b6739a
23 changed files with 1182 additions and 85 deletions

View File

@@ -15,6 +15,8 @@
#include "xenia/cpu/processor.h"
#include "xenia/profiling.h"
DECLARE_bool(debug);
DEFINE_bool(store_all_context_values, false,
"Don't strip dead context stores to aid in debugging.");
@@ -74,7 +76,9 @@ bool ContextPromotionPass::Run(HIRBuilder* builder) {
}
// Remove all dead stores.
if (!FLAGS_store_all_context_values) {
// This will break debugging as we can't recover this information when
// trying to extract stack traces/register values, so we don't do that.
if (!FLAGS_debug && !FLAGS_store_all_context_values) {
block = builder->first_block();
while (block) {
RemoveDeadStoresBlock(block);

View File

@@ -12,82 +12,140 @@
#include <cinttypes>
#include <cstdlib>
#include "xenia/base/assert.h"
#include "xenia/base/string_util.h"
namespace xe {
namespace cpu {
namespace frontend {
uint64_t ParseInt64(const char* value) {
return std::strtoull(value, nullptr, 0);
}
double ParseFloat64(const char* value) {
if (strstr(value, "0x") == value) {
union {
uint64_t ui;
double dbl;
} v;
v.ui = ParseInt64(value);
return v.dbl;
std::string PPCContext::GetRegisterName(PPCRegister reg) {
switch (reg) {
case PPCRegister::kLR:
return "lr";
case PPCRegister::kCTR:
return "ctr";
case PPCRegister::kXER:
return "xer";
case PPCRegister::kFPSCR:
return "fpscr";
case PPCRegister::kVSCR:
return "vscr";
case PPCRegister::kCR:
return "cr";
default:
if (static_cast<int>(reg) >= static_cast<int>(PPCRegister::kR0) &&
static_cast<int>(reg) <= static_cast<int>(PPCRegister::kR31)) {
return std::string("r") +
std::to_string(static_cast<int>(reg) -
static_cast<int>(PPCRegister::kR0));
} else if (static_cast<int>(reg) >= static_cast<int>(PPCRegister::kFR0) &&
static_cast<int>(reg) <=
static_cast<int>(PPCRegister::kFR31)) {
return std::string("fr") +
std::to_string(static_cast<int>(reg) -
static_cast<int>(PPCRegister::kFR0));
} else if (static_cast<int>(reg) >= static_cast<int>(PPCRegister::kVR0) &&
static_cast<int>(reg) <=
static_cast<int>(PPCRegister::kVR128)) {
return std::string("vr") +
std::to_string(static_cast<int>(reg) -
static_cast<int>(PPCRegister::kVR0));
} else {
assert_unhandled_case(reg);
return "?";
}
}
return std::strtod(value, nullptr);
}
vec128_t ParseVec128(const char* value) {
vec128_t v;
char* p = const_cast<char*>(value);
if (*p == '[') ++p;
v.i32[0] = std::strtoul(p, &p, 16);
while (*p == ' ' || *p == ',') ++p;
v.i32[1] = std::strtoul(p, &p, 16);
while (*p == ' ' || *p == ',') ++p;
v.i32[2] = std::strtoul(p, &p, 16);
while (*p == ' ' || *p == ',') ++p;
v.i32[3] = std::strtoul(p, &p, 16);
return v;
std::string PPCContext::GetStringFromValue(PPCRegister reg) const {
switch (reg) {
case PPCRegister::kLR:
return string_util::to_hex_string(lr);
case PPCRegister::kCTR:
return string_util::to_hex_string(ctr);
case PPCRegister::kXER:
// return Int64ToHex(xer_ca);
return "?";
case PPCRegister::kFPSCR:
// return Int64ToHex(fpscr);
return "?";
case PPCRegister::kVSCR:
// return Int64ToHex(vscr_sat);
return "?";
case PPCRegister::kCR:
// return Int64ToHex(cr);
return "?";
default:
if (static_cast<int>(reg) >= static_cast<int>(PPCRegister::kR0) &&
static_cast<int>(reg) <= static_cast<int>(PPCRegister::kR31)) {
return string_util::to_hex_string(
r[static_cast<int>(reg) - static_cast<int>(PPCRegister::kR0)]);
} else if (static_cast<int>(reg) >= static_cast<int>(PPCRegister::kFR0) &&
static_cast<int>(reg) <=
static_cast<int>(PPCRegister::kFR31)) {
return string_util::to_hex_string(
f[static_cast<int>(reg) - static_cast<int>(PPCRegister::kFR0)]);
} else if (static_cast<int>(reg) >= static_cast<int>(PPCRegister::kVR0) &&
static_cast<int>(reg) <=
static_cast<int>(PPCRegister::kVR128)) {
return string_util::to_hex_string(
v[static_cast<int>(reg) - static_cast<int>(PPCRegister::kVR0)]);
} else {
assert_unhandled_case(reg);
return "";
}
}
}
void PPCContext::SetValueFromString(PPCRegister reg, std::string value) {
// TODO(benvanik): set value from string to replace SetRegFromString?
assert_always(false);
}
void PPCContext::SetRegFromString(const char* name, const char* value) {
int n;
if (sscanf(name, "r%d", &n) == 1) {
this->r[n] = ParseInt64(value);
this->r[n] = string_util::from_string<uint64_t>(value);
} else if (sscanf(name, "f%d", &n) == 1) {
this->f[n] = ParseFloat64(value);
this->f[n] = string_util::from_string<double>(value);
} else if (sscanf(name, "v%d", &n) == 1) {
this->v[n] = ParseVec128(value);
this->v[n] = string_util::from_string<vec128_t>(value);
} else {
printf("Unrecognized register name: %s\n", name);
}
}
bool PPCContext::CompareRegWithString(const char* name, const char* value,
char* out_value, size_t out_value_size) {
char* out_value,
size_t out_value_size) const {
int n;
if (sscanf(name, "r%d", &n) == 1) {
uint64_t expected = ParseInt64(value);
uint64_t expected = string_util::from_string<uint64_t>(value);
if (this->r[n] != expected) {
snprintf(out_value, out_value_size, "%016" PRIX64, this->r[n]);
std::snprintf(out_value, out_value_size, "%016" PRIX64, this->r[n]);
return false;
}
return true;
} else if (sscanf(name, "f%d", &n) == 1) {
double expected = ParseFloat64(value);
double expected = string_util::from_string<double>(value);
// TODO(benvanik): epsilon
if (this->f[n] != expected) {
snprintf(out_value, out_value_size, "%f", this->f[n]);
std::snprintf(out_value, out_value_size, "%f", this->f[n]);
return false;
}
return true;
} else if (sscanf(name, "v%d", &n) == 1) {
vec128_t expected = ParseVec128(value);
vec128_t expected = string_util::from_string<vec128_t>(value);
if (this->v[n] != expected) {
snprintf(out_value, out_value_size, "[%.8X, %.8X, %.8X, %.8X]",
this->v[n].i32[0], this->v[n].i32[1], this->v[n].i32[2],
this->v[n].i32[3]);
std::snprintf(out_value, out_value_size, "[%.8X, %.8X, %.8X, %.8X]",
this->v[n].i32[0], this->v[n].i32[1], this->v[n].i32[2],
this->v[n].i32[3]);
return false;
}
return true;
} else {
printf("Unrecognized register name: %s\n", name);
assert_always("Unrecognized register name: %s\n", name);
return false;
}
}

View File

@@ -11,6 +11,7 @@
#define XENIA_CPU_FRONTEND_PPC_CONTEXT_H_
#include <cstdint>
#include <string>
#include "xenia/base/vec128.h"
@@ -40,6 +41,209 @@ namespace frontend {
// 100: invalid
// 128-256: VR
enum class PPCRegister {
kR0 = 0,
kR1,
kR2,
kR3,
kR4,
kR5,
kR6,
kR7,
kR8,
kR9,
kR10,
kR11,
kR12,
kR13,
kR14,
kR15,
kR16,
kR17,
kR18,
kR19,
kR20,
kR21,
kR22,
kR23,
kR24,
kR25,
kR26,
kR27,
kR28,
kR29,
kR30,
kR31,
kFR0 = 32,
kFR1,
kFR2,
kFR3,
kFR4,
kFR5,
kFR6,
kFR7,
kFR8,
kFR9,
kFR10,
kFR11,
kFR12,
kFR13,
kFR14,
kFR15,
kFR16,
kFR17,
kFR18,
kFR19,
kFR20,
kFR21,
kFR22,
kFR23,
kFR24,
kFR25,
kFR26,
kFR27,
kFR28,
kFR29,
kFR30,
kFR31,
kVR0 = 64,
kVR1,
kVR2,
kVR3,
kVR4,
kVR5,
kVR6,
kVR7,
kVR8,
kVR9,
kVR10,
kVR11,
kVR12,
kVR13,
kVR14,
kVR15,
kVR16,
kVR17,
kVR18,
kVR19,
kVR20,
kVR21,
kVR22,
kVR23,
kVR24,
kVR25,
kVR26,
kVR27,
kVR28,
kVR29,
kVR30,
kVR31,
kVR32,
kVR33,
kVR34,
kVR35,
kVR36,
kVR37,
kVR38,
kVR39,
kVR40,
kVR41,
kVR42,
kVR43,
kVR44,
kVR45,
kVR46,
kVR47,
kVR48,
kVR49,
kVR50,
kVR51,
kVR52,
kVR53,
kVR54,
kVR55,
kVR56,
kVR57,
kVR58,
kVR59,
kVR60,
kVR61,
kVR62,
kVR63,
kVR64,
kVR65,
kVR66,
kVR67,
kVR68,
kVR69,
kVR70,
kVR71,
kVR72,
kVR73,
kVR74,
kVR75,
kVR76,
kVR77,
kVR78,
kVR79,
kVR80,
kVR81,
kVR82,
kVR83,
kVR84,
kVR85,
kVR86,
kVR87,
kVR88,
kVR89,
kVR90,
kVR91,
kVR92,
kVR93,
kVR94,
kVR95,
kVR96,
kVR97,
kVR98,
kVR99,
kVR100,
kVR101,
kVR102,
kVR103,
kVR104,
kVR105,
kVR106,
kVR107,
kVR108,
kVR109,
kVR110,
kVR111,
kVR112,
kVR113,
kVR114,
kVR115,
kVR116,
kVR117,
kVR118,
kVR119,
kVR120,
kVR121,
kVR122,
kVR123,
kVR124,
kVR125,
kVR126,
kVR127,
kVR128,
kLR,
kCTR,
kXER,
kFPSCR,
kVSCR,
kCR,
};
#pragma pack(push, 8)
typedef struct alignas(64) PPCContext_s {
// Must be stored at 0x0 for now.
@@ -218,9 +422,13 @@ typedef struct alignas(64) PPCContext_s {
// Keep the struct padded out to 64b total.
uint8_t _padding[8];
static std::string GetRegisterName(PPCRegister reg);
std::string GetStringFromValue(PPCRegister reg) const;
void SetValueFromString(PPCRegister reg, std::string value);
void SetRegFromString(const char* name, const char* value);
bool CompareRegWithString(const char* name, const char* value,
char* out_value, size_t out_value_size);
char* out_value, size_t out_value_size) const;
} PPCContext;
#pragma pack(pop)
static_assert(sizeof(PPCContext) % 64 == 0, "64b padded");

View File

@@ -14,6 +14,7 @@
#include <string>
#include "xenia/cpu/function.h"
#include "xenia/cpu/x64_context.h"
namespace xe {
namespace cpu {
@@ -81,6 +82,7 @@ class StackWalker {
virtual size_t CaptureStackTrace(void* thread_handle,
uint64_t* frame_host_pcs,
size_t frame_offset, size_t frame_count,
X64Context* out_host_context,
uint64_t* out_stack_hash = nullptr) = 0;
// Resolves symbol information for the given stack frames.

View File

@@ -152,7 +152,10 @@ class Win32StackWalker : public StackWalker {
size_t CaptureStackTrace(void* thread_handle, uint64_t* frame_host_pcs,
size_t frame_offset, size_t frame_count,
X64Context* out_host_context,
uint64_t* out_stack_hash) override {
// TODO(benvanik): use xstate?
// https://msdn.microsoft.com/en-us/library/windows/desktop/hh134240(v=vs.85).aspx
// Query context. Thread must be suspended.
// Need at least CONTEXT_CONTROL (for rip and rsp) and CONTEXT_INTEGER (for
// rbp).
@@ -163,6 +166,15 @@ class Win32StackWalker : public StackWalker {
return 0;
}
if (out_host_context) {
out_host_context->rip = thread_context.Rip;
out_host_context->eflags = thread_context.EFlags;
std::memcpy(&out_host_context->int_registers.values, &thread_context.Rax,
sizeof(out_host_context->int_registers.values));
std::memcpy(&out_host_context->xmm_registers.values, &thread_context.Xmm0,
sizeof(out_host_context->xmm_registers.values));
}
// Setup the frame for walking.
STACKFRAME64 stack_frame = {0};
stack_frame.AddrPC.Mode = AddrModeFlat;

View File

@@ -0,0 +1,63 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/cpu/x64_context.h"
#include "xenia/base/assert.h"
#include "xenia/base/string_util.h"
namespace xe {
namespace cpu {
// NOTE: this order matches 1:1 with the X64Register enum.
static const char* kRegisterNames[] = {
"rip", "eflags", "rax", "rcx", "rdx", "rbx", "rsp",
"rbp", "rsi", "rdi", "r8", "r9", "r10", "r11",
"r12", "r13", "r14", "r15", "xmm0", "xmm1", "xmm2",
"xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9",
"xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15",
};
const char* X64Context::GetRegisterName(X64Register reg) {
return kRegisterNames[static_cast<int>(reg)];
}
std::string X64Context::GetStringFromValue(X64Register reg) const {
switch (reg) {
case X64Register::kRip:
return string_util::to_hex_string(rip);
case X64Register::kEflags:
return string_util::to_hex_string(eflags);
default:
if (static_cast<int>(reg) >= static_cast<int>(X64Register::kRax) &&
static_cast<int>(reg) <= static_cast<int>(X64Register::kR15)) {
return string_util::to_hex_string(
int_registers.values[static_cast<int>(reg) -
static_cast<int>(X64Register::kRax)]);
} else if (static_cast<int>(reg) >=
static_cast<int>(X64Register::kXmm0) &&
static_cast<int>(reg) <=
static_cast<int>(X64Register::kXmm15)) {
return string_util::to_hex_string(
xmm_registers.values[static_cast<int>(reg) -
static_cast<int>(X64Register::kXmm0)]);
} else {
assert_unhandled_case(reg);
return "";
}
}
}
void X64Context::SetValueFromString(X64Register reg, std::string value) {
// TODO(benvanik): set value from string.
assert_always(false);
}
} // namespace cpu
} // namespace xe

113
src/xenia/cpu/x64_context.h Normal file
View File

@@ -0,0 +1,113 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_CPU_X64_CONTEXT_H_
#define XENIA_CPU_X64_CONTEXT_H_
#include <cstdint>
#include <string>
namespace xe {
namespace cpu {
enum class X64Register {
// NOTE: this order matches 1:1 with the order in the X64Context.
// NOTE: this order matches 1:1 with a string table in the x64_context.cc.
kRip,
kEflags,
kRax,
kRcx,
kRdx,
kRbx,
kRsp,
kRbp,
kRsi,
kRdi,
kR8,
kR9,
kR10,
kR11,
kR12,
kR13,
kR14,
kR15,
kXmm0,
kXmm1,
kXmm2,
kXmm3,
kXmm4,
kXmm5,
kXmm6,
kXmm7,
kXmm8,
kXmm9,
kXmm10,
kXmm11,
kXmm12,
kXmm13,
kXmm14,
kXmm15,
};
struct X64Context {
// TODO(benvanik): x64 registers.
uint64_t rip;
uint32_t eflags;
union {
struct {
uint64_t rax;
uint64_t rcx;
uint64_t rdx;
uint64_t rbx;
uint64_t rsp;
uint64_t rbp;
uint64_t rsi;
uint64_t rdi;
uint64_t r8;
uint64_t r9;
uint64_t r10;
uint64_t r11;
uint64_t r12;
uint64_t r13;
uint64_t r14;
uint64_t r15;
};
uint64_t values[16];
} int_registers;
union {
struct {
__m128 xmm0;
__m128 xmm1;
__m128 xmm2;
__m128 xmm3;
__m128 xmm4;
__m128 xmm5;
__m128 xmm6;
__m128 xmm7;
__m128 xmm8;
__m128 xmm9;
__m128 xmm10;
__m128 xmm11;
__m128 xmm12;
__m128 xmm13;
__m128 xmm14;
__m128 xmm15;
};
__m128 values[16];
} xmm_registers;
static const char* GetRegisterName(X64Register reg);
std::string GetStringFromValue(X64Register reg) const;
void SetValueFromString(X64Register reg, std::string value);
};
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_X64_CONTEXT_H_