Source/Tx.Bond/Internal/BinaryEtwParser.cs (152 lines of code) (raw):
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace Tx.Bond
{
using System;
using System.Collections.Generic;
using System.Reactive;
using Tx.Windows;
internal sealed class BinaryEtwParser
{
/// <summary>
/// The Provider used to log Binary objects in ETW stream.
/// </summary>
internal static readonly Guid EtwBinaryEventManifestProviderId = BinaryEventSource.Log.Guid;
private readonly List<byte[]> eventCache = new List<byte[]>();
private uint currentEventPackageId;
private readonly List<string> manifestCache = new List<string>();
private uint currentManifestPackageId;
private readonly Guid etwProviderId;
public BinaryEtwParser(Guid etwProviderId)
{
this.etwProviderId = etwProviderId;
}
internal Envelope Parse(EtwNativeEvent etwNativeEvent)
{
Envelope result = null;
if (etwNativeEvent.ProviderId == this.etwProviderId)
{
switch (etwNativeEvent.Id)
{
case 0:
result = ParseV0(etwNativeEvent);
break;
case 1:
result = ParseV1(etwNativeEvent);
break;
case 2:
result = this.ParseV2(etwNativeEvent);
break;
}
}
return result;
}
internal EventManifest ParseManifest(EtwNativeEvent etwNativeEvent)
{
EventManifest result = null;
if (etwNativeEvent.ProviderId == this.etwProviderId)
{
switch (etwNativeEvent.Id)
{
case 3:
result = ParseRegularManifest(etwNativeEvent);
break;
case 4:
result = this.ParseChunkedManifest(etwNativeEvent);
break;
}
}
return result;
}
private static Envelope ParseV0(EtwNativeEvent etwNativeEvent)
{
// Reading like this ensures that if exception is thrown, we know what failed
long occurenceFileTimeUtc = etwNativeEvent.ReadInt64();
long receiveFileTimeUtc = etwNativeEvent.ReadInt64();
string protocol = etwNativeEvent.ReadUnicodeString();
bool isValid = !(protocol ?? string.Empty).StartsWith(@"rceException: ", StringComparison.OrdinalIgnoreCase);
string source;
string manifestId;
uint eventPayloadLength;
byte[] eventPayload;
if (isValid)
{
source = etwNativeEvent.ReadUnicodeString();
manifestId = etwNativeEvent.ReadUnicodeString();
eventPayloadLength = etwNativeEvent.ReadUInt32(); // There is a side-effect being used here with the binary length.
// Payload overflow events also could be saved with event Id 0
eventPayload = (eventPayloadLength < 65000) ? etwNativeEvent.ReadBytes() : new byte[0];
}
else
{
protocol = string.Empty;
source = string.Empty;
manifestId = string.Empty;
eventPayloadLength = 0;
eventPayload = new byte[0];
}
return new Envelope(
occurenceFileTimeUtc >= 0
? DateTimeOffset.FromFileTime(occurenceFileTimeUtc)
: DateTimeOffset.MinValue,
receiveFileTimeUtc >= 0
? DateTimeOffset.FromFileTime(receiveFileTimeUtc)
: DateTimeOffset.MinValue,
protocol,
source,
manifestId,
eventPayload,
null);
}
private static EventManifest ParseRegularManifest(EtwNativeEvent etwNativeEvent)
{
// EventId is one. This is a log written using EventSource Byte Array logging support.
// Reading like this ensures that if exception is thrown, we know what failed
long occurenceFileTimeUtc = etwNativeEvent.ReadInt64();
long receiveFileTimeUtc = etwNativeEvent.ReadInt64();
string protocol = etwNativeEvent.ReadUnicodeString();
string source = etwNativeEvent.ReadUnicodeString();
string manifestId = etwNativeEvent.ReadUnicodeString();
string manifest = etwNativeEvent.ReadUnicodeString();
return new EventManifest
{
ActivityId = etwNativeEvent.ActivityId,
OccurenceFileTimeUtc = occurenceFileTimeUtc,
ReceiveFileTimeUtc = receiveFileTimeUtc,
Protocol = protocol,
Source = source,
ManifestId = manifestId,
Manifest = manifest,
};
}
private EventManifest ParseChunkedManifest(EtwNativeEvent etwNativeEvent)
{
uint packageId = etwNativeEvent.ReadUInt32();
long occurenceFileTimeUtc = etwNativeEvent.ReadInt64();
long receiveFileTimeUtc = etwNativeEvent.ReadInt64();
string protocol = etwNativeEvent.ReadUnicodeString();
string source = etwNativeEvent.ReadUnicodeString();
string manifestId = etwNativeEvent.ReadUnicodeString();
uint chunkCount = etwNativeEvent.ReadUInt32();
uint currentChunkNumber = etwNativeEvent.ReadUInt32();
string manifest = etwNativeEvent.ReadUnicodeString();
if (chunkCount == 1)
{
this.manifestCache.Clear();
return new EventManifest
{
ActivityId = etwNativeEvent.ActivityId,
OccurenceFileTimeUtc = occurenceFileTimeUtc,
ReceiveFileTimeUtc = receiveFileTimeUtc,
Protocol = protocol,
Source = source,
ManifestId = manifestId,
Manifest = manifest
};
}
else if (chunkCount > currentChunkNumber)
{
if (this.currentManifestPackageId != packageId || this.manifestCache.Count != currentChunkNumber)
{
this.manifestCache.Clear();
this.currentManifestPackageId = packageId;
}
this.manifestCache.Add(manifest);
if (chunkCount == (currentChunkNumber + 1))
{
string payload = string.Join("", this.manifestCache.ToArray());
this.manifestCache.Clear();
return new EventManifest
{
ActivityId = etwNativeEvent.ActivityId,
OccurenceFileTimeUtc = occurenceFileTimeUtc,
ReceiveFileTimeUtc = receiveFileTimeUtc,
Protocol = protocol,
Source = source,
ManifestId = manifestId,
Manifest = payload,
};
}
}
else
{
this.manifestCache.Clear();
}
return null;
}
private static Envelope ParseV1(EtwNativeEvent etwNativeEvent)
{
// EventId is one. This is a log written using EventSource Byte Array logging support.
// Reading like this ensures that if exception is thrown, we know what failed
long occurenceFileTimeUtc = etwNativeEvent.ReadInt64();
long receiveFileTimeUtc = etwNativeEvent.ReadInt64();
string protocol = etwNativeEvent.ReadUnicodeString();
string source = etwNativeEvent.ReadUnicodeString();
string manifestId = etwNativeEvent.ReadUnicodeString();
uint eventPayloadLength = etwNativeEvent.ReadUInt32(); // There is a side-effect being used here with the binary length.
etwNativeEvent.ReadInt32(); // EventSource based byte array writer actually stores the byte array length here. Skip 4 bytes to account for it.
byte[] eventPayload = etwNativeEvent.ReadBytes();
return new Envelope(
occurenceFileTimeUtc >= 0
? DateTimeOffset.FromFileTime(occurenceFileTimeUtc)
: DateTimeOffset.MinValue,
receiveFileTimeUtc >= 0
? DateTimeOffset.FromFileTime(receiveFileTimeUtc)
: DateTimeOffset.MinValue,
protocol,
source,
manifestId,
eventPayload,
null);
}
private Envelope ParseV2(EtwNativeEvent etwNativeEvent)
{
uint packageId = etwNativeEvent.ReadUInt32();
long occurenceFileTimeUtc = etwNativeEvent.ReadInt64();
long receiveFileTimeUtc = etwNativeEvent.ReadInt64();
string protocol = etwNativeEvent.ReadUnicodeString();
string source = etwNativeEvent.ReadUnicodeString();
string manifestId = etwNativeEvent.ReadUnicodeString();
uint chunkCount = etwNativeEvent.ReadUInt32();
uint currentChunkNumber = etwNativeEvent.ReadUInt32();
uint eventPayloadLength = etwNativeEvent.ReadUInt32(); // There is a side-effect being used here with the binary length.
etwNativeEvent.ReadUInt32();
byte[] eventPayload = etwNativeEvent.ReadBytes();
if (chunkCount == 1)
{
this.eventCache.Clear();
return new Envelope(
occurenceFileTimeUtc >= 0
? DateTimeOffset.FromFileTime(occurenceFileTimeUtc)
: DateTimeOffset.MinValue,
receiveFileTimeUtc >= 0
? DateTimeOffset.FromFileTime(receiveFileTimeUtc)
: DateTimeOffset.MinValue,
protocol,
source,
manifestId,
eventPayload,
null);
}
else if (chunkCount > currentChunkNumber)
{
if (this.currentEventPackageId != packageId || this.eventCache.Count != currentChunkNumber)
{
this.eventCache.Clear();
this.currentEventPackageId = packageId;
}
this.eventCache.Add(eventPayload);
if (chunkCount == (currentChunkNumber + 1))
{
var payload = ByteArrayHelper.Join(this.eventCache);
this.eventCache.Clear();
return new Envelope(
occurenceFileTimeUtc >= 0
? DateTimeOffset.FromFileTime(occurenceFileTimeUtc)
: DateTimeOffset.MinValue,
receiveFileTimeUtc >= 0
? DateTimeOffset.FromFileTime(receiveFileTimeUtc)
: DateTimeOffset.MinValue,
protocol,
source,
manifestId,
eventPayload,
null);
}
}
else
{
this.eventCache.Clear();
}
return null;
}
}
}