Skip to content
This repository has been archived by the owner on Apr 17, 2020. It is now read-only.

Classpath2 #2

Merged
merged 15 commits into from
May 16, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 202 additions & 17 deletions SymbolSort.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Dia2Lib;

// Most of the interop with msdia90.dll can be generated automatically
Expand Down Expand Up @@ -96,6 +97,33 @@ enum LocationType
LocTypeMax
}

// See https://msdn.microsoft.com/en-us/library/kszfk0fs.aspx
// for documentation of IDiaSymbol::get_undecoratedNameEx flags
[Flags]
enum IDiaSymbolUndecoratedNameExFlags : uint
{
UNDNAME_COMPLETE = 0x0000,
UNDNAME_NO_LEADING_UNDERSCORES = 0x0001,
UNDNAME_NO_MS_KEYWORDS = 0x0002,
UNDNAME_NO_FUNCTION_RETURNS = 0x0004,
UNDNAME_NO_ALLOCATION_MODEL = 0x0008,
UNDNAME_NO_ALLOCATION_LANGUAGE = 0x0010,
UNDNAME_RESERVED1 = 0x0020,
UNDNAME_RESERVED2 = 0x0040,
UNDNAME_NO_THISTYPE = 0x0060,
UNDNAME_NO_ACCESS_SPECIFIERS = 0x0080,
UNDNAME_NO_THROW_SIGNATURES = 0x0100,
UNDNAME_NO_MEMBER_TYPE = 0x0200,
UNDNAME_NO_RETURN_UDT_MODEL = 0x0400,
UNDNAME_32_BIT_DECODE = 0x0800,
UNDNAME_NAME_ONLY = 0x1000,
UNDNAME_TYPE_ONLY = 0x2000,
UNDNAME_HAVE_PARAMETERS = 0x4000,
UNDNAME_NO_ECSU = 0x8000,
UNDNAME_NO_IDENT_CHAR_CHECK = 0x10000,
UNDNAME_NO_PTR64 = 0x20000,
}

// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms680341(v=vs.85).aspx for
// more flag options and descriptions
[Flags]
Expand Down Expand Up @@ -176,8 +204,10 @@ class Symbol
public int rva_end;
public string name;
public string short_name;
public string raw_name; //decorated symbol name
public string source_filename;
public string section;
public string[] classpath;
public SymbolFlags flags = 0;
};

Expand Down Expand Up @@ -316,6 +346,125 @@ private static string ExtractGroupedSubstrings(string name, char groupBegin, cha
return ungrouped_name;
}

private class MainClassPathGetter
{
private static string[] splitByColons(string x)
{
return x.Split(new string[] { "::" }, StringSplitOptions.None);
}

private static string allowedSpecials = @"<=>,\[\]()!~^&|+\-*\/%" + "$";
private static string reClassWord = @"[\w " + allowedSpecials + "]+";
private static string reClassPath = String.Format(@"({0}::)*{0}", reClassWord);
private static Regex regexClassPath = new Regex("^" + reClassPath + "$", RegexOptions.Compiled);
private static string reLocalEnd = @".*"; //@"(`.+'|[\w]+(\$0)?)";
private static Regex regexFuncLocalVar = new Regex(String.Format(@"^`({0})'::`[\d]+'::{1}$", reClassPath, reLocalEnd));

//extracts the classpath (i.e. namespaces::classes::method) from undecorated symbol name
//input symbol must be stripped of return value and parameters (output from undname.exe with some flags)
//function may return null if symbol format is unknown
public static string[] Run(string short_name)
{
//(all string constaints)
if (short_name == "`string'")
return new string[] { short_name };

// Array<SharedPtr<Curve>, Allocator<SharedPtr<Curve>>>::Buffer::capacity"
// std::_Error_objects<int>::_System_object$initializer$
if (regexClassPath.IsMatch(short_name))
return splitByColons(short_name);

// std::bad_alloc `RTTI Type Descriptor'
const string rttiDescr = " `RTTI Type Descriptor'";
if (short_name.EndsWith(rttiDescr))
{
string[] res = Run(short_name.Substring(0, short_name.Length - rttiDescr.Length));
if (res == null) return null;
return res.Concat(new string[] { rttiDescr.Substring(1) }).ToArray();
}

// `CustomHeap::~CustomHeap'::`1'::dtor$0
// `std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Copy'::`1'::catch$0
// `CustomHeap<ShapeImpl>::instance'::`2'::some_var
// `HeapWrap < ShapeImpl >::Stub::get'::`7'::`local static guard'
// `HeapWrap<ShapeImpl>::Stub::get'::`7'::`dynamic atexit destructor for 'g_myHeap''
// `Mesh::projectPoints'::`13'::$S1
// `GroupElement::getNumElements'::`2'::MyCounter::`vftable'
if (regexFuncLocalVar.IsMatch(short_name))
return Run(regexFuncLocalVar.Match(short_name).Groups[1].Value);

// `dynamic initializer for 'BoundingBox::Invalid''
// `dynamic initializer for 'std::_Error_objects<int>::_System_object''
// std::`dynamic initializer for '_Tuple_alloc''
// UniquePtr<Service>::`scalar deleting destructor'
if (short_name.EndsWith("'"))
{
int backtickPos = short_name.IndexOf('`');
if (backtickPos >= 0)
{
string prefix = short_name.Substring(0, backtickPos);
string quoted = short_name.Substring(backtickPos + 1, short_name.Length - backtickPos - 2);
if (quoted.Count(c => c == '\'') == 2)
{
int left = quoted.IndexOf('\'');
int right = quoted.LastIndexOf('\'');
quoted = quoted.Substring(left + 1, right - left - 1);
}
string[] quotedWords = Run(quoted);
if (quotedWords == null)
return null;
string[] prefixWords = splitByColons(prefix);
return prefixWords.Take(prefixWords.Length - 1).Concat(quotedWords).ToArray();
}
}

//Console.WriteLine(short_name);
return null;
}
}

private static string[] RunUndName(string[] symbols, uint flags)
{
//write all symbols to temporary file
string inFName = Path.GetTempFileName();
var inWriter = new StreamWriter(inFName);
foreach (string s in symbols)
inWriter.WriteLine(s);
inWriter.Close();

//run undname.exe on the file
var arguments = String.Format("0x{0:X} {1}", flags, inFName);
var process = new Process
{
StartInfo = new ProcessStartInfo("undname", arguments)
{
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true,
}
};

try
{
process.Start();
string output = process.StandardOutput.ReadToEnd();
System.Threading.Thread.Sleep(50); //just to be sure
Debug.Assert(process.HasExited);

//postprocess output
string[] lines = output.Split(new string[] { "\r\n" }, StringSplitOptions.None);
if (lines.Length > symbols.Length && lines.Skip(symbols.Length).All(x => x == ""))
lines = lines.Take(symbols.Length).ToArray();
Debug.Assert(lines.Length == symbols.Length);

return lines;
}
catch
{
return new string[symbols.Length];
}
}

private static void ParseBsdSymbol(string line, out Symbol symbol)
{
symbol = null;
Expand Down Expand Up @@ -529,7 +678,7 @@ private static void ReadSymbolsFromCOMDAT(List<Symbol> symbols, string inFilenam
{
Regex regexName = new Regex(@"\n[ \t]*([^ \t]+)[ \t]+name");
Regex regexSize = new Regex(@"\n[ \t]*([A-Za-z0-9]+)[ \t]+size of raw data");
Regex regexCOMDAT = new Regex(@"\n[ \t]*COMDAT; sym= \""([^\n\""]+)");
Regex regexCOMDAT = new Regex(@"\n[ \t]*COMDAT; sym= \""([^\n\""]+)\"" \(([^\n()]+)\)");

StreamReader reader = new StreamReader(inFilename);

Expand Down Expand Up @@ -576,6 +725,7 @@ record += ln;

m = regexCOMDAT.Match(record);
symbol.name = m.Groups[1].Value;
symbol.raw_name = m.Groups[2].Value;
if (symbol.name != "")
{
symbol.rva_start = 0;
Expand Down Expand Up @@ -918,6 +1068,17 @@ private static void ReadSymbolsFromScope(IDiaSymbol parent, Dia2Lib.SymTagEnum t
symbol.short_name = diaSymbol.name == null ? "" : diaSymbol.name;
symbol.name = diaSymbol.undecoratedName == null ? symbol.short_name : diaSymbol.undecoratedName;
symbol.flags = additionalFlags;

//extract raw symbol name (see https://stackoverflow.com/a/19637731/556899)
string rawName;
IDiaSymbolUndecoratedNameExFlags flags = IDiaSymbolUndecoratedNameExFlags.UNDNAME_32_BIT_DECODE | IDiaSymbolUndecoratedNameExFlags.UNDNAME_TYPE_ONLY;
diaSymbol.get_undecoratedNameEx((uint)flags, out rawName);
if (rawName != null) {
//ignore trashy names like " ?? :: ?? ::Z::_NPEBDI_N * __ptr64 volatile "
if (!rawName.Contains(' '))
symbol.raw_name = rawName;
}

switch (type)
{
case SymTagEnum.SymTagData:
Expand Down Expand Up @@ -1294,38 +1455,33 @@ private static void WriteSourceStatsList(TextWriter writer, IEnumerable<KeyValue
writer.WriteLine();
}

private static void DumpFolderStats(TextWriter writer, List<Symbol> symbolList, int maxCount, bool showDifferences, List<RegexReplace> pathReplacements)
private static void DumpFolderStats(TextWriter writer, List<Symbol> symbolList, int maxCount, bool showDifferences, Func<Symbol, string[]> pathFunc, string separator)
{
Dictionary<string, SymbolSourceStats> sourceStats = new Dictionary<string, SymbolSourceStats>();
int childCount = 0;
foreach (Symbol s in symbolList)
{
string filename = s.source_filename;
filename = PerformRegexReplacements(filename, pathReplacements);
for ( ; ; )
string[] parts = pathFunc(s);
for (int k = parts.Length; k > 0; k--)
{
SymbolSourceStats stat;
if (sourceStats.ContainsKey(filename))
string currentname = String.Join(separator, parts, 0, k);
if (sourceStats.ContainsKey(currentname))
{
stat = sourceStats[filename];
stat = sourceStats[currentname];
}
else
{
stat = new SymbolSourceStats();
stat.count = 0;
stat.size = 0;
stat.singleChild = false;
sourceStats.Add(filename, stat);
sourceStats.Add(currentname, stat);
}
stat.count += s.count;
stat.size += s.size;
stat.singleChild = (stat.count == childCount);
childCount = stat.count;

int searchPos = filename.LastIndexOf('\\');
if (searchPos < 0)
break;
filename = filename.Remove(searchPos);
}
}

Expand All @@ -1336,9 +1492,6 @@ private static void DumpFolderStats(TextWriter writer, List<Symbol> symbolList,
return s1.Value.size - s0.Value.size;
} );

writer.WriteLine("File Contributions");
writer.WriteLine("--------------------------------------");

if (showDifferences)
{
writer.WriteLine("Increases in Size");
Expand Down Expand Up @@ -1859,7 +2012,39 @@ static void Main(string[] args)
}

Console.WriteLine("Building folder stats...");
DumpFolderStats(writer, symbols, maxCount, differenceFiles.Any(), pathReplacements);
writer.WriteLine("File Contributions");
writer.WriteLine("--------------------------------------");
DumpFolderStats(writer, symbols, maxCount, differenceFiles.Any(),
delegate(Symbol s)
{
string path = PerformRegexReplacements(s.source_filename, pathReplacements);
return path.Split("/\\".ToCharArray());
}, "\\");

Console.WriteLine("Building class and namespace stats...");
writer.WriteLine("Namespaces and classes Contributions");
writer.WriteLine("--------------------------------------");
string[] easyUndecoratedNames = RunUndName(symbols.Select(s => s.raw_name).ToArray(), 0x29FFF);
for (int i = 0; i < symbols.Count; i++)
{
string n = easyUndecoratedNames[i];
if (n != null)
{
n = ExtractGroupedSubstrings(n, '<', '>', "T");
string[] parts = MainClassPathGetter.Run(n);
symbols[i].classpath = parts;
}
}
DumpFolderStats(writer, symbols, maxCount, differenceFiles.Any(),
delegate (Symbol s)
{
string[] parts = s.classpath;
if (parts == null || parts.Length == 0) {
return new string[] { "[unknown]" };
}
parts = new string[] { "." }.Concat(parts.Take(parts.Length - 1)).ToArray();
return parts;
}, "::");

Console.WriteLine("Computing section stats...");
writer.WriteLine("Merged Sections / Types");
Expand Down