Fix race condition in Exporters (#1729)
authorSung Yoon Whang <suwhang@microsoft.com>
Tue, 17 Nov 2020 04:22:15 +0000 (20:22 -0800)
committerGitHub <noreply@github.com>
Tue, 17 Nov 2020 04:22:15 +0000 (20:22 -0800)
src/Tools/dotnet-counters/Exporters/CSVExporter.cs
src/Tools/dotnet-counters/Exporters/JSONExporter.cs

index c02c09f4e1060572a46fca65b5863cf1b8d28818..9272039d040896ed0acb2c9b313086eb9cd77d17 100644 (file)
@@ -11,6 +11,7 @@ namespace Microsoft.Diagnostics.Tools.Counters.Exporters
 {
     class CSVExporter : ICounterRenderer
     {
+        private object _lock = new object(); // protects the StringBuilder instance.
         private string _output;
         private StringBuilder builder;
         private int flushLength = 10_000; // Arbitrary length to flush
@@ -36,8 +37,11 @@ namespace Microsoft.Diagnostics.Tools.Counters.Exporters
                 Console.WriteLine($"[Warning] {_output} already exists. This file will be overwritten.");
                 File.Delete(_output);
             }
-            builder = new StringBuilder();
-            builder.AppendLine("Timestamp,Provider,Counter Name,Counter Type,Mean/Increment");
+            lock (_lock)
+            {
+                builder = new StringBuilder();
+                builder.AppendLine("Timestamp,Provider,Counter Name,Counter Type,Mean/Increment");
+            }
         }
 
         public void EventPipeSourceConnected()
@@ -52,24 +56,32 @@ namespace Microsoft.Diagnostics.Tools.Counters.Exporters
 
         public void CounterPayloadReceived(string providerName, ICounterPayload payload, bool _)
         {
-            if (builder.Length > flushLength)
+            lock (_lock)
             {
-                File.AppendAllText(_output, builder.ToString());
-                builder.Clear();
-            }
+                if (builder.Length > flushLength)
+                {
+                    File.AppendAllText(_output, builder.ToString());
+                    builder.Clear();
+                }
 
-            builder
-                .Append(DateTime.UtcNow.ToString()).Append(',')
-                .Append(providerName).Append(',')
-                .Append(payload.GetDisplay()).Append(',')
-                .Append(payload.GetCounterType()).Append(',')
-                .Append(payload.GetValue().ToString(CultureInfo.InvariantCulture)).Append('\n');
+                builder
+                    .Append(DateTime.UtcNow.ToString()).Append(',')
+                    .Append(providerName).Append(',')
+                    .Append(payload.GetDisplay()).Append(',')
+                    .Append(payload.GetCounterType()).Append(',')
+                    .Append(payload.GetValue().ToString(CultureInfo.InvariantCulture)).Append('\n');
+            }
         }
 
         public void Stop()
         {
+            string outputString;
             // Append all the remaining text to the file.
-            File.AppendAllText(_output, builder.ToString());
+            lock (_lock)
+            {
+                outputString = builder.ToString();
+            }
+            File.AppendAllText(_output, outputString);
             Console.WriteLine("File saved to " + _output);
         }
     }
index dfc46c258bd5183371580cdb10183c37d7c96a0f..6592865cb7b90e75099f25809cb2bbbe2ca38a8a 100644 (file)
@@ -11,6 +11,7 @@ namespace Microsoft.Diagnostics.Tools.Counters.Exporters
 {
     class JSONExporter : ICounterRenderer
     {
+        private object _lock = new object();
         private string _output;
         private string _processName;
         private StringBuilder builder;
@@ -36,11 +37,14 @@ namespace Microsoft.Diagnostics.Tools.Counters.Exporters
                 File.Delete(_output);
             }
 
-            builder = new StringBuilder();
-            builder
-                .Append("{ \"TargetProcess\": \"").Append(_processName).Append("\", ")
-                .Append("\"StartTime\": \"").Append(DateTime.Now.ToString()).Append("\", ")
-                .Append("\"Events\": [");
+            lock (_lock)
+            {
+                builder = new StringBuilder();
+                builder
+                    .Append("{ \"TargetProcess\": \"").Append(_processName).Append("\", ")
+                    .Append("\"StartTime\": \"").Append(DateTime.Now.ToString()).Append("\", ")
+                    .Append("\"Events\": [");
+            }
         }
 
         public void EventPipeSourceConnected()
@@ -54,26 +58,32 @@ namespace Microsoft.Diagnostics.Tools.Counters.Exporters
 
         public void CounterPayloadReceived(string providerName, ICounterPayload payload, bool _)
         {
-            if (builder.Length > flushLength)
+            lock (_lock)
             {
-                File.AppendAllText(_output, builder.ToString());
-                builder.Clear();
-            }
+                if (builder.Length > flushLength)
+                {
+                    File.AppendAllText(_output, builder.ToString());
+                    builder.Clear();
+                }
 
-            builder
-                .Append("{ \"timestamp\": \"").Append(DateTime.Now.ToString("u")).Append("\", ")
-                .Append(" \"provider\": \"").Append(providerName).Append("\", ")
-                .Append(" \"name\": \"").Append(payload.GetDisplay()).Append("\", ")
-                .Append(" \"counterType\": \"").Append(payload.GetCounterType()).Append("\", ")
-                .Append(" \"value\": ").Append(payload.GetValue().ToString(CultureInfo.InvariantCulture)).Append(" },");
+                builder
+                    .Append("{ \"timestamp\": \"").Append(DateTime.Now.ToString("u")).Append("\", ")
+                    .Append(" \"provider\": \"").Append(providerName).Append("\", ")
+                    .Append(" \"name\": \"").Append(payload.GetDisplay()).Append("\", ")
+                    .Append(" \"counterType\": \"").Append(payload.GetCounterType()).Append("\", ")
+                    .Append(" \"value\": ").Append(payload.GetValue().ToString(CultureInfo.InvariantCulture)).Append(" },");
+            }
         }
 
         public void Stop()
         {
-            builder.Remove(builder.Length - 1, 1); // Remove the last comma to ensure valid JSON format.
-            builder.Append("]}");
-            // Append all the remaining text to the file.
-            File.AppendAllText(_output, builder.ToString());
+            lock (_lock)
+            {
+                builder.Remove(builder.Length - 1, 1); // Remove the last comma to ensure valid JSON format.
+                builder.Append("]}");
+                // Append all the remaining text to the file.
+                File.AppendAllText(_output, builder.ToString());
+            }
             Console.WriteLine("File saved to " + _output);
         }
     }