-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Text-Based Performance Log #5861
Conversation
src/Framework/MSBuildEventSource.cs
Outdated
@@ -244,14 +249,14 @@ public void ExpandGlobStop(string rootDirectory, string glob, string excludedPat | |||
/// Call this method to notify listeners of timing related to loading an XmlDocumentWithLocation from a path. | |||
/// <param name="fullPath">Path to the document to load.</param> | |||
/// </summary> | |||
[Event(29)] | |||
[Event(29, Keywords = Keywords.PerformanceLog)] |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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:
- Gives a coarse view of what's happening, and time spans for those actions.
- 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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
@@ -85,6 +87,15 @@ public static string RunProcessAndGetOutput(string process, string parameters, o | |||
UseShellExecute = false, | |||
Arguments = parameters | |||
}; | |||
|
|||
if(environmentVariables != null) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
// 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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
Assert.NotEmpty(files); | |
files.ShouldNotBeEmpty(); |
And also convert the rest to Shouldly?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Directory.CreateDirectory(perfLogDir); | ||
Directory.CreateDirectory(projectDir); | ||
|
||
string content = ObjectModelHelpers.CleanupFileContents("<Project ToolsVersion='msbuilddefaulttoolsversion' xmlns='msbuildnamespace'><Target Name='t'><Warning Text='[A=$(A)]'/></Target></Project>"); |
There was a problem hiding this comment.
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?
bool successfulExit; | ||
RunnerUtilities.ExecMSBuild(RunnerUtilities.PathToCurrentlyRunningMsBuildExe, msbuildParameters, out successfulExit, environmentVariables: environmentVariables); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tiny nit:
bool successfulExit; | |
RunnerUtilities.ExecMSBuild(RunnerUtilities.PathToCurrentlyRunningMsBuildExe, msbuildParameters, out successfulExit, environmentVariables: environmentVariables); | |
RunnerUtilities.ExecMSBuild(RunnerUtilities.PathToCurrentlyRunningMsBuildExe, msbuildParameters, out bool successfulExit, environmentVariables: environmentVariables); |
public void TestPerfLogEnabledProducedLogFile() | ||
{ | ||
// Setup perf log. | ||
string perfLogDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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>);
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 "); |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
public void TestPerfLogEnabledProducedLogFile() | ||
{ | ||
// Setup perf log. | ||
string perfLogDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")); |
There was a problem hiding this comment.
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.
Thank you both for the reviews. I think I've covered all of the feedback. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
@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. |
@marcpopMSFT this is MSBuild :) it's in the 2xx branch already |
@rainersigwald DOH! I was just in the SDK repo and got my wires crossed. Carry on. |
Implements a text-based performance log scheme that writes coarse-grained performance logs:
DOTNET_PERFLOG_DIR
. This is currently enabled when a user opts-in via a CLI command, but can be set adhoc as well.MSBuildEventSource
so that the events can be consumed through other profilers, such as those that use ETW.cc: @marcpopMSFT, @davidfowl, @DamianEdwards, @bwadswor