Basic debugger networking.
This commit is contained in:
parent
969badd8c3
commit
576d6492dc
|
@ -9,8 +9,8 @@
|
|||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\third_party\gflags\src\;$(SolutionDir)\src\;$(SolutionDir)\third_party;$(SolutionDir)\</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>GLEW_STATIC=1;GLEW_MX=1;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32;_WIN64=1;_AMD64=1;MICROPROFILE_MAX_THREADS=256;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\third_party\flatbuffers\include\;$(SolutionDir)\third_party\gflags\src\;$(SolutionDir)\src\;$(SolutionDir)\third_party;$(SolutionDir)\</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>GLEW_STATIC=1;GLEW_MX=1;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;WIN32;_WIN64=1;_AMD64=1;MICROPROFILE_MAX_THREADS=128;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
|
|
|
@ -92,7 +92,6 @@
|
|||
<ClCompile Include="src\xenia\cpu\thread_state.cc" />
|
||||
<ClCompile Include="src\xenia\cpu\xex_module.cc" />
|
||||
<ClCompile Include="src\xenia\debug\debugger.cc" />
|
||||
<ClCompile Include="src\xenia\debug\debug_server.cc" />
|
||||
<ClCompile Include="src\xenia\emulator.cc" />
|
||||
<ClCompile Include="src\xenia\gpu\gl4\blitter.cc" />
|
||||
<ClCompile Include="src\xenia\gpu\gl4\circular_buffer.cc" />
|
||||
|
@ -291,9 +290,10 @@
|
|||
<ClInclude Include="src\xenia\cpu\xex_module.h" />
|
||||
<ClInclude Include="src\xenia\debug\breakpoint.h" />
|
||||
<ClInclude Include="src\xenia\debug\debugger.h" />
|
||||
<ClInclude Include="src\xenia\debug\debug_server.h" />
|
||||
<ClInclude Include="src\xenia\debug\function_data.h" />
|
||||
<ClInclude Include="src\xenia\debug\function_trace_data.h" />
|
||||
<ClInclude Include="src\xenia\debug\proto\breakpoints_generated.h" />
|
||||
<ClInclude Include="src\xenia\debug\proto\messages_generated.h" />
|
||||
<ClInclude Include="src\xenia\emulator.h" />
|
||||
<ClInclude Include="src\xenia\gpu\gl4\blitter.h" />
|
||||
<ClInclude Include="src\xenia\gpu\gl4\circular_buffer.h" />
|
||||
|
@ -419,6 +419,7 @@
|
|||
<ItemGroup>
|
||||
<None Include="src\xenia\cpu\backend\x64\x64_sequence.inl" />
|
||||
<None Include="src\xenia\cpu\hir\opcodes.inl" />
|
||||
<None Include="src\xenia\debug\proto\messages.fbs" />
|
||||
<None Include="src\xenia\gpu\register_table.inc" />
|
||||
<None Include="src\xenia\kernel\util\export_table_post.inc" />
|
||||
<None Include="src\xenia\kernel\util\export_table_pre.inc" />
|
||||
|
@ -485,7 +486,7 @@
|
|||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>BEA_ENGINE_STATIC=1;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\third_party\libav-bin\include\;$(SolutionDir)\third_party\beaengine\include\;$(SolutionDir)\third_party\llvm\include\;$(SolutionDir)\third_party\gflags\src\;$(SolutionDir)\src\;$(SolutionDir)\third_party;$(SolutionDir)\</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\third_party\libav-bin\include\;$(SolutionDir)\third_party\beaengine\include\;$(SolutionDir)\third_party\llvm\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
|
@ -503,7 +504,7 @@
|
|||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>BEA_ENGINE_STATIC=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\third_party\libav-bin\include\;$(SolutionDir)\third_party\beaengine\include\;$(SolutionDir)\third_party\llvm\include\;$(SolutionDir)\third_party\gflags\src\;$(SolutionDir)\src\;$(SolutionDir)\third_party;$(SolutionDir)\</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\third_party\libav-bin\include\;$(SolutionDir)\third_party\beaengine\include\;$(SolutionDir)\third_party\llvm\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
|
@ -523,7 +524,7 @@
|
|||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>BEA_ENGINE_STATIC=1;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\third_party\libav-bin\include\;$(SolutionDir)\third_party\beaengine\include\;$(SolutionDir)\third_party\llvm\include\;$(SolutionDir)\third_party\gflags\src\;$(SolutionDir)\src\;$(SolutionDir)\third_party;$(SolutionDir)\</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\third_party\libav-bin\include\;$(SolutionDir)\third_party\beaengine\include\;$(SolutionDir)\third_party\llvm\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
|
|
|
@ -382,9 +382,6 @@
|
|||
<ClCompile Include="src\xenia\debug\debugger.cc">
|
||||
<Filter>src\xenia\debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\xenia\debug\debug_server.cc">
|
||||
<Filter>src\xenia\debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\xenia\cpu\compiler\passes\finalization_pass.cc">
|
||||
<Filter>src\xenia\cpu\compiler\passes</Filter>
|
||||
</ClCompile>
|
||||
|
@ -984,9 +981,6 @@
|
|||
<ClInclude Include="src\xenia\debug\debugger.h">
|
||||
<Filter>src\xenia\debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\xenia\debug\debug_server.h">
|
||||
<Filter>src\xenia\debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\xenia\debug\function_data.h">
|
||||
<Filter>src\xenia\debug</Filter>
|
||||
</ClInclude>
|
||||
|
@ -1349,6 +1343,11 @@
|
|||
</ClInclude>
|
||||
<ClInclude Include="src\xenia\apu\audio_decoder.h">
|
||||
<Filter>src\xenia\apu</Filter>
|
||||
<ClInclude Include="src\xenia\debug\proto\messages_generated.h">
|
||||
<Filter>src\xenia\debug\proto</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\xenia\debug\proto\breakpoints_generated.h">
|
||||
<Filter>src\xenia\debug\proto</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -1379,5 +1378,8 @@
|
|||
<None Include="src\xenia\kernel\xboxkrnl_table.inc">
|
||||
<Filter>src\xenia\kernel</Filter>
|
||||
</None>
|
||||
<None Include="src\xenia\debug\proto\messages.fbs">
|
||||
<Filter>src\xenia\debug\proto</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -29,7 +29,7 @@
|
|||
this.mainToolStrip = new System.Windows.Forms.ToolStrip();
|
||||
this.toolStripButton1 = new System.Windows.Forms.ToolStripButton();
|
||||
this.statusStrip = new System.Windows.Forms.StatusStrip();
|
||||
this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel();
|
||||
this.statusMessageLabel = new System.Windows.Forms.ToolStripStatusLabel();
|
||||
this.mainMenuStrip.SuspendLayout();
|
||||
this.mainToolStrip.SuspendLayout();
|
||||
this.statusStrip.SuspendLayout();
|
||||
|
@ -72,18 +72,18 @@
|
|||
// statusStrip
|
||||
//
|
||||
this.statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.toolStripStatusLabel1});
|
||||
this.statusMessageLabel});
|
||||
this.statusStrip.Location = new System.Drawing.Point(0, 1081);
|
||||
this.statusStrip.Name = "statusStrip";
|
||||
this.statusStrip.Size = new System.Drawing.Size(1571, 22);
|
||||
this.statusStrip.TabIndex = 4;
|
||||
this.statusStrip.Text = "statusStrip1";
|
||||
//
|
||||
// toolStripStatusLabel1
|
||||
// statusMessageLabel
|
||||
//
|
||||
this.toolStripStatusLabel1.Name = "toolStripStatusLabel1";
|
||||
this.toolStripStatusLabel1.Size = new System.Drawing.Size(118, 17);
|
||||
this.toolStripStatusLabel1.Text = "toolStripStatusLabel1";
|
||||
this.statusMessageLabel.Name = "statusMessageLabel";
|
||||
this.statusMessageLabel.Size = new System.Drawing.Size(118, 17);
|
||||
this.statusMessageLabel.Text = "";
|
||||
//
|
||||
// MainWindow
|
||||
//
|
||||
|
@ -115,7 +115,7 @@
|
|||
private System.Windows.Forms.ToolStrip mainToolStrip;
|
||||
private System.Windows.Forms.ToolStripButton toolStripButton1;
|
||||
private System.Windows.Forms.StatusStrip statusStrip;
|
||||
private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1;
|
||||
private System.Windows.Forms.ToolStripStatusLabel statusMessageLabel;
|
||||
private WeifenLuo.WinFormsUI.Docking.DockPanel dockPanel;
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ using System.Threading.Tasks;
|
|||
using System.Windows.Forms;
|
||||
using WeifenLuo.WinFormsUI.Docking;
|
||||
using Xenia.Debug.UI.Views;
|
||||
using Xenia.Debug.Utilities;
|
||||
|
||||
namespace Xenia.Debug.UI {
|
||||
public partial class MainWindow : Form {
|
||||
|
@ -16,14 +17,14 @@ namespace Xenia.Debug.UI {
|
|||
|
||||
private BreakpointsPanel breakpointsPanel;
|
||||
private CallstackPanel callstackPanel;
|
||||
private List<CodeDocument> codeDocuments = new List<CodeDocument>();
|
||||
private readonly List<CodeDocument> codeDocuments = new List<CodeDocument>();
|
||||
private FilesystemPanel filesystemPanel;
|
||||
private FunctionsPanel functionsPanel;
|
||||
private HeapDocument heapDocument;
|
||||
private List<MemoryDocument> memoryDocuments = new List<MemoryDocument>();
|
||||
private readonly List<MemoryDocument> memoryDocuments = new List<MemoryDocument>();
|
||||
private ModulesPanel modulesPanel;
|
||||
private ProfilePanel profilePanel;
|
||||
private List<RegistersPanel> registersPanels = new List<RegistersPanel>();
|
||||
private readonly List<RegistersPanel> registersPanels = new List<RegistersPanel>();
|
||||
private StatisticsDocument statisticsDocument;
|
||||
private ThreadsPanel threadsPanel;
|
||||
private TracePanel tracePanel;
|
||||
|
@ -44,7 +45,9 @@ namespace Xenia.Debug.UI {
|
|||
Controls.Add(dockPanel);
|
||||
Controls.SetChildIndex(dockPanel, 0);
|
||||
|
||||
this.Debugger = new Debugger();
|
||||
Debugger = new Debugger((AsyncTask task) => {
|
||||
BeginInvoke(task);
|
||||
});
|
||||
|
||||
breakpointsPanel = new BreakpointsPanel(Debugger);
|
||||
callstackPanel = new CallstackPanel(Debugger);
|
||||
|
@ -66,6 +69,28 @@ namespace Xenia.Debug.UI {
|
|||
// new DeserializeDockContent(GetContentFromPersistString);
|
||||
|
||||
SetupDefaultLayout();
|
||||
|
||||
Debugger.StateChanged += Debugger_StateChanged;
|
||||
Debugger_StateChanged(this, Debugger.CurrentState);
|
||||
|
||||
Debugger.Attach();
|
||||
}
|
||||
|
||||
private void Debugger_StateChanged(object sender, Debugger.State e) {
|
||||
switch (e) {
|
||||
case Debugger.State.Idle:
|
||||
statusMessageLabel.Text = "Idle";
|
||||
break;
|
||||
case Debugger.State.Attaching:
|
||||
statusMessageLabel.Text = "Attaching";
|
||||
break;
|
||||
case Debugger.State.Attached:
|
||||
statusMessageLabel.Text = "Attached";
|
||||
break;
|
||||
case Debugger.State.Detached:
|
||||
statusMessageLabel.Text = "Detached";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupDefaultLayout() {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
@ -6,16 +7,26 @@ using System.Threading.Tasks;
|
|||
using Xenia.Debug.Utilities;
|
||||
|
||||
namespace Xenia.Debug {
|
||||
public class BreakpointList : Changeable {
|
||||
public class BreakpointList : Changeable, IReadOnlyCollection<Breakpoint> {
|
||||
private readonly Debugger debugger;
|
||||
private readonly List<Breakpoint> breakpoints = new List<Breakpoint>();
|
||||
|
||||
public void Add(Breakpoint breakpoint) {
|
||||
public BreakpointList(Debugger debugger) {
|
||||
this.debugger = debugger;
|
||||
}
|
||||
|
||||
public void Remove(Breakpoint breakpoint) {
|
||||
public int Count {
|
||||
get {
|
||||
return breakpoints.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
public IEnumerator<Breakpoint> GetEnumerator() {
|
||||
return breakpoints.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() {
|
||||
return breakpoints.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Xenia.Debug {
|
||||
public class DebugClient {
|
||||
}
|
||||
}
|
|
@ -1,15 +1,47 @@
|
|||
using System;
|
||||
using FlatBuffers;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using xe.debug.proto;
|
||||
using Xenia.Debug.Utilities;
|
||||
|
||||
namespace Xenia.Debug {
|
||||
|
||||
public class Debugger {
|
||||
public DebugClient DebugClient {
|
||||
get;
|
||||
private readonly static string kServerHostname = "";
|
||||
private readonly static int kServerPort = 19000;
|
||||
|
||||
public enum State {
|
||||
Idle,
|
||||
Attaching,
|
||||
Attached,
|
||||
Detached,
|
||||
}
|
||||
|
||||
private class PendingRequest {
|
||||
public uint requestId;
|
||||
public ByteBuffer byteBuffer;
|
||||
public TaskCompletionSource<Response> responseTask;
|
||||
}
|
||||
|
||||
private Socket socket;
|
||||
private readonly ConcurrentDictionary<uint, PendingRequest>
|
||||
pendingRequests = new ConcurrentDictionary<uint, PendingRequest>();
|
||||
private uint nextRequestId = 1;
|
||||
|
||||
public event EventHandler<State> StateChanged;
|
||||
|
||||
public State CurrentState {
|
||||
get; private set;
|
||||
}
|
||||
= State.Idle;
|
||||
|
||||
public BreakpointList BreakpointList {
|
||||
get;
|
||||
}
|
||||
|
@ -26,27 +58,186 @@ namespace Xenia.Debug {
|
|||
get;
|
||||
}
|
||||
|
||||
public Debugger() {
|
||||
this.DebugClient = new DebugClient();
|
||||
public Debugger(AsyncTaskRunner asyncTaskRunner) {
|
||||
Dispatch.AsyncTaskRunner = asyncTaskRunner;
|
||||
|
||||
this.BreakpointList = new BreakpointList();
|
||||
this.FunctionList = new FunctionList();
|
||||
this.Memory = new Memory();
|
||||
this.ModuleList = new ModuleList();
|
||||
this.ThreadList = new ThreadList();
|
||||
this.BreakpointList = new BreakpointList(this);
|
||||
this.FunctionList = new FunctionList(this);
|
||||
this.Memory = new Memory(this);
|
||||
this.ModuleList = new ModuleList(this);
|
||||
this.ThreadList = new ThreadList(this);
|
||||
}
|
||||
|
||||
public bool Open() {
|
||||
return true;
|
||||
public Task Attach() {
|
||||
return Attach(CancellationToken.None);
|
||||
}
|
||||
|
||||
public delegate void ChangedEventHandler(EventArgs e);
|
||||
public async Task Attach(CancellationToken cancellationToken) {
|
||||
System.Diagnostics.Debug.Assert(CurrentState == State.Idle);
|
||||
|
||||
public event ChangedEventHandler Changed;
|
||||
SocketPermission permission = new SocketPermission(
|
||||
NetworkAccess.Connect,
|
||||
TransportType.Tcp,
|
||||
kServerHostname,
|
||||
kServerPort
|
||||
);
|
||||
permission.Demand();
|
||||
|
||||
private void OnChanged(EventArgs e) {
|
||||
if (Changed != null) {
|
||||
Changed(e);
|
||||
IPAddress ipAddress = new IPAddress(new byte[] { 127, 0, 0, 1 });
|
||||
IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, kServerPort);
|
||||
|
||||
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
|
||||
ProtocolType.Tcp);
|
||||
socket.Blocking = false;
|
||||
socket.NoDelay = true;
|
||||
socket.ReceiveBufferSize = 1024 * 1024;
|
||||
socket.SendBufferSize = 1024 * 1024;
|
||||
socket.ReceiveTimeout = 0;
|
||||
socket.SendTimeout = 0;
|
||||
|
||||
OnStateChanged(State.Attaching);
|
||||
|
||||
while (true) {
|
||||
Task task = Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, ipEndPoint, null);
|
||||
try {
|
||||
await task.WithCancellation(cancellationToken);
|
||||
} catch (OperationCanceledException) {
|
||||
socket.Close();
|
||||
socket = null;
|
||||
OnStateChanged(State.Idle);
|
||||
return;
|
||||
} catch (SocketException e) {
|
||||
if (e.SocketErrorCode == SocketError.ConnectionRefused) {
|
||||
// Not found - emulator may still be starting.
|
||||
System.Diagnostics.Debug.WriteLine("Connection refused; trying again...");
|
||||
continue;
|
||||
}
|
||||
OnStateChanged(State.Idle);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Start recv pump.
|
||||
Dispatch.Issue(() => ReceivePump());
|
||||
|
||||
var fbb = BeginRequest();
|
||||
AttachRequest.StartAttachRequest(fbb);
|
||||
int requestDataOffset = AttachRequest.EndAttachRequest(fbb);
|
||||
var response = await CommitRequest(fbb, RequestData.AttachRequest, requestDataOffset);
|
||||
|
||||
OnStateChanged(State.Attached);
|
||||
}
|
||||
|
||||
public void Detach() {
|
||||
if (CurrentState == State.Idle || CurrentState == State.Detached) {
|
||||
return;
|
||||
}
|
||||
|
||||
socket.Close();
|
||||
socket = null;
|
||||
|
||||
OnStateChanged(State.Detached);
|
||||
}
|
||||
|
||||
private async void ReceivePump() {
|
||||
// Read length.
|
||||
var lengthBuffer = new byte[4];
|
||||
int receiveLength;
|
||||
try {
|
||||
receiveLength = await Task.Factory.FromAsync(
|
||||
(callback, state) => socket.BeginReceive(lengthBuffer, 0, lengthBuffer.Length,
|
||||
SocketFlags.None, callback, state),
|
||||
asyncResult => socket.EndReceive(asyncResult), null);
|
||||
} catch (SocketException) {
|
||||
System.Diagnostics.Debug.WriteLine("Socket read error; detaching");
|
||||
Detach();
|
||||
return;
|
||||
}
|
||||
if (receiveLength == 0 || receiveLength != 4) {
|
||||
// Failed?
|
||||
ReceivePump();
|
||||
return;
|
||||
}
|
||||
var length = BitConverter.ToInt32(lengthBuffer, 0);
|
||||
|
||||
// Read body.
|
||||
var bodyBuffer = new byte[length];
|
||||
receiveLength = await Task.Factory.FromAsync(
|
||||
(callback, state) => socket.BeginReceive(bodyBuffer, 0, bodyBuffer.Length, SocketFlags.None, callback, state),
|
||||
asyncResult => socket.EndReceive(asyncResult),
|
||||
null);
|
||||
if (receiveLength == 0 || receiveLength != bodyBuffer.Length) {
|
||||
// Failed?
|
||||
ReceivePump();
|
||||
return;
|
||||
}
|
||||
|
||||
// Emit message.
|
||||
OnMessageReceived(bodyBuffer);
|
||||
|
||||
// Continue pumping.
|
||||
Dispatch.Issue(() => ReceivePump());
|
||||
}
|
||||
|
||||
private void OnMessageReceived(byte[] buffer) {
|
||||
ByteBuffer byteBuffer = new ByteBuffer(buffer);
|
||||
var response = Response.GetRootAsResponse(byteBuffer);
|
||||
if (response.Id != 0) {
|
||||
// Response.
|
||||
PendingRequest pendingRequest;
|
||||
if (!pendingRequests.TryRemove(response.Id, out pendingRequest)) {
|
||||
System.Diagnostics.Debug.WriteLine("Unexpected message from debug server?");
|
||||
return;
|
||||
}
|
||||
pendingRequest.byteBuffer = byteBuffer;
|
||||
pendingRequest.responseTask.SetResult(response);
|
||||
} else {
|
||||
// Event.
|
||||
// TODO(benvanik): events.
|
||||
}
|
||||
}
|
||||
|
||||
public FlatBufferBuilder BeginRequest() {
|
||||
var fbb = new FlatBufferBuilder(32 * 1024);
|
||||
return fbb;
|
||||
}
|
||||
|
||||
public async Task<Response> CommitRequest(FlatBufferBuilder fbb,
|
||||
RequestData requestDataType,
|
||||
int requestDataOffset) {
|
||||
PendingRequest request = new PendingRequest();
|
||||
request.requestId = nextRequestId++;
|
||||
request.responseTask = new TaskCompletionSource<Response>();
|
||||
pendingRequests.TryAdd(request.requestId, request);
|
||||
|
||||
int requestOffset =
|
||||
Request.CreateRequest(fbb, request.requestId,
|
||||
requestDataType, requestDataOffset);
|
||||
fbb.Finish(requestOffset);
|
||||
|
||||
// Update the placeholder size.
|
||||
int bufferOffset = fbb.DataBuffer.Position;
|
||||
int bufferLength = fbb.DataBuffer.Length - fbb.DataBuffer.Position;
|
||||
fbb.DataBuffer.PutInt(bufferOffset - 4, bufferLength);
|
||||
|
||||
// Send request.
|
||||
await socket.SendTaskAsync(fbb.DataBuffer.Data, bufferOffset - 4,
|
||||
bufferLength + 4, SocketFlags.None);
|
||||
|
||||
// Await response.
|
||||
var response = await request.responseTask.Task;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private void OnStateChanged(State newState) {
|
||||
if (newState == CurrentState) {
|
||||
return;
|
||||
}
|
||||
CurrentState = newState;
|
||||
if (StateChanged != null) {
|
||||
StateChanged(this, newState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
@ -6,6 +7,26 @@ using System.Threading.Tasks;
|
|||
using Xenia.Debug.Utilities;
|
||||
|
||||
namespace Xenia.Debug {
|
||||
public class FunctionList : Changeable {
|
||||
public class FunctionList : Changeable, IReadOnlyCollection<Function> {
|
||||
private readonly Debugger debugger;
|
||||
private readonly List<Function> functions = new List<Function>();
|
||||
|
||||
public FunctionList(Debugger debugger) {
|
||||
this.debugger = debugger;
|
||||
}
|
||||
|
||||
public int Count {
|
||||
get {
|
||||
return functions.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<Function> GetEnumerator() {
|
||||
return functions.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() {
|
||||
return functions.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,12 @@ using Xenia.Debug.Utilities;
|
|||
|
||||
namespace Xenia.Debug {
|
||||
public class Memory : Changeable {
|
||||
private readonly Debugger debugger;
|
||||
|
||||
public Memory(Debugger debugger) {
|
||||
this.debugger = debugger;
|
||||
}
|
||||
|
||||
public MemoryView CreateView() {
|
||||
return new MemoryView(this);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
@ -6,6 +7,26 @@ using System.Threading.Tasks;
|
|||
using Xenia.Debug.Utilities;
|
||||
|
||||
namespace Xenia.Debug {
|
||||
public class ModuleList : Changeable {
|
||||
public class ModuleList : Changeable, IReadOnlyCollection<Module> {
|
||||
private readonly Debugger debugger;
|
||||
private readonly List<Module> modules = new List<Module>();
|
||||
|
||||
public ModuleList(Debugger debugger) {
|
||||
this.debugger = debugger;
|
||||
}
|
||||
|
||||
public int Count {
|
||||
get {
|
||||
return modules.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<Module> GetEnumerator() {
|
||||
return modules.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() {
|
||||
return modules.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
using FlatBuffers;
|
||||
|
||||
public sealed class AddBreakpointRequest : Table {
|
||||
public static AddBreakpointRequest GetRootAsAddBreakpointRequest(ByteBuffer _bb) { return GetRootAsAddBreakpointRequest(_bb, new AddBreakpointRequest()); }
|
||||
public static AddBreakpointRequest GetRootAsAddBreakpointRequest(ByteBuffer _bb, AddBreakpointRequest obj) { return (obj.__init(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
|
||||
public AddBreakpointRequest __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
|
||||
|
||||
|
||||
public static void StartAddBreakpointRequest(FlatBufferBuilder builder) { builder.StartObject(0); }
|
||||
public static int EndAddBreakpointRequest(FlatBufferBuilder builder) {
|
||||
int o = builder.EndObject();
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
using FlatBuffers;
|
||||
|
||||
public sealed class AddBreakpointResponse : Table {
|
||||
public static AddBreakpointResponse GetRootAsAddBreakpointResponse(ByteBuffer _bb) { return GetRootAsAddBreakpointResponse(_bb, new AddBreakpointResponse()); }
|
||||
public static AddBreakpointResponse GetRootAsAddBreakpointResponse(ByteBuffer _bb, AddBreakpointResponse obj) { return (obj.__init(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
|
||||
public AddBreakpointResponse __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
|
||||
|
||||
|
||||
public static void StartAddBreakpointResponse(FlatBufferBuilder builder) { builder.StartObject(0); }
|
||||
public static int EndAddBreakpointResponse(FlatBufferBuilder builder) {
|
||||
int o = builder.EndObject();
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
using FlatBuffers;
|
||||
|
||||
public sealed class AttachRequest : Table {
|
||||
public static AttachRequest GetRootAsAttachRequest(ByteBuffer _bb) { return GetRootAsAttachRequest(_bb, new AttachRequest()); }
|
||||
public static AttachRequest GetRootAsAttachRequest(ByteBuffer _bb, AttachRequest obj) { return (obj.__init(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
|
||||
public AttachRequest __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
|
||||
|
||||
|
||||
public static void StartAttachRequest(FlatBufferBuilder builder) { builder.StartObject(0); }
|
||||
public static int EndAttachRequest(FlatBufferBuilder builder) {
|
||||
int o = builder.EndObject();
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
using FlatBuffers;
|
||||
|
||||
public sealed class AttachResponse : Table {
|
||||
public static AttachResponse GetRootAsAttachResponse(ByteBuffer _bb) { return GetRootAsAttachResponse(_bb, new AttachResponse()); }
|
||||
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 static void StartAttachResponse(FlatBufferBuilder builder) { builder.StartObject(0); }
|
||||
public static int EndAttachResponse(FlatBufferBuilder builder) {
|
||||
int o = builder.EndObject();
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
public enum Foo : sbyte
|
||||
{
|
||||
A = 1,
|
||||
B = 2,
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
using FlatBuffers;
|
||||
|
||||
public sealed class ListBreakpointsRequest : Table {
|
||||
public static ListBreakpointsRequest GetRootAsListBreakpointsRequest(ByteBuffer _bb) { return GetRootAsListBreakpointsRequest(_bb, new ListBreakpointsRequest()); }
|
||||
public static ListBreakpointsRequest GetRootAsListBreakpointsRequest(ByteBuffer _bb, ListBreakpointsRequest obj) { return (obj.__init(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
|
||||
public ListBreakpointsRequest __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
|
||||
|
||||
|
||||
public static void StartListBreakpointsRequest(FlatBufferBuilder builder) { builder.StartObject(0); }
|
||||
public static int EndListBreakpointsRequest(FlatBufferBuilder builder) {
|
||||
int o = builder.EndObject();
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
using FlatBuffers;
|
||||
|
||||
public sealed class ListBreakpointsResponse : Table {
|
||||
public static ListBreakpointsResponse GetRootAsListBreakpointsResponse(ByteBuffer _bb) { return GetRootAsListBreakpointsResponse(_bb, new ListBreakpointsResponse()); }
|
||||
public static ListBreakpointsResponse GetRootAsListBreakpointsResponse(ByteBuffer _bb, ListBreakpointsResponse obj) { return (obj.__init(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
|
||||
public ListBreakpointsResponse __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
|
||||
|
||||
|
||||
public static void StartListBreakpointsResponse(FlatBufferBuilder builder) { builder.StartObject(0); }
|
||||
public static int EndListBreakpointsResponse(FlatBufferBuilder builder) {
|
||||
int o = builder.EndObject();
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
using FlatBuffers;
|
||||
|
||||
public sealed class RemoveBreakpointRequest : Table {
|
||||
public static RemoveBreakpointRequest GetRootAsRemoveBreakpointRequest(ByteBuffer _bb) { return GetRootAsRemoveBreakpointRequest(_bb, new RemoveBreakpointRequest()); }
|
||||
public static RemoveBreakpointRequest GetRootAsRemoveBreakpointRequest(ByteBuffer _bb, RemoveBreakpointRequest obj) { return (obj.__init(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
|
||||
public RemoveBreakpointRequest __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
|
||||
|
||||
|
||||
public static void StartRemoveBreakpointRequest(FlatBufferBuilder builder) { builder.StartObject(0); }
|
||||
public static int EndRemoveBreakpointRequest(FlatBufferBuilder builder) {
|
||||
int o = builder.EndObject();
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
using FlatBuffers;
|
||||
|
||||
public sealed class RemoveBreakpointResponse : Table {
|
||||
public static RemoveBreakpointResponse GetRootAsRemoveBreakpointResponse(ByteBuffer _bb) { return GetRootAsRemoveBreakpointResponse(_bb, new RemoveBreakpointResponse()); }
|
||||
public static RemoveBreakpointResponse GetRootAsRemoveBreakpointResponse(ByteBuffer _bb, RemoveBreakpointResponse obj) { return (obj.__init(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
|
||||
public RemoveBreakpointResponse __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
|
||||
|
||||
|
||||
public static void StartRemoveBreakpointResponse(FlatBufferBuilder builder) { builder.StartObject(0); }
|
||||
public static int EndRemoveBreakpointResponse(FlatBufferBuilder builder) {
|
||||
int o = builder.EndObject();
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
using FlatBuffers;
|
||||
|
||||
public sealed class Request : Table {
|
||||
public static Request GetRootAsRequest(ByteBuffer _bb) { return GetRootAsRequest(_bb, new Request()); }
|
||||
public static Request GetRootAsRequest(ByteBuffer _bb, Request obj) { return (obj.__init(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
|
||||
public Request __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
|
||||
|
||||
public uint Id { get { int o = __offset(4); return o != 0 ? bb.GetUint(o + bb_pos) : (uint)0; } }
|
||||
public RequestData RequestDataType { get { int o = __offset(6); return o != 0 ? (RequestData)bb.Get(o + bb_pos) : (RequestData)0; } }
|
||||
public Table GetRequestData(Table obj) { int o = __offset(8); return o != 0 ? __union(obj, o) : null; }
|
||||
|
||||
public static int CreateRequest(FlatBufferBuilder builder,
|
||||
uint id = 0,
|
||||
RequestData request_data_type = 0,
|
||||
int request_data = 0) {
|
||||
builder.StartObject(3);
|
||||
Request.AddRequestData(builder, request_data);
|
||||
Request.AddId(builder, id);
|
||||
Request.AddRequestDataType(builder, request_data_type);
|
||||
return Request.EndRequest(builder);
|
||||
}
|
||||
|
||||
public static void StartRequest(FlatBufferBuilder builder) { builder.StartObject(3); }
|
||||
public static void AddId(FlatBufferBuilder builder, uint id) { builder.AddUint(0, id, 0); }
|
||||
public static void AddRequestDataType(FlatBufferBuilder builder, RequestData requestDataType) { builder.AddByte(1, (byte)(requestDataType), 0); }
|
||||
public static void AddRequestData(FlatBufferBuilder builder, int requestDataOffset) { builder.AddOffset(2, requestDataOffset, 0); }
|
||||
public static int EndRequest(FlatBufferBuilder builder) {
|
||||
int o = builder.EndObject();
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
public enum RequestData : byte
|
||||
{
|
||||
NONE = 0,
|
||||
AttachRequest = 1,
|
||||
ListBreakpointsRequest = 2,
|
||||
AddBreakpointRequest = 3,
|
||||
UpdateBreakpointRequest = 4,
|
||||
RemoveBreakpointRequest = 5,
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
using FlatBuffers;
|
||||
|
||||
public sealed class Response : Table {
|
||||
public static Response GetRootAsResponse(ByteBuffer _bb) { return GetRootAsResponse(_bb, new Response()); }
|
||||
public static Response GetRootAsResponse(ByteBuffer _bb, Response obj) { return (obj.__init(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
|
||||
public Response __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
|
||||
|
||||
public uint Id { get { int o = __offset(4); return o != 0 ? bb.GetUint(o + bb_pos) : (uint)0; } }
|
||||
public ResponseData ResponseDataType { get { int o = __offset(6); return o != 0 ? (ResponseData)bb.Get(o + bb_pos) : (ResponseData)0; } }
|
||||
public Table GetResponseData(Table obj) { int o = __offset(8); return o != 0 ? __union(obj, o) : null; }
|
||||
|
||||
public static int CreateResponse(FlatBufferBuilder builder,
|
||||
uint id = 0,
|
||||
ResponseData response_data_type = 0,
|
||||
int response_data = 0) {
|
||||
builder.StartObject(3);
|
||||
Response.AddResponseData(builder, response_data);
|
||||
Response.AddId(builder, id);
|
||||
Response.AddResponseDataType(builder, response_data_type);
|
||||
return Response.EndResponse(builder);
|
||||
}
|
||||
|
||||
public static void StartResponse(FlatBufferBuilder builder) { builder.StartObject(3); }
|
||||
public static void AddId(FlatBufferBuilder builder, uint id) { builder.AddUint(0, id, 0); }
|
||||
public static void AddResponseDataType(FlatBufferBuilder builder, ResponseData responseDataType) { builder.AddByte(1, (byte)(responseDataType), 0); }
|
||||
public static void AddResponseData(FlatBufferBuilder builder, int responseDataOffset) { builder.AddOffset(2, responseDataOffset, 0); }
|
||||
public static int EndResponse(FlatBufferBuilder builder) {
|
||||
int o = builder.EndObject();
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
public enum ResponseData : byte
|
||||
{
|
||||
NONE = 0,
|
||||
AttachResponse = 1,
|
||||
ListBreakpointsResponse = 2,
|
||||
AddBreakpointResponse = 3,
|
||||
UpdateBreakpointResponse = 4,
|
||||
RemoveBreakpointResponse = 5,
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
using FlatBuffers;
|
||||
|
||||
public sealed class StructTest : Struct {
|
||||
public StructTest __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
|
||||
|
||||
public short A { get { return bb.GetShort(bb_pos + 0); } }
|
||||
public sbyte B { get { return bb.GetSbyte(bb_pos + 2); } }
|
||||
public Foo C { get { return (Foo)bb.GetSbyte(bb_pos + 3); } }
|
||||
|
||||
public static int CreateStructTest(FlatBufferBuilder builder, short A, sbyte B, Foo C) {
|
||||
builder.Prep(2, 4);
|
||||
builder.PutSbyte((sbyte)(C));
|
||||
builder.PutSbyte(B);
|
||||
builder.PutShort(A);
|
||||
return builder.Offset;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
using FlatBuffers;
|
||||
|
||||
public sealed class TableTest : Table {
|
||||
public static TableTest GetRootAsTableTest(ByteBuffer _bb) { return GetRootAsTableTest(_bb, new TableTest()); }
|
||||
public static TableTest GetRootAsTableTest(ByteBuffer _bb, TableTest obj) { return (obj.__init(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
|
||||
public TableTest __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
|
||||
|
||||
public StructTest St { get { return GetSt(new StructTest()); } }
|
||||
public StructTest GetSt(StructTest obj) { int o = __offset(4); return o != 0 ? obj.__init(o + bb_pos, bb) : null; }
|
||||
public byte GetIv(int j) { int o = __offset(6); return o != 0 ? bb.Get(__vector(o) + j * 1) : (byte)0; }
|
||||
public int IvLength { get { int o = __offset(6); return o != 0 ? __vector_len(o) : 0; } }
|
||||
public string Name { get { int o = __offset(8); return o != 0 ? __string(o + bb_pos) : null; } }
|
||||
public uint Id { get { int o = __offset(10); return o != 0 ? bb.GetUint(o + bb_pos) : (uint)0; } }
|
||||
|
||||
public static void StartTableTest(FlatBufferBuilder builder) { builder.StartObject(4); }
|
||||
public static void AddSt(FlatBufferBuilder builder, int stOffset) { builder.AddStruct(0, stOffset, 0); }
|
||||
public static void AddIv(FlatBufferBuilder builder, int ivOffset) { builder.AddOffset(1, ivOffset, 0); }
|
||||
public static int CreateIvVector(FlatBufferBuilder builder, byte[] data) { builder.StartVector(1, data.Length, 1); for (int i = data.Length - 1; i >= 0; i--) builder.AddByte(data[i]); return builder.EndVector(); }
|
||||
public static void StartIvVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(1, numElems, 1); }
|
||||
public static void AddName(FlatBufferBuilder builder, int nameOffset) { builder.AddOffset(2, nameOffset, 0); }
|
||||
public static void AddId(FlatBufferBuilder builder, uint id) { builder.AddUint(3, id, 0); }
|
||||
public static int EndTableTest(FlatBufferBuilder builder) {
|
||||
int o = builder.EndObject();
|
||||
builder.Required(o, 8); // name
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
using FlatBuffers;
|
||||
|
||||
public sealed class UpdateBreakpointRequest : Table {
|
||||
public static UpdateBreakpointRequest GetRootAsUpdateBreakpointRequest(ByteBuffer _bb) { return GetRootAsUpdateBreakpointRequest(_bb, new UpdateBreakpointRequest()); }
|
||||
public static UpdateBreakpointRequest GetRootAsUpdateBreakpointRequest(ByteBuffer _bb, UpdateBreakpointRequest obj) { return (obj.__init(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
|
||||
public UpdateBreakpointRequest __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
|
||||
|
||||
|
||||
public static void StartUpdateBreakpointRequest(FlatBufferBuilder builder) { builder.StartObject(0); }
|
||||
public static int EndUpdateBreakpointRequest(FlatBufferBuilder builder) {
|
||||
int o = builder.EndObject();
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// automatically generated, do not modify
|
||||
|
||||
namespace xe.debug.proto
|
||||
{
|
||||
|
||||
using FlatBuffers;
|
||||
|
||||
public sealed class UpdateBreakpointResponse : Table {
|
||||
public static UpdateBreakpointResponse GetRootAsUpdateBreakpointResponse(ByteBuffer _bb) { return GetRootAsUpdateBreakpointResponse(_bb, new UpdateBreakpointResponse()); }
|
||||
public static UpdateBreakpointResponse GetRootAsUpdateBreakpointResponse(ByteBuffer _bb, UpdateBreakpointResponse obj) { return (obj.__init(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
|
||||
public UpdateBreakpointResponse __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }
|
||||
|
||||
|
||||
public static void StartUpdateBreakpointResponse(FlatBufferBuilder builder) { builder.StartObject(0); }
|
||||
public static int EndUpdateBreakpointResponse(FlatBufferBuilder builder) {
|
||||
int o = builder.EndObject();
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
@ -6,6 +7,26 @@ using System.Threading.Tasks;
|
|||
using Xenia.Debug.Utilities;
|
||||
|
||||
namespace Xenia.Debug {
|
||||
public class ThreadList : Changeable {
|
||||
public class ThreadList : Changeable, IReadOnlyCollection<Thread> {
|
||||
private readonly Debugger debugger;
|
||||
private readonly List<Thread> threads = new List<Thread>();
|
||||
|
||||
public ThreadList(Debugger debugger) {
|
||||
this.debugger = debugger;
|
||||
}
|
||||
|
||||
public int Count {
|
||||
get {
|
||||
return threads.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<Thread> GetEnumerator() {
|
||||
return threads.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() {
|
||||
return threads.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Xenia.Debug.Utilities {
|
||||
public delegate void AsyncTask();
|
||||
public delegate void AsyncTaskRunner(AsyncTask task);
|
||||
|
||||
public static class Dispatch {
|
||||
public static AsyncTaskRunner AsyncTaskRunner {
|
||||
get; set;
|
||||
}
|
||||
|
||||
public static void Issue(AsyncTask task) {
|
||||
AsyncTaskRunner(task);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Xenia.Debug.Utilities {
|
||||
public static class TaskExtensions {
|
||||
public static async Task
|
||||
WithCancellation(this Task task, CancellationToken cancellationToken) {
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
using (cancellationToken.Register(
|
||||
s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs)) {
|
||||
if (task != await Task.WhenAny(task, tcs.Task))
|
||||
throw new OperationCanceledException(cancellationToken);
|
||||
}
|
||||
await task;
|
||||
}
|
||||
|
||||
public static async Task<T> WithCancellation<T>(
|
||||
this Task<T> task, CancellationToken cancellationToken) {
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
using (cancellationToken.Register(
|
||||
s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs)) {
|
||||
if (task != await Task.WhenAny(task, tcs.Task))
|
||||
throw new OperationCanceledException(cancellationToken);
|
||||
}
|
||||
return await task;
|
||||
}
|
||||
|
||||
public static Task<int> SendTaskAsync(this Socket socket, byte[] buffer,
|
||||
int offset, int size, SocketFlags flags) {
|
||||
IAsyncResult result =
|
||||
socket.BeginSend(buffer, offset, size, flags, null, null);
|
||||
return Task.Factory.FromAsync(result, socket.EndSend);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,10 +43,24 @@
|
|||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\third_party\flatbuffers\net\FlatBuffers\ByteBuffer.cs">
|
||||
<Link>Flatbuffers\ByteBuffer.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\third_party\flatbuffers\net\FlatBuffers\FlatBufferBuilder.cs">
|
||||
<Link>Flatbuffers\FlatBufferBuilder.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\third_party\flatbuffers\net\FlatBuffers\FlatBufferConstants.cs">
|
||||
<Link>Flatbuffers\FlatBufferConstants.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\third_party\flatbuffers\net\FlatBuffers\Struct.cs">
|
||||
<Link>Flatbuffers\Struct.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\third_party\flatbuffers\net\FlatBuffers\Table.cs">
|
||||
<Link>Flatbuffers\Table.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Breakpoint.cs" />
|
||||
<Compile Include="BreakpointList.cs" />
|
||||
<Compile Include="Callstack.cs" />
|
||||
<Compile Include="DebugClient.cs" />
|
||||
<Compile Include="Debugger.cs" />
|
||||
<Compile Include="Function.cs" />
|
||||
<Compile Include="FunctionList.cs" />
|
||||
|
@ -55,9 +69,26 @@
|
|||
<Compile Include="Module.cs" />
|
||||
<Compile Include="ModuleList.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Proto\xe\debug\proto\AddBreakpointRequest.cs" />
|
||||
<Compile Include="Proto\xe\debug\proto\AddBreakpointResponse.cs" />
|
||||
<Compile Include="Proto\xe\debug\proto\AttachRequest.cs" />
|
||||
<Compile Include="Proto\xe\debug\proto\AttachResponse.cs" />
|
||||
<Compile Include="Proto\xe\debug\proto\Foo.cs" />
|
||||
<Compile Include="Proto\xe\debug\proto\ListBreakpointsRequest.cs" />
|
||||
<Compile Include="Proto\xe\debug\proto\ListBreakpointsResponse.cs" />
|
||||
<Compile Include="Proto\xe\debug\proto\RemoveBreakpointRequest.cs" />
|
||||
<Compile Include="Proto\xe\debug\proto\RemoveBreakpointResponse.cs" />
|
||||
<Compile Include="Proto\xe\debug\proto\Request.cs" />
|
||||
<Compile Include="Proto\xe\debug\proto\RequestData.cs" />
|
||||
<Compile Include="Proto\xe\debug\proto\Response.cs" />
|
||||
<Compile Include="Proto\xe\debug\proto\ResponseData.cs" />
|
||||
<Compile Include="Proto\xe\debug\proto\StructTest.cs" />
|
||||
<Compile Include="Proto\xe\debug\proto\TableTest.cs" />
|
||||
<Compile Include="Thread.cs" />
|
||||
<Compile Include="ThreadList.cs" />
|
||||
<Compile Include="Utilities\Changeable.cs" />
|
||||
<Compile Include="Utilities\Dispatch.cs" />
|
||||
<Compile Include="Utilities\TaskExtensions.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
|
|
@ -175,7 +175,7 @@ class TestRunner {
|
|||
memory.reset(new Memory());
|
||||
memory->Initialize();
|
||||
|
||||
processor.reset(new Processor(memory.get(), nullptr));
|
||||
processor.reset(new Processor(memory.get(), nullptr, nullptr));
|
||||
processor->Setup();
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "xenia/cpu/module.h"
|
||||
#include "xenia/cpu/thread_state.h"
|
||||
#include "xenia/cpu/xex_module.h"
|
||||
#include "xenia/debug/debugger.h"
|
||||
#include "xenia/profiling.h"
|
||||
|
||||
// TODO(benvanik): based on compiler support
|
||||
|
@ -70,8 +69,10 @@ class BuiltinModule : public Module {
|
|||
std::string name_;
|
||||
};
|
||||
|
||||
Processor::Processor(xe::Memory* memory, ExportResolver* export_resolver)
|
||||
Processor::Processor(xe::Memory* memory, ExportResolver* export_resolver,
|
||||
debug::Debugger* debugger)
|
||||
: memory_(memory),
|
||||
debugger_(debugger),
|
||||
debug_info_flags_(0),
|
||||
builtin_module_(nullptr),
|
||||
next_builtin_address_(0xFFFF0000ul),
|
||||
|
@ -92,7 +93,6 @@ Processor::~Processor() {
|
|||
modules_.clear();
|
||||
}
|
||||
|
||||
debugger_.reset();
|
||||
frontend_.reset();
|
||||
backend_.reset();
|
||||
}
|
||||
|
@ -107,10 +107,6 @@ bool Processor::Setup() {
|
|||
// Must be initialized by subclass before calling into this.
|
||||
assert_not_null(memory_);
|
||||
|
||||
// Create debugger first. Other types hook up to it.
|
||||
debugger_.reset(new xe::debug::Debugger(this));
|
||||
debugger_->StartSession();
|
||||
|
||||
std::unique_ptr<Module> builtin_module(new BuiltinModule(this));
|
||||
builtin_module_ = builtin_module.get();
|
||||
modules_.push_back(std::move(builtin_module));
|
||||
|
@ -302,7 +298,9 @@ bool Processor::DemandFunction(FunctionInfo* symbol_info,
|
|||
symbol_info->set_function(function);
|
||||
|
||||
// Before we give the symbol back to the rest, let the debugger know.
|
||||
if (debugger_) {
|
||||
debugger_->OnFunctionDefined(symbol_info, function);
|
||||
}
|
||||
|
||||
symbol_info->set_status(SymbolInfo::STATUS_DEFINED);
|
||||
symbol_status = symbol_info->status();
|
||||
|
|
|
@ -20,14 +20,9 @@
|
|||
#include "xenia/cpu/function.h"
|
||||
#include "xenia/cpu/module.h"
|
||||
#include "xenia/cpu/thread_state.h"
|
||||
#include "xenia/debug/debugger.h"
|
||||
#include "xenia/memory.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
class Debugger;
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
@ -43,11 +38,12 @@ enum class Irql : uint32_t {
|
|||
|
||||
class Processor {
|
||||
public:
|
||||
Processor(Memory* memory, ExportResolver* export_resolver);
|
||||
Processor(Memory* memory, ExportResolver* export_resolver,
|
||||
debug::Debugger* debugger);
|
||||
~Processor();
|
||||
|
||||
Memory* memory() const { return memory_; }
|
||||
debug::Debugger* debugger() const { return debugger_.get(); }
|
||||
debug::Debugger* debugger() const { return debugger_; }
|
||||
frontend::PPCFrontend* frontend() const { return frontend_.get(); }
|
||||
backend::Backend* backend() const { return backend_.get(); }
|
||||
ExportResolver* export_resolver() const { return export_resolver_; }
|
||||
|
@ -85,11 +81,10 @@ class Processor {
|
|||
bool DemandFunction(FunctionInfo* symbol_info, Function** out_function);
|
||||
|
||||
Memory* memory_;
|
||||
debug::Debugger* debugger_;
|
||||
|
||||
uint32_t debug_info_flags_;
|
||||
|
||||
std::unique_ptr<debug::Debugger> debugger_;
|
||||
|
||||
std::unique_ptr<frontend::PPCFrontend> frontend_;
|
||||
std::unique_ptr<backend::Backend> backend_;
|
||||
ExportResolver* export_resolver_;
|
||||
|
|
|
@ -38,7 +38,8 @@ class TestFunction {
|
|||
|
||||
#if XENIA_TEST_X64
|
||||
{
|
||||
auto processor = std::make_unique<Processor>(memory.get(), nullptr);
|
||||
auto processor =
|
||||
std::make_unique<Processor>(memory.get(), nullptr, nullptr);
|
||||
processor->Setup();
|
||||
processors.emplace_back(std::move(processor));
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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/debug/debug_server.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
|
||||
//
|
||||
|
||||
} // namespace debug
|
||||
} // namespace xe
|
|
@ -1,21 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_DEBUG_DEBUG_SERVER_H_
|
||||
#define XENIA_DEBUG_DEBUG_SERVER_H_
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
|
||||
//
|
||||
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_DEBUG_DEBUG_SERVER_H_
|
|
@ -10,14 +10,28 @@
|
|||
#include "xenia/debug/debugger.h"
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <mstcpip.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/string.h"
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/cpu/function.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
#include "xenia/emulator.h"
|
||||
|
||||
// Autogenerated Flatbuffers files:
|
||||
#include "xenia/debug/proto/breakpoints_generated.h"
|
||||
#include "xenia/debug/proto/messages_generated.h"
|
||||
|
||||
DEFINE_string(debug_session_path, "", "Debug output path.");
|
||||
DEFINE_int32(debug_port, 19000, "Port the debugger listens on.");
|
||||
DEFINE_bool(wait_for_debugger, false,
|
||||
"Waits for the debugger to attach before starting the game.");
|
||||
DEFINE_bool(exit_with_debugger, true, "Exit whe the debugger disconnects.");
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
|
@ -29,9 +43,19 @@ Breakpoint::Breakpoint(Type type, uint32_t address)
|
|||
|
||||
Breakpoint::~Breakpoint() = default;
|
||||
|
||||
Debugger::Debugger(cpu::Processor* processor) : processor_(processor) {}
|
||||
Debugger::Debugger(Emulator* emulator)
|
||||
: emulator_(emulator),
|
||||
listen_socket_(INVALID_SOCKET),
|
||||
client_socket_(INVALID_SOCKET) {
|
||||
WSADATA wsa_data;
|
||||
WSAStartup(MAKEWORD(2, 2), &wsa_data);
|
||||
}
|
||||
|
||||
Debugger::~Debugger() = default;
|
||||
Debugger::~Debugger() {
|
||||
if (listen_socket_ != INVALID_SOCKET) {
|
||||
StopSession();
|
||||
}
|
||||
}
|
||||
|
||||
bool Debugger::StartSession() {
|
||||
std::wstring session_path = xe::to_wstring(FLAGS_debug_session_path);
|
||||
|
@ -44,11 +68,194 @@ bool Debugger::StartSession() {
|
|||
xe::join_paths(session_path, L"functions.trace");
|
||||
functions_trace_file_ = ChunkedMappedMemoryWriter::Open(
|
||||
functions_trace_path, 32 * 1024 * 1024, true);
|
||||
|
||||
listen_socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (listen_socket_ < 1) {
|
||||
XELOGE("Failed to create debugger socket");
|
||||
return false;
|
||||
}
|
||||
struct tcp_keepalive alive;
|
||||
alive.onoff = TRUE;
|
||||
alive.keepalivetime = 7200000;
|
||||
alive.keepaliveinterval = 6000;
|
||||
DWORD bytes_returned;
|
||||
WSAIoctl(listen_socket_, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), nullptr,
|
||||
0, &bytes_returned, nullptr, nullptr);
|
||||
int opt_value = 1;
|
||||
setsockopt(listen_socket_, SOL_SOCKET, SO_REUSEADDR,
|
||||
reinterpret_cast<const char*>(&opt_value), sizeof(opt_value));
|
||||
opt_value = 1;
|
||||
setsockopt(listen_socket_, IPPROTO_TCP, TCP_NODELAY,
|
||||
reinterpret_cast<const char*>(&opt_value), sizeof(opt_value));
|
||||
|
||||
sockaddr_in socket_addr = {0};
|
||||
socket_addr.sin_family = AF_INET;
|
||||
socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
socket_addr.sin_port = htons(FLAGS_debug_port);
|
||||
if (bind(listen_socket_, reinterpret_cast<sockaddr*>(&socket_addr),
|
||||
sizeof(socket_addr)) == SOCKET_ERROR) {
|
||||
int e = WSAGetLastError();
|
||||
XELOGE("Unable to bind debug socket");
|
||||
return false;
|
||||
}
|
||||
if (listen(listen_socket_, 5) == SOCKET_ERROR) {
|
||||
XELOGE("Unable to listen on debug socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SendResponse(SOCKET client_socket, flatbuffers::FlatBufferBuilder& fbb,
|
||||
uint32_t request_id, proto::ResponseData response_data_type,
|
||||
flatbuffers::Offset<void> response_data_offset) {
|
||||
auto response_offset = proto::CreateResponse(
|
||||
fbb, request_id, response_data_type, response_data_offset);
|
||||
fbb.Finish(response_offset);
|
||||
int buffer_length = fbb.GetSize();
|
||||
send(client_socket, reinterpret_cast<const char*>(&buffer_length), 4, 0);
|
||||
send(client_socket, reinterpret_cast<const char*>(fbb.GetBufferPointer()),
|
||||
fbb.GetSize(), 0);
|
||||
}
|
||||
|
||||
void Debugger::PreLaunch() {
|
||||
accept_thread_ = std::thread([this]() {
|
||||
while (listen_socket_ != INVALID_SOCKET) {
|
||||
sockaddr_in6 client_addr;
|
||||
int client_count = sizeof(client_addr);
|
||||
SOCKET client_socket_id =
|
||||
accept(listen_socket_, reinterpret_cast<sockaddr*>(&client_addr),
|
||||
&client_count);
|
||||
if (client_socket_id == INVALID_SOCKET) {
|
||||
XELOGE("Failed to accept socket");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only one debugger at a time.
|
||||
if (client_socket_ != INVALID_SOCKET) {
|
||||
XELOGW("Ignoring debugger connection as one is already connected");
|
||||
closesocket(client_socket_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Setup recv thread.
|
||||
client_socket_ = client_socket_id;
|
||||
receive_thread_ = std::thread([this]() {
|
||||
while (client_socket_ != INVALID_SOCKET) {
|
||||
// Read length prefix.
|
||||
uint32_t length = 0;
|
||||
int r = recv(client_socket_, reinterpret_cast<char*>(&length), 4,
|
||||
MSG_WAITALL);
|
||||
if (r != 4) {
|
||||
// Failed?
|
||||
XELOGE("Failed to recv debug data length - dead connection?");
|
||||
if (FLAGS_exit_with_debugger) {
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Read body.
|
||||
std::vector<uint8_t> body(length);
|
||||
r = recv(client_socket_, reinterpret_cast<char*>(body.data()), length,
|
||||
MSG_WAITALL);
|
||||
if (r != length) {
|
||||
// Failed?
|
||||
XELOGE("Failed to recv debug data body - dead connection?");
|
||||
if (FLAGS_exit_with_debugger) {
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Read message contents and dispatch.
|
||||
OnMessage(std::move(body));
|
||||
}
|
||||
});
|
||||
|
||||
// This will WaitForClient if it was waiting.
|
||||
}
|
||||
});
|
||||
|
||||
if (FLAGS_wait_for_debugger) {
|
||||
// Wait for the first client.
|
||||
XELOGI("Waiting for debugger because of --wait_for_debugger...");
|
||||
accept_fence_.Wait();
|
||||
XELOGI("Debugger attached, continuing...");
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::OnMessage(std::vector<uint8_t> buffer) {
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
proto::ResponseData response_data_type;
|
||||
flatbuffers::Offset<void> response_data_offset;
|
||||
|
||||
auto request = flatbuffers::GetRoot<proto::Request>(buffer.data());
|
||||
switch (request->request_data_type()) {
|
||||
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();
|
||||
|
||||
// Allow continuation if we were blocked waiting for a client.
|
||||
accept_fence_.Signal();
|
||||
} break;
|
||||
|
||||
case proto::RequestData_ListBreakpointsRequest: {
|
||||
response_data_type = proto::ResponseData_ListBreakpointsResponse;
|
||||
auto response_data = proto::ListBreakpointsResponseBuilder(fbb);
|
||||
//
|
||||
response_data_offset = response_data.Finish().Union();
|
||||
} break;
|
||||
case proto::RequestData_AddBreakpointRequest: {
|
||||
response_data_type = proto::ResponseData_RemoveBreakpointResponse;
|
||||
auto response_data = proto::AddBreakpointResponseBuilder(fbb);
|
||||
//
|
||||
response_data_offset = response_data.Finish().Union();
|
||||
} break;
|
||||
case proto::RequestData_UpdateBreakpointRequest: {
|
||||
response_data_type = proto::ResponseData_UpdateBreakpointResponse;
|
||||
auto response_data = proto::UpdateBreakpointResponseBuilder(fbb);
|
||||
//
|
||||
response_data_offset = response_data.Finish().Union();
|
||||
} break;
|
||||
case proto::RequestData_RemoveBreakpointRequest: {
|
||||
response_data_type = proto::ResponseData_AddBreakpointResponse;
|
||||
auto response_data = proto::RemoveBreakpointResponseBuilder(fbb);
|
||||
//
|
||||
response_data_offset = response_data.Finish().Union();
|
||||
} break;
|
||||
|
||||
default:
|
||||
assert_unhandled_case(request->request_data_type());
|
||||
break;
|
||||
}
|
||||
|
||||
SendResponse(client_socket_, std::move(fbb), request->id(),
|
||||
response_data_type, response_data_offset);
|
||||
}
|
||||
|
||||
void Debugger::StopSession() {
|
||||
FlushSession();
|
||||
|
||||
if (client_socket_ != INVALID_SOCKET) {
|
||||
shutdown(client_socket_, SD_SEND);
|
||||
closesocket(client_socket_);
|
||||
client_socket_ = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
linger so_linger;
|
||||
so_linger.l_onoff = TRUE;
|
||||
so_linger.l_linger = 30;
|
||||
setsockopt(listen_socket_, SOL_SOCKET, SO_LINGER,
|
||||
reinterpret_cast<const char*>(&so_linger), sizeof(so_linger));
|
||||
shutdown(listen_socket_, SD_SEND);
|
||||
closesocket(listen_socket_);
|
||||
listen_socket_ = INVALID_SOCKET;
|
||||
accept_thread_.join();
|
||||
|
||||
functions_file_.reset();
|
||||
functions_trace_file_.reset();
|
||||
}
|
||||
|
@ -132,7 +339,8 @@ int Debugger::AddBreakpoint(Breakpoint* breakpoint) {
|
|||
}
|
||||
|
||||
// Find all functions that contain the breakpoint address.
|
||||
auto fns = processor_->FindFunctionsWithAddress(breakpoint->address());
|
||||
auto fns =
|
||||
emulator_->processor()->FindFunctionsWithAddress(breakpoint->address());
|
||||
|
||||
// Add.
|
||||
for (auto fn : fns) {
|
||||
|
@ -166,7 +374,8 @@ int Debugger::RemoveBreakpoint(Breakpoint* breakpoint) {
|
|||
}
|
||||
|
||||
// Find all functions that have the breakpoint set.
|
||||
auto fns = processor_->FindFunctionsWithAddress(breakpoint->address());
|
||||
auto fns =
|
||||
emulator_->processor()->FindFunctionsWithAddress(breakpoint->address());
|
||||
|
||||
// Remove.
|
||||
for (auto fn : fns) {
|
||||
|
|
|
@ -14,13 +14,19 @@
|
|||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "xenia/base/delegate.h"
|
||||
#include "xenia/base/mapped_memory.h"
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/cpu/thread_state.h"
|
||||
#include "xenia/debug/breakpoint.h"
|
||||
|
||||
namespace xe {
|
||||
class Emulator;
|
||||
} // namespace xe
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
class Function;
|
||||
|
@ -62,12 +68,13 @@ class BreakpointHitEvent : public DebugEvent {
|
|||
|
||||
class Debugger {
|
||||
public:
|
||||
Debugger(cpu::Processor* processor);
|
||||
Debugger(Emulator* emulator);
|
||||
~Debugger();
|
||||
|
||||
cpu::Processor* processor() const { return processor_; }
|
||||
Emulator* emulator() const { return emulator_; }
|
||||
|
||||
bool StartSession();
|
||||
void PreLaunch();
|
||||
void StopSession();
|
||||
void FlushSession();
|
||||
|
||||
|
@ -99,7 +106,15 @@ class Debugger {
|
|||
Delegate<BreakpointHitEvent> breakpoint_hit;
|
||||
|
||||
private:
|
||||
cpu::Processor* processor_;
|
||||
void OnMessage(std::vector<uint8_t> buffer);
|
||||
|
||||
Emulator* emulator_;
|
||||
|
||||
UINT_PTR listen_socket_;
|
||||
std::thread accept_thread_;
|
||||
xe::threading::Fence accept_fence_;
|
||||
UINT_PTR client_socket_;
|
||||
std::thread receive_thread_;
|
||||
|
||||
std::unique_ptr<ChunkedMappedMemoryWriter> functions_file_;
|
||||
std::unique_ptr<ChunkedMappedMemoryWriter> functions_trace_file_;
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
include "common.fbs";
|
||||
|
||||
namespace xe.debug.proto;
|
||||
|
||||
table ListBreakpointsRequest {
|
||||
}
|
||||
table ListBreakpointsResponse {
|
||||
}
|
||||
|
||||
table AddBreakpointRequest {
|
||||
}
|
||||
table AddBreakpointResponse {
|
||||
}
|
||||
|
||||
table UpdateBreakpointRequest {
|
||||
}
|
||||
table UpdateBreakpointResponse {
|
||||
}
|
||||
|
||||
table RemoveBreakpointRequest {
|
||||
}
|
||||
table RemoveBreakpointResponse {
|
||||
}
|
|
@ -0,0 +1,210 @@
|
|||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
#ifndef FLATBUFFERS_GENERATED_BREAKPOINTS_XE_DEBUG_PROTO_H_
|
||||
#define FLATBUFFERS_GENERATED_BREAKPOINTS_XE_DEBUG_PROTO_H_
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace proto {
|
||||
|
||||
struct ListBreakpointsRequest;
|
||||
struct ListBreakpointsResponse;
|
||||
struct AddBreakpointRequest;
|
||||
struct AddBreakpointResponse;
|
||||
struct UpdateBreakpointRequest;
|
||||
struct UpdateBreakpointResponse;
|
||||
struct RemoveBreakpointRequest;
|
||||
struct RemoveBreakpointResponse;
|
||||
|
||||
struct ListBreakpointsRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct ListBreakpointsRequestBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
ListBreakpointsRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
ListBreakpointsRequestBuilder &operator=(const ListBreakpointsRequestBuilder &);
|
||||
flatbuffers::Offset<ListBreakpointsRequest> Finish() {
|
||||
auto o = flatbuffers::Offset<ListBreakpointsRequest>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<ListBreakpointsRequest> CreateListBreakpointsRequest(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
ListBreakpointsRequestBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct ListBreakpointsResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct ListBreakpointsResponseBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
ListBreakpointsResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
ListBreakpointsResponseBuilder &operator=(const ListBreakpointsResponseBuilder &);
|
||||
flatbuffers::Offset<ListBreakpointsResponse> Finish() {
|
||||
auto o = flatbuffers::Offset<ListBreakpointsResponse>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<ListBreakpointsResponse> CreateListBreakpointsResponse(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
ListBreakpointsResponseBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct AddBreakpointRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct AddBreakpointRequestBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
AddBreakpointRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
AddBreakpointRequestBuilder &operator=(const AddBreakpointRequestBuilder &);
|
||||
flatbuffers::Offset<AddBreakpointRequest> Finish() {
|
||||
auto o = flatbuffers::Offset<AddBreakpointRequest>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<AddBreakpointRequest> CreateAddBreakpointRequest(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
AddBreakpointRequestBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct AddBreakpointResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct AddBreakpointResponseBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
AddBreakpointResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
AddBreakpointResponseBuilder &operator=(const AddBreakpointResponseBuilder &);
|
||||
flatbuffers::Offset<AddBreakpointResponse> Finish() {
|
||||
auto o = flatbuffers::Offset<AddBreakpointResponse>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<AddBreakpointResponse> CreateAddBreakpointResponse(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
AddBreakpointResponseBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct UpdateBreakpointRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct UpdateBreakpointRequestBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
UpdateBreakpointRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
UpdateBreakpointRequestBuilder &operator=(const UpdateBreakpointRequestBuilder &);
|
||||
flatbuffers::Offset<UpdateBreakpointRequest> Finish() {
|
||||
auto o = flatbuffers::Offset<UpdateBreakpointRequest>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<UpdateBreakpointRequest> CreateUpdateBreakpointRequest(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
UpdateBreakpointRequestBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct UpdateBreakpointResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct UpdateBreakpointResponseBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
UpdateBreakpointResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
UpdateBreakpointResponseBuilder &operator=(const UpdateBreakpointResponseBuilder &);
|
||||
flatbuffers::Offset<UpdateBreakpointResponse> Finish() {
|
||||
auto o = flatbuffers::Offset<UpdateBreakpointResponse>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<UpdateBreakpointResponse> CreateUpdateBreakpointResponse(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
UpdateBreakpointResponseBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct RemoveBreakpointRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct RemoveBreakpointRequestBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
RemoveBreakpointRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
RemoveBreakpointRequestBuilder &operator=(const RemoveBreakpointRequestBuilder &);
|
||||
flatbuffers::Offset<RemoveBreakpointRequest> Finish() {
|
||||
auto o = flatbuffers::Offset<RemoveBreakpointRequest>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<RemoveBreakpointRequest> CreateRemoveBreakpointRequest(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
RemoveBreakpointRequestBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct RemoveBreakpointResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct RemoveBreakpointResponseBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
RemoveBreakpointResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
RemoveBreakpointResponseBuilder &operator=(const RemoveBreakpointResponseBuilder &);
|
||||
flatbuffers::Offset<RemoveBreakpointResponse> Finish() {
|
||||
auto o = flatbuffers::Offset<RemoveBreakpointResponse>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<RemoveBreakpointResponse> CreateRemoveBreakpointResponse(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
RemoveBreakpointResponseBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
} // namespace proto
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // FLATBUFFERS_GENERATED_BREAKPOINTS_XE_DEBUG_PROTO_H_
|
|
@ -0,0 +1 @@
|
|||
namespace xe.debug.proto;
|
|
@ -0,0 +1,3 @@
|
|||
include "common.fbs";
|
||||
|
||||
namespace xe.debug.proto;
|
|
@ -0,0 +1,59 @@
|
|||
include "breakpoints.fbs";
|
||||
include "common.fbs";
|
||||
include "control.fbs";
|
||||
|
||||
namespace xe.debug.proto;
|
||||
|
||||
enum Foo:byte (bit_flags) {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
struct StructTest {
|
||||
a:short;
|
||||
b:byte;
|
||||
c:Foo;
|
||||
}
|
||||
|
||||
table TableTest {
|
||||
st:StructTest;
|
||||
iv:[ubyte];
|
||||
name:string (required);
|
||||
id:uint (key);
|
||||
}
|
||||
|
||||
table AttachRequest {
|
||||
//
|
||||
}
|
||||
table AttachResponse {
|
||||
// file paths
|
||||
// mmap name
|
||||
}
|
||||
|
||||
union RequestData {
|
||||
AttachRequest,
|
||||
|
||||
ListBreakpointsRequest,
|
||||
AddBreakpointRequest,
|
||||
UpdateBreakpointRequest,
|
||||
RemoveBreakpointRequest,
|
||||
}
|
||||
|
||||
table Request {
|
||||
id:uint;
|
||||
request_data:RequestData;
|
||||
}
|
||||
|
||||
union ResponseData {
|
||||
AttachResponse,
|
||||
|
||||
ListBreakpointsResponse,
|
||||
AddBreakpointResponse,
|
||||
UpdateBreakpointResponse,
|
||||
RemoveBreakpointResponse,
|
||||
}
|
||||
|
||||
table Response {
|
||||
id:uint;
|
||||
response_data:ResponseData;
|
||||
}
|
|
@ -0,0 +1,298 @@
|
|||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
#ifndef FLATBUFFERS_GENERATED_MESSAGES_XE_DEBUG_PROTO_H_
|
||||
#define FLATBUFFERS_GENERATED_MESSAGES_XE_DEBUG_PROTO_H_
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace proto {
|
||||
struct ListBreakpointsRequest;
|
||||
struct ListBreakpointsResponse;
|
||||
struct AddBreakpointRequest;
|
||||
struct AddBreakpointResponse;
|
||||
struct UpdateBreakpointRequest;
|
||||
struct UpdateBreakpointResponse;
|
||||
struct RemoveBreakpointRequest;
|
||||
struct RemoveBreakpointResponse;
|
||||
} // namespace proto
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace proto {
|
||||
|
||||
struct StructTest;
|
||||
struct TableTest;
|
||||
struct AttachRequest;
|
||||
struct AttachResponse;
|
||||
struct Request;
|
||||
struct Response;
|
||||
|
||||
enum Foo {
|
||||
Foo_A = 1,
|
||||
Foo_B = 2
|
||||
};
|
||||
|
||||
inline const char **EnumNamesFoo() {
|
||||
static const char *names[] = { "A", "B", nullptr };
|
||||
return names;
|
||||
}
|
||||
|
||||
inline const char *EnumNameFoo(Foo e) { return EnumNamesFoo()[e - Foo_A]; }
|
||||
|
||||
enum RequestData {
|
||||
RequestData_NONE = 0,
|
||||
RequestData_AttachRequest = 1,
|
||||
RequestData_ListBreakpointsRequest = 2,
|
||||
RequestData_AddBreakpointRequest = 3,
|
||||
RequestData_UpdateBreakpointRequest = 4,
|
||||
RequestData_RemoveBreakpointRequest = 5
|
||||
};
|
||||
|
||||
inline const char **EnumNamesRequestData() {
|
||||
static const char *names[] = { "NONE", "AttachRequest", "ListBreakpointsRequest", "AddBreakpointRequest", "UpdateBreakpointRequest", "RemoveBreakpointRequest", nullptr };
|
||||
return names;
|
||||
}
|
||||
|
||||
inline const char *EnumNameRequestData(RequestData e) { return EnumNamesRequestData()[e]; }
|
||||
|
||||
inline bool VerifyRequestData(flatbuffers::Verifier &verifier, const void *union_obj, RequestData type);
|
||||
|
||||
enum ResponseData {
|
||||
ResponseData_NONE = 0,
|
||||
ResponseData_AttachResponse = 1,
|
||||
ResponseData_ListBreakpointsResponse = 2,
|
||||
ResponseData_AddBreakpointResponse = 3,
|
||||
ResponseData_UpdateBreakpointResponse = 4,
|
||||
ResponseData_RemoveBreakpointResponse = 5
|
||||
};
|
||||
|
||||
inline const char **EnumNamesResponseData() {
|
||||
static const char *names[] = { "NONE", "AttachResponse", "ListBreakpointsResponse", "AddBreakpointResponse", "UpdateBreakpointResponse", "RemoveBreakpointResponse", nullptr };
|
||||
return names;
|
||||
}
|
||||
|
||||
inline const char *EnumNameResponseData(ResponseData e) { return EnumNamesResponseData()[e]; }
|
||||
|
||||
inline bool VerifyResponseData(flatbuffers::Verifier &verifier, const void *union_obj, ResponseData type);
|
||||
|
||||
MANUALLY_ALIGNED_STRUCT(2) StructTest FLATBUFFERS_FINAL_CLASS {
|
||||
private:
|
||||
int16_t a_;
|
||||
int8_t b_;
|
||||
int8_t c_;
|
||||
|
||||
public:
|
||||
StructTest(int16_t a, int8_t b, Foo c)
|
||||
: a_(flatbuffers::EndianScalar(a)), b_(flatbuffers::EndianScalar(b)), c_(flatbuffers::EndianScalar(static_cast<int8_t>(c))) { }
|
||||
|
||||
int16_t a() const { return flatbuffers::EndianScalar(a_); }
|
||||
int8_t b() const { return flatbuffers::EndianScalar(b_); }
|
||||
Foo c() const { return static_cast<Foo>(flatbuffers::EndianScalar(c_)); }
|
||||
};
|
||||
STRUCT_END(StructTest, 4);
|
||||
|
||||
struct TableTest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
const StructTest *st() const { return GetStruct<const StructTest *>(4); }
|
||||
const flatbuffers::Vector<uint8_t> *iv() const { return GetPointer<const flatbuffers::Vector<uint8_t> *>(6); }
|
||||
const flatbuffers::String *name() const { return GetPointer<const flatbuffers::String *>(8); }
|
||||
uint32_t id() const { return GetField<uint32_t>(10, 0); }
|
||||
bool KeyCompareLessThan(const TableTest *o) const { return id() < o->id(); }
|
||||
int KeyCompareWithValue(uint32_t val) const { return id() < val ? -1 : id() > val; }
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyField<StructTest>(verifier, 4 /* st */) &&
|
||||
VerifyField<flatbuffers::uoffset_t>(verifier, 6 /* iv */) &&
|
||||
verifier.Verify(iv()) &&
|
||||
VerifyFieldRequired<flatbuffers::uoffset_t>(verifier, 8 /* name */) &&
|
||||
verifier.Verify(name()) &&
|
||||
VerifyField<uint32_t>(verifier, 10 /* id */) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct TableTestBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_st(const StructTest *st) { fbb_.AddStruct(4, st); }
|
||||
void add_iv(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> iv) { fbb_.AddOffset(6, iv); }
|
||||
void add_name(flatbuffers::Offset<flatbuffers::String> name) { fbb_.AddOffset(8, name); }
|
||||
void add_id(uint32_t id) { fbb_.AddElement<uint32_t>(10, id, 0); }
|
||||
TableTestBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
TableTestBuilder &operator=(const TableTestBuilder &);
|
||||
flatbuffers::Offset<TableTest> Finish() {
|
||||
auto o = flatbuffers::Offset<TableTest>(fbb_.EndTable(start_, 4));
|
||||
fbb_.Required(o, 8); // name
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<TableTest> CreateTableTest(flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const StructTest *st = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> iv = 0,
|
||||
flatbuffers::Offset<flatbuffers::String> name = 0,
|
||||
uint32_t id = 0) {
|
||||
TableTestBuilder builder_(_fbb);
|
||||
builder_.add_id(id);
|
||||
builder_.add_name(name);
|
||||
builder_.add_iv(iv);
|
||||
builder_.add_st(st);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct AttachRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct AttachRequestBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
AttachRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
AttachRequestBuilder &operator=(const AttachRequestBuilder &);
|
||||
flatbuffers::Offset<AttachRequest> Finish() {
|
||||
auto o = flatbuffers::Offset<AttachRequest>(fbb_.EndTable(start_, 0));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<AttachRequest> CreateAttachRequest(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
AttachRequestBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct AttachResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct AttachResponseBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
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));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<AttachResponse> CreateAttachResponse(flatbuffers::FlatBufferBuilder &_fbb) {
|
||||
AttachResponseBuilder builder_(_fbb);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct Request FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
uint32_t id() const { return GetField<uint32_t>(4, 0); }
|
||||
RequestData request_data_type() const { return static_cast<RequestData>(GetField<uint8_t>(6, 0)); }
|
||||
const void *request_data() const { return GetPointer<const void *>(8); }
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyField<uint32_t>(verifier, 4 /* id */) &&
|
||||
VerifyField<uint8_t>(verifier, 6 /* request_data_type */) &&
|
||||
VerifyField<flatbuffers::uoffset_t>(verifier, 8 /* request_data */) &&
|
||||
VerifyRequestData(verifier, request_data(), request_data_type()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct RequestBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_id(uint32_t id) { fbb_.AddElement<uint32_t>(4, id, 0); }
|
||||
void add_request_data_type(RequestData request_data_type) { fbb_.AddElement<uint8_t>(6, static_cast<uint8_t>(request_data_type), 0); }
|
||||
void add_request_data(flatbuffers::Offset<void> request_data) { fbb_.AddOffset(8, request_data); }
|
||||
RequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
RequestBuilder &operator=(const RequestBuilder &);
|
||||
flatbuffers::Offset<Request> Finish() {
|
||||
auto o = flatbuffers::Offset<Request>(fbb_.EndTable(start_, 3));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<Request> CreateRequest(flatbuffers::FlatBufferBuilder &_fbb,
|
||||
uint32_t id = 0,
|
||||
RequestData request_data_type = RequestData_NONE,
|
||||
flatbuffers::Offset<void> request_data = 0) {
|
||||
RequestBuilder builder_(_fbb);
|
||||
builder_.add_request_data(request_data);
|
||||
builder_.add_id(id);
|
||||
builder_.add_request_data_type(request_data_type);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct Response FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
uint32_t id() const { return GetField<uint32_t>(4, 0); }
|
||||
ResponseData response_data_type() const { return static_cast<ResponseData>(GetField<uint8_t>(6, 0)); }
|
||||
const void *response_data() const { return GetPointer<const void *>(8); }
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyField<uint32_t>(verifier, 4 /* id */) &&
|
||||
VerifyField<uint8_t>(verifier, 6 /* response_data_type */) &&
|
||||
VerifyField<flatbuffers::uoffset_t>(verifier, 8 /* response_data */) &&
|
||||
VerifyResponseData(verifier, response_data(), response_data_type()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct ResponseBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_id(uint32_t id) { fbb_.AddElement<uint32_t>(4, id, 0); }
|
||||
void add_response_data_type(ResponseData response_data_type) { fbb_.AddElement<uint8_t>(6, static_cast<uint8_t>(response_data_type), 0); }
|
||||
void add_response_data(flatbuffers::Offset<void> response_data) { fbb_.AddOffset(8, response_data); }
|
||||
ResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
|
||||
ResponseBuilder &operator=(const ResponseBuilder &);
|
||||
flatbuffers::Offset<Response> Finish() {
|
||||
auto o = flatbuffers::Offset<Response>(fbb_.EndTable(start_, 3));
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<Response> CreateResponse(flatbuffers::FlatBufferBuilder &_fbb,
|
||||
uint32_t id = 0,
|
||||
ResponseData response_data_type = ResponseData_NONE,
|
||||
flatbuffers::Offset<void> response_data = 0) {
|
||||
ResponseBuilder builder_(_fbb);
|
||||
builder_.add_response_data(response_data);
|
||||
builder_.add_id(id);
|
||||
builder_.add_response_data_type(response_data_type);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline bool VerifyRequestData(flatbuffers::Verifier &verifier, const void *union_obj, RequestData type) {
|
||||
switch (type) {
|
||||
case RequestData_NONE: return true;
|
||||
case RequestData_AttachRequest: return verifier.VerifyTable(reinterpret_cast<const AttachRequest *>(union_obj));
|
||||
case RequestData_ListBreakpointsRequest: return verifier.VerifyTable(reinterpret_cast<const xe::debug::proto::ListBreakpointsRequest *>(union_obj));
|
||||
case RequestData_AddBreakpointRequest: return verifier.VerifyTable(reinterpret_cast<const xe::debug::proto::AddBreakpointRequest *>(union_obj));
|
||||
case RequestData_UpdateBreakpointRequest: return verifier.VerifyTable(reinterpret_cast<const xe::debug::proto::UpdateBreakpointRequest *>(union_obj));
|
||||
case RequestData_RemoveBreakpointRequest: return verifier.VerifyTable(reinterpret_cast<const xe::debug::proto::RemoveBreakpointRequest *>(union_obj));
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool VerifyResponseData(flatbuffers::Verifier &verifier, const void *union_obj, ResponseData type) {
|
||||
switch (type) {
|
||||
case ResponseData_NONE: return true;
|
||||
case ResponseData_AttachResponse: return verifier.VerifyTable(reinterpret_cast<const AttachResponse *>(union_obj));
|
||||
case ResponseData_ListBreakpointsResponse: return verifier.VerifyTable(reinterpret_cast<const xe::debug::proto::ListBreakpointsResponse *>(union_obj));
|
||||
case ResponseData_AddBreakpointResponse: return verifier.VerifyTable(reinterpret_cast<const xe::debug::proto::AddBreakpointResponse *>(union_obj));
|
||||
case ResponseData_UpdateBreakpointResponse: return verifier.VerifyTable(reinterpret_cast<const xe::debug::proto::UpdateBreakpointResponse *>(union_obj));
|
||||
case ResponseData_RemoveBreakpointResponse: return verifier.VerifyTable(reinterpret_cast<const xe::debug::proto::RemoveBreakpointResponse *>(union_obj));
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace proto
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // FLATBUFFERS_GENERATED_MESSAGES_XE_DEBUG_PROTO_H_
|
|
@ -1,27 +0,0 @@
|
|||
namespace Xenia.Debugger.Proto;
|
||||
|
||||
attribute "priority"; // ?
|
||||
|
||||
enum Foo:byte (bit_flags) {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
struct StructTest {
|
||||
a:short;
|
||||
b:byte;
|
||||
c:Foo;
|
||||
}
|
||||
|
||||
table TableTest {
|
||||
st:StructTest;
|
||||
iv:[ubyte];
|
||||
name:string (required);
|
||||
id:uint (required, key);
|
||||
}
|
||||
|
||||
union UnionTest {
|
||||
TableTest,
|
||||
}
|
||||
|
||||
root_type UnionTest;
|
|
@ -37,6 +37,9 @@ Emulator::Emulator(const std::wstring& command_line)
|
|||
Emulator::~Emulator() {
|
||||
// Note that we delete things in the reverse order they were initialized.
|
||||
|
||||
// Kill the debugger first, so that we don't have it messing with things.
|
||||
debugger_.reset();
|
||||
|
||||
// Give the systems time to shutdown before we delete them.
|
||||
graphics_system_->Shutdown();
|
||||
audio_system_->Shutdown();
|
||||
|
@ -80,9 +83,15 @@ X_STATUS Emulator::Setup() {
|
|||
// Shared export resolver used to attach and query for HLE exports.
|
||||
export_resolver_ = std::make_unique<xe::cpu::ExportResolver>();
|
||||
|
||||
// Debugger first, as other parts hook into it.
|
||||
debugger_.reset(new debug::Debugger(this));
|
||||
|
||||
// Create debugger first. Other types hook up to it.
|
||||
debugger_->StartSession();
|
||||
|
||||
// Initialize the CPU.
|
||||
processor_ =
|
||||
std::make_unique<Processor>(memory_.get(), export_resolver_.get());
|
||||
processor_ = std::make_unique<Processor>(
|
||||
memory_.get(), export_resolver_.get(), debugger_.get());
|
||||
|
||||
// Initialize the APU.
|
||||
audio_system_ = std::move(xe::apu::Create(this));
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include "xenia/debug/debugger.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/memory.h"
|
||||
#include "xenia/ui/main_window.h"
|
||||
|
@ -50,6 +51,8 @@ class Emulator {
|
|||
|
||||
Memory* memory() const { return memory_.get(); }
|
||||
|
||||
debug::Debugger* debugger() const { return debugger_.get(); }
|
||||
|
||||
cpu::Processor* processor() const { return processor_.get(); }
|
||||
apu::AudioSystem* audio_system() const { return audio_system_.get(); }
|
||||
gpu::GraphicsSystem* graphics_system() const {
|
||||
|
@ -81,6 +84,8 @@ class Emulator {
|
|||
|
||||
std::unique_ptr<Memory> memory_;
|
||||
|
||||
std::unique_ptr<debug::Debugger> debugger_;
|
||||
|
||||
std::unique_ptr<cpu::Processor> processor_;
|
||||
std::unique_ptr<apu::AudioSystem> audio_system_;
|
||||
std::unique_ptr<gpu::GraphicsSystem> graphics_system_;
|
||||
|
|
|
@ -184,6 +184,9 @@ int XboxkrnlModule::LaunchModule(const char* path) {
|
|||
// Set as the main module, while running.
|
||||
kernel_state_->SetExecutableModule(module);
|
||||
|
||||
// Waits for a debugger client, if desired.
|
||||
emulator()->debugger()->PreLaunch();
|
||||
|
||||
// Launch the module.
|
||||
// NOTE: this won't return until the module exits.
|
||||
result_code = module->Launch(0);
|
||||
|
|
43
xb.bat
43
xb.bat
|
@ -195,12 +195,47 @@ REM ============================================================================
|
|||
REM xb proto
|
||||
REM ============================================================================
|
||||
:perform_proto
|
||||
SETLOCAL
|
||||
SETLOCAL EnableDelayedExpansion
|
||||
ECHO Generating proto files...
|
||||
|
||||
SET FLATC=build\bin\Debug\flatc.exe
|
||||
IF NOT EXIST %FLATC% (
|
||||
SET FLATC=build\bin\Release\flatc.exe
|
||||
IF NOT EXIST %FLATC% (
|
||||
ECHO.
|
||||
ECHO ^> running flatc...
|
||||
REM foo
|
||||
ECHO ERROR: flatc not built - build before running this
|
||||
ENDLOCAL & SET _RESULT=1
|
||||
GOTO :eof
|
||||
)
|
||||
)
|
||||
|
||||
SET FBS_SRCS=src\xenia\debug\proto\
|
||||
SET CC_OUT=src\xenia\debug\proto\
|
||||
SET CS_OUT=src\Xenia.Debug\Proto\
|
||||
|
||||
SET ANY_ERRORS=0
|
||||
PUSHD %FBS_SRCS%
|
||||
FOR %%G in (*.fbs) DO (
|
||||
ECHO ^> generating %%~nG...
|
||||
POPD
|
||||
SET SRC_FILE=%FBS_SRCS%\%%G
|
||||
CMD /c build\bin\Debug\flatc.exe -c -o %CC_OUT% !SRC_FILE! 2>&1
|
||||
IF !ERRORLEVEL! NEQ 0 (
|
||||
SET ANY_ERRORS=1
|
||||
)
|
||||
CMD /c build\bin\Debug\flatc.exe -n -o %CS_OUT% !SRC_FILE! 2>&1
|
||||
IF !ERRORLEVEL! NEQ 0 (
|
||||
SET ANY_ERRORS=1
|
||||
)
|
||||
PUSHD %TEST_SRC%
|
||||
)
|
||||
POPD
|
||||
IF %ANY_ERRORS% NEQ 0 (
|
||||
ECHO.
|
||||
ECHO ERROR: failed to build one or more tests
|
||||
ENDLOCAL & SET _RESULT=1
|
||||
GOTO :eof
|
||||
)
|
||||
|
||||
ENDLOCAL & SET _RESULT=0
|
||||
GOTO :eof
|
||||
|
@ -307,7 +342,7 @@ FOR %%G in (*.s) DO (
|
|||
IF !ERRORLEVEL! NEQ 0 (
|
||||
SET ANY_ERRORS=1
|
||||
)
|
||||
PUSHD %TEST_SRC%
|
||||
PUSHD %TEST_SRC_WIN%
|
||||
)
|
||||
POPD
|
||||
IF %ANY_ERRORS% NEQ 0 (
|
||||
|
|
Loading…
Reference in New Issue