From 48a8c9617e661b075998867d83207ebf7bdb98a9 Mon Sep 17 00:00:00 2001 From: Dmitriy Shepelev Date: Wed, 1 Mar 2023 16:27:31 -0500 Subject: [PATCH] Implement `OutOfProcNodeFileAccessManager` --- .../BackEnd/BuildManager/BuildManager.cs | 28 ++++ .../FileAccesses/FileAccessReport.cs | 27 +++ .../OutOfProcNodeFileAccessManager.cs | 53 +++--- .../Components/FileAccesses/ProcessReport.cs | 27 +++ src/Build/BackEnd/Node/OutOfProcNode.cs | 6 +- src/Build/Microsoft.Build.csproj | 6 +- src/Shared/BinaryTranslator.cs | 158 ++++++++++++------ src/Shared/INodePacket.cs | 10 ++ src/Shared/ITranslator.cs | 12 ++ 9 files changed, 249 insertions(+), 78 deletions(-) create mode 100644 src/Build/BackEnd/Components/FileAccesses/FileAccessReport.cs create mode 100644 src/Build/BackEnd/Components/FileAccesses/ProcessReport.cs diff --git a/src/Build/BackEnd/BuildManager/BuildManager.cs b/src/Build/BackEnd/BuildManager/BuildManager.cs index 1630d1cb328..c79ec20593b 100644 --- a/src/Build/BackEnd/BuildManager/BuildManager.cs +++ b/src/Build/BackEnd/BuildManager/BuildManager.cs @@ -582,7 +582,9 @@ public void BeginBuild(BuildParameters parameters) _nodeManager.RegisterPacketHandler(NodePacketType.BuildRequestConfiguration, BuildRequestConfiguration.FactoryForDeserialization, this); _nodeManager.RegisterPacketHandler(NodePacketType.BuildRequestConfigurationResponse, BuildRequestConfigurationResponse.FactoryForDeserialization, this); _nodeManager.RegisterPacketHandler(NodePacketType.BuildResult, BuildResult.FactoryForDeserialization, this); + _nodeManager.RegisterPacketHandler(NodePacketType.FileAccessReport, FileAccessReport.FactoryForDeserialization, this); _nodeManager.RegisterPacketHandler(NodePacketType.NodeShutdown, NodeShutdown.FactoryForDeserialization, this); + _nodeManager.RegisterPacketHandler(NodePacketType.ProcessReport, ProcessReport.FactoryForDeserialization, this); _nodeManager.RegisterPacketHandler(NodePacketType.ResolveSdkRequest, SdkResolverRequest.FactoryForDeserialization, SdkResolverService as INodePacketHandler); _nodeManager.RegisterPacketHandler(NodePacketType.ResourceRequest, ResourceRequest.FactoryForDeserialization, this); @@ -1562,6 +1564,16 @@ private void ProcessPacket(int node, INodePacket packet) HandleNodeShutdown(node, shutdownPacket); break; + case NodePacketType.FileAccessReport: + FileAccessReport fileAccessReport = ExpectPacketType(packet, NodePacketType.FileAccessReport); + HandleFileAccessReport(node, fileAccessReport); + break; + + case NodePacketType.ProcessReport: + ProcessReport processReport = ExpectPacketType(packet, NodePacketType.ProcessReport); + HandleProcessReport(node, processReport); + break; + default: ErrorUtilities.ThrowInternalError("Unexpected packet received by BuildManager: {0}", packet.Type); break; @@ -2463,6 +2475,22 @@ private void HandleNodeShutdown(int node, NodeShutdown shutdownPacket) CheckForActiveNodesAndCleanUpSubmissions(); } + /// + /// Report the received to the . + /// + /// The id of the node from which the was received. + /// The file access to report to the . + private void HandleFileAccessReport(int nodeId, FileAccessReport fileAccessReport) => + ((FileAccessManager)((IBuildComponentHost)this).GetComponent(BuildComponentType.FileAccessManager)).ReportFileAccess(fileAccessReport.FileAccessData, nodeId); + + /// + /// Report the received to the . + /// + /// The id of the node from which the was received. + /// The process data to report to the . + private void HandleProcessReport(int nodeId, ProcessReport processReport) => + ((FileAccessManager)((IBuildComponentHost)this).GetComponent(BuildComponentType.FileAccessManager)).ReportProcess(processReport.ProcessData, nodeId); + /// /// If there are no more active nodes, cleans up any remaining submissions. /// diff --git a/src/Build/BackEnd/Components/FileAccesses/FileAccessReport.cs b/src/Build/BackEnd/Components/FileAccesses/FileAccessReport.cs new file mode 100644 index 00000000000..f69b6fd1580 --- /dev/null +++ b/src/Build/BackEnd/Components/FileAccesses/FileAccessReport.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Build.BackEnd; +using Microsoft.Build.Framework.FileAccess; + +namespace Microsoft.Build.FileAccesses +{ + internal sealed class FileAccessReport : INodePacket + { + private FileAccessData _fileAccessData; + + internal FileAccessReport(FileAccessData fileAccessData) => _fileAccessData = fileAccessData; + + private FileAccessReport(ITranslator translator) => Translate(translator); + + /// + public NodePacketType Type => NodePacketType.FileAccessReport; + + /// + public void Translate(ITranslator translator) => translator.Translate(ref _fileAccessData); + + internal FileAccessData FileAccessData => _fileAccessData; + + internal static INodePacket FactoryForDeserialization(ITranslator translator) => new FileAccessReport(translator); + } +} diff --git a/src/Build/BackEnd/Components/FileAccesses/OutOfProcNodeFileAccessManager.cs b/src/Build/BackEnd/Components/FileAccesses/OutOfProcNodeFileAccessManager.cs index 1730eb79139..d6fbd401329 100644 --- a/src/Build/BackEnd/Components/FileAccesses/OutOfProcNodeFileAccessManager.cs +++ b/src/Build/BackEnd/Components/FileAccesses/OutOfProcNodeFileAccessManager.cs @@ -9,12 +9,23 @@ namespace Microsoft.Build.FileAccesses { + /// + /// Reports file accesses and process data to the in-proc node. + /// internal sealed class OutOfProcNodeFileAccessManager : IFileAccessManager, IBuildComponent { - public static IBuildComponent CreateComponent(BuildComponentType type) + /// + /// The to report file accesses and process + /// data to the in-proc node. + /// + private readonly Action _sendPacket; + + private OutOfProcNodeFileAccessManager(Action sendPacket) => _sendPacket = sendPacket; + + public static IBuildComponent CreateComponent(BuildComponentType type, Action sendPacket) { ErrorUtilities.VerifyThrowArgumentOutOfRange(type == BuildComponentType.FileAccessManager, nameof(type)); - return new OutOfProcNodeFileAccessManager(); + return new OutOfProcNodeFileAccessManager(sendPacket); } public void InitializeComponent(IBuildComponentHost host) @@ -25,26 +36,28 @@ public void ShutdownComponent() { } - public void ReportFileAccess(FileAccessData fileAccessData, int nodeId) - { - // TODO: Send the packet to the main node. - } - - public void ReportProcess(ProcessData processData, int nodeId) - { - // TODO: Send the packet to the main node. - } - - public FileAccessManager.HandlerRegistration RegisterHandlers(Action fileAccessHandler, Action processHandler) - { - // This method should not be called in OOP nodes + /// + /// Reports a file access to the in-proc node. + /// + /// The file access to report to the in-proc node. + /// The id of the reporting out-of-proc node. + public void ReportFileAccess(FileAccessData fileAccessData, int nodeId) => _sendPacket(new FileAccessReport(fileAccessData)); + + /// + /// Reports process data to the in-proc node. + /// + /// The process data to report to the in-proc node. + /// The id of the reporting out-of-proc node. + public void ReportProcess(ProcessData processData, int nodeId) => _sendPacket(new ProcessReport(processData)); + + // This method should not be called in OOP nodes. + public FileAccessManager.HandlerRegistration RegisterHandlers( + Action fileAccessHandler, + Action processHandler) => throw new NotImplementedException(); - } - public void WaitForFileAccessReportCompletion(int globalRequestId, CancellationToken cancellationToken) - { - // This method should not be called in OOP nodes + // This method should not be called in OOP nodes. + public void WaitForFileAccessReportCompletion(int globalRequestId, CancellationToken cancellationToken) => throw new NotImplementedException(); - } } } diff --git a/src/Build/BackEnd/Components/FileAccesses/ProcessReport.cs b/src/Build/BackEnd/Components/FileAccesses/ProcessReport.cs new file mode 100644 index 00000000000..89bf533ed86 --- /dev/null +++ b/src/Build/BackEnd/Components/FileAccesses/ProcessReport.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Build.BackEnd; +using Microsoft.Build.Framework.FileAccess; + +namespace Microsoft.Build.FileAccesses +{ + internal sealed class ProcessReport : INodePacket + { + private ProcessData _processData; + + internal ProcessReport(ProcessData processData) => _processData = processData; + + private ProcessReport(ITranslator translator) => Translate(translator); + + /// + public NodePacketType Type => NodePacketType.ProcessReport; + + internal ProcessData ProcessData => _processData; + + internal static INodePacket FactoryForDeserialization(ITranslator translator) => new ProcessReport(translator); + + /// + public void Translate(ITranslator translator) => translator.Translate(ref _processData); + } +} diff --git a/src/Build/BackEnd/Node/OutOfProcNode.cs b/src/Build/BackEnd/Node/OutOfProcNode.cs index 2b37d711658..2d0e9337bb1 100644 --- a/src/Build/BackEnd/Node/OutOfProcNode.cs +++ b/src/Build/BackEnd/Node/OutOfProcNode.cs @@ -154,10 +154,10 @@ public OutOfProcNode() // Create a factory for the out-of-proc SDK resolver service which can pass our SendPacket delegate to be used for sending packets to the main node OutOfProcNodeSdkResolverServiceFactory sdkResolverServiceFactory = new OutOfProcNodeSdkResolverServiceFactory(SendPacket); ((IBuildComponentHost)this).RegisterFactory(BuildComponentType.SdkResolverService, sdkResolverServiceFactory.CreateInstance); - - ((IBuildComponentHost)this).RegisterFactory(BuildComponentType.FileAccessManager, OutOfProcNodeFileAccessManager.CreateComponent); - _sdkResolverService = (this as IBuildComponentHost).GetComponent(BuildComponentType.SdkResolverService) as ISdkResolverService; + ((IBuildComponentHost)this).RegisterFactory( + BuildComponentType.FileAccessManager, + (componentType) => OutOfProcNodeFileAccessManager.CreateComponent(componentType, SendPacket)); if (s_projectRootElementCacheBase == null) { diff --git a/src/Build/Microsoft.Build.csproj b/src/Build/Microsoft.Build.csproj index 5338cd0bf33..de5b1afa79d 100644 --- a/src/Build/Microsoft.Build.csproj +++ b/src/Build/Microsoft.Build.csproj @@ -166,9 +166,11 @@ - - + + + + diff --git a/src/Shared/BinaryTranslator.cs b/src/Shared/BinaryTranslator.cs index 4e54916b6c4..8d02092203a 100644 --- a/src/Shared/BinaryTranslator.cs +++ b/src/Shared/BinaryTranslator.cs @@ -403,6 +403,41 @@ public void Translate(ref BuildEventContext value) _reader.ReadInt32()); } + /// + public void Translate(ref FileAccessData fileAccessData) + { + ReportedFileOperation reportedFileOperation = default; + RequestedAccess requestedAccess = default; + uint processId = default; + uint error = default; + DesiredAccess desiredAccess = default; + FlagsAndAttributes flagsAndAttributes = default; + string path = default; +#nullable enable + string? processArgs = default; +#nullable disable + bool isAnAugmentedFileAccess = default; + TranslateEnum(ref reportedFileOperation, (int)reportedFileOperation); + TranslateEnum(ref requestedAccess, (int)requestedAccess); + Translate(ref processId); + Translate(ref error); + TranslateEnum(ref desiredAccess, (int)desiredAccess); + TranslateEnum(ref flagsAndAttributes, (int)flagsAndAttributes); + Translate(ref path); + Translate(ref processArgs); + Translate(ref isAnAugmentedFileAccess); + fileAccessData = new FileAccessData( + reportedFileOperation, + requestedAccess, + processId, + error, + desiredAccess, + flagsAndAttributes, + path, + processArgs, + isAnAugmentedFileAccess); + } + /// public void Translate(ref List fileAccessDataList) { @@ -416,38 +451,35 @@ public void Translate(ref List fileAccessDataList) fileAccessDataList = new List(count); for (int i = 0; i < count; i++) { - ReportedFileOperation reportedFileOperation = default; - RequestedAccess requestedAccess = default; - uint processId = default; - uint error = default; - DesiredAccess desiredAccess = default; - FlagsAndAttributes flagsAndAttributes = default; - string path = default; -#nullable enable - string? processArgs = default; -#nullable disable - bool isAnAugmentedFileAccess = default; - TranslateEnum(ref reportedFileOperation, (int)reportedFileOperation); - TranslateEnum(ref requestedAccess, (int)requestedAccess); - Translate(ref processId); - Translate(ref error); - TranslateEnum(ref desiredAccess, (int)desiredAccess); - TranslateEnum(ref flagsAndAttributes, (int)flagsAndAttributes); - Translate(ref path); - Translate(ref processArgs); - Translate(ref isAnAugmentedFileAccess); - fileAccessDataList.Add(new FileAccessData( - reportedFileOperation, - requestedAccess, - processId, - error, - desiredAccess, - flagsAndAttributes, - path, - processArgs, - isAnAugmentedFileAccess)); + FileAccessData fileAccessData = default; + Translate(ref fileAccessData); + fileAccessDataList.Add(fileAccessData); } } + + /// + public void Translate(ref ProcessData processData) + { + string processName = default; + uint processId = default; + uint parentProcessId = default; + DateTime creationDateTime = default; + DateTime exitDateTime = default; + uint exitCode = default; + Translate(ref processName); + Translate(ref processId); + Translate(ref parentProcessId); + Translate(ref creationDateTime); + Translate(ref exitDateTime); + Translate(ref exitCode); + processData = new ProcessData( + processName, + processId, + parentProcessId, + creationDateTime, + exitDateTime, + exitCode); + } #endif /// @@ -1108,6 +1140,31 @@ public void Translate(ref BuildEventContext value) _writer.Write(value.TaskId); } + /// + public void Translate(ref FileAccessData fileAccessData) + { + ReportedFileOperation reportedFileOperation = fileAccessData.Operation; + RequestedAccess requestedAccess = fileAccessData.RequestedAccess; + uint processId = fileAccessData.ProcessId; + uint error = fileAccessData.Error; + DesiredAccess desiredAccess = fileAccessData.DesiredAccess; + FlagsAndAttributes flagsAndAttributes = fileAccessData.FlagsAndAttributes; + string path = fileAccessData.Path; +#nullable enable + string? processArgs = fileAccessData.ProcessArgs; +#nullable disable + bool isAnAugmentedFileAccess = fileAccessData.IsAnAugmentedFileAccess; + TranslateEnum(ref reportedFileOperation, (int)reportedFileOperation); + TranslateEnum(ref requestedAccess, (int)requestedAccess); + Translate(ref processId); + Translate(ref error); + TranslateEnum(ref desiredAccess, (int)desiredAccess); + TranslateEnum(ref flagsAndAttributes, (int)flagsAndAttributes); + Translate(ref path); + Translate(ref processArgs); + Translate(ref isAnAugmentedFileAccess); + } + /// public void Translate(ref List fileAccessDataList) { @@ -1118,29 +1175,24 @@ public void Translate(ref List fileAccessDataList) int count = fileAccessDataList.Count; Translate(ref count); - fileAccessDataList.ForEach(fileAccessData => - { - ReportedFileOperation reportedFileOperation = fileAccessData.Operation; - RequestedAccess requestedAccess = fileAccessData.RequestedAccess; - uint processId = fileAccessData.ProcessId; - uint error = fileAccessData.Error; - DesiredAccess desiredAccess = fileAccessData.DesiredAccess; - FlagsAndAttributes flagsAndAttributes = fileAccessData.FlagsAndAttributes; - string path = fileAccessData.Path; -#nullable enable - string? processArgs = fileAccessData.ProcessArgs; -#nullable disable - bool isAnAugmentedFileAccess = fileAccessData.IsAnAugmentedFileAccess; - TranslateEnum(ref reportedFileOperation, (int)reportedFileOperation); - TranslateEnum(ref requestedAccess, (int)requestedAccess); - Translate(ref processId); - Translate(ref error); - TranslateEnum(ref desiredAccess, (int)desiredAccess); - TranslateEnum(ref flagsAndAttributes, (int)flagsAndAttributes); - Translate(ref path); - Translate(ref processArgs); - Translate(ref isAnAugmentedFileAccess); - }); + fileAccessDataList.ForEach(fileAccessData => Translate(ref fileAccessData)); + } + + /// + public void Translate(ref ProcessData processData) + { + string processName = processData.ProcessName; + uint processId = processData.ProcessId; + uint parentProcessId = processData.ParentProcessId; + DateTime creationDateTime = processData.CreationDateTime; + DateTime exitDateTime = processData.ExitDateTime; + uint exitCode = processData.ExitCode; + Translate(ref processName); + Translate(ref processId); + Translate(ref parentProcessId); + Translate(ref creationDateTime); + Translate(ref exitDateTime); + Translate(ref exitCode); } #endif diff --git a/src/Shared/INodePacket.cs b/src/Shared/INodePacket.cs index cb89889c3ac..4b077475510 100644 --- a/src/Shared/INodePacket.cs +++ b/src/Shared/INodePacket.cs @@ -213,6 +213,16 @@ internal enum NodePacketType : byte /// Keep this enum value constant intact as this is part of contract with dotnet CLI /// ServerNodeBuildCancel = 0xF3, + + /// + /// Message sent from a node reporting a file access. + /// + FileAccessReport, + + /// + /// Message sent from a node reporting process data. + /// + ProcessReport, } #endregion diff --git a/src/Shared/ITranslator.cs b/src/Shared/ITranslator.cs index eb9f90d0519..e3daab57216 100644 --- a/src/Shared/ITranslator.cs +++ b/src/Shared/ITranslator.cs @@ -237,11 +237,23 @@ BinaryWriter Writer /// The context to be translated. void Translate(ref BuildEventContext value); + /// + /// Translates . + /// + /// The to translate. + void Translate(ref FileAccessData fileAccessData); + /// /// Translates . /// /// The file accesses to translate. void Translate(ref List fileAccessDataList); + + /// + /// Translates . + /// + /// The to translate. + void Translate(ref ProcessData processData); #endif ///