{"id":891,"date":"2014-01-01T13:00:07","date_gmt":"2014-01-01T13:00:07","guid":{"rendered":"http:\/\/writeasync.net\/?p=891"},"modified":"2013-12-20T06:44:09","modified_gmt":"2013-12-20T06:44:09","slug":"using-pla-dll-to-collect-etw-traces","status":"publish","type":"post","link":"http:\/\/writeasync.net\/?p=891","title":{"rendered":"Using PLA.dll to collect ETW traces"},"content":{"rendered":"<p>As demonstrated previously, PLA.dll allows you to collect <a href=\"http:\/\/writeasync.net\/?p=711\">perf counter logs<\/a>. It can also be used to collect <a href=\"http:\/\/msdn.microsoft.com\/en-us\/magazine\/cc163437.aspx\">ETW traces<\/a>.<\/p>\n<p>To collect traces, you need to add one or more trace data providers to your collector. Trace data providers are identified by a GUID known as the ETW provider ID. Windows comes with many built-in providers. In addition, third-party services and applications will often register their own providers on installation. To get a list of provider names and IDs currently registered on your system, open an elevated command prompt and run <code>logman.exe query providers<\/code>.<\/p>\n<p>To represent a provider, I added a <code>ProviderInfo<\/code> class to the sample. (Remember, all this code is available on the <a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/tree\/master\/projects\/PlaSample\">GitHub PlaSample project<\/a>.)<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic class ProviderInfo\r\n{\r\n    public ProviderInfo(Guid id)\r\n    {\r\n        this.Id = id;\r\n    }\r\n\r\n    public Guid Id { get; private set; }\r\n\r\n    public uint? Level { get; set; }\r\n\r\n    public ulong? KeywordsAny { get; set; }\r\n\r\n    public ulong? KeywordsAll { get; set; }\r\n}\r\n<\/pre>\n<p>The level indicates the highest trace level to collect; ETW typically uses levels 1-5 (critical, error, warning, informational, verbose). The keywords are 64-bit masks which can be used as custom filters (e.g. <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ff357720(v=vs.110).aspx\">the CLR defines many keywords<\/a> in its trace provider), where &#8220;any&#8221; means &#8220;match events with any of these bits set&#8221; and &#8220;all&#8221; means &#8220;<em>only<\/em> match events with all these bits.&#8221;<\/p>\n<p>For the trace collector, I have exposed many options to control the log file size, whether to use a circular buffer (i.e. keep overwriting the log file with newer events once it reaches a max size), how big the event buffer should be, and so on. (It should be noted that many of these work with perf counter collectors as well, but were omitted for simplicity.)<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic class TraceCollectorInfo\r\n{\r\n    public TraceCollectorInfo(string name)\r\n    {\r\n        this.Name = name;\r\n        this.Providers = new List&lt;ProviderInfo&gt;();\r\n    }\r\n\r\n    public string Name { get; private set; }\r\n\r\n    public string OutputPath { get; set; }\r\n\r\n    public uint? BufferSizeInKB { get; set; }\r\n\r\n    public bool? Circular { get; set; }\r\n\r\n    public TimeSpan? FlushTimer { get; set; }\r\n\r\n    public TimeSpan? MaxDuration { get; set; }\r\n\r\n    public uint? MaxSizeInMB { get; set; }\r\n\r\n    public uint? MaximumBuffers { get; set; }\r\n\r\n    public uint? MinimumBuffers { get; set; }\r\n\r\n    public bool? Segmented { get; set; }\r\n\r\n    public IList&lt;ProviderInfo&gt; Providers { get; private set; }\r\n}\r\n<\/pre>\n<p>The PLA code to create a trace collector is fairly similar to the perf counter collector code. The differences are mostly in the number of properties to set and the way the provider list is built up (note the helper methods to simplify the optional value processing):<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic ICollectorSet Create()\r\n{\r\n    \/\/ Data collector set is the core abstraction for collecting diagnostic data.\r\n    DataCollectorSet dcs = new DataCollectorSet();\r\n\r\n    \/\/ Set base folder to place output files.\r\n    dcs.RootPath = this.OutputPath;\r\n\r\n    \/\/ Create a data collector for traces.\r\n    ITraceDataCollector dc = (ITraceDataCollector)dcs.DataCollectors.CreateDataCollector(DataCollectorType.plaTrace);\r\n    dc.name = this.Name + &quot;_DC&quot;;\r\n    dcs.DataCollectors.Add(dc);\r\n\r\n    \/\/ Set output file name to use a pattern, as described at\r\n    \/\/ http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/aa372131(v=vs.85).aspx .\r\n    dc.FileName = this.Name;\r\n    dc.FileNameFormat = AutoPathFormat.plaPattern;\r\n    dc.FileNameFormatPattern = @&quot;\\-yyyyMMdd\\-HHmmss&quot;;\r\n\r\n    \/\/ Set various values (if present)\r\n    SetValue(dc, this.BufferSizeInKB, (d, v) =&gt; d.BufferSize = v);\r\n    SetValue(dc, this.Circular, (d, v) =&gt; d.LogCircular = v);\r\n    SetValue(dc, this.FlushTimer, (d, v) =&gt; d.FlushTimer = (uint)v.TotalSeconds);\r\n    SetValue(dc, this.MaximumBuffers, (d, v) =&gt; d.MaximumBuffers = v);\r\n    SetValue(dc, this.MinimumBuffers, (d, v) =&gt; d.MinimumBuffers = v);\r\n    SetValue(dc, this.MinimumBuffers, (d, v) =&gt; d.MinimumBuffers = v);\r\n\r\n    SetValue(dcs, this.MaxDuration, (d, v) =&gt; d.SegmentMaxDuration = (uint)v.TotalSeconds);\r\n    SetValue(dcs, this.MaxSizeInMB, (d, v) =&gt; d.SegmentMaxSize = (uint)v);\r\n    SetValue(dcs, this.Segmented, (d, v) =&gt; d.Segment = v);\r\n\r\n    \/\/ Build up the list of providers.\r\n    foreach (ProviderInfo providerInfo in this.Providers)\r\n    {\r\n        TraceDataProvider provider = dc.TraceDataProviders.CreateTraceDataProvider();\r\n        dc.TraceDataProviders.Add(provider);\r\n\r\n        provider.Guid = providerInfo.Id;\r\n        AddValue(provider.KeywordsAll, providerInfo.KeywordsAll);\r\n        AddValue(provider.KeywordsAny, providerInfo.KeywordsAny);\r\n        AddValue(provider.Level, providerInfo.Level);\r\n    }\r\n\r\n    \/\/ Now actually create (or modify existing) the set.\r\n    dcs.Commit(this.Name, null, CommitMode.plaCreateOrModify);\r\n\r\n    \/\/ Return an opaque wrapper with which the user can control the session.\r\n    return new CollectorSetWrapper(dcs);\r\n}\r\n\r\nprivate static void SetValue&lt;TClass, TValue&gt;(TClass c, TValue? v, Action&lt;TClass, TValue&gt; setValue) where TValue : struct\r\n{\r\n    if (v.HasValue)\r\n    {\r\n        setValue(c, v.Value);\r\n    }\r\n}\r\n\r\nprivate static void AddValue&lt;TValue&gt;(IValueMap map, TValue? v) where TValue : struct\r\n{\r\n    if (v.HasValue)\r\n    {\r\n        map.Add(v.Value);\r\n    }\r\n}\r\n<\/pre>\n<p>Now some sample code to show how to use the trace collector. This example collects kernel process traces for about five seconds, creating new files after every one second. Remember to run this elevated: <\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nprivate static void CreateTraceCollector()\r\n{\r\n    TraceCollectorInfo info = new TraceCollectorInfo(&quot;MyTraces&quot;);\r\n\r\n    info.BufferSizeInKB = 64;\r\n    info.Segmented = true;\r\n    info.MaxDuration = TimeSpan.FromSeconds(1.0d);\r\n    info.OutputPath = Environment.CurrentDirectory;\r\n\r\n    \/\/ Microsoft-Windows-Kernel-Process         \r\n    Guid providerId = new Guid(&quot;{22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716}&quot;);\r\n\r\n    info.Providers.Add(new ProviderInfo(providerId) { Level = 5 });\r\n\r\n    ICollectorSet collector = info.Create();\r\n    collector.Start();\r\n\r\n    Thread.Sleep(5000);\r\n\r\n    collector.Stop();\r\n\r\n    collector.Delete();\r\n}\r\n<\/pre>\n<p>After the app finishes, you should see files like the following:<br \/>\n<code>MyTraces-20140101-125602.etl<br \/>\nMyTraces-20140101-125603.etl<br \/>\n...<\/code><br \/>\nThese files can be decoded by Windows <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/cc766042.aspx\">Event Viewer<\/a> or with tools like <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/bb490959.aspx\">tracerpt.exe<\/a>.<\/p>\n<p>Using <code>tracerpt.exe [file.etl] -o [file.xml]<\/code>, you can dump the traces to a human-readable XML file. The file will contain a sequence of <code>&lt;Event&gt;<\/code> elements such as the following:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;Event xmlns=&quot;http:\/\/schemas.microsoft.com\/win\/2004\/08\/events\/event&quot;&gt;\r\n  &lt;System&gt;\r\n    &lt;Provider Name=&quot;Microsoft-Windows-Kernel-Process&quot; Guid=&quot;{22fb2cd6-0e7b-422b-a0c7-2fad1fd0e716}&quot; \/&gt;\r\n    &lt;EventID&gt;8&lt;\/EventID&gt;\r\n    &lt;!-- ... --&gt;\r\n  &lt;\/System&gt;\r\n  &lt;EventData&gt;\r\n    &lt;Data Name=&quot;ProcessID&quot;&gt;    1788&lt;\/Data&gt;\r\n    &lt;Data Name=&quot;ThreadID&quot;&gt;  411224&lt;\/Data&gt;\r\n    &lt;Data Name=&quot;OldPriority&quot;&gt;16&lt;\/Data&gt;\r\n    &lt;Data Name=&quot;NewPriority&quot;&gt;10&lt;\/Data&gt;\r\n  &lt;\/EventData&gt;\r\n  &lt;RenderingInfo Culture=&quot;en-US&quot;&gt;\r\n    &lt;Level&gt;Information &lt;\/Level&gt;\r\n    &lt;Opcode&gt;Info &lt;\/Opcode&gt;\r\n    &lt;Keywords&gt;\r\n      &lt;Keyword&gt;WINEVENT_KEYWORD_CPU_PRIORITY&lt;\/Keyword&gt;\r\n    &lt;\/Keywords&gt;\r\n    &lt;Task&gt;CpuPriorityChange&lt;\/Task&gt;\r\n    &lt;Message&gt;CPU priority of thread 411224 in process 1788 was changed from 16 to 10. &lt;\/Message&gt;\r\n    &lt;Channel&gt;Microsoft-Windows-Kernel-Process\/Analytic&lt;\/Channel&gt;\r\n    &lt;Provider&gt;Microsoft-Windows-Kernel-Process &lt;\/Provider&gt;\r\n  &lt;\/RenderingInfo&gt;\r\n&lt;\/Event&gt;\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>As demonstrated previously, PLA.dll allows you to collect perf counter logs. It can also be used to collect ETW traces. To collect traces, you need to add one or more trace data providers to your collector. Trace data providers are&hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[71],"tags":[],"class_list":["post-891","post","type-post","status-publish","format-standard","hentry","category-diagnostics"],"_links":{"self":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/891","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=891"}],"version-history":[{"count":0,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/891\/revisions"}],"wp:attachment":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=891"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=891"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=891"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}