From 12bdd8ef4791a55c2091bee2ecabe85d0681d14d Mon Sep 17 00:00:00 2001 From: Radek Doulik Date: Wed, 16 Sep 2015 14:59:34 +0200 Subject: [PATCH] [linker-analyzer] intitial commit of the tool --- .../linker-analyzer/ConsoleDependencyGraph.cs | 146 ++++++++++++++++++ mcs/tools/linker-analyzer/Info.plist | 34 ++++ .../linker-analyzer/LinkerAnalyzer.csproj | 74 +++++++++ mcs/tools/linker-analyzer/LinkerAnalyzer.sln | 23 +++ .../LinkerAnalyzerCore/DependencyGraph.cs | 118 ++++++++++++++ .../LinkerAnalyzerCore.csproj | 61 ++++++++ .../Properties/AssemblyInfo.cs | 27 ++++ mcs/tools/linker-analyzer/Main.cs | 86 +++++++++++ mcs/tools/linker-analyzer/Makefile | 7 + .../linkeranalyzer.exe.sources | 5 + 10 files changed, 581 insertions(+) create mode 100644 mcs/tools/linker-analyzer/ConsoleDependencyGraph.cs create mode 100644 mcs/tools/linker-analyzer/Info.plist create mode 100644 mcs/tools/linker-analyzer/LinkerAnalyzer.csproj create mode 100644 mcs/tools/linker-analyzer/LinkerAnalyzer.sln create mode 100644 mcs/tools/linker-analyzer/LinkerAnalyzerCore/DependencyGraph.cs create mode 100644 mcs/tools/linker-analyzer/LinkerAnalyzerCore/LinkerAnalyzerCore.csproj create mode 100644 mcs/tools/linker-analyzer/LinkerAnalyzerCore/Properties/AssemblyInfo.cs create mode 100644 mcs/tools/linker-analyzer/Main.cs create mode 100644 mcs/tools/linker-analyzer/Makefile create mode 100644 mcs/tools/linker-analyzer/linkeranalyzer.exe.sources diff --git a/mcs/tools/linker-analyzer/ConsoleDependencyGraph.cs b/mcs/tools/linker-analyzer/ConsoleDependencyGraph.cs new file mode 100644 index 00000000000..a778ffd8598 --- /dev/null +++ b/mcs/tools/linker-analyzer/ConsoleDependencyGraph.cs @@ -0,0 +1,146 @@ +// +// ConsoleDependencyGraph.cs: text output related code for dependency graph +// +// Author: +// Radek Doulik (rodo@xamarin.com) +// +// Copyright 2015 Xamarin Inc (http://www.xamarin.com). +// +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using LinkerAnalyzer.Core; + +namespace LinkerAnalyzer +{ + public class ConsoleDependencyGraph : DependencyGraph + { + public bool Tree = false; + + public void ShowDependencies (string raw, List verticesList, string searchString) + { + VertexData vertex = Vertex (raw); + if (vertex == null) { + Regex regex = new Regex (searchString); + int count = 0; + + foreach (var v in verticesList) { + if (regex.Match (v.value) != Match.Empty) { + ShowDependencies (v); + count++; + } + } + + if (count == 0) + Console.WriteLine ("\nUnable to find vertex: {0}", raw); + else + Console.WriteLine ("\nFound {0} matches", count); + } else + ShowDependencies (vertex); + } + + public void ShowDependencies (VertexData vertex) + { + Header ("{0} dependencies", vertex.value); + if (vertex.parentIndexes == null) { + Console.WriteLine ("Root dependency"); + } else { + int i = 0; + foreach (int index in vertex.parentIndexes) { + Console.WriteLine ("Dependency #{0}", ++i); + Console.WriteLine ("\t{0}", vertex.value); + var childVertex = Vertex (index); + Console.WriteLine ("\t| {0}{1}", childVertex.value, childVertex.DepsCount); + while (childVertex.parentIndexes != null) { + childVertex = Vertex (childVertex.parentIndexes [0]); + Console.WriteLine ("\t| {0}{1}", childVertex.value, childVertex.DepsCount); + } + if (Tree) + break; + } + } + } + + public void ShowAllDependencies () + { + Header ("All dependencies"); + Console.WriteLine ("Types count: {0}", vertices.Count); + foreach (var vertex in vertices) + ShowDependencies (vertex); + } + + public void ShowTypesDependencies () + { + Header ("All types dependencies"); + Console.WriteLine ("Deps count: {0}", Types.Count); + foreach (var type in Types) + ShowDependencies (type); + } + + string Tabs (string key) + { + int count = Math.Max (1, 2 - key.Length / 8); + + if (count == 1) + return "\t"; + else + return "\t\t"; + } + + public void ShowStat (bool verbose = false) + { + Header ("Statistics"); + if (verbose) { + foreach (var key in counts.Keys) + Console.WriteLine ("Vertex type:\t{0}{1}count:{2}", key, Tabs (key), counts [key]); + } else { + Console.WriteLine ("Assemblies:\t{0}", counts ["Assembly"]); + Console.WriteLine ("Modules:\t{0}", counts ["Module"]); + Console.WriteLine ("Types:\t\t{0}", counts ["TypeDef"]); + Console.WriteLine ("Fields:\t\t{0}", counts ["Field"]); + Console.WriteLine ("Methods:\t{0}", counts ["Method"]); + } + + Console.WriteLine (); + Console.WriteLine ("Total vertices: {0}", vertices.Count); + } + + public void ShowRoots () + { + Header ("Root vertices"); + + int count = 0; + foreach (var vertex in vertices) { + if (vertex.parentIndexes == null) { + Console.WriteLine ("{0}", vertex.value); + count++; + } + } + + Console.WriteLine (); + Console.WriteLine ("Total root vertices: {0}", count); + } + + public void ShowRawDependencies (string raw) + { + Header ("Raw dependencies: '{0}'", raw); + ShowDependencies (raw, vertices, raw); + } + + public void ShowTypeDependencies (string raw) + { + Header ("Type dependencies: '{0}'", raw); + ShowDependencies ("TypeDef:" + raw, Types, raw); + } + + void Header (string header, params object[] values) + { + string formatted = string.Format (header, values); + Console.WriteLine (); + Console.Write ("--- {0} ", formatted); + for (int i=0; i< Math.Max (3, 64 - formatted.Length); i++) + Console.Write ('-'); + Console.WriteLine (); + } + } +} diff --git a/mcs/tools/linker-analyzer/Info.plist b/mcs/tools/linker-analyzer/Info.plist new file mode 100644 index 00000000000..19517b16df7 --- /dev/null +++ b/mcs/tools/linker-analyzer/Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDisplayName + LinkerAnalyzer + CFBundleDevelopmentRegion + en + CFBundleIconFile + + CFBundleIdentifier + com.your-company.LinkerAnalyzer + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + LinkerAnalyzer + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSMinimumSystemVersion + 10.8 + NSHumanReadableCopyright + rodo + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/mcs/tools/linker-analyzer/LinkerAnalyzer.csproj b/mcs/tools/linker-analyzer/LinkerAnalyzer.csproj new file mode 100644 index 00000000000..bf0c4536ddd --- /dev/null +++ b/mcs/tools/linker-analyzer/LinkerAnalyzer.csproj @@ -0,0 +1,74 @@ + + + + Debug + AnyCPU + {A3F8F2AB-B479-4A4A-A458-A89E7DC349F1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {B64F51B1-2D82-479C-B8F5-8875CDF06D95} + Exe + LinkerAnalyzer + Resources + LinkerAnalyzer + v2.0 + Xamarin.Mac + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + true + true + false + false + Mac Developer + false + false + + + full + true + bin\Release + prompt + 4 + false + SdkOnly + true + true + true + true + Developer ID Application + true + false + + + + + + + + + + + + + + + + + + Mono.Options\Options.cs + + + + + + {E1B8F203-5251-470E-A573-C9E79B60781B} + LinkerAnalyzerCore + + + \ No newline at end of file diff --git a/mcs/tools/linker-analyzer/LinkerAnalyzer.sln b/mcs/tools/linker-analyzer/LinkerAnalyzer.sln new file mode 100644 index 00000000000..1debe4c366c --- /dev/null +++ b/mcs/tools/linker-analyzer/LinkerAnalyzer.sln @@ -0,0 +1,23 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LinkerAnalyzer", "LinkerAnalyzer.csproj", "{B64F51B1-2D82-479C-B8F5-8875CDF06D95}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LinkerAnalyzerCore", "LinkerAnalyzerCore\LinkerAnalyzerCore.csproj", "{E1B8F203-5251-470E-A573-C9E79B60781B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B64F51B1-2D82-479C-B8F5-8875CDF06D95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B64F51B1-2D82-479C-B8F5-8875CDF06D95}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B64F51B1-2D82-479C-B8F5-8875CDF06D95}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B64F51B1-2D82-479C-B8F5-8875CDF06D95}.Release|Any CPU.Build.0 = Release|Any CPU + {E1B8F203-5251-470E-A573-C9E79B60781B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1B8F203-5251-470E-A573-C9E79B60781B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E1B8F203-5251-470E-A573-C9E79B60781B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1B8F203-5251-470E-A573-C9E79B60781B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/mcs/tools/linker-analyzer/LinkerAnalyzerCore/DependencyGraph.cs b/mcs/tools/linker-analyzer/LinkerAnalyzerCore/DependencyGraph.cs new file mode 100644 index 00000000000..e488209b109 --- /dev/null +++ b/mcs/tools/linker-analyzer/LinkerAnalyzerCore/DependencyGraph.cs @@ -0,0 +1,118 @@ +// +// DependencyGraph.cs: linker dependencies graph +// +// Author: +// Radek Doulik (rodo@xamarin.com) +// +// Copyright 2015 Xamarin Inc (http://www.xamarin.com). +// +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Xml; + +namespace LinkerAnalyzer.Core +{ + public class VertexData { + public string value; + public List parentIndexes; + public int index; + + public string DepsCount { + get { + if (parentIndexes == null || parentIndexes.Count < 1) + return ""; + return string.Format (" [{0} deps]", parentIndexes.Count); + } + } + }; + + public class DependencyGraph + { + protected List vertices = new List (); + public List Types = new List (); + Dictionary indexes = new Dictionary (); + protected Dictionary counts = new Dictionary (); + + public void Load (string filename) + { + Console.WriteLine ("Loading dependency tree from: {0}", filename); + + using (var fileStream = File.OpenRead (filename)) + using (var zipStream = new GZipStream (fileStream, CompressionMode.Decompress)) { + try { + Load (zipStream); + } catch (Exception) { + Console.WriteLine ("Unable to open and read the dependecies."); + Environment.Exit (1); + } + } + } + + void Load (GZipStream zipStream) { + using (XmlReader reader = XmlReader.Create (zipStream)) { + while (reader.Read ()) { + switch (reader.NodeType) { + case XmlNodeType.Element: + //Console.WriteLine (reader.Name); + if (reader.Name == "edge" && reader.IsStartElement ()) { + string b = reader.GetAttribute ("b"); + string e = reader.GetAttribute ("e"); + //Console.WriteLine ("edge value " + b + " --> " + e); + + if (e != b) { + VertexData begin = Vertex (b, true); + VertexData end = Vertex (e, true); + + if (end.parentIndexes == null) + end.parentIndexes = new List (); + if (!end.parentIndexes.Contains (begin.index)) { + end.parentIndexes.Add (begin.index); + //Console.WriteLine (" end parent index: {0}", end.parentIndexes); + } + } + } + break; + default: + //Console.WriteLine ("node: " + reader.NodeType); + break; + } + } + } + } + + public VertexData Vertex (string vertexName, bool create = false) + { + VertexData vertex; + + try { + vertex = vertices [indexes [vertexName]]; + } catch (KeyNotFoundException) { + if (create) { + int index = vertices.Count; + vertex = new VertexData () { value = vertexName, index = index }; + vertices.Add (vertex); + indexes.Add (vertexName, index); + string prefix = vertexName.Substring (0, vertexName.IndexOf (':')); + if (counts.ContainsKey (prefix)) + counts [prefix]++; + else + counts [prefix] = 1; + //Console.WriteLine ("prefix " + prefix + " count " + counts[prefix]); + if (prefix == "TypeDef") { + Types.Add (vertex); + } + } else + return null; + } + + return vertex; + } + + public VertexData Vertex (int index) + { + return vertices [index]; + } + } +} diff --git a/mcs/tools/linker-analyzer/LinkerAnalyzerCore/LinkerAnalyzerCore.csproj b/mcs/tools/linker-analyzer/LinkerAnalyzerCore/LinkerAnalyzerCore.csproj new file mode 100644 index 00000000000..64275160a35 --- /dev/null +++ b/mcs/tools/linker-analyzer/LinkerAnalyzerCore/LinkerAnalyzerCore.csproj @@ -0,0 +1,61 @@ + + + + Debug + AnyCPU + {A3F8F2AB-B479-4A4A-A458-A89E7DC349F1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {E1B8F203-5251-470E-A573-C9E79B60781B} + Library + LinkerAnalyzer.Core + Resources + LinkerAnalyzerCore + true + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + false + false + false + false + false + false + + + full + true + bin\Release + prompt + 4 + false + false + false + false + false + false + false + + + + + + + + ..\..\..\git\upstream\cecil\bin\net_4_0_Debug\Mono.Cecil.dll + + + + + + + + + + + \ No newline at end of file diff --git a/mcs/tools/linker-analyzer/LinkerAnalyzerCore/Properties/AssemblyInfo.cs b/mcs/tools/linker-analyzer/LinkerAnalyzerCore/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..b78aef59501 --- /dev/null +++ b/mcs/tools/linker-analyzer/LinkerAnalyzerCore/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle ("LinkerAnalyzerCore")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("rodo")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion ("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/mcs/tools/linker-analyzer/Main.cs b/mcs/tools/linker-analyzer/Main.cs new file mode 100644 index 00000000000..5c75ba5c863 --- /dev/null +++ b/mcs/tools/linker-analyzer/Main.cs @@ -0,0 +1,86 @@ +// +// Main.cs: Main program file of command line utility. +// +// Author: +// Radek Doulik (rodo@xamarin.com) +// +// Copyright 2015 Xamarin Inc (http://www.xamarin.com). +// +using System; +using Mono.Options; +using LinkerAnalyzer.Core; + +namespace LinkerAnalyzer +{ + static class MainClass + { + static void Main (string[] args) + { + bool showUsage = true; + bool showAllDeps = false; + bool showTypeDeps = false; + string typeName = null; + bool showRawDeps = false; + string rawName = null; + bool showRoots = false; + bool showSpaceUsage = false; + bool showStat = false; + bool showTypes = false; + bool reduceToTree = false; + bool verbose = false; + + var optionsParser = new OptionSet () { + { "a|alldeps", "show all dependencies", v => { showAllDeps = v != null; } }, + { "h|help", "show this message and exit.", v => showUsage = v != null }, + { "r|rawdeps=", "show raw vertex dependencies. Raw vertex VALUE is in the raw format written by linker to the dependency XML file. VALUE can be regular expression", v => { showRawDeps = v != null; rawName = v; } }, + { "roots", "show root dependencies.", v => showRoots = v != null }, + { "stat", "show statistic of loaded dependencies.", v => showStat = v != null }, + { "tree", "reduce the dependency graph to the tree.", v => reduceToTree = v != null }, + { "types", "show all types dependencies.", v => showTypes = v != null }, + { "t|typedeps=", "show type dependencies. The VALUE can be regular expression", v => { showTypeDeps = v != null; typeName = v; } }, + //{ "u|spaceusage", "show space analysis.", v => showSpaceUsage = v != null }, + { "v|verbose", "be more verbose. Enables stat and roots options.", v => verbose = v != null }, + }; + + if (args.Length > 0) { + showUsage = false; + optionsParser.Parse (args); + } + + if (showUsage) { + Console.WriteLine ("Usage:\n\n\tlinkeranalyzer [Options] \n\nOptions:\n"); + optionsParser.WriteOptionDescriptions (Console.Out); + Console.WriteLine (); + return; + } + + string dependencyFile = args [args.Length - 1]; + + ConsoleDependencyGraph deps = new ConsoleDependencyGraph () { Tree = reduceToTree }; + deps.Load (dependencyFile); + + if (showSpaceUsage) { +// SpaceAnalyzer sa = new SpaceAnalyzer (System.IO.Path.GetDirectoryName (dependencyFile)); +// sa.LoadAssemblies (verbose); + } + + if (verbose) { + showStat = true; + showRoots = true; + } + + if (showStat) + deps.ShowStat (verbose); + if (showRoots) + deps.ShowRoots (); + if (showRawDeps) + deps.ShowRawDependencies (rawName); + if (showTypeDeps) + deps.ShowTypeDependencies (typeName); + if (showAllDeps) + deps.ShowAllDependencies (); + else if (showTypes) + deps.ShowTypesDependencies (); + } + } +} diff --git a/mcs/tools/linker-analyzer/Makefile b/mcs/tools/linker-analyzer/Makefile new file mode 100644 index 00000000000..c8fd5ff3e33 --- /dev/null +++ b/mcs/tools/linker-analyzer/Makefile @@ -0,0 +1,7 @@ +thisdir = tools/linker-analyzer +SUBDIRS = +include ../../build/rules.make + +PROGRAM = linkeranalyzer.exe + +include ../../build/executable.make diff --git a/mcs/tools/linker-analyzer/linkeranalyzer.exe.sources b/mcs/tools/linker-analyzer/linkeranalyzer.exe.sources new file mode 100644 index 00000000000..1da8185832b --- /dev/null +++ b/mcs/tools/linker-analyzer/linkeranalyzer.exe.sources @@ -0,0 +1,5 @@ +ConsoleDependencyGraph.cs +Main.cs + +LinkerAnalyzerCore/DependencyGraph.cs +../../class/Mono.Options/Mono.Options/Options.cs -- 2.25.1