251 строка
6.2 KiB
C#
251 строка
6.2 KiB
C#
using Mono.Options;
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using static System.Console;
|
|
|
|
namespace jittimes {
|
|
class MainClass {
|
|
static bool Verbose;
|
|
static readonly string Name = "jit-times";
|
|
static readonly List<Regex> methodNameRegexes = new List<Regex> ();
|
|
|
|
enum SortKind {
|
|
Unsorted,
|
|
Self,
|
|
Total,
|
|
};
|
|
|
|
static SortKind sortKind = SortKind.Self;
|
|
|
|
static string ProcessArguments (string [] args)
|
|
{
|
|
var help = false;
|
|
var options = new OptionSet {
|
|
$"Usage: {Name}.exe OPTIONS* <methods-file>",
|
|
"",
|
|
"Processes JIT methods file from XA app with debug.mono.log=timing enabled",
|
|
"",
|
|
"Copyright 2019 Microsoft Corporation",
|
|
"",
|
|
"Options:",
|
|
{ "h|help|?",
|
|
"Show this message and exit",
|
|
v => help = v != null },
|
|
{ "m|method=",
|
|
"Process only methods whose names match {TYPE-REGEX}.",
|
|
v => methodNameRegexes.Add (new Regex (v)) },
|
|
{ "s",
|
|
"Sort by self times. (this is default ordering)",
|
|
v => sortKind = SortKind.Self },
|
|
{ "t",
|
|
"Sort by total times.",
|
|
v => sortKind = SortKind.Total },
|
|
{ "u",
|
|
"Show unsorted results.",
|
|
v => sortKind = SortKind.Unsorted },
|
|
{ "v|verbose",
|
|
"Output information about progress during the run of the tool",
|
|
v => Verbose = true },
|
|
};
|
|
|
|
var remaining = options.Parse (args);
|
|
|
|
if (help || args.Length < 1) {
|
|
options.WriteOptionDescriptions (Out);
|
|
|
|
Environment.Exit (0);
|
|
}
|
|
|
|
if (remaining.Count != 1) {
|
|
Error ("Please specify one <methods-file> to process.");
|
|
Environment.Exit (2);
|
|
}
|
|
|
|
return remaining [0];
|
|
}
|
|
|
|
static bool TryMatchTimeStamp (Regex regex, string line, out string method, out Timestamp time)
|
|
{
|
|
var match = regex.Match (line);
|
|
|
|
if (!match.Success || match.Groups.Count <= 2) {
|
|
method = null;
|
|
time = new Timestamp ();
|
|
return false;
|
|
}
|
|
|
|
method = match.Groups [1].Value;
|
|
time = Timestamp.Parse (match.Groups [2].Value);
|
|
|
|
return true;
|
|
}
|
|
|
|
static readonly Dictionary<string, MethodInfo> methods = new Dictionary<string, MethodInfo> ();
|
|
|
|
static bool ShouldPrint (string method)
|
|
{
|
|
if (methodNameRegexes.Count > 0) {
|
|
var success = false;
|
|
|
|
foreach (var filter in methodNameRegexes) {
|
|
var match = filter.Match (method);
|
|
success |= match.Success;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void PrintIndented (MethodInfo info, ref Timestamp sum, int level = 0)
|
|
{
|
|
if (!ShouldPrint (info.method))
|
|
return;
|
|
|
|
sum += info.self;
|
|
|
|
WriteLine ($"{info.total.Milliseconds (),10:F2} | {info.self.Milliseconds (),10:F2} | {"".PadRight (level * 2)}{info.method}");
|
|
|
|
if (info.inner == null)
|
|
return;
|
|
|
|
foreach (var im in info.inner)
|
|
PrintIndented (im, ref sum, level + 1);
|
|
}
|
|
|
|
static MethodInfo GetMethodInfo (string method)
|
|
{
|
|
MethodInfo info;
|
|
|
|
if (methods.TryGetValue (method, out info))
|
|
return info;
|
|
|
|
info = new MethodInfo { method = method };
|
|
methods [method] = info;
|
|
|
|
return info;
|
|
}
|
|
|
|
public static int Main (string [] args)
|
|
{
|
|
var path = ProcessArguments (args);
|
|
var file = File.OpenText (path);
|
|
|
|
var beginRegex = new Regex (@"^JIT method +begin: (.*) elapsed: (.*)$");
|
|
var doneRegex = new Regex (@"^JIT method +done: (.*) elapsed: (.*)$");
|
|
|
|
string line;
|
|
int lineNumber = 0;
|
|
|
|
var jitMethods = new Stack<MethodInfo> ();
|
|
string method;
|
|
Timestamp time;
|
|
Timestamp sum = new Timestamp ();
|
|
ColorWriteLine ("Total (ms) | Self (ms) | Method", ConsoleColor.Yellow);
|
|
|
|
while ((line = file.ReadLine ()) != null) {
|
|
lineNumber++;
|
|
|
|
if (TryMatchTimeStamp (beginRegex, line, out method, out time)) {
|
|
var info = GetMethodInfo (method);
|
|
|
|
if (info.state != MethodInfo.State.None && Verbose)
|
|
Warning ($"duplicit begin of `{info.method}`");
|
|
|
|
info.state = MethodInfo.State.Begin;
|
|
info.begin = time;
|
|
|
|
jitMethods.Push (info);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (TryMatchTimeStamp (doneRegex, line, out method, out time)) {
|
|
var info = GetMethodInfo (method);
|
|
|
|
if (info.state != MethodInfo.State.Begin) {
|
|
if (Verbose)
|
|
Warning ($"missing JIT begin for method {method}");
|
|
continue;
|
|
}
|
|
|
|
info.state = MethodInfo.State.Done;
|
|
info.done = time;
|
|
info.total = info.done - info.begin;
|
|
|
|
info.CalcSelfTime ();
|
|
|
|
jitMethods.Pop ();
|
|
|
|
if (jitMethods.Count > 0) {
|
|
var outerMethod = jitMethods.Peek ();
|
|
|
|
outerMethod.AddInner (info);
|
|
} else if (sortKind == SortKind.Unsorted)
|
|
PrintIndented (info, ref sum);
|
|
}
|
|
}
|
|
|
|
if (sortKind != SortKind.Unsorted)
|
|
sum = PrintSortedMethods ();
|
|
|
|
ColorWriteLine ($"Sum of self time (ms): {sum.Milliseconds ():F2}", ConsoleColor.Yellow);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static Timestamp PrintSortedMethods ()
|
|
{
|
|
IOrderedEnumerable<KeyValuePair<string, MethodInfo>> enumerable = null;
|
|
Timestamp sum = new Timestamp ();
|
|
|
|
switch (sortKind) {
|
|
case SortKind.Self:
|
|
enumerable = methods.OrderByDescending (p => p.Value.self);
|
|
break;
|
|
case SortKind.Total:
|
|
enumerable = methods.OrderByDescending (p => p.Value.total);
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException ("unknown sort order");
|
|
}
|
|
|
|
foreach (var pair in enumerable) {
|
|
if (!ShouldPrint (pair.Key))
|
|
continue;
|
|
|
|
var info = pair.Value;
|
|
WriteLine ($"{info.total.Milliseconds (),10:F2} | {info.self.Milliseconds (),10:F2} | {info.method}");
|
|
|
|
sum += info.self;
|
|
}
|
|
|
|
return sum;
|
|
}
|
|
|
|
static void ColorMessage (string message, ConsoleColor color, TextWriter writer, bool writeLine = true)
|
|
{
|
|
ForegroundColor = color;
|
|
|
|
if (writeLine)
|
|
writer.WriteLine (message);
|
|
else
|
|
writer.Write (message);
|
|
ResetColor ();
|
|
}
|
|
|
|
public static void ColorWriteLine (string message, ConsoleColor color) => ColorMessage (message, color, Out);
|
|
|
|
public static void ColorWrite (string message, ConsoleColor color) => ColorMessage (message, color, Out, false);
|
|
|
|
public static void Error (string message) => ColorMessage ($"Error: {Name}: {message}", ConsoleColor.Red, Console.Error);
|
|
|
|
public static void Warning (string message) => ColorMessage ($"Warning: {Name}: {message}", ConsoleColor.Yellow, Console.Error);
|
|
}
|
|
}
|