Skip to content

Commit

Permalink
Switched to inner exceptions
Browse files Browse the repository at this point in the history
Fixed exceptions
Better analyzer namings
  • Loading branch information
Aragas committed Apr 12, 2024
1 parent f882dcf commit 8aae085
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BUTR.CrashReport.Models" Version="13.0.0.81" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
<PackageReference Include="Vogen" Version="3.0.25-beta.1" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using BUTR.CrashReport.Models;

namespace BUTR.Site.NexusMods.Server.Models;

using TType = ExceptionTypeId;
Expand All @@ -12,6 +14,15 @@ namespace BUTR.Site.NexusMods.Server.Models;
public static bool IsInitialized(TType instance) => instance._isInitialized;
public static TType DeserializeDangerous(TValueType instance) => Deserialize(instance);

public static TType FromException(ExceptionModel exception)
{
var exc = exception;
while (exc.InnerException is not null)
exc = exc.InnerException;

return From(exc.Type);
}

public static int GetHashCode(TType instance) => VogenDefaults<TType, TValueType>.GetHashCode(instance);

public static bool Equals(TType left, TType right) => VogenDefaults<TType, TValueType>.Equals(left, right);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
<PackageReference Include="AsmResolver.DotNet" Version="5.5.1" />
<PackageReference Include="Bannerlord.ModuleManager" Version="5.0.221" />
<PackageReference Include="BUTR.Authentication.NexusMods" Version="1.0.0.6" />
<PackageReference Include="BUTR.CrashReport.Models" Version="13.0.0.74" />
<PackageReference Include="BUTR.CrashReport.Bannerlord.Parser" Version="13.0.0.74" />
<PackageReference Include="BUTR.CrashReport.Models" Version="13.0.0.81" />
<PackageReference Include="BUTR.CrashReport.Bannerlord.Parser" Version="13.0.0.81" />
<PackageReference Include="Community.Microsoft.Extensions.Caching.PostgreSql" Version="4.0.4" />
<PackageReference Include="CsvHelper" Version="31.0.3" />
<PackageReference Include="ICSharpCode.Decompiler" Version="9.0.0.7618-preview1" />
Expand Down
7 changes: 0 additions & 7 deletions src/BUTR.Site.NexusMods.Server/Contexts/EntityFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,6 @@ public ExceptionTypeEntity GetOrCreateExceptionType(ExceptionTypeId exception)

static ExceptionTypeEntity ValueFactory(ExceptionTypeId id, TenantId tenant) => ExceptionTypeEntity.Create(tenant, id);
}
public ExceptionTypeEntity GetOrCreateExceptionTypeFromException(string exception)
{
var tenant = _tenantContextAccessor.Current;
return _exceptionTypes.GetOrAdd(ExceptionTypeEntity.FromException(tenant, exception).ExceptionTypeId, ValueFactory, tenant);

static ExceptionTypeEntity ValueFactory(ExceptionTypeId id, TenantId tenant) => ExceptionTypeEntity.Create(tenant, id);
}

public async Task<bool> SaveCreatedAsync(CancellationToken ct)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using BUTR.Site.NexusMods.Server.Models.Database;
using BUTR.Site.NexusMods.Server.Utils;
using BUTR.Site.NexusMods.Server.Utils.BindingSources;
using BUTR.Site.NexusMods.Shared.Helpers;

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -67,7 +66,7 @@ public CrashReportsAnalyzerController(ILogger<CrashReportsAnalyzerController> lo

private async IAsyncEnumerable<ModuleSuggestedFix> GetModuleSuggestionsAsync(CrashReportModel crashReport, [EnumeratorCancellation] CancellationToken ct)
{
static bool GetTypeLoadExceptionLevel(ExceptionModel? exceptionModel, ref int level, CancellationToken ct)
static bool GetABIException(ref ExceptionModel? exceptionModel, CancellationToken ct)
{
if (ct.IsCancellationRequested)
return false;
Expand All @@ -78,28 +77,30 @@ static bool GetTypeLoadExceptionLevel(ExceptionModel? exceptionModel, ref int le
if (exceptionModel.Type == "System.TypeLoadException")
return true;

level++;
return GetTypeLoadExceptionLevel(exceptionModel.InnerException, ref level, ct);
if (exceptionModel.Type == "System.MissingFieldException")
return true;

if (exceptionModel.Type == "System.MissingMemberException")
return true;

if (exceptionModel.Type == "System.MissingMethodException")
return true;

exceptionModel = exceptionModel.InnerException;
return GetABIException(ref exceptionModel, ct);
}

await Task.Yield();

//
var level = 1;
if (GetTypeLoadExceptionLevel(crashReport.Exception, ref level, ct))
var exception = crashReport.Exception;
if (GetABIException(ref exception, ct) && exception?.SourceModuleId is { } moduleId)
{
var involvedModule = crashReport.InvolvedModules[level];
yield return new ModuleSuggestedFix
{
ModuleId = involvedModule.ModuleOrLoaderPluginId,
ModuleId = moduleId,
Type = ModuleSuggestedFixType.Update,
};
}
//

//var callStackLines = ex.CallStack.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);
//var firstCallStackLine = callStackLines[0].Trim();
//var stacktrace = crashReport.EnhancedStacktrace.FirstOrDefault(x => firstCallStackLine == $"at {x.Name}");
}

private async IAsyncEnumerable<ModuleUpdate> GetModuleUpdatesForBannerlordAsync(CrashReportModel crashReport, [EnumeratorCancellation] CancellationToken ct)
Expand All @@ -108,27 +109,28 @@ private async IAsyncEnumerable<ModuleUpdate> GetModuleUpdatesForBannerlordAsync(

if (!ApplicationVersion.TryParse(crashReport.Metadata.GameVersion, out var gVersion)) yield break;

var moduleIds = crashReport.Modules.Where(x => !x.IsOfficial && string.IsNullOrEmpty(x.Url) && x.UpdateInfo is null)
var currentModuleIdsWithoutAnyData = crashReport.Modules.Where(x => !x.IsOfficial && string.IsNullOrEmpty(x.Url) && x.UpdateInfo is null)
.Select(x => ModuleId.From(x.Id)).ToArray();
var nexusModsIds = crashReport.Modules.Where(x => !x.IsOfficial && !string.IsNullOrEmpty(x.Url))
var currentMexusModsUpdateInfos = crashReport.Modules.Where(x => !x.IsOfficial && x.UpdateInfo is { Value: "NexusMods" })
.Select(x => NexusModsModId.TryParse(x.UpdateInfo!.Value, out var modId) ? modId : NexusModsModId.None)
.Where(x => x != NexusModsModId.None).ToArray();
var currentNexusModsIds = crashReport.Modules.Where(x => !x.IsOfficial && !string.IsNullOrEmpty(x.Url))
.Select(x => NexusModsModId.TryParseUrl(x.Url, out var modId) ? modId : NexusModsModId.None)
.Where(x => x != NexusModsModId.None).ToArray();
var nexusModsIds2 = crashReport.Modules.Where(x => !x.IsOfficial && !string.IsNullOrEmpty(x.Url))
.Select(x => new { ModuelId = ModuleId.From(x.Id), NexusModsId = NexusModsModId.From(NexusModsUtils.TryParse(x.Url, out _, out var id) ? (int) id : -1) })
.Where(x => x.NexusModsId != NexusModsModId.None).ToArray();
var updateInfos = crashReport.Modules.Where(x => !x.IsOfficial && x.UpdateInfo is not null)
.Select(x => new { ModuleId = ModuleId.From(x.Id), x.UpdateInfo }).ToArray();
var moduleIdVersions = crashReport.Modules

var currentModules = crashReport.Modules
.Where(x => !x.IsOfficial).Select(x => new { ModuleId = ModuleId.From(x.Id), Version = ModuleVersion.From(x.Version) }).ToArray();

// SMAPI uses different update providers - Chucklefish, NexusMods, GitHub
// We curectly will only use NexusMods
var moduleIdEntries = _dbContextRead.NexusModsModToModuleInfoHistory.Where(x => nexusModsIds.Contains(x.NexusModsMod.NexusModsModId));
var nexusModsIdEntries = _dbContextRead.NexusModsModToModuleInfoHistory.Where(x => moduleIds.Contains(x.Module.ModuleId));
//var updateInfoEntries = _dbContextRead.NexusModsModToModuleInfoHistory.Where(x => moduleIds.Contains(x.Module.ModuleId));
//var entries = _dbContextRead.NexusModsModToModuleInfoHistory.Where(x => moduleIds.Contains(x.Module.ModuleId));
var entries = moduleIdEntries.Concat(nexusModsIdEntries);
var compatibleWithGameVersion = entries.AsEnumerable().Select(x => new
var historicEntriesBasedOnModuleId = _dbContextRead.NexusModsModToModuleInfoHistory.Where(x => currentModuleIdsWithoutAnyData.Contains(x.Module.ModuleId));
var historicEntriesBasedOnNexusModsId = _dbContextRead.NexusModsModToModuleInfoHistory.Where(x => currentNexusModsIds.Contains(x.NexusModsMod.NexusModsModId));
var historicEntriesBasedOnUpdateInfo = _dbContextRead.NexusModsModToModuleInfoHistory.Where(x => currentMexusModsUpdateInfos.Contains(x.NexusModsMod.NexusModsModId));
var allHistoricEntries = historicEntriesBasedOnModuleId.Concat(historicEntriesBasedOnNexusModsId).Concat(historicEntriesBasedOnUpdateInfo);

var historicEntriesCompatibleWithGameVersion = allHistoricEntries.AsEnumerable().Select(x => new
{
ModuleId = x.Module.ModuleId,
ModuleVersion = ApplicationVersion.TryParse(x.ModuleVersion.Value, out var v) ? v : ApplicationVersion.Empty,
Expand All @@ -147,11 +149,15 @@ private async IAsyncEnumerable<ModuleUpdate> GetModuleUpdatesForBannerlordAsync(
if (y.Version == ApplicationVersion.Empty && y.VersionRange == ApplicationVersionRange.Empty) return false;
return (y.Version != ApplicationVersion.Empty && y.Version <= gVersion) ||
(y.VersionRange != ApplicationVersionRange.Empty && y.VersionRange.Min <= gVersion && y.VersionRange.Max > gVersion);
})).GroupBy(x => x.ModuleId).Select(x => x.MaxBy(y => y.ModuleVersion, new ApplicationVersionComparer())!).ToArray();
})).ToArray();
var latestModulesCompatibleWithGameVersions = historicEntriesCompatibleWithGameVersion
.GroupBy(x => x.ModuleId)
.Select(x => x.MaxBy(y => y.ModuleVersion, new ApplicationVersionComparer())!)
.ToArray();

var latestUpdates = compatibleWithGameVersion.Where(x =>
var latestUpdates = latestModulesCompatibleWithGameVersions.Where(x =>
{
var tuple = moduleIdVersions.FirstOrDefault(y => y.ModuleId == x.ModuleId);
var tuple = currentModules.FirstOrDefault(y => y.ModuleId == x.ModuleId);
if (tuple is null)
return false;

Expand All @@ -160,13 +166,13 @@ private async IAsyncEnumerable<ModuleUpdate> GetModuleUpdatesForBannerlordAsync(
return false;
}).ToArray();

foreach (var x in latestUpdates)
foreach (var update in latestUpdates)
{
yield return new ModuleUpdate
{
ModuleId = x.ModuleId.Value,
ModuleVersion = x.ModuleVersion.ToString(),
IsModuleInvolved = crashReport.InvolvedModules.Any(y => y.ModuleOrLoaderPluginId == x.ModuleId)
ModuleId = update.ModuleId.Value,
ModuleVersion = update.ModuleVersion.ToString(),
IsModuleInvolved = crashReport.InvolvedModules.Any(y => y.ModuleOrLoaderPluginId == update.ModuleId)
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,4 @@ private ExceptionTypeEntity() { }
private ExceptionTypeEntity(TenantId tenant, ExceptionTypeId exceptionTypeId) : this() => (TenantId, ExceptionTypeId) = (tenant, exceptionTypeId);

public static ExceptionTypeEntity Create(TenantId tenant, ExceptionTypeId exceptionTypeId) => new(tenant, exceptionTypeId);
public static ExceptionTypeEntity FromException(TenantId tenant, string exception)
{
var exceptionType = "NO_EXCEPTION";
Span<Range> dest = stackalloc Range[32];
var idx = 0;
foreach (ReadOnlySpan<char> line in exception.SplitLines())
{
if (idx < 3)
{
idx++;
continue;
}

var count = line.Split(dest, ':');
if (count < 2) break;
exceptionType = line[dest[1]].Trim().ToString();
break;
}
return new() { TenantId = tenant, ExceptionTypeId = ExceptionTypeId.From(exceptionType) };
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ private async Task<int> WriteCrashReportsToDatabaseAsync(CancellationToken ct)
Url = CrashReportUrl.From(new Uri(new Uri(_options.Endpoint), fileId.ToString())),
Version = CrashReportVersion.From(report.Version),
GameVersion = GameVersion.From(report.Metadata.GameVersion),
ExceptionType = entityFactory.GetOrCreateExceptionType(ExceptionTypeId.From(report.Exception.Type)),
ExceptionType = entityFactory.GetOrCreateExceptionType(ExceptionTypeId.FromException(report.Exception)),
Exception = GetException(report.Exception),
CreatedAt = fileId.Value.Length == 8 ? DateTimeOffset.UnixEpoch.ToUniversalTime() : date.ToUniversalTime(),
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
Expand All @@ -20,8 +21,11 @@ public ApiResultProblemDetailsWriter(IActionResultExecutor<ObjectResult> actionR

public async ValueTask WriteAsync(ProblemDetailsContext context)
{
var endpoint = context.HttpContext.GetEndpoint() ??
context.HttpContext.Features.Get<IExceptionHandlerFeature>()?.Endpoint;

var routeData = context.HttpContext.GetRouteData();
var action = context.HttpContext.GetEndpoint()?.Metadata.GetMetadata<ActionDescriptor>();
var action = endpoint!.Metadata.GetMetadata<ActionDescriptor>();
if (action is null) return;
var actionContext = new ActionContext(context.HttpContext, routeData, action);
var result = ApiResult.FromError(context.HttpContext, context.ProblemDetails);
Expand All @@ -30,7 +34,9 @@ public async ValueTask WriteAsync(ProblemDetailsContext context)

public bool CanWrite(ProblemDetailsContext problemDetailsContext)
{
var endpoint = problemDetailsContext.HttpContext.GetEndpoint();
var endpoint = problemDetailsContext.HttpContext.GetEndpoint() ??
problemDetailsContext.HttpContext.Features.Get<IExceptionHandlerFeature>()?.Endpoint;

if (endpoint?.Metadata.GetMetadata<ControllerActionDescriptor>() is { } context)
return ApiResultUtils.IsReturnTypeApiResult(context.MethodInfo);

Expand Down

0 comments on commit 8aae085

Please sign in to comment.