From 402ee798ba21c74c8d9bc067e3ccf37f57f070c0 Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Thu, 3 Jul 2025 00:20:01 -0700 Subject: [PATCH 1/6] Solve simple serialization issues Fixes up simple annotation issues and APIs that have an easy AOT-safe alternative. --- Nuget.config => nuget.config | 0 src/FastSerialization/FastSerialization.cs | 479 +++++------ .../FastSerialization.csproj | 9 +- .../MemoryMappedFileStreamReader.cs | 6 +- .../AutomatedAnalysis/AnalyzerResolver.cs | 1 + .../DirectoryAnalyzerResolver.cs | 4 +- .../AutomatedAnalysis/IAnalyzerProvider.cs | 5 +- src/TraceEvent/Ctf/CtfChannel.cs | 18 +- src/TraceEvent/Ctf/CtfMetadataLegacyParser.cs | 2 +- src/TraceEvent/DynamicTraceEventParser.cs | 295 +++---- src/TraceEvent/ETWKernelControl.cs | 57 +- src/TraceEvent/ETWReloggerTraceEventSource.cs | 81 +- src/TraceEvent/NativeDlls.cs | 10 +- .../UniversalSystemTraceEventParser.cs | 55 +- src/TraceEvent/Symbols/NativeSymbolModule.cs | 261 +++--- src/TraceEvent/TraceEvent.cs | 806 +++++++++--------- src/TraceEvent/TraceEvent.csproj | 11 +- src/TraceEvent/TraceEventSession.cs | 31 +- src/TraceEvent/TraceLog.cs | 11 +- .../Utilities/WindowsDeviceToVolumeMap.cs | 6 +- src/TraceEvent/WPPTraceEventParser.cs | 37 +- 21 files changed, 1143 insertions(+), 1042 deletions(-) rename Nuget.config => nuget.config (100%) diff --git a/Nuget.config b/nuget.config similarity index 100% rename from Nuget.config rename to nuget.config diff --git a/src/FastSerialization/FastSerialization.cs b/src/FastSerialization/FastSerialization.cs index bcc2e4c5e..d82c44245 100644 --- a/src/FastSerialization/FastSerialization.cs +++ b/src/FastSerialization/FastSerialization.cs @@ -1,12 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. /* This file is best viewed using outline mode (Ctrl-M Ctrl-O) */ // This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in. -// It is available from http://www.codeplex.com/hyperAddin +// It is available from http://www.codeplex.com/hyperAddin /* If you uncomment this line, log.serialize.xml and log.deserialize.xml are created, which allow debugging */ // #define DEBUG_SERIALIZE using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; // For StringBuilder. @@ -14,7 +15,7 @@ namespace FastSerialization { // #Introduction - // + // // Sadly, System.Runtime.Serialization has a serious performance flaw. In the scheme created there, the // basic contract between an object and the serializer is fundamentally heavy. For serialization the // contract is for the object to implement System.Runtime.Serialization.ISerializable.GetObjectData @@ -23,7 +24,7 @@ namespace FastSerialization // is then the serializers job to actually send out the bits given this table. The REQUIRED work of // serializing an integers copying 4 bytes to some output buffer (a few instructions), however the // protocol above requires 1000s. - // + // // The classes in Serialize.cs are an attempt to create really light weight serialization. At the heart // of the design are two interfaces IStreamReader and IStreamWriter. They are a simplified // INTERFACE much like System.IO.BinaryReader and System.IO.BinaryWriter, that know how to @@ -33,38 +34,38 @@ namespace FastSerialization // complicated graph of objects in the stream. While IStreamWriter does not have the ability to seek, // the IStreamReader does (using StreamLabel), because it is expected that the reader will want // to follow StreamLabel 'pointers' to traverse the serialized data in a more random access way. - // + // // However, in general, an object needs more than a MemoryStreamWriter to serialize itself. When an object // graph could have cycles, it needs a way of remembering which objects it has already serialized. It // also needs a way encoding types, because in general the type of an object cannot always be inferred // from its context. This is the job of the Serializer class. A Serializer holds all the state // needed to represent a partially serialized object graph, but the most important part of a // Serializer is its Serializer.writer property, which holds the logical output stream. - // + // // Similarly a Deserializer holds all the 'in flight' information needed to deserialize a complete // object graph, and its most important property is its Deserializer.reader that holds the logical // input stream. - // + // // An object becomes serializable by doing two things // * implementing the IFastSerializable interface and implementing the // IFastSerializable.ToStream and IFastSerializable.FromStream methods. // * implementing a public constructor with no arguments (default constructor). This is needed because // an object needs to be created before IFastSerializable.FromStream can be called. - // + // // The IFastSerializable.ToStream method that the object implements is passed a Serializer, and // the object is free to take advantage of all the facilities (like its serialized object table) to help // serialize itself, however at its heart, the ToStream method tends to fetch the Serialier.writer // and write out the primitive fields in order. Similarly at the heart of the // IFastSerializable.FromStream method is fetching the Deserializer.reader and reading in a // series of primitive types. - // + // // Now the basic overhead of serializing a object in the common case is - // + // // * A interface call to IFastSerializable.ToStream. // * A fetch of IStreamWriter from the Serialier.writer field // * a series of IStreamWriter.Write operations which is an interface call, plus the logic to // store the actual data to the stream (the real work). - // + // // This is MUCH leaner, and now dominated by actual work of copying the data to the output buffer. /// @@ -137,14 +138,14 @@ private SerializationSettings( /// /// A StreamLabel represents a position in a IStreamReader or IStreamWriter. - /// In memory it is represented as a 64 bit signed value but to preserve compat + /// In memory it is represented as a 64 bit signed value but to preserve compat /// with the FastSerializer.1 format it is a 32 bit unsigned value when /// serialized in a file. FastSerializer can parse files exceeding 32 bit sizes - /// as long as the format doesn't persist a StreamLabel in the content. NetTrace + /// as long as the format doesn't persist a StreamLabel in the content. NetTrace /// is an example of this. /// During writing it is generated by the IStreamWriter.GetLabel method an /// consumed by the IStreamWriter.WriteLabel method. On reading you can use - /// IStreamReader.Current and and IStreamReader. + /// IStreamReader.Current and and IStreamReader. /// #if FASTSERIALIZATION_PUBLIC public @@ -159,11 +160,11 @@ enum StreamLabel : long /// /// IStreamWriter is meant to be a very simple streaming protocol. You can write integral types, - /// strings, and labels to the stream itself. - /// + /// strings, and labels to the stream itself. + /// /// IStreamWrite can be thought of a simplified System.IO.BinaryWriter, or maybe the writer /// part of a System.IO.Stream with a few helpers for primitive types. - /// + /// /// See also IStreamReader /// #if FASTSERIALIZATION_PUBLIC @@ -192,7 +193,7 @@ interface IStreamWriter : IDisposable /// void Write(StreamLabel value); /// - /// Write a string to a stream (supports null values). + /// Write a string to a stream (supports null values). /// void Write(string value); /// @@ -201,12 +202,12 @@ interface IStreamWriter : IDisposable /// StreamLabel GetLabel(); /// - /// Write a SuffixLabel it must be the last thing written to the stream. The stream + /// Write a SuffixLabel it must be the last thing written to the stream. The stream /// guarantees that this value can be efficiently read at any time (probably by seeking /// back from the end of the stream)). The idea is that when you generate a 'tableOfContents' /// you can only do this after processing the data (and probably writing it out), If you /// remember where you write this table of contents and then write a suffix label to it - /// as the last thing in the stream using this API, you guarantee that the reader can + /// as the last thing in the stream using this API, you guarantee that the reader can /// efficiently seek to the end, read the value, and then goto that position. (See /// IStreamReader.GotoSuffixLabel for more) /// @@ -220,11 +221,11 @@ interface IStreamWriter : IDisposable /// IStreamReader is meant to be a very simple streaming protocol. You can read integral types, - /// strings, and labels to the stream itself. You can also goto labels you have read from the stream. - /// + /// strings, and labels to the stream itself. You can also goto labels you have read from the stream. + /// /// IStreamReader can be thought of a simplified System.IO.BinaryReder, or maybe the reader /// part of a System.IO.Stream with a few helpers for primitive types. - /// + /// /// See also IStreamWriter #if FASTSERIALIZATION_PUBLIC public @@ -248,7 +249,7 @@ interface IStreamReader : IDisposable /// long ReadInt64(); /// - /// Read a string from the stream. Can represent null strings + /// Read a string from the stream. Can represent null strings /// string ReadString(); /// @@ -265,7 +266,7 @@ interface IStreamReader : IDisposable /// void Goto(StreamLabel label); /// - /// Returns the current position in the stream. + /// Returns the current position in the stream. /// StreamLabel Current { get; } @@ -273,16 +274,16 @@ interface IStreamReader : IDisposable /// Sometimes information is only known after writing the entire stream. This information can be put /// on the end of the stream, but there needs to be a way of finding it relative to the end, rather /// than from the beginning. A IStreamReader, however, does not actually let you go 'backwards' easily - /// because it does not guarantee the size what it writes out (it might compress). - /// - /// The solution is the concept of a 'suffixLabel' which is location in the stream where you can always + /// because it does not guarantee the size what it writes out (it might compress). + /// + /// The solution is the concept of a 'suffixLabel' which is location in the stream where you can always /// efficiently get to. - /// - /// It is written with a special API (WriteSuffixLabel that must be the last thing written. It is + /// + /// It is written with a special API (WriteSuffixLabel that must be the last thing written. It is /// expected that it simply write an uncompressed StreamLabel. It can then be used by using the /// GotoSTreamLabel() method below. This goes to this well know position in the stream. We expect - /// this is implemented by seeking to the end of the stream, reading the uncompressed streamLabel, - /// and then seeking to that position. + /// this is implemented by seeking to the end of the stream, reading the uncompressed streamLabel, + /// and then seeking to that position. /// void GotoSuffixLabel(); @@ -344,7 +345,7 @@ public static string ReadNullTerminatedUnicodeString(this IStreamReader reader, } /// - /// Returns a StreamLabel that is the sum of label + offset. + /// Returns a StreamLabel that is the sum of label + offset. /// public static StreamLabel Add(this StreamLabel label, int offset) { @@ -352,7 +353,7 @@ public static StreamLabel Add(this StreamLabel label, int offset) } /// - /// Returns the difference between two stream labels + /// Returns the difference between two stream labels /// public static long Sub(this StreamLabel label, StreamLabel other) { @@ -360,7 +361,7 @@ public static long Sub(this StreamLabel label, StreamLabel other) } /// - /// Convenience method for skipping a a certain number of bytes in the stream. + /// Convenience method for skipping a a certain number of bytes in the stream. /// public static void Skip(this IStreamReader reader, int byteCount) { @@ -370,10 +371,10 @@ public static void Skip(this IStreamReader reader, int byteCount) #endif /// - /// Like a StreamLabel, a ForwardReference represents a pointer to a location in the stream. + /// Like a StreamLabel, a ForwardReference represents a pointer to a location in the stream. /// However unlike a StreamLabel, the exact value in the stream does not need to be known at the - /// time the forward references is written. Instead the ID is written, and later that ID is - /// associated with the target location (using DefineForwardReference). + /// time the forward references is written. Instead the ID is written, and later that ID is + /// associated with the target location (using DefineForwardReference). /// #if FASTSERIALIZATION_PUBLIC public @@ -381,43 +382,43 @@ public static void Skip(this IStreamReader reader, int byteCount) enum ForwardReference : int { /// - /// Returned when no appropriate ForwardReference exists. + /// Returned when no appropriate ForwardReference exists. /// Invalid = -1 }; /// /// #SerializerIntroduction see also #StreamLayout - /// + /// /// The Serializer class is a general purpose object graph serializer helper. While it does not have /// any knowledge of the serialization format of individual object, it does impose conventions on how to /// serialize support information like the header (which holds versioning information), a trailer (which /// holds deferred pointer information), and how types are versioned. However these conventions are /// intended to be very generic and thus this class can be used for essentially any serialization need. - /// + /// /// Goals: /// * Allows full range of serialization, including subclassing and cyclic object graphs. /// * Can be serialized and deserialized efficiently sequentially (no seeks MANDATED on read or /// write). This allows the serializer to be used over pipes and other non-seekable devices). /// * Pay for play (thus very efficient in simple cases (no subclassing or cyclic graphs). /// * Ideally self-describing, and debuggable (output as XML if desired?) - /// + /// /// Versioning: /// * We want the ability for new formats to accept old versions if objects wish to support old /// formats /// * Also wish to allow new formats to be read by OLD version if the new format is just an /// 'extension' (data added to end of objects). This makes making new versions almost pain-free. - /// + /// /// Concepts: /// * No-seek requirement - /// + /// /// The serialized form should be such that it can be deserialized efficiently in a serial fashion /// (no seeks). This means all information needed to deserialize has to be 'just in time' (can't /// be some table at the end). Pragmatically this means that type information (needed to create /// instances), has to be output on first use, so it is available for the deserializer. - /// + /// /// * Laziness requirement - /// + /// /// While is should be possible to read the serialized for sequentially, we should also not force /// it. It should be possible to have a large file that represents a persisted structure that can /// be lazily brought into memory on demand. This means that all information needed to @@ -425,22 +426,22 @@ enum ForwardReference : int /// Pragmatically this means that type information, and forward forwardReference information needs to /// have a table in a well known Location at the end so that it can be found without having to /// search the file sequentially. - /// + /// /// * Versioning requirement - /// + /// /// To allow OLD code to access NEW formats, it must be the case that the serialized form of /// every instance knows how to 'skip' past any new data (even if it does not know its exact /// size). To support this, objects have 'begin' and 'end' tags, which allows the deserializer to /// skip the next object. - /// + /// /// * Polymorphism requirement - /// + /// /// Because the user of a filed may not know the exact instance stored there, in general objects /// need to store the exact type of the instance. Thus they need to store a type identifier, this /// can be folded into the 'begin' tag. - /// + /// /// * Arbitrary object graph (circularity) requirement (Forward references) - /// + /// /// The serializer needs to be able to serialize arbitrary object graphs, including those with /// cycles in them. While you can do this without forward references, the system is more flexible /// if it has the concept of a forward reference. Thus whenever a object reference is required, a @@ -450,9 +451,9 @@ enum ForwardReference : int /// Serializer.Tags.ForwardDefintion) or at the end of the serialization in a forward /// reference table (which allows forward references to be resolved without scanning then entire /// file. - /// + /// /// * Contract between objects IFastSerializable.ToStream: - /// + /// /// The heart of the serialization and deserialization process the IFastSerializable /// interface, which implements just two methods: ToStream (for serializing an object), and /// FromStream (for deserializing and object). This interfaces is the mechanism by which objects @@ -460,7 +461,7 @@ enum ForwardReference : int /// enough. An object that implements IFastSerializable must also implement a default /// constructor (constructor with no args), so that that deserializer can create the object (and /// then call FromStream to populated it). - /// + /// /// The ToStream method is only responsible for serializing the data in the object, and by itself /// is not sufficient to serialize an interconnected, polymorphic graph of objects. It needs /// help from the Serializer and Deserialize to do this. Serializer takes on the @@ -468,9 +469,9 @@ enum ForwardReference : int /// the correct type before IFastSerializable.FromStream is called). It is also the /// serializer's responsibility to provide the mechanism for dealing with circular object graphs /// and forward references. - /// + /// /// * Layout of a serialized object: A serialized object has the following basic format - /// + /// /// * If the object is the definition of a previous forward references, then the definition must /// begin with a Serializer.Tags.ForwardDefintion tag followed by a forward forwardReference /// index which is being defined. @@ -484,10 +485,10 @@ enum ForwardReference : int /// * Serializer.Tags.EndObject tag. This marks the end of the object. It quickly finds bugs /// in ToStream FromStream mismatches, and also allows for V1 deserializers to skip past /// additional fields added since V1. - /// + /// /// * Serializing Object references: /// When an object forwardReference is serialized, any of the following may follow in the stream - /// + /// /// * Serializer.Tags.NullReference used to encode a null object forwardReference. /// * Serializer.Tags.BeginObject or Serializer.Tags.ForwardDefintion, which indicates /// that this the first time the target object has been referenced, and the target is being @@ -498,17 +499,17 @@ enum ForwardReference : int /// indicates that the object is not yet serialized, but the serializer has chosen not to /// immediately serialize the object. Ultimately this object will be defined, but has not /// happened yet. - /// + /// /// * Serializing Types: /// Types are simply objects of type SerializationType which contain enough information about /// the type for the Deserializer to do its work (it full name and version number). They are /// serialized just like all other types. The only thing special about it is that references to - /// types after the BeginObject tag must not be forward references. - /// + /// types after the BeginObject tag must not be forward references. + /// /// #StreamLayout: /// The structure of the file as a whole is simply a list of objects. The first and last objects in - /// the file are part of the serialization infrastructure. - /// + /// the file are part of the serialization infrastructure. + /// /// Layout Synopsis /// * Signature representing Serializer format /// * EntryObject (most of the rest of the file) @@ -518,19 +519,19 @@ enum ForwardReference : int /// * Type for SerializationType POSITION1 /// * BeginObject tag /// * Type for SerializationType - /// * ObjectReference tag // This is how our recursion ends. + /// * ObjectReference tag // This is how our recursion ends. /// * StreamLabel for POSITION1 /// * Version Field for SerializationType /// * Minimum Version Field for SerializationType - /// * FullName string for SerializationType + /// * FullName string for SerializationType /// * EndObject tag /// * Version field for EntryObject's type /// * Minimum Version field for EntryObject's type /// * FullName string for EntryObject's type /// * EndObject tag - /// * Field1 - /// * Field2 - /// * V2_Field (this should be tagged so that it can be skipped by V1 deserializers. + /// * Field1 + /// * Field2 + /// * V2_Field (this should be tagged so that it can be skipped by V1 deserializers. /// * EndObject tag /// * ForwardReferenceTable pseudo-object /// * Count of forward references @@ -575,7 +576,7 @@ public Serializer(Stream outputStream, IFastSerializable entryObject, bool leave } /// - /// Create a serializer that writes 'entryObject' another IStreamWriter + /// Create a serializer that writes 'entryObject' another IStreamWriter /// public Serializer(IStreamWriter writer, IFastSerializable entryObject) { @@ -588,19 +589,19 @@ public Serializer(IStreamWriter writer, IFastSerializable entryObject) this.writer = writer; Log(""); - // Write the header. + // Write the header. Write("!FastSerialization.1"); - // Write the main object. This is recursive and does most of the work. + // Write the main object. This is recursive and does most of the work. Write(entryObject); - // Write any forward references. + // Write any forward references. WriteDeferedObjects(); - // Write an unbalanced EndObject tag to represent the end of objects. + // Write an unbalanced EndObject tag to represent the end of objects. WriteTag(Tags.EndObject); - // Write the forward forwardReference table (for random access lookup) + // Write the forward forwardReference table (for random access lookup) StreamLabel forwardRefsLabel = writer.GetLabel(); Log(""); if (forwardReferenceDefinitions != null) @@ -620,12 +621,12 @@ public Serializer(IStreamWriter writer, IFastSerializable entryObject) Log(""); - // Write the trailer currently it has only one item in it, however it is expandable. - // items. + // Write the trailer currently it has only one item in it, however it is expandable. + // items. StreamLabel trailerLabel = writer.GetLabel(); Log(""); Write(forwardRefsLabel); - // More stuff goes here in future versions. + // More stuff goes here in future versions. Log(""); Log(""); @@ -642,7 +643,7 @@ public Serializer(IStreamWriter writer, IFastSerializable entryObject) } } - // Convenience functions. + // Convenience functions. /// /// Write a bool to a stream /// @@ -747,26 +748,26 @@ public void Write(ForwardReference value) /// To tune working set (or disk seeks), or to make the dump of the format more readable, it is /// valuable to have control over which of several references to an object will actually cause it to /// be serialized (by default the first encountered does it). - /// + /// /// WriteDefered allows you to write just a forwardReference to an object with the expectation that /// somewhere later in the serialization process the object will be serialized. If no call to /// WriteObject() occurs, then the object is serialized automatically before the stream is closed - /// (thus dangling references are impossible). + /// (thus dangling references are impossible). /// public void WriteDefered(IFastSerializable obj) { WriteObjectRef(obj, true); } /// /// This is an optimized version of WriteObjectReference that can be used in some cases. - /// + /// /// If the object is not aliased (it has an 'owner' and only that owner has references to it (which /// implies its lifetime is strictly less than its owners), then the serialization system does not /// need to put the object in the 'interning' table. This saves a space (entries in the intern table /// as well as 'SyncEntry' overhead of creating hash codes for object) as well as time (to create /// that bookkeeping) for each object that is treated as private (which can add up if because it is /// common that many objects are private). The private instances are also marked in the serialized - /// format so on reading there is a similar bookkeeping savings. - /// + /// format so on reading there is a similar bookkeeping savings. + /// /// The ultimate bits written by WritePrivateObject are the same as WriteObject. - /// + /// /// TODO Need a DEBUG mode where we detect if others besides the owner reference the object. /// public void WritePrivate(IFastSerializable obj) @@ -777,9 +778,9 @@ public void WritePrivate(IFastSerializable obj) Log(""); } - // forward reference support + // forward reference support /// - /// Create a ForwardReference. At some point before the end of the serialization, DefineForwardReference must be called on this value + /// Create a ForwardReference. At some point before the end of the serialization, DefineForwardReference must be called on this value /// /// public ForwardReference GetForwardReference() @@ -794,7 +795,7 @@ public ForwardReference GetForwardReference() return ret; } /// - /// Define the ForwardReference forwardReference to point at the current write location. + /// Define the ForwardReference forwardReference to point at the current write location. /// /// public void DefineForwardReference(ForwardReference forwardReference) @@ -802,7 +803,7 @@ public void DefineForwardReference(ForwardReference forwardReference) forwardReferenceDefinitions[(int)forwardReference] = writer.GetLabel(); } - // data added after V1 needs to be tagged so that V1 deserializers can skip it. + // data added after V1 needs to be tagged so that V1 deserializers can skip it. /// /// Write a byte preceded by a tag that indicates its a byte. These should be read with the corresponding TryReadTagged operation @@ -835,16 +836,16 @@ public void WriteTagged(IFastSerializable value) { WriteTag(Tags.SkipRegion); ForwardReference endRegion = GetForwardReference(); - Write(endRegion); // Allow the reader to skip this. + Write(endRegion); // Allow the reader to skip this. Write(value); // Write the data we can skip - DefineForwardReference(endRegion); // This is where the forward reference refers to + DefineForwardReference(endRegion); // This is where the forward reference refers to } /// /// Writes the header for a skipping an arbitrary blob. THus it writes a Blob - /// tag and the size, and the caller must then write 'sizes' bytes of data in + /// tag and the size, and the caller must then write 'sizes' bytes of data in /// some way. This allows you to create regions of arbitrary size that can - /// be skipped by old as well as new parsers. + /// be skipped by old as well as new parsers. /// /// public void WriteTaggedBlobHeader(int size) @@ -854,17 +855,17 @@ public void WriteTaggedBlobHeader(int size) } /// - /// Writes an end tag (which is different from all others). This is useful - /// when you have a deferred region of tagged items. + /// Writes an end tag (which is different from all others). This is useful + /// when you have a deferred region of tagged items. /// public void WriteTaggedEnd() { WriteTag(Tags.EndObject); } /// - /// Retrieve the underlying stream we are writing to. Generally the Write* methods are enough. + /// Retrieve the underlying stream we are writing to. Generally the Write* methods are enough. /// public IStreamWriter Writer { get { return writer; } } /// - /// Completes the writing of the stream. + /// Completes the writing of the stream. /// public void Close() { @@ -876,7 +877,7 @@ public void Close() /// /// To help debug any serialization issues, you can write data to a side file called 'log.serialize.xml' - /// which can track exactly what serialization operations occurred. + /// which can track exactly what serialization operations occurred. /// [Conditional("DEBUG_SERIALIZE")] public void Log(string str) @@ -916,7 +917,7 @@ private void WriteObjectRef(IFastSerializable obj, bool defered) return; } - // If we have a forward forwardReference to this, get it. + // If we have a forward forwardReference to this, get it. ForwardReference forwardReference; if (defered) { @@ -937,14 +938,14 @@ private void WriteObjectRef(IFastSerializable obj, bool defered) // Write the forward forwardReference index Write((int)forwardReference); - // And its type. + // And its type. WriteTypeForObject(obj); Log(""); return; } - // At this point we are writing an actual object and not a reference. - // + // At this point we are writing an actual object and not a reference. + // StreamLabel objLabel = writer.GetLabel(); Log(" objs = new List(); while (ObjectsWithForwardReferences.Count > 0) { - // Copy the objects out because the calls to WriteObjectReference updates the collection. + // Copy the objects out because the calls to WriteObjectReference updates the collection. objs.AddRange(ObjectsWithForwardReferences.Keys); foreach (IFastSerializable obj in objs) { @@ -1021,7 +1022,7 @@ private SerializationType CreateTypeForObject(IFastSerializable instance) Type type = instance.GetType(); // Special case: the SerializationType for SerializationType itself is null. This avoids - // recursion. + // recursion. if (type == typeof(SerializationType)) { return null; @@ -1060,8 +1061,8 @@ public void Dispose() /// /// Deserializer is a helper class that holds all the information needed to deserialize an object /// graph as a whole (things like the table of objects already deserialized, and the list of types in - /// the object graph. - /// + /// the object graph. + /// /// see #SerializerIntroduction for more /// #if FASTSERIALIZATION_PUBLIC @@ -1079,7 +1080,7 @@ public Deserializer(string filePath, SerializationSettings settings) } /// - /// Create a Deserializer that reads its data from a given System.IO.Stream. The stream will be closed when the Deserializer is done with it. + /// Create a Deserializer that reads its data from a given System.IO.Stream. The stream will be closed when the Deserializer is done with it. /// public Deserializer(Stream inputStream, string streamName, SerializationSettings settings) { @@ -1099,7 +1100,7 @@ public Deserializer(Stream inputStream, string streamName, bool leaveOpen, Seria } /// - /// Create a Deserializer that reads its data from a given IStreamReader. The stream will be closed when the Deserializer is done with it. + /// Create a Deserializer that reads its data from a given IStreamReader. The stream will be closed when the Deserializer is done with it. /// public Deserializer(IStreamReader reader, string streamName) { @@ -1140,8 +1141,8 @@ private void Initialize(IStreamReader reader, string streamName) /// /// Returns the full name of the type of the entry object without actually creating it. - /// Will return null on failure. - /// + /// Will return null on failure. + /// public string GetEntryTypeName() { StreamLabel origPosition = reader.Current; @@ -1180,7 +1181,7 @@ public IFastSerializable GetEntryObject() if (entryObject == null) { // If you are going to deserialize the world, better to do it in order, which means deferring - // forward references (since you will get to them eventually). + // forward references (since you will get to them eventually). if (!allowLazyDeserialization) { deferForwardReferences = true; @@ -1190,7 +1191,7 @@ public IFastSerializable GetEntryObject() entryObject = ReadObjectDefintion(); // If we are reading sequentially, read the position of the objects (will be marked by a - // unmatched EndObject tag. + // unmatched EndObject tag. if (!allowLazyDeserialization) { for (; ; ) @@ -1211,7 +1212,7 @@ public IFastSerializable GetEntryObject() return entryObject; } - // For FromStream method bodies. + // For FromStream method bodies. public void Read(byte[] buffer, int offset, int length) { #if DEBUG @@ -1423,7 +1424,7 @@ public IFastSerializable ReadObject() } else if (tag == Tags.Blob) { - // If it is a blob skip it and try again (presumably other things point at it. + // If it is a blob skip it and try again (presumably other things point at it. int size = reader.ReadInt32(); reader.Skip(size); return ReadObject(); @@ -1566,9 +1567,9 @@ public ForwardReference ReadForwardReference() // forward reference support /// - /// Given a forward reference find the StreamLabel (location in the stream) that it points at). - /// Normally this call preserves the current read location, but if you do don't care you can - /// set preserveCurrent as an optimization to make it more efficient. + /// Given a forward reference find the StreamLabel (location in the stream) that it points at). + /// Normally this call preserves the current read location, but if you do don't care you can + /// set preserveCurrent as an optimization to make it more efficient. /// public StreamLabel ResolveForwardReference(ForwardReference reference, bool preserveCurrent = true) { @@ -1629,15 +1630,15 @@ public StreamLabel ResolveForwardReference(ForwardReference reference, bool pres } /// - /// Meant to be called from FromStream. It returns the version number of the + /// Meant to be called from FromStream. It returns the version number of the /// type being deserialized. It can be used so that new code can recognizes that it - /// is reading an old file format and adjust what it reads. + /// is reading an old file format and adjust what it reads. /// public int VersionBeingRead { get { return typeBeingRead.Version; } } /// /// Meant to be called from FromStream. It returns the version number of the MinimumReaderVersion - /// of the type that was serialized. + /// of the type that was serialized. /// public int MinimumReaderVersionBeingRead { get { return typeBeingRead.MinimumReaderVersion; } } @@ -1654,7 +1655,7 @@ public StreamLabel ResolveForwardReference(ForwardReference reference, bool pres /// /// Registers a creation factory for a type. - /// + /// /// When the Deserializerencounters a serialized type, it will look for a registered factory or type registration /// so that it knows how to construct an empty instance of the type that can be filled. All non-primitive types /// must either be registered by calling RegisterFactory or RegisterType. @@ -1666,7 +1667,7 @@ public void RegisterFactory(Type type, Func factory) /// /// Registers a creation factory for a type name. - /// + /// /// When the Deserializerencounters a serialized type, it will look for a registered factory or type registration /// so that it knows how to construct an empty instance of the type that can be filled. All non-primitive types /// must either be registered by calling RegisterFactory or RegisterType. @@ -1678,12 +1679,12 @@ public void RegisterFactory(string typeName, Func factory) /// /// Registers a type that can be created by instantiating the parameterless constructor. - /// + /// /// When the Deserializerencounters a serialized type, it will look for a registered factory or type registration /// so that it knows how to construct an empty instance of the type that can be filled. All non-primitive types /// must either be registered by calling RegisterFactory or RegisterType. /// - public void RegisterType(Type type) + public void RegisterType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) { RegisterFactory(type, () => { @@ -1691,7 +1692,7 @@ public void RegisterType(Type type) }); } - private static IFastSerializable CreateDefault(Type type) + private static IFastSerializable CreateDefault([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) { try { @@ -1705,9 +1706,9 @@ private static IFastSerializable CreateDefault(Type type) } // For FromStream method bodies, reading tagged values (for post V1 field additions) - // If the item is present, it is read into 'ret' otherwise 'ret' is left unchanged. + // If the item is present, it is read into 'ret' otherwise 'ret' is left unchanged. // If after V1 you always add fields at the end, and you always use WriteTagged() and TryReadTagged() - // to write and read them, then you always get perfect backward and forward compatibility! + // to write and read them, then you always get perfect backward and forward compatibility! // For things like collections you can do // // in collectionLen = 0; @@ -1716,8 +1717,8 @@ private static IFastSerializable CreateDefault(Type type) // for(int i =0; i < collectionLenght; i++) // Read(out this.array[i]); // - // notice that the reading the array elements is does not use TryReadTagged, but IS - // conditional on the collection length (which is tagged). + // notice that the reading the array elements is does not use TryReadTagged, but IS + // conditional on the collection length (which is tagged). /// /// Try to read tagged value from the stream. If it is a tagged bool, return int in ret and return true, otherwise leave the cursor unchanged and return false @@ -1807,9 +1808,9 @@ public bool TryReadTagged(ref string ret) } /// /// Try to read the header for a tagged blob of bytes. If Current points at a tagged - /// blob it succeeds and returns the size of the blob (the caller must read or skip + /// blob it succeeds and returns the size of the blob (the caller must read or skip /// past it manually) If it is not a tagged blob it returns a size of 0 and resets - /// the read pointer to what it was before this method was called. + /// the read pointer to what it was before this method was called. /// public int TryReadTaggedBlobHeader() { @@ -1827,12 +1828,12 @@ public int TryReadTaggedBlobHeader() /// public bool TryReadTagged(ref T ret) where T : IFastSerializable { - // Tagged objects always start with a SkipRegion so we don't need to know its size. + // Tagged objects always start with a SkipRegion so we don't need to know its size. Tags tag = ReadTag(); if (tag == Tags.SkipRegion) { - ReadForwardReference(); // Skip the forward reference which is part of SkipRegion - ret = (T)ReadObject(); // Read the real object + ReadForwardReference(); // Skip the forward reference which is part of SkipRegion + ret = (T)ReadObject(); // Read the real object return true; } reader.Goto(Current - 1); @@ -1843,12 +1844,12 @@ public bool TryReadTagged(ref T ret) where T : IFastSerializable /// public IFastSerializable TryReadTaggedObject() { - // Tagged objects always start with a SkipRegion so we don't need to know its size. + // Tagged objects always start with a SkipRegion so we don't need to know its size. Tags tag = ReadTag(); if (tag == Tags.SkipRegion) { - ReadForwardReference(); // Skip the forward reference which is part of SkipRegion - return ReadObject(); // Read the real object + ReadForwardReference(); // Skip the forward reference which is part of SkipRegion + return ReadObject(); // Read the real object } reader.Goto(Current - 1); return null; @@ -1870,11 +1871,11 @@ public void Goto(ForwardReference reference) Goto(ResolveForwardReference(reference, false)); } /// - /// Returns the current read position in the stream. + /// Returns the current read position in the stream. /// public StreamLabel Current { get { return reader.Current; } } /// - /// Fetch the underlying IStreamReader that the deserializer reads data from + /// Fetch the underlying IStreamReader that the deserializer reads data from /// public IStreamReader Reader { get { return reader; } } /// @@ -1898,10 +1899,10 @@ public void Dispose() } } - #region private + #region private private StreamWriter log; [Conditional("DEBUG_SERIALIZE")] - // see also Serializer.Log + // see also Serializer.Log public void Log(string str) { if (log == null) @@ -1971,7 +1972,7 @@ private IFastSerializable ReadObjectDefinition(Tags tag, StreamLabel objectLabel Log(""); // Create the instance (or get it from the unInitializedForwardReferences if it was created - // that way). + // that way). if (forwardReference != ForwardReference.Invalid) { DefineForwardReference(forwardReference, objectLabel); @@ -2053,27 +2054,27 @@ internal Func GetFactory(string fullName) private void FindEndTag(SerializationType type, IFastSerializable objectBeingDeserialized) { - // Skip any extra fields in the object that I don't understand. + // Skip any extra fields in the object that I don't understand. Log(""); int i = 0; for (; ; ) { Debug.Assert(i == 0 || type.Version != 0); StreamLabel objectLabel = reader.Current; - // If this fails, the likely culprit is the FromStream of the objectBeingDeserialized. + // If this fails, the likely culprit is the FromStream of the objectBeingDeserialized. Tags tag = ReadTag(); // TODO this is a hack. The .NET Core Runtime < V2.1 do not emit an EndObject tag // properly for its V1 EventPipeFile object. The next object is always data that happens // to be size that is likley to be in the range 0x50-0xFF. Thus we can fix this // and implicilty insert the EndObject and fix things up. This is acceptable because - // it really does not change non-error behavior. + // it really does not change non-error behavior. // V2.1 of NET Core will ship in 4/2018 so a year or so after that is is probably OK // to remove this hack (basically dropping support for V1 of EventPipeFile) if (type.FullName == "Microsoft.DotNet.Runtime.EventPipeFile" && type.Version <= 2 && (int)tag > 0x50) { - reader.Skip(-1); // Undo the read of the byte - tag = Tags.EndObject; // And make believe we saw the EntObject instead. + reader.Skip(-1); // Undo the read of the byte + tag = Tags.EndObject; // And make believe we saw the EntObject instead. } int nesting = 0; @@ -2108,7 +2109,7 @@ private void FindEndTag(SerializationType type, IFastSerializable objectBeingDes reader.ReadLabel(); break; case Tags.SkipRegion: - // Allow the region to be skipped. + // Allow the region to be skipped. ForwardReference endSkipRef = ReadForwardReference(); StreamLabel endSkip = ResolveForwardReference(endSkipRef); reader.Goto(endSkip); @@ -2132,7 +2133,7 @@ private void FindEndTag(SerializationType type, IFastSerializable objectBeingDes } done: Log(""); - // TODO would like some redundancy, so that failure happen close to the cause. + // TODO would like some redundancy, so that failure happen close to the cause. } private void DefineForwardReference(ForwardReference forwardReference, StreamLabel definitionLabel) @@ -2150,7 +2151,7 @@ private void DefineForwardReference(ForwardReference forwardReference, StreamLab forwardReferenceDefinitions.Add(StreamLabel.Invalid); } - // If it is already defined, it better match! + // If it is already defined, it better match! Debug.Assert(forwardReferenceDefinitions[idx] == StreamLabel.Invalid || forwardReferenceDefinitions[idx] == definitionLabel); @@ -2180,9 +2181,9 @@ private Tags ReadTag() internal List forwardReferenceDefinitions; internal bool allowLazyDeserialization; /// - /// When we encounter a forward reference, we can either go to the forward reference table immediately and resolve it + /// When we encounter a forward reference, we can either go to the forward reference table immediately and resolve it /// (deferForwardReferences == false), or simply remember that that position needs to be fixed up and continue with - /// the deserialization. This later approach allows 'no seek' deserialization. This variable which scheme we do. + /// the deserialization. This later approach allows 'no seek' deserialization. This variable which scheme we do. /// internal bool deferForwardReferences; private Dictionary> factories; @@ -2191,26 +2192,26 @@ private Tags ReadTag() /// - /// #DeferedRegionOverview. - /// + /// #DeferedRegionOverview. + /// /// A DeferedRegion help make 'lazy' objects. You will have a DeferedRegion for each block of object you /// wish to independently decide whether to deserialize lazily (typically you have one per object however /// in the limit you can have one per field, it is up to you). - /// + /// /// When you call DeferedRegion.Write you give it a delegate that will write all the deferred fields. /// The Write operation will place a forward reference in the stream that skips all the fields written, /// then the fields themselves, then define the forward reference. This allows readers to skip the /// deferred fields. - /// + /// /// When you call DeferedRegion.Read you also give it a delegate that reads all the deferred fields. /// However when 'Read' instead of reading the fields it - /// + /// /// * remembers the deserializer, stream position, and reading delegate. /// * it uses the forward reference to skip the region. - /// - /// When DeferedRegion.FinishRead is called, it first checks if the region was already restored. + /// + /// When DeferedRegion.FinishRead is called, it first checks if the region was already restored. /// If not it used the information to read in the deferred region and returns. Thus this FinishRead - /// should be called before any deferred field is used. + /// should be called before any deferred field is used. /// #if FASTSERIALIZATION_PUBLIC public @@ -2218,29 +2219,29 @@ private Tags ReadTag() struct DeferedRegion { /// - /// see #DeferedRegionOverview. - /// TODO more + /// see #DeferedRegionOverview. + /// TODO more /// public void Write(Serializer serializer, Action toStream) { serializer.Log("\r\n"); // We actually don't use the this pointer! We did this for symmetry with Read ForwardReference endRegion = serializer.GetForwardReference(); - serializer.Write(endRegion); // Allow the reader to skip this. - toStream(); // Write the deferred data. + serializer.Write(endRegion); // Allow the reader to skip this. + toStream(); // Write the deferred data. serializer.DefineForwardReference(endRegion); serializer.Log("\r\n"); } /// - /// See overview in DeferedRegion class comment. + /// See overview in DeferedRegion class comment. /// This call indicates that the 'fromStream' delegate can deserialize a region of the object, which /// was serialized with the DeferedRegion.Write method. The read skips the data for the region (thus - /// no objects associated with the region are created in memory) but the deferred object remembers - /// 'fromStream' and will call it when 'FinishRead()' is called. + /// no objects associated with the region are created in memory) but the deferred object remembers + /// 'fromStream' and will call it when 'FinishRead()' is called. /// public void Read(Deserializer deserializer, Action fromStream) { - Debug.Assert(this.fromStream == null); // For now, don't call this more than once. + Debug.Assert(this.fromStream == null); // For now, don't call this more than once. deserializer.Log(""); ForwardReference endReference = deserializer.ReadForwardReference(); this.deserializer = deserializer; @@ -2253,7 +2254,7 @@ public void Read(Deserializer deserializer, Action fromStream) /// FinishRead indicates that you need to deserialize the lazy region you defined with the 'Read' method. /// If the region has already been deserialized, nothing is done. Otherwise when you call this /// method the current position in the stream is put back to where it was when Read was called and the - /// 'fromStream' delegate registered in 'Read' is called to perform the deserialization. + /// 'fromStream' delegate registered in 'Read' is called to perform the deserialization. /// public void FinishRead(bool preserveStreamPosition = false) { @@ -2263,7 +2264,7 @@ public void FinishRead(bool preserveStreamPosition = false) } } /// - /// Returns true if the FinsihRead() has already been called. + /// Returns true if the FinsihRead() has already been called. /// public bool IsFinished { get { return fromStream == null; } } @@ -2277,11 +2278,11 @@ public void FinishRead(bool preserveStreamPosition = false) public StreamLabel StartPosition { get { return startPosition; } } #region private /// - /// This helper is just here to ensure that FinishRead gets inlined + /// This helper is just here to ensure that FinishRead gets inlined /// private void FinishReadHelper(bool preserveStreamPosition) { - StreamLabel originalPosition = 0; // keeps the compiler happy. + StreamLabel originalPosition = 0; // keeps the compiler happy. if (preserveStreamPosition) { originalPosition = deserializer.Current; @@ -2291,7 +2292,7 @@ private void FinishReadHelper(bool preserveStreamPosition) deserializer.Goto(startPosition); fromStream(); deserializer.Log(""); - fromStream = null; // Indicates we ran it. + fromStream = null; // Indicates we ran it. if (preserveStreamPosition) { @@ -2308,10 +2309,10 @@ private void FinishReadHelper(bool preserveStreamPosition) /// /// A type can opt into being serializable by implementing IFastSerializable and a default constructor /// (constructor that takes not arguments). - /// + /// /// Conceptually all clients of IFastSerializable also implement IFastSerializableVersion /// however the serializer will assume a default implementation of IFastSerializableVersion (that - /// Returns version 1 and assumes all versions are allowed to deserialize it. + /// Returns version 1 and assumes all versions are allowed to deserialize it. /// #if FASTSERIALIZATION_PUBLIC public @@ -2322,46 +2323,46 @@ interface IFastSerializable /// Given a Serializer, write yourself to the output stream. Conceptually this routine is NOT /// responsible for serializing its type information but only its field values. However it is /// conceptually responsible for the full transitive closure of its fields. - /// + /// /// * For primitive fields, the choice is easy, simply call Serializer.Write /// * For object fields there is a choice /// * If is is only references by the enclosing object (eg and therefore field's lifetime is /// identical to referencing object), then the Serialize.WritePrivateObject can be /// used. This skips placing the object in the interning table (that ensures it is written - /// exactly once). + /// exactly once). /// * Otherwise call Serialize.WriteObject - /// * For value type fields (or collections of structs), you serialize the component fields. - /// * For collections, typically you serialize an integer inclusiveCountRet followed by each object. + /// * For value type fields (or collections of structs), you serialize the component fields. + /// * For collections, typically you serialize an integer inclusiveCountRet followed by each object. /// void ToStream(Serializer serializer); /// - /// + /// /// Given a reader, and a 'this' instance, made by calling the default constructor, create a fully /// initialized instance of the object from the reader stream. The deserializer provides the extra - /// state needed to do this for cyclic object graphs. - /// + /// state needed to do this for cyclic object graphs. + /// /// Note that it is legal for the instance to cache the deserializer and thus be 'lazy' about when /// the actual deserialization happens (thus large persisted strucuture on the disk might stay on the - /// disk). - /// + /// disk). + /// /// Typically the FromStream implementation is an exact mirror of the ToStream implementation, where - /// there is a Read() for every Write(). + /// there is a Read() for every Write(). /// void FromStream(Deserializer deserializer); } - // TODO fix the versioning so you don't have to create an instance of the type on serialization. + // TODO fix the versioning so you don't have to create an instance of the type on serialization. /// /// Objects implement IFastSerializableVersion to indicate what the current version is for writing /// and which readers can read the current version. If this interface is not implemented a default is - /// provided (assuming version 1 for writing and MinimumVersion = 0). - /// + /// provided (assuming version 1 for writing and MinimumVersion = 0). + /// /// By default Serializer.WriteObject will place marks when the object ends and always skip to the /// end even if the FromStream did not read all the object data. This allows considerable versioning /// flexibility. Simply by placing the new data at the end of the existing serialization, new versions /// of the type can be read by OLD deserializers (new fields will have the value determined by the /// default constructor (typically 0 or null). This makes is relatively easy to keep MinimumVersion = 0 - /// (the ideal case). + /// (the ideal case). /// #if FASTSERIALIZATION_PUBLIC public @@ -2370,8 +2371,8 @@ interface IFastSerializableVersion { /// /// This is the version number for the serialization CODE (that is the app decoding the format) - /// It should be incremented whenever a change is made to IFastSerializable.ToStream and the format - /// is publicly disseminated. It must not vary from instance to instance. This is pretty straightforward. + /// It should be incremented whenever a change is made to IFastSerializable.ToStream and the format + /// is publicly disseminated. It must not vary from instance to instance. This is pretty straightforward. /// It defaults to 0 /// int Version { get; } @@ -2381,26 +2382,26 @@ interface IFastSerializableVersion /// This is the Minimum version of the serialized data that this reader can deserialize. Trying /// to read wire formats strictly smaller (older) than this will fail. Setting this to the current /// version indicates that you don't care about ever reading data generated with an older version - /// of the code. - /// + /// of the code. + /// /// If you set this to something other than your current version, you are obligated to ensure that - /// your FromStream() method can handle all formats >= than this number. + /// your FromStream() method can handle all formats >= than this number. + /// + /// You can achieve this if you simply use the 'WriteTagged' and 'ReadTagged' APIs in your 'ToStream' + /// and 'FromStream' after your V1 AND you always add new fields to the end of your class. + /// This is the best practice. Thus /// - /// You can achieve this if you simply use the 'WriteTagged' and 'ReadTagged' APIs in your 'ToStream' - /// and 'FromStream' after your V1 AND you always add new fields to the end of your class. - /// This is the best practice. Thus - /// /// void IFastSerializable.ToStream(Serializer serializer) /// { /// serializer.Write(Ver_1_Field1); /// serializer.Write(Ver_1_Field2); /// // ... - /// serializer.WriteTagged(Ver_2_Field1); + /// serializer.WriteTagged(Ver_2_Field1); /// serializer.WriteTagged(Ver_2_Field2); /// // ... /// serializer.WriteTagged(Ver_3_Field1); /// } - /// + /// /// void IFastSerializable.FromStream(Deserializer deserializer) /// { /// deserializer.Read(out Ver_1_Field1); @@ -2409,46 +2410,46 @@ interface IFastSerializableVersion /// deserializer.TryReadTagged(ref Ver_2_Field1); // If data no present (old format) then Ver_2_Field1 not set. /// deserializer.TryReadTagged(ref Ver_2_Field2); // ditto... /// // ... - /// deserializer.TryReadTagged(ref Ver_3_Field1); - /// } - /// + /// deserializer.TryReadTagged(ref Ver_3_Field1); + /// } + /// /// Tagging outputs a byte tag in addition to the field itself. If that is a problem you can also use the - /// VersionBeingRead to find out what format is being read and write code that explicitly handles it. - /// Note however that this only gets you Backward compatibility (new readers can read the old format, but old readers - /// will still not be able to read the new format), which is why this is not the preferred method. - /// + /// VersionBeingRead to find out what format is being read and write code that explicitly handles it. + /// Note however that this only gets you Backward compatibility (new readers can read the old format, but old readers + /// will still not be able to read the new format), which is why this is not the preferred method. + /// /// void IFastSerializable.FromStream(Deserializer deserializer) /// { /// // We assume that MinVersionCanRead == 4 - /// // Deserialize things that are common to all versions (4 and earlier) - /// + /// // Deserialize things that are common to all versions (4 and earlier) + /// /// if (deserializer.VersionBeingRead >= 5) /// { /// deserializer.Read(AVersion5Field); /// if (deserializer.VersionBeingRead >= 5) - /// deserializer.ReadTagged(AVersion6Field); + /// deserializer.ReadTagged(AVersion6Field); /// } /// } /// int MinimumVersionCanRead { get; } /// /// This is the minimum version of a READER that can read this format. If you don't support forward - /// compatibility (old readers reading data generated by new readers) then this should be set to - /// the current version. - /// + /// compatibility (old readers reading data generated by new readers) then this should be set to + /// the current version. + /// /// If you set this to something besides the current version you are obligated to ensure that your /// ToStream() method ONLY adds fields at the end, AND that all of those added fields use the WriteTagged() /// operations (which tags the data in a way that old readers can skip even if they don't know what it is) - /// In addition your FromStream() method must read these with the ReadTagged() deserializer APIs. - /// - /// See the comment in front of MinimumVersionCanRead for an example of using the WriteTagged() and ReadTagged() - /// methods. + /// In addition your FromStream() method must read these with the ReadTagged() deserializer APIs. + /// + /// See the comment in front of MinimumVersionCanRead for an example of using the WriteTagged() and ReadTagged() + /// methods. /// int MinimumReaderVersion { get; } } /// - /// Thrown when the deserializer detects an error. + /// Thrown when the deserializer detects an error. /// #if FASTSERIALIZATION_PUBLIC public @@ -2456,7 +2457,7 @@ interface IFastSerializableVersion class SerializationException : Exception { /// - /// Thown when a error occurs in serialization. + /// Thown when a error occurs in serialization. /// public SerializationException(string message) : base(message) @@ -2469,15 +2470,15 @@ internal sealed class SerializationType : IFastSerializable { /// /// This is the version represents the version of both the reading - /// code and the version for the format for this type in serialized form. - /// See IFastSerializableVersion for more. + /// code and the version for the format for this type in serialized form. + /// See IFastSerializableVersion for more. /// public int Version { get { return version; } } /// - /// The version the the smallest (oldest) reader code that can read - /// this file format. Readers strictly less than this are rejected. - /// This allows support for forward compatbility. - /// See IFastSerializableVersion for more. + /// The version the the smallest (oldest) reader code that can read + /// this file format. Readers strictly less than this are rejected. + /// This allows support for forward compatbility. + /// See IFastSerializableVersion for more. /// public int MinimumReaderVersion { get { return minimumReaderVersion; } } public string FullName { get { return fullName; } } @@ -2515,12 +2516,12 @@ void IFastSerializable.FromStream(Deserializer deserializer) factory = deserializer.GetFactory(fullName); // This is only here for efficiency (you don't have to cast every instance) // since most objects won't be versioned. However it means that you have to - // opt into versioning or you will break on old formats. + // opt into versioning or you will break on old formats. if (minimumReaderVersion > 0) { IFastSerializableVersion instance = factory() as IFastSerializableVersion; - // File version must meet minimum version requirements. + // File version must meet minimum version requirements. if (instance != null && !(version >= instance.MinimumVersionCanRead)) { throw new SerializationException(string.Format("File format is version {0} App accepts formats >= {1}.", @@ -2550,15 +2551,15 @@ void IFastSerializable.FromStream(Deserializer deserializer) internal enum Tags : byte { - Error, // To improve debugabilty, 0 is an illegal tag. - NullReference, // Tag for a null object forwardReference. - ObjectReference, // followed by StreamLabel + Error, // To improve debugabilty, 0 is an illegal tag. + NullReference, // Tag for a null object forwardReference. + ObjectReference, // followed by StreamLabel ForwardReference, // followed by an index (Int32) into the Forward forwardReference array and a Type object BeginObject, // followed by Type object, ToStream data, tagged EndObject - BeginPrivateObject, // Like beginObject, but not placed in interning table on deserialiation - EndObject, // placed after an object to mark its end (for V2 fields, and debugability). + BeginPrivateObject, // Like beginObject, but not placed in interning table on deserialiation + EndObject, // placed after an object to mark its end (for V2 fields, and debugability). ForwardDefinition, // followed by a forward forwardReference index and an Object definition (BeginObject) - // This is used when a a forward forwardReference is actually defined. + // This is used when a a forward forwardReference is actually defined. // In important invarient is that you must always be able to 'skip' to the next object in the // serialization stream. For the first version, this happens naturally, but if you add @@ -2568,19 +2569,19 @@ internal enum Tags : byte // reading objects until it finds an unmatched 'EndObject' tag. Thus even though the V1 // FromStream call has no knowledge of the extra fields, they are properly skipped. For this // to work all most V1 fields must be tagged (so we know how to skip them even if we don't - // understand what they are for). That is what these tags are for. - // + // understand what they are for). That is what these tags are for. + // // ToStream routines are free to encode all fields with tags, which allows a bit more // debuggability, because the object's data can be decoded even if the Deserializer is not - // available. + // available. Byte, Int16, Int32, Int64, SkipRegion, - String, // Size of string (in bytes) followed by UTF8 bytes. + String, // Size of string (in bytes) followed by UTF8 bytes. Blob, - Limit, // Just past the last valid tag, used for asserts. + Limit, // Just past the last valid tag, used for asserts. } #endregion } diff --git a/src/FastSerialization/FastSerialization.csproj b/src/FastSerialization/FastSerialization.csproj index 7944e5d50..18e289168 100644 --- a/src/FastSerialization/FastSerialization.csproj +++ b/src/FastSerialization/FastSerialization.csproj @@ -2,7 +2,7 @@ - netstandard2.0 + net9.0;netstandard2.0 Microsoft.Diagnostics.FastSerialization Microsoft.Diagnostics.FastSerialization true @@ -14,6 +14,9 @@ $(FastSerializationVersion) $(FastSerializationVersion) $(FastSerializationVersion) + true + true + 9.0 @@ -27,6 +30,10 @@ + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/src/FastSerialization/MemoryMappedFileStreamReader.cs b/src/FastSerialization/MemoryMappedFileStreamReader.cs index 653f5dee3..f2ab96c2b 100644 --- a/src/FastSerialization/MemoryMappedFileStreamReader.cs +++ b/src/FastSerialization/MemoryMappedFileStreamReader.cs @@ -2,8 +2,8 @@ // This file is best viewed using outline mode (Ctrl-M Ctrl-O) // // This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in. -// It is available from http://www.codeplex.com/hyperAddin -// +// It is available from http://www.codeplex.com/hyperAddin +// using System; using System.IO; using System.Text; // For StringBuilder. @@ -12,6 +12,7 @@ using System.Runtime.InteropServices; using DeferedStreamLabel = FastSerialization.StreamLabel; using System.Diagnostics; +using System.Runtime.Versioning; namespace FastSerialization { @@ -29,6 +30,7 @@ public class MemoryMappedFileStreamReader : IStreamReader private long _capacity; private long _offset; + [SupportedOSPlatform("windows")] public MemoryMappedFileStreamReader(string mapName, long length, SerializationSettings settings) : this(MemoryMappedFile.OpenExisting(mapName, MemoryMappedFileRights.Read), length, leaveOpen: false, settings) { diff --git a/src/TraceEvent/AutomatedAnalysis/AnalyzerResolver.cs b/src/TraceEvent/AutomatedAnalysis/AnalyzerResolver.cs index 59f071be5..d2e839131 100644 --- a/src/TraceEvent/AutomatedAnalysis/AnalyzerResolver.cs +++ b/src/TraceEvent/AutomatedAnalysis/AnalyzerResolver.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis { diff --git a/src/TraceEvent/AutomatedAnalysis/DirectoryAnalyzerResolver.cs b/src/TraceEvent/AutomatedAnalysis/DirectoryAnalyzerResolver.cs index 42db06e57..d832db02b 100644 --- a/src/TraceEvent/AutomatedAnalysis/DirectoryAnalyzerResolver.cs +++ b/src/TraceEvent/AutomatedAnalysis/DirectoryAnalyzerResolver.cs @@ -1,4 +1,5 @@ -using System.IO; +using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Reflection; namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis @@ -6,6 +7,7 @@ namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis /// /// Analyzer resolver that searches the specified directory. /// + [RequiresUnreferencedCode("Loads new assemblies")] public class DirectoryAnalyzerResolver : AnalyzerResolver { private static string _baseDirectory; diff --git a/src/TraceEvent/AutomatedAnalysis/IAnalyzerProvider.cs b/src/TraceEvent/AutomatedAnalysis/IAnalyzerProvider.cs index c610c0d42..8c493afa5 100644 --- a/src/TraceEvent/AutomatedAnalysis/IAnalyzerProvider.cs +++ b/src/TraceEvent/AutomatedAnalysis/IAnalyzerProvider.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis { @@ -13,7 +15,7 @@ public sealed class AnalyzerProviderAttribute : Attribute /// Create an new instance of AnalyzerProviderAttribute which stores the Type of the IAnalyzerProvider for the assembly. /// /// The type contained in this assembly that implements IAnalyzerProvider. - public AnalyzerProviderAttribute(Type providerType) + public AnalyzerProviderAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type providerType) { ProviderType = providerType; } @@ -21,6 +23,7 @@ public AnalyzerProviderAttribute(Type providerType) /// /// The type that implements IAnalyzerProvider. /// + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public Type ProviderType { get; } } diff --git a/src/TraceEvent/Ctf/CtfChannel.cs b/src/TraceEvent/Ctf/CtfChannel.cs index 36e7e76e6..1fb2f30d2 100644 --- a/src/TraceEvent/Ctf/CtfChannel.cs +++ b/src/TraceEvent/Ctf/CtfChannel.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.InteropServices; @@ -162,23 +163,6 @@ private void ReadHeader() int bytes = _ctfStream.EventHeader.GetSize(); } - private bool ReadStruct(out T result) where T : struct - { - int size = Marshal.SizeOf(typeof(T)); - int read = TryReadExactlyCount(_buffer, 0, size); - - _position += read; - - if (size != read) - { - result = default(T); - return false; - } - - result = (T)Marshal.PtrToStructure(_handle.AddrOfPinnedObject(), typeof(T)); - return true; - } - public override bool CanRead { get diff --git a/src/TraceEvent/Ctf/CtfMetadataLegacyParser.cs b/src/TraceEvent/Ctf/CtfMetadataLegacyParser.cs index 98c2f2be7..8b7fbd5a0 100644 --- a/src/TraceEvent/Ctf/CtfMetadataLegacyParser.cs +++ b/src/TraceEvent/Ctf/CtfMetadataLegacyParser.cs @@ -426,7 +426,7 @@ public unsafe string GetMetadata() { // TODO: Currently we read all of metadata and then parse it. We could do better by // only reading one packet at a time. - byte[] headerBufer = new byte[Marshal.SizeOf(typeof(MetadataPacketHeader))]; + byte[] headerBufer = new byte[Marshal.SizeOf()]; byte[] buffer = null; StringBuilder sb = new StringBuilder(); diff --git a/src/TraceEvent/DynamicTraceEventParser.cs b/src/TraceEvent/DynamicTraceEventParser.cs index 1f80e3cf1..60d001f68 100644 --- a/src/TraceEvent/DynamicTraceEventParser.cs +++ b/src/TraceEvent/DynamicTraceEventParser.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Tracing; using System.IO; using System.Text; @@ -16,11 +17,11 @@ namespace Microsoft.Diagnostics.Tracing.Parsers { /// - /// A DynamicTraceEventParser is a parser that understands how to read the embedded manifests that occur in the - /// dataStream (System.Diagnostics.Tracing.EventSources do this). - /// + /// A DynamicTraceEventParser is a parser that understands how to read the embedded manifests that occur in the + /// dataStream (System.Diagnostics.Tracing.EventSources do this). + /// /// See also TDHDynamicTraceEventParser which knows how to read the manifest that are registered globally with - /// the machine. + /// the machine. /// public class DynamicTraceEventParser : TraceEventParser { @@ -31,12 +32,12 @@ public class DynamicTraceEventParser : TraceEventParser /// /// Create a new DynamicTraceEventParser (which can parse ETW providers that dump their manifests - /// to the ETW data stream) an attach it to the ETW data stream 'source'. + /// to the ETW data stream) an attach it to the ETW data stream 'source'. /// public DynamicTraceEventParser(TraceEventSource source) : base(source) { - // Try to retrieve persisted state + // Try to retrieve persisted state state = (DynamicTraceEventParserState)StateObject; if (state == null) { @@ -45,9 +46,9 @@ public DynamicTraceEventParser(TraceEventSource source) this.source.RegisterUnhandledEvent(CheckForDynamicManifest); } - // make a registeredParser to resolve self-describing events (and more). + // make a registeredParser to resolve self-describing events (and more). registeredParser = new RegisteredTraceEventParser(source, true); - // But cause any of its new definitions to work on my subscriptions. + // But cause any of its new definitions to work on my subscriptions. registeredParser.NewEventDefinition = OnNewEventDefintion; // make an eventPipeTraceEventParser to resolve EventPipe events eventPipeTraceEventParser = new EventPipeTraceEventParser(source, dontRegister: true); @@ -55,7 +56,7 @@ public DynamicTraceEventParser(TraceEventSource source) } /// - /// Returns a list of providers (their manifest) that this TraceParser knows about. + /// Returns a list of providers (their manifest) that this TraceParser knows about. /// public IEnumerable DynamicProviders { @@ -66,7 +67,7 @@ public IEnumerable DynamicProviders } /// - /// Given a manifest describing the provider add its information to the parser. + /// Given a manifest describing the provider add its information to the parser. /// public void AddDynamicProvider(ProviderManifest providerManifest, bool noThrowOnError = false) { @@ -76,7 +77,7 @@ public void AddDynamicProvider(ProviderManifest providerManifest, bool noThrowOn ProviderManifest prevManifest = null; if (state.providers.TryGetValue(providerManifest.Guid, out prevManifest)) { - // If the new manifest is not strictly better than the one we already have, ignore it. + // If the new manifest is not strictly better than the one we already have, ignore it. if (!providerManifest.BetterThan(prevManifest)) { // Trace.WriteLine("Dynamic: existing manifest just as good, returning"); @@ -84,7 +85,7 @@ public void AddDynamicProvider(ProviderManifest providerManifest, bool noThrowOn } } - // Register the new definitions. + // Register the new definitions. providerManifest.ParseProviderEvents(delegate (DynamicTraceEventData template) { return OnNewEventDefintion(template, prevManifest != null); @@ -93,12 +94,12 @@ public void AddDynamicProvider(ProviderManifest providerManifest, bool noThrowOn // Remember this serialized information.(do it afterward so ContainKey call above is accurate) state.providers[providerManifest.Guid] = providerManifest; - // Register the manifest event with myself so that I continue to get updated manifests. - // TODO we are 'leaking' these today. Clean them up on Dispose. + // Register the manifest event with myself so that I continue to get updated manifests. + // TODO we are 'leaking' these today. Clean them up on Dispose. var callback = new DynamicManifestTraceEventData(delegate (TraceEvent data) { CheckForDynamicManifest(data); }, providerManifest); source.RegisterEventTemplate(callback); - // Raise the event that says we found a new provider. + // Raise the event that says we found a new provider. var newProviderCallback = DynamicProviderAdded; if (newProviderCallback != null) { @@ -117,17 +118,17 @@ public void WriteAllManifests(string directoryPath) Directory.CreateDirectory(directoryPath); foreach (var providerManifest in DynamicProviders) { - + var filePath = Path.Combine(directoryPath, providerManifest.Name + ".manifest.xml"); providerManifest.WriteToFile(filePath); } } /// - /// Utility method that read all the manifests the directory 'directoryPath' into the parser. + /// Utility method that read all the manifests the directory 'directoryPath' into the parser. /// Manifests must end in a .man or .manifest.xml suffix. It will throw an error if - /// the manifest is incorrect or using unsupported options. - /// + /// the manifest is incorrect or using unsupported options. + /// public void ReadAllManifests(string directoryPath) { foreach (var fileName in Directory.GetFiles(directoryPath, "*.manifest.xml")) @@ -141,7 +142,7 @@ public void ReadAllManifests(string directoryPath) } /// - /// Override. + /// Override. /// public override bool IsStatic { get { return false; } } @@ -150,11 +151,11 @@ public void ReadAllManifests(string directoryPath) /// of ETW providers known to this DynamicTraceEventParser. This includes /// when the EventSource manifest events are encountered as well as any /// explicit calls to AddDynamicProvider. (including ReadAllManifests). - /// + /// /// The Parser will filter out duplicate manifest events, however if an /// old version of a provider's manifest is encountered, and later a newer /// version is encountered, you can receive this event more than once for - /// a single provider. + /// a single provider. /// public event Action DynamicProviderAdded; @@ -164,7 +165,7 @@ public void ReadAllManifests(string directoryPath) /// protected override string GetProviderName() { - // This parser covers more than one provider, so the convention is that you return null for the provider name. + // This parser covers more than one provider, so the convention is that you return null for the provider name. return null; } @@ -178,7 +179,7 @@ private bool CheckForDynamicManifest(TraceEvent data) return false; } - // We also are expecting only these tasks and opcodes. + // We also are expecting only these tasks and opcodes. if (data.Opcode != (TraceEventOpcode)0xFE || data.Task != (TraceEventTask)0xFFFE) { return false; @@ -192,7 +193,7 @@ private bool CheckForDynamicManifest(TraceEvent data) return false; } - // Look up our information. + // Look up our information. List partialManifestsForGuid; if (!partialManifests.TryGetValue(data.ProviderGuid, out partialManifestsForGuid)) { @@ -201,7 +202,7 @@ private bool CheckForDynamicManifest(TraceEvent data) } PartialManifestInfo partialManifest = null; - // PERF: Expansion of + // PERF: Expansion of // partialManifest = partialManifestsForGuid.Find(e => data.ProcessID == e.ProcessID && data.ThreadID == e.ThreadID); // that avoids the delegate allocation. foreach (var p in partialManifestsForGuid) @@ -220,7 +221,7 @@ private bool CheckForDynamicManifest(TraceEvent data) } ProviderManifest provider = partialManifest.AddChunk(data); - // We have a completed manifest, add it to our list. + // We have a completed manifest, add it to our list. if (provider != null) { partialManifestsForGuid.Remove(partialManifest); @@ -233,17 +234,17 @@ private bool CheckForDynamicManifest(TraceEvent data) } AddDynamicProvider(provider, true); - return true; // I should have added a manifest event, so re-lookup the event + return true; // I should have added a manifest event, so re-lookup the event } return false; } /// - /// Override + /// Override /// protected internal override void EnumerateTemplates(Func eventsToObserve, Action callback) { - // Normally state is setup in the constructor, but call can be invoked before the constructor has finished, + // Normally state is setup in the constructor, but call can be invoked before the constructor has finished, if (state == null) { state = (DynamicTraceEventParserState)StateObject; @@ -262,12 +263,12 @@ protected internal override void EnumerateTemplates(Func> partialManifests; - // It is not intuitive that self-describing events (which are arguably 'dynamic') are resolved by - // the RegisteredTraceEventParser. This is even more wacky in a mixed EventSource where some events + // It is not intuitive that self-describing events (which are arguably 'dynamic') are resolved by + // the RegisteredTraceEventParser. This is even more wacky in a mixed EventSource where some events // are resolved by dynamic manifest and some are self-describing. To avoid these issues DynamicTraceEventParsers - // be able to handle both (it can resolve anything a RegisteredTraceEventParser can). This - // RegisteredTraceEventParser is how this gets accomplished. + // be able to handle both (it can resolve anything a RegisteredTraceEventParser can). This + // RegisteredTraceEventParser is how this gets accomplished. private RegisteredTraceEventParser registeredParser; // It is enabling DynamicTraceEventParsers to handle the EventSource events from EventPipe. @@ -405,10 +406,10 @@ internal unsafe ProviderManifest AddChunk(TraceEvent data) #region internal classes /// /// DynamicTraceEventData is an event that knows how to take runtime information to parse event fields (and payload) - /// + /// /// This meta-data is distilled down to a array of field names and an array of PayloadFetches which contain enough - /// information to find the field data in the payload blob. This meta-data is used in the - /// DynamicTraceEventData.PayloadNames and DynamicTraceEventData.PayloadValue methods. + /// information to find the field data in the payload blob. This meta-data is used in the + /// DynamicTraceEventData.PayloadNames and DynamicTraceEventData.PayloadValue methods. /// internal class DynamicTraceEventData : TraceEvent, IFastSerializable { @@ -451,7 +452,7 @@ public override object PayloadValue(int index) Debug.Assert(computedSize <= this.EventDataLength); if ((int)ID != 0xFFFE) // If it is not a manifest event { - // TODO FIX NOW the || condition is a hack because PerfVIew.ClrEnableParameters fails. + // TODO FIX NOW the || condition is a hack because PerfVIew.ClrEnableParameters fails. Debug.Assert(computedSize <= this.EventDataLength || this.ProviderName == "PerfView"); } #endif @@ -461,7 +462,7 @@ public override object PayloadValue(int index) offset = SkipToField(payloadFetches, index, 0, EventDataLength, true); } - // Fields that are simply not present, (perfectly) we simply return null for. + // Fields that are simply not present, (perfectly) we simply return null for. if (offset == EventDataLength) { return null; @@ -483,7 +484,7 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int } Type type = payloadFetch.Type; - // Is this a struct field? + // Is this a struct field? PayloadFetchClassInfo classInfo = payloadFetch.Class; if (classInfo != null) { @@ -502,7 +503,7 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int { var arrayCount = GetCountForArray(payloadFetch, arrayInfo, ref offset); var elementType = arrayInfo.Element.Type; - + // Arrays of characters can be deserialized as strings if desired. if(type == typeof(string)) { @@ -525,7 +526,7 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int } // TODO this is very inefficient for blitable types. Optimize that. - var ret = Array.CreateInstance(elementType, arrayCount); + var ret = new object[arrayCount]; for (int i = 0; i < arrayCount; i++) { object value = GetPayloadValueAt(ref arrayInfo.Element, offset, payloadLength); @@ -553,10 +554,10 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int // CONSIDER: The code below ensures that if you have fields that are // 'off the end' of a data that you return the default value. That // allows the parser to gracefully handle old events that have fewer - // fields but does NOT guarantee we don't read past the end of the + // fields but does NOT guarantee we don't read past the end of the // buffer in all cases (if you have corrupt/mismatched data). The // code below does ensure this but is more expensive. For now I have - // chosen the cheaper solution. + // chosen the cheaper solution. // // if ((uint)EventDataLength < OffsetOfNextField(offset, index)) // return GetDefaultValueByType(payloadFetches[index].type); @@ -601,7 +602,7 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int } if (unicodeByteCountString) { - size /= 2; // Unicode string with BYTE count. Element count is half that. + size /= 2; // Unicode string with BYTE count. Element count is half that. } } else @@ -609,7 +610,7 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int return "[CANT PARSE STRING]"; } } - else if (size > 0x8000) // What is this? looks like a hack. + else if (size > 0x8000) // What is this? looks like a hack. { size -= 0x8000; isAnsi = true; @@ -630,7 +631,7 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int { return (char)GetByteAt(offset); } - else + else { return (char)GetInt16At(offset); } @@ -714,8 +715,8 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int } /// - /// Used by PayloadValue to represent a structure. It is basically a IDictionary with a ToString() that - /// returns the value as JSON. + /// Used by PayloadValue to represent a structure. It is basically a IDictionary with a ToString() that + /// returns the value as JSON. /// internal class StructValue : IDictionary { @@ -867,10 +868,10 @@ public bool ContainsKey(string key) #endregion } - // Return the default value for a given type - private object GetDefaultValueByType(Type type) + // Return the default value for a given type + private object GetDefaultValueByType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) { - if (type == typeof(string)) // Activator.CreateInstance does not work on strings. + if (type == typeof(string)) // Activator.CreateInstance does not work on strings. { return String.Empty; } @@ -885,7 +886,7 @@ private object GetDefaultValueByType(Type type) /// public override string PayloadString(int index, IFormatProvider formatProvider = null) { - // See if you can do enumeration mapping. + // See if you can do enumeration mapping. var map = payloadFetches[index].Map; if (map != null) { @@ -899,7 +900,7 @@ public override string PayloadString(int index, IFormatProvider formatProvider = if (map is SortedDictionary) { StringBuilder sb = new StringBuilder(); - // It is a bitmap, compute the bits from the bitmap. + // It is a bitmap, compute the bits from the bitmap. foreach (var keyValue in map) { if (asLong == 0) @@ -953,7 +954,7 @@ public override string PayloadString(int index, IFormatProvider formatProvider = } } - // Otherwise do the default transformations. + // Otherwise do the default transformations. return base.PayloadString(index, formatProvider); } /// @@ -979,24 +980,24 @@ public override string GetFormattedMessage(IFormatProvider formatProvider) return base.GetFormattedMessage(formatProvider); } - // TODO is this error handling OK? - // Replace all %N with the string value for that parameter. + // TODO is this error handling OK? + // Replace all %N with the string value for that parameter. return paramReplacer.Replace(MessageFormat, delegate (Match m) { int targetIndex = int.Parse(m.Groups[1].Value) - 1; // for some array and string values, we remove the length field. Account // for that when we are resolving the %X qualifers by searching up from - // 0 adjusting along the way for removed fields. + // 0 adjusting along the way for removed fields. int index = 0; for (int fixedIndex = 0; fixedIndex < payloadFetches.Length; fixedIndex++) { - // This field is the length field that was removed from the payloafFetches array. + // This field is the length field that was removed from the payloafFetches array. if (DynamicTraceEventData.ConsumesFields(payloadFetches[fixedIndex].Size)) { if (index == targetIndex) { - // Try to output the correct length by getting the next value and computing its length. + // Try to output the correct length by getting the next value and computing its length. object obj = PayloadValue(fixedIndex); string asString = obj as string; if (asString != null) @@ -1010,9 +1011,9 @@ public override string GetFormattedMessage(IFormatProvider formatProvider) return asArray.Length.ToString(); } - return ""; // give up and return an empty string. + return ""; // give up and return an empty string. } - index++; // skip the removed field. + index++; // skip the removed field. } if (index == targetIndex) { @@ -1035,13 +1036,13 @@ private int SkipToField(PayloadFetch[] payloadFetches, int targetFieldIdx, int s // First find a valid fieldIdx, fieldOffset pair if (useCache && cachedEventId == EventIndex && cachedFieldIdx <= targetFieldIdx && startOffset == 0) { - // We fetched a previous field, great, start from there. + // We fetched a previous field, great, start from there. fieldOffset = cachedFieldOffset; fieldIdx = cachedFieldIdx; } else { - // no cached value, search backwards for the first field that has a fixed offset. + // no cached value, search backwards for the first field that has a fixed offset. fieldOffset = 0; fieldIdx = targetFieldIdx; while (0 < fieldIdx) @@ -1056,25 +1057,25 @@ private int SkipToField(PayloadFetch[] payloadFetches, int targetFieldIdx, int s fieldOffset += startOffset; } - // If we try to skip t fields that are not present, we simply stop at the end of the buffer. + // If we try to skip t fields that are not present, we simply stop at the end of the buffer. if (payloadLength <= fieldOffset) { return payloadLength; } // This can be N*N but because of our cache, it is not in the common case when you fetch - // fields in order. + // fields in order. while (fieldIdx < targetFieldIdx) { fieldOffset = OffsetOfNextField(ref payloadFetches[fieldIdx], fieldOffset, payloadLength); - // If we try to skip to fields that are not present, we simply stop at the end of the buffer. + // If we try to skip to fields that are not present, we simply stop at the end of the buffer. if (fieldOffset == payloadLength) { return payloadLength; } - // however if we truly go past the end of the buffer, something went wrong and we want to signal that. + // however if we truly go past the end of the buffer, something went wrong and we want to signal that. if (payloadLength < fieldOffset) { throw new ArgumentOutOfRangeException("Payload size exceeds buffer size."); @@ -1083,11 +1084,11 @@ private int SkipToField(PayloadFetch[] payloadFetches, int targetFieldIdx, int s fieldIdx++; } - // Remember our answer since can start there for the next field efficiently. + // Remember our answer since can start there for the next field efficiently. if (useCache && startOffset == 0) { #if DEBUG - // If we computed the result using the cache, compute it again without the cache and we should get the same answer. + // If we computed the result using the cache, compute it again without the cache and we should get the same answer. if (cachedEventId == this.EventIndex) { cachedEventId = EventIndex.Invalid; @@ -1103,7 +1104,7 @@ private int SkipToField(PayloadFetch[] payloadFetches, int targetFieldIdx, int s /// /// Returns the count of elements for the array represented by 'arrayInfo' - /// It also will adjust 'offset' so that it points at the first array element. + /// It also will adjust 'offset' so that it points at the first array element. /// private int GetCountForArray(PayloadFetch payloadFetch, PayloadFetchArrayInfo arrayInfo, ref int offset) { @@ -1171,7 +1172,7 @@ internal int OffsetOfNextField(ref PayloadFetch payloadFetch, int offset, int pa return SkipToField(classInfo.FieldFetches, classInfo.FieldFetches.Length, offset, payloadLength, false); } - // TODO cache this when you parse the value so that you don't need to do it twice. Right now it is pretty inefficient. + // TODO cache this when you parse the value so that you don't need to do it twice. Right now it is pretty inefficient. PayloadFetchArrayInfo arrayInfo = payloadFetch.Array; if (arrayInfo != null) { @@ -1226,7 +1227,7 @@ internal int OffsetOfNextField(ref PayloadFetch payloadFetch, int offset, int pa } if ((size & IS_ANSI) == 0 && (size & ELEM_COUNT) != 0) { - elemSize *= 2; // Counted (not byte counted) unicode string. chars are 2 wide. + elemSize *= 2; // Counted (not byte counted) unicode string. chars are 2 wide. } return offset + elemSize; @@ -1258,7 +1259,7 @@ internal static ushort SizeOfType(Type type) return 2; case TypeCode.UInt32: case TypeCode.Int32: - case TypeCode.Boolean: // We follow windows conventions and use 4 bytes for bool. + case TypeCode.Boolean: // We follow windows conventions and use 4 bytes for bool. case TypeCode.Single: return 4; case TypeCode.UInt64: @@ -1277,40 +1278,40 @@ internal static ushort SizeOfType(Type type) return POINTER_SIZE; } - throw new Exception("Unsupported type " + type.Name); // TODO + throw new Exception("Unsupported type " + type.Name); // TODO } } // IS_ANSI can be used to modify COUNTED_SIZE as well as NULL_TERMINATED - internal const ushort IS_ANSI = 1; // If set the string is ASCII, unset is UNICODE - // The following 3 bits are used to modify the 'COUNTED_SIZE' constant + internal const ushort IS_ANSI = 1; // If set the string is ASCII, unset is UNICODE + // The following 3 bits are used to modify the 'COUNTED_SIZE' constant internal const ushort BIT_32 = 2; // If set the count is a 32 bit number. unset is 16 bit - internal const ushort CONSUMES_FIELD = 4; // If set there was a explicit count field in the manifest, unset means no explicit field - internal const ushort ELEM_COUNT = 8; // If set count is a char/element count. unset means count is a count of BYTES. Does not include the size prefix itself + internal const ushort CONSUMES_FIELD = 4; // If set there was a explicit count field in the manifest, unset means no explicit field + internal const ushort ELEM_COUNT = 8; // If set count is a char/element count. unset means count is a count of BYTES. Does not include the size prefix itself internal static bool IsNullTerminated(ushort size) { return (size & ~IS_ANSI) == NULL_TERMINATED; } internal static bool IsCountedSize(ushort size) { return size >= COUNTED_SIZE; } internal static bool ConsumesFields(ushort size) { return IsCountedSize(size) && (size & CONSUMES_FIELD) != 0; } - // These are special sizes + // These are special sizes // sizes from 0xFFF0 through 0xFFFF are variations of VAR_SIZE - internal const ushort COUNTED_SIZE = 0xFFF0; // The size is variable. Size preceded the data, bits above tell more. + internal const ushort COUNTED_SIZE = 0xFFF0; // The size is variable. Size preceded the data, bits above tell more. // Size 0xFFEF is NULL_TERMINATED | IS_ANSI - internal const ushort NULL_TERMINATED = 0xFFEE; // value is a null terminated string. + internal const ushort NULL_TERMINATED = 0xFFEE; // value is a null terminated string. internal const ushort POINTER_SIZE = 0xFFED; // It is the pointer size of the target machine. internal const ushort VARINT = 0xFFEC; internal const ushort UNKNOWN_SIZE = 0xFFEB; // Generic unknown. - internal const ushort SPECIAL_SIZES = UNKNOWN_SIZE; // This is always the smallest size as an unsiged number. + internal const ushort SPECIAL_SIZES = UNKNOWN_SIZE; // This is always the smallest size as an unsiged number. internal struct PayloadFetch { /// /// Constructor for normal types, (int, string) ...) Also handles Enums (which are ints with a map) /// - public PayloadFetch(ushort offset, ushort size, Type type, IDictionary map = null) + public PayloadFetch(ushort offset, ushort size, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type, IDictionary map = null) { Offset = offset; Size = size; @@ -1320,7 +1321,7 @@ public PayloadFetch(ushort offset, ushort size, Type type, IDictionary /// Initialized a PayloadFetch for a given inType. REturns Size = DynamicTraceEventData.UNKNOWN_SIZE - /// if the type is unknown. + /// if the type is unknown. /// public PayloadFetch(ushort offset, RegisteredTraceEventParser.TdhInputType inType, int outType) @@ -1347,7 +1348,7 @@ public PayloadFetch(ushort offset, RegisteredTraceEventParser.TdhInputType inTyp } goto case RegisteredTraceEventParser.TdhInputType.Int8; // Fall through case RegisteredTraceEventParser.TdhInputType.Binary: - // Binary is an array of bytes. The later logic will transform it to array, thus Binary is like byte + // Binary is an array of bytes. The later logic will transform it to array, thus Binary is like byte case RegisteredTraceEventParser.TdhInputType.Int8: Type = typeof(byte); Size = 1; @@ -1407,7 +1408,7 @@ public PayloadFetch(ushort offset, RegisteredTraceEventParser.TdhInputType inTyp case RegisteredTraceEventParser.TdhInputType.CountedAnsiString: case RegisteredTraceEventParser.TdhInputType.CountedMbcsString: Type = typeof(string); - Size = DynamicTraceEventData.COUNTED_SIZE | DynamicTraceEventData.IS_ANSI + DynamicTraceEventData.ELEM_COUNT; // 16 Bit. + Size = DynamicTraceEventData.COUNTED_SIZE | DynamicTraceEventData.IS_ANSI + DynamicTraceEventData.ELEM_COUNT; // 16 Bit. break; case RegisteredTraceEventParser.TdhInputType.Struct: Type = typeof(DynamicTraceEventData.StructValue); @@ -1430,14 +1431,14 @@ private static bool ArrayElementCanProjectToString(PayloadFetch element) } /// - /// Returns a payload fetch for a Array. If you know the count, then you can give it. + /// Returns a payload fetch for a Array. If you know the count, then you can give it. /// public static PayloadFetch ArrayPayloadFetch(ushort offset, PayloadFetch element, ushort size, ushort fixedCount = 0, bool projectCharArrayAsString = true) { var ret = new PayloadFetch(); ret.Offset = offset; ret.Size = size; - ret.info = new PayloadFetchArrayInfo() + ret.info = new PayloadFetchArrayInfo() { Element = element, FixedCount = fixedCount, @@ -1477,7 +1478,7 @@ public static PayloadFetch RelLocPayloadFetch(ushort offset, PayloadFetch elemen var ret = new PayloadFetch(); ret.Offset = offset; ret.Size = 4; - ret.info = new PayloadFetchArrayInfo() + ret.info = new PayloadFetchArrayInfo() { Element = element, FixedCount = 0, @@ -1499,7 +1500,7 @@ public static PayloadFetch DataLocPayloadFetch(ushort offset, PayloadFetch eleme var ret = new PayloadFetch(); ret.Offset = offset; ret.Size = 4; - ret.info = new PayloadFetchArrayInfo() + ret.info = new PayloadFetchArrayInfo() { Element = element, FixedCount = 0, @@ -1543,12 +1544,12 @@ public static PayloadFetch StructPayloadFetch(ushort offset, PayloadFetchClassIn } /// - /// Offset from the beginning of the struct. + /// Offset from the beginning of the struct. /// public ushort Offset; // offset == MaxValue means variable size. // TODO come up with a real encoding for variable sized things - // See special encodings above (also size > 0x8000 means fixed length ANSI). + // See special encodings above (also size > 0x8000 means fixed length ANSI). public ushort Size; public bool IsFixedSize => Size < SPECIAL_SIZES; @@ -1559,13 +1560,14 @@ internal PayloadFetchClassInfo Class get { return info as PayloadFetchClassInfo; } } - // Non null if 'Type' is an array + // Non null if 'Type' is an array public PayloadFetchArrayInfo Array { get { return info as PayloadFetchArrayInfo; } } - public Type Type; // Currently null for arrays. + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + public Type Type; // Non null of 'Type' is a enum public IDictionary Map @@ -1583,10 +1585,10 @@ public IDictionary Map var asLazyMap = LazyMap; if (asLazyMap != null) { - ret = asLazyMap(); // resolve it. + ret = asLazyMap(); // resolve it. if (ret != null) { - info = ret; // If it resolves, remember the resolution for next time. + info = ret; // If it resolves, remember the resolution for next time. } } } @@ -1600,9 +1602,9 @@ public IDictionary Map } /// - /// LazyMap allow out to set a function that returns a map + /// LazyMap allow out to set a function that returns a map /// instead of the map itself. This will be evaluated when the map - /// is fetched (which gives time for the map table to be populated. + /// is fetched (which gives time for the map table to be populated. /// public Func> LazyMap { @@ -1742,7 +1744,7 @@ public void FromStream(Deserializer deserializer) } Map = map; } - else if (fetchType == 3) // Class + else if (fetchType == 3) // Class { PayloadFetchClassInfo classInfo = new PayloadFetchClassInfo(); @@ -1777,7 +1779,7 @@ public void FromStream(Deserializer deserializer) } } - private object info; // different things for enums, structs, or arrays. + private object info; // different things for enums, structs, or arrays. #endregion }; @@ -1874,7 +1876,7 @@ public void FromStream(Deserializer deserializer) internal string MessageFormat; // This is in ETW conventions (%N) internal bool registeredWithTraceEventSource; - // These are used to improve the performance of SkipToField. + // These are used to improve the performance of SkipToField. private EventIndex cachedEventId; private int cachedFieldIdx; private int cachedFieldOffset; @@ -1904,7 +1906,7 @@ internal DynamicManifestTraceEventData(Action action, ProviderManife public override object PayloadValue(int index) { - // The length of the manifest chunk is useful, so we expose it as an explict 'field' + // The length of the manifest chunk is useful, so we expose it as an explict 'field' if (index == 6) { return EventDataLength; @@ -1957,7 +1959,7 @@ public override StringBuilder ToXml(StringBuilder sb) /// /// DynamicTraceEventParserState represents the state of a DynamicTraceEventParser that needs to be /// serialized to a log file. It does NOT include information about what events are chosen but DOES contain - /// any other necessary information that came from the ETL data file. + /// any other necessary information that came from the ETL data file. /// internal class DynamicTraceEventParserState : IFastSerializable { @@ -1993,7 +1995,7 @@ void IFastSerializable.FromStream(Deserializer deserializer) #endregion /// - /// A ProviderManifest represents the XML manifest associated with the provider. + /// A ProviderManifest represents the XML manifest associated with the provider. /// public sealed class ProviderManifest : IFastSerializable { @@ -2007,10 +2009,10 @@ public ProviderManifest(Stream manifestStream, int manifestLen = int.MaxValue) id = "Stream"; int len = Math.Min((int)(manifestStream.Length - manifestStream.Position), manifestLen); serializedManifest = new byte[len]; - manifestStream.Read(serializedManifest, 0, len); + manifestStream.ReadExactly(serializedManifest, 0, len); } /// - /// Read a ProviderManifest from a file. + /// Read a ProviderManifest from a file. /// public ProviderManifest(string manifestFilePath) { @@ -2022,14 +2024,14 @@ public ProviderManifest(string manifestFilePath) /// /// Normally ProviderManifest will fail silently if there is a problem with the manifest. If /// you want to see this error you can all this method to force it explicitly It will - /// throw if there is a problem parsing the manifest. + /// throw if there is a problem parsing the manifest. /// public void ValidateManifest() { ParseProviderEvents((DynamicTraceEventData data) => EventFilterResponse.AcceptEvent, false); } - // write a manifest to a stream or a file. + // write a manifest to a stream or a file. /// /// Writes the manifest to 'outputStream' (as UTF8 XML text) /// @@ -2050,7 +2052,7 @@ public void WriteToFile(string filePath) } /// - /// Set if this manifest came from the ETL data stream file. + /// Set if this manifest came from the ETL data stream file. /// public bool ISDynamic { get; internal set; } /// @@ -2062,14 +2064,14 @@ public void WriteToFile(string filePath) /// public Guid Guid { get { if (!inited) { Init(); } return guid; } } /// - /// The version is defined as the sum of all the version numbers of event version numbers + the number of events defined. - /// This has the property that if you follow correct versioning protocol (all versions for a linear sequence where a new - /// versions is only modifies is predecessor by adding new events or INCREASING the version numbers of existing events) - /// then the version number defined below will always strictly increase. + /// The version is defined as the sum of all the version numbers of event version numbers + the number of events defined. + /// This has the property that if you follow correct versioning protocol (all versions for a linear sequence where a new + /// versions is only modifies is predecessor by adding new events or INCREASING the version numbers of existing events) + /// then the version number defined below will always strictly increase. /// /// It turns out that .NET Core removed some events from the TplEtwProvider. To allow removal of truly old events /// we also add 100* the largest event ID defined to the version number. That way if you add new events, even if you - /// removes some (less than 100) it will consider your 'better'. + /// removes some (less than 100) it will consider your 'better'. /// public int Version { @@ -2113,7 +2115,7 @@ public int Version } /// /// This is an arbitrary id given when the Manifest is created that - /// identifies where the manifest came from (e.g. a file name or an event etc). + /// identifies where the manifest came from (e.g. a file name or an event etc). /// public string Id { get { return id; } } @@ -2121,7 +2123,7 @@ public int Version /// Returns true if the current manifest is better to use than 'otherManifest' A manifest is /// better if it has a larger version number OR, they have the same version number and it is /// physically larger (we assume what happened is people added more properties but did not - /// update the version field appropriately). + /// update the version field appropriately). /// public bool BetterThan(ProviderManifest otherManifest) { @@ -2173,7 +2175,7 @@ internal ProviderManifest(byte[] serializedManifest, ManifestEnvelope.ManifestFo /// /// Call 'callback the the parsed templates for this provider. If 'callback' returns RejectProvider, bail early - /// Note that the DynamicTraceEventData passed to the delegate needs to be cloned if you use subscribe to it. + /// Note that the DynamicTraceEventData passed to the delegate needs to be cloned if you use subscribe to it. /// internal void ParseProviderEvents(Func callback, bool noThrowOnError) { @@ -2222,8 +2224,8 @@ internal void ParseProviderEvents(Func 2) { @@ -2409,13 +2411,13 @@ internal void ParseProviderEvents(Func /// Returns the .NET type corresponding to the manifest type 'manifestTypeName' - /// Returns null if it could not be found. + /// Returns null if it could not be found. /// + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] private static Type GetTypeForManifestTypeName(string manifestTypeName) { switch (manifestTypeName) @@ -2612,7 +2615,7 @@ private static Type GetTypeForManifestTypeName(string manifestTypeName) case "win:UnicodeString": return typeof(string); case "win:Binary": - return typeof(byte); // We special case this later to make it an array of this type. + return typeof(byte); // We special case this later to make it an array of this type. case "win:GUID": return typeof(Guid); case "win:FILETIME": @@ -2661,7 +2664,7 @@ void IFastSerializable.FromStream(Deserializer deserializer) /// /// Initialize the provider. This means to advance the instance variable 'reader' until it it is at the 'provider' node - /// in the XML. It also has the side effect of setting the name and guid. The rest waits until events are registered. + /// in the XML. It also has the side effect of setting the name and guid. The rest waits until events are registered. /// private void Init() { diff --git a/src/TraceEvent/ETWKernelControl.cs b/src/TraceEvent/ETWKernelControl.cs index 970a2fc53..d936c03ef 100644 --- a/src/TraceEvent/ETWKernelControl.cs +++ b/src/TraceEvent/ETWKernelControl.cs @@ -4,11 +4,13 @@ using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Reflection; using Microsoft.Diagnostics.Tracing.Parsers; using Microsoft.Win32; using KernelKeywords = Microsoft.Diagnostics.Tracing.Parsers.KernelTraceEventParser.Keywords; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Diagnostics.Tracing { @@ -19,7 +21,7 @@ public unsafe static class ETWKernelControl /// public static int StartKernelSession( out ulong TraceHandle, // EVENT_TRACE_PROPERTIES - void* propertyBuff, + void* propertyBuff, int propertyBuffLength, STACK_TRACING_EVENT_ID* stackTracingEventIds, int cStackTracingEventIds) @@ -71,7 +73,7 @@ public static int StartWindowsHeapSession( List extensions = new List(); /* Prep Extensions */ - // Turn on the Pids feature, selects which process to turn on. + // Turn on the Pids feature, selects which process to turn on. var pids = new ExtensionItem(ExtensionItemTypes.ETW_EXT_PIDS); pids.Data.Add(pid); extensions.Add(pids); @@ -91,7 +93,7 @@ public static int StartWindowsHeapSession( // Save Extensions. SaveExtensions(extensions, properties, extensionsOffset); - // Get the session name from the properties. + // Get the session name from the properties. var sessionName = new String((char*)(((byte*)properties) + properties->LoggerNameOffset)); // Actually start the session. @@ -103,6 +105,7 @@ public static int StartWindowsHeapSession( /// Turn on windows heap logging for a particular EXE file name (just the file name, no directory). /// This API is OK to call from one thread while Process() is being run on another. /// + [SupportedOSPlatform("windows")] public static int StartWindowsHeapSession( out ulong traceHandle, void* propertyBuff, @@ -113,7 +116,7 @@ public static int StartWindowsHeapSession( { var properties = (EVENT_TRACE_PROPERTIES*)propertyBuff; - // Get the session name from the properties. + // Get the session name from the properties. var sessionName = new String((char*)(((byte*)properties) + properties->LoggerNameOffset)); SetImageTracingFlags(sessionName, exeFileName, true); @@ -125,6 +128,7 @@ public static int StartWindowsHeapSession( /// Resets any windows heap tracing flags that might be set. /// Called during Stop. /// + [SupportedOSPlatform("windows")] public static void ResetWindowsHeapTracingFlags(string sessionName, bool noThrow = false) { lock ((object)s_KernelTraceControlLoaded) @@ -153,12 +157,12 @@ public static void ResetWindowsHeapTracingFlags(string sessionName, bool noThrow } /// - /// It is sometimes useful to merge the contents of several ETL files into a single - /// output ETL file. This routine does that. It also will attach additional - /// information that will allow correct file name and symbolic lookup if the + /// It is sometimes useful to merge the contents of several ETL files into a single + /// output ETL file. This routine does that. It also will attach additional + /// information that will allow correct file name and symbolic lookup if the /// ETL file is used on a machine other than the one that the data was collected on. - /// If you wish to transport the file to another machine you need to merge it, even - /// if you have only one file so that this extra information get incorporated. + /// If you wish to transport the file to another machine you need to merge it, even + /// if you have only one file so that this extra information get incorporated. /// /// The input ETL files to merge /// The output ETL file to produce. @@ -169,7 +173,7 @@ public static void Merge(string[] inputETLFileNames, string outputETLFileName, E IntPtr state = IntPtr.Zero; - // If we happen to be in the WOW, disable file system redirection as you don't get the System32 dlls otherwise. + // If we happen to be in the WOW, disable file system redirection as you don't get the System32 dlls otherwise. bool disableRedirection = Wow64DisableWow64FsRedirection(ref state) != 0; try { @@ -186,7 +190,7 @@ public static void Merge(string[] inputETLFileNames, string outputETLFileName, E } } - #region private + #region private #region ETW Tracing from KernelTraceControl.h [DllImport("KernelTraceControl.dll", CharSet = CharSet.Unicode)] @@ -270,8 +274,20 @@ private static void LoadKernelTraceControl() { try { - string myAssemblyPath = typeof(ETWKernelControl).GetTypeInfo().Assembly.ManifestModule.FullyQualifiedName; - string myAssemblyDir = Path.GetDirectoryName(myAssemblyPath); + [UnconditionalSuppressMessage( + "SingleFile", + "IL3000:Avoid accessing Assembly file path when publishing as a single file", + Justification = "Correctly handles the case that Location is empty" + )] + string GetToolsDir() + { + var assemblyLoc = typeof(ETWKernelControl).Assembly.Location; + return string.IsNullOrEmpty(assemblyLoc) + ? AppContext.BaseDirectory + : Path.GetDirectoryName(assemblyLoc); + } + + string myAssemblyDir = GetToolsDir(); string arch = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"); var kernelTraceControlPath = Path.Combine(Path.Combine(myAssemblyDir, arch), "KernelTraceControl.dll"); IntPtr result = LoadLibrary(kernelTraceControlPath); @@ -291,7 +307,7 @@ private static void LoadKernelTraceControl() } /// - /// Computes the location (offset) where extensions can be put in 'Properties'. + /// Computes the location (offset) where extensions can be put in 'Properties'. /// private static unsafe int ExtensionsOffset(EVENT_TRACE_PROPERTIES* properties, int propertyBuffLength) { @@ -372,7 +388,7 @@ unsafe private static int SaveExtensions(List extensions, // First WORD is len (including header) in DWORDS, next is Type *ptr++ = (extension.Data.Count + 1) + (((int)extension.Type) << 16); - // Write the data + // Write the data for (int i = 0; i < extension.Data.Count; i++) *ptr++ = extension.Data[i]; } @@ -394,10 +410,10 @@ private static unsafe void PutStacksIntoExtensions(List extension converter[VirtualAllocTaskGuid] = 0x2; // EVENT_TRACE_GROUP_MEMORY converter[MemoryTaskGuid] = 0x2; converter[ProcessTaskGuid] = 0x3; - converter[FileIOTaskGuid] = 0x4; // EVENT_TRACE_GROUP_FILE + converter[FileIOTaskGuid] = 0x4; // EVENT_TRACE_GROUP_FILE converter[ThreadTaskGuid] = 0x5; - converter[RegistryTaskGuid] = 0x9; // EVENT_TRACE_GROUP_REGISTRY - converter[PerfInfoTaskGuid] = 0xF; // EVENT_TRACE_GROUP_PERFINFO + converter[RegistryTaskGuid] = 0x9; // EVENT_TRACE_GROUP_REGISTRY + converter[PerfInfoTaskGuid] = 0xF; // EVENT_TRACE_GROUP_PERFINFO converter[ObjectTaskGuid] = 0x11; // EVENT_TRACE_GROUP_OBJECT var stackSpec = new ExtensionItem(ExtensionItemTypes.ETW_EXT_STACKWALK_FILTER); @@ -480,6 +496,7 @@ private static void PutEnableFlagsIntoExtensions(List extensions, /// /// Helper function used to implement EnableWindowsHeapProvider. /// + [SupportedOSPlatform("windows")] private static void SetImageTracingFlags(string sessionName, string exeFileName, bool set) { // We always want the native registry for the OS (64 bit on a 64 bit machine). @@ -501,7 +518,7 @@ private static void SetImageTracingFlags(string sessionName, string exeFileName, using (RegistryKey perfViewKey = Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\TraceEvent\HeapTracing")) { var prevValue = perfViewKey.GetValue(sessionName, null); - // Remove any old values. + // Remove any old values. if (prevValue != null) ResetWindowsHeapTracingFlags(sessionName); perfViewKey.SetValue(sessionName, exeFileName, RegistryValueKind.String); @@ -590,7 +607,7 @@ public struct STACK_TRACING_EVENT_ID } /// - /// Flags to influence what happens when ETL files are Merged. + /// Flags to influence what happens when ETL files are Merged. /// [Flags] public enum EVENT_TRACE_MERGE_EXTENDED_DATA diff --git a/src/TraceEvent/ETWReloggerTraceEventSource.cs b/src/TraceEvent/ETWReloggerTraceEventSource.cs index 088f38a43..aef171621 100644 --- a/src/TraceEvent/ETWReloggerTraceEventSource.cs +++ b/src/TraceEvent/ETWReloggerTraceEventSource.cs @@ -6,26 +6,27 @@ using System.Diagnostics; using System.Diagnostics.Tracing; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using TraceReloggerLib; using Microsoft.Diagnostics.Utilities; -#pragma warning disable 0414 // This is is because m_scratchBufferSize was #if conditionally removed, and I don't want it to complain about it. +#pragma warning disable 0414 // This is is because m_scratchBufferSize was #if conditionally removed, and I don't want it to complain about it. namespace Microsoft.Diagnostics.Tracing { /// - /// ETWReloggerTraceEventSource is designed to be able to write ETW files using an existing ETW input stream (either a file, files or real time session) as a basis. - /// The relogger capabilities only exist on Windows 8 OSes and beyond. - /// + /// ETWReloggerTraceEventSource is designed to be able to write ETW files using an existing ETW input stream (either a file, files or real time session) as a basis. + /// The relogger capabilities only exist on Windows 8 OSes and beyond. + /// /// The right way to think about this class is that it is just like ETWTraceEventSource, but it also has a output file associated with it, and WriteEvent APIs that - /// can be used to either copy events from the event stream (the common case), or inject new events (high level stats). + /// can be used to either copy events from the event stream (the common case), or inject new events (high level stats). /// public unsafe class ETWReloggerTraceEventSource : TraceEventDispatcher { /// /// Create an ETWReloggerTraceEventSource that can takes its input from the family of etl files inputFileName /// and can write them to the ETL file outputFileName (.kernel*.etl, .user*.etl .clr*.etl) - /// + /// /// This is a shortcut for ETWReloggerTraceEventSource(inputFileName, TraceEventSourceType.MergeAll, outputFileStream) /// public ETWReloggerTraceEventSource(string inputFileName, string outputFileName) @@ -35,7 +36,7 @@ public ETWReloggerTraceEventSource(string inputFileName, string outputFileName) /// /// Create an ETWReloggerTraceEventSource that can takes its input from a variety of sources (either a single file, /// a set of files, or a real time ETW session (based on 'type'), and can write these events to a new ETW output - /// file 'outputFileName. + /// file 'outputFileName. /// public ETWReloggerTraceEventSource(string fileOrSessionName, TraceEventSourceType type, string outputFileName) : base() @@ -78,12 +79,12 @@ public ETWReloggerTraceEventSource(string fileOrSessionName, TraceEventSourceTyp } /// - /// The output file can use a compressed form or not. Compressed forms can only be read on Win8 and beyond. Defaults to true. + /// The output file can use a compressed form or not. Compressed forms can only be read on Win8 and beyond. Defaults to true. /// public bool OutputUsesCompressedFormat { set { m_relogger.SetCompressionMode((sbyte)(value ? 1 : 0)); } } /// - /// Writes an event from the input stream to the output stream of events. + /// Writes an event from the input stream to the output stream of events. /// public void WriteEvent(TraceEvent data) { @@ -96,11 +97,11 @@ public void WriteEvent(TraceEvent data) } /// - /// Connect the given EventSource so any events logged from it will go to the output stream of events. + /// Connect the given EventSource so any events logged from it will go to the output stream of events. /// Once connected, you may only write events from this EventSource while processing the input stream /// (that is during the callback of an input stream event), because the context for the EventSource event /// (e.g. timestamp, proesssID, threadID ...) will be derived from the current event being processed by - /// the input stream. + /// the input stream. /// public void ConnectEventSource(EventSource eventSource) { @@ -113,10 +114,10 @@ public void ConnectEventSource(EventSource eventSource) } #if true // TODO Decide if we want to expose these or not, ConnenctEventSource may be enough. These are a bit clunky especially but do allow the - // ability to modify events you don't own, which may be useful. + // ability to modify events you don't own, which may be useful. /// - /// Writes an event that did not exist previously into the data stream, The context data (time, process, thread, activity, comes from 'an existing event') + /// Writes an event that did not exist previously into the data stream, The context data (time, process, thread, activity, comes from 'an existing event') /// public unsafe void WriteEvent(Guid providerId, ref _EVENT_DESCRIPTOR eventDescriptor, TraceEvent template, params object[] payload) { @@ -128,8 +129,8 @@ public unsafe void WriteEvent(Guid providerId, ref _EVENT_DESCRIPTOR eventDescri fixed (_EVENT_DESCRIPTOR* fixedEventDescr = &eventDescriptor) { - // The interop assembly has its own def of EventDescriptor, but they are identical, so we use unsafe casting to - // bridge the gap. + // The interop assembly has its own def of EventDescriptor, but they are identical, so we use unsafe casting to + // bridge the gap. _EVENT_DESCRIPTOR* ptrDescr = (_EVENT_DESCRIPTOR*)fixedEventDescr; newEvent.SetEventDescriptor(ref *ptrDescr); @@ -144,14 +145,14 @@ public unsafe void WriteEvent(Guid providerId, ref _EVENT_DESCRIPTOR eventDescri public unsafe void WriteEvent(Guid providerId, ref _EVENT_DESCRIPTOR eventDescriptor, DateTime timeStamp, int processId, int processorIndex, int threadID, Guid activityID, params object[] payload) { - // Today we always create 64 bit events on 64 bit OSes. + // Today we always create 64 bit events on 64 bit OSes. var newEvent = m_relogger.CreateEventInstance(m_traceHandleForFirstStream, (pointerSize == 8) ? TraceEventNativeMethods.EVENT_HEADER_FLAG_64_BIT_HEADER : TraceEventNativeMethods.EVENT_HEADER_FLAG_32_BIT_HEADER); fixed (_EVENT_DESCRIPTOR* fixedEventDescr = &eventDescriptor) { - // The interop assembly has its own def of EventDescriptor, but they are identical, so we use unsafe casting to - // bridge the gap. + // The interop assembly has its own def of EventDescriptor, but they are identical, so we use unsafe casting to + // bridge the gap. _EVENT_DESCRIPTOR* ptrDescr = (_EVENT_DESCRIPTOR*)fixedEventDescr; newEvent.SetEventDescriptor(ref *ptrDescr); @@ -193,13 +194,14 @@ public override bool Process() /// /// Implements TraceEventDispatcher.Dispose /// + [SupportedOSPlatform("windows")] protected override void Dispose(bool disposing) { if (disposing) { if (m_relogger != null) { - Marshal.FinalReleaseComObject(m_relogger); // Force the com object to die. + Marshal.FinalReleaseComObject(m_relogger); // Force the com object to die. } if (m_eventListener != null) @@ -235,7 +237,7 @@ private unsafe void SetPayload(ITraceEvent newEvent, IList payloadArgs) // Where we are writing the serialized data in m_scratchBuffer int curBlobPtr = 0; - // Need to serialize the objects according to ETW serialization conventions. + // Need to serialize the objects according to ETW serialization conventions. foreach (var payloadArg in payloadArgs) { var argType = payloadArg.GetType(); @@ -246,7 +248,7 @@ private unsafe void SetPayload(ITraceEvent newEvent, IList payloadArgs) var newCurBlobPtr = curBlobPtr + bytesNeeded; EnsureSratchBufferSpace(newCurBlobPtr); - // Copy the string. + // Copy the string. char* toPtr = (char*)(&m_scratchBuffer[curBlobPtr]); fixed (char* fromPtr = asString) { @@ -310,7 +312,7 @@ private void EnsureSratchBufferSpace(int requriedSize) } /// - /// This is used by the ConnectEventSource to route events from the EventSource to the relogger. + /// This is used by the ConnectEventSource to route events from the EventSource to the relogger. /// private class ReloggerEventListener : EventListener { @@ -326,8 +328,8 @@ protected override void OnEventWritten(EventWrittenEventArgs eventData) var relogger = m_relogger; var newEvent = relogger.m_relogger.CreateEventInstance(relogger.m_traceHandleForFirstStream, 0); - // The interop assembly has its own def of EventDescriptor, but they are identical, so we use unsafe casting to - // bridge the gap. + // The interop assembly has its own def of EventDescriptor, but they are identical, so we use unsafe casting to + // bridge the gap. _EVENT_DESCRIPTOR descr = new _EVENT_DESCRIPTOR(); descr.Id = (ushort)eventData.EventId; descr.Keyword = (ulong)eventData.Keywords; @@ -347,7 +349,7 @@ protected override void OnEventWritten(EventWrittenEventArgs eventData) newEvent.SetProcessId(eventRecord->EventHeader.ProcessId); newEvent.SetTimeStamp(ref eventRecord->EventHeader.TimeStamp); - // Copy over the payload. + // Copy over the payload. relogger.SetPayload(newEvent, eventData.Payload); relogger.m_relogger.Inject(newEvent); } @@ -369,7 +371,7 @@ private void SendManifestIfNecessary(EventSource eventSource) m_sentManifest[eventSourceIdx] = true; - // Get the manifest and send it. + // Get the manifest and send it. var manifestStr = EventSource.GenerateManifest(eventSource.GetType(), eventSource.Name); var manifestBytes = System.Text.Encoding.UTF8.GetBytes(manifestStr); m_relogger.SendManifest(manifestBytes, eventSource); @@ -380,7 +382,7 @@ private void SendManifestIfNecessary(EventSource eventSource) } /// - /// This is the class the Win32 APIs call back on. + /// This is the class the Win32 APIs call back on. /// private unsafe class ReloggerCallbacks : ITraceEventCallback { @@ -392,6 +394,7 @@ public void OnBeginProcessTrace(ITraceEvent headerEvent, CTraceRelogger relogger Initialize(rawData); } + [SupportedOSPlatform("windows")] public void OnEvent(ITraceEvent eventData, CTraceRelogger relogger) { var rawData = (TraceEventNativeMethods.EVENT_RECORD*)eventData.GetEventRecord(); @@ -410,7 +413,7 @@ public void OnEvent(ITraceEvent eventData, CTraceRelogger relogger) Debug.Assert(rawData->EventHeader.HeaderType == 0); // if non-zero probably old-style ETW header - // Give it an event ID if it does not have one. + // Give it an event ID if it does not have one. source.m_traceLoggingEventId.TestForTraceLoggingEventAndFixupIfNeeded(rawData); // Lookup the event; @@ -422,7 +425,7 @@ public void OnEvent(ITraceEvent eventData, CTraceRelogger relogger) // Keep in mind that for UnhandledTraceEvent 'PrepForCallback' has NOT been called, which means the // opcode, guid and eventIds are not correct at this point. The ToString() routine WILL call // this so if that is in your debug window, it will have this side effect (which is good and bad) - // Looking at rawData will give you the truth however. + // Looking at rawData will give you the truth however. anEvent.DebugValidate(); if (anEvent.NeedsFixup) @@ -432,8 +435,8 @@ public void OnEvent(ITraceEvent eventData, CTraceRelogger relogger) source.Dispatch(anEvent); - // Release the COM object aggressively Otherwise you build up quite a few of these before - // the GC kicks in and cleans them all up. + // Release the COM object aggressively Otherwise you build up quite a few of these before + // the GC kicks in and cleans them all up. Marshal.FinalReleaseComObject(source.m_curITraceEvent); source.m_curITraceEvent = null; } @@ -455,7 +458,7 @@ private unsafe void Initialize(TraceEventNativeMethods.EVENT_RECORD* rawData) { // We did not get a if (m_source.sessionStartTimeQPC != 0) EventTraceHeaderTraceData either in the OnBeginProcessTrace callback (file based case) or the // first event (real time case). This is really a problem, as we need that information, but we will go ahead and - // try to initialize as best we can. + // try to initialize as best we can. m_source.pointerSize = ETWTraceEventSource.GetOSPointerSize(); m_source.numberOfProcessors = Environment.ProcessorCount; @@ -507,7 +510,7 @@ internal unsafe void SendManifest(byte[] rawManifest, EventSource eventSource) envelope.Format = ManifestEnvelope.ManifestFormats.SimpleXmlFormat; envelope.MajorVersion = 1; envelope.MinorVersion = 0; - envelope.Magic = 0x5B; // An unusual number that can be checked for consistancy. + envelope.Magic = 0x5B; // An unusual number that can be checked for consistancy. int dataLeft = rawManifest.Length; envelope.TotalChunks = (ushort)((dataLeft + (ManifestEnvelope.MaxChunkSize - 1)) / ManifestEnvelope.MaxChunkSize); envelope.ChunkNumber = 0; @@ -534,7 +537,7 @@ internal unsafe void SendManifest(byte[] rawManifest, EventSource eventSource) int bufferSize = sizeof(ManifestEnvelope) + Math.Min(ManifestEnvelope.MaxChunkSize, rawManifest.Length); byte[] buffer = new byte[bufferSize]; - int manifestIdx = 0; // Where we are in the manifest. + int manifestIdx = 0; // Where we are in the manifest. while (dataLeft > 0) { // Copy envelope into buffer @@ -545,13 +548,13 @@ internal unsafe void SendManifest(byte[] rawManifest, EventSource eventSource) buffer[bufferIdx++] = *envelopePtr++; } - // Copy chunk of manifest into buffer + // Copy chunk of manifest into buffer while (bufferIdx < buffer.Length && manifestIdx < rawManifest.Length) { buffer[bufferIdx++] = rawManifest[manifestIdx++]; } - // write the envelope + chunk. + // write the envelope + chunk. fixed (byte* bufferPtr = buffer) { manifestEvent.SetPayload(ref *bufferPtr, (uint)bufferIdx); @@ -571,9 +574,9 @@ internal unsafe void SendManifest(byte[] rawManifest, EventSource eventSource) private int m_eventsLost; private byte* m_scratchBuffer; private int m_scratchBufferSize; - private ITraceEvent m_curITraceEvent; // Before we make callbacks we remember the ITraceEvent - private TraceEventNativeMethods.EVENT_RECORD* m_curTraceEventRecord; // This is the TraceEvent eventRecord that corresponds to the ITraceEvent. - private TraceLoggingEventId m_traceLoggingEventId; // Used to give TraceLogging events Event IDs. + private ITraceEvent m_curITraceEvent; // Before we make callbacks we remember the ITraceEvent + private TraceEventNativeMethods.EVENT_RECORD* m_curTraceEventRecord; // This is the TraceEvent eventRecord that corresponds to the ITraceEvent. + private TraceLoggingEventId m_traceLoggingEventId; // Used to give TraceLogging events Event IDs. #endregion } diff --git a/src/TraceEvent/NativeDlls.cs b/src/TraceEvent/NativeDlls.cs index 9a7868896..4a5088d01 100644 --- a/src/TraceEvent/NativeDlls.cs +++ b/src/TraceEvent/NativeDlls.cs @@ -1,12 +1,13 @@ using Microsoft.Diagnostics.Tracing.Compatibility; using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using System.Runtime.InteropServices; /// -/// Finds native DLLS next to the managed DLL that uses them. +/// Finds native DLLS next to the managed DLL that uses them. /// internal class NativeDlls { @@ -17,14 +18,15 @@ internal class NativeDlls /// /// Loads a native DLL with a filename-extension of 'simpleName' by adding the path of the currently executing assembly - /// + /// /// /// + [UnconditionalSuppressMessage("SingleFile", "IL3002", Justification = "This method correctly handles the case where the assembly has an unknown location")] public static void LoadNative(string simpleName) { // When TraceEvent is loaded as embedded assembly the manifest path is // We use as fallback in that case the process executable location to enable scenarios where TraceEvent and related dlls - // are loaded from byte arrays into the AppDomain to create self contained executables with no other dependent libraries. + // are loaded from byte arrays into the AppDomain to create self contained executables with no other dependent libraries. string assemblyLocation = typeof(NativeDlls).GetTypeInfo().Assembly.ManifestModule.FullyQualifiedName; if (assemblyLocation == UnknownLocation) { @@ -49,7 +51,7 @@ public static void LoadNative(string simpleName) return; } - // Try in ../native/. This is where it will be in a nuget package. + // Try in ../native/. This is where it will be in a nuget package. dllName = Path.Combine(Path.GetDirectoryName(thisDllDir), "native", ProcessArchitectureDirectory, simpleName); ret = LoadLibraryEx(dllName, IntPtr.Zero, LoadLibraryFlags.LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); if (ret != IntPtr.Zero) diff --git a/src/TraceEvent/Parsers/UniversalSystemTraceEventParser.cs b/src/TraceEvent/Parsers/UniversalSystemTraceEventParser.cs index 4694b1a3e..93454aa05 100644 --- a/src/TraceEvent/Parsers/UniversalSystemTraceEventParser.cs +++ b/src/TraceEvent/Parsers/UniversalSystemTraceEventParser.cs @@ -3,6 +3,7 @@ using System.Text; using System.Text.Json; using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; using Address = System.UInt64; #pragma warning disable 1591 // disable warnings on XML comments not being present @@ -353,11 +354,11 @@ public sealed class ProcessMappingMetadataTraceData : TraceEvent { public ulong Id { get { return GetVarUIntAt(0); } } - public string SymbolMetadata {get { return GetShortUTF8StringAt(SkipVarInt(0)); } } + public string SymbolMetadata { get { return GetShortUTF8StringAt(SkipVarInt(0)); } } internal ProcessMappingSymbolMetadata ParsedSymbolMetadata { get { return ProcessMappingSymbolMetadataParser.TryParse(SymbolMetadata); } } - public string VersionMetadata {get {return GetShortUTF8StringAt(SkipShortUTF8String(SkipVarInt(0))); } } + public string VersionMetadata { get { return GetShortUTF8StringAt(SkipShortUTF8String(SkipVarInt(0))); } } #region Private internal ProcessMappingMetadataTraceData(Action action, int eventID, int task, string taskName, Guid taskGuid, int opcode, string opcodeName, Guid providerGuid, string providerName) @@ -428,17 +429,18 @@ internal static class ProcessMappingSymbolMetadataParser PropertyNameCaseInsensitive = true, Converters = { - new JsonStringEnumConverter(), + new JsonStringEnumConverter(), new GuidConverter(), new ProcessMappingSymbolMetadataConverter() - } + }, + TypeInfoResolver = UniversalSystemJsonContext.Default }; internal static ProcessMappingSymbolMetadata TryParse(string json) { try { - return JsonSerializer.Deserialize(json, Options); + return SourceGenJson.JsonDeserialize(json, Options); } catch { @@ -506,6 +508,11 @@ internal class ProcessMappingSymbolMetadataConverter : JsonConverter(root.GetRawText(), options); + return SourceGenJson.JsonDeserialize(root.GetRawText(), options); } else if (string.Equals("ELF", type, StringComparison.OrdinalIgnoreCase)) { - return JsonSerializer.Deserialize(root.GetRawText(), options); + return SourceGenJson.JsonDeserialize(root.GetRawText(), options); } else { @@ -528,7 +535,39 @@ public override ProcessMappingSymbolMetadata Read(ref Utf8JsonReader reader, Typ public override void Write(Utf8JsonWriter writer, ProcessMappingSymbolMetadata value, JsonSerializerOptions options) { - JsonSerializer.Serialize(writer, value, value.GetType(), options); + SourceGenJson.JsonSerialize(writer, value, UniversalSystemJsonContext.Default, options); } } + + [JsonSerializable(typeof(PEProcessMappingSymbolMetadata))] + [JsonSerializable(typeof(ELFProcessMappingSymbolMetadata))] + [JsonSerializable(typeof(ProcessMappingSymbolMetadata))] + internal partial class UniversalSystemJsonContext : JsonSerializerContext + { } +} + +internal static class SourceGenJson +{ + /// + /// Deserialize a JSON string into an object of type T using the provided JsonSerializerOptions. + /// Assumes that the options are already configured with the appropriate TypeInfoResolver. + /// + public static T JsonDeserialize(string json, JsonSerializerOptions options) + { + return JsonSerializer.Deserialize(json, (JsonTypeInfo)options.GetTypeInfo(typeof(T))); + } + + public static void JsonSerialize(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, (JsonTypeInfo)options.GetTypeInfo(typeof(T))); + } + + public static void JsonSerialize(Utf8JsonWriter writer, T value, JsonSerializerContext ctx, JsonSerializerOptions options) + { + options = new JsonSerializerOptions(options) + { + TypeInfoResolver = ctx + }; + JsonSerialize(writer, value, options); + } } diff --git a/src/TraceEvent/Symbols/NativeSymbolModule.cs b/src/TraceEvent/Symbols/NativeSymbolModule.cs index 42d5e1cbe..35bfd1ee7 100644 --- a/src/TraceEvent/Symbols/NativeSymbolModule.cs +++ b/src/TraceEvent/Symbols/NativeSymbolModule.cs @@ -9,21 +9,22 @@ using System.Text; using System.Text.RegularExpressions; using Microsoft.Diagnostics.Utilities; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Diagnostics.Symbols { /// - /// A NativeSymbolModule represents symbol information for a native code module. - /// NativeSymbolModules can potentially represent Managed modules (which is why it is a subclass of that interface). - /// + /// A NativeSymbolModule represents symbol information for a native code module. + /// NativeSymbolModules can potentially represent Managed modules (which is why it is a subclass of that interface). + /// /// NativeSymbolModule should just be the CONTRACT for Native Symbols (some subclass implements /// it for a particular format like Windows PDBs), however today because we have only one file format we - /// simply implement Windows PDBS here. This can be factored out of this class when we + /// simply implement Windows PDBS here. This can be factored out of this class when we /// support other formats (e.g. Dwarf). - /// - /// To implement support for Windows PDBs we use the Debug Interface Access (DIA). See + /// + /// To implement support for Windows PDBs we use the Debug Interface Access (DIA). See /// http://msdn.microsoft.com/library/x93ctkx8.aspx for more. I have only exposed what - /// I need, and the interface is quite large (and not super pretty). + /// I need, and the interface is quite large (and not super pretty). /// public unsafe class NativeSymbolModule : ManagedSymbolModule, IDisposable, ISymbolLookup { @@ -39,8 +40,8 @@ public string GetTypeForHeapAllocationSite(uint rva) } /// - /// Finds a (method) symbolic name for a given relative virtual address of some code. - /// Returns an empty string if a name could not be found. + /// Finds a (method) symbolic name for a given relative virtual address of some code. + /// Returns an empty string if a name could not be found. /// public string FindNameForRva(uint rva) { @@ -50,15 +51,15 @@ public string FindNameForRva(uint rva) return FindNameForRva(rva, ref dummy); } /// - /// Finds a (method) symbolic name for a given relative virtual address of some code. - /// Returns an empty string if a name could not be found. - /// symbolStartRva is set to the start of the symbol start + /// Finds a (method) symbolic name for a given relative virtual address of some code. + /// Returns an empty string if a name could not be found. + /// symbolStartRva is set to the start of the symbol start /// public string FindNameForRva(uint rva, ref uint symbolStartRva) { ThrowIfDisposed(); - System.Threading.Thread.Sleep(0); // Allow cancellation. + System.Threading.Thread.Sleep(0); // Allow cancellation. if (m_symbolsByAddr == null) { return ""; @@ -90,13 +91,13 @@ public string FindNameForRva(uint rva, ref uint symbolStartRva) { m_reader.Log.WriteLine("Warning: NOT IN RANGE: address 0x{0:x} start {2:x} end {3:x} Offset {4:x} Len {5:x}, symbol {1}, prefixing with ??.", rva, ret, symbolRva, symbolRva + symbolLen, rva - symbolRva, symbolLen); - ret = "??" + ret; // Prefix with ?? to indicate it is questionable. + ret = "??" + ret; // Prefix with ?? to indicate it is questionable. } // TODO FIX NOW, should not need to do this hand-unmangling. if (0 <= ret.IndexOf('@')) { - // TODO relatively inefficient. + // TODO relatively inefficient. string unmangled = null; symbol.get_undecoratedNameEx(0x1000, out unmangled); if (unmangled != null) @@ -121,14 +122,14 @@ public string FindNameForRva(uint rva, ref uint symbolStartRva) } } - // See if this is a NGEN mangled name, which is $#Assembly#Token suffix. If so strip it off. + // See if this is a NGEN mangled name, which is $#Assembly#Token suffix. If so strip it off. var dollarIdx = ret.LastIndexOf('$'); if (0 <= dollarIdx && dollarIdx + 2 < ret.Length && ret[dollarIdx + 1] == '#' && 0 <= ret.IndexOf('#', dollarIdx + 2)) { ret = ret.Substring(0, dollarIdx); } - // See if we have a Project N map that maps $_NN to a pre-merged assembly name + // See if we have a Project N map that maps $_NN to a pre-merged assembly name var mergedAssembliesMap = GetMergedAssembliesMap(); if (mergedAssembliesMap != null) { @@ -142,7 +143,7 @@ public string FindNameForRva(uint rva, ref uint symbolStartRva) return GetAssemblyNameFromModuleIndex(mergedAssembliesMap, moduleIndex, original); }); - // By default - .NET native compilers do not generate a $#_ prefix for the methods coming from + // By default - .NET native compilers do not generate a $#_ prefix for the methods coming from // the assembly containing System.Object - the implicit module number is int.MaxValue if (!prefixMatchFound) @@ -164,7 +165,7 @@ private static string GetAssemblyNameFromModuleIndex(Dictionary mer var assemblyName = new AssemblyName(fullAssemblyName); return assemblyName.Name + "!"; } - catch (Exception) { } // Catch all AssemblyName fails with ' in the name. + catch (Exception) { } // Catch all AssemblyName fails with ' in the name. } return defaultValue; @@ -172,7 +173,7 @@ private static string GetAssemblyNameFromModuleIndex(Dictionary mer /// /// Fetches the source location (line number and file), given the relative virtual address (RVA) - /// of the location in the executable. + /// of the location in the executable. /// public SourceLocation SourceLocationForRva(uint rva) { @@ -184,13 +185,13 @@ public SourceLocation SourceLocationForRva(uint rva) /// /// This overload of SourceLocationForRva like the one that takes only an RVA will return a source location - /// if it can. However this version has additional support for NGEN images. In the case of NGEN images - /// for .NET V4.6.1 or later), the NGEN images can't convert all the way back to a source location, but they + /// if it can. However this version has additional support for NGEN images. In the case of NGEN images + /// for .NET V4.6.1 or later), the NGEN images can't convert all the way back to a source location, but they /// can convert the RVA back to IL artifacts (ilAssemblyName, methodMetadataToken, iloffset). These can then - /// be used to look up the source line using the IL PDB. - /// - /// Thus if the return value from this is null, check to see if the ilAssemblyName is non-null, and if not - /// you can look up the source location using that information. + /// be used to look up the source line using the IL PDB. + /// + /// Thus if the return value from this is null, check to see if the ilAssemblyName is non-null, and if not + /// you can look up the source location using that information. /// public SourceLocation SourceLocationForRva(uint rva, out string ilAssemblyName, out uint methodMetadataToken, out int ilOffset) { @@ -201,7 +202,7 @@ public SourceLocation SourceLocationForRva(uint rva, out string ilAssemblyName, ilOffset = -1; m_reader.m_log.WriteLine("SourceLocationForRva: looking up RVA {0:x} ", rva); - // First fetch the line number information 'normally'. (for the non-NGEN case, and old style NGEN (with /lines)). + // First fetch the line number information 'normally'. (for the non-NGEN case, and old style NGEN (with /lines)). uint fetchCount; IDiaEnumLineNumbers sourceLocs; m_session.findLinesByRVA(rva, 0, out sourceLocs); @@ -209,14 +210,14 @@ public SourceLocation SourceLocationForRva(uint rva, out string ilAssemblyName, sourceLocs.Next(1, out sourceLoc, out fetchCount); if (fetchCount == 0) { - // We have no native line number information. See if we are an NGEN image and we can convert the RVA to an IL Offset. + // We have no native line number information. See if we are an NGEN image and we can convert the RVA to an IL Offset. m_reader.m_log.WriteLine("SourceLocationForRva: did not find line info Looking for mangled symbol name (for NGEN pdbs)"); IDiaSymbol method = m_symbolsByAddr.symbolByRVA(rva); if (method != null) { // Check to see if the method name follows the .NET V4.6.1 conventions // of $#ASSEMBLY#TOKEN. If so the line number we got back is not a line number at all but - // an ILOffset. + // an ILOffset. string name = method.name; if (name != null) { @@ -238,8 +239,8 @@ public SourceLocation SourceLocationForRva(uint rva, out string ilAssemblyName, return null; } - // We need the metadata token and assembly. We get this from the name mangling of the method symbol, - // so look that up. + // We need the metadata token and assembly. We get this from the name mangling of the method symbol, + // so look that up. if (tokenIdx == suffixIdx + 2) // The assembly name is null { ilAssemblyName = Path.GetFileNameWithoutExtension(SymbolFilePath); @@ -259,7 +260,7 @@ public SourceLocation SourceLocationForRva(uint rva, out string ilAssemblyName, m_reader.m_log.WriteLine("SourceLocationForRva: Looking up IL Offset by RVA 0x{0:x}", rva); m_session.findILOffsetsByRVA(rva, 0, out sourceLocs); - // FEEFEE is some sort of illegal line number that is returned some time, It is better to ignore it. + // FEEFEE is some sort of illegal line number that is returned some time, It is better to ignore it. // and take the next valid line for (; ; ) { @@ -279,7 +280,7 @@ public SourceLocation SourceLocationForRva(uint rva, out string ilAssemblyName, ilOffset = 0; } m_reader.m_log.WriteLine("SourceLocationForRva: Found native to IL mappings, IL offset 0x{0:x}", ilOffset); - return null; // we don't have source information but we did return the IL information. + return null; // we don't have source information but we did return the IL information. } } } @@ -287,7 +288,7 @@ public SourceLocation SourceLocationForRva(uint rva, out string ilAssemblyName, return null; } - // If we reach here we are in the non-NGEN case, we are not mapping to IL information and + // If we reach here we are in the non-NGEN case, we are not mapping to IL information and IDiaSourceFile diaSrcFile = sourceLoc.sourceFile; var lineNum = (int)sourceLoc.lineNumber; @@ -325,7 +326,7 @@ public override SourceLocation SourceLocationForManagedCode(uint methodMetadataT IDiaEnumLineNumbers sourceLocs; IDiaLineNumber sourceLoc; - // TODO FIX NOW, this code here is for debugging only turn if off when we are happy. + // TODO FIX NOW, this code here is for debugging only turn if off when we are happy. //m_session.findLinesByRVA(methodSym.relativeVirtualAddress, (uint)(ilOffset + 256), out sourceLocs); //for (int i = 0; ; i++) //{ @@ -340,7 +341,7 @@ public override SourceLocation SourceLocationForManagedCode(uint methodMetadataT // For managed code, the 'RVA' is a 'cumulative IL offset' (amount of IL bytes before this in the module) // Thus you find the line number of a particular IL offset by adding the offset within the method to - // the cumulative IL offset of the start of the method. + // the cumulative IL offset of the start of the method. m_session.findLinesByRVA(methodSym.relativeVirtualAddress + (uint)ilOffset, 256, out sourceLocs); sourceLocs.Next(1, out sourceLoc, out fetchCount); if (fetchCount == 0) @@ -352,7 +353,7 @@ public override SourceLocation SourceLocationForManagedCode(uint methodMetadataT var sourceFile = new MicrosoftPdbSourceFile(this, sourceLoc.sourceFile); IDiaLineNumber lineNum = null; - // FEEFEE is some sort of illegal line number that is returned some time, It is better to ignore it. + // FEEFEE is some sort of illegal line number that is returned some time, It is better to ignore it. // and take the next valid line for (; ; ) { @@ -381,15 +382,15 @@ public override SourceLocation SourceLocationForManagedCode(uint methodMetadataT } /// - /// The symbol representing the module as a whole. All global symbols are children of this symbol + /// The symbol representing the module as a whole. All global symbols are children of this symbol /// - public Symbol GlobalSymbol - { - get + public Symbol GlobalSymbol + { + get { ThrowIfDisposed(); - return new Symbol(this, m_session.globalScope); - } + return new Symbol(this, m_session.globalScope); + } } #if TEST_FIRST @@ -430,41 +431,41 @@ public IEnumerable AllSourceFiles() #endif /// - /// The a unique identifier that is used to relate the DLL and its PDB. + /// The a unique identifier that is used to relate the DLL and its PDB. /// - public override Guid PdbGuid - { - get + public override Guid PdbGuid + { + get { ThrowIfDisposed(); - return m_session.globalScope.guid; - } + return m_session.globalScope.guid; + } } /// - /// Along with the PdbGuid, there is a small integer - /// call the age is also used to find the PDB (it represents the different - /// post link transformations the DLL has undergone). + /// Along with the PdbGuid, there is a small integer + /// call the age is also used to find the PDB (it represents the different + /// post link transformations the DLL has undergone). /// - public override int PdbAge - { - get + public override int PdbAge + { + get { ThrowIfDisposed(); - return (int)m_session.globalScope.age; - } + return (int)m_session.globalScope.age; + } } #region private /// /// A source file represents a source file from a PDB. This is not just a string /// because the file has a build time path, a checksum, and it needs to be 'smart' - /// to copy down the file if requested. - /// + /// to copy down the file if requested. + /// /// TODO We don't need this subclass. We can have SourceFile simply a container /// that holds the BuildTimePath, hashType and hashValue. The lookup of the - /// source can then be put on NativeSymbolModule and called from SourceFile generically. - /// This makes the different symbol files more similar and is a nice simplification. + /// source can then be put on NativeSymbolModule and called from SourceFile generically. + /// This makes the different symbol files more similar and is a nice simplification. /// public class MicrosoftPdbSourceFile : SourceFile { @@ -481,7 +482,7 @@ public override bool GetSourceLinkInfo(out string url, out string relativePath) } else { - // Try to convert srcsrv information + // Try to convert srcsrv information GetSourceServerTargetAndCommand(out string target, out _); if (!string.IsNullOrEmpty(target) && Uri.IsWellFormedUriString(target, UriKind.Absolute)) @@ -496,29 +497,29 @@ public override bool GetSourceLinkInfo(out string url, out string relativePath) } /// - /// Try to fetch the source file associated with 'buildTimeFilePath' from the symbol server - /// information from the PDB from 'pdbPath'. Will return a path to the returned file (uses - /// SourceCacheDirectory associated symbol reader for context where to put the file), - /// or null if unsuccessful. - /// - /// There is a tool called pdbstr associated with srcsrv that basically does this. + /// Try to fetch the source file associated with 'buildTimeFilePath' from the symbol server + /// information from the PDB from 'pdbPath'. Will return a path to the returned file (uses + /// SourceCacheDirectory associated symbol reader for context where to put the file), + /// or null if unsuccessful. + /// + /// There is a tool called pdbstr associated with srcsrv that basically does this. /// pdbstr -r -s:srcsrv -p:PDBPATH - /// will dump it. + /// will dump it. + /// + /// The basic flow is /// - /// The basic flow is - /// /// There is a variables section and a files section - /// + /// /// The file section is a list of items separated by *. The first is the path, the rest are up to you - /// + /// /// You form a command by using the SRCSRVTRG variable and substituting variables %var1 where var1 is the first item in the * separated list /// There are special operators %fnfile%(XXX), etc that manipulate the string XXX (get file name, translate \ to / ... - /// - /// If what is at the end is a valid URL it is looked up. + /// + /// If what is at the end is a valid URL it is looked up. /// protected override string GetSourceFromSrcServer() { - // Try getting the source from the source server using SourceLink information. + // Try getting the source from the source server using SourceLink information. var ret = base.GetSourceFromSrcServer(); if (ret != null) { @@ -561,10 +562,8 @@ protected override string GetSourceFromSrcServer() if (!File.Exists(target) && fetchCmdStr != null) { _log.WriteLine("Trying to generate the file {0}.", target); - var toolsDir = Path.GetDirectoryName(typeof(SourceFile).GetTypeInfo().Assembly.ManifestModule.FullyQualifiedName); - var archToolsDir = Path.Combine(toolsDir, NativeDlls.ProcessArchitectureDirectory); - // Find the EXE to do the source server fetch. We only support TF.exe. + // Find the EXE to do the source server fetch. We only support TF.exe. string addToPath = null; if (fetchCmdStr.StartsWith("tf.exe ", StringComparison.OrdinalIgnoreCase)) { @@ -606,7 +605,7 @@ protected override string GetSourceFromSrcServer() if (File.Exists(target)) { - // If TF.exe command files it might still create an empty output file. Fix that + // If TF.exe command files it might still create an empty output file. Fix that if (new FileInfo(target).Length == 0) { File.Delete(target); @@ -682,7 +681,7 @@ private void TryInitializeCppChecksum(IDiaSourceFile sourceFile) sourceFile.get_checksum(0, out hashSizeInBytes, out *dummy); // MD5 is 16 bytes - // SHA1 is 20 bytes + // SHA1 is 20 bytes // SHA-256 is 32 bytes _hash = new byte[hashSizeInBytes]; @@ -713,8 +712,8 @@ private void TryInitializeManagedChecksum(NativeSymbolModule module) } SrcFormat srcFormat = new SrcFormat(); - int srcFormatSize = Marshal.SizeOf(typeof(SrcFormat)); - int srcFormatHeaderSize = Marshal.SizeOf(typeof(SrcFormatHeader)); + int srcFormatSize = Marshal.SizeOf(); + int srcFormatHeaderSize = Marshal.SizeOf(); byte* pSrcFormat = (byte*)&srcFormat; injectedSource.get_source((uint)srcFormatSize, out uint sizeAvailable, out *pSrcFormat); @@ -751,19 +750,19 @@ private void TryInitializeManagedChecksum(NativeSymbolModule module) /// /// Parse the 'srcsrv' stream in a PDB file and return the target for SourceFile /// represented by the 'this' pointer. This target is either a ULR or a local file - /// path. - /// - /// You can dump the srcsrv stream using a tool called pdbstr + /// path. + /// + /// You can dump the srcsrv stream using a tool called pdbstr /// pdbstr -r -s:srcsrv -p:PDBPATH - /// + /// /// The target in this stream is called SRCSRVTRG and there is another variable SRCSRVCMD /// which represents the command to run to fetch the source into SRCSRVTRG - /// + /// /// To form the target, the stream expect you to private a %targ% variable which is a directory /// prefix to tell where to put the source file being fetched. If the source file is - /// available via a URL this variable is not needed. - /// - /// ********* This is a typical example of what is in a PDB with source server information. + /// available via a URL this variable is not needed. + /// + /// ********* This is a typical example of what is in a PDB with source server information. /// SRCSRV: ini ------------------------------------------------ /// VERSION=3 /// INDEXVERSION=2 @@ -783,8 +782,8 @@ private void TryInitializeManagedChecksum(NativeSymbolModule module) /// f:\dd\externalapis\legacy\vctools\vc12\inc\cvinfo.h*VSTFDEVDIV_DEVDIV2*/DevDiv/Fx/Rel/NetFxRel3Stage/externalapis/legacy/vctools/vc12/inc/cvinfo.h*1363200 /// f:\dd\externalapis\legacy\vctools\vc12\inc\vc\ammintrin.h*VSTFDEVDIV_DEVDIV2*/DevDiv/Fx/Rel/NetFxRel3Stage/externalapis/legacy/vctools/vc12/inc/vc/ammintrin.h*1363200 /// SRCSRV: end ------------------------------------------------ - /// - /// ********* And here is a more modern one where the source code is available via a URL. + /// + /// ********* And here is a more modern one where the source code is available via a URL. /// SRCSRV: ini ------------------------------------------------ /// VERSION=2 /// INDEXVERSION=2 @@ -796,7 +795,7 @@ private void TryInitializeManagedChecksum(NativeSymbolModule module) /// SRCSRV: source files --------------------------------------- /// c:\Users\dev\Documents\Visual Studio 2012\Projects\DavidSymbolSourceTest\DavidSymbolSourceTest\Demo.cs*SQPvxWBMtvANyCp8Pd3OjoZEUgpKvjDVIY1WbaiFPMw= /// SRCSRV: end ------------------------------------------------ - /// + /// /// /// returns the target source file path /// returns the command to fetch the target source file @@ -861,7 +860,7 @@ private void GetSourceServerTargetAndCommand(out string target, out string comma // log.WriteLine("Found source {0} in the PDB", buildTimePath); if (string.Compare(BuildTimeFilePath, buildTimePath, StringComparison.OrdinalIgnoreCase) == 0) { - // Create variables for each of the pieces. + // Create variables for each of the pieces. for (int i = 0; i < pieces.Length; i++) { vars.Add("var" + (i + 1).ToString(), pieces[i]); @@ -876,7 +875,7 @@ private void GetSourceServerTargetAndCommand(out string target, out string comma } else if (inVars) { - // Gather up the KEY=VALUE pairs into a dictionary. + // Gather up the KEY=VALUE pairs into a dictionary. var m = Regex.Match(line, @"^(\w+)=(.*?)\s*$"); if (m.Success) { @@ -887,12 +886,12 @@ private void GetSourceServerTargetAndCommand(out string target, out string comma } /// - /// Returns the location of the tf.exe executable or + /// Returns the location of the tf.exe executable or /// /// private static string FindTfExe() { - // If you have VS installed used that TF.exe associated with that. + // If you have VS installed used that TF.exe associated with that. var progFiles = Environment.GetEnvironmentVariable("ProgramFiles (x86)"); if (progFiles == null) { @@ -937,7 +936,7 @@ private string SourceServerEvaluate(string result, Dictionary va { if (0 <= result.IndexOf('%')) { - // see http://msdn.microsoft.com/en-us/library/windows/desktop/ms680641(v=vs.85).aspx for details on the %fn* variables + // see http://msdn.microsoft.com/en-us/library/windows/desktop/ms680641(v=vs.85).aspx for details on the %fn* variables result = Regex.Replace(result, @"%fnvar%\((.*?)\)", delegate (Match m) { return SourceServerFetchVar(SourceServerEvaluate(m.Groups[1].Value, vars), vars); @@ -959,7 +958,7 @@ private string SourceServerEvaluate(string result, Dictionary va return result; } - // Here is an example of the srcsrv stream. + // Here is an example of the srcsrv stream. #if false SRCSRV: ini ------------------------------------------------ VERSION=3 @@ -984,11 +983,11 @@ private string SourceServerEvaluate(string result, Dictionary va SRCSRV: end ------------------------------------------------ #endif #if false - // Here is ana example of the stream in use for the jithlp.asm file. + // Here is ana example of the stream in use for the jithlp.asm file. f:\dd\ndp\clr\src\vm\i386\jithelp.asm*DEVDIV_TFS2*/DevDiv/D11RelS/FX45RTMGDR/ndp/clr/src/VM/i386/jithelp.asm*592925 - // Here is the command that it issues. + // Here is the command that it issues. tf.exe view /version:592925 /noprompt "$/DevDiv/D11RelS/FX45RTMGDR/ndp/clr/src/VM/i386/jithelp.asm" /server:http://vstfdevdiv.redmond.corp.microsoft.com:8080/devdiv2 /console >"C:\Users\vancem\AppData\Local\Temp\PerfView\src\DEVDIV_TFS2\DevDiv\D11RelS\FX45RTMGDR\ndp\clr\src\VM\i386\jithelp.asm\592925\jithelp.asm" #endif @@ -1052,18 +1051,18 @@ internal void LogManagedInfo(string pdbName, Guid pdbGuid, int pdbAge) } /// - /// Gets the 'srcsvc' data stream from the PDB and return it in as a string. Returns null if it is not present. - /// - /// There is a tool called pdbstr associated with srcsrv that basically does this. + /// Gets the 'srcsvc' data stream from the PDB and return it in as a string. Returns null if it is not present. + /// + /// There is a tool called pdbstr associated with srcsrv that basically does this. /// pdbstr -r -s:srcsrv -p:PDBPATH - /// will dump it. + /// will dump it. /// internal string GetSrcSrvStream() { ThrowIfDisposed(); - // In order to get the IDiaDataSource3 which includes 'getStreamSize' API, you need to use the - // dia2_internal.idl file from devdiv to produce the Interop.Dia2Lib.dll + // In order to get the IDiaDataSource3 which includes 'getStreamSize' API, you need to use the + // dia2_internal.idl file from devdiv to produce the Interop.Dia2Lib.dll // see class DiaLoader for more var log = m_reader.m_log; log.WriteLine("Getting source server stream for PDB {0}", SymbolFilePath); @@ -1320,7 +1319,7 @@ protected virtual void Dispose(bool disposing) /// /// This function checks if the SymbolModule is disposed before proceeding with the call. - /// This is important because DIA doesn't provide any guarantees as to what will happen if + /// This is important because DIA doesn't provide any guarantees as to what will happen if /// one attempts to call after the session is disposed, so this at least ensure that we /// fail cleanly in non-concurrent cases. /// @@ -1333,9 +1332,9 @@ private void ThrowIfDisposed() } /// - /// This static class contains the GetTypeName method for retrieving the type name of - /// a heap allocation site. - /// + /// This static class contains the GetTypeName method for retrieving the type name of + /// a heap allocation site. + /// /// See https://github.com/KirillOsenkov/Dia2Dump/blob/master/PrintSymbol.cpp for more details /// private static class HeapAllocationTypeInfo @@ -1539,12 +1538,12 @@ struct SrcFormat } /// - /// Represents a single symbol in a PDB file. + /// Represents a single symbol in a PDB file. /// public class Symbol : IComparable { /// - /// The name for the symbol + /// The name for the symbol /// public string Name { get { return m_name; } } /// @@ -1552,18 +1551,18 @@ public class Symbol : IComparable /// public uint RVA { get { return m_diaSymbol.relativeVirtualAddress; } } /// - /// The length of the memory that the symbol represents. + /// The length of the memory that the symbol represents. /// public ulong Length { get { return m_diaSymbol.length; } } /// - /// A small integer identifier tat is unique for that symbol in the DLL. + /// A small integer identifier tat is unique for that symbol in the DLL. /// public uint Id { get { return m_diaSymbol.symIndexId; } } /// - /// Decorated names are names that most closely resemble the source code (have overloading). + /// Decorated names are names that most closely resemble the source code (have overloading). /// However when the linker does not directly support all the expressiveness of the - /// source language names are encoded to represent this. This return this encoded name. + /// source language names are encoded to represent this. This return this encoded name. /// public string UndecoratedName { @@ -1586,7 +1585,7 @@ public static bool InSameSection(Symbol a, Symbol b) } /// - /// Returns the children of the symbol. Will return null if there are no children. + /// Returns the children of the symbol. Will return null if there are no children. /// public IEnumerable GetChildren() { @@ -1594,7 +1593,7 @@ public IEnumerable GetChildren() } /// - /// Returns the children of the symbol, with the given tag. Will return null if there are no children. + /// Returns the children of the symbol, with the given tag. Will return null if there are no children. /// public IEnumerable GetChildren(SymTagEnum tag) { @@ -1741,21 +1740,21 @@ namespace Dia2Lib /// The DiaLoader class knows how to load the msdia140.dll (the Debug Access Interface) (see docs at /// http://msdn.microsoft.com/en-us/library/x93ctkx8.aspx), without it being registered as a COM object. /// Basically it just called the DllGetClassObject interface directly. - /// - /// It has one public method 'GetDiaSourceObject' which knows how to create a IDiaDataSource object. - /// From there you can do anything you need. - /// - /// In order to get IDiaDataSource3 which includes 'getStreamSize' API, you need to use the + /// + /// It has one public method 'GetDiaSourceObject' which knows how to create a IDiaDataSource object. + /// From there you can do anything you need. + /// + /// In order to get IDiaDataSource3 which includes 'getStreamSize' API, you need to use the /// vctools\langapi\idl\dia2_internal.idl file from devdiv to produce Dia2Lib.dll - /// - /// roughly what you need to do is + /// + /// roughly what you need to do is /// copy vctools\langapi\idl\dia2_internal.idl . /// copy vctools\langapi\idl\dia2.idl . /// copy vctools\langapi\include\cvconst.h . /// Change dia2.idl to include interface IDiaDataSource3 inside library Dia2Lib->importlib->coclass DiaSource /// midl dia2_internal.idl /D CC_DP_CXX /// tlbimp dia2_internal.tlb - /// REM result is Dia2Lib.dll + /// REM result is Dia2Lib.dll /// internal static class DiaLoader { @@ -1774,10 +1773,10 @@ private static IClassFactory CreateDiaClassFactory() // Ensure that the native DLL we need exists. NativeDlls.LoadNative("msdia140.dll"); - // This is the value it was for msdia120 and before + // This is the value it was for msdia120 and before // var diaSourceClassGuid = new Guid("{3BFCEA48-620F-4B6B-81F7-B9AF75454C7D}"); - // This is the value for msdia140. + // This is the value for msdia140. var diaSourceClassGuid = new Guid("{e6756135-1e65-4d17-8576-610761398c3c}"); return (IClassFactory)DllGetClassObject(diaSourceClassGuid, typeof(IClassFactory).GetTypeInfo().GUID); } @@ -1888,7 +1887,7 @@ interface IDiaSession3 void Reserved70(); // prepareEnCRebuild #endregion - [PreserveSig] + [PreserveSig] int dispose(); }; } diff --git a/src/TraceEvent/TraceEvent.cs b/src/TraceEvent/TraceEvent.cs index e6045231b..09d83feaa 100644 --- a/src/TraceEvent/TraceEvent.cs +++ b/src/TraceEvent/TraceEvent.cs @@ -2,8 +2,8 @@ // This file is best viewed using outline mode (Ctrl-M Ctrl-O) // // This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in. -// It is available from http://www.codeplex.com/hyperAddin -// +// It is available from http://www.codeplex.com/hyperAddin +// using Microsoft.Diagnostics.Tracing.Parsers; using Microsoft.Diagnostics.Tracing.Parsers.GCDynamic; using Microsoft.Diagnostics.Tracing.Parsers.Kernel; @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.IO; using System.Reflection; @@ -19,50 +20,50 @@ using System.Text; using Address = System.UInt64; -// #Introduction -// -// Note that TraceEvent lives in a Nuget package. See +// #Introduction +// +// Note that TraceEvent lives in a Nuget package. See // http://blogs.msdn.com/b/vancem/archive/2014/03/15/walk-through-getting-started-with-etw-traceevent-nuget-samples-package.aspx -// and +// and // http://blogs.msdn.com/b/vancem/archive/2013/08/15/traceevent-etw-library-published-as-a-nuget-package.aspx -// +// // For more details. In particular the second blog post will contain the TraceEventProgrammersGuide.docx, which has // more background. // // Finally if you are interested in creating your own TraceEventParsers for your ETW provider, inside Microsoft you can access -// the TraceParserGen tool at http://toolbox/TraceParserGen. There is also a copy available externally at http://1drv.ms/1Rxk2iD -// in the TraceParserGen.zip file and the TraceParserGen.src.zip file. +// the TraceParserGen tool at http://toolbox/TraceParserGen. There is also a copy available externally at http://1drv.ms/1Rxk2iD +// in the TraceParserGen.zip file and the TraceParserGen.src.zip file. // // The the heart of the ETW reader are two important classes. -// +// // * TraceEventSource which is an abstract represents the stream of events as a whole. Thus it // has holds things like session start and stop times, number of lost events etc. -// +// // * TraceEvent is a base class that represents an individual event payload. Because different // events have different fields, this is actually the base of a class hierarchy. TraceEvent // itself holds all properties that are common to all events (like TimeDateStamp, ProcessID, // ThreadID, etc). Subclasses then add properties that know how to parse that specific event // type. -// +// // However these two classes are not enough. ETW has a model where there can be many independent // providers each of which contributed to the event stream. Since the number of providers is unknown, // TraceEventSource can not know the details of decoding all possible events. Instead we introduce // a class -// +// // TraceEventParser -// +// // one for each provider. This class knows the details of taking a binary blob representing a event and // turning it into a TraceEvent. Since each provider has different details of how to do this, // TraceEventParser is actually just a base class and specific subclasses of TraceEventParser // like KernelTraceEventParser or ClrTraceEventParser do the real work. -// +// // TraceEventParsers have a very ridged layout that closely parallels the data in the providers's ETW // manifest (in fact there is a tool for creating TraceEventParser's from a ETW manifest). A // TraceEventParser has a C# event (callback) for each different event the provider can generate. These // events callbacks have an argument that is the specific subclass of TraceEvent that represents // the payload for that event. This allows client code to 'subscribe' to events in a strongly typed way. // For example: -// +// // * ETWTraceEventSource source = new ETWTraceEventSource("output.etl"); // open an ETL file // * KernelTraceEventParser kernelEvents = new KernelTraceEventParser(source); // Attach Kernel Parser. // * @@ -73,7 +74,7 @@ // * // * // Attach more parsers, and subscribe to more events. // * source.Process(); // Read through the stream, calling all the callbacks in one pass. -// +// // In the example above, ETWTraceEventSource (a specific subclass of TraceEventSource that understand // ETL files) is created by opening the 'output.etl' file. Then the KernelTraceEventParser is 'attached' // to the source so that kernel events can be decoded. Finally a callback is registered with the @@ -82,26 +83,26 @@ // like 'BaseAddress' and 'FileName' which are specific to that particular event. The user can subscribe // to many such events (each having different event-specific data), and then finally call Process() which // causes the source to enumerate the event stream, calling the appropriate callbacks. -// +// // This model has the important attribute that new TraceEventParsers (ETW providers), can be crated and // used by user code WITHOUT changing the code associated with TraceEventSource. Unfortunately, it // has a discoverability drawback. Given a TraceEventSource (like ETWTraceEventSource), it is difficult // discover that you need classes like KernelTraceEventParser to do anything useful with the // source. As a concession to discoverability, TraceEventSource provides properties ('Kernel' and CLR) // for two 'well known' parsers. Thus the above example can be written -// +// // * ETWTraceEventSource source = new ETWTraceEventSource("output.etl"); // open an ETL file // * source.Kernel.ImageLoad += delegate(ImageLoadTraceData data) { // * Console.WriteLine("Got Image Base {0} ModuleFile {1} ", data.BaseAddress, data.FileName); // * }; // * source.Process(); // Read through the stream, calling all the callbacks in one pass. -// +// // To keep efficiently high, this basic decode in Process() does NOT allocate new event every time a // callback is made. Instead TraceEvent passed to the callback is reused on later events, so // clients must copy the data out if they need to persist it past the time the callback returns. The // TraceEvent.Clone method can be used to form a copy of a TraceEvent that will not be reused // by the TraceEventSource. -// +// // Another important attribute of the system is that decoding of the fields of TraceEvent is done // lazily. For example ImageLoadTraceData does not actually contain fields for things like // 'BaseAddress' or 'FileName', but simply a pointer to the raw bits from the file. It is only when a @@ -110,9 +111,9 @@ // event's payload may be ignored by any particular client. A consequence of this approach is that for // properties that do non-trivial work (like create a string from the raw data) it is better not to call // the property multiple times (instead cache it locally in a local variable). -// +// // Supporting Sources that don't implement a callback model -// +// // In the previous example ETWTraceEventSource supported the subscription model where the client // registers a set of callbacks and then calls Process() to cause the callbacks to happen. This model // is very efficient and allows a lot of logically distinct processing to be done in 'one pass'. However @@ -121,9 +122,9 @@ // as the Process()) method), is actually put in a subclass of TraceEventSource called // TraceEventDispatcher. Those sources that support the subscription model inherit from // TraceEventSource, and those that do not inherit directly from TraceEventSource. -// +// // The Protocol between TraceEventParser and TraceEventSource -// +// // What is common among all TraceEventSources (even if they do not support callbacks), is that parsers // need to be registered with the source so that the source can decode the events. This is the purpose // of the TraceEventSource.RegisterParser and TraceEventSource.RegisterEventTemplate methods. @@ -132,7 +133,7 @@ // about this new parser. Also any time a user subscribes to a particular event in the parser, the // source needs to know about so that its (shared) event dispatch table can be updated this is what // RegisterEventTemplate is for. -// +// // * See also // * code:ETWTraceEventSource a trace event source for a .ETL file or a 'real time' ETW stream. // * code:ETLXTraceEventSource a trace event source for a ETLX file (post-processes ETL file). @@ -142,12 +143,12 @@ namespace Microsoft.Diagnostics.Tracing { /// - /// TraceEventSource is an abstract base class that represents the output of a ETW session (e.g. a ETL file + /// TraceEventSource is an abstract base class that represents the output of a ETW session (e.g. a ETL file /// or ETLX file or a real time stream). This base class is NOT responsible for actually processing /// the events, but contains methods for properties associated with the session /// like its start and end time, filename, and characteristics of the machine it was collected on. /// This class has two main subclasses: - /// * which implements a 'push' (callback) model and is the only mode for ETL files. + /// * which implements a 'push' (callback) model and is the only mode for ETL files. /// ETWTraceEventSource is the most interesting subclass of TraceEventDispatcher. /// * see TraceLog which implements both a 'push' (callback) as well as pull (foreach) model but only works on ETLX files. /// This is the end. @@ -156,10 +157,10 @@ namespace Microsoft.Diagnostics.Tracing /// public abstract unsafe class TraceEventSource : ITraceParserServices, IDisposable { - // Properties to subscribe to find important parsers (these are convenience routines). + // Properties to subscribe to find important parsers (these are convenience routines). /// - /// For convenience, we provide a property returns a ClrTraceEventParser that knows + /// For convenience, we provide a property returns a ClrTraceEventParser that knows /// how to parse all the Common Language Runtime (CLR .NET) events into callbacks. /// public ClrTraceEventParser Clr @@ -176,7 +177,7 @@ public ClrTraceEventParser Clr } /// - /// For convenience, we provide a property returns a KernelTraceEventParser that knows + /// For convenience, we provide a property returns a KernelTraceEventParser that knows /// how to parse all the Kernel events into callbacks. /// public KernelTraceEventParser Kernel @@ -195,12 +196,12 @@ public KernelTraceEventParser Kernel #if !NOT_WINDOWS && !NO_DYNAMIC_TRACEEVENTPARSER /// - /// For convenience, we provide a property returns a DynamicTraceEventParser that knows + /// For convenience, we provide a property returns a DynamicTraceEventParser that knows /// how to parse all event providers that dynamically log their schemas into the event streams. - /// In particular, it knows how to parse any events from a System.Diagnostics.Tracing.EventSources. - /// + /// In particular, it knows how to parse any events from a System.Diagnostics.Tracing.EventSources. + /// /// Note that the DynamicTraceEventParser has subsumed the functionality of RegisteredTraceEventParser - /// so any registered providers are also looked up here. + /// so any registered providers are also looked up here. /// public DynamicTraceEventParser Dynamic { @@ -215,11 +216,11 @@ public DynamicTraceEventParser Dynamic } } /// - /// For convenience, we provide a property returns a RegisteredTraceEventParser that knows + /// For convenience, we provide a property returns a RegisteredTraceEventParser that knows /// how to parse all providers that are registered with the operating system. - /// + /// /// Because the DynamicTraceEventParser has will parse all providers that that RegisteredTraceEventParser - /// will parse, this function is obsolete, you should use Dynamic instead. + /// will parse, this function is obsolete, you should use Dynamic instead. /// [Obsolete("Use Dynamic instead. DynamicTraceEventParser decodes everything that RegisteredTraceEventParser can.")] public RegisteredTraceEventParser Registered @@ -237,7 +238,7 @@ public RegisteredTraceEventParser Registered #endif // !NOT_WINDOWS /// - /// The time when session started logging. + /// The time when session started logging. /// public DateTime SessionStartTime { @@ -268,7 +269,7 @@ public double SessionEndTimeRelativeMSec get { Debug.Assert(sessionStartTimeQPC <= sessionEndTimeQPC); - Debug.Assert((sessionEndTimeQPC - sessionStartTimeQPC) < _QPCFreq * 3600 * 24 * 10); // less than 10 days. + Debug.Assert((sessionEndTimeQPC - sessionStartTimeQPC) < _QPCFreq * 3600 * 24 * 10); // less than 10 days. var ret = QPCTimeToRelMSec(sessionEndTimeQPC); return ret; } @@ -279,7 +280,7 @@ public double SessionEndTimeRelativeMSec public TimeSpan SessionDuration { get { return SessionEndTime - SessionStartTime; } } /// - /// The size of the trace, if it is known. Will return 0 if it is not known. + /// The size of the trace, if it is known. Will return 0 if it is not known. /// public virtual long Size { get { return 0; } } /// @@ -291,11 +292,11 @@ public double SessionEndTimeRelativeMSec /// public abstract int EventsLost { get; } /// - /// The number of processors on the machine doing the logging. + /// The number of processors on the machine doing the logging. /// public int NumberOfProcessors { get { return numberOfProcessors; } } /// - /// Cpu speed of the machine doing the logging. + /// Cpu speed of the machine doing the logging. /// public int CpuSpeedMHz { get { return cpuSpeedMHz; } } /// @@ -303,12 +304,12 @@ public double SessionEndTimeRelativeMSec /// public Version OSVersion { get { return osVersion; } } /// - /// Returns true if this is a real time session. + /// Returns true if this is a real time session. /// public bool IsRealTime { get; protected set; } /// - /// Time based threshold for how long data should be retained + /// Time based threshold for how long data should be retained /// by accumulates that are processing this TraceEventSource. /// A value of 0, the default, indicates an infinite accumulation. /// @@ -339,10 +340,10 @@ public void Dispose() } /// - /// TraceEventSource supports attaching arbitrary user data to the source. This property returns a key-value bag of these attached values. + /// TraceEventSource supports attaching arbitrary user data to the source. This property returns a key-value bag of these attached values. /// /// One convention that has been established is that TraceEventParsers that need additional state to parse their events should - /// store them in UserData under the key 'parsers\(ParserName)' + /// store them in UserData under the key 'parsers\(ParserName)' /// /// public IDictionary UserData { get { return userData; } } @@ -352,7 +353,7 @@ public void Dispose() internal /*protected*/ TraceEventSource() { userData = new Dictionary(); - _QPCFreq = 1; // Anything non-zero so we don't get divide by zero failures in degenerate cases. + _QPCFreq = 1; // Anything non-zero so we don't get divide by zero failures in degenerate cases. } // [SecuritySafeCritical] @@ -382,7 +383,7 @@ void ITraceParserServices.RegisterEventTemplate(TraceEvent template) Debug.Assert(template.eventRecord == null); Debug.Assert(template.next == null); - template.next = null; // Should be a no-op but I want to be extra sure. + template.next = null; // Should be a no-op but I want to be extra sure. RegisterEventTemplateImpl(template); } @@ -437,7 +438,7 @@ internal void CopyFrom(TraceEventSource source) // Used to convert from Query Performance Counter (QPC) units to DateTime. internal /*protected*/ long _QPCFreq; internal /*protected*/ long _syncTimeQPC; // An instant in time measured in QPC units (of _QPCFreq) - internal /*protected*/ DateTime _syncTimeUTC; // The same instant as a DateTime. This is the only fundamental DateTime in the object. + internal /*protected*/ DateTime _syncTimeUTC; // The same instant as a DateTime. This is the only fundamental DateTime in the object. internal /*protected*/ long sessionStartTimeQPC; internal /*protected*/ long sessionEndTimeQPC; @@ -451,17 +452,17 @@ internal void CopyFrom(TraceEventSource source) #endregion #region private /// - /// This is the high frequency tick clock on the processor (what QueryPerformanceCounter uses). - /// You should not need + /// This is the high frequency tick clock on the processor (what QueryPerformanceCounter uses). + /// You should not need /// internal long QPCFreq { get { return _QPCFreq; } } /// - /// Converts the Query Performance Counter (QPC) ticks to a number of milliseconds from the start of the trace. + /// Converts the Query Performance Counter (QPC) ticks to a number of milliseconds from the start of the trace. /// internal double QPCTimeToRelMSec(long QPCTime) { - // Ensure that we have a certain amount of sanity (events don't occur before sessionStartTime). + // Ensure that we have a certain amount of sanity (events don't occur before sessionStartTime). if (QPCTime < sessionStartTimeQPC) { QPCTime = sessionStartTimeQPC; @@ -470,19 +471,19 @@ internal double QPCTimeToRelMSec(long QPCTime) // We used to have a sanity check to ensure that the time was always inside sessionEndTimeQPC // ETLX files enforce this, but sometimes ETWTraceEventParser (ETL) traces have bad session times. // After some thought, the best answer seems to be not to try to enforce this consistantancy. - // (it will be true for ETLX but maybe not for ETWTraceEventParser scenarios). + // (it will be true for ETLX but maybe not for ETWTraceEventParser scenarios). Debug.Assert(sessionStartTimeQPC != 0 && _syncTimeQPC != 0 && _syncTimeUTC.Ticks != 0 && _QPCFreq != 0); - // TODO this does not work for very long traces. + // TODO this does not work for very long traces. long diff = (QPCTime - sessionStartTimeQPC); // For real time providers, the session start time is the time when the TraceEventSource was turned on // but the session was turned on before that and events might have been buffered, which means you can - // have negative numbers. + // have negative numbers. return diff * 1000.0 / QPCFreq; } /// - /// Converts a Relative MSec time to the Query Performance Counter (QPC) ticks + /// Converts a Relative MSec time to the Query Performance Counter (QPC) ticks /// internal long RelativeMSecToQPC(double relativeMSec) { @@ -491,34 +492,34 @@ internal long RelativeMSecToQPC(double relativeMSec) } /// - /// Converts a DateTime to the Query Performance Counter (QPC) ticks + /// Converts a DateTime to the Query Performance Counter (QPC) ticks /// internal long UTCDateTimeToQPC(DateTime time) { Debug.Assert(_QPCFreq != 0); long ret = (long)((time.Ticks - _syncTimeUTC.Ticks) / 10000000.0 * _QPCFreq) + _syncTimeQPC; - // The sessionEndTimeQPC == 0 effectively means 'called during trace startup' and we use it here to disable the - // assert. During that time we get a wrong QPC we only use this to initialize sessionStartTimeQPC and - // sessionEndTimeQPC and we fix these up when we see the first event (see kernelParser.EventTraceHeader += handler). + // The sessionEndTimeQPC == 0 effectively means 'called during trace startup' and we use it here to disable the + // assert. During that time we get a wrong QPC we only use this to initialize sessionStartTimeQPC and + // sessionEndTimeQPC and we fix these up when we see the first event (see kernelParser.EventTraceHeader += handler). Debug.Assert(sessionEndTimeQPC == 0 || (QPCTimeToDateTimeUTC(ret) - time).TotalMilliseconds < 1); return ret; } /// - /// Converts the Query Performance Counter (QPC) ticks to a DateTime + /// Converts the Query Performance Counter (QPC) ticks to a DateTime /// internal DateTime QPCTimeToDateTimeUTC(long QPCTime) { - if (QPCTime == long.MaxValue) // We treat maxvalue as a special case. + if (QPCTime == long.MaxValue) // We treat maxvalue as a special case. { return DateTime.MaxValue; } - // We expect all the time variables used to compute this to be set. + // We expect all the time variables used to compute this to be set. Debug.Assert(_syncTimeQPC != 0 && _syncTimeUTC.Ticks != 0 && _QPCFreq != 0); long inTicks = (long)((QPCTime - _syncTimeQPC) * 10000000.0 / _QPCFreq) + _syncTimeUTC.Ticks; - // Avoid illegal DateTime values. + // Avoid illegal DateTime values. if (inTicks < 0 || DateTime.MaxValue.Ticks < inTicks) { inTicks = DateTime.MaxValue.Ticks; @@ -535,8 +536,8 @@ internal virtual string ProcessName(int processID, long timeQPC) /// /// Some events (like HardFault) do not have a thread ID or a process ID, but they MIGHT have a Stack - /// If they do try to get the ThreadID for the event from that. Return -1 if not successful. - /// This is intended to be overridden by the TraceLog class that has this additional information. + /// If they do try to get the ThreadID for the event from that. Return -1 if not successful. + /// This is intended to be overridden by the TraceLog class that has this additional information. /// internal virtual int LastChanceGetThreadID(TraceEvent data) { return -1; } internal virtual int LastChanceGetProcessID(TraceEvent data) { return -1; } @@ -545,7 +546,7 @@ internal virtual unsafe Guid GetRelatedActivityID(TraceEventNativeMethods.EVENT_ if (eventRecord->ExtendedDataCount != 0) { var extendedData = eventRecord->ExtendedData; - Debug.Assert((ulong)extendedData > 0x10000); // Make sure this looks like a pointer. + Debug.Assert((ulong)extendedData > 0x10000); // Make sure this looks like a pointer. for (int i = 0; i < eventRecord->ExtendedDataCount; i++) { if (extendedData[i].ExtType == TraceEventNativeMethods.EVENT_HEADER_EXT_TYPE_RELATED_ACTIVITYID) @@ -583,16 +584,16 @@ internal virtual unsafe string GetContainerID(TraceEventNativeMethods.EVENT_RECO } /// - /// TraceEvent an abstract class represents the data from one event in the stream of events in a TraceEventSource. + /// TraceEvent an abstract class represents the data from one event in the stream of events in a TraceEventSource. /// The TraceEvent class has all the properties of an event that are common to all ETW events, including TimeStamp /// ProviderGuid, ProcessID etc. Subclasses of TraceEvent then extend this abstract class to include properties - /// specific to a particular payload. + /// specific to a particular payload. /// /// An important architectural point is that TraceEvent classes are aggressively reused by default. The TraceEvent that is /// passed to any TraceEventParser callback or in a foreach is ONLY valid for the duration for that callback (or one /// iteration of the foreach). If you need save a copy of the event data, you must call the Clone() method to make /// a copy. The IObservable interfaces (TraceEventParser.Observe* methods) however implicitly call Clone() so you - /// do not have to call Clone() when processing with IObservables (but these are slower). + /// do not have to call Clone() when processing with IObservables (but these are slower). /// /// public abstract unsafe class TraceEvent @@ -603,8 +604,8 @@ public abstract unsafe class TraceEvent : DynamicObject { /// - /// The GUID that uniquely identifies the Provider for this event. This can return Guid.Empty for classic (Pre-VISTA) ETW providers. - /// + /// The GUID that uniquely identifies the Provider for this event. This can return Guid.Empty for classic (Pre-VISTA) ETW providers. + /// public Guid ProviderGuid { get { return providerGuid; } } /// @@ -613,7 +614,7 @@ public abstract unsafe class TraceEvent public Guid TaskGuid { get { return taskGuid; } } /// - /// The name of the provider associated with the event. It may be of the form Provider(GUID) or UnknownProvider in some cases but is never null. + /// The name of the provider associated with the event. It may be of the form Provider(GUID) or UnknownProvider in some cases but is never null. /// public string ProviderName { @@ -649,8 +650,8 @@ public string ProviderName } } /// - /// A name for the event. This is simply the concatenation of the task and opcode names (separated by a /). If the - /// event has no opcode, then the event name is just the task name. + /// A name for the event. This is simply the concatenation of the task and opcode names (separated by a /). If the + /// event has no opcode, then the event name is just the task name. /// public string EventName { @@ -687,13 +688,13 @@ public TraceEventID ID /// /// Events for a given provider can be given a group identifier (integer) called a Task that indicates the /// broad area within the provider that the event pertains to (for example the Kernel provider has - /// Tasks for Process, Threads, etc). + /// Tasks for Process, Threads, etc). /// public TraceEventTask Task { get { return task; } } /// /// The human readable name for the event's task (group of related events) (eg. process, thread, /// image, GC, ...). May return a string Task(GUID) or Task(TASK_NUM) if no good symbolic name is - /// available. It never returns null. + /// available. It never returns null. /// public string TaskName { @@ -716,7 +717,7 @@ public string TaskName } else { - eventNameIsJustTaskName = true; // Don't suffix this with the opcode. + eventNameIsJustTaskName = true; // Don't suffix this with the opcode. if (eventID == 0) { taskName = "EventWriteString"; @@ -727,7 +728,7 @@ public string TaskName } } } - // Old EventSources did not have tasks for event names so we make an exception for these + // Old EventSources did not have tasks for event names so we make an exception for these #if !NOT_WINDOWS && !NO_DYNAMIC_TRACEEVENTPARSER Debug.Assert(!string.IsNullOrEmpty(taskName) || (this is DynamicTraceEventData && ProviderName == "TplEtwProvider")); #endif @@ -735,17 +736,17 @@ public string TaskName } } /// - /// An opcode is a numeric identifier (integer) that identifies the particular event within the group of events + /// An opcode is a numeric identifier (integer) that identifies the particular event within the group of events /// identified by the event's task. Often events have opcode 'Info' (0), which is the default. This value /// is interpreted as having no-opcode (the task is sufficient to identify the event). /// /// Generally the most useful opcodes are the Start and Stop opcodes which are used to indicate the beginning and the - /// end of a interval of time. Many tools will match up start and stop opcodes automatically and compute durations. + /// end of a interval of time. Many tools will match up start and stop opcodes automatically and compute durations. /// /// public TraceEventOpcode Opcode { get { return opcode; } } /// - /// Returns the human-readable string name for the Opcode property. + /// Returns the human-readable string name for the Opcode property. /// public string OpcodeName { @@ -773,7 +774,7 @@ public TraceEventLevel Level } /// /// The version number for this event. The only compatible change to an event is to add new properties at the end. - /// When this is done the version numbers is incremented. + /// When this is done the version numbers is incremented. /// public int Version { @@ -781,9 +782,9 @@ public int Version get { return eventRecord->EventHeader.Version; } } /// - /// ETW Event providers can specify a 64 bit bitfield called 'keywords' that define provider-specific groups of - /// events which can be enabled and disabled independently. - /// Each event is given a keywords mask that identifies which groups the event belongs to. This property returns this mask. + /// ETW Event providers can specify a 64 bit bitfield called 'keywords' that define provider-specific groups of + /// events which can be enabled and disabled independently. + /// Each event is given a keywords mask that identifies which groups the event belongs to. This property returns this mask. /// public TraceEventKeyword Keywords { @@ -791,8 +792,8 @@ public TraceEventKeyword Keywords get { return (TraceEventKeyword)eventRecord->EventHeader.Keyword; } } /// - /// A Channel is a identifier (integer) that defines an 'audience' for the event (admin, operational, ...). - /// Channels are only used for Windows Event Log integration. + /// A Channel is a identifier (integer) that defines an 'audience' for the event (admin, operational, ...). + /// Channels are only used for Windows Event Log integration. /// public TraceEventChannel Channel { @@ -801,14 +802,14 @@ public TraceEventChannel Channel } /// - /// The time of the event. You may find TimeStampRelativeMSec more convenient. + /// The time of the event. You may find TimeStampRelativeMSec more convenient. /// public DateTime TimeStamp { get { return traceEventSource.QPCTimeToDateTimeUTC(TimeStampQPC).ToLocalTime(); } } /// - /// Returns a double representing the number of milliseconds since the beginning of the session. + /// Returns a double representing the number of milliseconds since the beginning of the session. /// public double TimeStampRelativeMSec { @@ -836,7 +837,7 @@ public int ThreadID } } /// - /// The process ID of the process which logged the event. + /// The process ID of the process which logged the event. /// This field may return -1 for some events when the process ID is not known. /// public virtual int ProcessID @@ -855,7 +856,7 @@ public virtual int ProcessID } /// /// Returns a short name for the process. This the image file name (without the path or extension), - /// or if that is not present, then the string 'Process(XXXX)' + /// or if that is not present, then the string 'Process(XXXX)' /// public string ProcessName { @@ -865,8 +866,8 @@ public string ProcessName } } /// - /// The processor Number (from 0 to TraceEventSource.NumberOfProcessors) that logged this event. - /// event. + /// The processor Number (from 0 to TraceEventSource.NumberOfProcessors) that logged this event. + /// event. /// public int ProcessorNumber { @@ -880,7 +881,7 @@ public int ProcessorNumber } } /// - /// Get the size of a pointer associated with process that logged the event (thus it is 4 for a 32 bit process). + /// Get the size of a pointer associated with process that logged the event (thus it is 4 for a 32 bit process). /// public int PointerSize { @@ -895,19 +896,19 @@ public int PointerSize /// /// Conceptually every ETW event can be given a ActivityID (GUID) that uniquely identifies the logical /// work being carried out (the activity). This property returns this GUID. Can return Guid.Empty - /// if the thread logging the event has no activity ID associated with it. + /// if the thread logging the event has no activity ID associated with it. /// public Guid ActivityID { get { return eventRecord->EventHeader.ActivityId; } } /// /// ETW supports the ability to take events with another GUID called the related activity that is either /// causes or is caused by the current activity. This property returns that GUID (or Guid.Empty if the - /// event has not related activity. + /// event has not related activity. /// public Guid RelatedActivityID { get { - // handle the cloned case, we put it first in the buffer. + // handle the cloned case, we put it first in the buffer. if (myBuffer != IntPtr.Zero) { if (myBuffer != (IntPtr)eventRecord) @@ -926,7 +927,7 @@ public Guid RelatedActivityID } } /// - /// Event Providers can define a 'message' for each event that are meant for human consumption. + /// Event Providers can define a 'message' for each event that are meant for human consumption. /// FormattedMessage returns this string with the values of the payload filled in at the appropriate places. /// It will return null if the event provider did not define a 'message' for this event /// @@ -934,11 +935,11 @@ public Guid RelatedActivityID /// /// Creates and returns the value of the 'message' for the event with payload values substituted. - /// Payload values are formatted using the given formatProvider. + /// Payload values are formatted using the given formatProvider. /// public virtual string GetFormattedMessage(IFormatProvider formatProvider) { - // This lets simple string payloads be shown as the FormattedMessage. + // This lets simple string payloads be shown as the FormattedMessage. if (IsEventWriteString || (eventRecord->EventHeader.Id == 0 && eventRecord->EventHeader.Opcode == 0 && eventRecord->EventHeader.Task == 0)) { @@ -951,7 +952,7 @@ public virtual string GetFormattedMessage(IFormatProvider formatProvider) /// /// An EventIndex is a integer that is guaranteed to be unique for this event over the entire log. Its /// primary purpose is to act as a key that allows side tables to be built up that allow value added - /// processing to 'attach' additional data to this particular event unambiguously. + /// processing to 'attach' additional data to this particular event unambiguously. /// This property is only set for ETLX file. For ETL or real time streams it returns 0 /// EventIndex is currently a 4 byte quantity. This does limit this property to 4Gig of events /// @@ -963,7 +964,7 @@ public EventIndex EventIndex // This is a guard against code running in TraceLog.CopyRawEvents that attempts to use // the EventIndex for an event returned by ETWTraceEventSource. It is unsafe to do so // because the EventIndex returned represents the index in the ETW stream, but user - // code needs the index in the newly created ETLX stream (which does not include + // code needs the index in the newly created ETLX stream (which does not include // "bookkeeping" events. User code should use the captured variable eventCount instead. Debug.Assert(!DisallowEventIndexAccess, "Illegal access to EventIndex"); #endif @@ -971,7 +972,7 @@ public EventIndex EventIndex } } /// - /// The TraceEventSource associated with this event. + /// The TraceEventSource associated with this event. /// public TraceEventSource Source { get { return traceEventSource; } } /// @@ -1072,20 +1073,20 @@ public override bool TryGetMember(GetMemberBinder binder, out object result) return result != null; } - // Getting at payload values. + // Getting at payload values. /// - /// Returns the names of all the manifest declared field names for the event. May be empty if the manifest is not available. + /// Returns the names of all the manifest declared field names for the event. May be empty if the manifest is not available. /// public abstract string[] PayloadNames { get; } /// - /// Given an index from 0 to PayloadNames.Length-1, return the value for that payload item as an object (boxed if necessary). + /// Given an index from 0 to PayloadNames.Length-1, return the value for that payload item as an object (boxed if necessary). /// public abstract object PayloadValue(int index); /// /// PayloadString is like PayloadValue(index).ToString(), however it can do a better job in some cases. In particular /// if the payload is a enumeration or a bitfield and the manifest defined the enumeration values, then it will print the string name - /// of the enumeration value instead of the integer value. + /// of the enumeration value instead of the integer value. /// public virtual string PayloadString(int index, IFormatProvider formatProvider = null) { @@ -1126,7 +1127,7 @@ public virtual string PayloadString(int index, IFormatProvider formatProvider = if (value is long) { - if (PayloadNames[index] == "objectId") // TODO this is a hack. + if (PayloadNames[index] == "objectId") // TODO this is a hack. { return "0x" + ((long)value).ToString("x8"); } @@ -1193,7 +1194,7 @@ public virtual string PayloadString(int index, IFormatProvider formatProvider = sb.Append('[').Append(new System.Net.IPAddress(ipV6).ToString()).Append("]:").Append(port); } } - // If we did not find a way of pretty printing int, dump it as bytes. + // If we did not find a way of pretty printing int, dump it as bytes. if (sb.Length == 0) { var limit = Math.Min(asByteArray.Length, 16); @@ -1247,7 +1248,7 @@ public virtual string PayloadString(int index, IFormatProvider formatProvider = } } /// - /// Returns the index in 'PayloadNames for field 'propertyName'. Returns something less than 0 if not found. + /// Returns the index in 'PayloadNames for field 'propertyName'. Returns something less than 0 if not found. /// public int PayloadIndex(string propertyName) { @@ -1263,7 +1264,7 @@ public int PayloadIndex(string propertyName) return -1; } /// - /// PayloadByName fetches the value of a payload property by the name of the property. + /// PayloadByName fetches the value of a payload property by the name of the property. /// It will return null if propertyName is not found. /// This method is not intended to be used in performance critical code. /// @@ -1278,7 +1279,7 @@ public object PayloadByName(string propertyName) return null; } /// - /// PayloadStringByName functions the same as PayloadByName, but uses PayloadString instead of PayloadValue. + /// PayloadStringByName functions the same as PayloadByName, but uses PayloadString instead of PayloadValue. /// It will return null if propertyName is not found. /// This method is not intended to be used in performance critical code. /// @@ -1304,7 +1305,7 @@ public int EventDataLength get { return eventRecord->UserDataLength; } } /// - /// Returns an array of bytes representing the event-specific payload associated with the event. + /// Returns an array of bytes representing the event-specific payload associated with the event. /// Normally this method is not used because some TraceEventParser has built a subclass of /// TraceEvent that parses the payload /// @@ -1314,7 +1315,7 @@ public byte[] EventData() } /// /// Gets the event data and puts it in 'targetBuffer' at 'targetStartIndex' and returns the resulting buffer. - /// If 'targetBuffer is null, it will allocate a buffer of the correct size. + /// If 'targetBuffer is null, it will allocate a buffer of the correct size. /// Normally this method is not used because some TraceEventParser has built a subclass of /// TraceEvent that parses the payload /// @@ -1348,14 +1349,14 @@ public byte[] EventData(byte[] targetBuffer, int targetStartIndex, int sourceSta /// public virtual unsafe TraceEvent Clone() { - TraceEvent ret = (TraceEvent)MemberwiseClone(); // Clone myself. - ret.next = null; // the clone is not in any linked list. + TraceEvent ret = (TraceEvent)MemberwiseClone(); // Clone myself. + ret.next = null; // the clone is not in any linked list. if (eventRecord != null) { int userDataLength = (EventDataLength + 3) / 4 * 4; // DWORD align int extendedDataLength = 0; - // We need to copy out the RelatedActivityID if it is there. + // We need to copy out the RelatedActivityID if it is there. Guid relatedActivityID = RelatedActivityID; if (relatedActivityID != default(Guid)) { @@ -1390,7 +1391,7 @@ public virtual unsafe TraceEvent Clone() } /// - /// Pretty print the event. It uses XML syntax.. + /// Pretty print the event. It uses XML syntax.. /// public override string ToString() { @@ -1406,7 +1407,7 @@ public virtual string ToString(IFormatProvider formatProvider) } /// - /// Write an XML representation to the stringBuilder sb and return it. + /// Write an XML representation to the stringBuilder sb and return it. /// public virtual StringBuilder ToXml(StringBuilder sb) { @@ -1414,7 +1415,7 @@ public virtual StringBuilder ToXml(StringBuilder sb) } /// - /// Writes an XML representation of the event to a StringBuilder sb, formatting data using the passed format provider. + /// Writes an XML representation of the event to a StringBuilder sb, formatting data using the passed format provider. /// Returns the StringBuilder. /// public virtual StringBuilder ToXml(StringBuilder sb, IFormatProvider formatProvider) @@ -1436,7 +1437,7 @@ public virtual StringBuilder ToXml(StringBuilder sb, IFormatProvider formatProvi { string payloadName = payloadNames[i]; - // XML does not allow you to repeat attributes, so we need change the name if that happens. + // XML does not allow you to repeat attributes, so we need change the name if that happens. // Note that this is not perfect, but avoids the likley cases if (payloadName == "ProviderName" || payloadName == "FormattedMessage" || payloadName == "MSec" || payloadName == "PID" || payloadName == "PName" || payloadName == "TID" || payloadName == "ActivityID") @@ -1551,19 +1552,19 @@ public string Dump(bool includePrettyPrint = false, bool truncateDump = false) } /// - /// EventTypeUserData is a field users get to use to attach their own data on a per-event-type basis. + /// EventTypeUserData is a field users get to use to attach their own data on a per-event-type basis. /// public object EventTypeUserData; /// /// Returns the raw IntPtr pointer to the data blob associated with the event. This is the way the - /// subclasses of TraceEvent get at the data to display it in a efficient (but unsafe) manner. + /// subclasses of TraceEvent get at the data to display it in a efficient (but unsafe) manner. /// public IntPtr DataStart { get { return userData; } } #region Protected /// - /// Create a template with the given event meta-data. Used by TraceParserGen. + /// Create a template with the given event meta-data. Used by TraceParserGen. /// protected TraceEvent(int eventID, int task, string taskName, Guid taskGuid, int opcode, string opcodeName, Guid providerGuid, string providerName) { @@ -1598,7 +1599,7 @@ protected internal int SkipShortUTF8String(int offset) /// /// Skip UTF8 string starting at 'offset' bytes into the payload blob. - /// + /// /// Offset just after the string protected internal int SkipUTF8String(int offset) { @@ -1613,7 +1614,7 @@ protected internal int SkipUTF8String(int offset) } /// /// Skip Unicode string starting at 'offset' bytes into the payload blob. - /// + /// /// Offset just after the string protected internal int SkipUnicodeString(int offset) { @@ -1628,7 +1629,7 @@ protected internal int SkipUnicodeString(int offset) } /// /// Skip 'stringCount' Unicode strings starting at 'offset' bytes into the payload blob. - /// + /// /// Offset just after the last string protected internal int SkipUnicodeString(int offset, int stringCount) { @@ -1641,18 +1642,18 @@ protected internal int SkipUnicodeString(int offset, int stringCount) } /// /// Skip a Security ID (SID) starting at 'offset' bytes into the payload blob. - /// + /// /// Offset just after the Security ID internal int SkipSID(int offset) { IntPtr mofData = DataStart; - // This is a Security Token. Either it is null, which takes 4 bytes, + // This is a Security Token. Either it is null, which takes 4 bytes, // Otherwise it is an 8 byte structure (TOKEN_USER) followed by SID, which is variable // size (sigh) depending on the 2nd byte in the SID int sid = TraceEventRawReaders.ReadInt32(mofData, offset); if (sid == 0) { - return offset + 4; // TODO confirm + return offset + 4; // TODO confirm } else { @@ -1689,7 +1690,7 @@ protected internal int HostOffset(int offset, int numPointers) return offset + (PointerSize - 4) * numPointers; } /// - /// Computes the size of 'numPointers' pointers on the machine where the event was collected. + /// Computes the size of 'numPointers' pointers on the machine where the event was collected. /// internal int HostSizePtr(int numPointers) { @@ -1749,8 +1750,8 @@ internal string GetFixedAnsiStringAt(int charCount, int offset) /// /// Given an Offset to a fixed sized string at 'offset', whose buffer size is 'charCount' /// Returns the string value. A null in the string will terminate the string before the - /// end of the buffer. - /// + /// end of the buffer. + /// internal string GetFixedUnicodeStringAt(int charCount, int offset) { StringBuilder sb = new StringBuilder(); @@ -1766,7 +1767,7 @@ internal string GetFixedUnicodeStringAt(int charCount, int offset) return sb.ToString(); } /// - /// Returns the encoding of a Version 6 IP address that has been serialized at 'offset' in the payload bytes. + /// Returns the encoding of a Version 6 IP address that has been serialized at 'offset' in the payload bytes. /// internal System.Net.IPAddress GetIPAddrV6At(int offset) { @@ -1779,14 +1780,14 @@ internal System.Net.IPAddress GetIPAddrV6At(int offset) return new System.Net.IPAddress(addrBytes); } /// - /// Returns the GUID serialized at 'offset' in the payload bytes. + /// Returns the GUID serialized at 'offset' in the payload bytes. /// protected internal Guid GetGuidAt(int offset) { return TraceEventRawReaders.ReadGuid(DataStart, offset); } /// - /// Get the DateTime that serialized (as a windows FILETIME) at 'offset' in the payload bytes. + /// Get the DateTime that serialized (as a windows FILETIME) at 'offset' in the payload bytes. /// protected internal DateTime GetDateTimeAt(int offset) { @@ -1794,7 +1795,7 @@ protected internal DateTime GetDateTimeAt(int offset) } /// /// Given an Offset to a null terminated Unicode string in an payload bytes, return the string that is - /// held there. + /// held there. /// protected internal string GetUnicodeStringAt(int offset) { @@ -1878,7 +1879,7 @@ protected internal long GetIntPtrAt(int offset) } } /// - /// Gets something that is pointer sized for the provider that collected the data. + /// Gets something that is pointer sized for the provider that collected the data. /// protected internal Address GetAddressAt(int offset) { @@ -2010,7 +2011,7 @@ private static StringBuilder XmlAttribPrefix(StringBuilder sb, string attribName /// /// Prints a standard prefix for a event (includes the time of the event, the process ID and the - /// thread ID. + /// thread ID. /// protected internal StringBuilder Prefix(StringBuilder sb) { @@ -2027,7 +2028,7 @@ protected internal StringBuilder Prefix(StringBuilder sb) return sb; } - // If non-null, when reading from ETL files, call this routine to fix poorly formed Event headers. + // If non-null, when reading from ETL files, call this routine to fix poorly formed Event headers. // Ideally this would not be needed, and is never used on ETLX files. internal /*protected*/ bool NeedsFixup; internal /*protected*/ virtual void FixupData() { } @@ -2035,12 +2036,12 @@ protected internal StringBuilder Prefix(StringBuilder sb) internal /*protected*/ int ParentThread; /// - /// Because we want the ThreadID to be the ID of the CREATED thread, and the stack - /// associated with the event is the parentThreadID + /// Because we want the ThreadID to be the ID of the CREATED thread, and the stack + /// associated with the event is the parentThreadID /// internal int ThreadIDforStacks() { -#if !NOT_WINDOWS +#if !NOT_WINDOWS if (0 <= ParentThread) { Debug.Assert(this is ProcessTraceData || this is ThreadTraceData); @@ -2055,13 +2056,13 @@ internal int ThreadIDforStacks() #endif /// - /// Returns (or sets) the delegate associated with this event. + /// Returns (or sets) the delegate associated with this event. /// protected internal abstract Delegate Target { get; set; } /// - /// If this TraceEvent belongs to a parser that needs state, then this callback will set the state. - /// Parsers with state are reasonably rare, the main examples are KernelTraceEventParser and ClrTraceEventParser. + /// If this TraceEvent belongs to a parser that needs state, then this callback will set the state. + /// Parsers with state are reasonably rare, the main examples are KernelTraceEventParser and ClrTraceEventParser. /// protected internal virtual void SetState(object state) { } @@ -2088,8 +2089,8 @@ private static char HexDigit(int digit) } } /// - /// Returns the Timestamp for the event using Query Performance Counter (QPC) ticks. - /// The start time for the QPC tick counter is arbitrary and the units also vary. + /// Returns the Timestamp for the event using Query Performance Counter (QPC) ticks. + /// The start time for the QPC tick counter is arbitrary and the units also vary. /// [Obsolete("Not Obsolete but Discouraged. Please use TimeStampRelativeMSec.")] [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] @@ -2097,7 +2098,7 @@ private static char HexDigit(int digit) /// /// A standard way for events to are that certain addresses are addresses in code and ideally have - /// symbolic information associated with them. Returns true if successful. + /// symbolic information associated with them. Returns true if successful. /// internal virtual bool LogCodeAddresses(Func callBack) { @@ -2114,7 +2115,7 @@ internal bool IsEventWriteString } /// - /// Used for binary searching of event IDs. Abstracts the size (currently a int, could go to long) + /// Used for binary searching of event IDs. Abstracts the size (currently a int, could go to long) /// internal static int Compare(EventIndex id1, EventIndex id2) { @@ -2122,7 +2123,7 @@ internal static int Compare(EventIndex id1, EventIndex id2) } /// - /// Returns true if the two traceEvents have the same identity. + /// Returns true if the two traceEvents have the same identity. /// internal bool Matches(TraceEvent other) { @@ -2153,11 +2154,11 @@ internal bool Matches(TraceEvent other) /// - /// Normally TraceEvent does not have unmanaged data, but if you call 'Clone' it will. + /// Normally TraceEvent does not have unmanaged data, but if you call 'Clone' it will. /// ~TraceEvent() { - // Most Data does not own its data, so this is usually a no-op. + // Most Data does not own its data, so this is usually a no-op. if (myBuffer != IntPtr.Zero) { @@ -2166,7 +2167,7 @@ internal bool Matches(TraceEvent other) } /// - /// For debugging. dumps an array. If you specify a size of 0 (the default) it dumps the whole array. + /// For debugging. dumps an array. If you specify a size of 0 (the default) it dumps the whole array. /// internal static string DumpArray(byte[] bytes, int size = 0) { @@ -2188,7 +2189,7 @@ internal static void DumpBytes(byte[] bytes, int length, TextWriter output, stri int rowStartIdx = 0; while (rowStartIdx < length) { - // if we have asked for truncation and we have written 4 rows, skip to the last 4 rows. + // if we have asked for truncation and we have written 4 rows, skip to the last 4 rows. if (rowStartIdx == startTruncate) { var afterSkipIdx = (length - startTruncate + 15) & ~0xF; // round down to the next row of 15 @@ -2307,12 +2308,12 @@ internal unsafe string EventDataAsString() } char* ptr = (char*)DataStart; - if (ptr[numChars - 1] != '\0') // Needs to be null terminated. + if (ptr[numChars - 1] != '\0') // Needs to be null terminated. { return null; } - for (int i = 0; i < numChars - 1; i++) // Rest need to be printable ASCII chars. + for (int i = 0; i < numChars - 1; i++) // Rest need to be printable ASCII chars. { char c = ptr[i]; if (!((' ' <= c && c <= '~') || c == '\n' || c == '\r')) @@ -2336,14 +2337,14 @@ protected internal virtual void Dispatch() } /// - /// This is a DEBUG-ONLY routine that allows a routine to do consistency checking in a debug build. + /// This is a DEBUG-ONLY routine that allows a routine to do consistency checking in a debug build. /// protected internal virtual void Validate() { } /// - /// Validate that the events is not trash. + /// Validate that the events is not trash. /// [Conditional("DEBUG")] protected internal void DebugValidate() @@ -2352,9 +2353,9 @@ protected internal void DebugValidate() } // Note that you can't use the ExtendedData, UserData or UserContext fields, they are not set - // properly in all cases. + // properly in all cases. internal TraceEventNativeMethods.EVENT_RECORD* eventRecord; // points at the record data itself. (fixed size) - internal IntPtr userData; // The event-specific payload. + internal IntPtr userData; // The event-specific payload. /// /// TraceEvent knows where to dispatch to. To support many subscriptions to the same event we chain @@ -2368,7 +2369,7 @@ protected internal void DebugValidate() internal bool containsSelfDescribingMetadata; // These are constant over the TraceEvent's lifetime (after setup) (except for the UnhandledTraceEvent - internal TraceEventID eventID; // The ID you should switch on. + internal TraceEventID eventID; // The ID you should switch on. internal /*protected*/ TraceEventOpcode opcode; internal /*protected*/ string opcodeName; internal /*protected*/ TraceEventTask task; @@ -2380,11 +2381,11 @@ protected internal void DebugValidate() internal /*protected*/ string eventName; /// - /// The array of names for each property in the payload (in order). + /// The array of names for each property in the payload (in order). /// protected internal string[] payloadNames; internal TraceEventSource traceEventSource; - internal EventIndex eventIndex; // something that uniquely identifies this event in the stream. + internal EventIndex eventIndex; // something that uniquely identifies this event in the stream. internal IntPtr myBuffer; // If the raw data is owned by this instance, this points at it. Normally null. internal string instanceContainerID; // If the raw data is owned by this instance (e.g. the event has been cloned), then if there is a container ID it will be saved here. Normally null. #endregion @@ -2392,19 +2393,19 @@ protected internal void DebugValidate() /// /// Individual event providers can supply many different types of events. These are distinguished from each - /// other by a TraceEventID, which is just a 16 bit number. Its meaning is provider-specific. + /// other by a TraceEventID, which is just a 16 bit number. Its meaning is provider-specific. /// public enum TraceEventID : ushort { /// - /// Illegal is a EventID that is not used by a normal event. + /// Illegal is a EventID that is not used by a normal event. /// Illegal = 0xFFFF, } /// /// Providers can define different audiences or Channels for an event (eg Admin, Developer ...). - /// It is only used for Windows Event log support. + /// It is only used for Windows Event log support. /// public enum TraceEventChannel : byte { @@ -2423,7 +2424,7 @@ public enum TraceEventChannel : byte public enum TraceEventOpcode : byte { /// - /// Generic opcode that does not have specific semantics associated with it. + /// Generic opcode that does not have specific semantics associated with it. /// Info = 0, /// @@ -2442,7 +2443,7 @@ public enum TraceEventOpcode : byte /// /// The entity (process, thread, ...) did not terminate before data collection ended, so indicate /// this at data collection termination time. This is mostly for 'flight recorder' scenarios where - /// you only have the 'tail' of the data and would like to know about everything that existed. + /// you only have the 'tail' of the data and would like to know about everything that existed. /// DataCollectionStop = 4, /// @@ -2466,12 +2467,12 @@ public enum TraceEventOpcode : byte /// Transfer = 9, // Receive = 240, - // 255 is used as in 'illegal opcode' and signifies a WPP style event. These events - // use the event ID and the TASK Guid as their lookup key. + // 255 is used as in 'illegal opcode' and signifies a WPP style event. These events + // use the event ID and the TASK Guid as their lookup key. }; /// - /// Indicates to a provider whether verbose events should be logged. + /// Indicates to a provider whether verbose events should be logged. /// public enum TraceEventLevel { @@ -2505,7 +2506,7 @@ public enum TraceEventLevel /// ETW defines the concept of a Keyword, which is a 64 bit bitfield. Each bit in the bitfield /// represents some provider defined 'area' that is useful for filtering. When processing the events, it /// is then possible to filter based on whether various bits in the bitfield are set. There are some - /// standard keywords, but most are provider specific. + /// standard keywords, but most are provider specific. /// [Flags] public enum TraceEventKeyword : long @@ -2524,33 +2525,33 @@ public enum TraceEventKeyword : long } /// - /// Tasks are groups of related events for a given provider (for example Process, or Thread, Kernel Provider). - /// They are defined by the provider. + /// Tasks are groups of related events for a given provider (for example Process, or Thread, Kernel Provider). + /// They are defined by the provider. /// public enum TraceEventTask : ushort { /// - /// If you don't explicitly choose a task you get the default + /// If you don't explicitly choose a task you get the default /// Default = 0 } /// - /// EventIdex is a unsigned integer that is unique to a particular event. EventIndex is guaranteed to be - /// unique over the whole log. It is only used by ETLX files. + /// EventIdex is a unsigned integer that is unique to a particular event. EventIndex is guaranteed to be + /// unique over the whole log. It is only used by ETLX files. /// /// Currently the event ID simply the index in the log file of the event. We don't however guarantee ordering. /// In the future we may add new events to the log and given them IDs 'at the end' even if the events are not - /// at the end chronologically. + /// at the end chronologically. /// /// - /// EventIndex is a 32 bit number limits it to 4Gig events in an ETLX file. + /// EventIndex is a 32 bit number limits it to 4Gig events in an ETLX file. /// /// public enum EventIndex : uint { /// - /// Invalid is an EventIndex that will not be used by a normal event. + /// Invalid is an EventIndex that will not be used by a normal event. /// Invalid = unchecked((uint)-1) }; @@ -2559,52 +2560,52 @@ public enum EventIndex : uint /// TraceEventSource has two roles. The first is the obvious one of providing some properties /// like 'SessionStartTime' for clients. The other role is provide an interface for TraceEventParsers /// to 'hook' to so that events can be decoded. ITraceParserServices is the API service for this - /// second role. It provides the methods that parsers register templates for subclasses of - /// the TraceEvent class that know how to parse particular events. + /// second role. It provides the methods that parsers register templates for subclasses of + /// the TraceEvent class that know how to parse particular events. /// public interface ITraceParserServices { /// - /// RegisterEventTemplate is the mechanism a particular event payload description 'template' + /// RegisterEventTemplate is the mechanism a particular event payload description 'template' /// (a subclass of TraceEvent) is injected into the event processing stream. Once registered, an /// event is 'parsed' simply by setting the 'rawData' field in the event. It is up to the template /// then to take this raw data an present it in a useful way to the user (via properties). Note that /// parsing is thus 'lazy' in no processing of the raw data is not done at event dispatch time but /// only when the properties of an event are accessed. - /// + /// /// Ownership of the template transfers when this call is made. The source will modify this and - /// assumes it has exclusive use (thus you should clone the template if necessary). + /// assumes it has exclusive use (thus you should clone the template if necessary). /// /// Another important aspect is that templates are reused by TraceEventSource aggressively. The - /// expectation is that no memory needs to be allocated during a normal dispatch + /// expectation is that no memory needs to be allocated during a normal dispatch /// /// void RegisterEventTemplate(TraceEvent template); /// - /// UnregisterEventTemplate undoes the action of RegisterEventTemplate. Logically you would + /// UnregisterEventTemplate undoes the action of RegisterEventTemplate. Logically you would /// pass the template to unregister, but typically you don't have that at unregistration time. /// To avoid forcing clients to remember the templates they registered, UnregisterEventTemplate /// takes three things that will uniquely identify the template to unregister. These are - /// the eventID, and provider ID and the Action (callback) for the template. + /// the eventID, and provider ID and the Action (callback) for the template. /// void UnregisterEventTemplate(Delegate action, int eventId, Guid providerGuid); /// /// It is expected that when a subclass of TraceEventParser is created, it calls this - /// method on the source. This allows the source to do any Parser-specific initialization. + /// method on the source. This allows the source to do any Parser-specific initialization. /// void RegisterParser(TraceEventParser parser); - // TODO should have an UnRegisterParser(TraceEventParser parser) API. + // TODO should have an UnRegisterParser(TraceEventParser parser) API. /// /// Indicates that this callback should be called on any unhandled event. The callback /// returns true if the lookup should be retried after calling this (that is there is - /// the unhandled event was found). + /// the unhandled event was found). /// void RegisterUnhandledEvent(Func callback); - // TODO Add an unregister API. + // TODO Add an unregister API. /// /// Looks if any provider has registered an event with task with 'taskGuid'. Will return null if @@ -2620,25 +2621,26 @@ public interface ITraceParserServices /// /// TraceEventParser Represents a class that knows how to decode particular set of events (typically - /// all the events of a single ETW provider). It is expected that subclasses of TraceEventParser - /// have a constructor that takes a TraceEventSource as an argument that 'attaches' th parser + /// all the events of a single ETW provider). It is expected that subclasses of TraceEventParser + /// have a constructor that takes a TraceEventSource as an argument that 'attaches' th parser /// to the TraceEventSource. TraceEventParsers break into two groups. /// /// * Those that work on a single provider, and thus the provider name is implicit in th parser. This is the common case. /// The AddCallbackForEvent* methods are meant to be used for these TraceEventParsers /// - /// * Those that work on multiple providers. There are only a handful of these (DynamicTraceEventParser, ...). + /// * Those that work on multiple providers. There are only a handful of these (DynamicTraceEventParser, ...). /// The AddCallbackForProviderEvent* methods which take 'Provider' parameters are meant to be used for these TraceEventParsers /// /// /// In addition to the AddCallback* methods on TraceEventParser, there are also Observe* extension methods that - /// provide callbacks using the IObservable style. + /// provide callbacks using the IObservable style. /// /// + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] public abstract class TraceEventParser { /// - /// Get the source this TraceEventParser is attached to. + /// Get the source this TraceEventParser is attached to. /// public TraceEventSource Source { get { return (TraceEventSource)source; } } @@ -2657,11 +2659,11 @@ public virtual event Action All } } - // subscribe to a single event for parsers that handle a single provider + // subscribe to a single event for parsers that handle a single provider /// /// A shortcut that adds 'callback' in the provider associated with this parser (ProvderName) and an event name 'eventName'. 'eventName' - /// can be null in which case any event that matches 'Action{T}' will call the callback. - /// 'eventName is of the form 'TaskName/OpcodeName' if the event has a non-trivial opcode, otherwise it is 'TaskName'. + /// can be null in which case any event that matches 'Action{T}' will call the callback. + /// 'eventName is of the form 'TaskName/OpcodeName' if the event has a non-trivial opcode, otherwise it is 'TaskName'. /// /// The callback alone is used as the subscription id for unregistration, so the callback delegate should be unique (by delegate comparison) /// @@ -2679,19 +2681,19 @@ public void AddCallbackForEvent(string eventName, Action callback) where T } // subscribe to a set of events for parsers that handle a single provider. (These are more strongly typed to provider specific payloads) /// - /// Causes 'callback' to be called for any event in the provider associated with this parser (ProviderName) whose type is compatible with T and - /// whose eventName will pass 'eventNameFilter'. + /// Causes 'callback' to be called for any event in the provider associated with this parser (ProviderName) whose type is compatible with T and + /// whose eventName will pass 'eventNameFilter'. /// public virtual void AddCallbackForEvents(Action callback) where T : TraceEvent { AddCallbackForEvents(null, null, callback); } /// - /// Causes 'callback' to be called for any event in the provider associated with this parser (ProviderName) whose type is compatible with T and - /// whose eventName will pass 'eventNameFilter'. The eventNameFilter parameter can be null, in which case all events that are compatible - /// with T will be selected. - /// - /// eventNames passed to the filer are of the form 'TaskName/OpcodeName' if the event has a non-trivial opcode, otherwise it is 'TaskName'. + /// Causes 'callback' to be called for any event in the provider associated with this parser (ProviderName) whose type is compatible with T and + /// whose eventName will pass 'eventNameFilter'. The eventNameFilter parameter can be null, in which case all events that are compatible + /// with T will be selected. + /// + /// eventNames passed to the filer are of the form 'TaskName/OpcodeName' if the event has a non-trivial opcode, otherwise it is 'TaskName'. /// public virtual void AddCallbackForEvents(Predicate eventNameFilter, Action callback) where T : TraceEvent { @@ -2699,16 +2701,16 @@ public virtual void AddCallbackForEvents(Predicate eventNameFilter, A } /// - /// Causes 'callback' to be called for any event in the provider associated with this parser (ProviderName) whose type is compatible with T and - /// whose eventName will pass 'eventNameFilter'. The eventNameFilter parameter can be null, in which case all events that are compatible - /// with T will be selected. + /// Causes 'callback' to be called for any event in the provider associated with this parser (ProviderName) whose type is compatible with T and + /// whose eventName will pass 'eventNameFilter'. The eventNameFilter parameter can be null, in which case all events that are compatible + /// with T will be selected. /// /// A 'subscriptionID' can be passed and this value along with the callback can be used /// to uniquely identify subscription to remove using the 'RemoveCallback' API. If null is passed, then only the identity of the callback can - /// be used to identify the subscription to remove. - /// - /// eventNames passed to the filer are of the form 'TaskName/OpcodeName' if the event has a non-trivial opcode, otherwise it is 'TaskName'. - /// + /// be used to identify the subscription to remove. + /// + /// eventNames passed to the filer are of the form 'TaskName/OpcodeName' if the event has a non-trivial opcode, otherwise it is 'TaskName'. + /// /// public void AddCallbackForEvents(Predicate eventNameFilter, object subscriptionId, Action callback) where T : TraceEvent { @@ -2717,7 +2719,7 @@ public void AddCallbackForEvents(Predicate eventNameFilter, object su throw new InvalidOperationException("Need to use AddCalbackForProviderEvents for Event Parsers that handle more than one provider"); } - // Convert the eventNameFilter to the more generic filter that take a provider name as well. + // Convert the eventNameFilter to the more generic filter that take a provider name as well. Func eventsToObserve = delegate (string pName, string eName) { if (pName != GetProviderName()) @@ -2739,7 +2741,7 @@ public void AddCallbackForEvents(Predicate eventNameFilter, object su }; // This is almost the same code as in AddCallbackForProviderEvents, however it is different because it - // checks that the template is of type T (not just that the name and provider match). This is important. + // checks that the template is of type T (not just that the name and provider match). This is important. var newSubscription = new SubscriptionRequest(eventsToObserve, callback, subscriptionId); m_subscriptionRequests.Add(newSubscription); var templateState = StateObject; @@ -2756,9 +2758,9 @@ public void AddCallbackForEvents(Predicate eventNameFilter, object su /// /// A shortcut that adds 'callback' for the event in 'providerName' and an event name 'eventName' /// The callback alone is used as the subscription id for unregistration, so the callback delegate should be unique (by delegate comparison) - /// - /// eventName is of the of the form 'TaskName/OpcodeName' if the event has a non-trivial opcode, otherwise it is 'TaskName'. - /// + /// + /// eventName is of the of the form 'TaskName/OpcodeName' if the event has a non-trivial opcode, otherwise it is 'TaskName'. + /// /// public void AddCallbackForProviderEvent(string providerName, string eventName, Action callback) { @@ -2784,17 +2786,17 @@ public void AddCallbackForProviderEvent(string providerName, string eventName, A return EventFilterResponse.RejectEvent; }, callback); } - // subscribe to a set of events for TraceEventParsers that handle more than one provider + // subscribe to a set of events for TraceEventParsers that handle more than one provider /// /// Cause 'callback' to be called for any event that this parser recognizes for which the function 'eventsToObserve' /// returns 'AcceptEvent'. The 'eventsToObserve is given both the provider name (first) and the event name and can return - /// 'AcceptEvent' 'RejectEvent' or 'RejectProvider' (in which case it may not be called again for that provider). - /// eventsToObserver can be null in which case all events that match the parser recognizes are selected. - /// - /// eventNames passed to the filer are of the form 'TaskName/OpcodeName' if the event has a non-trivial opcode, otherwise it is 'TaskName'. - /// + /// 'AcceptEvent' 'RejectEvent' or 'RejectProvider' (in which case it may not be called again for that provider). + /// eventsToObserver can be null in which case all events that match the parser recognizes are selected. + /// + /// eventNames passed to the filer are of the form 'TaskName/OpcodeName' if the event has a non-trivial opcode, otherwise it is 'TaskName'. + /// /// - /// Thus this method works for parsers that parse more than one provider (e.g. DynamicTraceEventParser). + /// Thus this method works for parsers that parse more than one provider (e.g. DynamicTraceEventParser). /// /// public virtual void AddCallbackForProviderEvents(Func eventsToObserve, Action callback) @@ -2804,15 +2806,15 @@ public virtual void AddCallbackForProviderEvents(Func /// Cause 'callback' to be called for any event that this parser recognizes for which the function 'eventsToObserve' /// returns 'AcceptEvent'. The 'eventsToObserve is given both the provider name (first) and the event name and can return - /// 'AcceptEvent' 'RejectEvent' or 'RejectProvider' (in which case it may not be called again for that provider). - /// eventsToObserver can be null in which case all events that match the parser recognizes are selected. - /// - /// eventNames passed to the filer are of the form 'TaskName/OpcodeName' if the event has a non-trivial opcode, otherwise it is 'TaskName'. /// + /// 'AcceptEvent' 'RejectEvent' or 'RejectProvider' (in which case it may not be called again for that provider). + /// eventsToObserver can be null in which case all events that match the parser recognizes are selected. + /// + /// eventNames passed to the filer are of the form 'TaskName/OpcodeName' if the event has a non-trivial opcode, otherwise it is 'TaskName'. /// /// - /// Thus this method works for parsers that parse more than one provider (e.g. DynamicTraceEventParser). + /// Thus this method works for parsers that parse more than one provider (e.g. DynamicTraceEventParser). /// /// A subscriptionID can optionally be passed. This is used (along with the callback identity) to identify this to the 'RemoveCallback' If you - /// don't need to remove the callback or you will do it in bulk, you don't need this parameter. + /// don't need to remove the callback or you will do it in bulk, you don't need this parameter. /// /// public virtual void AddCallbackForProviderEvents(Func eventsToObserve, object subscriptionId, Action callback) @@ -2828,11 +2830,11 @@ public virtual void AddCallbackForProviderEvents(Func - /// Remove all subscriptions added with 'AddCallback' (any overload), that is compatible with T, has a callback 'callback' and subscriptionId 'subscriptionId' - /// where 'subscriptionId' was the value that was optionally passed to 'AddCallback' to provide exactly this disambiguation. + /// Remove all subscriptions added with 'AddCallback' (any overload), that is compatible with T, has a callback 'callback' and subscriptionId 'subscriptionId' + /// where 'subscriptionId' was the value that was optionally passed to 'AddCallback' to provide exactly this disambiguation. /// - /// 'callback' or 'subscriptionId' can be null, in which case it acts as a wild card. Thus RemoveCallback{TraceEvent}(null, null) will remove all callbacks - /// that were registered through this parser. + /// 'callback' or 'subscriptionId' can be null, in which case it acts as a wild card. Thus RemoveCallback{TraceEvent}(null, null) will remove all callbacks + /// that were registered through this parser. /// /// public virtual void RemoveCallback(Action callback, object subscriptionId = null) where T : TraceEvent @@ -2861,14 +2863,14 @@ public virtual void RemoveCallback(Action callback, object subscriptionId } /// - /// A static TraceEventParser is a parser where the set of events that can be subscribed to (and their payload fields) are known at + /// A static TraceEventParser is a parser where the set of events that can be subscribed to (and their payload fields) are known at /// compile time. There are very few dynamic TraceEventParsers (DynamicTraceEventParser, RegisteredTraceEventParser and WPPTraceEventParser) /// public virtual bool IsStatic { get { return true; } } #region protected /// - /// All TraceEventParsers invoke this constructor. If 'dontRegister' is true it is not registered with the source. + /// All TraceEventParsers invoke this constructor. If 'dontRegister' is true it is not registered with the source. /// protected TraceEventParser(TraceEventSource source, bool dontRegister = false) { @@ -2892,14 +2894,14 @@ protected TraceEventParser(TraceEventSource source, bool dontRegister = false) /// /// Normally a TraceEvent parser knows how to parse only one provider. If this is true - /// ProviderName returns the name of this provider. If the parser knows how to parse - /// more than one provider, this property returns null. + /// ProviderName returns the name of this provider. If the parser knows how to parse + /// more than one provider, this property returns null. /// protected abstract string GetProviderName(); /// - /// If the parser needs to persist data along with the events we put it in a separate object. - /// This object and then implement serialization functionality that allows it to be persisted (this is for ETLX support). + /// If the parser needs to persist data along with the events we put it in a separate object. + /// This object and then implement serialization functionality that allows it to be persisted (this is for ETLX support). /// protected internal object StateObject { @@ -2916,14 +2918,14 @@ protected internal object StateObject } /// - /// Returns a list of all templates currently existing (new ones can come in, but OnNewEventDefintion must be called + /// Returns a list of all templates currently existing (new ones can come in, but OnNewEventDefintion must be called /// whenever that happens. Note that the returned templates MUST be cloned and do not have their source or parser state - /// fields set. These must be set as part of subscription (after you know if you care about them or not). - /// + /// fields set. These must be set as part of subscription (after you know if you care about them or not). + /// /// eventsToObserver is given the provider name and event name and those events that return AcceptEvent will - /// have the 'callback' function called on that template. eventsToObserver can be null which mean all events. - /// - /// The returned template IS READ ONLY! If you need a read-write copy (typical), clone it first. + /// have the 'callback' function called on that template. eventsToObserver can be null which mean all events. + /// + /// The returned template IS READ ONLY! If you need a read-write copy (typical), clone it first. /// protected internal abstract void EnumerateTemplates(Func eventsToObserve, Action callback); @@ -2940,8 +2942,8 @@ protected internal virtual IEnumerable EnumerateCtfEventMapping #if DEBUG /// - /// Debug-only code Confirm that some did not add an event, but forgot to add it to the enumeration that EnumerateTemplates - /// returns. + /// Debug-only code Confirm that some did not add an event, but forgot to add it to the enumeration that EnumerateTemplates + /// returns. /// private bool m_ConfirmedAllEventsAreInEnumeration; @@ -2949,8 +2951,8 @@ private void ConfirmAllEventsAreInEnumeration() { var declaredSet = new SortedDictionary(); - // TODO FIX NOW currently we have a hack where we know we are not correct - // for JScriptTraceEventParser. + // TODO FIX NOW currently we have a hack where we know we are not correct + // for JScriptTraceEventParser. if (GetType().Name == "JScriptTraceEventParser") { return; @@ -2962,7 +2964,7 @@ private void ConfirmAllEventsAreInEnumeration() return; } - // Use reflection to see what events have declared + // Use reflection to see what events have declared MethodInfo[] methods = GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); for (int i = 0; i < methods.Length; i++) { @@ -2991,22 +2993,22 @@ private void ConfirmAllEventsAreInEnumeration() continue; } - if (eventName == "MemoryPageAccess" || eventName == "MemoryProcessMemInfo") // One event has two templates. + if (eventName == "MemoryPageAccess" || eventName == "MemoryProcessMemInfo") // One event has two templates. { continue; } - if (eventName == "GCSampledObjectAllocation") // One event has two templates. + if (eventName == "GCSampledObjectAllocation") // One event has two templates. { continue; } - if (eventName == "PerfInfoISR") // One event has two templates. + if (eventName == "PerfInfoISR") // One event has two templates. { continue; } - if (eventName == "MethodTailCallFailedAnsi") // One event has two templates. + if (eventName == "MethodTailCallFailedAnsi") // One event has two templates. { continue; } @@ -3028,7 +3030,7 @@ private void ConfirmAllEventsAreInEnumeration() var enumSet = new SortedDictionary(); - // Make sure that we have all the event we should have + // Make sure that we have all the event we should have EnumerateTemplates(null, delegate (TraceEvent template) { // the CLR provider calls this callback twice. Rather then refactoring EnumerateTemplates for all parsers @@ -3039,29 +3041,29 @@ private void ConfirmAllEventsAreInEnumeration() } var eventName = template.EventName.Replace("/", ""); - if (eventName == "MemoryPageAccess" || eventName == "MemoryProcessMemInfo") // One event has two templates. + if (eventName == "MemoryPageAccess" || eventName == "MemoryProcessMemInfo") // One event has two templates. { return; } - if (eventName == "GCSampledObjectAllocation") // One event has two templates. + if (eventName == "GCSampledObjectAllocation") // One event has two templates. { return; } - if (eventName == "PerfInfoISR") // One event has two templates. + if (eventName == "PerfInfoISR") // One event has two templates. { return; } if (eventName == "FileIO") { - eventName = "FileIOName"; // They use opcode 0 which gets truncated. + eventName = "FileIOName"; // They use opcode 0 which gets truncated. } if (eventName == "EventTrace") { - eventName = "EventTraceHeader"; // They use opcode 0 which gets truncated. + eventName = "EventTraceHeader"; // They use opcode 0 which gets truncated. } if (eventName == "Jscript_GC_IdleCollect") @@ -3124,23 +3126,23 @@ private void ConfirmAllEventsAreInEnumeration() /// /// If the parser can change over time (it can add new definitions), It needs to support this interface. See EnumerateDynamicTemplates for details. - /// This function should be called any time a new event is now parsable by the parser. If it is guaranteed that the particular event is - /// definitely being ADDED (it never existed in the past), then you can set 'mayHaveExistedBefore' to false and save some time. - /// - /// It returns false if there are no definitions for that particular Provider (and thus you can skip callback if desired). + /// This function should be called any time a new event is now parsable by the parser. If it is guaranteed that the particular event is + /// definitely being ADDED (it never existed in the past), then you can set 'mayHaveExistedBefore' to false and save some time. + /// + /// It returns false if there are no definitions for that particular Provider (and thus you can skip callback if desired). /// internal virtual EventFilterResponse OnNewEventDefintion(TraceEvent template, bool mayHaveExistedBefore) { #if !NOT_WINDOWS && !NO_DYNAMIC_TRACEEVENTPARSER Debug.Assert(template is DynamicTraceEventData || template is PredefinedDynamicEvent); #endif - EventFilterResponse combinedResponse = EventFilterResponse.RejectProvider; // This is the combined result from all subscriptions. + EventFilterResponse combinedResponse = EventFilterResponse.RejectProvider; // This is the combined result from all subscriptions. var templateState = StateObject; - // Does it match any subscription request we already have? + // Does it match any subscription request we already have? for (int i = 0; i < m_subscriptionRequests.Count; i++) { var cur = m_subscriptionRequests[i]; - // TODO sort template by provider so we can optimize. + // TODO sort template by provider so we can optimize. if (cur.m_eventToObserve != null) { var response = cur.m_eventToObserve(template.ProviderName, template.EventName); @@ -3149,7 +3151,7 @@ internal virtual EventFilterResponse OnNewEventDefintion(TraceEvent template, bo continue; } - // We know that we are not rejecting the provider as a whole. + // We know that we are not rejecting the provider as a whole. if (combinedResponse == EventFilterResponse.RejectProvider) { combinedResponse = EventFilterResponse.RejectEvent; @@ -3161,19 +3163,19 @@ internal virtual EventFilterResponse OnNewEventDefintion(TraceEvent template, bo } } - combinedResponse = EventFilterResponse.AcceptEvent; // There is at least one subscription for this template. + combinedResponse = EventFilterResponse.AcceptEvent; // There is at least one subscription for this template. Subscribe(cur, template, templateState, mayHaveExistedBefore); } return combinedResponse; } /// - /// Given a subscription request, and a template that can now be parsed (and its state, which is just TraceEventParser.StateObj) - /// If subscription states that the template should be registered with the source, then do the registration. - /// + /// Given a subscription request, and a template that can now be parsed (and its state, which is just TraceEventParser.StateObj) + /// If subscription states that the template should be registered with the source, then do the registration. + /// /// if 'mayHaveExistedBefore' means that this template definition may have been seen before (DynamicTraceEventParsers do this as /// you may get newer versions dynamically registering themselves). In that case this should be set. If you can guaranteed that - /// a particular template (provider-eventID pair) will only be subscribed at most once you can set this to false. + /// a particular template (provider-eventID pair) will only be subscribed at most once you can set this to false. /// private void Subscribe(SubscriptionRequest cur, TraceEvent template, object templateState, bool mayHaveExistedBefore) { @@ -3193,15 +3195,15 @@ private void Subscribe(SubscriptionRequest cur, TraceEvent template, object temp { // Trace.WriteLine("Subscribe: Found existing subscription, removing it for update"); - // Unsubscribe + // Unsubscribe source.UnregisterEventTemplate(activeSubscription.Target, (int)activeSubscription.ID, activeSubscription.ProviderGuid); - // TODO support WPP. + // TODO support WPP. if (activeSubscription.taskGuid != Guid.Empty) { source.UnregisterEventTemplate(activeSubscription.Target, (int)activeSubscription.Opcode, activeSubscription.taskGuid); } - // Update it in place the active subscription in place. + // Update it in place the active subscription in place. cur.m_activeSubscriptions[i] = templateWithCallback; } } @@ -3218,11 +3220,11 @@ private void Subscribe(SubscriptionRequest cur, TraceEvent template, object temp } } #endif - // we can simply add to our list of subscriptions. + // we can simply add to our list of subscriptions. cur.m_activeSubscriptions.Add(templateWithCallback); } - // Actually Register it with the source. + // Actually Register it with the source. source.RegisterEventTemplate(templateWithCallback); #if !DOTNET_V35 Debug.Assert(templateWithCallback.traceEventSource == Source || @@ -3232,7 +3234,7 @@ private void Subscribe(SubscriptionRequest cur, TraceEvent template, object temp } /// - /// Keeps track of a single 'AddCallback' request so it can be removed later. It also handles lazy addition of events. + /// Keeps track of a single 'AddCallback' request so it can be removed later. It also handles lazy addition of events. /// private class SubscriptionRequest { @@ -3247,15 +3249,15 @@ internal SubscriptionRequest(Func eventsToO m_subscriptionId = subscriptionId; } - // We need the original arguments to the AddCallback so that we can repeat them when new events are discovered. + // We need the original arguments to the AddCallback so that we can repeat them when new events are discovered. internal Func m_eventToObserve; // (Gives provider name then eventName). This is null for parsers that don't support OnNewEventDefintion (the strongly types ones) internal Delegate m_callback; - internal object m_subscriptionId; // Identifies this subscription so we could delete it. - internal GrowableArray m_activeSubscriptions; // The actual TraceEvents that have been registered with the TraceEventSource for this subscription. + internal object m_subscriptionId; // Identifies this subscription so we could delete it. + internal GrowableArray m_activeSubscriptions; // The actual TraceEvents that have been registered with the TraceEventSource for this subscription. } /// - /// The source that this parser is connected to. + /// The source that this parser is connected to. /// protected internal ITraceParserServices source; private GrowableArray m_subscriptionRequests; @@ -3265,7 +3267,7 @@ internal SubscriptionRequest(Func eventsToO } /// - /// EventFilterResponse is the set of responses a user-defined filtering routine, might return. This is used in the TraceEventParser.AddCallbackForProviderEvents method. + /// EventFilterResponse is the set of responses a user-defined filtering routine, might return. This is used in the TraceEventParser.AddCallbackForProviderEvents method. /// public enum EventFilterResponse { @@ -3300,7 +3302,7 @@ public sealed class TraceEventDispatcherOptions } /// - /// A TraceEventDispatcher is a TraceEventSource that supports a callback model for dispatching events. + /// A TraceEventDispatcher is a TraceEventSource that supports a callback model for dispatching events. /// public abstract unsafe class TraceEventDispatcher : TraceEventSource { @@ -3347,7 +3349,7 @@ public static TraceEventDispatcher GetDispatcherFromFileName(string traceFileNam // there are a couple of events that TraceEventDispatcher can handle directly. /// /// Subscribers to UnhandledEvent are called if no other hander has processed the event. It is - /// generally used in DEBUG builds to validate that events are getting to the source at all. + /// generally used in DEBUG builds to validate that events are getting to the source at all. /// public event Action UnhandledEvents { @@ -3363,10 +3365,10 @@ public event Action UnhandledEvents /// /// Subscribers to EveryEvent are called on every event in the trace. Normally you don't want /// to subscribe to this but rather use a TraceEvenParser (which knows how to decode the payloads) - /// and subscribe to particular events through that. For example Using TraceEventSource.Dynamic.All + /// and subscribe to particular events through that. For example Using TraceEventSource.Dynamic.All /// or TraceEventSource.Dynamic.All is more likely to be what you are looking for. AllEvents is only /// an event callback of last resort, that only gives you the 'raw' data (common fields but no - /// payload). + /// payload). /// /// This is called AFTER any event-specific handlers. /// @@ -3375,7 +3377,7 @@ public event Action UnhandledEvents /// /// Subscribers to UnhandledEvent are called if no other hander has processed the event. It is - /// generally used in DEBUG builds to validate that events are getting to the source at all. + /// generally used in DEBUG builds to validate that events are getting to the source at all. /// [Obsolete("Use UnhandledEvents")] public event Action UnhandledEvent @@ -3392,7 +3394,7 @@ public event Action UnhandledEvent /// /// Subscribers to EveryEvent are called on every event in the trace. Normally you don't want /// to subscribe to this but rather use a TraceEvenParser and subscribe to particular events - /// through that. + /// through that. /// /// This is called AFTER any event-specific handlers. /// @@ -3412,44 +3414,44 @@ public event Action EveryEvent /// /// Once a client has subscribed to the events of interest, calling Process actually causes - /// the callbacks to happen. + /// the callbacks to happen. /// - /// Subclasses implementing this method should call 'OnCompleted' - /// before returning. + /// Subclasses implementing this method should call 'OnCompleted' + /// before returning. /// /// /// false If StopProcessing was called public abstract bool Process(); /// /// Calling StopProcessing in a callback when 'Process()' is running will indicate that processing - /// should be stopped immediately and that the Process() method should return. - /// + /// should be stopped immediately and that the Process() method should return. + /// /// Note that this stop request will not be honored until the next event from the source. Thus - /// for real time sessions there is an indeterminate delay before the stop will complete. - /// If you need to force the stop you should instead call Dispose() on the session associated with + /// for real time sessions there is an indeterminate delay before the stop will complete. + /// If you need to force the stop you should instead call Dispose() on the session associated with /// the real time session. This will cause the source to be shut down and thus also stop processing - /// (Process() will return) but is guaranteed to complete in a timely manner. + /// (Process() will return) but is guaranteed to complete in a timely manner. /// public virtual void StopProcessing() { stopProcessing = true; } /// - /// Subscribers of Completed will be called after processing is complete (right before TraceEventDispatcher.Process returns. + /// Subscribers of Completed will be called after processing is complete (right before TraceEventDispatcher.Process returns. /// public event Action Completed; /// - /// Wrap (or filter) the dispatch of every event from the TraceEventDispatcher stream. + /// Wrap (or filter) the dispatch of every event from the TraceEventDispatcher stream. /// Instead of calling the normal code it calls 'hook' with both the event to be dispatched - /// and the method the would normally do the processing. Thus the routine has + /// and the method the would normally do the processing. Thus the routine has /// the option to call normal processing, surround it with things like a lock /// or skip it entirely. This can be called more than once, in which case the last /// hook method gets called first (which may end up calling the second ...) - /// - /// For example,here is an example that uses AddDispatchHook to - /// take a lock is taken whenever dispatch work is being performed. - /// + /// + /// For example,here is an example that uses AddDispatchHook to + /// take a lock is taken whenever dispatch work is being performed. + /// /// AddDispatchHook((anEvent, dispatcher) => { lock (this) { dispatcher(anEvent); } }); /// public void AddDispatchHook(Action> hook) @@ -3459,20 +3461,20 @@ public void AddDispatchHook(Action> hook) throw new ArgumentException("Must provide a non-null callback", nameof(hook), null); } - // Make a new dispatcher which calls the hook with the old dispatcher. + // Make a new dispatcher which calls the hook with the old dispatcher. Action oldUserDefinedDispatch = userDefinedDispatch ?? DoDispatch; userDefinedDispatch = delegate (TraceEvent anEvent) { hook(anEvent, oldUserDefinedDispatch); }; } #region protected /// - /// Called when processing is complete. You can call this more than once if your not sure if it has already been called. - /// however we do guard against races. + /// Called when processing is complete. You can call this more than once if your not sure if it has already been called. + /// however we do guard against races. /// protected void OnCompleted() { var completed = Completed; - Completed = null; // We set it to null so we only do it once. + Completed = null; // We set it to null so we only do it once. if (completed != null) { completed(); @@ -3503,7 +3505,7 @@ internal void DumpToDebugString() #endif internal bool AllEventsHasCallback { get { return AllEvents != null; } } /// - /// Number of different events that have callbacks associated with them + /// Number of different events that have callbacks associated with them /// internal int DistinctCallbackCount() { @@ -3520,7 +3522,7 @@ internal int DistinctCallbackCount() } /// - /// Total number of callbacks that are registered. Even if they are for the same event. + /// Total number of callbacks that are registered. Even if they are for the same event. /// /// internal int CallbackCount() @@ -3566,7 +3568,7 @@ internal string DumpHash() internal /*protected*/ TraceEventDispatcher() { - // Initialize our data structures. + // Initialize our data structures. unhandledEventTemplate = new UnhandledTraceEvent(); unhandledEventTemplate.traceEventSource = this; ReHash(); // Allocates the hash table @@ -3579,7 +3581,7 @@ internal override void RegisterUnhandledEventImpl(Func callbac } else { - // Put it on the end of the array. + // Put it on the end of the array. var newLastChanceHandlers = new Func[lastChanceHandlers.Length + 1]; Array.Copy(lastChanceHandlers, newLastChanceHandlers, lastChanceHandlers.Length); newLastChanceHandlers[lastChanceHandlers.Length] = callback; @@ -3591,8 +3593,8 @@ internal override void RegisterUnhandledEventImpl(Func callbac /// This is the routine that is called back when any event arrives. Basically it looks up the GUID /// and the opcode associated with the event and finds right subclass of TraceEvent that /// knows how to decode the packet, and calls its virtual TraceEvent.Dispatch method. Note - /// that TraceEvent does NOT have a copy of the data, but rather just a pointer to it. - /// This data is ONLY valid during the callback. + /// that TraceEvent does NOT have a copy of the data, but rather just a pointer to it. + /// This data is ONLY valid during the callback. /// protected internal void Dispatch(TraceEvent anEvent) { @@ -3688,7 +3690,7 @@ internal TraceEvent Lookup(TraceEventNativeMethods.EVENT_RECORD* eventRecord) // calculate the hash, and look it up in the table please note that this was hand // inlined, and is replicated in TraceEventDispatcher.Insert - int* guidPtr = (int*)&eventRecord->EventHeader.ProviderId; // This is the taskGuid for Classic events. + int* guidPtr = (int*)&eventRecord->EventHeader.ProviderId; // This is the taskGuid for Classic events. int hash = (*guidPtr + eventID * 9) & templatesLengthMask; for (; ; ) { @@ -3702,12 +3704,12 @@ internal TraceEvent Lookup(TraceEventNativeMethods.EVENT_RECORD* eventRecord) if (curTemplate != null) { // Since provider and task guids can not overlap, we can only match if - // we are using the correct format. + // we are using the correct format. curTemplate.eventRecord = eventRecord; curTemplate.userData = eventRecord->UserData; curTemplate.eventIndex = currentID; - if ((((int)currentID) & 0xFFFF) == 0) // Every 64K events allow Thread.Interrupt. + if ((((int)currentID) & 0xFFFF) == 0) // Every 64K events allow Thread.Interrupt. { System.Threading.Thread.Sleep(0); } @@ -3761,7 +3763,7 @@ internal TraceEvent Lookup(TraceEventNativeMethods.EVENT_RECORD* eventRecord) unhandledEventTemplate.userData = eventRecord->UserData; unhandledEventTemplate.eventIndex = currentID; unhandledEventTemplate.lookupAsClassic = unhandledEventTemplate.IsClassicProvider; - if ((((int)currentID) & 0xFFFF) == 0) // Every 64K events allow Thread.Interrupt. + if ((((int)currentID) & 0xFFFF) == 0) // Every 64K events allow Thread.Interrupt. { System.Threading.Thread.Sleep(0); } @@ -3778,14 +3780,14 @@ internal TraceEvent Lookup(TraceEventNativeMethods.EVENT_RECORD* eventRecord) unhandledEventTemplate.taskGuid = Guid.Empty; #endif - // Allow the last chance handlers to get a crack at it. + // Allow the last chance handlers to get a crack at it. if (lastChanceHandlers != null && lastChanceHandlerChecked < lastChanceHandlers.Length) { unhandledEventTemplate.PrepForCallback(); do { - // Check the next last chance handler. If it returns true it may have modified the lookup table, so retry the lookup. - // In ether case we move past the current last chance handler to the next one (ensuring termination). + // Check the next last chance handler. If it returns true it may have modified the lookup table, so retry the lookup. + // In ether case we move past the current last chance handler to the next one (ensuring termination). var lastChanceHandlerModifiedLookup = lastChanceHandlers[lastChanceHandlerChecked](unhandledEventTemplate); lastChanceHandlerChecked++; if (lastChanceHandlerModifiedLookup) @@ -3826,7 +3828,7 @@ internal unsafe TraceEvent LookupTemplate(Guid guid, TraceEventID eventID_) } /// - /// Dispose pattern. + /// Dispose pattern. /// protected override void Dispose(bool disposing) { @@ -3836,7 +3838,7 @@ protected override void Dispose(bool disposing) } else { - // To avoid AVs after someone calls Dispose, only clean up this memory on finalization. + // To avoid AVs after someone calls Dispose, only clean up this memory on finalization. if (templatesInfo != null) { Marshal.FreeHGlobal((IntPtr)templatesInfo); @@ -3859,7 +3861,7 @@ private void ReHash() int newLength = 256; if (templates != null) { - // Find the number of real entries. + // Find the number of real entries. int minSize = DistinctCallbackCount() * 2; // round up to a power of 2 because we use a mask to do modulus @@ -3874,7 +3876,7 @@ private void ReHash() TraceEvent[] oldTemplates = templates; templates = new TraceEvent[newLength]; - // Reuse the memory if you can, otherwise free what we have and allocate new. + // Reuse the memory if you can, otherwise free what we have and allocate new. if (oldTemplates != null) { if (newLength != oldTemplates.Length) @@ -3909,7 +3911,7 @@ private void ReHash() } /// - /// Inserts 'template' into the hash table, using 'providerGuid' and and 'eventID' as the key. + /// Inserts 'template' into the hash table, using 'providerGuid' and and 'eventID' as the key. /// For Vista ETW events 'providerGuid' must match the provider GUID and the 'eventID' the ID filed. /// For PreVist ETW events 'providerGuid must match the task GUID the 'eventID' is the Opcode /// @@ -3925,7 +3927,7 @@ private unsafe void Insert(TraceEvent template) ReHash(); } - // We need the size to be a power of two since we use a mask to do the modulus. + // We need the size to be a power of two since we use a mask to do the modulus. Debug.Assert(((templates.Length - 1) & templates.Length) == 0, "array length must be a power of 2"); // Which conventions are we using? @@ -3973,7 +3975,7 @@ private unsafe void Insert(TraceEvent template) { Debug.Assert(curTemplate != template); // In the rehash scenario we expect curTemplate to be a list (not a singleton), however we never go - // in that case templates[hash] == null and we don't go down this branch. + // in that case templates[hash] == null and we don't go down this branch. Debug.Assert(template.next == null); // Normally goto the end of the list (callbacks happen @@ -3989,11 +3991,11 @@ private unsafe void Insert(TraceEvent template) } else { - // However the template is null, this is the 'canonical' template + // However the template is null, this is the 'canonical' template // and should be first (so that adding callbacks does not change the // canonical template) There is no point in having more than one - // so ignore it if there already was one, but otherwise put it in - // the front of the list + // so ignore it if there already was one, but otherwise put it in + // the front of the list if (curTemplate.Target != null) { template.next = curTemplate; @@ -4024,7 +4026,7 @@ private unsafe void Insert(TraceEvent template) /// /// A helper for creating a set of related guids (knowing the providerGuid can can deduce the - /// 'taskNumber' member of this group. All we do is add the taskNumber to GUID as a number. + /// 'taskNumber' member of this group. All we do is add the taskNumber to GUID as a number. /// private static Guid GenTaskGuidFromProviderGuid(Guid providerGuid, ushort taskNumber) { @@ -4039,13 +4041,13 @@ private static Guid GenTaskGuidFromProviderGuid(Guid providerGuid, ushort taskNu private struct TemplateEntry { public Guid eventGuid; - public ushort eventID; // Event ID for Vista events, Opcode for Classic events. - public bool inUse; // This entry is in use. + public ushort eventID; // Event ID for Vista events, Opcode for Classic events. + public bool inUse; // This entry is in use. } - private TemplateEntry* templatesInfo; // unmanaged array, this is the hash able. + private TemplateEntry* templatesInfo; // unmanaged array, this is the hash able. - private TraceEvent[] templates; // Logically a field in TemplateEntry + private TraceEvent[] templates; // Logically a field in TemplateEntry private struct NamesEntry { public NamesEntry(string taskName, string providerName) { this.taskName = taskName; this.providerName = providerName; } @@ -4114,7 +4116,7 @@ internal override void RegisterEventTemplateImpl(TraceEvent template) // If the provider supports both pre-vista events (Guid non-empty), (The CLR does this) // Because the template is chained, we need to clone the template to insert it - // again. + // again. if (template.taskGuid != Guid.Empty) { template = template.Clone(); @@ -4131,7 +4133,7 @@ internal override void UnregisterEventTemplateImpl(Delegate action, Guid provide { // Trace.WriteLine("Unregistering TEMPLATE " + providerGuid + " " + eventID); - // The dispatcher was disposed. + // The dispatcher was disposed. if (templatesInfo == null) { return; @@ -4153,7 +4155,7 @@ internal override void UnregisterEventTemplateImpl(Delegate action, Guid provide { if (curTemplate.Target == action) { - // Remove the first matching entry from the list. + // Remove the first matching entry from the list. if (prevTemplate == null) { templates[hash] = curTemplate.next; @@ -4241,7 +4243,7 @@ private void LookupGuid(Guid guid, out NamesEntry ret) // Generic events for very simple cases (no payload, one value) /// - /// TraceEventParsers can use this template to define the event for the trivial case where the event has no user-defined payload + /// TraceEventParsers can use this template to define the event for the trivial case where the event has no user-defined payload /// This is only useful to TraceEventParsers. /// public sealed class EmptyTraceData : TraceEvent @@ -4256,14 +4258,14 @@ public EmptyTraceData(Action action, int eventID, int task, stri } #region Private /// - /// implementation of TraceEvent Interface. + /// implementation of TraceEvent Interface. /// public override StringBuilder ToXml(StringBuilder sb) { return Prefix(sb).Append("/>"); } /// - /// implementation of TraceEvent Interface. + /// implementation of TraceEvent Interface. /// public override string[] PayloadNames { @@ -4278,7 +4280,7 @@ public override string[] PayloadNames } } /// - /// implementation of TraceEvent Interface. + /// implementation of TraceEvent Interface. /// public override object PayloadValue(int index) { @@ -4288,7 +4290,7 @@ public override object PayloadValue(int index) private event Action Action; /// - /// Dispatches the event to the action associated with the template. + /// Dispatches the event to the action associated with the template. /// protected internal override void Dispatch() { @@ -4313,7 +4315,7 @@ protected internal override Delegate Target public sealed class StringTraceData : TraceEvent { /// - /// The value of the one string payload property. + /// The value of the one string payload property. /// public string Value { @@ -4340,7 +4342,7 @@ public StringTraceData(Action action, int eventID, int task, st } #region Private /// - /// implementation of TraceEvent Interface. + /// implementation of TraceEvent Interface. /// public override StringBuilder ToXml(StringBuilder sb) { @@ -4350,7 +4352,7 @@ public override StringBuilder ToXml(StringBuilder sb) return sb; } /// - /// implementation of TraceEvent Interface. + /// implementation of TraceEvent Interface. /// public override string[] PayloadNames { @@ -4365,7 +4367,7 @@ public override string[] PayloadNames } } /// - /// implementation of TraceEvent Interface. + /// implementation of TraceEvent Interface. /// public override object PayloadValue(int index) { @@ -4381,7 +4383,7 @@ public override object PayloadValue(int index) private event Action Action; /// - /// implementation of TraceEvent Interface. + /// implementation of TraceEvent Interface. /// protected internal override void Dispatch() { @@ -4401,13 +4403,13 @@ protected internal override Delegate Target } /// - /// UnhandledTraceEvent is a TraceEvent when is used when no manifest information is available for the event. + /// UnhandledTraceEvent is a TraceEvent when is used when no manifest information is available for the event. /// public unsafe class UnhandledTraceEvent : TraceEvent { #region private /// - /// implementation of TraceEvent Interface. + /// implementation of TraceEvent Interface. /// public override StringBuilder ToXml(StringBuilder sb) { @@ -4482,7 +4484,7 @@ public override StringBuilder ToXml(StringBuilder sb) return sb; } /// - /// implementation of TraceEvent Interface. + /// implementation of TraceEvent Interface. /// public override string[] PayloadNames { @@ -4497,7 +4499,7 @@ public override string[] PayloadNames } } /// - /// implementation of TraceEvent Interface. + /// implementation of TraceEvent Interface. /// public override object PayloadValue(int index) { @@ -4508,7 +4510,7 @@ public override object PayloadValue(int index) internal event Action Action; internal UnhandledTraceEvent() : base(0, 0, null, Guid.Empty, 0, null, Guid.Empty, null) { } /// - /// implementation of TraceEvent Interface. + /// implementation of TraceEvent Interface. /// protected internal override void Dispatch() { @@ -4527,11 +4529,11 @@ protected internal override Delegate Target set { Action = (Action)value; } } /// - /// implementation of TraceEvent Interface. + /// implementation of TraceEvent Interface. /// public override string ToString() { - // This is only needed so that when we print when debugging we get sane results. + // This is only needed so that when we print when debugging we get sane results. if (eventID == TraceEventID.Illegal) { PrepForCallback(); @@ -4542,13 +4544,13 @@ public override string ToString() /// /// There is some work needed to prepare the generic unhandledTraceEvent that we defer - /// late (since we often don't care about unhandled events) - /// + /// late (since we often don't care about unhandled events) + /// /// TODO this is probably not worth the complexity... /// internal void PrepForCallback() { - // Could not find the event, populate the shared 'unhandled event' information. + // Could not find the event, populate the shared 'unhandled event' information. if (IsClassicProvider) { providerGuid = Guid.Empty; @@ -4643,17 +4645,17 @@ internal static unsafe string ReadUnicodeString(IntPtr pointer, int offset, int { // Really we should be able to count on pointers being null terminated. However we have had instances // where this is not true. To avoid scanning the string twice we first check if the last character - // in the buffer is a 0 if so, we KNOW we can use the 'fast path' Otherwise we check + // in the buffer is a 0 if so, we KNOW we can use the 'fast path' Otherwise we check byte* ptr = (byte*)pointer; char* charEnd = (char*)(ptr + bufferLength); if (charEnd[-1] == 0) // Is the last character a null? { - return new string((char*)(ptr + offset)); // We can count on a null, so we do an optimized path + return new string((char*)(ptr + offset)); // We can count on a null, so we do an optimized path } - // (works for last string, and other times by luck). - // but who cares as long as it stays in the buffer. + // (works for last string, and other times by luck). + // but who cares as long as it stays in the buffer. - // unoptimized path. Carefully count characters and create a string up to the null. + // unoptimized path. Carefully count characters and create a string up to the null. char* charStart = (char*)(ptr + offset); int maxPos = (bufferLength - offset) / sizeof(char); int curPos = 0; @@ -4661,7 +4663,7 @@ internal static unsafe string ReadUnicodeString(IntPtr pointer, int offset, int { curPos++; } - // CurPos now points at the end (either buffer end or null terminator, make just the right sized string. + // CurPos now points at the end (either buffer end or null terminator, make just the right sized string. return new string(charStart, 0, curPos); } internal static unsafe string ReadUTF8String(IntPtr pointer, int offset, int bufferLength) @@ -4679,7 +4681,7 @@ internal static unsafe string ReadUTF8String(IntPtr pointer, int offset, int buf buff[i++] = c; } - return Encoding.UTF8.GetString(buff, 0, i); // Convert to unicode. + return Encoding.UTF8.GetString(buff, 0, i); // Convert to unicode. } internal static unsafe string ReadShortUTF8String(IntPtr pointer, int offset, int bufferLength) @@ -4716,12 +4718,12 @@ public static class ObservableExtensions { /// /// Returns an IObservable that observes all events that 'parser' knows about that return a T. If eventName is - /// non-null, the event's name must match 'eventName', but if eventName is null, any event that returns a T is observed. + /// non-null, the event's name must match 'eventName', but if eventName is null, any event that returns a T is observed. /// - /// This means that Observe{TraceEvent}(parser) will observe all events that the parser can parse. - /// - /// Note that unlike the methods on TraceEventParser, the TraceEvent object returned is already Cloned() and thus can be - /// referenced for as long as you like. + /// This means that Observe{TraceEvent}(parser) will observe all events that the parser can parse. + /// + /// Note that unlike the methods on TraceEventParser, the TraceEvent object returned is already Cloned() and thus can be + /// referenced for as long as you like. /// /// public static IObservable Observe(this TraceEventParser parser, string eventName) where T : TraceEvent @@ -4730,10 +4732,10 @@ public static IObservable Observe(this TraceEventParser parser, string eve } /// /// Returns an IObservable that observes all events that 'parser' knows about that return a T and whose event - /// name matches the 'eventNameFilter' predicate. - /// - /// Note that unlike the methods on TraceEventParser, the TraceEvent object returned is already Cloned() and thus can be - /// referenced for as long as you like. + /// name matches the 'eventNameFilter' predicate. + /// + /// Note that unlike the methods on TraceEventParser, the TraceEvent object returned is already Cloned() and thus can be + /// referenced for as long as you like. /// public static IObservable Observe(this TraceEventParser parser, Predicate eventNameFilter = null) where T : TraceEvent { @@ -4745,9 +4747,9 @@ public static IObservable Observe(this TraceEventParser parser, Predicate< } /// /// Observe a particular event from a particular provider. If eventName is null, it will return every event from the provider - /// - /// Note that unlike the methods on TraceEventParser, the TraceEvent object returned is already Cloned() and thus can be - /// referenced for as long as you like. + /// + /// Note that unlike the methods on TraceEventParser, the TraceEvent object returned is already Cloned() and thus can be + /// referenced for as long as you like. /// public static IObservable Observe(this TraceEventParser parser, string providerName, string eventName) { @@ -4772,12 +4774,12 @@ public static IObservable Observe(this TraceEventParser parser, stri }); } /// - /// Given a predicate 'eventToObserve' which takes the name of a provider (which may be of the form Provider(GUID)) (first) and + /// Given a predicate 'eventToObserve' which takes the name of a provider (which may be of the form Provider(GUID)) (first) and /// an event name (which may be of the form EventID(NUM)) and indicates which events to observe, return an IObservable - /// that observes those events. - /// - /// Note that unlike the methods on TraceEventParser, the TraceEvent object returned is already Cloned() and thus can be - /// referenced for as long as you like. . + /// that observes those events. + /// + /// Note that unlike the methods on TraceEventParser, the TraceEvent object returned is already Cloned() and thus can be + /// referenced for as long as you like. . /// public static IObservable Observe(this TraceEventParser parser, Func eventsToObserve) { @@ -4788,9 +4790,9 @@ public static IObservable Observe(this TraceEventParser parser, Func } /// /// Returns an observable that observes all events from the event source 'source' - /// - /// Note that unlike the methods on TraceEventParser, the TraceEvent object returned is already Cloned() and thus can be - /// referenced for as long as you like. + /// + /// Note that unlike the methods on TraceEventParser, the TraceEvent object returned is already Cloned() and thus can be + /// referenced for as long as you like. /// public static IObservable ObserveAll(this TraceEventDispatcher source) { @@ -4800,9 +4802,9 @@ public static IObservable ObserveAll(this TraceEventDispatcher sourc } /// /// Returns an observable that observes all events from the event source 'source' which are not handled by a callback connected to 'source' - /// - /// Note that unlike the methods on TraceEventParser, the TraceEvent object returned is already Cloned() and thus can be - /// referenced for as long as you like. + /// + /// Note that unlike the methods on TraceEventParser, the TraceEvent object returned is already Cloned() and thus can be + /// referenced for as long as you like. /// public static IObservable ObserveUnhandled(this TraceEventDispatcher source) { @@ -4813,8 +4815,8 @@ public static IObservable ObserveUnhandled(this TraceEventDispatcher #region private /// - /// A TraceEventObservable is a helper class that implements the IObservable pattern for TraceEventDispatcher - /// (like ETWTraceEventDispatcher). It is called from the TraceEventParser.Observe*{T} methods. + /// A TraceEventObservable is a helper class that implements the IObservable pattern for TraceEventDispatcher + /// (like ETWTraceEventDispatcher). It is called from the TraceEventParser.Observe*{T} methods. /// /// internal class TraceEventObservable : IObservable where T : TraceEvent @@ -4833,8 +4835,8 @@ public IDisposable Subscribe(IObserver observer) #region private /// - /// A TraceEventSubscription is helper class that hooks 'callback' and 'completedCallback' to the 'observable' and - /// unhooks them when 'Dispose' is called. + /// A TraceEventSubscription is helper class that hooks 'callback' and 'completedCallback' to the 'observable' and + /// unhooks them when 'Dispose' is called. /// private class TraceEventSubscription : IDisposable { @@ -4852,7 +4854,7 @@ internal TraceEventSubscription(Action callback, Action completedCallback, Tr } public void Dispose() { - // Clean things up. + // Clean things up. m_observable.m_source.Completed -= m_completedCallback; m_observable.m_removeHander(m_callback, this); } diff --git a/src/TraceEvent/TraceEvent.csproj b/src/TraceEvent/TraceEvent.csproj index a6726ea86..429966a7d 100644 --- a/src/TraceEvent/TraceEvent.csproj +++ b/src/TraceEvent/TraceEvent.csproj @@ -2,7 +2,7 @@ - netstandard2.0 + net9.0;netstandard2.0 @@ -25,6 +25,11 @@ Microsoft.Diagnostics.Tracing.TraceEvent.nuspec $(GenerateNuspecDependsOn);SetNuspecProperties + true + true + 9.0 + + $(NoWarn);CA2014 @@ -55,6 +60,10 @@ + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/src/TraceEvent/TraceEventSession.cs b/src/TraceEvent/TraceEventSession.cs index 8752bfa17..969adc3e8 100644 --- a/src/TraceEvent/TraceEventSession.cs +++ b/src/TraceEvent/TraceEventSession.cs @@ -13,6 +13,7 @@ using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Text; using System.Threading; @@ -201,6 +202,7 @@ public static TraceEventSession GetActiveSession(string sessionName) /// is a special value which is a provider defined default, which is usually 'everything' /// Additional options for the provider (e.g. taking a stack trace), arguments ... /// true if the session already existed and needed to be restarted. + [SupportedOSPlatform("windows")] public bool EnableProvider(string providerName, TraceEventLevel providerLevel = TraceEventLevel.Verbose, ulong matchAnyKeywords = ulong.MaxValue, TraceEventProviderOptions options = null) { var providerGuid = TraceEventProviders.GetProviderGuidByName(providerName); @@ -222,6 +224,7 @@ public bool EnableProvider(string providerName, TraceEventLevel providerLevel = /// is a special value which is a provider defined default, which is usually 'everything' /// Additional options for the provider (e.g. taking a stack trace), arguments ... /// true if the session already existed and needed to be restarted. + [SupportedOSPlatform("windows")] public bool EnableProvider(Guid providerGuid, TraceEventLevel providerLevel = TraceEventLevel.Verbose, ulong matchAnyKeywords = ulong.MaxValue, TraceEventProviderOptions options = null) { lock (this) @@ -519,6 +522,7 @@ public bool EnableProvider(Guid providerGuid, TraceEventLevel providerLevel = Tr /// command will be sent (which causes EventSources to re-dump their manifest to the ETW log. /// true if the session already existed and needed to be restarted. [Obsolete("Use EnableProvider(string, TraceEventLevel, ulong, TraceEventProviderOptions) overload instead")] + [SupportedOSPlatform("windows")] public bool EnableProvider(string providerName, TraceEventLevel providerLevel, ulong matchAnyKeywords, TraceEventOptions options, IEnumerable> values = null) { var providerGuid = TraceEventProviders.GetProviderGuidByName(providerName); @@ -545,6 +549,7 @@ public bool EnableProvider(string providerName, TraceEventLevel providerLevel, u /// command will be sent (which causes EventSources to re-dump their manifest to the ETW log. /// true if the session already existed and needed to be restarted. [Obsolete("Use EnableProvider(Guid, TraceEventLevel, ulong, TraceEventProviderOptions) overload instead")] + [SupportedOSPlatform("windows")] public bool EnableProvider(Guid providerGuid, TraceEventLevel providerLevel, ulong matchAnyKeywords, TraceEventOptions options, IEnumerable> values = null) { var args = new TraceEventProviderOptions() { Arguments = values }; @@ -563,6 +568,7 @@ public bool EnableProvider(Guid providerGuid, TraceEventLevel providerLevel, ulo /// providers. Ideally new providers follow the key-value convention and EnableProvider can be used. /// [Obsolete("Use TraceEventProviderOptions.RawArguments overload instead")] + [SupportedOSPlatform("windows")] public void EnableProviderWithRawProviderData(Guid providerGuid, TraceEventLevel providerLevel, ulong matchAnyKeywords, TraceEventOptions options, byte[] providerData, int providerDataSize) { var exactArray = providerData; @@ -604,6 +610,7 @@ public static Dictionary MakeDictionary(params string[] keyValue /// /// Specifies which events should have their stack traces captured when an event is logged /// Returns true if the session existed before and was restarted (see TraceEventSession) + [SupportedOSPlatform("windows")] public unsafe bool EnableKernelProvider(KernelTraceEventParser.Keywords flags, KernelTraceEventParser.Keywords stackCapture = KernelTraceEventParser.Keywords.None) { // Setting stack capture implies that it is on. @@ -822,6 +829,7 @@ public void EnableWindowsHeapProvider(int pid) /// This API is OK to call from one thread while Process() is being run on another /// /// + [SupportedOSPlatform("windows")] public void EnableWindowsHeapProvider(string exeFileName) { if (IsValidSession) @@ -889,6 +897,7 @@ public void DisableProvider(string providerName) /// implicitly stopped when the TraceEventSession is closed unless the StopOnDispose property is set to false. /// This API is OK to call from one thread while Process() is being run on another /// + [SupportedOSPlatform("windows")] public bool Stop(bool noThrow = false) { lock (this) @@ -937,6 +946,7 @@ public bool Stop(bool noThrow = false) /// This API is OK to call from one thread while Process() is being run on another. Calling Dispose is on /// a real time session is the way you can force a real time session to stop in a timely manner. /// + [SupportedOSPlatform("windows")] public void Dispose() { lock (this) // It is pretty common to want do do this on different threads. @@ -1441,6 +1451,7 @@ public string FileName /// If this is a real time session you can fetch the source associated with the session to start receiving events. /// Currently does not work on file based sources (we expect you to wait until the file is complete). /// + [SupportedOSPlatform("windows")] public ETWTraceEventSource Source { get @@ -1522,6 +1533,7 @@ public int EventsLost /// to the TraceEventSession constructor to open it. /// /// A enumeration of strings, each of which is a name of a session + [SupportedOSPlatform("windows")] public static unsafe List GetActiveSessionNames() { int MAX_SESSIONS = GetETWMaxLoggers(); @@ -1535,13 +1547,13 @@ public static unsafe List GetActiveSessionNames() int hr; byte[] sessionsArr = null; int previousSessionCount = 0; - + // Query in a loop until we succeed or get a non-recoverable error do { // Allocate buffer for the number of sessions we expect sessionsArr = new byte[numSessions * sizeOfProperties]; - + fixed (byte* sessionsArray = sessionsArr) { TraceEventNativeMethods.EVENT_TRACE_PROPERTIES** propertiesArray = stackalloc TraceEventNativeMethods.EVENT_TRACE_PROPERTIES*[numSessions]; @@ -1555,10 +1567,10 @@ public static unsafe List GetActiveSessionNames() properties->LogFileNameOffset = (uint)sizeof(TraceEventNativeMethods.EVENT_TRACE_PROPERTIES) + sizeof(char) * TraceEventSession.MaxNameSize; propertiesArray[i] = properties; } - + // Try to get all active sessions hr = TraceEventNativeMethods.QueryAllTraces((IntPtr)propertiesArray, numSessions, ref sessionCount); - + // If we succeeded, extract the session names if (hr == 0) { @@ -1578,7 +1590,7 @@ public static unsafe List GetActiveSessionNames() { Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRFromWin32(hr)); } - + previousSessionCount = sessionCount; numSessions = sessionCount; // sessionCount is updated by QueryAllTraces with the actual count } @@ -1590,7 +1602,7 @@ public static unsafe List GetActiveSessionNames() } } while (hr == TraceEventNativeMethods.ERROR_MORE_DATA); - + return activeTraceNames; } @@ -1603,6 +1615,7 @@ public static unsafe List GetActiveSessionNames() /// Get the maximum number of ETW loggers supported by the current machine /// /// The maximum number of supported ETW loggers + [SupportedOSPlatform("windows")] private static int GetETWMaxLoggers() { const string MaxEtwRegistryKey = "SYSTEM\\CurrentControlSet\\Control\\WMI"; @@ -1740,6 +1753,7 @@ public static void SetDebugPrivilege() /// The 'properties' field is only the header information. There is 'tail' that is /// required. 'ToUnmangedBuffer' fills in this tail properly. /// + [SupportedOSPlatform("windows")] ~TraceEventSession() { Dispose(); @@ -1899,6 +1913,7 @@ private static int FindFreeSessionKeyword(Guid providerGuid) /// /// Cleans out all provider data associated with this session. /// + [SupportedOSPlatform("windows")] private void CleanFilterDataForEtwSession() { // Optimization, kernel sessions don't need filter cleanup. @@ -1978,7 +1993,7 @@ private void CleanFilterDataForEtwSession() private string GetEventSourceRegistryBaseLocation() { - if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8) + if (IntPtr.Size == 8) { return @"Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Winevt\Publishers"; } @@ -2006,6 +2021,7 @@ private string GetEventSourceRegistryBaseLocation() /// This is present only used for compatibility /// /// the session index that will be used for this session. Returns -1 if an entry could not be found + [SupportedOSPlatform("windows")] private void SetFilterDataForEtwSession(string providerGuid, byte[] data, bool V4_5EventSource = false) { string baseKeyName = GetEventSourceRegistryBaseLocation(); @@ -2407,6 +2423,7 @@ private static unsafe int SetStackTraceIds(KernelTraceEventParser.Keywords stack Debug.Assert(curID <= stackTracingIdsMax); return curID; } + [SupportedOSPlatform("windows")] private void EnsureStarted(TraceEventNativeMethods.EVENT_TRACE_PROPERTIES* properties = null) { if (!m_Create) diff --git a/src/TraceEvent/TraceLog.cs b/src/TraceEvent/TraceLog.cs index 8d5a97a28..e703f384e 100644 --- a/src/TraceEvent/TraceLog.cs +++ b/src/TraceEvent/TraceLog.cs @@ -30,6 +30,7 @@ using System.Linq; using System.Reflection; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Threading; @@ -155,6 +156,7 @@ public static TraceLog OpenOrConvert(string etlOrEtlxFilePath, TraceLogOptions o /// and only windows 8 has a session that allows both kernel and user mode events simultaneously. Thus this is most useful /// on Win 8 systems. /// + [SupportedOSPlatform("windows")] public static TraceLogEventSource CreateFromTraceEventSession(TraceEventSession session) { return CreateFromTraceEventSession(session, 1000); @@ -171,6 +173,7 @@ public static TraceLogEventSource CreateFromTraceEventSession(TraceEventSession /// on Win 8 systems. /// /// The delay in milliseconds between when an event is received in TraceLog and when it is dispatched to the real time event source. + [SupportedOSPlatform("windows")] public static TraceLogEventSource CreateFromTraceEventSession(TraceEventSession session, int minDispatchDelayMSec) { if (minDispatchDelayMSec < 10) @@ -7116,13 +7119,13 @@ internal TraceModuleFile UniversalMapping(ProcessMappingTraceData data, ProcessM // Get or create the loaded module. TraceLoadedModule loadedModule = FindModuleAndIndexContainingAddress(data.StartAddress, data.TimeStampQPC, out index); if (loadedModule == null) - { + { // The module file is what is used when looking up the module for an arbitrary address, so it must save both the start address and image size. loadedModule = new TraceLoadedModule(process, moduleFile, data.StartAddress); - + // All mappings are enumerated at the beginning of the trace. loadedModule.loadTimeQPC = process.Log.sessionStartTimeQPC; - + InsertAndSetOverlap(index + 1, loadedModule); } @@ -8528,7 +8531,7 @@ internal void AddUniversalDynamicSymbol(ProcessSymbolTraceData data, TraceProces module = process.LoadedModules.GetOrCreateManagedModule(loadedModule.ModuleID, data.TimeStampQPC); moduleFileIndex = module.ModuleFile.ModuleFileIndex; methodIndex = methods.NewMethod(data.Name, moduleFileIndex, (int)data.Id); - + // When universal traces support re-use of address space, we'll need to support it here. } diff --git a/src/TraceEvent/Utilities/WindowsDeviceToVolumeMap.cs b/src/TraceEvent/Utilities/WindowsDeviceToVolumeMap.cs index c6883d198..9d751f575 100644 --- a/src/TraceEvent/Utilities/WindowsDeviceToVolumeMap.cs +++ b/src/TraceEvent/Utilities/WindowsDeviceToVolumeMap.cs @@ -157,7 +157,7 @@ private static string CharArrayToString(char[] charArray) } length++; } - + return length > 0 ? new string(charArray, 0, length) : string.Empty; } @@ -169,6 +169,9 @@ private static string CharArrayToString(char[] charArray) /// private static void DisableLegacyPathHandling() { +#if NET + AppContext.SetSwitch("Switch.System.IO.UseLegacyPathHandling", false); +#else AssemblyName assemblyName = new AssemblyName("mscorlib"); Assembly systemAssembly = Assembly.Load(assemblyName); if (systemAssembly != null) @@ -195,6 +198,7 @@ private static void DisableLegacyPathHandling() } } } +#endif } } diff --git a/src/TraceEvent/WPPTraceEventParser.cs b/src/TraceEvent/WPPTraceEventParser.cs index 2edeb91f7..349ceec05 100644 --- a/src/TraceEvent/WPPTraceEventParser.cs +++ b/src/TraceEvent/WPPTraceEventParser.cs @@ -2,6 +2,7 @@ using Microsoft.Diagnostics.Utilities; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; using System.Text.RegularExpressions; @@ -10,10 +11,10 @@ namespace Microsoft.Diagnostics.Tracing.Parsers { /// /// This parser knows how to decode Windows Software Trace Preprocessor (WPP) events. In order to decode - /// the events it needs access to the TMF files that describe the events (these are created from the PDB at - /// build time). + /// the events it needs access to the TMF files that describe the events (these are created from the PDB at + /// build time). ///
- /// You will generally use this for the 'FormattedMessage' property of the event. + /// You will generally use this for the 'FormattedMessage' property of the event. ///
public sealed class WppTraceEventParser : ExternalTraceEventParser { @@ -21,8 +22,8 @@ public sealed class WppTraceEventParser : ExternalTraceEventParser /// Construct a new WPPTraceEventParser that is attached to 'source'. Once you do this the source /// will understand WPP events. In particular you can subscribe to the Wpp.All event to get the /// stream of WPP events in the source. For WppTraceEventParser to function, it needs the TMF - /// files for the events it will decode. You should pass the directory to find these TMF files - /// in 'TMFDirectory'. Each file should have the form of a GUID.tmf. + /// files for the events it will decode. You should pass the directory to find these TMF files + /// in 'TMFDirectory'. Each file should have the form of a GUID.tmf. /// /// /// @@ -36,7 +37,7 @@ public WppTraceEventParser(TraceEventSource source, string TMFDirectory) internal override unsafe DynamicTraceEventData TryLookup(TraceEvent unknownEvent) { - // WPP is always classic + // WPP is always classic if (unknownEvent.IsClassicProvider) { var taskGuid = unknownEvent.taskGuid; @@ -45,7 +46,7 @@ internal override unsafe DynamicTraceEventData TryLookup(TraceEvent unknownEvent { var templates = CreateTemplatesForTMFFile(taskGuid, tmfPath); - // Register all the templates in the file, and if we found the specific one we are looking for return that one. + // Register all the templates in the file, and if we found the specific one we are looking for return that one. DynamicTraceEventData ret = null; foreach (var template in templates) { @@ -58,7 +59,7 @@ internal override unsafe DynamicTraceEventData TryLookup(TraceEvent unknownEvent OnNewEventDefintion(template, false); } } - // If we fail, remove the file so we don't ever try to this Task's events again. + // If we fail, remove the file so we don't ever try to this Task's events again. m_tmfDataFilePathsByFileNameBase[taskGuid.ToString()] = null; return ret; } @@ -85,7 +86,9 @@ private string GetTmfPathForTaskGuid(Guid taskGuid) private struct TypeAndFormat { - public TypeAndFormat(Type Type, IDictionary Map) { this.Type = Type; this.Map = Map; } + public TypeAndFormat([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type Type, IDictionary Map) { this.Type = Type; this.Map = Map; } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public Type Type; public IDictionary Map; } @@ -169,7 +172,7 @@ private List CreateTemplatesForTMFFile(Guid taskGuid, str var eventId = int.Parse(m.Groups[3].Value); var formatStr = m.Groups[4].Value; - // Substitute in %!NAME! for their values as defined in the tail + // Substitute in %!NAME! for their values as defined in the tail if (formatStr.Contains("%!")) { var tail = m.Groups[5].Value; @@ -187,7 +190,7 @@ private List CreateTemplatesForTMFFile(Guid taskGuid, str if (m1.Success) { varValue = m1.Groups[1].Value; - varValue = Regex.Replace(varValue, @" \w+=.*", ""); // Remove things that look like the next key-value + varValue = Regex.Replace(varValue, @" \w+=.*", ""); // Remove things that look like the next key-value } formatStr = formatStr.Replace("%!" + varName + "!", varValue); } @@ -200,7 +203,7 @@ private List CreateTemplatesForTMFFile(Guid taskGuid, str } var template = new DynamicTraceEventData(null, eventId, 0, fileName + "/" + m.Groups[2].Value, taskGuid, 0, "", providerGuid, eventProviderName); - template.lookupAsWPP = true; // Use WPP lookup conventions. + template.lookupAsWPP = true; // Use WPP lookup conventions. parameterTypes.Clear(); @@ -225,7 +228,7 @@ private List CreateTemplatesForTMFFile(Guid taskGuid, str IDictionary map = null; if (typeStr == "String") { - type = typeof(StringBuilder); // We use StringBuild to represent a ANSI string + type = typeof(StringBuilder); // We use StringBuild to represent a ANSI string } else if (typeStr == "WString") { @@ -239,7 +242,7 @@ private List CreateTemplatesForTMFFile(Guid taskGuid, str { type = typeof(int); // By making map non-null we indicate that this is a enum, but we don't add any enum - // mappings, which makes it print as Hex. Thus we are just saying 'print as hex' + // mappings, which makes it print as Hex. Thus we are just saying 'print as hex' map = new SortedDictionary(); } else if (typeStr == "Double") @@ -250,7 +253,7 @@ private List CreateTemplatesForTMFFile(Guid taskGuid, str { type = typeof(IntPtr); } - else if (typeStr.StartsWith("Enum(")) // TODO more support for enums + else if (typeStr.StartsWith("Enum(")) // TODO more support for enums { type = typeof(int); } @@ -306,12 +309,12 @@ private List CreateTemplatesForTMFFile(Guid taskGuid, str } } - formatStr = formatStr.Replace("%0", ""); // TODO What is this? Why is it here? + formatStr = formatStr.Replace("%0", ""); // TODO What is this? Why is it here? formatStr = Regex.Replace(formatStr, @"%(\d+)!(\w?)\w*!", delegate (Match match) { var argNum = int.Parse(match.Groups[1].Value) - 10; // 0 first arg ... - // If it has a !x qualifer after it change th map so it will be decoded as hex. + // If it has a !x qualifer after it change th map so it will be decoded as hex. if (match.Groups[2].Value == "x" && 0 <= argNum && argNum < template.payloadFetches.Length && template.payloadFetches[argNum].Map == null) { From c37d1e01db06bbc7ce744b37c5c38002580bbdad Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Fri, 4 Jul 2025 22:12:18 -0700 Subject: [PATCH 2/6] Make TraceEvent and FastSerialization AOT-compatible --- src/TraceEvent/DynamicTraceEventParser.cs | 57 +++++++++++++++++++ ...soft.Diagnostics.Tracing.TraceEvent.nuspec | 9 +-- ...oft.Diagnostics.Tracing.TraceEvent.targets | 11 ++++ src/TraceEvent/TraceEvent.cs | 3 + src/TraceEvent/TraceEvent.csproj | 1 + 5 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 src/TraceEvent/Microsoft.Diagnostics.Tracing.TraceEvent.targets diff --git a/src/TraceEvent/DynamicTraceEventParser.cs b/src/TraceEvent/DynamicTraceEventParser.cs index 60d001f68..1c62bc130 100644 --- a/src/TraceEvent/DynamicTraceEventParser.cs +++ b/src/TraceEvent/DynamicTraceEventParser.cs @@ -1712,6 +1712,27 @@ public void ToStream(Serializer serializer) serializer.Write((byte)0); } } + +#if NET9_0_OR_GREATER + /// + /// Feature switch that turns off deserialization of any types that are not primitive types. + /// Specifically, the property must either be a primitive type, + /// or null. + /// + internal static class PrimitiveOnlyDeserialization + { + private const string FeatureSwitchName = "PrimitiveOnlyDeserialization.IsDisabled"; + + + [FeatureSwitchDefinition(FeatureSwitchName)] + [FeatureGuard(typeof(RequiresUnreferencedCodeAttribute))] +#pragma warning disable IL4000 // The feature switch should be make the return value match RequiresUnreferencedCode + // We reverse the logic here because FeatureGuards must always return false when the target type is not available. + public static bool IsDisabled => AppContext.TryGetSwitch(FeatureSwitchName, out bool disabled) ? disabled : true; +#pragma warning restore IL4000 + } +#endif + public void FromStream(Deserializer deserializer) { Offset = (ushort)deserializer.ReadInt16(); @@ -1719,7 +1740,38 @@ public void FromStream(Deserializer deserializer) var typeName = deserializer.ReadString(); if (typeName != null) { +#if NET9_0_OR_GREATER + if (PrimitiveOnlyDeserialization.IsDisabled) + { + Type = Type.GetType(typeName); + } + else + { + Type = typeName switch + { + "System.Char" => typeof(char), + "System.String" => typeof(string), + "System.Int32" => typeof(int), + "System.Int64" => typeof(long), + "System.Boolean" => typeof(bool), + "System.Double" => typeof(double), + "System.Single" => typeof(float), + "System.Byte" => typeof(byte), + "System.SByte" => typeof(sbyte), + "System.UInt16" => typeof(ushort), + "System.Int16" => typeof(short), + "System.UInt32" => typeof(uint), + "System.UInt64" => typeof(ulong), + "System.Guid" => typeof(Guid), + "System.DateTime" => typeof(DateTime), + "System.IntPtr" => typeof(IntPtr), + "Microsoft.Diagnostics.Tracing.Parsers.DynamicTraceEventData.StructValue" => typeof(StructValue), + _ => null, + }; + } +#else Type = Type.GetType(typeName); +#endif } var fetchType = deserializer.ReadByte(); @@ -2009,7 +2061,12 @@ public ProviderManifest(Stream manifestStream, int manifestLen = int.MaxValue) id = "Stream"; int len = Math.Min((int)(manifestStream.Length - manifestStream.Position), manifestLen); serializedManifest = new byte[len]; +#if NET manifestStream.ReadExactly(serializedManifest, 0, len); +#else + manifestStream.Read(serializedManifest, 0, len); +#endif + } /// /// Read a ProviderManifest from a file. diff --git a/src/TraceEvent/Microsoft.Diagnostics.Tracing.TraceEvent.nuspec b/src/TraceEvent/Microsoft.Diagnostics.Tracing.TraceEvent.nuspec index 185283f94..29f86aacb 100644 --- a/src/TraceEvent/Microsoft.Diagnostics.Tracing.TraceEvent.nuspec +++ b/src/TraceEvent/Microsoft.Diagnostics.Tracing.TraceEvent.nuspec @@ -10,17 +10,17 @@ MIT true - Event Tracing for Windows (ETW) is a powerful logging mechanism built into the Windows OS and is used extensively in Windows. + Event Tracing for Windows (ETW) is a powerful logging mechanism built into the Windows OS and is used extensively in Windows. You can also log ETW events yourself code using the System.Diagnostics.Tracing.EventSource class. - The TraceEvent library conains the classes needed to control ETW providers (including .NET EventSources) + The TraceEvent library conains the classes needed to control ETW providers (including .NET EventSources) and parse the events they emit. The library includes -- TraceEventSession which can enable ETW providers, -- EtwTraceEventSource which lets you read the stream of ETW events, and -- TraceLog which is is digested form of ETW events which include decoded stack traces associated with the events. - + See https://github.com/Microsoft/perfview/blob/master/documentation/TraceEvent/TraceEventLibrary.md for more. TraceEvent is a .NET Framework library for capturing and analyzing ETW events. @@ -45,6 +45,7 @@ + @@ -58,7 +59,7 @@ - + diff --git a/src/TraceEvent/Microsoft.Diagnostics.Tracing.TraceEvent.targets b/src/TraceEvent/Microsoft.Diagnostics.Tracing.TraceEvent.targets new file mode 100644 index 000000000..5b77847e6 --- /dev/null +++ b/src/TraceEvent/Microsoft.Diagnostics.Tracing.TraceEvent.targets @@ -0,0 +1,11 @@ + + + + + + + diff --git a/src/TraceEvent/TraceEvent.cs b/src/TraceEvent/TraceEvent.cs index 09d83feaa..0f8fd3dfc 100644 --- a/src/TraceEvent/TraceEvent.cs +++ b/src/TraceEvent/TraceEvent.cs @@ -1566,6 +1566,9 @@ public string Dump(bool includePrettyPrint = false, bool truncateDump = false) /// /// Create a template with the given event meta-data. Used by TraceParserGen. /// + [UnconditionalSuppressMessage("Trimming", "IL3050", + Justification = "Warning is due to inheriting from DynamicObject, but there is only a problem if the caller uses dynamic, in which case they will receive a separate warning." + )] protected TraceEvent(int eventID, int task, string taskName, Guid taskGuid, int opcode, string opcodeName, Guid providerGuid, string providerName) { diff --git a/src/TraceEvent/TraceEvent.csproj b/src/TraceEvent/TraceEvent.csproj index 429966a7d..0c7d75307 100644 --- a/src/TraceEvent/TraceEvent.csproj +++ b/src/TraceEvent/TraceEvent.csproj @@ -160,6 +160,7 @@ + From e9866139f0bfd0a73806d4e48b87e3dfe919e9ee Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Sun, 6 Jul 2025 09:15:07 -0700 Subject: [PATCH 3/6] Update DynamicTraceEventParser.cs Add back comment --- src/TraceEvent/DynamicTraceEventParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TraceEvent/DynamicTraceEventParser.cs b/src/TraceEvent/DynamicTraceEventParser.cs index 1c62bc130..caa236f15 100644 --- a/src/TraceEvent/DynamicTraceEventParser.cs +++ b/src/TraceEvent/DynamicTraceEventParser.cs @@ -1567,7 +1567,7 @@ public PayloadFetchArrayInfo Array } [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - public Type Type; + public Type Type; // currently null for arrays // Non null of 'Type' is a enum public IDictionary Map From e9c3b1145e102832d10d689ff618bc489d9fdbde Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Wed, 23 Jul 2025 15:55:41 -0700 Subject: [PATCH 4/6] Rewrite feature switch as enum --- src/TraceEvent/DynamicTraceEventParser.cs | 400 ++++++++++++------ src/TraceEvent/EventPipe/EventPipeMetadata.cs | 61 +-- ...soft.Diagnostics.Tracing.TraceEvent.nuspec | 1 - ...oft.Diagnostics.Tracing.TraceEvent.targets | 11 - src/TraceEvent/RegisteredTraceEventParser.cs | 147 +++---- src/TraceEvent/TraceEvent.csproj | 3 +- src/TraceEvent/WPPTraceEventParser.cs | 24 +- 7 files changed, 391 insertions(+), 256 deletions(-) delete mode 100644 src/TraceEvent/Microsoft.Diagnostics.Tracing.TraceEvent.targets diff --git a/src/TraceEvent/DynamicTraceEventParser.cs b/src/TraceEvent/DynamicTraceEventParser.cs index caa236f15..28717f1db 100644 --- a/src/TraceEvent/DynamicTraceEventParser.cs +++ b/src/TraceEvent/DynamicTraceEventParser.cs @@ -12,6 +12,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Xml; +using static Microsoft.Diagnostics.Tracing.Parsers.DynamicTraceEventData.PayloadFetch; using Address = System.UInt64; namespace Microsoft.Diagnostics.Tracing.Parsers @@ -482,7 +483,7 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int { throw new ArgumentOutOfRangeException("Payload size exceeds buffer size."); } - Type type = payloadFetch.Type; + FetchType? typeOpt = payloadFetch.Type; // Is this a struct field? PayloadFetchClassInfo classInfo = payloadFetch.Class; @@ -505,9 +506,9 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int var elementType = arrayInfo.Element.Type; // Arrays of characters can be deserialized as strings if desired. - if(type == typeof(string)) + if(typeOpt == FetchType.System_String) { - Debug.Assert(arrayInfo.Element.Type == typeof(char)); + Debug.Assert(arrayInfo.Element.Type == FetchType.System_Char); Debug.Assert(arrayInfo.Element.Size == 1 || arrayInfo.Element.Size == 2); if (arrayInfo.Element.Size == 1) { @@ -520,7 +521,7 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int } // Byte array short-circuit. - if (elementType == typeof(byte)) + if (elementType == FetchType.System_Byte) { return GetByteArrayAt(offset, arrayCount); } @@ -530,9 +531,9 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int for (int i = 0; i < arrayCount; i++) { object value = GetPayloadValueAt(ref arrayInfo.Element, offset, payloadLength); - if (value.GetType() != elementType) + if (arrayInfo.Element.Type != elementType) { - value = ((IConvertible)value).ToType(elementType, null); + value = FetchTypeHelpers.Convert(elementType, value); // ((IConvertible)value).ToType(elementType, null); } ret.SetValue(value, i); @@ -541,7 +542,7 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int return ret; } - if (type == null) + if (typeOpt is not {} type) { return "[CANT PARSE]"; } @@ -564,11 +565,11 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int if ((uint)EventDataLength <= (uint)offset) { - return GetDefaultValueByType(type); + return FetchTypeHelpers.GetDefaultValueByType(type); } ushort size = payloadFetch.Size; - switch (Type.GetTypeCode(type)) + switch (FetchTypeHelpers.GetTypeCode(type)) { case TypeCode.String: { @@ -674,7 +675,7 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int case TypeCode.Double: return GetDoubleAt(offset); default: - if (type == typeof(IntPtr)) + if (typeOpt == FetchType.System_IntPtr) { if (PointerSize == 4) { @@ -685,11 +686,11 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int return (Address)GetInt64At(offset); } } - else if (type == typeof(Guid)) + else if (typeOpt == FetchType.System_Guid) { return GetGuidAt(offset); } - else if (type == typeof(DateTime)) + else if (typeOpt == FetchType.System_DateTime) { if (payloadFetch.Size == 16) { @@ -1212,7 +1213,7 @@ internal int OffsetOfNextField(ref PayloadFetch payloadFetch, int offset, int pa { return offset + PointerSize; } - else if (IsCountedSize(size) && payloadFetch.Type == typeof(string)) + else if (IsCountedSize(size) && payloadFetch.Type == FetchType.System_String) { int elemSize; if (((size & BIT_32) != 0)) @@ -1245,9 +1246,9 @@ internal int OffsetOfNextField(ref PayloadFetch payloadFetch, int offset, int pa } } - internal static ushort SizeOfType(Type type) + internal static ushort SizeOfType(FetchType type) { - switch (Type.GetTypeCode(type)) + switch (FetchTypeHelpers.GetTypeCode(type)) { case TypeCode.String: return NULL_TERMINATED; @@ -1268,17 +1269,17 @@ internal static ushort SizeOfType(Type type) case TypeCode.DateTime: return 8; default: - if (type == typeof(Guid)) + if (type == FetchType.System_Guid) { return 16; } - if (type == typeof(IntPtr)) + if (type == FetchType.System_IntPtr) { return POINTER_SIZE; } - throw new Exception("Unsupported type " + type.Name); // TODO + throw new Exception("Unsupported type " + FetchTypeHelpers.GetName(type)); // TODO } } @@ -1311,11 +1312,11 @@ internal struct PayloadFetch /// /// Constructor for normal types, (int, string) ...) Also handles Enums (which are ints with a map) /// - public PayloadFetch(ushort offset, ushort size, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type, IDictionary map = null) + public PayloadFetch(ushort offset, ushort size, FetchType fetchType, IDictionary map = null) { Offset = offset; Size = size; - Type = type; + Type = fetchType; info = map; } @@ -1332,17 +1333,17 @@ public PayloadFetch(ushort offset, RegisteredTraceEventParser.TdhInputType inTyp switch (inType) { case RegisteredTraceEventParser.TdhInputType.UnicodeString: - Type = typeof(string); + Type = FetchType.System_String; Size = DynamicTraceEventData.NULL_TERMINATED; break; case RegisteredTraceEventParser.TdhInputType.AnsiString: - Type = typeof(string); + Type = FetchType.System_String; Size = DynamicTraceEventData.NULL_TERMINATED | DynamicTraceEventData.IS_ANSI; break; case RegisteredTraceEventParser.TdhInputType.UInt8: if (outType == 13) // Encoding for boolean { - Type = typeof(bool); + Type = FetchType.System_Boolean; Size = 1; break; } @@ -1350,7 +1351,7 @@ public PayloadFetch(ushort offset, RegisteredTraceEventParser.TdhInputType inTyp case RegisteredTraceEventParser.TdhInputType.Binary: // Binary is an array of bytes. The later logic will transform it to array, thus Binary is like byte case RegisteredTraceEventParser.TdhInputType.Int8: - Type = typeof(byte); + Type = FetchType.System_Byte; Size = 1; break; case RegisteredTraceEventParser.TdhInputType.Int16: @@ -1358,76 +1359,76 @@ public PayloadFetch(ushort offset, RegisteredTraceEventParser.TdhInputType inTyp Size = 2; if (outType == 1) // Encoding for String { - Type = typeof(char); + Type = FetchType.System_Char; break; } - Type = typeof(short); + Type = FetchType.System_Int16; break; case RegisteredTraceEventParser.TdhInputType.Int32: case RegisteredTraceEventParser.TdhInputType.UInt32: case RegisteredTraceEventParser.TdhInputType.HexInt32: - Type = typeof(int); + Type = FetchType.System_Int32; Size = 4; break; case RegisteredTraceEventParser.TdhInputType.Int64: case RegisteredTraceEventParser.TdhInputType.UInt64: case RegisteredTraceEventParser.TdhInputType.HexInt64: - Type = typeof(long); + Type = FetchType.System_Int64; Size = 8; break; case RegisteredTraceEventParser.TdhInputType.Float: - Type = typeof(float); + Type = FetchType.System_Single; Size = 4; break; case RegisteredTraceEventParser.TdhInputType.Double: - Type = typeof(double); + Type = FetchType.System_Double; Size = 8; break; case RegisteredTraceEventParser.TdhInputType.Boolean: - Type = typeof(bool); + Type = FetchType.System_Boolean; Size = 4; break; case RegisteredTraceEventParser.TdhInputType.GUID: - Type = typeof(Guid); + Type = FetchType.System_Guid; Size = 16; break; case RegisteredTraceEventParser.TdhInputType.Pointer: case RegisteredTraceEventParser.TdhInputType.SizeT: - Type = typeof(IntPtr); + Type = FetchType.System_IntPtr; Size = DynamicTraceEventData.POINTER_SIZE; break; case RegisteredTraceEventParser.TdhInputType.FILETIME: - Type = typeof(DateTime); + Type = FetchType.System_DateTime; Size = 8; break; case RegisteredTraceEventParser.TdhInputType.CountedUtf16String: case RegisteredTraceEventParser.TdhInputType.CountedString: - Type = typeof(string); + Type = FetchType.System_String; Size = DynamicTraceEventData.COUNTED_SIZE; // Unicode, 16 bit, byteCount break; case RegisteredTraceEventParser.TdhInputType.CountedAnsiString: case RegisteredTraceEventParser.TdhInputType.CountedMbcsString: - Type = typeof(string); + Type = FetchType.System_String; Size = DynamicTraceEventData.COUNTED_SIZE | DynamicTraceEventData.IS_ANSI + DynamicTraceEventData.ELEM_COUNT; // 16 Bit. break; case RegisteredTraceEventParser.TdhInputType.Struct: - Type = typeof(DynamicTraceEventData.StructValue); + Type = FetchType.Microsoft_Diagnostics_Tracing_Parsers_DynamicTraceEventData_StructValue; Size = DynamicTraceEventData.UNKNOWN_SIZE; break; case RegisteredTraceEventParser.TdhInputType.SYSTEMTIME: - Type = typeof(DateTime); + Type = FetchType.System_DateTime; Size = 16; break; default: Size = DynamicTraceEventData.UNKNOWN_SIZE; - Type = null; + Type = null; // Unknown type. break; } } private static bool ArrayElementCanProjectToString(PayloadFetch element) { - return element.Type == typeof(char) && (element.Size == 1 || element.Size == 2); + return element.Type == FetchType.System_Char && (element.Size == 1 || element.Size == 2); } /// @@ -1446,9 +1447,9 @@ public static PayloadFetch ArrayPayloadFetch(ushort offset, PayloadFetch element }; // If the array is a char array, then we can project it as a string. - if(projectCharArrayAsString && ArrayElementCanProjectToString(element)) + if (projectCharArrayAsString && ArrayElementCanProjectToString(element)) { - ret.Type = typeof(string); + ret.Type = FetchType.System_String; } return ret; @@ -1462,7 +1463,7 @@ public static PayloadFetch FixedCountArrayPayloadFetch(ushort offset, PayloadFet uint fixedSize = (uint)(elementCount * element.Size); if (fixedSize >= SPECIAL_SIZES) { - throw new ArgumentOutOfRangeException($"FixedCountArray cannot exceed {SPECIAL_SIZES-1} bytes. ElementCount: {elementCount}, ElementTypeSize: {element.Size}"); + throw new ArgumentOutOfRangeException($"FixedCountArray cannot exceed {SPECIAL_SIZES - 1} bytes. ElementCount: {elementCount}, ElementTypeSize: {element.Size}"); } size = (ushort)fixedSize; } @@ -1471,7 +1472,7 @@ public static PayloadFetch FixedCountArrayPayloadFetch(ushort offset, PayloadFet public static PayloadFetch RelLocPayloadFetch(ushort offset, PayloadFetch element, bool projectCharArrayAsString = true) { - if(!element.IsFixedSize) + if (!element.IsFixedSize) { throw new ArgumentException("RelLocPayloadFetch requires a fixed size element type."); } @@ -1484,16 +1485,16 @@ public static PayloadFetch RelLocPayloadFetch(ushort offset, PayloadFetch elemen FixedCount = 0, Kind = ArrayKind.RelLoc }; - if(projectCharArrayAsString && ArrayElementCanProjectToString(element)) + if (projectCharArrayAsString && ArrayElementCanProjectToString(element)) { - ret.Type = typeof(string); + ret.Type = FetchType.System_String; } return ret; } public static PayloadFetch DataLocPayloadFetch(ushort offset, PayloadFetch element, bool projectCharArrayAsString = true) { - if(!element.IsFixedSize) + if (!element.IsFixedSize) { throw new ArgumentException("DataLocPayloadFetch requires a fixed size element type."); } @@ -1506,9 +1507,9 @@ public static PayloadFetch DataLocPayloadFetch(ushort offset, PayloadFetch eleme FixedCount = 0, Kind = ArrayKind.DataLoc }; - if(projectCharArrayAsString && ArrayElementCanProjectToString(element)) + if (projectCharArrayAsString && ArrayElementCanProjectToString(element)) { - ret.Type = typeof(string); + ret.Type = FetchType.System_String; } return ret; } @@ -1538,7 +1539,7 @@ public static PayloadFetch StructPayloadFetch(ushort offset, PayloadFetchClassIn size = DynamicTraceEventData.UNKNOWN_SIZE; // We don't know the size of the field. } ret.Size = (ushort)size; - ret.Type = typeof(StructValue); + ret.Type = FetchType.Microsoft_Diagnostics_Tracing_Parsers_DynamicTraceEventData_StructValue; ret.info = fields; return ret; } @@ -1566,8 +1567,189 @@ public PayloadFetchArrayInfo Array get { return info as PayloadFetchArrayInfo; } } - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - public Type Type; // currently null for arrays + public FetchType? Type; // currently null for arrays + + public enum FetchType + { + System_Boolean, + System_Char, + System_String, + System_SByte, + System_Int16, + System_Int32, + System_Int64, + System_Byte, + System_UInt16, + System_UInt32, + System_UInt64, + System_Single, + System_Double, + System_Decimal, + System_DateTime, + System_Guid, + System_IntPtr, + Microsoft_Diagnostics_Tracing_Parsers_DynamicTraceEventData_StructValue, + } + + public static class FetchTypeHelpers + { + public static FetchType Parse(string typeName) + { + return typeName switch + { + "System.Boolean" => FetchType.System_Boolean, + "System.Char" => FetchType.System_Char, + "System.String" => FetchType.System_String, + "System.SByte" => FetchType.System_SByte, + "System.Int16" => FetchType.System_Int16, + "System.Int32" => FetchType.System_Int32, + "System.Int64" => FetchType.System_Int64, + "System.Byte" => FetchType.System_Byte, + "System.UInt16" => FetchType.System_UInt16, + "System.UInt32" => FetchType.System_UInt32, + "System.UInt64" => FetchType.System_UInt64, + "System.Single" => FetchType.System_Single, + "System.Double" => FetchType.System_Double, + "System.Decimal" => FetchType.System_Decimal, + "System.DateTime" => FetchType.System_DateTime, + "System.Guid" => FetchType.System_Guid, + "Microsoft.Diagnostics.Tracing.Parsers.DynamicTraceEventData.StructValue" => FetchType.Microsoft_Diagnostics_Tracing_Parsers_DynamicTraceEventData_StructValue, + "System.IntPtr" => FetchType.System_IntPtr, + + _ => throw new InvalidDataException("Unknown FetchType: " + typeName), + }; + } + public static string GetName(FetchType fetchType) => fetchType switch + { + FetchType.System_Boolean => "Boolean", + FetchType.System_Char => "Char", + FetchType.System_String => "String", + FetchType.System_SByte => "SByte", + FetchType.System_Int16 => "Int16", + FetchType.System_Int32 => "Int32", + FetchType.System_Int64 => "Int64", + FetchType.System_Byte => "Byte", + FetchType.System_UInt16 => "UInt16", + FetchType.System_UInt32 => "UInt32", + FetchType.System_UInt64 => "UInt64", + FetchType.System_IntPtr => "IntPtr", + FetchType.System_Single => "Single", + FetchType.System_Double => "Double", + FetchType.System_Decimal => "Decimal", + FetchType.System_DateTime => "DateTime", + FetchType.System_Guid => "Guid", + FetchType.Microsoft_Diagnostics_Tracing_Parsers_DynamicTraceEventData_StructValue => "StructValue", + }; + + internal static TypeCode GetTypeCode(FetchType type) + { + return type switch + { + FetchType.System_Boolean => TypeCode.Boolean, + FetchType.System_Char => TypeCode.Char, + FetchType.System_String => TypeCode.String, + FetchType.System_SByte => TypeCode.SByte, + FetchType.System_Int16 => TypeCode.Int16, + FetchType.System_Int32 => TypeCode.Int32, + FetchType.System_Int64 => TypeCode.Int64, + FetchType.System_Byte => TypeCode.Byte, + FetchType.System_UInt16 => TypeCode.UInt16, + FetchType.System_UInt32 => TypeCode.UInt32, + FetchType.System_UInt64 => TypeCode.UInt64, + FetchType.System_Single => TypeCode.Single, + FetchType.System_Double => TypeCode.Double, + FetchType.System_Decimal => TypeCode.Decimal, + FetchType.System_DateTime => TypeCode.DateTime, + FetchType.System_Guid => TypeCode.Object, + FetchType.Microsoft_Diagnostics_Tracing_Parsers_DynamicTraceEventData_StructValue => TypeCode.Object, + FetchType.System_IntPtr => TypeCode.Object + }; + } + + internal static object GetDefaultValueByType(FetchType type) + { + return type switch + { + FetchType.System_Boolean => default(bool), + FetchType.System_Char => default(char), + FetchType.System_String => string.Empty, + FetchType.System_SByte => default(sbyte), + FetchType.System_Int16 => default(short), + FetchType.System_Int32 => default(int), + FetchType.System_Int64 => default(long), + FetchType.System_Byte => default(byte), + FetchType.System_UInt16 => default(ushort), + FetchType.System_UInt32 => default(uint), + FetchType.System_UInt64 => default(ulong), + FetchType.System_Single => default(float), + FetchType.System_Double => default(double), + FetchType.System_Decimal => default(decimal), + FetchType.System_DateTime => default(DateTime), + FetchType.System_Guid => default(Guid), + FetchType.System_IntPtr => IntPtr.Zero, + FetchType.Microsoft_Diagnostics_Tracing_Parsers_DynamicTraceEventData_StructValue => default(StructValue), + }; + } + + internal static string GetFullName(FetchType type) + { + return type switch + { + FetchType.System_Boolean => "System.Boolean", + FetchType.System_Char => "System.Char", + FetchType.System_String => "System.String", + FetchType.System_SByte => "System.SByte", + FetchType.System_Int16 => "System.Int16", + FetchType.System_Int32 => "System.Int32", + FetchType.System_Int64 => "System.Int64", + FetchType.System_Byte => "System.Byte", + FetchType.System_UInt16 => "System.UInt16", + FetchType.System_UInt32 => "System.UInt32", + FetchType.System_UInt64 => "System.UInt64", + FetchType.System_Single => "System.Single", + FetchType.System_Double => "System.Double", + FetchType.System_Decimal => "System.Decimal", + FetchType.System_DateTime => "System.DateTime", + FetchType.System_Guid => "System.Guid", + FetchType.System_IntPtr => "System.IntPtr", + FetchType.Microsoft_Diagnostics_Tracing_Parsers_DynamicTraceEventData_StructValue => "Microsoft.Diagnostics.Tracing.Parsers.DynamicTraceEventData.StructValue", + }; + } + + internal static object Convert(FetchType? typeOpt, object value) + { + if (typeOpt is not { } type) + { + if (value == null) + { + return null; // No type, no value. + } + throw new InvalidOperationException($"Cannot convert value '{value}' when type is null."); + } + + return type switch + { + FetchType.System_Boolean => System.Convert.ToBoolean(value), + FetchType.System_Char => System.Convert.ToChar(value), + FetchType.System_String => System.Convert.ToString(value), + FetchType.System_SByte => System.Convert.ToSByte(value), + FetchType.System_Int16 => System.Convert.ToInt16(value), + FetchType.System_Int32 => System.Convert.ToInt32(value), + FetchType.System_Int64 => System.Convert.ToInt64(value), + FetchType.System_Byte => System.Convert.ToByte(value), + FetchType.System_UInt16 => System.Convert.ToUInt16(value), + FetchType.System_UInt32 => System.Convert.ToUInt32(value), + FetchType.System_UInt64 => System.Convert.ToUInt64(value), + FetchType.System_Single => System.Convert.ToSingle(value), + FetchType.System_Double => System.Convert.ToDouble(value), + FetchType.System_Decimal => System.Convert.ToDecimal(value), + FetchType.System_DateTime => System.Convert.ToDateTime(value), + FetchType.System_Guid => System.Guid.Parse(System.Convert.ToString(value)), + FetchType.System_IntPtr => new IntPtr(System.Convert.ToInt64(value)), + FetchType.Microsoft_Diagnostics_Tracing_Parsers_DynamicTraceEventData_StructValue => (StructValue)value, + }; + } + } // Non null of 'Type' is a enum public IDictionary Map @@ -1621,7 +1803,7 @@ public Func> LazyMap public override string ToString() { StringWriter sw = new StringWriter(); - sw.Write(""); @@ -1653,13 +1835,13 @@ public void ToStream(Serializer serializer) { serializer.Write((short)Offset); serializer.Write((short)Size); - if (Type == null) + if (Type is not { } t) { serializer.Write((string)null); } else { - serializer.Write(Type.FullName); + serializer.Write(FetchTypeHelpers.GetFullName(t)); } var map = Map; @@ -1713,26 +1895,6 @@ public void ToStream(Serializer serializer) } } -#if NET9_0_OR_GREATER - /// - /// Feature switch that turns off deserialization of any types that are not primitive types. - /// Specifically, the property must either be a primitive type, - /// or null. - /// - internal static class PrimitiveOnlyDeserialization - { - private const string FeatureSwitchName = "PrimitiveOnlyDeserialization.IsDisabled"; - - - [FeatureSwitchDefinition(FeatureSwitchName)] - [FeatureGuard(typeof(RequiresUnreferencedCodeAttribute))] -#pragma warning disable IL4000 // The feature switch should be make the return value match RequiresUnreferencedCode - // We reverse the logic here because FeatureGuards must always return false when the target type is not available. - public static bool IsDisabled => AppContext.TryGetSwitch(FeatureSwitchName, out bool disabled) ? disabled : true; -#pragma warning restore IL4000 - } -#endif - public void FromStream(Deserializer deserializer) { Offset = (ushort)deserializer.ReadInt16(); @@ -1740,38 +1902,7 @@ public void FromStream(Deserializer deserializer) var typeName = deserializer.ReadString(); if (typeName != null) { -#if NET9_0_OR_GREATER - if (PrimitiveOnlyDeserialization.IsDisabled) - { - Type = Type.GetType(typeName); - } - else - { - Type = typeName switch - { - "System.Char" => typeof(char), - "System.String" => typeof(string), - "System.Int32" => typeof(int), - "System.Int64" => typeof(long), - "System.Boolean" => typeof(bool), - "System.Double" => typeof(double), - "System.Single" => typeof(float), - "System.Byte" => typeof(byte), - "System.SByte" => typeof(sbyte), - "System.UInt16" => typeof(ushort), - "System.Int16" => typeof(short), - "System.UInt32" => typeof(uint), - "System.UInt64" => typeof(ulong), - "System.Guid" => typeof(Guid), - "System.DateTime" => typeof(DateTime), - "System.IntPtr" => typeof(IntPtr), - "Microsoft.Diagnostics.Tracing.Parsers.DynamicTraceEventData.StructValue" => typeof(StructValue), - _ => null, - }; - } -#else - Type = Type.GetType(typeName); -#endif + Type = FetchTypeHelpers.Parse(typeName); } var fetchType = deserializer.ReadByte(); @@ -1946,12 +2077,12 @@ internal DynamicManifestTraceEventData(Action action, ProviderManife this.manifest = manifest; payloadNames = new string[] { "Format", "MajorVersion", "MinorVersion", "Magic", "TotalChunks", "ChunkNumber", "PayloadLength" }; payloadFetches = new PayloadFetch[] { - new PayloadFetch(0, 1, typeof(byte)), - new PayloadFetch(1, 1, typeof(byte)), - new PayloadFetch(2, 1, typeof(byte)), - new PayloadFetch(3, 1, typeof(byte)), - new PayloadFetch(4, 2, typeof(ushort)), - new PayloadFetch(6, 2, typeof(ushort)), + new PayloadFetch(0, 1, FetchType.System_Byte), + new PayloadFetch(1, 1, FetchType.System_Byte), + new PayloadFetch(2, 1, FetchType.System_Byte), + new PayloadFetch(3, 1, FetchType.System_Byte), + new PayloadFetch(4, 2, FetchType.System_UInt16), + new PayloadFetch(6, 2, FetchType.System_UInt16), }; m_target += action; } @@ -2560,15 +2691,15 @@ private static TemplateInfo ComputeFieldInfo(XmlReader reader, Dictionary - [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] - private static Type GetTypeForManifestTypeName(string manifestTypeName) + private static FetchType? GetTypeForManifestTypeName(string manifestTypeName) { switch (manifestTypeName) { case "win:Pointer": case "trace:SizeT": - return typeof(IntPtr); + return FetchType.System_IntPtr; case "win:Boolean": - return typeof(bool); + return FetchType.System_Boolean; case "win:UInt8": - return typeof(byte); + return FetchType.System_Byte; case "win:Int8": - return typeof(sbyte); + return FetchType.System_SByte; case "win:Int16": - return typeof(short); + return FetchType.System_Int16; case "win:UInt16": case "trace:Port": - return typeof(ushort); + return FetchType.System_UInt16; case "win:Int32": - return typeof(int); + return FetchType.System_Int32; case "win:UInt32": case "trace: ": case "trace:IPAddrV4": - return typeof(uint); + return FetchType.System_UInt32; case "win:Int64": case "trace:WmiTime": - return typeof(long); + return FetchType.System_Int64; case "win:UInt64": - return typeof(ulong); + return FetchType.System_UInt64; case "win:Double": - return typeof(double); + return FetchType.System_Double; case "win:Float": - return typeof(float); + return FetchType.System_Single; case "win:AnsiString": case "win:UnicodeString": - return typeof(string); + return FetchType.System_String; case "win:Binary": - return typeof(byte); // We special case this later to make it an array of this type. + return FetchType.System_Byte; // We special case this later to make it an array of this type. case "win:GUID": - return typeof(Guid); + return FetchType.System_Guid; case "win:FILETIME": - return typeof(DateTime); + return FetchType.System_DateTime; default: return null; } diff --git a/src/TraceEvent/EventPipe/EventPipeMetadata.cs b/src/TraceEvent/EventPipe/EventPipeMetadata.cs index fd12df0bf..75445cc92 100644 --- a/src/TraceEvent/EventPipe/EventPipeMetadata.cs +++ b/src/TraceEvent/EventPipe/EventPipeMetadata.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; +using static Microsoft.Diagnostics.Tracing.Parsers.DynamicTraceEventData.PayloadFetch; namespace Microsoft.Diagnostics.Tracing { @@ -209,7 +210,7 @@ private void InitDefaultParameters() ushort extendedDataCount = 0; TraceEventNativeMethods.EVENT_HEADER_EXTENDED_DATA_ITEM* curExtendedBufferPtr = _extendedDataBuffer; _eventRecord->ExtendedData = _extendedDataBuffer; - + if (stackBytesSize > 0) { if ((_eventRecord->EventHeader.Flags & TraceEventNativeMethods.EVENT_HEADER_FLAG_32_BIT_HEADER) != 0) @@ -449,112 +450,112 @@ private DynamicTraceEventData.PayloadFetch ParseType( { case EventPipeTypeCode.Boolean: { - payloadFetch.Type = typeof(bool); + payloadFetch.Type = FetchType.System_Boolean; payloadFetch.Size = 4; // We follow windows conventions and use 4 bytes for bool. payloadFetch.Offset = offset; break; } case EventPipeTypeCode.UTF16CodeUnit: { - payloadFetch.Type = typeof(char); + payloadFetch.Type = FetchType.System_Char; payloadFetch.Size = sizeof(char); payloadFetch.Offset = offset; break; } case EventPipeTypeCode.SByte: { - payloadFetch.Type = typeof(SByte); + payloadFetch.Type = FetchType.System_SByte; payloadFetch.Size = sizeof(SByte); payloadFetch.Offset = offset; break; } case EventPipeTypeCode.Byte: { - payloadFetch.Type = typeof(byte); + payloadFetch.Type = FetchType.System_Byte; payloadFetch.Size = sizeof(byte); payloadFetch.Offset = offset; break; } case EventPipeTypeCode.Int16: { - payloadFetch.Type = typeof(Int16); + payloadFetch.Type = FetchType.System_Int16; payloadFetch.Size = sizeof(Int16); payloadFetch.Offset = offset; break; } case EventPipeTypeCode.UInt16: { - payloadFetch.Type = typeof(UInt16); + payloadFetch.Type = FetchType.System_UInt16; payloadFetch.Size = sizeof(UInt16); payloadFetch.Offset = offset; break; } case EventPipeTypeCode.Int32: { - payloadFetch.Type = typeof(Int32); + payloadFetch.Type = FetchType.System_Int32; payloadFetch.Size = sizeof(Int32); payloadFetch.Offset = offset; break; } case EventPipeTypeCode.UInt32: { - payloadFetch.Type = typeof(UInt32); + payloadFetch.Type = FetchType.System_UInt32; payloadFetch.Size = sizeof(UInt32); payloadFetch.Offset = offset; break; } case EventPipeTypeCode.Int64: { - payloadFetch.Type = typeof(Int64); + payloadFetch.Type = FetchType.System_Int64; payloadFetch.Size = sizeof(Int64); payloadFetch.Offset = offset; break; } case EventPipeTypeCode.UInt64: { - payloadFetch.Type = typeof(UInt64); + payloadFetch.Type = FetchType.System_UInt64; payloadFetch.Size = sizeof(UInt64); payloadFetch.Offset = offset; break; } case EventPipeTypeCode.Single: { - payloadFetch.Type = typeof(Single); + payloadFetch.Type = FetchType.System_Single; payloadFetch.Size = sizeof(Single); payloadFetch.Offset = offset; break; } case EventPipeTypeCode.Double: { - payloadFetch.Type = typeof(Double); + payloadFetch.Type = FetchType.System_Double; payloadFetch.Size = sizeof(Double); payloadFetch.Offset = offset; break; } case EventPipeTypeCode.Decimal: { - payloadFetch.Type = typeof(Decimal); + payloadFetch.Type = FetchType.System_Decimal; payloadFetch.Size = sizeof(Decimal); payloadFetch.Offset = offset; break; } case EventPipeTypeCode.DateTime: { - payloadFetch.Type = typeof(DateTime); + payloadFetch.Type = FetchType.System_DateTime; payloadFetch.Size = 8; payloadFetch.Offset = offset; break; } case EventPipeTypeCode.Guid: { - payloadFetch.Type = typeof(Guid); + payloadFetch.Type = FetchType.System_Guid; payloadFetch.Size = 16; payloadFetch.Offset = offset; break; } case EventPipeTypeCode.NullTerminatedUTF16String: { - payloadFetch.Type = typeof(String); + payloadFetch.Type = FetchType.System_String; payloadFetch.Size = DynamicTraceEventData.NULL_TERMINATED; payloadFetch.Offset = offset; break; @@ -587,7 +588,7 @@ private DynamicTraceEventData.PayloadFetch ParseType( { throw new FormatException($"VarInt is not a valid type code for this metadata."); } - payloadFetch.Type = typeof(long); + payloadFetch.Type = FetchType.System_Int64; payloadFetch.Size = DynamicTraceEventData.VARINT; payloadFetch.Offset = offset; break; @@ -598,7 +599,7 @@ private DynamicTraceEventData.PayloadFetch ParseType( { throw new FormatException($"VarUInt is not a valid type code for this metadata."); } - payloadFetch.Type = typeof(ulong); + payloadFetch.Type = FetchType.System_UInt64; payloadFetch.Size = DynamicTraceEventData.VARINT; payloadFetch.Offset = offset; break; @@ -619,7 +620,7 @@ private DynamicTraceEventData.PayloadFetch ParseType( { throw new FormatException($"FixedLengthArray too large", e); } - + break; } case EventPipeTypeCode.UTF8CodeUnit: @@ -628,7 +629,7 @@ private DynamicTraceEventData.PayloadFetch ParseType( { throw new FormatException($"UTF8CodeUnit is not a valid type code for this metadata."); } - payloadFetch.Type = typeof(char); + payloadFetch.Type = FetchType.System_Char; payloadFetch.Size = sizeof(byte); payloadFetch.Offset = offset; break; @@ -673,7 +674,7 @@ private DynamicTraceEventData.PayloadFetch ParseType( enum EventPipeTypeCode { Object = 1, // Concatenate together all of the encoded fields - Boolean = 3, // A 4-byte LE integer with value 0=false and 1=true. + Boolean = 3, // A 4-byte LE integer with value 0=false and 1=true. UTF16CodeUnit = 4, // a 2-byte UTF16 code unit SByte = 5, // 1-byte signed integer Byte = 6, // 1-byte unsigned integer @@ -694,7 +695,7 @@ enum EventPipeTypeCode VarUInt = 21, // New in V6: variable-length unsigned integer (ULEB128) FixedLengthArray = 22, // New in V6: A fixed-length array of elements. The length is determined by the metadata. UTF8CodeUnit = 23, // New in V6: A single UTF8 code unit (1 byte). - RelLoc = 24, // New in V6: An array at a relative location within the payload. + RelLoc = 24, // New in V6: An array at a relative location within the payload. DataLoc = 25 // New in V6: An absolute data location within the payload. } @@ -789,7 +790,7 @@ public void ApplyTransforms() //TraceEvent expects empty name to be canonicalized as null rather than "" if (EventName == "") { - EventName = null; + EventName = null; } if(ProviderId == Guid.Empty) @@ -804,12 +805,12 @@ public void ApplyTransforms() { PopulateWellKnownEventParameters(); } - + if (Opcode == 0) { ExtractImpliedOpcode(); } - + StripStartStopInEventName(); } @@ -876,23 +877,23 @@ private void PopulateWellKnownEventParameters() { DynamicTraceEventData.PayloadFetch[] fieldFetches = new DynamicTraceEventData.PayloadFetch[3]; string[] fieldNames = new string[3]; - fieldFetches[0].Type = typeof(string); + fieldFetches[0].Type = FetchType.System_String; fieldFetches[0].Size = DynamicTraceEventData.NULL_TERMINATED; fieldFetches[0].Offset = 0; fieldNames[0] = "SourceName"; - fieldFetches[1].Type = typeof(string); + fieldFetches[1].Type = FetchType.System_String; fieldFetches[1].Size = DynamicTraceEventData.NULL_TERMINATED; fieldFetches[1].Offset = ushort.MaxValue; fieldNames[1] = "EventName"; DynamicTraceEventData.PayloadFetch[] keyValuePairFieldFetches = new DynamicTraceEventData.PayloadFetch[2]; string[] keyValuePairFieldNames = new string[2]; - keyValuePairFieldFetches[0].Type = typeof(string); + keyValuePairFieldFetches[0].Type = FetchType.System_String; keyValuePairFieldFetches[0].Size = DynamicTraceEventData.NULL_TERMINATED; keyValuePairFieldFetches[0].Offset = 0; keyValuePairFieldNames[0] = "Key"; - keyValuePairFieldFetches[1].Type = typeof(string); + keyValuePairFieldFetches[1].Type = FetchType.System_String; keyValuePairFieldFetches[1].Size = DynamicTraceEventData.NULL_TERMINATED; keyValuePairFieldFetches[1].Offset = ushort.MaxValue; keyValuePairFieldNames[1] = "Value"; diff --git a/src/TraceEvent/Microsoft.Diagnostics.Tracing.TraceEvent.nuspec b/src/TraceEvent/Microsoft.Diagnostics.Tracing.TraceEvent.nuspec index 29f86aacb..329ac62ed 100644 --- a/src/TraceEvent/Microsoft.Diagnostics.Tracing.TraceEvent.nuspec +++ b/src/TraceEvent/Microsoft.Diagnostics.Tracing.TraceEvent.nuspec @@ -45,7 +45,6 @@ - diff --git a/src/TraceEvent/Microsoft.Diagnostics.Tracing.TraceEvent.targets b/src/TraceEvent/Microsoft.Diagnostics.Tracing.TraceEvent.targets deleted file mode 100644 index 5b77847e6..000000000 --- a/src/TraceEvent/Microsoft.Diagnostics.Tracing.TraceEvent.targets +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - diff --git a/src/TraceEvent/RegisteredTraceEventParser.cs b/src/TraceEvent/RegisteredTraceEventParser.cs index eedf36ade..40137e166 100644 --- a/src/TraceEvent/RegisteredTraceEventParser.cs +++ b/src/TraceEvent/RegisteredTraceEventParser.cs @@ -9,12 +9,13 @@ using System.IO; using System.Runtime.InteropServices; using System.Text.RegularExpressions; +using static Microsoft.Diagnostics.Tracing.Parsers.DynamicTraceEventData.PayloadFetch; namespace Microsoft.Diagnostics.Tracing.Parsers { /// /// RegisteredTraceEventParser uses the standard windows provider database (TDH, what gets registered with wevtutil) - /// to find the names of events and fields of the events). + /// to find the names of events and fields of the events). /// public sealed unsafe class RegisteredTraceEventParser : ExternalTraceEventParser { @@ -35,11 +36,11 @@ public RegisteredTraceEventParser(TraceEventSource source, bool dontRegister = f // Uncomment this if you want to see the template in the debugger at this point // template.source = data.source; // template.eventRecord = data.eventRecord; - // template.userData = data.userData; + // template.userData = data.userData; m_state.m_templates[template] = template; }; - // Try to parse bitmap and value map information. + // Try to parse bitmap and value map information. symbolSource.MetaDataEventMapInfo += delegate (EmptyTraceData data) { try @@ -66,7 +67,7 @@ public RegisteredTraceEventParser(TraceEventSource source, bool dontRegister = f /// Given a provider name that has been registered with the operating system, get /// a string representing the ETW manifest for that provider. Note that this /// manifest is not as rich as the original source manifest because some information - /// is not actually compiled into the binary manifest that is registered with the OS. + /// is not actually compiled into the binary manifest that is registered with the OS. /// public static string GetManifestForRegisteredProvider(string providerName) { @@ -82,11 +83,11 @@ public static string GetManifestForRegisteredProvider(string providerName) /// Given a provider GUID that has been registered with the operating system, get /// a string representing the ETW manifest for that provider. Note that this /// manifest is not as rich as the original source manifest because some information - /// is not actually compiled into the binary manifest that is registered with the OS. + /// is not actually compiled into the binary manifest that is registered with the OS. /// public static string GetManifestForRegisteredProvider(Guid providerGuid) { - int buffSize = 84000; // Still in the small object heap. + int buffSize = 84000; // Still in the small object heap. var buffer = new byte[buffSize]; // Still in the small object heap. byte* enumBuffer = null; @@ -98,18 +99,18 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid) SortedDictionary events = new SortedDictionary(); // We keep tasks separated by task ID SortedDictionary tasks = new SortedDictionary(); - // Templates where the KEY is the template string and the VALUE is the template name (backwards) + // Templates where the KEY is the template string and the VALUE is the template name (backwards) Dictionary templateIntern = new Dictionary(8); - // Remember any enum types we have. Value is XML for the enum (normal) + // Remember any enum types we have. Value is XML for the enum (normal) Dictionary enumIntern = new Dictionary(); StringWriter enumLocalizations = new StringWriter(); - // Any task names used so far + // Any task names used so far Dictionary taskNames = new Dictionary(); - // Any es used so far + // Any es used so far Dictionary opcodeNames = new Dictionary(); - // This ensures that we have unique event names. + // This ensures that we have unique event names. Dictionary eventNames = new Dictionary(); SortedDictionary keywords = new SortedDictionary(); @@ -118,7 +119,7 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid) { foreach (var keywordItem in keywordsItems) { - // Skip the reserved keywords. + // Skip the reserved keywords. if (keywordItem.Value >= 1000000000000UL) { continue; @@ -200,7 +201,7 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid) taskName = "task_" + eventInfo->EventDescriptor.Task.ToString(); } - // Ensure task name is unique. + // Ensure task name is unique. int taskNumForName; if (taskNames.TryGetValue(taskName, out taskNumForName) && taskNumForName != eventInfo->EventDescriptor.Task) { @@ -223,7 +224,7 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid) } } - // Ensure opcode name is unique. + // Ensure opcode name is unique. int opcodeNumForName; if (opcodeNames.TryGetValue(opcodeName, out opcodeNumForName) && opcodeNumForName != eventInfo->EventDescriptor.Opcode) { @@ -237,7 +238,7 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid) } opcodeNames[opcodeName] = eventInfo->EventDescriptor.Opcode; - // And event name + // And event name string eventName = taskName; if (!taskName.EndsWith(opcodeName, StringComparison.OrdinalIgnoreCase)) { @@ -280,9 +281,9 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid) if (eventInfo->EventDescriptor.Opcode != 0) { string opcodeId; - if (eventInfo->EventDescriptor.Opcode < 10) // It is a reserved opcode. + if (eventInfo->EventDescriptor.Opcode < 10) // It is a reserved opcode. { - // For some reason opcodeName does not have the underscore, which we need. + // For some reason opcodeName does not have the underscore, which we need. if (eventInfo->EventDescriptor.Opcode == (byte)TraceEventOpcode.DataCollectionStart) { opcodeId = "win:DC_Start"; @@ -311,7 +312,7 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid) } eventWriter.Write(" opcode=\"{0}\"", opcodeId); } - // TODO handle cases outside standard levels + // TODO handle cases outside standard levels if ((int)TraceEventLevel.Always <= eventInfo->EventDescriptor.Level && eventInfo->EventDescriptor.Level <= (int)TraceEventLevel.Verbose) { var asLevel = (TraceEventLevel)eventInfo->EventDescriptor.Level; @@ -352,7 +353,7 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid) var hr = TdhGetEventMapInformation(&eventRecord, mapName, enumInfo, ref buffSize); if (hr == 0) { - // We only support manifest enums for now. + // We only support manifest enums for now. if (enumInfo->Flag == MAP_FLAGS.EVENTMAP_INFO_FLAG_MANIFEST_VALUEMAP || enumInfo->Flag == MAP_FLAGS.EVENTMAP_INFO_FLAG_MANIFEST_BITMAP) { @@ -409,7 +410,7 @@ public static string GetManifestForRegisteredProvider(Guid providerGuid) } var templateStr = templateWriter.ToString(); - // See if this template already exists, and if not make it + // See if this template already exists, and if not make it string templateName; if (!templateIntern.TryGetValue(templateStr, out templateName)) { @@ -463,7 +464,7 @@ 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. + task.Opcodes == null ? "/" : ""); // If no opcodes, terminate immediately. localizedStrings.WriteLine(" ", task.Name, task.Name); if (task.Opcodes != null) { @@ -555,7 +556,7 @@ private static string MakeLegalIdentifier(string name) /// /// Generates a space separated list of set of keywords 'keywordSet' using the table 'keywords' - /// It will generate new keyword names if needed and add them to 'keywords' if they are not present. + /// It will generate new keyword names if needed and add them to 'keywords' if they are not present. /// private static string GetKeywordStr(SortedDictionary keywords, ulong keywordSet) { @@ -617,11 +618,11 @@ internal override DynamicTraceEventData TryLookup(TraceEvent unknownEvent) /// /// Try to look up 'unknonwEvent using TDH or the TraceLogging mechanism. if 'mapTable' is non-null it will be used /// look up the string names for fields that have bitsets or enumerated values. This is only need for the KernelTraceControl - /// case where the map information is logged as special events and can't be looked up with TDH APIs. + /// case where the map information is logged as special events and can't be looked up with TDH APIs. /// internal static DynamicTraceEventData TryLookupWorker(TraceEvent unknownEvent, Dictionary> mapTable = null) { - // Is this a TraceLogging style + // Is this a TraceLogging style DynamicTraceEventData ret = null; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -666,16 +667,16 @@ internal static DynamicTraceEventData TryLookupWorker(TraceEvent unknownEvent, D /// /// TdhEventParser takes the Trace Diagnostics Helper (TDH) TRACE_EVENT_INFO structure and - /// (passed as a byte*) and converts it to a DynamicTraceEventData which which + /// (passed as a byte*) and converts it to a DynamicTraceEventData which which /// can be used to parse events of that type. You first create TdhEventParser and then - /// call ParseEventMetaData to do the parsing. + /// call ParseEventMetaData to do the parsing. /// internal class TdhEventParser { /// /// Creates a new parser from the TRACE_EVENT_INFO held in 'buffer'. Use /// ParseEventMetaData to then parse it into a DynamicTraceEventData structure. - /// EventRecord can be null and mapTable if present allow the parser to resolve maps (enums), and can be null. + /// EventRecord can be null and mapTable if present allow the parser to resolve maps (enums), and can be null. /// public TdhEventParser(byte* eventInfo, TraceEventNativeMethods.EVENT_RECORD* eventRecord, Dictionary> mapTable) { @@ -718,7 +719,7 @@ public DynamicTraceEventData ParseEventMetaData() } var eventID = eventInfo->EventDescriptor.Id; - // Mark it as a classic event if necessary. + // Mark it as a classic event if necessary. if (eventInfo->DecodingSource == 1) // means it is from MOF (Classic) { eventID = (int)TraceEventID.Illegal; @@ -745,13 +746,13 @@ public DynamicTraceEventData ParseEventMetaData() newTemplate.payloadNames = fields.FieldNames; newTemplate.payloadFetches = fields.FieldFetches; - return newTemplate; // return this as the event template for this lookup. + return newTemplate; // return this as the event template for this lookup. } /// - /// Parses at most 'maxFields' fields starting at the current position. + /// Parses at most 'maxFields' fields starting at the current position. /// Will return the parse fields in 'payloadNamesRet' and 'payloadFetchesRet' - /// Will return true if successful, false means an error occurred. + /// Will return true if successful, false means an error occurred. /// private DynamicTraceEventData.PayloadFetchClassInfo ParseFields(int startField, int numFields) { @@ -767,9 +768,9 @@ private DynamicTraceEventData.PayloadFetchClassInfo ParseFields(int startField, // Remove anything that does not look like an ID (.e.g space) propertyName = Regex.Replace(propertyName, "[^A-Za-z0-9_]", ""); - // If it is an array, the field offset starts over at 0 because they are + // If it is an array, the field offset starts over at 0 because they are // describing the ELMEMENT not the array and thus each element starts at 0 - // Strings do NOT describe the element and thus don't get this treatment. + // Strings do NOT describe the element and thus don't get this treatment. var arrayFieldOffset = fieldOffset; if ((propertyInfo->Flags & (PROPERTY_FLAGS.ParamCount | PROPERTY_FLAGS.ParamLength)) != 0 && propertyInfo->InType != TdhInputType.UnicodeString && propertyInfo->InType != TdhInputType.AnsiString) @@ -805,10 +806,10 @@ private DynamicTraceEventData.PayloadFetchClassInfo ParseFields(int startField, { string mapName = new string((char*)(&eventBuffer[propertyInfo->MapNameOffset])); - // Normal case, you can look up the enum information immediately. + // Normal case, you can look up the enum information immediately. if (eventRecord != null) { - int buffSize = 84000; // TODO this is inefficient (and incorrect for very large enums). + int buffSize = 84000; // TODO this is inefficient (and incorrect for very large enums). byte* enumBuffer = (byte*)System.Runtime.InteropServices.Marshal.AllocHGlobal(buffSize); EVENT_MAP_INFO* enumInfo = (EVENT_MAP_INFO*)enumBuffer; @@ -824,10 +825,10 @@ private DynamicTraceEventData.PayloadFetchClassInfo ParseFields(int startField, { // This is the kernelTraceControl case, the map information will be provided // later, so we have to set up a LAZY map which will be evaluated when we need the - // enum (giving time for the enum definition to be processed. + // enum (giving time for the enum definition to be processed. var mapKey = new MapKey(eventInfo->ProviderGuid, mapName); - // Set the map to be a lazyMap, which is a Func that returns a map. + // Set the map to be a lazyMap, which is a Func that returns a map. Func> lazyMap = delegate () { IDictionary map = null; @@ -847,7 +848,7 @@ private DynamicTraceEventData.PayloadFetchClassInfo ParseFields(int startField, // Is it an array (binary and not a struct) (seems InType is not valid if property is a struct, so need to test for both. if ((propertyInfo->Flags & (PROPERTY_FLAGS.ParamCount | PROPERTY_FLAGS.ParamLength | PROPERTY_FLAGS.ParamFixedCount)) != 0 || propertyInfo->CountOrCountIndex > 1 || (propertyInfo->InType == TdhInputType.Binary && (propertyInfo->Flags & PROPERTY_FLAGS.Struct) == 0)) { - // silliness where if it is a byte[] they use Length otherwise they use count. Normalize it. + // silliness where if it is a byte[] they use Length otherwise they use count. Normalize it. var countOrCountIndex = propertyInfo->CountOrCountIndex; if ((propertyInfo->Flags & PROPERTY_FLAGS.ParamLength) != 0 || propertyInfo->InType == TdhInputType.Binary) { @@ -864,8 +865,8 @@ private DynamicTraceEventData.PayloadFetchClassInfo ParseFields(int startField, else { // We only support the case where the length/count is right before the array. We remove this field - // and use the PREFIX size to indicate that the size of the array is determined by the 32 or 16 bit number before - // the array data. + // and use the PREFIX size to indicate that the size of the array is determined by the 32 or 16 bit number before + // the array data. if (countOrCountIndex == startField + curField - 1) { var lastFieldIdx = fieldFetches.Count - 1; @@ -896,33 +897,33 @@ private DynamicTraceEventData.PayloadFetchClassInfo ParseFields(int startField, } } - // Strings are treated specially (we don't treat them as an array of chars). + // Strings are treated specially (we don't treat them as an array of chars). // They don't need an arrayFetch but DO need set the size and offset appropriately - if (propertyFetch.Type == typeof(string)) + if (propertyFetch.Type == FetchType.System_String) { // This is a string with its size determined by another field. Set the size - // based on 'arraySize' but preserver the IS_ANSI that we got from looking at the tdhInType. + // based on 'arraySize' but preserver the IS_ANSI that we got from looking at the tdhInType. propertyFetch.Size = (ushort)(arraySize | (propertyFetch.Size & DynamicTraceEventData.IS_ANSI)); propertyFetch.Offset = arrayFieldOffset; } else { - Debug.WriteLine(" Field is an array of size " + ((fixedCount != 0) ? fixedCount.ToString() : "VARIABLE") + " of type " + ((propertyFetch.Type ?? typeof(void))) + " at offset " + arrayFieldOffset.ToString("x")); + Debug.WriteLine(" Field is an array of size " + ((fixedCount != 0) ? fixedCount.ToString() : "VARIABLE") + " of type " + (propertyFetch.Type is { } t1 ? FetchTypeHelpers.GetFullName(t1) : "System.Void") + " at offset " + arrayFieldOffset.ToString("x")); propertyFetch = DynamicTraceEventData.PayloadFetch.ArrayPayloadFetch(arrayFieldOffset, propertyFetch, arraySize, fixedCount, projectCharArrayAsString:false); } - fieldOffset = ushort.MaxValue; // Indicate that the next offset must be computed at run time. + fieldOffset = ushort.MaxValue; // Indicate that the next offset must be computed at run time. } fieldFetches.Add(propertyFetch); fieldNames.Add(propertyName); var size = propertyFetch.Size; - Debug.WriteLine(" Got TraceLogging Field " + propertyName + " " + (propertyFetch.Type ?? typeof(void)) + " size " + size.ToString("x") + " offset " + fieldOffset.ToString("x") + " (void probably means array)"); + Debug.WriteLine(" Got TraceLogging Field " + propertyName + " " + (propertyFetch.Type is { } t2 ? FetchTypeHelpers.GetFullName(t2) : "System.Void") + " size " + size.ToString("x") + " offset " + fieldOffset.ToString("x") + " (void probably means array)"); Debug.Assert(0 < size); if (size >= DynamicTraceEventData.SPECIAL_SIZES) { - fieldOffset = ushort.MaxValue; // Indicate that the offset must be computed at run time. + fieldOffset = ushort.MaxValue; // Indicate that the offset must be computed at run time. } else if (fieldOffset != ushort.MaxValue) { @@ -937,11 +938,11 @@ private DynamicTraceEventData.PayloadFetchClassInfo ParseFields(int startField, } // Parses a EVENT_MAP_INFO into a Dictionary for a Value map or a SortedDictionary for a Bitmap - // returns null if it does not know how to parse it. + // returns null if it does not know how to parse it. internal static unsafe IDictionary ParseMap(EVENT_MAP_INFO* enumInfo, byte* enumBuffer) { IDictionary map = null; - // We only support manifest enums for now. + // We only support manifest enums for now. if (enumInfo->Flag == MAP_FLAGS.EVENTMAP_INFO_FLAG_MANIFEST_VALUEMAP || enumInfo->Flag == MAP_FLAGS.EVENTMAP_INFO_FLAG_MANIFEST_BITMAP) { @@ -971,9 +972,9 @@ internal static unsafe IDictionary ParseMap(EVENT_MAP_INFO* enumIn #region private private TRACE_EVENT_INFO* eventInfo; private TraceEventNativeMethods.EVENT_RECORD* eventRecord; - private Dictionary> mapTable; // table of enums that have defined. + private Dictionary> mapTable; // table of enums that have defined. private EVENT_PROPERTY_INFO* propertyInfos; - private byte* eventBuffer; // points at the eventInfo, but in increments of bytes + private byte* eventBuffer; // points at the eventInfo, but in increments of bytes #endregion // private } @@ -1034,8 +1035,8 @@ internal struct EVENT_MAP_INFO public int NameOffset; public MAP_FLAGS Flag; public int EntryCount; - public int ValueType; // I don't expect patterns, I expect this to be 0 (which means normal enums). - public EVENT_MAP_ENTRY MapEntryArray; // Actually an array, this is the first element. + public int ValueType; // I don't expect patterns, I expect this to be 0 (which means normal enums). + public EVENT_MAP_ENTRY MapEntryArray; // Actually an array, this is the first element. } [StructLayout(LayoutKind.Sequential)] @@ -1060,7 +1061,7 @@ internal struct TRACE_EVENT_INFO public int PropertyCount; public int TopLevelPropertyCount; public int Flags; - public EVENT_PROPERTY_INFO EventPropertyInfoArray; // Actually an array, this is the first element. + public EVENT_PROPERTY_INFO EventPropertyInfoArray; // Actually an array, this is the first element. } public struct EVENT_DESCRIPTOR @@ -1079,12 +1080,12 @@ internal struct EVENT_PROPERTY_INFO public PROPERTY_FLAGS Flags; public int NameOffset; - // These are valid if Flags & Struct not set. + // These are valid if Flags & Struct not set. public TdhInputType InType; public ushort OutType; // Really TDH_OUT_TYPE public int MapNameOffset; - // These are valid if Flags & Struct is set. + // These are valid if Flags & Struct is set. public int StructStartIndex { get @@ -1103,7 +1104,7 @@ public int NumOfStructMembers } // Normally Count is 1 (thus every field in an array, it is just that most array have fixed size of 1) - public ushort CountOrCountIndex; // Flags & ParamFixedLength determines if it count, otherwise countIndex + public ushort CountOrCountIndex; // Flags & ParamFixedLength determines if it count, otherwise countIndex // Normally Length is the size of InType (thus is fixed), but can be variable for blobs. public ushort LengthOrLengthIndex; // Flags & ParamLength determines if it lengthIndex otherwise it is the length InType public int Reserved; @@ -1166,7 +1167,7 @@ internal enum TdhInputType : ushort /// /// ExternalTraceEventParser is an abstract class that acts as a parser for any 'External' resolution - /// This include the TDH (RegisteredTraceEventParser) as well as the WPPTraceEventParser. + /// This include the TDH (RegisteredTraceEventParser) as well as the WPPTraceEventParser. /// public abstract unsafe class ExternalTraceEventParser : TraceEventParser { @@ -1184,7 +1185,7 @@ protected unsafe ExternalTraceEventParser(TraceEventSource source, bool dontRegi this.source.RegisterUnhandledEvent(delegate (TraceEvent unknown) { - // See if we already have this definition + // See if we already have this definition DynamicTraceEventData parsedTemplate = null; if (!m_state.m_templates.TryGetValue(unknown, out parsedTemplate)) @@ -1192,7 +1193,7 @@ protected unsafe ExternalTraceEventParser(TraceEventSource source, bool dontRegi parsedTemplate = TryLookup(unknown); if (parsedTemplate == null) { - m_state.m_templates.Add(unknown.Clone(), null); // add an entry to remember that we tried and failed. + m_state.m_templates.Add(unknown.Clone(), null); // add an entry to remember that we tried and failed. } } if (parsedTemplate == null) @@ -1203,13 +1204,13 @@ protected unsafe ExternalTraceEventParser(TraceEventSource source, bool dontRegi // registeredWithTraceEventSource is a fail safe. Basically if you added yourself to the table // (In OnNewEventDefinition) then you should not come back as unknown, however because of dual events // and just general fragility we don't want to rely on that. So we keep a bit and ensure that we - // only add the event definition once. + // only add the event definition once. if (!parsedTemplate.registeredWithTraceEventSource) { parsedTemplate.registeredWithTraceEventSource = true; bool ret = OnNewEventDefintion(parsedTemplate, false) == EventFilterResponse.AcceptEvent; - // If we have subscribers, notify them as well. + // If we have subscribers, notify them as well. var newEventDefinition = NewEventDefinition; if (newEventDefinition != null) { @@ -1224,7 +1225,7 @@ protected unsafe ExternalTraceEventParser(TraceEventSource source, bool dontRegi } /// - /// Override. + /// Override. /// public override bool IsStatic { get { return false; } } @@ -1236,7 +1237,7 @@ protected unsafe ExternalTraceEventParser(TraceEventSource source, bool dontRegi /// protected override string GetProviderName() { - // We handle more than one provider, so the convention is to return null. + // We handle more than one provider, so the convention is to return null. return null; } @@ -1263,7 +1264,7 @@ internal bool HasDefinitionForTemplate(TraceEvent template) /// protected internal override void EnumerateTemplates(Func eventsToObserve, Action callback) { - // Normally state is setup in the constructor, but call can be invoked before the constructor has finished, + // Normally state is setup in the constructor, but call can be invoked before the constructor has finished, if (m_state == null) { m_state = (ExternalTraceEventParserState)StateObject; @@ -1287,7 +1288,7 @@ protected internal override void EnumerateTemplates(Func - /// Register 'template' so that if there are any subscriptions to template they get registered with the source. + /// Register 'template' so that if there are any subscriptions to template they get registered with the source. /// internal override EventFilterResponse OnNewEventDefintion(TraceEvent template, bool mayHaveExistedBefore) { @@ -1312,14 +1313,14 @@ internal Dictionary> MapTable } } - private Dictionary> m_maps; // Any maps (enums or bitsets) defined by KernelTraceControl events. + private Dictionary> m_maps; // Any maps (enums or bitsets) defined by KernelTraceControl events. #endregion } #region internal classes /// - /// Used to look up Enums (provider x enumName); Very boring class. + /// Used to look up Enums (provider x enumName); Very boring class. /// internal class MapKey : IEquatable { @@ -1351,19 +1352,19 @@ public bool Equals(MapKey other) /// /// TDHDynamicTraceEventParserState represents the state of a TDHDynamicTraceEventParser that needs to be /// serialized to a log file. It does NOT include information about what events are chosen but DOES contain - /// any other necessary information that came from the ETL data file or the OS TDH APIs. + /// any other necessary information that came from the ETL data file or the OS TDH APIs. /// internal class ExternalTraceEventParserState : IFastSerializable { public ExternalTraceEventParserState() { } - // This is set of all dynamic event templates that this parser has TRIED to resolve. If resolution - // failed then the value is null, otherwise it is the same as the key. + // This is set of all dynamic event templates that this parser has TRIED to resolve. If resolution + // failed then the value is null, otherwise it is the same as the key. internal Dictionary m_templates; /// /// This defines what it means to be the same event. For manifest events it means provider and event ID - /// for classic, it means that taskGuid and opcode match. + /// for classic, it means that taskGuid and opcode match. /// internal class TraceEventComparer : IEqualityComparer { @@ -1419,7 +1420,7 @@ public int GetHashCode(TraceEvent obj) /// public virtual void ToStream(Serializer serializer) { - // Calculate the count. + // Calculate the count. var count = 0; foreach (var template in m_templates.Values) { diff --git a/src/TraceEvent/TraceEvent.csproj b/src/TraceEvent/TraceEvent.csproj index 0c7d75307..dde2667e0 100644 --- a/src/TraceEvent/TraceEvent.csproj +++ b/src/TraceEvent/TraceEvent.csproj @@ -29,7 +29,7 @@ true 9.0 - $(NoWarn);CA2014 + $(NoWarn);CA2014;CS8524 @@ -160,7 +160,6 @@ - diff --git a/src/TraceEvent/WPPTraceEventParser.cs b/src/TraceEvent/WPPTraceEventParser.cs index 349ceec05..e45bbb317 100644 --- a/src/TraceEvent/WPPTraceEventParser.cs +++ b/src/TraceEvent/WPPTraceEventParser.cs @@ -6,6 +6,7 @@ using System.IO; using System.Text; using System.Text.RegularExpressions; +using static Microsoft.Diagnostics.Tracing.Parsers.DynamicTraceEventData.PayloadFetch; namespace Microsoft.Diagnostics.Tracing.Parsers { @@ -86,13 +87,27 @@ private string GetTmfPathForTaskGuid(Guid taskGuid) private struct TypeAndFormat { - public TypeAndFormat([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type Type, IDictionary Map) { this.Type = Type; this.Map = Map; } + public TypeAndFormat(Type Type, IDictionary Map) { this.Type = Type; this.Map = Map; } - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public Type Type; public IDictionary Map; } + /// + /// This is only meant to be used in + /// + private static FetchType TypeToFetchType(Type type) => type switch + { + { } t when t == typeof(double) => FetchType.System_Double, + { } t when t == typeof(string) => FetchType.System_String, + { } t when t == typeof(IntPtr) => FetchType.System_IntPtr, + { } t when t == typeof(int) => FetchType.System_Int32, + { } t when t == typeof(long) => FetchType.System_Int64, + { } t when t == typeof(bool) => FetchType.System_Boolean, + { } t when t == typeof(Guid) => FetchType.System_Guid, + _ => throw new ArgumentException($"Unknown type {type.FullName} for WPPTraceEventParser"), + }; + private List CreateTemplatesForTMFFile(Guid taskGuid, string tmfPath) { List templates = new List(); @@ -295,9 +310,10 @@ private List CreateTemplatesForTMFFile(Guid taskGuid, str type = typeof(string); size |= DynamicTraceEventData.IS_ANSI; } - size |= DynamicTraceEventData.SizeOfType(type); + var fetchType = TypeToFetchType(type); + size |= DynamicTraceEventData.SizeOfType(fetchType); template.payloadFetches[i].Size = size; - template.payloadFetches[i].Type = type; + template.payloadFetches[i].Type = fetchType; if (size >= DynamicTraceEventData.SPECIAL_SIZES || offset == ushort.MaxValue) { From ad4b69146b8170486e6b27383980a9ef41c352ae Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Fri, 25 Jul 2025 14:37:36 -0700 Subject: [PATCH 5/6] Revert other changes --- src/FastSerialization/FastSerialization.csproj | 2 +- src/TraceEvent/DynamicTraceEventParser.cs | 5 ----- src/TraceEvent/TraceLog.cs | 14 +++++++++++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/FastSerialization/FastSerialization.csproj b/src/FastSerialization/FastSerialization.csproj index 173febbc5..def7b15fa 100644 --- a/src/FastSerialization/FastSerialization.csproj +++ b/src/FastSerialization/FastSerialization.csproj @@ -29,7 +29,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/TraceEvent/DynamicTraceEventParser.cs b/src/TraceEvent/DynamicTraceEventParser.cs index 28717f1db..7abd19540 100644 --- a/src/TraceEvent/DynamicTraceEventParser.cs +++ b/src/TraceEvent/DynamicTraceEventParser.cs @@ -2192,12 +2192,7 @@ public ProviderManifest(Stream manifestStream, int manifestLen = int.MaxValue) id = "Stream"; int len = Math.Min((int)(manifestStream.Length - manifestStream.Position), manifestLen); serializedManifest = new byte[len]; -#if NET - manifestStream.ReadExactly(serializedManifest, 0, len); -#else manifestStream.Read(serializedManifest, 0, len); -#endif - } /// /// Read a ProviderManifest from a file. diff --git a/src/TraceEvent/TraceLog.cs b/src/TraceEvent/TraceLog.cs index da3c8788d..9a58ec9d6 100644 --- a/src/TraceEvent/TraceLog.cs +++ b/src/TraceEvent/TraceLog.cs @@ -8539,9 +8539,17 @@ internal void AddUniversalDynamicSymbol(ProcessSymbolTraceData data, TraceProces { int index; TraceLoadedModule loadedModule = process.LoadedModules.FindModuleAndIndexContainingAddress(data.StartAddress, data.TimeStampQPC, out index); - module = process.LoadedModules.GetOrCreateManagedModule(loadedModule.ModuleID, data.TimeStampQPC); - moduleFileIndex = module.ModuleFile.ModuleFileIndex; - methodIndex = methods.NewMethod(data.Name, moduleFileIndex, (int)data.Id); + + // We should always get a loadedModule here because if we have a symbol, then we should have a module that contains it. + // Assert so that we can detect bugs here during development. + Debug.Assert(loadedModule != null, "loadedModule is missing for symbol"); + + if (loadedModule != null) + { + module = process.LoadedModules.GetOrCreateManagedModule(loadedModule.ModuleID, data.TimeStampQPC); + moduleFileIndex = module.ModuleFile.ModuleFileIndex; + methodIndex = methods.NewMethod(data.Name, moduleFileIndex, (int)data.Id); + } // When universal traces support re-use of address space, we'll need to support it here. } From e6c081ab5c037c4282bc08b5c5b30740eed98945 Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Fri, 25 Jul 2025 14:41:30 -0700 Subject: [PATCH 6/6] Revert unneeded changes --- src/TraceEvent/AutomatedAnalysis/AnalyzerResolver.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/TraceEvent/AutomatedAnalysis/AnalyzerResolver.cs b/src/TraceEvent/AutomatedAnalysis/AnalyzerResolver.cs index d2e839131..59f071be5 100644 --- a/src/TraceEvent/AutomatedAnalysis/AnalyzerResolver.cs +++ b/src/TraceEvent/AutomatedAnalysis/AnalyzerResolver.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Reflection; -using System.Diagnostics.CodeAnalysis; namespace Microsoft.Diagnostics.Tracing.AutomatedAnalysis {