Sharing memory.

This commit is contained in:
Ben Vanik 2015-05-24 00:02:47 -07:00
parent 576d6492dc
commit da655d15b3
12 changed files with 373 additions and 18 deletions

View File

@ -12,7 +12,6 @@ using xe.debug.proto;
using Xenia.Debug.Utilities;
namespace Xenia.Debug {
public class Debugger {
private readonly static string kServerHostname = "";
private readonly static int kServerPort = 19000;
@ -35,6 +34,19 @@ namespace Xenia.Debug {
pendingRequests = new ConcurrentDictionary<uint, PendingRequest>();
private uint nextRequestId = 1;
private FileMappingHandle memoryHandle;
public IntPtr Membase {
get;
}
public unsafe byte* TranslateVirtual(uint address) {
return (byte*)Membase.ToPointer() + address;
}
public unsafe byte* TranslatePhysical(uint address) {
return (byte*)Membase.ToPointer() + 0xFFFFFFFF + address;
}
public event EventHandler<State> StateChanged;
public State CurrentState {
@ -126,6 +138,27 @@ namespace Xenia.Debug {
int requestDataOffset = AttachRequest.EndAttachRequest(fbb);
var response = await CommitRequest(fbb, RequestData.AttachRequest, requestDataOffset);
System.Diagnostics.Debug.Assert(response.ResponseDataType ==
ResponseData.AttachResponse);
var attachResponse = new AttachResponse();
response.GetResponseData(attachResponse);
// Open mmap to share memroy.
memoryHandle = FileMapping.OpenFileMapping(
FileMapAccess.FILE_MAP_ALL_ACCESS, false, attachResponse.MemoryFile);
if (memoryHandle.IsInvalid) {
System.Diagnostics.Debug.Fail("Unable to open target memory");
Detach();
return;
}
// Setup the memory system. This maps the emulator memory into our address
// space.
if (!Memory.InitializeMapping(memoryHandle)) {
Detach();
return;
}
OnStateChanged(State.Attached);
}
@ -134,8 +167,17 @@ namespace Xenia.Debug {
return;
}
Memory.UninitializeMapping();
if (memoryHandle != null) {
memoryHandle.Close();
memoryHandle = null;
}
if (socket != null) {
socket.Close();
socket = null;
}
OnStateChanged(State.Detached);
}

View File

@ -6,15 +6,100 @@ using System.Threading.Tasks;
using Xenia.Debug.Utilities;
namespace Xenia.Debug {
public class Memory : Changeable {
public class Memory : Changeable, IDisposable {
private readonly Debugger debugger;
private class MapInfo {
public ulong virtualAddressStart;
public ulong virtualAddressEnd;
public ulong targetAddress;
public IntPtr ptr;
}
private MapInfo[] mapInfos = new MapInfo[]{
// From memory.cc:
new MapInfo(){virtualAddressStart = 0x00000000, virtualAddressEnd = 0x3FFFFFFF,
targetAddress = 0x0000000000000000},
new MapInfo(){virtualAddressStart = 0x40000000, virtualAddressEnd = 0x7EFFFFFF,
targetAddress = 0x0000000040000000},
new MapInfo(){virtualAddressStart = 0x7F000000, virtualAddressEnd = 0x7FFFFFFF,
targetAddress = 0x0000000100000000},
new MapInfo(){virtualAddressStart = 0x80000000, virtualAddressEnd = 0x8FFFFFFF,
targetAddress = 0x0000000080000000},
new MapInfo(){virtualAddressStart = 0x90000000, virtualAddressEnd = 0x9FFFFFFF,
targetAddress = 0x0000000080000000},
new MapInfo(){virtualAddressStart = 0xA0000000, virtualAddressEnd = 0xBFFFFFFF,
targetAddress = 0x0000000100000000},
new MapInfo(){virtualAddressStart = 0xC0000000, virtualAddressEnd = 0xDFFFFFFF,
targetAddress = 0x0000000100000000},
new MapInfo(){virtualAddressStart = 0xE0000000, virtualAddressEnd = 0xFFFFFFFF,
targetAddress = 0x0000000100000000},
new MapInfo(){virtualAddressStart = 0x100000000, virtualAddressEnd = 0x11FFFFFFF,
targetAddress = 0x0000000100000000},
};
public UIntPtr VirtualMembase;
public UIntPtr PhysicalMembase;
public Memory(Debugger debugger) {
this.debugger = debugger;
}
public bool InitializeMapping(FileMappingHandle mappingHandle) {
ulong mappingBase = 0x100000000;
foreach (MapInfo mapInfo in mapInfos) {
uint targetAddressLow = (uint)mapInfo.targetAddress;
uint targetAddressHigh = (uint)(mapInfo.targetAddress >> 32);
mapInfo.ptr = FileMapping.MapViewOfFileEx(
mappingHandle, FileMapAccess.FILE_MAP_ALL_ACCESS,
targetAddressHigh, targetAddressLow,
mapInfo.virtualAddressEnd - mapInfo.virtualAddressStart + 1,
mappingBase + mapInfo.virtualAddressStart);
if (mapInfo.ptr == IntPtr.Zero) {
System.Diagnostics.Debug.Fail("Unable to place memory at target address");
return false;
}
}
VirtualMembase = new UIntPtr(mappingBase);
PhysicalMembase = new UIntPtr(mappingBase + 0x100000000);
return true;
}
public void UninitializeMapping() {
foreach (MapInfo mapInfo in mapInfos) {
FileMapping.UnmapViewOfFile(mapInfo.ptr);
mapInfo.ptr = IntPtr.Zero;
}
}
private void OnDispose() {
UninitializeMapping();
}
public MemoryView CreateView() {
return new MemoryView(this);
}
#region Dispose
private bool disposed = false;
~Memory() {
Dispose(false);
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (!disposed) {
if (disposing) {
// TODO: dispose managed state (managed objects).
}
OnDispose();
disposed = true;
}
}
#endregion
}
}

View File

@ -10,8 +10,25 @@ public sealed class AttachResponse : Table {
public static AttachResponse GetRootAsAttachResponse(ByteBuffer _bb, AttachResponse obj) { return (obj.__init(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
public AttachResponse __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
public string MemoryFile { get { int o = __offset(4); return o != 0 ? __string(o + bb_pos) : null; } }
public string FunctionsFile { get { int o = __offset(6); return o != 0 ? __string(o + bb_pos) : null; } }
public string FunctionsTraceFile { get { int o = __offset(8); return o != 0 ? __string(o + bb_pos) : null; } }
public static void StartAttachResponse(FlatBufferBuilder builder) { builder.StartObject(0); }
public static int CreateAttachResponse(FlatBufferBuilder builder,
int memory_file = 0,
int functions_file = 0,
int functions_trace_file = 0) {
builder.StartObject(3);
AttachResponse.AddFunctionsTraceFile(builder, functions_trace_file);
AttachResponse.AddFunctionsFile(builder, functions_file);
AttachResponse.AddMemoryFile(builder, memory_file);
return AttachResponse.EndAttachResponse(builder);
}
public static void StartAttachResponse(FlatBufferBuilder builder) { builder.StartObject(3); }
public static void AddMemoryFile(FlatBufferBuilder builder, int memoryFileOffset) { builder.AddOffset(0, memoryFileOffset, 0); }
public static void AddFunctionsFile(FlatBufferBuilder builder, int functionsFileOffset) { builder.AddOffset(1, functionsFileOffset, 0); }
public static void AddFunctionsTraceFile(FlatBufferBuilder builder, int functionsTraceFileOffset) { builder.AddOffset(2, functionsTraceFileOffset, 0); }
public static int EndAttachResponse(FlatBufferBuilder builder) {
int o = builder.EndObject();
return o;

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Xenia.Debug.Utilities {
public class Disposable : IDisposable {
private bool disposed = false;
protected virtual void OnDispose() {
}
protected virtual void Dispose(bool disposing) {
if (!disposed) {
if (disposing) {
// TODO: dispose managed state (managed objects).
}
OnDispose();
disposed = true;
}
}
~Disposable() {
Dispose(false);
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
}
}

View File

@ -0,0 +1,142 @@
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
namespace Xenia.Debug.Utilities {
// From: http://1code.codeplex.com/SourceControl/latest#Visual Studio 2008/CSFileMappingClient/Program.cs
/// <summary>
/// Access rights for file mapping objects
/// http://msdn.microsoft.com/en-us/library/aa366559.aspx
/// </summary>
[Flags]
public enum FileMapAccess {
FILE_MAP_COPY = 0x0001,
FILE_MAP_WRITE = 0x0002,
FILE_MAP_READ = 0x0004,
FILE_MAP_ALL_ACCESS = 0x000F001F
}
/// <summary>
/// Represents a wrapper class for a file mapping handle.
/// </summary>
[SuppressUnmanagedCodeSecurity,
HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort = true)]
public sealed class FileMappingHandle : SafeHandleZeroOrMinusOneIsInvalid {
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
private FileMappingHandle()
: base(true) {
}
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
public FileMappingHandle(IntPtr handle, bool ownsHandle)
: base(ownsHandle) {
base.SetHandle(handle);
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success),
DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr handle);
protected override bool ReleaseHandle() {
return CloseHandle(base.handle);
}
}
/// <summary>
/// The class exposes Windows APIs used in this code sample.
/// </summary>
[SuppressUnmanagedCodeSecurity]
public class FileMapping {
/// <summary>
/// Opens a named file mapping object.
/// </summary>
/// <param name="dwDesiredAccess">
/// The access to the file mapping object. This access is checked against
/// any security descriptor on the target file mapping object.
/// </param>
/// <param name="bInheritHandle">
/// If this parameter is TRUE, a process created by the CreateProcess
/// function can inherit the handle; otherwise, the handle cannot be
/// inherited.
/// </param>
/// <param name="lpName">
/// The name of the file mapping object to be opened.
/// </param>
/// <returns>
/// If the function succeeds, the return value is an open handle to the
/// specified file mapping object.
/// </returns>
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern FileMappingHandle OpenFileMapping(
FileMapAccess dwDesiredAccess, bool bInheritHandle, string lpName);
/// <summary>
/// Maps a view of a file mapping into the address space of a calling
/// process.
/// </summary>
/// <param name="hFileMappingObject">
/// A handle to a file mapping object. The CreateFileMapping and
/// OpenFileMapping functions return this handle.
/// </param>
/// <param name="dwDesiredAccess">
/// The type of access to a file mapping object, which determines the
/// protection of the pages.
/// </param>
/// <param name="dwFileOffsetHigh">
/// A high-order DWORD of the file offset where the view begins.
/// </param>
/// <param name="dwFileOffsetLow">
/// A low-order DWORD of the file offset where the view is to begin.
/// </param>
/// <param name="dwNumberOfBytesToMap">
/// The number of bytes of a file mapping to map to the view. All bytes
/// must be within the maximum size specified by CreateFileMapping.
/// </param>
/// <returns>
/// If the function succeeds, the return value is the starting address
/// of the mapped view.
/// </returns>
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr MapViewOfFile(
FileMappingHandle hFileMappingObject,
FileMapAccess dwDesiredAccess,
uint dwFileOffsetHigh,
uint dwFileOffsetLow,
ulong dwNumberOfBytesToMap);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr MapViewOfFileEx(
FileMappingHandle hFileMappingObject,
FileMapAccess dwDesiredAccess,
uint dwFileOffsetHigh,
uint dwFileOffsetLow,
ulong dwNumberOfBytesToMap,
ulong lpBaseAddress);
/// <summary>
/// Unmaps a mapped view of a file from the calling process's address
/// space.
/// </summary>
/// <param name="lpBaseAddress">
/// A pointer to the base address of the mapped view of a file that
/// is to be unmapped.
/// </param>
/// <returns></returns>
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
}
}

View File

@ -21,6 +21,7 @@
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>..\..\build\bin\Release\</OutputPath>
@ -31,6 +32,7 @@
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@ -88,6 +90,8 @@
<Compile Include="ThreadList.cs" />
<Compile Include="Utilities\Changeable.cs" />
<Compile Include="Utilities\Dispatch.cs" />
<Compile Include="Utilities\Disposable.cs" />
<Compile Include="Utilities\FileMapping.cs" />
<Compile Include="Utilities\TaskExtensions.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View File

@ -60,14 +60,13 @@ Debugger::~Debugger() {
bool Debugger::StartSession() {
std::wstring session_path = xe::to_wstring(FLAGS_debug_session_path);
std::wstring functions_path = xe::join_paths(session_path, L"functions");
functions_path_ = xe::join_paths(session_path, L"functions");
functions_file_ =
ChunkedMappedMemoryWriter::Open(functions_path, 32 * 1024 * 1024, false);
ChunkedMappedMemoryWriter::Open(functions_path_, 32 * 1024 * 1024, false);
std::wstring functions_trace_path =
xe::join_paths(session_path, L"functions.trace");
functions_trace_path_ = xe::join_paths(session_path, L"functions.trace");
functions_trace_file_ = ChunkedMappedMemoryWriter::Open(
functions_trace_path, 32 * 1024 * 1024, true);
functions_trace_path_, 32 * 1024 * 1024, true);
listen_socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listen_socket_ < 1) {
@ -195,9 +194,12 @@ void Debugger::OnMessage(std::vector<uint8_t> buffer) {
case proto::RequestData_AttachRequest: {
// Send debug info.
response_data_type = proto::ResponseData_AttachResponse;
auto response_data = proto::AttachResponseBuilder(fbb);
//
response_data_offset = response_data.Finish().Union();
response_data_offset =
proto::CreateAttachResponse(
fbb, fbb.CreateString(
xe::to_string(emulator()->memory()->file_name())),
fbb.CreateString(xe::to_string(functions_path_)),
fbb.CreateString(xe::to_string(functions_trace_path_))).Union();
// Allow continuation if we were blocked waiting for a client.
accept_fence_.Signal();

View File

@ -116,7 +116,9 @@ class Debugger {
UINT_PTR client_socket_;
std::thread receive_thread_;
std::wstring functions_path_;
std::unique_ptr<ChunkedMappedMemoryWriter> functions_file_;
std::wstring functions_trace_path_;
std::unique_ptr<ChunkedMappedMemoryWriter> functions_trace_file_;
std::mutex threads_lock_;

View File

@ -26,8 +26,9 @@ table AttachRequest {
//
}
table AttachResponse {
// file paths
// mmap name
memory_file:string;
functions_file:string;
functions_trace_file:string;
}
union RequestData {

View File

@ -167,8 +167,17 @@ inline flatbuffers::Offset<AttachRequest> CreateAttachRequest(flatbuffers::FlatB
}
struct AttachResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
const flatbuffers::String *memory_file() const { return GetPointer<const flatbuffers::String *>(4); }
const flatbuffers::String *functions_file() const { return GetPointer<const flatbuffers::String *>(6); }
const flatbuffers::String *functions_trace_file() const { return GetPointer<const flatbuffers::String *>(8); }
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<flatbuffers::uoffset_t>(verifier, 4 /* memory_file */) &&
verifier.Verify(memory_file()) &&
VerifyField<flatbuffers::uoffset_t>(verifier, 6 /* functions_file */) &&
verifier.Verify(functions_file()) &&
VerifyField<flatbuffers::uoffset_t>(verifier, 8 /* functions_trace_file */) &&
verifier.Verify(functions_trace_file()) &&
verifier.EndTable();
}
};
@ -176,16 +185,25 @@ struct AttachResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
struct AttachResponseBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_memory_file(flatbuffers::Offset<flatbuffers::String> memory_file) { fbb_.AddOffset(4, memory_file); }
void add_functions_file(flatbuffers::Offset<flatbuffers::String> functions_file) { fbb_.AddOffset(6, functions_file); }
void add_functions_trace_file(flatbuffers::Offset<flatbuffers::String> functions_trace_file) { fbb_.AddOffset(8, functions_trace_file); }
AttachResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
AttachResponseBuilder &operator=(const AttachResponseBuilder &);
flatbuffers::Offset<AttachResponse> Finish() {
auto o = flatbuffers::Offset<AttachResponse>(fbb_.EndTable(start_, 0));
auto o = flatbuffers::Offset<AttachResponse>(fbb_.EndTable(start_, 3));
return o;
}
};
inline flatbuffers::Offset<AttachResponse> CreateAttachResponse(flatbuffers::FlatBufferBuilder &_fbb) {
inline flatbuffers::Offset<AttachResponse> CreateAttachResponse(flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<flatbuffers::String> memory_file = 0,
flatbuffers::Offset<flatbuffers::String> functions_file = 0,
flatbuffers::Offset<flatbuffers::String> functions_trace_file = 0) {
AttachResponseBuilder builder_(_fbb);
builder_.add_functions_trace_file(functions_trace_file);
builder_.add_functions_file(functions_file);
builder_.add_memory_file(memory_file);
return builder_.Finish();
}

View File

@ -16,6 +16,7 @@
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/threading.h"
#include "xenia/cpu/mmio_handler.h"
// TODO(benvanik): move xbox.h out
@ -114,13 +115,17 @@ Memory::~Memory() {
}
int Memory::Initialize() {
wchar_t file_name[256];
wsprintf(file_name, L"Local\\xenia_memory_%p", xe::threading::ticks());
file_name_ = file_name;
// Create main page file-backed mapping. This is all reserved but
// uncommitted (so it shouldn't expand page file).
#if XE_PLATFORM_WIN32
mapping_ = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE | SEC_RESERVE,
// entire 4gb space + 512mb physical:
1, 0x1FFFFFFF, NULL);
1, 0x1FFFFFFF, file_name_.c_str());
#else
char mapping_path[] = "/xenia/mapping/XXXXXX";
mktemp(mapping_path);

View File

@ -13,6 +13,7 @@
#include <cstdint>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include "xenia/base/platform.h"
@ -158,6 +159,8 @@ class Memory {
int Initialize();
const std::wstring& file_name() const { return file_name_; }
inline uint8_t* virtual_membase() const { return virtual_membase_; }
inline uint8_t* TranslateVirtual(uint32_t guest_address) const {
return virtual_membase_ + guest_address;
@ -211,6 +214,7 @@ class Memory {
void UnmapViews();
private:
std::wstring file_name_;
uint32_t system_page_size_;
uint8_t* virtual_membase_;
uint8_t* physical_membase_;