-
Notifications
You must be signed in to change notification settings - Fork 25
/
CallStackManager.cs
125 lines (112 loc) · 5.94 KB
/
CallStackManager.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
using Microsoft.Diagnostics.Symbols;
using Microsoft.Diagnostics.Tracing;
using Microsoft.Diagnostics.Tracing.Etlx;
using Microsoft.Diagnostics.Tracing.Parsers;
using Microsoft.Diagnostics.Tracing.Parsers.Clr;
using Microsoft.Diagnostics.Tracing.Session;
using realmon.Utilities;
using System.IO;
namespace realmon.CallStackResolution
{
public static class CallStackManager
{
private static SymbolReader m_symbolReader;
internal static TraceLogEventSource InitializeAndRegisterCallStacks(TraceEventSession traceEventSession,
IConsoleOut consoleOut,
int processId)
{
// TODO: Add configurations for the Symbol Reader Text Output path and the NT_SymbolPath.
string symbolPath = SymbolPath.SymbolPathFromEnvironment;
// If _NT_SYMBOL_PATH isn't set, force it to default to the one mentioned in the README of the project.
if (string.IsNullOrWhiteSpace(symbolPath))
{
symbolPath = @";SRV*C:\Symbols*https://msdl.microsoft.com/download/symbols;SRV*C:\Symbols*https://nuget.smbsrc.net;SRV*C:\Symbols*https://referencesource.microsoft.com/symbols";
}
m_symbolReader = new SymbolReader(TextWriter.Null, symbolPath);
m_symbolReader.SecurityCheck = path => true;
// Setup the additional providers needed for the enabling of the callstacks.
// Image Load and Process needed for Native Symbol Resolution.
// Requires Admin privileges, else this will throw.
traceEventSession.EnableKernelProvider(
flags:
KernelTraceEventParser.Keywords.ImageLoad |
KernelTraceEventParser.Keywords.Process,
stackCapture:
KernelTraceEventParser.Keywords.None
);
// Needed for the GC events + call stacks.
TraceEventProviderOptions options = new TraceEventProviderOptions() { ProcessIDFilter = new[] { processId }};
traceEventSession.EnableProvider(
ClrTraceEventParser.ProviderGuid,
TraceEventLevel.Verbose, // Verbose needed for call stacks.
(ulong)(ClrTraceEventParser.Keywords.GC |
ClrTraceEventParser.Keywords.Loader |
ClrTraceEventParser.Keywords.JittedMethodILToNativeMap |
ClrTraceEventParser.Keywords.JITSymbols |
ClrTraceEventParser.Keywords.JitTracing |
ClrTraceEventParser.Keywords.Jit |
ClrTraceEventParser.Keywords.Codesymbols |
ClrTraceEventParser.Keywords.Interop |
ClrTraceEventParser.Keywords.NGen |
ClrTraceEventParser.Keywords.MethodDiagnostic |
ClrTraceEventParser.Keywords.Stack), options);
// Needed for JIT Compile code that was already compiled.
traceEventSession.EnableProvider(ClrRundownTraceEventParser.ProviderGuid, TraceEventLevel.Verbose,
(ulong)(ClrTraceEventParser.Keywords.Jit |
ClrTraceEventParser.Keywords.Loader |
ClrTraceEventParser.Keywords.JittedMethodILToNativeMap |
ClrTraceEventParser.Keywords.JITSymbols |
ClrTraceEventParser.Keywords.Jit |
ClrTraceEventParser.Keywords.JitTracing |
ClrTraceEventParser.Keywords.Codesymbols |
ClrTraceEventParser.Keywords.Interop |
ClrTraceEventParser.Keywords.GC |
ClrTraceEventParser.Keywords.NGen |
ClrTraceEventParser.Keywords.MethodDiagnostic |
ClrTraceEventParser.Keywords.StartEnumeration), options);
// Subscribe to the requested events.
TraceLogEventSource traceLogEventSource = TraceLog.CreateFromTraceEventSession(traceEventSession);
traceLogEventSource.TraceLog.Clr.GCTriggered += (GCTriggeredTraceData traceEvent) =>
{
if (traceEvent.Reason == GCReason.Induced)
{
HandlePrintingCallStacks(traceEvent, consoleOut);
}
};
traceLogEventSource.TraceLog.Clr.GCAllocationTick += (GCAllocationTickTraceData traceEvent) =>
{
if (traceEvent.AllocationKind == GCAllocationKind.Large)
{
HandlePrintingCallStacks(traceEvent, consoleOut);
}
};
return traceLogEventSource;
}
internal static void HandlePrintingCallStacks(TraceEvent data, IConsoleOut consoleOut)
{
var callStack = data.CallStack();
if (callStack != null)
{
ResolveSymbols(callStack);
// consoleOut.PrintCallStack holds a write lock => don't resolve symbols, that can potentially be a long running task in the lock.
consoleOut.PrintCallStack(callStack, data.EventName);
}
}
internal static void ResolveSymbols(TraceCallStack callStack)
{
while (callStack != null)
{
var codeAddress = callStack.CodeAddress;
if (codeAddress.Method == null)
{
var moduleFile = codeAddress.ModuleFile;
if (moduleFile != null)
{
codeAddress.CodeAddresses.LookupSymbolsForModule(m_symbolReader, moduleFile);
}
}
callStack = callStack.Caller;
}
}
}
}