From 6880a8916bec2ac4ba68180ee28aaca715fd33cf Mon Sep 17 00:00:00 2001 From: Jan Krivanek Date: Tue, 7 Feb 2023 16:26:02 +0100 Subject: [PATCH] Add support for logging assembly loads logging --- .../AssemblyLoadBuildEventArgs2.cs | 67 +++++++++++++++++++ .../BinaryLogger/BinaryLogRecordKind.cs | 3 +- .../BinaryLogger/BinaryLogger.cs | 4 +- .../BinaryLogger/BuildEventArgsReader.cs | 32 +++++++++ .../BinaryLogger/BuildEventArgsWriter.cs | 20 ++++++ 5 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 src/StructuredLogger/AssemblyLoadBuildEventArgs2.cs diff --git a/src/StructuredLogger/AssemblyLoadBuildEventArgs2.cs b/src/StructuredLogger/AssemblyLoadBuildEventArgs2.cs new file mode 100644 index 000000000..b96f05d28 --- /dev/null +++ b/src/StructuredLogger/AssemblyLoadBuildEventArgs2.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Shared; + +// TODO: remove whole file once AssemblyLoadBuildEventArgs are imported via Microsoft.Build.Framework package + +namespace Microsoft.Build.Framework +{ + internal enum AssemblyLoadingContext + { + TaskRun, + Evaluation, + SdkResolution, + LoggerInitialization, + } + + internal sealed class AssemblyLoadBuildEventArgs : BuildMessageEventArgs + { + private const string DefaultAppDomainDescriptor = "[Default]"; + + public AssemblyLoadBuildEventArgs() + { } + + public AssemblyLoadBuildEventArgs( + AssemblyLoadingContext loadingContext, + string? loadingInitiator, + string? assemblyName, + string assemblyPath, + Guid mvid, + string? customAppDomainDescriptor, + MessageImportance importance = MessageImportance.Low) + : base(null, null, null, importance, DateTime.UtcNow, assemblyName, assemblyPath, mvid) + { + LoadingContext = loadingContext; + LoadingInitiator = loadingInitiator; + AssemblyName = assemblyName; + AssemblyPath = assemblyPath; + MVID = mvid; + AppDomainDescriptor = customAppDomainDescriptor; + } + + public AssemblyLoadingContext LoadingContext { get; private set; } + public string? LoadingInitiator { get; private set; } + public string? AssemblyName { get; private set; } + public string? AssemblyPath { get; private set; } + public Guid MVID { get; private set; } + // Null string indicates that load occurred on Default AppDomain (for both Core and Framework). + public string? AppDomainDescriptor { get; private set; } + + public override string Message + { + get + { + if (RawMessage == null) + { + string? loadingInitiator = LoadingInitiator == null ? null : $" ({LoadingInitiator})"; + RawMessage = string.Format("Assembly loaded during {0}{1}: {2} (location: {3}, MVID: {4}, AppDomain: {5})", LoadingContext.ToString(), loadingInitiator, AssemblyName, AssemblyPath, MVID.ToString(), AppDomainDescriptor ?? DefaultAppDomainDescriptor); + } + + return RawMessage; + } + } + } +} diff --git a/src/StructuredLogger/BinaryLogger/BinaryLogRecordKind.cs b/src/StructuredLogger/BinaryLogger/BinaryLogRecordKind.cs index a4352199a..81f545c25 100644 --- a/src/StructuredLogger/BinaryLogger/BinaryLogRecordKind.cs +++ b/src/StructuredLogger/BinaryLogger/BinaryLogRecordKind.cs @@ -28,6 +28,7 @@ public enum BinaryLogRecordKind NameValueList, String, TaskParameter, - FileUsed + FileUsed, + AssemblyLoad } } diff --git a/src/StructuredLogger/BinaryLogger/BinaryLogger.cs b/src/StructuredLogger/BinaryLogger/BinaryLogger.cs index 290cb8774..eb4f4675a 100644 --- a/src/StructuredLogger/BinaryLogger/BinaryLogger.cs +++ b/src/StructuredLogger/BinaryLogger/BinaryLogger.cs @@ -57,7 +57,9 @@ public sealed class BinaryLogger : ILogger // - TargetSkippedEventArgs: added SkipReason, OriginalBuildEventContext // version 15: // - new record kind: FileUsedEventArgs - internal const int FileFormatVersion = 15; + // version 16: + // - AssemblyLoadBuildEventArgs + internal const int FileFormatVersion = 16; private Stream stream; private BinaryWriter binaryWriter; diff --git a/src/StructuredLogger/BinaryLogger/BuildEventArgsReader.cs b/src/StructuredLogger/BinaryLogger/BuildEventArgsReader.cs index eed42e3ce..3af1e1d68 100644 --- a/src/StructuredLogger/BinaryLogger/BuildEventArgsReader.cs +++ b/src/StructuredLogger/BinaryLogger/BuildEventArgsReader.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; using System.Text; using Microsoft.Build.BackEnd; using Microsoft.Build.Collections; @@ -180,6 +181,9 @@ public BuildEventArgs Read() case BinaryLogRecordKind.FileUsed: result = ReadFileUsedEventArgs(); break; + case BinaryLogRecordKind.AssemblyLoad: + result = ReadAssemblyLoadEventArgs(); + break; default: break; } @@ -768,6 +772,29 @@ private BuildEventArgs ReadFileUsedEventArgs() return e; } + private BuildEventArgs ReadAssemblyLoadEventArgs() + { + var fields = ReadBuildEventArgsFields(readImportance: false); + + AssemblyLoadingContext context = (AssemblyLoadingContext)ReadInt32(); + string loadingInitiator = ReadDeduplicatedString(); + string assemblyName = ReadDeduplicatedString(); + string assemblyPath = ReadDeduplicatedString(); + Guid mvid = ReadGuid(); + string appDomainName = ReadDeduplicatedString(); + + var e = new AssemblyLoadBuildEventArgs( + context, + loadingInitiator, + assemblyName, + assemblyPath, + mvid, + appDomainName); + SetCommonFields(e, fields); + + return e; + } + private BuildEventArgs ReadPropertyReassignmentEventArgs() { var fields = ReadBuildEventArgsFields(readImportance: true); @@ -1220,6 +1247,11 @@ private bool ReadBoolean() return binaryReader.ReadBoolean(); } + private Guid ReadGuid() + { + return new Guid(binaryReader.ReadBytes(Marshal.SizeOf(typeof(Guid)))); + } + private DateTime ReadDateTime() { return new DateTime(binaryReader.ReadInt64(), (DateTimeKind)ReadInt32()); diff --git a/src/StructuredLogger/BinaryLogger/BuildEventArgsWriter.cs b/src/StructuredLogger/BinaryLogger/BuildEventArgsWriter.cs index e23837b31..f18ac389b 100644 --- a/src/StructuredLogger/BinaryLogger/BuildEventArgsWriter.cs +++ b/src/StructuredLogger/BinaryLogger/BuildEventArgsWriter.cs @@ -151,6 +151,7 @@ Base types and inheritance ("EventArgs" suffix omitted): TaskCommandLine TaskParameter UninitializedPropertyRead + AssemblyLoaded BuildStatus TaskStarted TaskFinished @@ -431,6 +432,7 @@ private void Write(BuildMessageEventArgs e) case PropertyInitialValueSetEventArgs propertyInitialValueSet: Write(propertyInitialValueSet); break; case CriticalBuildMessageEventArgs criticalBuildMessage: Write(criticalBuildMessage); break; case FileUsedEventArgs fileUsed: Write(fileUsed); break; + case AssemblyLoadBuildEventArgs assemblyLoaded: Write(assemblyLoaded); break; default: // actual BuildMessageEventArgs Write(BinaryLogRecordKind.Message); WriteMessageFields(e, writeImportance: true); @@ -508,6 +510,18 @@ private void Write(FileUsedEventArgs e) WriteDeduplicatedString(e.FilePath); } + private void Write(AssemblyLoadBuildEventArgs e) + { + Write(BinaryLogRecordKind.AssemblyLoad); + WriteMessageFields(e, writeMessage: false, writeImportance: false); + Write((int)e.LoadingContext); + WriteDeduplicatedString(e.LoadingInitiator); + WriteDeduplicatedString(e.AssemblyName); + WriteDeduplicatedString(e.AssemblyPath); + Write(e.MVID); + WriteDeduplicatedString(e.AppDomainDescriptor); + } + private void Write(TaskCommandLineEventArgs e) { Write(BinaryLogRecordKind.TaskCommandLine); @@ -1107,6 +1121,12 @@ private void Write(bool boolean) binaryWriter.Write(boolean); } + private void Write(Guid guid) + { + // To avoid allocation (as is done in MSBuild code base) we'd need unsafe code. + binaryWriter.Write(guid.ToByteArray()); + } + private void WriteDeduplicatedString(string text) { var (recordId, _) = HashString(text);