Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions src/TraceEvent/RegisteredTraceEventParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid)
Dictionary<string, string> enumIntern = new Dictionary<string, string>();
StringWriter enumLocalizations = new StringWriter();

// Track emitted string IDs to prevent duplicates in the stringTable
HashSet<string> emittedStringIds = new HashSet<string>();

// Any task names used so far
Dictionary<string, int> taskNames = new Dictionary<string, int>();
// Any es used so far
Expand Down Expand Up @@ -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(" <map value=\"0x{0:x}\" message=\"$(string.map_{1}{2})\"/>", value, enumName, valueName);
enumLocalizations.WriteLine(" <string id=\"map_{0}{1}\" value=\"{2}\"/>", enumName, valueName, valueName);
string stringId = $"map_{enumName}{valueName}";
if (emittedStringIds.Add(stringId))
{
enumLocalizations.WriteLine(" <string id=\"{0}\" value=\"{1}\"/>", stringId, valueName);
}
}
if (enumInfo->Flag == MAP_FLAGS.EVENTMAP_INFO_FLAG_MANIFEST_VALUEMAP)
{
Expand Down Expand Up @@ -453,7 +460,11 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid)
{
manifest.WriteLine(" <keyword name=\"{0}\" message=\"$(string.keyword_{1})\" mask=\"0x{2:x}\"/>",
keyValue.Value, keyValue.Value, keyValue.Key);
localizedStrings.WriteLine(" <string id=\"keyword_{0}\" value=\"{1}\"/>", keyValue.Value, keyValue.Value);
string stringId = $"keyword_{keyValue.Value}";
if (emittedStringIds.Add(stringId))
{
localizedStrings.WriteLine(" <string id=\"{0}\" value=\"{1}\"/>", stringId, keyValue.Value);
}
}
manifest.WriteLine(" </keywords>");
}
Expand All @@ -464,7 +475,11 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid)
var task = tasks[taskValue];
manifest.WriteLine(" <task name=\"{0}\" message=\"$(string.task_{1})\" value=\"{2}\"{3}>", task.Name, task.Name, taskValue,
task.Opcodes == null ? "/" : ""); // If no opcodes, terminate immediately.
localizedStrings.WriteLine(" <string id=\"task_{0}\" value=\"{1}\"/>", task.Name, task.Name);
string taskStringId = $"task_{task.Name}";
if (emittedStringIds.Add(taskStringId))
{
localizedStrings.WriteLine(" <string id=\"{0}\" value=\"{1}\"/>", taskStringId, task.Name);
}
if (task.Opcodes != null)
{
manifest.WriteLine(">");
Expand All @@ -473,7 +488,11 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid)
{
manifest.WriteLine(" <opcode name=\"{0}\" message=\"$(string.opcode_{1}{2})\" value=\"{3}\"/>",
keyValue.Value, task.Name, keyValue.Value, keyValue.Key);
localizedStrings.WriteLine(" <string id=\"opcode_{0}{1}\" value=\"{2}\"/>", task.Name, keyValue.Value, keyValue.Value);
string opcodeStringId = $"opcode_{task.Name}{keyValue.Value}";
if (emittedStringIds.Add(opcodeStringId))
{
localizedStrings.WriteLine(" <string id=\"{0}\" value=\"{1}\"/>", opcodeStringId, keyValue.Value);
}
}
manifest.WriteLine(" </opcodes>");
manifest.WriteLine(" </task>");
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}

/// <summary>
/// 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.
/// </summary>
[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: <string id="..." value="..."/>
var stringIdPattern = new Regex(@"<string\s+id=""([^""]+)""", RegexOptions.Compiled);
var matches = stringIdPattern.Matches(manifest);

var stringIds = new List<string>();
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);
}
}
}