-
Notifications
You must be signed in to change notification settings - Fork 0
/
DotStack.cs
121 lines (108 loc) · 4.64 KB
/
DotStack.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
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Diagnostics.Tracing;
using Microsoft.Diagnostics.Symbols;
using Microsoft.Diagnostics.NETCore.Client;
using Microsoft.Diagnostics.Tracing;
using Microsoft.Diagnostics.Tracing.Etlx;
using Microsoft.Diagnostics.Tracing.Stacks;
namespace Repro
{
class Program
{
static string[] _helpFlags = { "-h", "-?", "/h", "/?" };
static async Task<int> Main(string[] args)
{
if (args.Length == 0 || args.Length > 1 || (args.Length == 1 && _helpFlags.Contains(args[0])))
{
PrintUsage();
return -1;
}
var pid = int.Parse(args[0]);
var tempNetTraceFilename = Path.GetRandomFileName() + ".nettrace";
var tempEtlxFilename = "";
try
{
var client = new DiagnosticsClient(pid);
var providers = new List<EventPipeProvider>()
{
new EventPipeProvider("Microsoft-DotNETCore-SampleProfiler", EventLevel.Informational)
};
using (EventPipeSession session = client.StartEventPipeSession(providers))
using (FileStream fs = File.OpenWrite(tempNetTraceFilename))
{
Task copyTask = session.EventStream.CopyToAsync(fs);
await Task.Delay(10);
session.Stop();
await copyTask;
}
tempEtlxFilename = TraceLog.CreateFromEventPipeDataFile(tempNetTraceFilename);
using (var symbolReader = new SymbolReader(System.IO.TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath })
using (var eventLog = new TraceLog(tempEtlxFilename))
{
var stackSource = new MutableTraceEventStackSource(eventLog)
{
OnlyManagedCodeStacks = true
};
var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader);
computer.GenerateThreadTimeStacks(stackSource);
var samplesForThread = new Dictionary<string, List<StackSourceSample>>();
stackSource.ForEach((sample) =>
{
var stackIndex = sample.StackIndex;
while (!stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), false).StartsWith("Thread ("))
stackIndex = stackSource.GetCallerIndex(stackIndex);
var threadName = stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), false);
if (samplesForThread.TryGetValue(threadName, out var samples))
{
samples.Add(sample);
}
else
{
samplesForThread[threadName] = new List<StackSourceSample>() { sample };
}
});
foreach (var (threadName, samples) in samplesForThread)
{
#if DEBUG
Console.WriteLine($"Found {samples.Count} stacks for thread {threadName}");
#endif
PrintStack(threadName, samples[0], stackSource);
}
}
return 0;
}
catch (System.Exception e)
{
Console.WriteLine(e);
return -1;
}
finally
{
if (File.Exists(tempNetTraceFilename))
File.Delete(tempNetTraceFilename);
if (File.Exists(tempEtlxFilename))
File.Delete(tempEtlxFilename);
}
}
private static void PrintStack(string threadName, StackSourceSample stackSourceSample, StackSource stackSource)
{
Console.WriteLine($"Stack for {threadName}:");
var stackIndex = stackSourceSample.StackIndex;
while (!stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), verboseName: false).StartsWith("Thread ("))
{
Console.WriteLine($" {stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), verboseName: false)}");
stackIndex = stackSource.GetCallerIndex(stackIndex);
}
Console.WriteLine();
}
static void PrintUsage()
{
Console.WriteLine("DotStack Usage:");
Console.WriteLine("\tdotstack <pid>");
}
}
}