diff --git a/src/TraceEvent/RegisteredTraceEventParser.cs b/src/TraceEvent/RegisteredTraceEventParser.cs index eedf36ade..7ed71299c 100644 --- a/src/TraceEvent/RegisteredTraceEventParser.cs +++ b/src/TraceEvent/RegisteredTraceEventParser.cs @@ -105,6 +105,9 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid) Dictionary enumIntern = new Dictionary(); StringWriter enumLocalizations = new StringWriter(); + // Track emitted string IDs to prevent duplicates in the stringTable + HashSet emittedStringIds = new HashSet(); + // Any task names used so far Dictionary taskNames = new Dictionary(); // Any es used so far @@ -374,7 +377,11 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid) int value = mapEntries[k].Value; string valueName = new string((char*)(&enumBuffer[mapEntries[k].NameOffset])).Trim(); enumWriter.WriteLine(" ", value, enumName, valueName); - enumLocalizations.WriteLine(" ", enumName, valueName, valueName); + string stringId = $"map_{enumName}{valueName}"; + if (emittedStringIds.Add(stringId)) + { + enumLocalizations.WriteLine(" ", stringId, valueName); + } } if (enumInfo->Flag == MAP_FLAGS.EVENTMAP_INFO_FLAG_MANIFEST_VALUEMAP) { @@ -453,7 +460,11 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid) { manifest.WriteLine(" ", keyValue.Value, keyValue.Value, keyValue.Key); - localizedStrings.WriteLine(" ", keyValue.Value, keyValue.Value); + string stringId = $"keyword_{keyValue.Value}"; + if (emittedStringIds.Add(stringId)) + { + localizedStrings.WriteLine(" ", stringId, keyValue.Value); + } } manifest.WriteLine(" "); } @@ -464,7 +475,11 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid) var task = tasks[taskValue]; manifest.WriteLine(" ", task.Name, task.Name, taskValue, task.Opcodes == null ? "/" : ""); // If no opcodes, terminate immediately. - localizedStrings.WriteLine(" ", task.Name, task.Name); + string taskStringId = $"task_{task.Name}"; + if (emittedStringIds.Add(taskStringId)) + { + localizedStrings.WriteLine(" ", taskStringId, task.Name); + } if (task.Opcodes != null) { manifest.WriteLine(">"); @@ -473,7 +488,11 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid) { manifest.WriteLine(" ", keyValue.Value, task.Name, keyValue.Value, keyValue.Key); - localizedStrings.WriteLine(" ", task.Name, keyValue.Value, keyValue.Value); + string opcodeStringId = $"opcode_{task.Name}{keyValue.Value}"; + if (emittedStringIds.Add(opcodeStringId)) + { + localizedStrings.WriteLine(" ", opcodeStringId, keyValue.Value); + } } manifest.WriteLine(" "); manifest.WriteLine(" "); diff --git a/src/TraceEvent/TraceEvent.Tests/Parsing/RegisteredTraceEventParserTests.cs b/src/TraceEvent/TraceEvent.Tests/Parsing/RegisteredTraceEventParserTests.cs new file mode 100644 index 000000000..d947146f4 --- /dev/null +++ b/src/TraceEvent/TraceEvent.Tests/Parsing/RegisteredTraceEventParserTests.cs @@ -0,0 +1,70 @@ +using Microsoft.Diagnostics.Tracing.Parsers; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Xunit; +using Xunit.Abstractions; + +namespace TraceEventTests +{ + public class RegisteredTraceEventParserTests + { + private readonly ITestOutputHelper _output; + + public RegisteredTraceEventParserTests(ITestOutputHelper output) + { + _output = output; + } + + /// + /// Test that GetManifestForRegisteredProvider does not produce duplicate string IDs in the stringTable. + /// This test uses the Microsoft-Windows-DotNETRuntime provider which is known to exist on all Windows machines. + /// + [WindowsFact] + public void GetManifestForRegisteredProvider_NoDuplicateStringTableEntries() + { + // Microsoft-Windows-DotNETRuntime is a well-known provider that exists on all Windows machines + const string providerName = "Microsoft-Windows-DotNETRuntime"; + + // Get the manifest for the provider + string manifest = RegisteredTraceEventParser.GetManifestForRegisteredProvider(providerName); + + Assert.NotNull(manifest); + Assert.NotEmpty(manifest); + + _output.WriteLine($"Generated manifest for {providerName} (length: {manifest.Length} chars)"); + + // Extract all string IDs from the manifest's stringTable + // The format is: + var stringIdPattern = new Regex(@"(); + foreach (Match match in matches) + { + stringIds.Add(match.Groups[1].Value); + } + + _output.WriteLine($"Found {stringIds.Count} string entries in stringTable"); + + // Check for duplicates + var duplicates = stringIds + .GroupBy(id => id) + .Where(g => g.Count() > 1) + .Select(g => new { Id = g.Key, Count = g.Count() }) + .ToList(); + + if (duplicates.Any()) + { + _output.WriteLine($"Found {duplicates.Count} duplicate string IDs:"); + foreach (var dup in duplicates) + { + _output.WriteLine($" '{dup.Id}' appears {dup.Count} times"); + } + } + + // Assert no duplicates exist + Assert.Empty(duplicates); + } + } +}