Skip to content

Implement Text-Based Performance Log#5861

Merged
brianrob merged 9 commits intodotnet:masterfrom
brianrob:perf-log
Nov 6, 2020
Merged

Implement Text-Based Performance Log#5861
brianrob merged 9 commits intodotnet:masterfrom
brianrob:perf-log

Conversation

@brianrob
Copy link
Copy Markdown
Member

@brianrob brianrob commented Nov 5, 2020

Implements a text-based performance log scheme that writes coarse-grained performance logs:

  • No logs are written unless the user opts-in by setting the log location via the environment variable DOTNET_PERFLOG_DIR. This is currently enabled when a user opts-in via a CLI command, but can be set adhoc as well.
  • Most logs will be produced at the request of CLI commands. The lifetime of these logs is managed by the CLI - see Implement Text-Based Performance Log sdk#14409 for details.
  • Data in the logs are emitted via MSBuildEventSource so that the events can be consumed through other profilers, such as those that use ETW.

cc: @marcpopMSFT, @davidfowl, @DamianEdwards, @bwadswor

Comment thread src/Framework/MSBuildEventSource.cs Outdated
/// <param name="fullPath">Path to the document to load.</param>
/// </summary>
[Event(29)]
[Event(29, Keywords = Keywords.PerformanceLog)]
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.

How did you choose which events to add this to?

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.

What I chose is based on two criteria:

  1. Gives a coarse view of what's happening, and time spans for those actions.
  2. Keeps the rate of events relatively low so that the impact on performance isn't too much.

If you think there are other events that should be included, I'm definitely open to that - just being careful with the perf overhead.

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.

Maybe RAROverallStart/Stop, BuildStart/Stop, and BuildProjectStart/Stop? I'd put those higher than LoadDocument, Parse, or MSBuildExe.

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.

Done. Thanks for the feedback on this.

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.

For command line scenarios I suspect Build and MSBuildExe will be fairly redundant: all MSBuild.exe does is start a build. But let's leave it in for v1.

Comment thread src/UnitTests.Shared/RunnerUtilities.cs Outdated
Arguments = parameters
};

if(environmentVariables != null)
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.

We've worked around the inability to set environment variables in a different way elsewhere. (See here.) Do you think it would be reasonable to combine them?

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.

I imagine so. Are you proposing a separate class, or moving the usage in XMake_Tests to use what I've done here, or something else?

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.

I'd do the latter or move what's in XMake_Tests here and call it. I don't have a strong preference between them, but I'd like it to be in one spot.

Comment thread src/MSBuild.UnitTests/PerfLog_Tests.cs Outdated
// It's most important to ensure that at least one file shows up because any others that show up will be there because MSBuild properly
// enabled this functionality.
string[] files = Directory.GetFiles(perfLogDir, "perf-*.log");
Assert.NotEmpty(files);
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.

nit:

Suggested change
Assert.NotEmpty(files);
files.ShouldNotBeEmpty();

And also convert the rest to Shouldly?

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.

Do you think you should also check to make sure those files didn't exist before? I don't think GetTempPath returns a truly random path each time.

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.

Updated the rest to Shoudly. Thanks for pointing this out.

With regard to the directory, GetTempPath doesn't return a random path, but the test creates a random GUID directory to ensure that the directory is empty, and then creates it, which would result in a thrown exception if it already existed.

Comment thread src/MSBuild.UnitTests/PerfLog_Tests.cs Outdated
Directory.CreateDirectory(perfLogDir);
Directory.CreateDirectory(projectDir);

string content = ObjectModelHelpers.CleanupFileContents("<Project ToolsVersion='msbuilddefaulttoolsversion' xmlns='msbuildnamespace'><Target Name='t'><Warning Text='[A=$(A)]'/></Target></Project>");
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.

nit:
split across multiple lines?

Comment thread src/MSBuild.UnitTests/PerfLog_Tests.cs Outdated
Comment on lines +63 to +64
bool successfulExit;
RunnerUtilities.ExecMSBuild(RunnerUtilities.PathToCurrentlyRunningMsBuildExe, msbuildParameters, out successfulExit, environmentVariables: environmentVariables);
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.

tiny nit:

Suggested change
bool successfulExit;
RunnerUtilities.ExecMSBuild(RunnerUtilities.PathToCurrentlyRunningMsBuildExe, msbuildParameters, out successfulExit, environmentVariables: environmentVariables);
RunnerUtilities.ExecMSBuild(RunnerUtilities.PathToCurrentlyRunningMsBuildExe, msbuildParameters, out bool successfulExit, environmentVariables: environmentVariables);

Comment thread src/MSBuild.UnitTests/PerfLog_Tests.cs Outdated
public void TestPerfLogEnabledProducedLogFile()
{
// Setup perf log.
string perfLogDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
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.

I think all this would be easier with a TestEnvironment.

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.

Can you please point me to an example that I should follow?

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.

I like this example, but I'm a little biased. You can also use env.SetEnvironmentVariable(<name>, <value>);.

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.

Using the test environment should make this simpler (since it handles cleanup), and allow avoiding the RunnerUtilities changes.

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.

Yes, this definitely cleaned things up.

s_builder.Clear();
}

s_builder.Append($"[{DateTime.UtcNow.ToString("o")}] Event={eventData.EventSource.Name}/{eventData.EventName} ProcessID={_processIDStr} ThreadID={System.Threading.Thread.CurrentThread.ManagedThreadId}\t ");
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.

There's an AppendFormat method for StringBuilder. I remember having tested whether it was faster than this, but I don't remember the answer. I think .Append over and over was fastest but also ugliest.


lock (this)
{
if (_writer != null)
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.

When is _writer null, and should we error?

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.

My goal is to make this log as inobtrusive as possible. So if something goes wrong, I want the log to fail silently and/or just drop data rather than error. Essentially, preserve correctness.

{
if (eventListener != null)
{
eventListener.Dispose();
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.

if (eventListener != null && eventListener threw an exception) shouldn't we return null or throw?

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.

Yes, that's a good catch. Thanks.

Comment thread src/MSBuild/PerformanceLogEventListener.cs
Comment thread src/MSBuild.UnitTests/PerfLog_Tests.cs Outdated
public void TestPerfLogEnabledProducedLogFile()
{
// Setup perf log.
string perfLogDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
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.

Using the test environment should make this simpler (since it handles cleanup), and allow avoiding the RunnerUtilities changes.

@brianrob
Copy link
Copy Markdown
Member Author

brianrob commented Nov 6, 2020

Thank you both for the reviews. I think I've covered all of the feedback.

Copy link
Copy Markdown
Contributor

@Forgind Forgind left a comment

Choose a reason for hiding this comment

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

LGTM!

@brianrob brianrob merged commit 7452552 into dotnet:master Nov 6, 2020
@brianrob brianrob deleted the perf-log branch November 6, 2020 21:59
@marcpopMSFT
Copy link
Copy Markdown
Member

@brianrob Note that you checked this in for 6.0. If you want it included in 5.0.2xx for earlier testing, it'll need to go in that branch as well.

@rainersigwald
Copy link
Copy Markdown
Member

@marcpopMSFT this is MSBuild :) it's in the 2xx branch already

@marcpopMSFT
Copy link
Copy Markdown
Member

@rainersigwald DOH! I was just in the SDK repo and got my wires crossed. Carry on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants