Skip to content

Commit

Permalink
Add trace sessions and related unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
lilhoser committed Nov 3, 2023
1 parent 3f04968 commit a6b13e9
Show file tree
Hide file tree
Showing 28 changed files with 1,532 additions and 869 deletions.
526 changes: 526 additions & 0 deletions EnabledProvider.cs

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions EventParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ EventParserBuffers Buffers

m_ParsedEvent.Provider.Source =
m_Buffers.m_TraceEventInfo.Source.ToString();
m_ParsedEvent.Keywords = new List<string>();
var keywords = new List<string>();
if (m_Buffers.m_TraceEventInfo.KeywordsNameOffset > 0)
{
for (int offset = m_Buffers.m_TraceEventInfo.KeywordsNameOffset; ;)
Expand All @@ -321,10 +321,14 @@ EventParserBuffers Buffers
{
break;
}
m_ParsedEvent.Keywords.Add(str.Trim());
keywords.Add(str.Trim());
offset += Encoding.Unicode.GetByteCount(str) + 2;
}
}
if (keywords.Count > 0)
{
m_ParsedEvent.Keywords = string.Join(",", keywords);
}

if (m_Buffers.m_TraceEventInfo.TaskNameOffset > 0)
{
Expand Down Expand Up @@ -403,8 +407,10 @@ EventParserBuffers Buffers
{
case EventHeaderExtendedDataType.Sid:
{
m_ParsedEvent!.UserSid = new byte[size];
Marshal.Copy(data, m_ParsedEvent.UserSid, 0, size);
var binarySid = new byte[size];
Marshal.Copy(data, binarySid, 0, size);
var sid = new System.Security.Principal.SecurityIdentifier(binarySid, 0);
m_ParsedEvent!.UserSid = sid.ToString();
break;
}
case EventHeaderExtendedDataType.RelatedActivityId:
Expand Down
111 changes: 7 additions & 104 deletions FileTrace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,128 +17,31 @@ specific language governing permissions and limitations
under the License.
*/
using System.Diagnostics;
using System.Runtime.InteropServices;
using static etwlib.NativeTraceConsumer;
using static etwlib.NativeTraceControl;

namespace etwlib
{
using static TraceLogger;

public class FileTrace : IDisposable
public class FileTrace : TraceSession
{
private bool m_Disposed;
private readonly string m_EtlFileName;
private long m_PerfFreq;

public FileTrace(string EtlFileName)
{
m_EtlFileName = EtlFileName;
m_Disposed = false;
m_LogFile.LogFileName = EtlFileName;
m_LogFile.ProcessTraceMode = ProcessTraceMode.EventRecord;
}

~FileTrace()
{
Dispose(false);
}

protected virtual void Dispose(bool disposing)
{
if (m_Disposed)
{
return;
}

Trace(TraceLoggerType.FileTrace,
TraceEventType.Information,
"Disposing FileTrace");
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

public void Consume(
EventRecordCallback EventCallback,
BufferCallback BufferCallback
)
public override void Start()
{
var logfile = new EVENT_TRACE_LOGFILE()
{
LogFileName = m_EtlFileName,
EventCallback = EventCallback,
BufferCallback = BufferCallback,
ProcessTraceMode = ProcessTraceMode.EventRecord
};
var logFilePointer = Marshal.AllocHGlobal(Marshal.SizeOf(logfile));
Trace(TraceLoggerType.FileTrace,
TraceEventType.Information,
"Consuming events from ETL file " + m_EtlFileName);
Marshal.StructureToPtr(logfile, logFilePointer, false);
var handle = OpenTrace(logFilePointer);
//
// Marshal the structure back so we can get the PerfFreq
//
logfile = (EVENT_TRACE_LOGFILE)Marshal.PtrToStructure(
logFilePointer, typeof(EVENT_TRACE_LOGFILE))!;
Marshal.FreeHGlobal(logFilePointer);
logFilePointer = nint.Zero;
if (handle == -1 || handle == 0)
{
var error = "OpenTrace() returned an invalid handle: 0x" +
Marshal.GetLastWin32Error().ToString("X");
Trace(TraceLoggerType.FileTrace,
TraceEventType.Error,
error);
throw new Exception(error);
}

Trace(TraceLoggerType.FileTrace,
TraceEventType.Information,
"Trace session successfully opened, processing trace..");

try
{
//
// Update PerfFreq so event's timestamps can be parsed.
//
m_PerfFreq = logfile.LogfileHeader.PerfFreq.QuadPart;

//
// Blocking call. The caller's BufferCallback must return false to
// unblock this routine.
//
var status = ProcessTrace(
new long[1] { handle },
1,
nint.Zero,
nint.Zero);
if (status != ERROR_SUCCESS)
{
var error = "ProcessTrace() failed: 0x" + status.ToString("X") +
", GetLastError: " + Marshal.GetLastWin32Error().ToString("X");
Trace(TraceLoggerType.FileTrace,
TraceEventType.Error,
error);
throw new Exception(error);
}
Trace(TraceLoggerType.FileTrace,
TraceEventType.Information,
"Trace processing successfully completed.");
}
finally
{
CloseTrace(handle);
}
}

public
long
GetPerfFreq()
{
return m_PerfFreq;
TraceEventType.Information,
$"Starting FileTrace for log {m_LogFile.LogFileName}");
return;
}
}
}
84 changes: 84 additions & 0 deletions NativeDefinitions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public enum EventControlCode : uint
CaptureState = 2,
}

[Flags]
public enum EnableTraceProperties : uint
{
Sid = 0x1,
Expand Down Expand Up @@ -151,6 +152,46 @@ public enum WNodeClientContext : uint
CpuCycleCounter = 3
}

public enum TRACE_QUERY_INFO_CLASS : uint
{
TraceGuidQueryList = 0,
TraceGuidQueryInfo = 1,
TraceGuidQueryProcess = 2,
TraceStackTracingInfo = 3,
TraceSystemTraceEnableFlagsInfo = 4,
TraceSampledProfileIntervalInfo = 5,
TraceProfileSourceConfigInfo = 6,
TraceProfileSourceListInfo = 7,
TracePmcEventListInfo = 8,
TracePmcCounterListInfo = 9,
TraceSetDisallowList = 10,
TraceVersionInfo = 11,
TraceGroupQueryList = 12,
TraceGroupQueryInfo = 13,
TraceDisallowListQuery = 14,
TraceInfoReserved15,
TracePeriodicCaptureStateListInfo = 16,
TracePeriodicCaptureStateInfo = 17,
TraceProviderBinaryTracking = 18,
TraceMaxLoggersQuery = 19,
TraceLbrConfigurationInfo = 20,
TraceLbrEventListInfo = 21,
TraceMaxPmcCounterQuery = 22,
TraceStreamCount = 23,
TraceStackCachingInfo = 24,
TracePmcCounterOwners = 25,
TraceUnifiedStackCachingInfo = 26,
TracePmcSessionInformation = 27,
MaxTraceSetInfoClass = 28
}

[Flags]
public enum TRACE_PROVIDER_INSTANCE_FLAGS : uint
{
TRACE_PROVIDER_FLAG_LEGACY = 1,
TRACE_PROVIDER_FLAG_PRE_ENABLE = 2
}

//
// ETW filtering
//
Expand Down Expand Up @@ -321,6 +362,38 @@ public struct EVENT_FILTER_LEVEL_KW
public bool FilterIn;
}

//
// advapi structs for enumerating trace sessions
//
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct TRACE_GUID_INFO
{
public uint InstanceCount;
public uint Reserved;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct TRACE_PROVIDER_INSTANCE_INFO
{
public uint NextOffset;
public uint EnableCount;
public uint Pid;
public TRACE_PROVIDER_INSTANCE_FLAGS Flags;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct TRACE_ENABLE_INFO
{
public uint IsEnabled;
public EventTraceLevel Level;
public byte Reserved1;
public ushort LoggerId;
public EnableTraceProperties EnableProperty;
public uint Reserved2;
public ulong MatchAnyKeyword;
public ulong MatchAllKeyword;
}

#endregion

#region APIs
Expand Down Expand Up @@ -904,6 +977,16 @@ internal static extern uint TdhQueryProviderFieldInformation(
[In, Out] nint Buffer, // PPROVIDER_FIELD_INFOARRAY
[In, Out] ref uint BufferSize
);

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern uint EnumerateTraceGuidsEx(
[In] NativeTraceControl.TRACE_QUERY_INFO_CLASS InfoClass,
[In] nint InBuffer,
[In] uint InBufferSize,
[In, Out] nint OutBuffer,
[In] uint OutBufferSize,
[In, Out] ref uint ReturnLength
);
#endregion

public const int ERROR_SUCCESS = 0;
Expand All @@ -915,6 +998,7 @@ internal static extern uint TdhQueryProviderFieldInformation(
public const int ERROR_NOT_FOUND = 1168;
public const int ERROR_XML_PARSE_ERROR = 1465;
public const int ERROR_RESOURCE_TYPE_NOT_FOUND = 1813;
public const int ERROR_WMI_GUID_NOT_FOUND = 4200;
public const int ERROR_EMPTY = 4306;
public const int ERROR_EVT_INVALID_EVENT_DATA = 15005;
public const int ERROR_MUI_FILE_NOT_FOUND = 15100;
Expand Down
13 changes: 3 additions & 10 deletions ParsedEtwEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ public class ParsedEtwEvent
public uint ProcessId { get; set; }
public long ProcessStartKey { get; set; }
public uint ThreadId { get; set; }
public byte[]? UserSid { get; set; }
public string? UserSid { get; set; }
public Guid ActivityId { get; set; }
public DateTime Timestamp { get; set; }
public string Level { get; set; }
public ParsedEtwString? Channel { get; set; }
public List<string>? Keywords { get; set; }
public string? Keywords { get; set; }
public ulong KeywordsUlong { get; set; }
public ParsedEtwString? Task { get; set; }
public ParsedEtwString? Opcode { get; set; }
Expand Down Expand Up @@ -64,14 +64,7 @@ public override string ToString()
}
sb.AppendLine($"Level: {Level}");
sb.AppendLine($"Channel: {Channel}"); ;
if (Keywords != null)
{
sb.AppendLine("Keywords: ");
foreach (var kw in Keywords)
{
sb.AppendLine($" {kw}");
}
}
sb.AppendLine($"Keywords: {Keywords}");
sb.AppendLine($"Task: {Task}");
sb.AppendLine($"Opcode: {Opcode}");
sb.AppendLine($"Template data:");
Expand Down
6 changes: 1 addition & 5 deletions ParsedEtwManifestEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,7 @@ public override bool Equals(object? Other)
return false;
}
var field = Other as ParsedEtwManifestEvent;
if (field == null)
{
return false;
}
return Equals(Other);
return Equals(field);
}

public bool Equals(ParsedEtwManifestEvent? Other)
Expand Down
6 changes: 1 addition & 5 deletions ParsedEtwManifestField.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,7 @@ public override bool Equals(object? Other)
return false;
}
var field = Other as ParsedEtwManifestField;
if (field == null)
{
return false;
}
return Equals(Other);
return Equals(field);
}

public bool Equals(ParsedEtwManifestField? Other)
Expand Down
6 changes: 1 addition & 5 deletions ParsedEtwProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,7 @@ public override bool Equals(object? Other)
return false;
}
var field = Other as ParsedEtwProvider;
if (field == null)
{
return false;
}
return Equals(Other);
return Equals(field);
}

public bool Equals(ParsedEtwProvider? Other)
Expand Down
Loading

0 comments on commit a6b13e9

Please sign in to comment.