154 lines
4.2 KiB
C#
Raw Normal View History

// SpaceAnalyzer.cs
//
// Author:
// Radek Doulik <radou@microsoft.com>
//
// Copyright (C) 2018 Microsoft Corporation (http://www.microsoft.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using Mono.Cecil;
namespace LinkerAnalyzer.Core
{
public class SpaceAnalyzer
{
string assembliesDirectory;
List<AssemblyDefinition> assemblies = new List<AssemblyDefinition> ();
readonly Dictionary<string, int> sizes = new Dictionary<string, int> ();
public SpaceAnalyzer (string assembliesDirectory)
{
this.assembliesDirectory = assembliesDirectory;
}
static bool IsAssemblyBound (TypeDefinition td)
{
do {
if (td.IsNestedPrivate || td.IsNestedAssembly || td.IsNestedFamilyAndAssembly)
return true;
td = td.DeclaringType;
} while (td != null);
return false;
}
string GetTypeKey (TypeDefinition td)
{
if (td == null)
return "";
var addAssembly = td.IsNotPublic || IsAssemblyBound (td);
var addition = addAssembly ? $":{td.Module}" : "";
return $"{td.MetadataToken.TokenType}:{td}{addition}";
}
string GetKey (IMetadataTokenProvider provider)
{
return $"{provider.MetadataToken.TokenType}:{provider}";
}
int GetMethodSize (MethodDefinition method)
{
var key = GetKey (method);
if (sizes.ContainsKey (key))
return sizes [key];
var msize = method.Body.CodeSize;
msize += method.Name.Length;
sizes.Add (key, msize);
return msize;
}
int ProcessType (TypeDefinition type)
{
int size = type.Name.Length;
foreach (var field in type.Fields)
size += field.Name.Length;
foreach (var method in type.Methods) {
method.Resolve ();
if (method.Body != null)
size += GetMethodSize (method);
}
var resolvedType = type.Resolve ();
try {
sizes.Add (GetTypeKey (type), size);
} catch (ArgumentException e) {
Console.WriteLine ($"\nWarning: duplicated type '{type}' scope '{type.Scope}'\n{e}");
}
return size;
}
public void LoadAssemblies (bool verbose = true)
{
if (verbose) {
ConsoleDependencyGraph.Header ("Space analyzer");
Console.WriteLine ("Load assemblies from {0}", assembliesDirectory);
} else
Console.Write ("Analyzing assemblies .");
var resolver = new DefaultAssemblyResolver ();
resolver.AddSearchDirectory (assembliesDirectory);
int totalSize = 0;
foreach (var file in System.IO.Directory.GetFiles (assembliesDirectory, "*.dll")) {
if (verbose)
Console.WriteLine ($"Analyzing {file}");
else
Console.Write (".");
ReaderParameters parameters = new ReaderParameters () { ReadingMode = ReadingMode.Immediate, AssemblyResolver = resolver};
var assembly = AssemblyDefinition.ReadAssembly (file, parameters);
assemblies.Add (assembly);
foreach (var module in assembly.Modules) {
foreach (var type in module.Types) {
totalSize += ProcessType (type);
foreach (var child in type.NestedTypes)
totalSize += ProcessType (child);
}
}
}
if (verbose)
Console.WriteLine ("Total known size: {0}", totalSize);
else
System.Console.WriteLine ();
}
public int GetSize (VertexData vertex)
{
if (sizes.ContainsKey (vertex.value))
return sizes [vertex.value];
return 0;
}
}
}