CosmosOS / IL2CPU

IL2CPU is a compiler for .NET IL code to compile to assembly language for direct booting.
BSD 3-Clause "New" or "Revised" License
279 stars 71 forks source link

Kernel panic with debug output of InterfaceMethodId not found. #213

Open Guillermo-Santos opened 8 months ago

Guillermo-Santos commented 8 months ago

I have a logging implementation that use two interfaces and implement all the methods, building is done correctly but at runtime i get a kernel panic indicating that an interface method is not found. Quite strange behavior.

Type
0x0000028D
TypeName: Zaphyros.Core.Logging.MultiTargetLogger, Zaphyros.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
InterfaceMethodId
0x0000033A
Not FOUND!

Implementation can be found on Zaphyros's logging feacture branch

Guillermo-Santos commented 8 months ago

Discovered that this issue happens when using an interface or abstract class (at least that is qhat it seens to be) when instantiating the logger with its type intead of the interface and called the Log method directly, it worked correctly.

Guillermo-Santos commented 8 months ago

This is just a crazy teory, but what if my Logger is parsed before the interface? That is the only way is see the methods id to be different. Or does IL2CPU checks if the interfaces and classes implemented were already parsed to use the same id for the methods?

quajak commented 8 months ago

Could you please make a simple reproducible code sample to make the issue apparent and debug-able?

Guillermo-Santos commented 8 months ago

Could you please make a simple reproducible code sample to make the issue apparent and debug-able?

Of course, this is the code sample: Using the Object as the interface:

var logger = (ILogger)new Logger("Kernel"); // Here a message is printed from the ctor, just to show that the method is really implemented.
// Fails on next line with method not found...
logger.Log(LogLevel.Information, 1, "", null, (string state, Exception? error) => "Cosmos booted successfully. Type a line of text to get it echoed back.");
logger.LogInformation("Hola Mundo");

Using Object Directly:

var logger = new Logger("Kernel"); // Here a message is printed from the ctor, just to show that the method is really implemented.
// This one Prints right
logger.Log(LogLevel.Information, 1, "", null, (string state, Exception? error) => "Cosmos booted successfully. Type a line of text to get it echoed back.");
// This extension Method of the interfaces fails.
logger.LogInformation("Hola Mundo");

Logger Class Code:

public sealed class Logger : ILogger
{
    private readonly string _categoryName;

    public Logger(string categoryName)
    {
        _categoryName = categoryName;
        Log(LogLevel.Debug, 0, "Creating new Logger", null, (string state, Exception? error) => state.ToString());
    }

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
    {
        if (!this.IsEnabled(logLevel))
        {
            return;
        }

        var foregroundColor = Console.ForegroundColor;
        Console.ForegroundColor = logLevel switch
        {
            LogLevel.Trace => ConsoleColor.DarkCyan,
            LogLevel.Debug => ConsoleColor.Cyan,
            LogLevel.Information => ConsoleColor.Green,
            LogLevel.Warning => ConsoleColor.Yellow,
            LogLevel.Error or LogLevel.Critical => ConsoleColor.Red,
            _ => foregroundColor,
        };
        Console.Write($"[{GetLogLevelString(logLevel)}]");
        Console.ForegroundColor = foregroundColor;
        Console.Write(": ");
        Console.ForegroundColor = ConsoleColor.Yellow;
        Console.WriteLine(_categoryName);
        Console.ForegroundColor = foregroundColor;
        Console.WriteLine($"\t{formatter(state, null)}");
    }

    public bool IsEnabled(LogLevel logLevel)
    {
        // In a real logger, you might implement more sophisticated logic based on log level.
        return true;
    }

    public IDisposable? BeginScope<TState>(TState state) where TState : notnull
    {
        return null;
    }
    private static string GetLogLevelString(LogLevel logLevel)
    {
        return logLevel switch
        {
            LogLevel.Debug => "DEBUG",
            LogLevel.Trace => "TRACE",
            LogLevel.Information => "INFO",
            LogLevel.Warning => "WARN",
            LogLevel.Error => "ERROR",
            LogLevel.Critical => "CRITICAL",
            LogLevel.None => string.Empty,
            _ => throw new ArgumentOutOfRangeException(nameof(logLevel)),
        };
    }
}

The problem really is on any use of the interface.

Guillermo-Santos commented 8 months ago

the ILogger interface and the extension method used are from Microsoft.Extensions.Logging nugget package

Guillermo-Santos commented 8 months ago

this is a crazy assumption, but could it be that it is detecting the method as private for the interface? as it does not have it explicitly on the code.