Skip to content

Commit

Permalink
Migrated locks and a few other features to c# 13 for improved perform…
Browse files Browse the repository at this point in the history
…ance
  • Loading branch information
jschick04 authored and bill-long committed Nov 18, 2024
1 parent ef92069 commit 96aa96a
Show file tree
Hide file tree
Showing 15 changed files with 315 additions and 276 deletions.
6 changes: 5 additions & 1 deletion src/EventLogExpert.EventDbTool/ShowLocalCommand.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

using EventLogExpert.Eventing.Helpers;
using EventLogExpert.Eventing.Providers;
using Microsoft.Extensions.Logging;
using System.CommandLine;

namespace EventLogExpert.EventDbTool;

public class ShowLocalCommand : DbToolCommand
{
private static readonly ITraceLogger s_logger = new TraceLogger(LogLevel.Information);

public static Command GetCommand()
{
var showProvidersCommand = new Command(
Expand All @@ -33,7 +37,7 @@ public static void ShowProviderInfo(string filter, bool verbose)
LogProviderDetailHeader(providerNames);
foreach (var providerName in providerNames)
{
var provider = new EventMessageProvider(providerName, verbose ? (s, log) => Console.WriteLine(s) : (s, log) => { });
var provider = new EventMessageProvider(providerName, verbose ? s_logger : null);
var details = provider.LoadProviderDetails();
if (details != null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ public sealed class EventResolverTests(ITestOutputHelper outputHelper)
{
internal class UnitTestEventResolver : EventResolverBase, IEventResolver
{
internal UnitTestEventResolver(List<ProviderDetails> providerDetailsList) :
base((s, log) => Debug.WriteLine(s)) =>
internal UnitTestEventResolver(List<ProviderDetails> providerDetailsList) =>
providerDetailsList.ForEach(p => providerDetails.TryAdd(p.ProviderName, p));

public void ResolveProviderDetails(EventRecord eventRecord, string owningLogName)
Expand All @@ -26,15 +25,15 @@ public void ResolveProviderDetails(EventRecord eventRecord, string owningLogName
return;
}

var details = new EventMessageProvider(eventRecord.ProviderName, tracer).LoadProviderDetails();
var details = new EventMessageProvider(eventRecord.ProviderName).LoadProviderDetails();
providerDetails.TryAdd(eventRecord.ProviderName, details);
}
}

private readonly ITestOutputHelper _outputHelper = outputHelper;

[Fact]
public void CanResolveMSExchangeRepl4114()
public void EventResolver_MSExchangeReplId4114_ShouldResolve()
{
// This event has a message in the legacy provider, but a task in the modern provider.
EventRecord eventRecord = new()
Expand Down
59 changes: 22 additions & 37 deletions src/EventLogExpert.Eventing/EventResolvers/EventResolverBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ public partial class EventResolverBase
{ 0x80000000000000, "Classic" }
};

protected readonly ITraceLogger? logger;
protected readonly ConcurrentDictionary<string, ProviderDetails?> providerDetails = new();
protected readonly ReaderWriterLockSlim providerDetailsLock = new();
protected readonly ITraceLogger? logger;

private readonly Regex _sectionsToReplace = WildcardWithNumberRegex();

Expand Down Expand Up @@ -174,10 +174,15 @@ protected string FormatDescription(
// when the entire description is a string literal, and there is no provider DLL needed.
// Found a few providers that have their properties wrapped with \r\n for some reason
return properties.Count == 1 ?
properties[0].Trim(['\0', '\r', '\n']) :
properties[0].TrimEnd('\0', '\r', '\n') :
"Unable to resolve description, see XML for more details.";
}

if (properties.Count <= 0)
{
return descriptionTemplate.TrimEnd('\0', '\r', '\n');
}

ReadOnlySpan<char> description = descriptionTemplate
.Replace("\r\n%n", " \r\n")
.Replace("%n\r\n", "\r\n ")
Expand All @@ -194,10 +199,8 @@ protected string FormatDescription(

ReadOnlySpan<char> propString = description[match.Index..(match.Index + match.Length)];

if (!propString.StartsWith("%%"))
if (!propString.StartsWith("%%") && int.TryParse(propString.Trim(['{', '}', '%']), out var propIndex))
{
var propIndex = int.Parse(propString.Trim(['{', '}', '%']));

if (propIndex - 1 >= properties.Count)
{
return "Unable to resolve description, see XML for more details.";
Expand Down Expand Up @@ -243,7 +246,7 @@ protected string FormatDescription(
catch (InvalidOperationException)
{
// If the regex fails to match, then we just return the original description.
return description.TrimEnd(['\0', '\r', '\n']).ToString();
return descriptionTemplate.TrimEnd('\0', '\r', '\n');
}
catch (Exception ex)
{
Expand Down Expand Up @@ -317,47 +320,29 @@ private static List<string> GetFormattedProperties(string? template, IEnumerable
return providers;
}

[GeneratedRegex("%+[0-9]+")]
private static partial Regex WildcardWithNumberRegex();

private EventModel? GetModernEvent(EventRecord eventRecord, ProviderDetails details)
private static EventModel? GetModernEvent(EventRecord eventRecord, ProviderDetails details)
{
if (eventRecord is { Version: null, LogName: null })
{
return null;
}

var modernEvents = details.Events
.Where(e => e.Id == eventRecord.Id &&
EventModel? modernEvent = details.Events
.FirstOrDefault(e => e.Id == eventRecord.Id &&
e.Version == eventRecord.Version &&
e.LogName == eventRecord.LogName).ToList();
e.LogName == eventRecord.LogName);

if (modernEvents is { Count: 0 })
if (modernEvent is not null)
{
// Try again forcing the long to a short and with no log name.
// This is needed for providers such as Microsoft-Windows-Complus
modernEvents = details.Events
.Where(e => (short)e.Id == eventRecord.Id && e.Version == eventRecord.Version).ToList();
return modernEvent;
}

if (modernEvents is not { Count: > 0 })
{
return null;
}

if (modernEvents.Count <= 1)
{
return modernEvents[0];
}

logger?.Trace("Ambiguous modern event found:");

foreach (var modernEvent in modernEvents)
{
logger?.Trace($" Version: {modernEvent.Version} Id: {modernEvent.Id} " +
$"LogName: {modernEvent.LogName} Description: {modernEvent.Description}");
}

return modernEvents[0];
// Try again forcing the long to a short and with no log name.
// This is needed for providers such as Microsoft-Windows-Complus
return details.Events
.FirstOrDefault(e => (short)e.Id == eventRecord.Id && e.Version == eventRecord.Version);
}

[GeneratedRegex("%+[0-9]+")]
private static partial Regex WildcardWithNumberRegex();
}
2 changes: 1 addition & 1 deletion src/EventLogExpert.Eventing/Helpers/EventMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ internal static IList<object> RenderEventProperties(EvtHandle eventHandle)
properties.Add(ConvertVariant(property) ?? throw new InvalidDataException());
}

return properties;
return properties.AsReadOnly();
}
finally
{
Expand Down
95 changes: 45 additions & 50 deletions src/EventLogExpert.Eventing/Helpers/NativeMethods.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

using EventLogExpert.Eventing.Models;
using System.Runtime.InteropServices;

// ReSharper disable InconsistentNaming
Expand All @@ -9,55 +10,49 @@

namespace EventLogExpert.Eventing.Helpers;

public sealed class NativeMethods
[Flags]
internal enum LoadLibraryFlags : uint
{
public const int RT_MESSAGETABLE = 11;

public delegate bool EnumResTypeProc(nint hModule, string lpszType, nint lParam);

[Flags]
public enum LoadLibraryFlags : uint
{
None = 0,
DONT_RESOLVE_DLL_REFERENCES = 0x00000001,
LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010,
LOAD_LIBRARY_AS_DATAFILE = 0x00000002,
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040,
LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020,
LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200,
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000,
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100,
LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800,
LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400,
LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008
}

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool EnumResourceTypes(nint hModule, EnumResTypeProc lpEnumFunc, nint lParam);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern nint FindResource(nint hModule, int lpID, int lpType);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool FreeLibrary(nint hModule);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern nint LoadLibrary(string fileName);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern nint LoadLibraryEx(string lpFileName, nint hReservedNull, LoadLibraryFlags dwFlags);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern nint LoadResource(nint hModule, nint hResInfo);

[DllImport("kernel32.dll")]
public static extern nint LockResource(nint hResData);

[StructLayout(LayoutKind.Sequential)]
public struct MESSAGE_RESOURCE_BLOCK
{
public int LowId;
public int HighId;
public int OffsetToEntries;
}
None = 0,
DONT_RESOLVE_DLL_REFERENCES = 0x00000001,
LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010,
LOAD_LIBRARY_AS_DATAFILE = 0x00000002,
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040,
LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020,
LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200,
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000,
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100,
LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800,
LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400,
LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008
}

internal sealed partial class NativeMethods
{
internal const int RT_MESSAGETABLE = 11;

private const string Kernel32Api = "kernel32.dll";

[LibraryImport(Kernel32Api, SetLastError = true)]
internal static partial LibraryHandle FindResourceExA(
LibraryHandle hModule,
int lpType,
int lpName,
ushort wLanguage = 0);

[LibraryImport(Kernel32Api, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool FreeLibrary(IntPtr hModule);

[LibraryImport(Kernel32Api, SetLastError = true)]
internal static partial LibraryHandle LoadLibraryExW(
[MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
IntPtr hReservedNull,
LoadLibraryFlags dwFlags);

[LibraryImport(Kernel32Api, SetLastError = true)]
internal static partial IntPtr LoadResource(LibraryHandle hModule, LibraryHandle hResInfo);

[LibraryImport(Kernel32Api)]
internal static partial IntPtr LockResource(IntPtr hResData);
}
2 changes: 1 addition & 1 deletion src/EventLogExpert.Eventing/Models/EventRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public sealed record EventRecord

public long? RecordId { get; set; }

public IList<object> Properties { get; set; } = [];
public IEnumerable<object> Properties { get; set; } = [];

public string ProviderName { get; set; } = string.Empty;

Expand Down
33 changes: 33 additions & 0 deletions src/EventLogExpert.Eventing/Models/LibraryHandle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

using EventLogExpert.Eventing.Helpers;
using Microsoft.Win32.SafeHandles;

namespace EventLogExpert.Eventing.Models;

internal sealed partial class LibraryHandle : SafeHandleZeroOrMinusOneIsInvalid
{
// Must be public for P/Invoke to work
public LibraryHandle() : base(true) { }

internal LibraryHandle(IntPtr handle) : base(true)
{
SetHandle(handle);
}

internal LibraryHandle(IntPtr handle, bool ownsHandle) : base(ownsHandle)
{
SetHandle(handle);
}

internal static LibraryHandle Zero => new();

protected override bool ReleaseHandle()
{
NativeMethods.FreeLibrary(handle);
handle = IntPtr.Zero;

return true;
}
}
14 changes: 14 additions & 0 deletions src/EventLogExpert.Eventing/Models/MessageResourceBlock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

using System.Runtime.InteropServices;

namespace EventLogExpert.Eventing.Models;

[StructLayout(LayoutKind.Sequential)]
internal struct MessageResourceBlock
{
internal int LowId;
internal int HighId;
internal int OffsetToEntries;
}
Loading

0 comments on commit 96aa96a

Please sign in to comment.