Skip to content

Commit

Permalink
improved debugability
Browse files Browse the repository at this point in the history
  • Loading branch information
JorisVanEijden committed Aug 1, 2023
1 parent a486e4c commit 0f84bdb
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 49 deletions.
2 changes: 1 addition & 1 deletion src/Spice86.Core/Emulator/CPU/Stack.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public string PeekWindow(int range = 8) {
if (i == PhysicalAddress) {
sb.Append('*');
}
sb.Append("[0x").AppendFormat("{0:X6}", i).Append("] 0x").AppendFormat("{0:X4}", memory.UInt16[i]).AppendLine();
sb.Append("[0x").AppendFormat("{0:X6}", i).Append("] 0x").AppendFormat("{0:X4}", _memory.UInt16[i]).AppendLine();
}
return sb.ToString();
}
Expand Down
53 changes: 45 additions & 8 deletions src/Spice86.Core/Emulator/Function/ExecutionFlowRecorder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using Spice86.Shared.Emulator.Memory;
using Spice86.Shared.Utils;

using System.Text;

/// <summary>
/// A class that records machine code execution flow.
/// </summary>
Expand All @@ -21,37 +23,43 @@ public class ExecutionFlowRecorder {
/// Gets or sets whether we register calls, jumps, returns, and unaligned returns.
/// </summary>
public bool RecordData { get; set; }

/// <summary>
/// Gets a dictionary of calls from one address to another.
/// </summary>
public IDictionary<uint, ISet<SegmentedAddress>> CallsFromTo { get; set; }

private readonly ISet<ulong> _callsEncountered = new HashSet<ulong>();

/// <summary>
/// Gets a dictionary of jumps from one address to another.
/// </summary>
public IDictionary<uint, ISet<SegmentedAddress>> JumpsFromTo { get; set; }

private readonly ISet<ulong> _jumpsEncountered = new HashSet<ulong>();

/// <summary>
/// Gets a dictionary of returns from one address to another.
/// </summary>
public IDictionary<uint, ISet<SegmentedAddress>> RetsFromTo { get; set; }

private readonly ISet<ulong> _retsEncountered = new HashSet<ulong>();

/// <summary>
/// Gets a dictionary of unaligned returns from one address to another.
/// </summary>
public IDictionary<uint, ISet<SegmentedAddress>> UnalignedRetsFromTo { get; set; }

private readonly ISet<ulong> _unalignedRetsEncountered = new HashSet<ulong>();

/// <summary>
/// Gets the set of executed instructions.
/// </summary>
public ISet<SegmentedAddress> ExecutedInstructions { get; set; }

private readonly ISet<uint> _instructionsEncountered = new HashSet<uint>();
private readonly ISet<uint> _executableCodeAreasEncountered = new HashSet<uint>();
private readonly CircularStringBuffer _callStack = new CircularStringBuffer(20);

/// <summary>
/// Gets or sets whether we register self modifying machine code.
Expand Down Expand Up @@ -106,6 +114,7 @@ internal void PreAllocatePossibleExecutionFlowBreakPoints(Machine machine) {
/// <param name="toIP">The offset of the address being called.</param>
public void RegisterCall(ushort fromCS, ushort fromIP, ushort toCS, ushort toIP) {
RegisterAddressJump(CallsFromTo, _callsEncountered, fromCS, fromIP, toCS, toIP);
_callStack.Add($"{fromCS:X4}:{fromIP:X4} -> {toCS:X4}:{toIP:X4}");
}

/// <summary>
Expand Down Expand Up @@ -140,7 +149,7 @@ public void RegisterReturn(ushort fromCS, ushort fromIP, ushort toCS, ushort toI
public void RegisterUnalignedReturn(ushort fromCS, ushort fromIP, ushort toCS, ushort toIP) {
RegisterAddressJump(UnalignedRetsFromTo, _unalignedRetsEncountered, fromCS, fromIP, toCS, toIP);
}

/// <summary>
/// Registers executed CPU instruction.
/// </summary>
Expand All @@ -150,7 +159,7 @@ public void RegisterExecutedInstruction(ushort cs, ushort ip) {
if (!AddSegmentedAddressInCache(_instructionsEncountered, cs, ip)) {
return;
}

ExecutedInstructions.Add(new SegmentedAddress(cs, ip));
}

Expand Down Expand Up @@ -199,7 +208,7 @@ public void RegisterExecutableByteModificationBreakPoint(Machine machine, uint p
if (!_addressBreakPoints.TryGetValue(physicalAddress, out breakPoint)) {
breakPoint = GenerateBreakPoint(machine, physicalAddress);
}

machine.MachineBreakpoints.ToggleBreakPoint(breakPoint, true);
}

Expand Down Expand Up @@ -254,4 +263,32 @@ private void RegisterAddressJump(IDictionary<uint, ISet<SegmentedAddress>> FromT
}
destinationAddresses.Add(new SegmentedAddress(toCS, toIP));
}

public string Dump() {
return _callStack.Dump();
}
}

public class CircularStringBuffer {
private readonly string[] _buffer;
private int _index;

public CircularStringBuffer(int capacity) {
_buffer = new string[capacity];
}

public void Add(string value) {
_buffer[_index] = value;
_index = (_index + 1) % _buffer.Length;
}

public string Dump() {
var sb = new StringBuilder();
int bufferLength = _buffer.Length;
for (int i = _index; i < _index + bufferLength; i++) {
int bufferIndex = i % bufferLength;
sb.AppendLine(_buffer[bufferIndex]);
}
return sb.ToString();
}
}
23 changes: 11 additions & 12 deletions src/Spice86.Core/Emulator/Gdb/GdbIo.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
namespace Spice86.Core.Emulator.Gdb;

using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Serilog.Events;

using Spice86.Shared.Interfaces;

using Spice86.Shared.Utils;

using System.Net;
using System.Net.Sockets;
using System.Text;

/// <summary>
/// Handles the I/O operations for a GDB (GNU Debugger) connection.
/// </summary>
Expand All @@ -27,9 +27,8 @@ public sealed class GdbIo : IDisposable {
/// <param name="port">The port number to listen on.</param>
/// <param name="loggerService">The logger service implementation.</param>
public GdbIo(int port, ILoggerService loggerService) {
_loggerService = loggerService;
IPHostEntry host = Dns.GetHostEntry("localhost");
IPAddress ip = new IPAddress(host.AddressList.First().GetAddressBytes());
_loggerService = loggerService.WithLogLevel(LogEventLevel.Debug);
IPAddress ip = IPAddress.Any;
_tcpListener = new TcpListener(ip, port);
}

Expand All @@ -39,7 +38,7 @@ public GdbIo(int port, ILoggerService loggerService) {
public void WaitForConnection() {
_tcpListener.Start();
_socket = _tcpListener.AcceptSocket();
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Information)) {
if (_loggerService.IsEnabled(LogEventLevel.Information)) {
int port = ((IPEndPoint)_tcpListener.LocalEndpoint).Port;
_loggerService.Information("GDB Server listening on port {Port}", port);
_loggerService.Information("Client connected: {@CanonicalHostName}", _socket.RemoteEndPoint);
Expand Down Expand Up @@ -123,7 +122,7 @@ public string ReadCommand() {
chr = _stream.ReadByte();
}
string payload = GetPayload(resBuilder);
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Debug)) {
if (_loggerService.IsEnabled(LogEventLevel.Debug)) {
_loggerService.Debug("Received command from GDB {GdbPayload}", payload);
}
return payload;
Expand All @@ -135,14 +134,14 @@ public string ReadCommand() {
/// <param name="data">The response data to send.</param>
public void SendResponse(string? data) {
if (!IsClientConnected) {
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Debug)) {
if (_loggerService.IsEnabled(LogEventLevel.Debug)) {
_loggerService.Debug("Cannot send response, client is not connected anymore");
}
// Happens when the emulator thread reaches a breakpoint but the client is gone
return;
}
if (data != null) {
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Verbose)) {
if (_loggerService.IsEnabled(LogEventLevel.Verbose)) {
_loggerService.Verbose("Sending response {ResponseData}", data);
}
_stream?.Write(Encoding.UTF8.GetBytes(data));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -531,8 +531,8 @@ public void MapUnmapMultipleHandlePages() {
ushort handleId = _state.DX;
ushort numberOfPages = _state.CX;
uint mapAddress = MemoryUtils.ToPhysicalAddress(_state.DS, _state.SI);
if (_loggerService.IsEnabled(LogEventLevel.Information)) {
_loggerService.Information(
if (_loggerService.IsEnabled(LogEventLevel.Verbose)) {
_loggerService.Verbose(
"EMS: {@MethodName} Map {@NumberOfPages} pages from handle {@Handle} according to the map at address 0x{@MapAddress:X6}",
nameof(MapUnmapMultipleHandlePages), numberOfPages, handleId, mapAddress);
}
Expand Down
41 changes: 20 additions & 21 deletions src/Spice86.Core/Emulator/OperatingSystem/DosFileManager.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@

namespace Spice86.Core.Emulator.OperatingSystem;

using Serilog;
using Serilog.Events;

using System.Linq;
using System.Diagnostics;

using Spice86.Core.Emulator.Memory;
using Spice86.Core.Emulator.OperatingSystem.Devices;
using Spice86.Core.Emulator.OperatingSystem.Enums;
Expand All @@ -15,6 +11,9 @@ namespace Spice86.Core.Emulator.OperatingSystem;
using Spice86.Shared.Interfaces;
using Spice86.Shared.Utils;

using System.Diagnostics;
using System.Linq;

/// <summary>
/// The class that implements DOS file operations, such as finding files, allocating file handles, and updating the Dos Transfer Area.
/// </summary>
Expand Down Expand Up @@ -76,7 +75,7 @@ public DosFileOperationResult CloseFile(ushort fileHandle) {
return FileNotOpenedError(fileHandle);
}

if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Debug)) {
if (_loggerService.IsEnabled(LogEventLevel.Debug)) {
_loggerService.Debug("Closed {ClosedFileName}, file was loaded in ram in those addresses: {ClosedFileAddresses}", file.Name, file.LoadedMemoryRanges);
}

Expand Down Expand Up @@ -175,7 +174,7 @@ public DosFileOperationResult FindFirstMatchingFile(string fileSpec) {
return FindNextMatchingFile();
} catch (IOException e) {
e.Demystify();
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Error)) {
if (_loggerService.IsEnabled(LogEventLevel.Error)) {
_loggerService.Error(e, "Error while walking path {CurrentMatchingFileSearchFolder} or getting attributes", _currentMatchingFileSearchFolder);
}
}
Expand All @@ -188,7 +187,7 @@ public DosFileOperationResult FindFirstMatchingFile(string fileSpec) {
/// <returns>A <see cref="DosFileOperationResult"/> with details about the result of the operation.</returns>
public DosFileOperationResult FindNextMatchingFile() {
if (_matchingFilesIterator == null) {
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Warning)) {
if (_loggerService.IsEnabled(LogEventLevel.Warning)) {
_loggerService.Warning("No search was done");
}
return FileNotFoundError(null);
Expand All @@ -204,7 +203,7 @@ public DosFileOperationResult FindNextMatchingFile() {
UpdateDosTransferAreaFromFile(_matchingFilesIterator.Current);
} catch (IOException e) {
e.Demystify();
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Warning)) {
if (_loggerService.IsEnabled(LogEventLevel.Warning)) {
_loggerService.Warning(e, "Error while getting attributes");
}
return FileNotFoundError(null);
Expand Down Expand Up @@ -241,7 +240,7 @@ public DosFileOperationResult MoveFilePointerUsingHandle(byte originOfMove, usho
return FileNotOpenedError(fileHandle);
}

if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Verbose)) {
if (_loggerService.IsEnabled(LogEventLevel.Verbose)) {
_loggerService.Verbose("Moving in file {FileMove}", file.Name);
}
Stream randomAccessFile = file.RandomAccessFile;
Expand All @@ -250,7 +249,7 @@ public DosFileOperationResult MoveFilePointerUsingHandle(byte originOfMove, usho
return DosFileOperationResult.Value32(newOffset);
} catch (IOException e) {
e.Demystify();
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Error)) {
if (_loggerService.IsEnabled(LogEventLevel.Error)) {
_loggerService.Error(e, "An error occurred while seeking file {Error}", e);
}
return DosFileOperationResult.Error(ErrorCode.InvalidHandle);
Expand All @@ -268,18 +267,18 @@ public DosFileOperationResult OpenFile(string fileName, byte rwAccessMode) {

CharacterDevice? device = _dosVirtualDevices.OfType<CharacterDevice>().FirstOrDefault(device => device.Name == fileName);
if (device is not null) {
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Verbose)) {
if (_loggerService.IsEnabled(LogEventLevel.Verbose)) {
_loggerService.Verbose("Opening device {FileName} with mode {OpenMode}", fileName, openMode);
}
return OpenDeviceInternal(device, openMode);
}

string? hostFileName = _dosPathResolver.GetFullHostPathFromDosOrDefault(fileName);
if (string.IsNullOrWhiteSpace(hostFileName)) {
return FileNotFoundError(fileName);
return FileNotFoundError($"'{fileName}'");
}

if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Debug)) {
if (_loggerService.IsEnabled(LogEventLevel.Debug)) {
_loggerService.Debug("Opening file {HostFileName} with mode {OpenMode}", hostFileName, openMode);
}

Expand Down Expand Up @@ -324,7 +323,7 @@ public DosFileOperationResult ReadFile(ushort fileHandle, ushort readLength, uin
return FileNotOpenedError(fileHandle);
}

if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Verbose)) {
if (_loggerService.IsEnabled(LogEventLevel.Verbose)) {
_loggerService.Verbose("Reading from file {FileName}", file.Name);
}

Expand Down Expand Up @@ -376,7 +375,7 @@ public void SetDiskTransferAreaAddress(ushort diskTransferAreaAddressSegment, us
/// <returns>A <see cref="DosFileOperationResult"/> object representing the result of the operation.</returns>
public DosFileOperationResult WriteFileUsingHandle(ushort fileHandle, ushort writeLength, uint bufferAddress) {
if (!IsValidFileHandle(fileHandle)) {
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Warning)) {
if (_loggerService.IsEnabled(LogEventLevel.Warning)) {
_loggerService.Warning("Invalid or unsupported file handle {FileHandle}. Doing nothing", fileHandle);
}

Expand Down Expand Up @@ -416,15 +415,15 @@ private DosFileOperationResult PathNotFoundError(string? path) {
}

private DosFileOperationResult FileOperationErrorWithLog(string message, ErrorCode errorCode) {
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Warning)) {
if (_loggerService.IsEnabled(LogEventLevel.Warning)) {
_loggerService.Warning("{FileNotFoundErrorWithLog}: {Message}", nameof(FileOperationErrorWithLog), message);
}

return DosFileOperationResult.Error(errorCode);
}

private DosFileOperationResult FileNotOpenedError(int fileHandle) {
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Warning)) {
if (_loggerService.IsEnabled(LogEventLevel.Warning)) {
_loggerService.Warning("File not opened: {FileHandle}", fileHandle);
}
return DosFileOperationResult.Error(ErrorCode.InvalidHandle);
Expand Down Expand Up @@ -452,7 +451,7 @@ private DosFileOperationResult FileNotOpenedError(int fileHandle) {
private static bool IsValidFileHandle(ushort fileHandle) => fileHandle <= MaxOpenFiles;

private DosFileOperationResult NoFreeHandleError() {
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Warning)) {
if (_loggerService.IsEnabled(LogEventLevel.Warning)) {
_loggerService.Warning("Could not find a free handle {MethodName}", nameof(NoFreeHandleError));
}
return DosFileOperationResult.Error(ErrorCode.TooManyOpenFiles);
Expand Down Expand Up @@ -543,7 +542,7 @@ private static uint Seek(Stream randomAccessFile, byte originOfMove, uint offset
private void SetOpenFile(ushort fileHandle, OpenFile? openFile) => OpenFiles[fileHandle] = openFile;

private void UpdateDosTransferAreaFromFile(string matchingFile) {
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Verbose)) {
if (_loggerService.IsEnabled(LogEventLevel.Verbose)) {
_loggerService.Verbose("Found matching file {MatchingFile}", matchingFile);
}
DosDiskTransferArea dosDiskTransferArea = new(_memory, GetDiskTransferAreaAddressPhysical());
Expand Down Expand Up @@ -581,7 +580,7 @@ public DosFileOperationResult CreateDirectory(string dosDirectory) {
}
return DosFileOperationResult.NoValue();
} catch (IOException e) {
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Warning)) {
if (_loggerService.IsEnabled(LogEventLevel.Warning)) {
_loggerService.Warning(e, "Error while creating directory {CaseInsensitivePath}: {Exception}",
prefixedDosDirectory, e);
}
Expand Down Expand Up @@ -610,7 +609,7 @@ public DosFileOperationResult RemoveDirectory(string dosDirectory) {

return DosFileOperationResult.NoValue();
} catch (IOException e) {
if (_loggerService.IsEnabled(Serilog.Events.LogEventLevel.Warning)) {
if (_loggerService.IsEnabled(LogEventLevel.Warning)) {
_loggerService.Warning(e, "Error while creating directory {CaseInsensitivePath}: {Exception}",
fullHostPath, e);
}
Expand Down
Loading

0 comments on commit 0f84bdb

Please sign in to comment.