Postmortem debug target now loads/scans the trace and inits the filesystem.
This commit is contained in:
@@ -11,6 +11,20 @@
|
||||
|
||||
namespace xdb {
|
||||
|
||||
//
|
||||
bool DebugTarget::InitializeFileSystem(const std::wstring& path) {
|
||||
file_system_.reset(new xe::kernel::fs::FileSystem());
|
||||
|
||||
// Infer the type (stfs/iso/etc) from the path.
|
||||
auto file_system_type = file_system_->InferType(path);
|
||||
|
||||
// Setup the file system exactly like the emulator does - this way we can
|
||||
// access all the same files.
|
||||
if (file_system_->InitializeFromPath(file_system_type, path)) {
|
||||
XELOGE("Unable to initialize filesystem from path");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace xdb
|
||||
|
||||
@@ -10,12 +10,22 @@
|
||||
#ifndef XDB_DEBUG_TARGET_H_
|
||||
#define XDB_DEBUG_TARGET_H_
|
||||
|
||||
#include <xenia/kernel/fs/filesystem.h>
|
||||
|
||||
namespace xdb {
|
||||
|
||||
class DebugTarget {
|
||||
public:
|
||||
virtual ~DebugTarget() = default;
|
||||
|
||||
xe::kernel::fs::FileSystem* file_system() const { return file_system_.get(); }
|
||||
|
||||
protected:
|
||||
DebugTarget() = default;
|
||||
|
||||
bool InitializeFileSystem(const std::wstring& path);
|
||||
|
||||
std::unique_ptr<xe::kernel::fs::FileSystem> file_system_;
|
||||
};
|
||||
|
||||
} // namespace xdb
|
||||
|
||||
@@ -9,19 +9,109 @@
|
||||
|
||||
#include <xdb/postmortem_debug_target.h>
|
||||
|
||||
#include <poly/poly.h>
|
||||
#include <xenia/logging.h>
|
||||
|
||||
namespace xdb {
|
||||
|
||||
bool PostmortemDebugTarget::LoadTrace(const std::wstring& path) {
|
||||
// TODO(benvanik): memory map trace.
|
||||
return true;
|
||||
using xdb::protocol::EventType;
|
||||
|
||||
// Matches the EventType ordering to allow for quick event size checks.
|
||||
const size_t event_sizes[] = {
|
||||
0,
|
||||
sizeof(protocol::ProcessStartEvent),
|
||||
sizeof(protocol::ProcessExitEvent),
|
||||
sizeof(protocol::ModuleLoadEvent),
|
||||
sizeof(protocol::ModuleUnloadEvent),
|
||||
sizeof(protocol::ThreadCreateEvent),
|
||||
sizeof(protocol::ThreadInfoEvent),
|
||||
sizeof(protocol::ThreadExitEvent),
|
||||
sizeof(protocol::FunctionCompiledEvent),
|
||||
sizeof(protocol::OutputStringEvent),
|
||||
sizeof(protocol::KernelCallEvent),
|
||||
sizeof(protocol::KernelCallReturnEvent),
|
||||
sizeof(protocol::UserCallEvent),
|
||||
sizeof(protocol::UserCallReturnEvent),
|
||||
sizeof(protocol::InstrEvent),
|
||||
sizeof(protocol::InstrEventR8),
|
||||
sizeof(protocol::InstrEventR8R8),
|
||||
sizeof(protocol::InstrEventR8R16),
|
||||
sizeof(protocol::InstrEventR16),
|
||||
sizeof(protocol::InstrEventR16R8),
|
||||
sizeof(protocol::InstrEventR16R16),
|
||||
};
|
||||
|
||||
PostmortemDebugTarget::PostmortemDebugTarget()
|
||||
: file_(nullptr),
|
||||
file_mapping_(nullptr),
|
||||
trace_base_(0),
|
||||
process_start_event_(nullptr),
|
||||
process_exit_event_(nullptr) {}
|
||||
|
||||
PostmortemDebugTarget::~PostmortemDebugTarget() {
|
||||
if (trace_base_) {
|
||||
UnmapViewOfFile(trace_base_);
|
||||
}
|
||||
CloseHandle(file_mapping_);
|
||||
CloseHandle(file_);
|
||||
}
|
||||
|
||||
bool PostmortemDebugTarget::LoadContent(const std::wstring& path) {
|
||||
// If no path is provided attempt to infer from the trace.
|
||||
if (path.empty()) {
|
||||
// TODO(benvanik): find process info block and read source path.
|
||||
bool PostmortemDebugTarget::LoadTrace(const std::wstring& path,
|
||||
const std::wstring& content_path) {
|
||||
file_ = CreateFile(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY, nullptr);
|
||||
if (!file_) {
|
||||
XELOGE("Could not open trace file for writing");
|
||||
return false;
|
||||
}
|
||||
// TODO(benvanik): initialize filesystem and load iso/stfs/etc to get at xex.
|
||||
|
||||
file_mapping_ = CreateFileMapping(file_, nullptr, PAGE_READONLY, 0, 0,
|
||||
L"Local\\xenia_xdb_trace");
|
||||
if (!file_mapping_) {
|
||||
XELOGE("Could not create trace file mapping");
|
||||
return false;
|
||||
}
|
||||
|
||||
trace_base_ = reinterpret_cast<const uint8_t*>(
|
||||
MapViewOfFile(file_mapping_, FILE_MAP_READ, 0, 0, 0));
|
||||
if (!trace_base_) {
|
||||
XELOGE("Could not map view of trace file");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the process start event - it should be near the top and we need it for
|
||||
// path lookup.
|
||||
const uint8_t* ptr = trace_base_ + 8;
|
||||
EventType event_type;
|
||||
while ((event_type = poly::load<EventType>(ptr)) !=
|
||||
EventType::END_OF_STREAM) {
|
||||
switch (event_type) {
|
||||
case EventType::PROCESS_START: {
|
||||
process_start_event_ = protocol::ProcessStartEvent::Get(ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (process_start_event_) {
|
||||
break;
|
||||
}
|
||||
ptr += event_sizes[static_cast<uint8_t>(event_type)];
|
||||
}
|
||||
|
||||
bool initialized_filesystem = false;
|
||||
if (!content_path.empty()) {
|
||||
initialized_filesystem = InitializeFileSystem(content_path);
|
||||
} else {
|
||||
// If no path was provided just use what's in the trace.
|
||||
auto trace_content_path =
|
||||
poly::to_wstring(std::string(process_start_event_->launch_path));
|
||||
initialized_filesystem = InitializeFileSystem(trace_content_path);
|
||||
}
|
||||
if (!initialized_filesystem) {
|
||||
XELOGE("Unable to initialize filesystem.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -32,6 +122,96 @@ bool PostmortemDebugTarget::Prepare() {
|
||||
|
||||
bool PostmortemDebugTarget::Prepare(std::atomic<bool>& cancelled) {
|
||||
// TODO(benvanik): scan file, build indicies, etc.
|
||||
|
||||
const uint8_t* ptr = trace_base_ + 8;
|
||||
EventType event_type;
|
||||
while ((event_type = poly::load<EventType>(ptr)) !=
|
||||
EventType::END_OF_STREAM) {
|
||||
switch (event_type) {
|
||||
case EventType::PROCESS_START: {
|
||||
process_start_event_ = protocol::ProcessStartEvent::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::PROCESS_EXIT: {
|
||||
process_exit_event_ = protocol::ProcessExitEvent::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::MODULE_LOAD: {
|
||||
auto ev = protocol::ModuleLoadEvent::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::MODULE_UNLOAD: {
|
||||
auto ev = protocol::ModuleUnloadEvent::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::THREAD_CREATE: {
|
||||
auto ev = protocol::ThreadCreateEvent::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::THREAD_INFO: {
|
||||
auto ev = protocol::ThreadInfoEvent::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::THREAD_EXIT: {
|
||||
auto ev = protocol::ThreadExitEvent::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::FUNCTION_COMPILED: {
|
||||
auto ev = protocol::FunctionCompiledEvent::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::OUTPUT_STRING: {
|
||||
auto ev = protocol::OutputStringEvent::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::KERNEL_CALL: {
|
||||
auto ev = protocol::KernelCallEvent::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::KERNEL_CALL_RETURN: {
|
||||
auto ev = protocol::KernelCallReturnEvent::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::USER_CALL: {
|
||||
auto ev = protocol::UserCallEvent::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::USER_CALL_RETURN: {
|
||||
auto ev = protocol::UserCallReturnEvent::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::INSTR: {
|
||||
auto ev = protocol::InstrEvent::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::INSTR_R8: {
|
||||
auto ev = protocol::InstrEventR8::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::INSTR_R8_R8: {
|
||||
auto ev = protocol::InstrEventR8R8::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::INSTR_R8_R16: {
|
||||
auto ev = protocol::InstrEventR8R16::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::INSTR_R16: {
|
||||
auto ev = protocol::InstrEventR16::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::INSTR_R16_R8: {
|
||||
auto ev = protocol::InstrEventR16R8::Get(ptr);
|
||||
break;
|
||||
}
|
||||
case EventType::INSTR_R16_R16: {
|
||||
auto ev = protocol::InstrEventR16R16::Get(ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ptr += event_sizes[static_cast<uint8_t>(event_type)];
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,22 +10,34 @@
|
||||
#ifndef XDB_POSTMORTEM_DEBUG_TARGET_H_
|
||||
#define XDB_POSTMORTEM_DEBUG_TARGET_H_
|
||||
|
||||
// TODO(benvanik): abstract mapping type.
|
||||
#include <Windows.h>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
|
||||
#include <xdb/debug_target.h>
|
||||
#include <xdb/protocol.h>
|
||||
|
||||
namespace xdb {
|
||||
|
||||
class PostmortemDebugTarget : public DebugTarget {
|
||||
public:
|
||||
PostmortemDebugTarget() = default;
|
||||
PostmortemDebugTarget();
|
||||
~PostmortemDebugTarget() override;
|
||||
|
||||
bool LoadTrace(const std::wstring& path);
|
||||
bool LoadContent(const std::wstring& path = L"");
|
||||
bool LoadTrace(const std::wstring& path,
|
||||
const std::wstring& content_path = L"");
|
||||
|
||||
bool Prepare();
|
||||
bool Prepare(std::atomic<bool>& cancelled);
|
||||
|
||||
private:
|
||||
HANDLE file_;
|
||||
HANDLE file_mapping_;
|
||||
const uint8_t* trace_base_;
|
||||
|
||||
const protocol::ProcessStartEvent* process_start_event_;
|
||||
const protocol::ProcessExitEvent* process_exit_event_;
|
||||
};
|
||||
|
||||
} // namespace xdb
|
||||
|
||||
@@ -23,7 +23,7 @@ using vec128_t = alloy::vec128_t;
|
||||
#pragma pack(push, 4)
|
||||
|
||||
enum class EventType : uint8_t {
|
||||
SETUP,
|
||||
END_OF_STREAM = 0,
|
||||
|
||||
PROCESS_START,
|
||||
PROCESS_EXIT,
|
||||
@@ -61,15 +61,16 @@ struct Event {
|
||||
sizeof(T), reinterpret_cast<uint64_t*>(trace_base));
|
||||
return reinterpret_cast<T*>(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
struct SetupEvent : public Event<SetupEvent> {
|
||||
EventType type;
|
||||
uint64_t membase;
|
||||
static const T* Get(const void* ptr) {
|
||||
return reinterpret_cast<const T*>(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
struct ProcessStartEvent : public Event<ProcessStartEvent> {
|
||||
EventType type;
|
||||
uint64_t membase;
|
||||
char launch_path[256];
|
||||
};
|
||||
|
||||
struct ProcessExitEvent : public Event<ProcessExitEvent> {
|
||||
|
||||
@@ -10,10 +10,9 @@
|
||||
#include <xdb/ui/xdb_app.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <codecvt>
|
||||
#include <thread>
|
||||
|
||||
#include <poly/assert.h>
|
||||
#include <poly/poly.h>
|
||||
#include <xdb/postmortem_debug_target.h>
|
||||
#include <xdb/ui/main_frame.h>
|
||||
#include <xdb/ui/open_postmortem_trace_dialog.h>
|
||||
@@ -46,25 +45,19 @@ void XdbApp::OpenEmpty() {
|
||||
|
||||
bool XdbApp::OpenTraceFile(const std::string& trace_file_path,
|
||||
const std::string& content_file_path) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
return OpenTraceFile(converter.from_bytes(trace_file_path),
|
||||
converter.from_bytes(content_file_path));
|
||||
return OpenTraceFile(poly::to_wstring(trace_file_path),
|
||||
poly::to_wstring(content_file_path));
|
||||
}
|
||||
|
||||
bool XdbApp::OpenTraceFile(const std::wstring& trace_file_path,
|
||||
const std::wstring& content_file_path) {
|
||||
std::unique_ptr<PostmortemDebugTarget> target(new PostmortemDebugTarget());
|
||||
|
||||
if (!target->LoadTrace(trace_file_path)) {
|
||||
if (!target->LoadTrace(trace_file_path, content_file_path)) {
|
||||
HandleOpenError("Unable to load trace file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!target->LoadContent(content_file_path)) {
|
||||
HandleOpenError("Unable to load source game content module.");
|
||||
return false;
|
||||
}
|
||||
|
||||
wxProgressDialog progress_dialog(
|
||||
"Preparing trace...", "This may take some time.", 100, nullptr,
|
||||
wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_CAN_ABORT |
|
||||
|
||||
Reference in New Issue
Block a user