182 lines
6.8 KiB
C#
182 lines
6.8 KiB
C#
//
|
|
// Copyright (c) 2022-present, Trail of Bits, Inc.
|
|
// All rights reserved.
|
|
//
|
|
// This source code is licensed in accordance with the terms specified in
|
|
// the LICENSE file found in the root directory of this source tree.
|
|
//
|
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
using System;
|
|
using System.Threading.Tasks;
|
|
using System.Runtime.InteropServices;
|
|
using RpcInvestigator.Util;
|
|
using System.IO;
|
|
|
|
namespace UnitTests
|
|
{
|
|
using static NativeTraceConsumer;
|
|
using static NativeTraceControl;
|
|
|
|
[TestClass]
|
|
public class EtwTests
|
|
{
|
|
private readonly Guid s_RpcEtwGuid =
|
|
new Guid("6ad52b32-d609-4be9-ae07-ce8dae937e39");
|
|
private readonly int s_NumEvents = 1000;
|
|
|
|
[DataTestMethod]
|
|
[DataRow(true)]
|
|
[DataRow(false)]
|
|
public async Task RealTimeEtwTraceTest(bool EnableDebugEvents)
|
|
{
|
|
var env = new Environment();
|
|
env.Initialize();
|
|
int eventsConsumed = 0;
|
|
|
|
await Task.Run(() =>
|
|
{
|
|
//
|
|
// This trace will automatically terminate after a set number
|
|
// of ETW events have been successfully consumed/parsed.
|
|
//
|
|
using (var trace = new EtwRealTimeTrace(
|
|
"RPC Investigator Unit Test Real-Time Tracing",
|
|
s_RpcEtwGuid,
|
|
EnableDebugEvents))
|
|
using (var parserBuffers = new EtwEventParserBuffers())
|
|
{
|
|
try
|
|
{
|
|
trace.Start();
|
|
|
|
//
|
|
// Begin consuming events. This is a blocking call.
|
|
//
|
|
trace.Consume(new EventRecordCallback((Event) =>
|
|
{
|
|
var evt = (EVENT_RECORD)Marshal.PtrToStructure(
|
|
Event, typeof(EVENT_RECORD));
|
|
|
|
var parser = new EtwEventParser(
|
|
evt,
|
|
parserBuffers,
|
|
trace.GetPerfFreq());
|
|
try
|
|
{
|
|
var result = parser.Parse();
|
|
Assert.IsNotNull(result);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Assert.Fail("Unable to parse event: " + ex.Message);
|
|
}
|
|
eventsConsumed++;
|
|
}),
|
|
new BufferCallback((LogFile) =>
|
|
{
|
|
var logfile = new EVENT_TRACE_LOGFILE();
|
|
try
|
|
{
|
|
logfile = (EVENT_TRACE_LOGFILE)
|
|
Marshal.PtrToStructure(LogFile, typeof(EVENT_TRACE_LOGFILE));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Assert.Fail("Unable to cast EVENT_TRACE_LOGFILE: " + ex.Message);
|
|
}
|
|
if (eventsConsumed >= s_NumEvents)
|
|
{
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Assert.Fail("An exception occurred when consuming events: " + ex.Message);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
[TestMethod]
|
|
[DeploymentItem(@"..\..\data\trace_files\ms-rpc-capture-arrays.etl")]
|
|
public void FileEtwTraceTest()
|
|
{
|
|
var env = new Environment();
|
|
env.Initialize();
|
|
int eventsConsumed = 0;
|
|
|
|
//
|
|
// This trace will automatically terminate after a set number
|
|
// of ETW events have been successfully consumed/parsed.
|
|
//
|
|
var current = Directory.GetCurrentDirectory();
|
|
var target = Path.Combine(current, "ms-rpc-capture-arrays.etl");
|
|
|
|
using (var trace = new EtwFileTrace(target))
|
|
using (var parserBuffers = new EtwEventParserBuffers())
|
|
{
|
|
try
|
|
{
|
|
//
|
|
// Begin consuming events. This is a blocking call.
|
|
//
|
|
trace.Consume(new EventRecordCallback((Event) =>
|
|
{
|
|
var evt = (EVENT_RECORD)Marshal.PtrToStructure(
|
|
Event, typeof(EVENT_RECORD));
|
|
|
|
if (!evt.EventHeader.ProviderId.Equals(s_RpcEtwGuid))
|
|
{
|
|
//
|
|
// Skip events from other providers, because it might not
|
|
// be a builtin provider, in which case we'd need to go
|
|
// find the right manifest and that is overly complex for
|
|
// this unit test.
|
|
//
|
|
return;
|
|
}
|
|
var parser = new EtwEventParser(
|
|
evt,
|
|
parserBuffers,
|
|
trace.GetPerfFreq());
|
|
try
|
|
{
|
|
var result = parser.Parse();
|
|
Assert.IsNotNull(result, "Failed to parse the event");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Assert.Fail("Unable to parse event: " + ex.Message);
|
|
}
|
|
eventsConsumed++;
|
|
}),
|
|
new BufferCallback((LogFile) =>
|
|
{
|
|
var logfile = new EVENT_TRACE_LOGFILE();
|
|
try
|
|
{
|
|
logfile = (EVENT_TRACE_LOGFILE)
|
|
Marshal.PtrToStructure(LogFile, typeof(EVENT_TRACE_LOGFILE));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Assert.Fail("Unable to cast EVENT_TRACE_LOGFILE: " + ex.Message);
|
|
}
|
|
if (eventsConsumed >= s_NumEvents)
|
|
{
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Assert.Fail("An exception occurred when consuming events: " + ex.Message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|