Created
August 23, 2016 15:06
-
-
Save lowleveldesign/bf4cf3e02e06d15f446658dd84296074 to your computer and use it in GitHub Desktop.
Code which enumerates appdomains in a remote process using ETW
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.Diagnostics.Tracing; | |
using Microsoft.Diagnostics.Tracing.Session; | |
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Threading; | |
namespace ClrDacManaged | |
{ | |
class Program | |
{ | |
private const ushort DCStartInitEventId = 147; | |
private const ushort DCStartCompleteEventId = 145; | |
private const ushort AppDomainDCStartEventId = 157; | |
private readonly TimeSpan rundownTimeout = TimeSpan.FromSeconds(3); | |
private readonly Dictionary<int, ICollection<AppDomainInfo>> processAppDomainsMap = new Dictionary<int, ICollection<AppDomainInfo>>(); | |
private readonly int currentProcessId = Process.GetCurrentProcess().Id; | |
private DateTime lastTimeEventWasReceivedUtc; | |
private TraceEventSession session; | |
private bool completed; | |
public struct AppDomainInfo | |
{ | |
public long Id; | |
public string Name; | |
} | |
public void CollectAppDomainInfo() | |
{ | |
Debug.Assert(!completed); | |
using (session = new TraceEventSession("MusketeerEtwSession")) { | |
session.Source.Dynamic.All += ProcessTraceEvent; | |
session.EnableProvider("Microsoft-Windows-DotNETRuntimeRundown", TraceEventLevel.Verbose, | |
0x40L | // StartRundownKeyword | |
0x8L // LoaderRundownKeyword | |
); | |
ThreadPool.QueueUserWorkItem(WatchDog); | |
lastTimeEventWasReceivedUtc = DateTime.UtcNow; | |
session.Source.Process(); | |
} | |
completed = true; | |
} | |
void WatchDog(object o) | |
{ | |
while (true) { | |
Thread.Sleep(TimeSpan.FromSeconds(1)); | |
if (session.IsActive && DateTime.UtcNow.Subtract( | |
lastTimeEventWasReceivedUtc) > rundownTimeout) { | |
// rundown should be finished by now | |
session.Stop(); | |
break; | |
} | |
} | |
} | |
public void Stop() | |
{ | |
if (session != null && session.IsActive) { | |
session.Stop(); | |
} | |
} | |
void ProcessTraceEvent(TraceEvent traceEvent) | |
{ | |
lastTimeEventWasReceivedUtc = DateTime.UtcNow; | |
if (traceEvent.ProcessID == currentProcessId) { | |
return; | |
} | |
if ((ushort)traceEvent.ID == DCStartInitEventId) { | |
Debug.Assert(!processAppDomainsMap.ContainsKey(traceEvent.ProcessID)); | |
processAppDomainsMap.Add(traceEvent.ProcessID, new List<AppDomainInfo>()); | |
} else if ((ushort)traceEvent.ID == AppDomainDCStartEventId) { | |
Debug.Assert(processAppDomainsMap.ContainsKey(traceEvent.ProcessID)); | |
processAppDomainsMap[traceEvent.ProcessID].Add(new AppDomainInfo() { | |
Id = (long)traceEvent.PayloadByName("AppDomainID"), | |
Name = (string)traceEvent.PayloadByName("AppDomainName") | |
}); | |
} | |
} | |
public IEnumerable<int> GetManagedProcessIds() | |
{ | |
Debug.Assert(completed); | |
return processAppDomainsMap.Keys; | |
} | |
public IEnumerable<AppDomainInfo> GetAppDomainsForProcess(int pid) | |
{ | |
Debug.Assert(completed); | |
ICollection<AppDomainInfo> appDomains; | |
return processAppDomainsMap.TryGetValue(pid, out appDomains) ? appDomains : new AppDomainInfo[0]; | |
} | |
static void Main(string[] args) | |
{ | |
var p = new Program(); | |
Console.CancelKeyPress += (o, ev) => { | |
p.Stop(); | |
}; | |
p.CollectAppDomainInfo(); | |
foreach (var pid in p.GetManagedProcessIds()) { | |
Console.WriteLine("### Managed process: {0} ###", pid); | |
foreach (var appdomain in p.GetAppDomainsForProcess(pid)) { | |
Console.WriteLine("==> AppDomain: {0} ({1})", appdomain.Name, appdomain.Id); | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment