Skip to content

Commit

Permalink
Rearrange debugging capabilities into a separate module.
Browse files Browse the repository at this point in the history
  • Loading branch information
vddCore committed May 18, 2024
1 parent f485d41 commit 797ee6c
Show file tree
Hide file tree
Showing 5 changed files with 273 additions and 202 deletions.
136 changes: 0 additions & 136 deletions VirtualMachine/Ceres.Runtime/Modules/CoreModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using Ceres.ExecutionEngine.TypeSystem;
using Ceres.Runtime.Extensions;
using EVIL.CommonTypes.TypeSystem;
using static EVIL.CommonTypes.TypeSystem.DynamicValueType;
using EvilArray = Ceres.ExecutionEngine.Collections.Array;

namespace Ceres.Runtime.Modules
Expand All @@ -14,141 +13,6 @@ public sealed class CoreModule : RuntimeModule
{
public override string FullyQualifiedName => "core";

[RuntimeModuleFunction("gc.mem.get_used_total")]
[EvilDocFunction(
"Attempts to retrieve the amount of managed memory currently thought to be allocated.",
Returns = "The amount of managed memory - in bytes - currently thought to be allocated.",
ReturnType = Number
)]
[EvilDocArgument(
"force_full_collection",
"`true` to indicate waiting for GC, `false` to return an immediate value.",
DynamicValueType.Boolean,
DefaultValue = "false"
)]
private static DynamicValue GcMemGetTotal(Fiber _, params DynamicValue[] args)
{
args.ExpectAtMost(1);
args.OptionalBooleanAt(0, false, out var forceFullCollection);

return GC.GetTotalMemory(forceFullCollection);
}

[RuntimeModuleFunction("gc.mem.get_info")]
[EvilDocFunction(
"Retrieves memory statistics of the garbage collector.",
Returns = "A table containing information about GC memory statistics.",
ReturnType = DynamicValueType.Table
)]
private static DynamicValue GcMemGetInfo(Fiber _, params DynamicValue[] args)
{
args.ExpectNone();

var info = GC.GetGCMemoryInfo();

return new Table
{
["index"] = info.Index,
["generation"] = info.Generation,
["is_compacted"] = info.Compacted,
["is_concurrent"] = info.Concurrent,
["finalization_pending_count"] = info.FinalizationPendingCount,
["fragmented_bytes"] = info.FragmentedBytes,
["promoted_bytes"] = info.PromotedBytes,
["heap_size_bytes"] = info.HeapSizeBytes,
["total_committed_bytes"] = info.TotalCommittedBytes,
["total_avail_mem_bytes"] = info.TotalAvailableMemoryBytes,
["pause_time_percent"] = info.PauseTimePercentage,
["pinned_object_count"] = info.PinnedObjectsCount
};
}

[RuntimeModuleFunction("gc.collect")]
[EvilDocFunction("Forces a compacting asynchronous aggressive garbage collection for all generations.")]
private static DynamicValue GcCollect(Fiber _, params DynamicValue[] args)
{
args.ExpectNone();
GC.Collect(
GC.MaxGeneration,
GCCollectionMode.Aggressive,
false,
true
);

return DynamicValue.Nil;
}

[RuntimeModuleFunction("strace")]
[EvilDocFunction(
"Gets the current stack trace in the form of raw data.",
Returns = "Array containing Table values with stack frame information available at the time of invocation.",
ReturnType = DynamicValueType.Array
)]
[EvilDocArgument(
"skip_native_frames",
"Set to `true` to skip CLR (native) frames, `false` to include them in the output.",
DynamicValueType.Boolean,
DefaultValue = "false"
)]
private static DynamicValue StackTrace(Fiber fiber, params DynamicValue[] args)
{
args.ExpectAtMost(1)
.OptionalBooleanAt(0, defaultValue: false, out var skipNativeFrames);

var callStack = fiber.CallStack.ToArray(skipNativeFrames);
var array = new EvilArray(callStack.Length);

for (var i = 0; i < callStack.Length; i++)
{
if (callStack[i] is ScriptStackFrame ssf)
{
array[i] = new Table
{
{ "is_script", true },
{ "fn_name", ssf.Chunk.Name ?? "<unknown>" },
{ "ip", ssf.IP },
{ "line", ssf.Chunk.HasDebugInfo ? ssf.Chunk.DebugDatabase.GetLineForIP((int)ssf.PreviousOpCodeIP) : DynamicValue.Nil },
{ "locals", ssf.Locals?.ToTable() ?? DynamicValue.Nil },
{ "args", ssf.Arguments.ToTable() },
{ "xargs", ssf.ExtraArguments.DeepCopy() },
{ "def_on_line", ssf.Chunk.DebugDatabase.DefinedOnLine > 0 ? ssf.Chunk.DebugDatabase.DefinedOnLine : DynamicValue.Nil },
{ "def_in_file", !string.IsNullOrEmpty(ssf.Chunk.DebugDatabase.DefinedInFile) ? ssf.Chunk.DebugDatabase.DefinedInFile : DynamicValue.Nil }
};
}
else if (callStack[i] is NativeStackFrame nsf && !skipNativeFrames)
{
array[i] = new Table
{
{ "is_script", false },
{ "fn_name", nsf.NativeFunction.Method.Name },
{ "decl_type", nsf.NativeFunction.Method.DeclaringType!.FullName! },
};
}
}

return array;
}

[RuntimeModuleFunction("strace_s")]
[EvilDocFunction(
"Gets the current stack trace as a formatted string.",
Returns = "Formatted string containing the stack trace available at the time of the invocation.",
ReturnType = DynamicValueType.Boolean
)]
[EvilDocArgument(
"skip_native_frames",
"Set to `true` to CLR (native) frames, `false` to include them in the output.",
DynamicValueType.Boolean,
DefaultValue = "false"
)]
private static DynamicValue StackTraceString(Fiber fiber, params DynamicValue[] args)
{
args.ExpectAtMost(1)
.OptionalBooleanAt(0, defaultValue: false, out var skipNativeFrames);

return fiber.StackTrace(skipNativeFrames);
}

[RuntimeModuleFunction("fail")]
[EvilDocFunction(
"Terminates VM execution with a user-requested runtime failure. This function does not return."
Expand Down
76 changes: 76 additions & 0 deletions VirtualMachine/Ceres.Runtime/Modules/DebugModule.GC.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using Ceres.ExecutionEngine.Collections;
using Ceres.ExecutionEngine.Concurrency;
using Ceres.ExecutionEngine.TypeSystem;
using Ceres.Runtime.Extensions;
using EVIL.CommonTypes.TypeSystem;

namespace Ceres.Runtime.Modules
{
public partial class DebugModule
{
[RuntimeModuleFunction("gc.mem.get_used_total")]
[EvilDocFunction(
"Attempts to retrieve the amount of managed memory currently thought to be allocated.",
Returns = "The amount of managed memory - in bytes - currently thought to be allocated.",
ReturnType = DynamicValueType.Number
)]
[EvilDocArgument(
"force_full_collection",
"`true` to indicate waiting for GC, `false` to return an immediate value.",
DynamicValueType.Boolean,
DefaultValue = "false"
)]
private static DynamicValue GcMemGetTotal(Fiber _, params DynamicValue[] args)
{
args.ExpectAtMost(1);
args.OptionalBooleanAt(0, false, out var forceFullCollection);

return GC.GetTotalMemory(forceFullCollection);
}

[RuntimeModuleFunction("gc.mem.get_info")]
[EvilDocFunction(
"Retrieves memory statistics of the garbage collector.",
Returns = "A table containing information about GC memory statistics.",
ReturnType = DynamicValueType.Table
)]
private static DynamicValue GcMemGetInfo(Fiber _, params DynamicValue[] args)
{
args.ExpectNone();

var info = GC.GetGCMemoryInfo();

return new Table
{
["index"] = info.Index,
["generation"] = info.Generation,
["is_compacted"] = info.Compacted,
["is_concurrent"] = info.Concurrent,
["finalization_pending_count"] = info.FinalizationPendingCount,
["fragmented_bytes"] = info.FragmentedBytes,
["promoted_bytes"] = info.PromotedBytes,
["heap_size_bytes"] = info.HeapSizeBytes,
["total_committed_bytes"] = info.TotalCommittedBytes,
["total_avail_mem_bytes"] = info.TotalAvailableMemoryBytes,
["pause_time_percent"] = info.PauseTimePercentage,
["pinned_object_count"] = info.PinnedObjectsCount
};
}

[RuntimeModuleFunction("gc.collect")]
[EvilDocFunction("Forces a compacting asynchronous aggressive garbage collection for all generations.")]
private static DynamicValue GcCollect(Fiber _, params DynamicValue[] args)
{
args.ExpectNone();
GC.Collect(
GC.MaxGeneration,
GCCollectionMode.Aggressive,
false,
true
);

return DynamicValue.Nil;
}
}
}
119 changes: 119 additions & 0 deletions VirtualMachine/Ceres.Runtime/Modules/DebugModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using Ceres.ExecutionEngine.Collections;
using Ceres.ExecutionEngine.Concurrency;
using Ceres.ExecutionEngine.Diagnostics;
using Ceres.ExecutionEngine.TypeSystem;
using Ceres.Runtime.Extensions;
using EVIL.CommonTypes.TypeSystem;
using Array = Ceres.ExecutionEngine.Collections.Array;

namespace Ceres.Runtime.Modules
{
public partial class DebugModule : RuntimeModule
{
public override string FullyQualifiedName => "debug";

[RuntimeModuleFunction("strace")]
[EvilDocFunction(
"Gets the current stack trace in the form of raw data.",
Returns = "Array containing Table values with stack frame information available at the time of invocation.",
ReturnType = DynamicValueType.Array
)]
[EvilDocArgument(
"skip_native_frames",
"Set to `true` to skip CLR (native) frames, `false` to include them in the output.",
DynamicValueType.Boolean,
DefaultValue = "false"
)]
private static DynamicValue StackTrace(Fiber fiber, params DynamicValue[] args)
{
args.ExpectAtMost(1)
.OptionalBooleanAt(0, defaultValue: false, out var skipNativeFrames);

var callStack = fiber.CallStack.ToArray(skipNativeFrames);
var array = new Array(callStack.Length);

for (var i = 0; i < callStack.Length; i++)
{
if (callStack[i] is ScriptStackFrame ssf)
{
array[i] = new Table
{
{ "is_script", true },
{ "fn_name", ssf.Chunk.Name ?? "<unknown>" },
{ "ip", ssf.IP },
{ "line", ssf.Chunk.HasDebugInfo ? ssf.Chunk.DebugDatabase.GetLineForIP((int)ssf.PreviousOpCodeIP) : DynamicValue.Nil },
{ "locals", ssf.Locals?.ToTable() ?? DynamicValue.Nil },
{ "args", ssf.Arguments.ToTable() },
{ "xargs", ssf.ExtraArguments.DeepCopy() },
{ "has_self", ssf.Chunk.IsSelfAware },
{ "def_on_line", ssf.Chunk.DebugDatabase.DefinedOnLine > 0 ? ssf.Chunk.DebugDatabase.DefinedOnLine : DynamicValue.Nil },
{ "def_in_file", !string.IsNullOrEmpty(ssf.Chunk.DebugDatabase.DefinedInFile) ? ssf.Chunk.DebugDatabase.DefinedInFile : DynamicValue.Nil }
};
}
else if (callStack[i] is NativeStackFrame nsf && !skipNativeFrames)
{
array[i] = new Table
{
{ "is_script", false },
{ "fn_name", nsf.NativeFunction.Method.Name },
{ "decl_type", nsf.NativeFunction.Method.DeclaringType!.FullName! },
};
}
}

return array;
}

[RuntimeModuleFunction("strace_s")]
[EvilDocFunction(
"Gets the current stack trace as a formatted string.",
Returns = "Formatted string containing the stack trace available at the time of the invocation.",
ReturnType = DynamicValueType.Boolean
)]
[EvilDocArgument(
"skip_native_frames",
"Set to `true` to CLR (native) frames, `false` to include them in the output.",
DynamicValueType.Boolean,
DefaultValue = "false"
)]
private static DynamicValue StackTraceString(Fiber fiber, params DynamicValue[] args)
{
args.ExpectAtMost(1)
.OptionalBooleanAt(0, defaultValue: false, out var skipNativeFrames);

return fiber.StackTrace(skipNativeFrames);
}

[RuntimeModuleFunction("assert")]
[EvilDocFunction(
"Throws if the provided condition does not evaluate to a `true`.",
ReturnType = DynamicValueType.Nil
)]
[EvilDocArgument(
"expr",
"Expression to be evaluated and checked for truth.",
DynamicValueType.Boolean,
CanBeNil = false
)]
[EvilDocArgument(
"fail_msg",
"Message to be set for the thrown Error if assertion fails.",
DynamicValueType.String,
CanBeNil = true,
DefaultValue = "Assertion failed."
)]
private static DynamicValue Assert(Fiber fiber, params DynamicValue[] args)
{
args.ExpectAtLeast(1)
.ExpectBooleanAt(0, out var expr)
.OptionalStringAt(1, "Assertion failed.", out var failMsg);

if (!expr)
{
return fiber.ThrowFromNative(new Error(failMsg));
}

return DynamicValue.Nil;
}
}
}
Loading

0 comments on commit 797ee6c

Please sign in to comment.