diff --git a/Nuget.config b/nuget.config
similarity index 100%
rename from Nuget.config
rename to nuget.config
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 89b599aca..9eac1ccd7 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -18,6 +18,7 @@
+
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 59988c3cf..def7b15fa 100644
--- a/src/FastSerialization/FastSerialization.csproj
+++ b/src/FastSerialization/FastSerialization.csproj
@@ -2,7 +2,7 @@
- netstandard2.0
+ net9.0;netstandard2.0Microsoft.Diagnostics.FastSerializationMicrosoft.Diagnostics.FastSerializationtrue
@@ -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/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..7abd19540 100644
--- a/src/TraceEvent/DynamicTraceEventParser.cs
+++ b/src/TraceEvent/DynamicTraceEventParser.cs
@@ -6,21 +6,23 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using System.IO;
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
{
///
- /// 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 +33,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 +47,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 +57,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 +68,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 +78,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 +86,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 +95,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 +119,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 +143,7 @@ public void ReadAllManifests(string directoryPath)
}
///
- /// Override.
+ /// Override.
///
public override bool IsStatic { get { return false; } }
@@ -150,11 +152,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 +166,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 +180,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 +194,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 +203,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 +222,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 +235,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 +264,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 +407,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 +453,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 +463,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;
@@ -481,9 +483,9 @@ 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?
+ // Is this a struct field?
PayloadFetchClassInfo classInfo = payloadFetch.Class;
if (classInfo != null)
{
@@ -502,11 +504,11 @@ 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))
+ 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)
{
@@ -519,19 +521,19 @@ 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);
}
// 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);
- 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);
@@ -540,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]";
}
@@ -553,21 +555,21 @@ 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);
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:
{
@@ -601,7 +603,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 +611,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 +632,7 @@ private object GetPayloadValueAt(ref PayloadFetch payloadFetch, int offset, int
{
return (char)GetByteAt(offset);
}
- else
+ else
{
return (char)GetInt16At(offset);
}
@@ -673,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)
{
@@ -684,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)
{
@@ -714,8 +716,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 +869,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 +887,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 +901,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 +955,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 +981,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 +1012,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 +1037,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 +1058,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 +1085,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 +1105,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 +1173,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)
{
@@ -1211,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))
@@ -1226,7 +1228,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;
@@ -1244,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;
@@ -1258,7 +1260,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:
@@ -1267,60 +1269,60 @@ 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
}
}
// 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, FetchType fetchType, IDictionary map = null)
{
Offset = offset;
Size = size;
- Type = type;
+ Type = fetchType;
info = map;
}
///
/// 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)
@@ -1331,25 +1333,25 @@ 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;
}
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);
+ Type = FetchType.System_Byte;
Size = 1;
break;
case RegisteredTraceEventParser.TdhInputType.Int16:
@@ -1357,87 +1359,87 @@ 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);
- Size = DynamicTraceEventData.COUNTED_SIZE | DynamicTraceEventData.IS_ANSI + DynamicTraceEventData.ELEM_COUNT; // 16 Bit.
+ 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);
}
///
- /// 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,
@@ -1445,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;
@@ -1461,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;
}
@@ -1470,44 +1472,44 @@ 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.");
}
var ret = new PayloadFetch();
ret.Offset = offset;
ret.Size = 4;
- ret.info = new PayloadFetchArrayInfo()
+ ret.info = new PayloadFetchArrayInfo()
{
Element = element,
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.");
}
var ret = new PayloadFetch();
ret.Offset = offset;
ret.Size = 4;
- ret.info = new PayloadFetchArrayInfo()
+ ret.info = new PayloadFetchArrayInfo()
{
Element = element,
FixedCount = 0,
Kind = ArrayKind.DataLoc
};
- if(projectCharArrayAsString && ArrayElementCanProjectToString(element))
+ if (projectCharArrayAsString && ArrayElementCanProjectToString(element))
{
- ret.Type = typeof(string);
+ ret.Type = FetchType.System_String;
}
return ret;
}
@@ -1537,18 +1539,18 @@ 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;
}
///
- /// 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 +1561,195 @@ 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.
+ 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
@@ -1583,10 +1767,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 +1784,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
{
@@ -1619,7 +1803,7 @@ public Func> LazyMap
public override string ToString()
{
StringWriter sw = new StringWriter();
- sw.Write("");
@@ -1651,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;
@@ -1710,6 +1894,7 @@ public void ToStream(Serializer serializer)
serializer.Write((byte)0);
}
}
+
public void FromStream(Deserializer deserializer)
{
Offset = (ushort)deserializer.ReadInt16();
@@ -1717,7 +1902,7 @@ public void FromStream(Deserializer deserializer)
var typeName = deserializer.ReadString();
if (typeName != null)
{
- Type = Type.GetType(typeName);
+ Type = FetchTypeHelpers.Parse(typeName);
}
var fetchType = deserializer.ReadByte();
@@ -1742,7 +1927,7 @@ public void FromStream(Deserializer deserializer)
}
Map = map;
}
- else if (fetchType == 3) // Class
+ else if (fetchType == 3) // Class
{
PayloadFetchClassInfo classInfo = new PayloadFetchClassInfo();
@@ -1777,7 +1962,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 +2059,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;
@@ -1892,19 +2077,19 @@ 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;
}
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 +2142,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 +2178,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
{
@@ -2010,7 +2195,7 @@ public ProviderManifest(Stream manifestStream, int manifestLen = int.MaxValue)
manifestStream.Read(serializedManifest, 0, len);
}
///
- /// Read a ProviderManifest from a file.
+ /// Read a ProviderManifest from a file.
///
public ProviderManifest(string manifestFilePath)
{
@@ -2022,14 +2207,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 +2235,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 +2247,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 +2298,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 +2306,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 +2358,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 +2407,8 @@ internal void ParseProviderEvents(Func 2)
{
@@ -2409,13 +2594,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.
///
- 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;
}
@@ -2661,7 +2846,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