Source/Tx.Windows.TypeGeneration/TmfParser.cs (315 lines of code) (raw):

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; using System.Text.RegularExpressions; using Microsoft.CSharp; namespace Tx.Windows { public class TmfParser { private static readonly Regex _header = new Regex(@"^(\S*) (\S*) //.*SRC=(\S+)"); private static readonly Regex _field = new Regex("^(.*), (\\S*)"); // TMF files from Lync contain extra token, compared to TMF-s from SCOM private static readonly Regex _eventHeader1 = new Regex("^#typev\\s+(\\S*)\\s+(\\d*)\\s+\"(.*)\"\\s+//.*FUNC=(\\S*)"); private static readonly Regex _eventHeader2 = new Regex("^#typev\\s+(\\S*)\\s+(\\S*)\\s+(\\d*)\\s+\"(.*)\"\\s+//.*FUNC=(\\S*)"); private static readonly CSharpCodeProvider _provider = new CSharpCodeProvider(); private static readonly SortedSet<string> _namespacesEmitted = new SortedSet<string>(); private static readonly Dictionary<string, string> _typeMap = new Dictionary<string, string> { {"ItemChar", "win:UInt8"}, {"ItemUChar", "win:UInt8"}, {"ItemCharShort", "win:UInt8"}, {"ItemCharSign", "win:UInt8"}, {"ItemShort", "win:Int16"}, {"ItemUShort", "win:UInt16"}, {"ItemLong", "win:Int32"}, {"ItemULong", "win:UInt32"}, {"ItemULongX", "win:UInt32"}, {"ItemLongLong", "win:Int64"}, {"ItemULongLong", "win:UInt64"}, {"ItemLongLongX", "win:UInt64"}, {"ItemLongLongXX", "win:UInt64"}, {"ItemLongLongO", "win:UInt64"}, {"ItemString", "win:AnsiString"}, {"ItemWString", "win:UnicodeString"}, {"ItemRString", "win:AnsiString"}, {"ItemRWString", "win:UnicodeString"}, {"ItemPString", "win:AnsiStringPref"}, {"ItemPWString", "win:UnicodeStringPref"}, {"ItemPII", "win:UnicodeString"}, {"ItemDSString", "win:AnsiStringPref"}, {"ItemDSWString", "win:UnicodeStringPref"}, {"ItemSid", "win:SID"}, {"ItemChar4", null}, {"ItemIPAddr", "win:UInt32"}, {"ItemIPV6Addr", null}, {"ItemMACAddr", "win:GUID"}, {"ItemPort", "win:UInt16"}, {"ItemMLString", "win:AnsiStringPref"}, {"ItemNWString", "win:UnicodeString"}, {"ItemPtr", "win:UInt64"}, {"ItemListLong", "win:Int32"}, {"ItemListShort", "win:Int16"}, {"ItemListwin:UInt8", "win:Int8"}, {"ItemNTerror", null}, {"ItemMerror", null}, {"ItemTimestamp", "win:UInt64"}, {"ItemTimeStamp", "win:UInt64"}, // Wow do we really see both? {"ItemGuid", "win:GUID"}, {"ItemNTSTATUS", "win:UInt16"}, {"ItemWINERROR", "win:UInt16"}, {"ItemNETEVENT", null}, {"ItemWaitTime", null}, {"ItemTimeDelta", null}, {"ItemSetLong", null}, {"ItemSetShort", null}, {"ItemSetwin:UInt8", null}, {"ItemDouble", "win:Double"}, {"ItemHRESULT", "win:UInt16"}, {"ItemCharHidden", "win:UInt8"}, {"ItemWChar", "win.UInt16"}, {"ItemHexDump", null}, {"ItemEventLog", null}, {"ItemSRB", null}, {"ItemSenseData", null}, {"ItemEnum", "win:Int32"}, {"ItemResource", null}, {"ItemCLSID", "win:GUID"}, {"ItemIID", "win:GUID"}, {"ItemLIBID", "win:GUID"}, {"ItemSockAddr", null}, {"ItemKSid", null}, {"ItemCWString", null}, {"ItemNStrings", null}, {"ItemFQDN", "win:UnicodeString"}, {"ItemURI", "win:UnicodeString"}, {"ItemURIums", "win:UnicodeString"}, {"ItemE164ums", "win:UnicodeString"}, {"ItemIP", "win:UnicodeString"}, {"ItemHOST", "win:UnicodeString"}, {"ItemListLong(false,true)", "win:UnicodeString"}, }; private readonly string _code; private readonly string _componentName; private readonly string _providerId; private readonly TextReader _reader; private readonly StringBuilder _sb; private readonly string _srcfile; private int _classCounter; private SortedSet<string> _classesEmitted; private SortedSet<string> _fieldsEmitted; private string _function; private TmfParser(string tmfFile) { _reader = File.OpenText(tmfFile); _reader.ReadLine(); //PDB: e:\lcsrgs.obj.amd64chk\rgs\dev\...\microsoft.rtc.rgs.clients.pdb _reader.ReadLine(); //PDB: Last Updated :2012-5-4:12:51:1:778 (UTC) [ManagedWPP] string line = _reader.ReadLine(); Match m = _header.Match(line); if (!m.Success) throw new Exception("failed to match the header line in file " + tmfFile); // the expected format is like // f73bbb29-e2f0-93e7-ee22-e396f8fe1570 RgsClientsLib // SRC=matchmakinglocator.cs MJ= MN= _providerId = m.Groups[1].Value; _componentName = m.Groups[2].Value; _srcfile = m.Groups[3].Value .Replace('-', '_') .Replace('.', '_'); _sb = new StringBuilder( @"// // This code was generated by EtwEventTypeGen.exe // using System; "); _sb.Append("namespace Microsoft.Etw."); _sb.Append(_provider.CreateValidIdentifier(_componentName)); _sb.Append('.'); _sb.AppendLine(_srcfile.Replace('.', '_')); _sb.AppendLine("{"); while (true) { if (!ReadEvent()) break; } _sb.AppendLine(" }"); _sb.AppendLine("}"); _code = _sb.ToString(); } public string Code { get { return _code; } } public static string Parse(string tmfFile) { var p = new TmfParser(tmfFile); return p.Code; } private bool ReadEvent() { string fileLine; string lineNumber; string opcode; string function; string line = _reader.ReadLine(); if (line == null) return false; while (line.StartsWith("#enumv")) { ReadEnum(); line = _reader.ReadLine(); if (line == null) return false; } if (line.StartsWith("// PDB")) return false; // looks like some TMFs contain the same information over and over again from different PDBs while (true) { string more = _reader.ReadLine(); if (more == "{") break; line += " " + more; } Match m = _eventHeader1.Match(line); if (m.Success) { fileLine = m.Groups[1].Value; lineNumber = fileLine.Substring(_srcfile.Length); opcode = m.Groups[2].Value; function = CreateIdentifier(m.Groups[4].Value); EmitClass(function, lineNumber, opcode); } else { m = _eventHeader2.Match(line); if (!m.Success) throw new Exception("the TMF format does not match one of the two supported formats"); fileLine = m.Groups[1].Value; lineNumber = fileLine.Substring(_srcfile.Length); opcode = m.Groups[3].Value; function = CreateIdentifier(m.Groups[5].Value); EmitClass(function, lineNumber, opcode); } return true; } private void EmitClass(string function, string lineNumber, string opcode) { EmitNamespaceIfChanged(function); if (_classCounter++ > 0) _sb.AppendLine(); _sb.AppendFormat(" [ManifestEvent(\"{0}\", {1})]", _providerId, opcode); _sb.AppendLine(); _sb.Append(" class Line"); _sb.AppendLine(CreateUniqueClassName(lineNumber)); _sb.AppendLine(" {"); int fieldIndex = 0; _fieldsEmitted = new SortedSet<string>(); while (true) { string line = _reader.ReadLine(); if (line == "}") break; EmitField(line, fieldIndex++); } _sb.AppendLine(" }"); } private void EmitNamespaceIfChanged(string function) { if (function != _function) { if (_function != null) { _sb.AppendLine(" }"); _sb.AppendLine(); } _function = CreateUniquNamespaceName(function); _sb.Append(" namespace "); _sb.AppendLine(_function); _sb.AppendLine(" {"); _classCounter = 0; _classesEmitted = new SortedSet<string>(); } } private void EmitField(string line, int fieldIndex) { Match m = _field.Match(line); if (!m.Success) throw new Exception("field definition does not match expected format: " + line); string name = CreateUniqieFieldName(m.Groups[1].Value, fieldIndex); string type = m.Groups[2].Value; if (fieldIndex > 0) _sb.AppendLine(); string manifestType; if (!_typeMap.TryGetValue(type, out manifestType) || manifestType == null) { manifestType = "win:UnicodeString"; } _sb.AppendFormat(" [EventField(0, {0}, \"{1}\")]", fieldIndex, manifestType); _sb.AppendLine(); string cShapType = ManifestParser.CleanType(manifestType); _sb.AppendFormat(" public {0} {1}", cShapType, name); _sb.AppendLine(" { get; set; }"); } private string CreateIdentifier(string s) { char[] chars = s.ToCharArray(); for (int i = 0; i < chars.Length; i++) { if (!Char.IsLetterOrDigit(chars[i])) chars[i] = ' '; } string name = new string(chars).Replace(" ", ""); if (name.Length > 0 && !Char.IsLetter(name[0])) name = "_" + name; return _provider.CreateValidIdentifier(name); } private string CreateUniqueClassName(string name) { string uniqueName = name; int counter = 1; while (_classesEmitted.Contains(uniqueName)) { uniqueName = name + "_" + counter.ToString(CultureInfo.InvariantCulture); counter++; } _classesEmitted.Add(uniqueName); return uniqueName; } private string CreateUniquNamespaceName(string name) { string uniqueName = name; int counter = 1; while (_namespacesEmitted.Contains(uniqueName)) { uniqueName = name + "_" + counter.ToString(CultureInfo.InvariantCulture); counter++; } _namespacesEmitted.Add(uniqueName); return uniqueName; } private string CreateUniqieFieldName(string s, int fieldIndex) { string name = CreateIdentifier(s); if (name == "") return "field" + fieldIndex.ToString(CultureInfo.InvariantCulture); if (name.Length > 30) name = name.Substring(0, 30); string uniqueName = name; int counter = 1; while (_fieldsEmitted.Contains(uniqueName)) { uniqueName = name + counter.ToString(CultureInfo.InvariantCulture); counter++; } _fieldsEmitted.Add(uniqueName); return uniqueName; } private void ReadEnum() { string line; do { line = _reader.ReadLine(); } while (line != "}"); } } }