Skip to content

Improve Server Logging#52572

Merged
jaredpar merged 4 commits intodotnet:mainfrom
jaredpar:log
Apr 14, 2021
Merged

Improve Server Logging#52572
jaredpar merged 4 commits intodotnet:mainfrom
jaredpar:log

Conversation

@jaredpar
Copy link
Copy Markdown
Member

The goal if this PR is to change the logging so that:

  1. It's easier to identify the process writing the message. The prefix
    for messages will be prefixed with either "VBCSCompiler" or "MSBuild
    ".
  2. Add a Guid to a logical build request that is included in many
    messages. This will make it easier to track requests as they travel
    between the MSBuild and VBCSCompiler processes.
  3. Ensure all failure states have an explicit log call

The motivation for this change is we are seeing the compiler server
crash in build correctness and source build legs. From the log it's hard
to see what is happening. The failure is essentially silent. This is an
attempt to get more data so we can see what is actually going on during
failure.

The goal if this PR is to change the logging so that:

1. It's easier to identify the process writing the message. The prefix
for messages will be prefixed with either "VBCSCompiler" or "MSBuild
<process id>".
1. Add a `Guid` to a logical build request that is included in many
messages. This will make it easier to track requests as they travel
between the MSBuild and VBCSCompiler processes.
1. Ensure all failure states have an explicit log call

The motivation for this change is we are seeing the compiler server
crash in build correctness and source build legs. From the log it's hard
to see what is happening. The failure is essentially silent. This is an
attempt to get more data so we can see what is actually going on during
failure.
@jaredpar jaredpar requested a review from a team as a code owner April 12, 2021 16:12
@jaredpar
Copy link
Copy Markdown
Member Author

@dotnet/roslyn-compiler PTAL

/// ----------------------------------------------------
/// Length Integer 4
/// ProtocolVersion Integer 4
/// RequestId Guid 16
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the early days of the compiler server the ProtocolVersion field was intended to be a versioning mechanism. Every time we changed the protocol we update this number and make sure that we maintain compatibility. Later on though we added the compiler hash to the build protocol and both sides reject messages where the hashes don't match. This effectively renders the ProtocolVersion field meaningless hence I've removed it as a part of adding RequestId in.

/// A value which can be used to identify this connection for logging purposes only. It has
/// no guarantee of uniqueness.
/// </summary>
string LoggingIdentifier { get; }
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This field was used to identify client connections for logging purposes in the server. Now that there is a Guid that tracks the connection in both client and server this just isn't needed anymore.

Guid? requestId = null)
{
ProtocolVersion = protocolVersion;
RequestId = requestId ?? Guid.Empty;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the times we'd expect the request id to be empty?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the build is invoked via test code. There isn't much value in tracking the requests there that I could see.


using var logger = new CompilerServerLogger();
return BuildClient.Run(args, RequestLanguage.CSharpCompile, Csc.Run, logger);
var requestId = Guid.NewGuid();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like the only time we aren't generating a new request ID per logger is in VBCSCompiler, but it doensn't seem to call any of the log-with-id methods anyway.

Would it make more sense to just have the logger make a new Guid when you create it, and have it append to the log messages, rather than making the clients of the logger responsible for tracking it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two types of IDs now:

  • ID of the instance of CompilerServer logger. This is generally a named instance which makes it easy for us to know which process is writing the log entry.
  • ID of an individual compiler request. This is generated at the MSBuild and command line compiler layer. This ID allows us to track a request, by simple text search in the log, as the request pings between the client and compiler server. Particularly it lets us track back a failure to the original request and lets us see where exactly the request failed.

@jaredpar jaredpar requested a review from a team as a code owner April 12, 2021 21:14
@jaredpar
Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

@jaredpar
Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

finally {
if ($enableDumps) {
$key = "HKLM:\\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps"
Remove-ItemProperty -Path $key -Name 'DumpType'
Copy link
Copy Markdown
Member

@jcouv jcouv Apr 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we remember the original values instead?
Also, I'm not sure what level of isolation we get in Azure. Do we risk impacting other builds by changing global/machine settings? #Closed

Copy link
Copy Markdown
Member Author

@jaredpar jaredpar Apr 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The azure machines are assumed to reset items like this between runs. This pattern of set then unset is used in other areas like RunTests which we do in CI. #Closed

Comment thread src/Compilers/Core/CommandLine/BuildProtocol.cs Outdated
Comment thread src/Compilers/Core/CommandLine/BuildProtocol.cs Outdated
try
{
using var logger = new CompilerServerLogger();
using var logger = new CompilerServerLogger($"MSBuild {Process.GetCurrentProcess().Id}");
Copy link
Copy Markdown
Member

@jcouv jcouv Apr 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to include the process ID into the identifier string, since CompilerServerLogger already captures the process ID? #Closed

Copy link
Copy Markdown
Member Author

@jaredpar jaredpar Apr 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CompilerServerLogger only uses the process ID when generating the name for the log file on disk, it doesn't actually use that ID within the log. The use of the process ID here is important because there are multiple MSBuild process instances in the build and we want to differentiate between them. Unlike VBSCompiler of which there is one hence not a lot of value in dumping that in the log. #Closed

// It is okay for this call to fail. Errors will be reflected in the
// IsConnected property which will be read on the next iteration of the
logger.LogException(e, $"Error poking pipe {identifier}.");
logger.LogException(e, $"Error poking pipe {requestId}.");
Copy link
Copy Markdown
Member

@jcouv jcouv Apr 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: would it be worthwhile to have the logger handle the requestId (add it to the log) so that we don't have to pass it around in so many places that already have the logger? Or do the requestId and logger have different lifetimes? #Closed

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mm... I see that for VBCSCompiler we don't create the requestId at the same time that we create the logger, so my proposal probably wouldn't work.


In reply to: 612732135 [](ancestors = 612732135)

Copy link
Copy Markdown
Member Author

@jaredpar jaredpar Apr 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That wouldn't work though. The intent of the request id is to track a compilation request across processes. The request originates in MSBuild, is passed to VBCSCompiler, then back to MSBuild once the request completes. There is no way to generate this before hand on both sides of the pipe. #Closed

Comment thread src/Compilers/Server/VBCSCompiler/VBCSCompiler.cs Outdated
}
catch (Exception ex)
{
Logger.LogException(ex, $"Running compilation for {request.RequestId}");
Copy link
Copy Markdown
Member

@jcouv jcouv Apr 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the message correct? #Closed

Copy link
Copy Markdown
Member Author

@jaredpar jaredpar Apr 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. The second argument to LogException describes the operation which was occurring when the exception was thrown. #Closed

@jcouv jcouv self-assigned this Apr 13, 2021
Copy link
Copy Markdown
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with review pass (iteration 3)

@jaredpar
Copy link
Copy Markdown
Member Author

Based on the review comments it seems I haven't done a good job of explaining what I'm trying to accomplish here.

A compilation event is a muli-process problem for C# / VB. The MSBuild process (of which there are multiple) begins the compilation event by constructing a message that it sends to the VBCSCompiler process (of which there is one). The compiler receives the request, kicks off a Thread and then processes the compilation from there. Upon completion it constructs a response and sends it back to MSBuild.

The log file contains messages from all these different MSBuild processes and all of the threads within VBCSCompiler interleaved. The only identifying marker for message in the log is the process and thread ID. That means reading a log file requires you to figure out which PID maps to which process (MSBuild or VBCSCompiler) and which TID within specific PID (the VBCSCompiler) maps to a given compilation event. That doesn't help a lot thouh though with the core problem which is typically trying to find the compilation that failed then look at all of the log entries for that compilation. Basically filter the log down to all of the events for a compilation and see where it went wrong.

This PR attempts to address that by doing the following:

  • Replace the PID in the log file with a concrete name: either MSBuild <PID>, csc / vbc <PID> or VBCSCompiler. Now at a glance can tell which process wrote the line
  • Use a request id (simple Guid.NewGuid()) to track a compilation event across the processes. It is created by MSBuild, attached to the protocol and then every process puts that Guid into messages when they're writing a log event specific to a request. That makes it incredibly easy to filter out the log file to the events for a given process

Below is a sample of how the log file looks after this change. Notice that c8d7ad91-490f-420c-9a96-f3881a8804ca is the request for a given compilation. I can easily just search for that in the log file, read through the messages now and easily see ever hop along the way to understand how this event was processed.

ID=MSBuild 9120 TID=5: Begin writing request for c8d7ad91-490f-420c-9a96-f3881a8804ca
ID=MSBuild 9120 TID=5: End writing request for c8d7ad91-490f-420c-9a96-f3881a8804ca
ID=MSBuild 9120 TID=5: Begin reading response for c8d7ad91-490f-420c-9a96-f3881a8804ca
ID=VCBCSCompiler TID=7: Received request c8d7ad91-490f-420c-9a96-f3881a8804ca of type Microsoft.CodeAnalysis.CommandLine.BuildRequest
ID=VCBCSCompiler TID=41: 
Run Compilation for c8d7ad91-490f-420c-9a96-f3881a8804ca
  Language = C#
  CurrentDirectory = 'D:\workspace\_work\1\s\src\Compilers\Core\Portable
  LIB = ''
ID=VCBCSCompiler TID=41: Begin Analyzer Consistency Check
ID=VCBCSCompiler TID=41: End Analyzer Consistency Check
ID=VCBCSCompiler TID=41: Begin c8d7ad91-490f-420c-9a96-f3881a8804ca C# compiler run
ID=VCBCSCompiler TID=41: End c8d7ad91-490f-420c-9a96-f3881a8804ca C# compiler run
Return code: 0
Output:
Microsoft (R) Visual C# Compiler version 3.10.0-ci.21212.1 (<developer build>)
Copyright (C) Microsoft Corporation. All rights reserved.


ID=VCBCSCompiler TID=7: Writing Completed response for c8d7ad91-490f-420c-9a96-f3881a8804ca
ID=MSBuild 9120 TID=14: End reading response for c8d7ad91-490f-420c-9a96-f3881a8804ca
ID=MSBuild 9120 TID=8: CompilerServer: server - server processed compilation - c8d7ad91-490f-420c-9a96-f3881a8804ca
ID=VCBCSCompiler TID=1: Client request completed
ID=MSBuild 5956 TID=3: End reading response for 25074eec-9cf6-4fd9-b5f5-c9dbfd5aea78
ID=MSBuild 8408 TID=24: End reading response for 59d13f52-ec71-43df-b120-3a061af351c8
ID=MSBuild 5956 TID=3: Error Error: 'EndOfStreamException' 'Reached end of stream before end of read.' occurred during 'Reading response for 25074eec-9cf6-4fd9-b5f5-c9dbfd5aea78'
Stack trace:
   at Microsoft.CodeAnalysis.CommandLine.BuildProtocolConstants.<ReadAllAsync>d__4.MoveNext() in /_/src/Compilers/Core/CommandLine/BuildProtocol.cs:line 623
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
   at Microsoft.CodeAnalysis.CommandLine.BuildResponse.<ReadAsync>d__5.MoveNext() in /_/src/Compilers/Core/CommandLine/BuildProtocol.cs:line 340
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
   at Microsoft.CodeAnalysis.CommandLine.BuildServerConnection.<TryCompileAsync>d__6.MoveNext() in /_/src/Compilers/Shared/BuildServerConnection.cs:line 289

public static int Main(string[] args)
{
using var logger = new CompilerServerLogger();
using var logger = new CompilerServerLogger("VBCSCompiler");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"VBCSCompiler"

Perhaps use nameof(VBCSCompiler).

Copy link
Copy Markdown
Member Author

@jaredpar jaredpar Apr 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I did that though how would I introduce typos into my PRs? #Closed

Copy link
Copy Markdown
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM Thanks (iteration 4)

@jaredpar jaredpar merged commit b77d3bb into dotnet:main Apr 14, 2021
@ghost ghost added this to the Next milestone Apr 14, 2021
@jaredpar jaredpar deleted the log branch April 14, 2021 16:59
@dibarbet dibarbet modified the milestones: Next, 16.10.P3 Apr 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants