Skip to content

Commit 4e094f5

Browse files
committed
Add initial assembly comparison
Example output: mono apkdiff.exe Xamarin.Forms_Performance_Integration-Signed-NewNDK-Default.apk Xamarin.Forms_Performance_Integration-Signed-NewNTR-Default.apk Size difference in bytes ([*1] apk1 only, [*2] apk2 only): + 42496 assemblies/Mono.Android.dll + Type Java.Nio.ByteBuffer + Type Java.Nio.ByteBuffer/__<$>_jni_marshal_methods + Type Java.Nio.ByteBufferInvoker + Type Java.Nio.Channels.FileChannel + Type Java.Nio.Channels.FileChannel/__<$>_jni_marshal_methods + Type Java.Nio.Channels.FileChannelInvoker + Type Java.Nio.Channels.FileChannelInvoker/__<$>_jni_marshal_methods + Type Java.Nio.Channels.IByteChannel + Type Java.Nio.Channels.IByteChannelInvoker + Type Java.Nio.Channels.IByteChannelInvoker/__<$>_jni_marshal_methods + Type Java.Nio.Channels.IChannel + Type Java.Nio.Channels.IChannelInvoker + Type Java.Nio.Channels.IChannelInvoker/__<$>_jni_marshal_methods + Type Java.Nio.Channels.IGatheringByteChannel + Type Java.Nio.Channels.IGatheringByteChannelInvoker + Type Java.Nio.Channels.IGatheringByteChannelInvoker/__<$>_jni_marshal_methods + Type Java.Nio.Channels.IInterruptibleChannel + Type Java.Nio.Channels.IInterruptibleChannelInvoker + Type Java.Nio.Channels.IInterruptibleChannelInvoker/__<$>_jni_marshal_methods + Type Java.Nio.Channels.IReadableByteChannel + Type Java.Nio.Channels.IReadableByteChannelInvoker + Type Java.Nio.Channels.IReadableByteChannelInvoker/__<$>_jni_marshal_methods + Type Java.Nio.Channels.IScatteringByteChannel + Type Java.Nio.Channels.IScatteringByteChannelInvoker + Type Java.Nio.Channels.IScatteringByteChannelInvoker/__<$>_jni_marshal_methods + Type Java.Nio.Channels.ISeekableByteChannel + Type Java.Nio.Channels.ISeekableByteChannelInvoker + Type Java.Nio.Channels.ISeekableByteChannelInvoker/__<$>_jni_marshal_methods + Type Java.Nio.Channels.IWritableByteChannel + Type Java.Nio.Channels.IWritableByteChannelInvoker + Type Java.Nio.Channels.IWritableByteChannelInvoker/__<$>_jni_marshal_methods + Type Java.Nio.Channels.Spi.AbstractInterruptibleChannel + Type Java.Nio.Channels.Spi.AbstractInterruptibleChannel/__<$>_jni_marshal_methods + Type Java.Nio.Channels.Spi.AbstractInterruptibleChannelInvoker + Type Java.IO.FileInputStream + Type Java.IO.FileInputStream/__<$>_jni_marshal_methods + 6264 lib/armeabi-v7a/libxamarin-app.so Symbol size difference + 3480 mj_typemap + 2784 jm_typemap + 6264 lib/x86/libxamarin-app.so Symbol size difference + 3480 mj_typemap + 2784 jm_typemap + 3584 assemblies/Java.Interop.dll - Type Java.Interop.JniRuntime/JniValueManager/<>c__DisplayClass38_0 - Type Java.Interop.JniRuntime/JniTypeManager/<CreateGetTypesEnumerator>d__18 - Type Java.Interop.JniRuntime/JniTypeManager/<CreateGetTypesForSimpleReferenceEnumerator>d__20 + Type Microsoft.CodeAnalysis.EmbeddedAttribute + Type System.Runtime.CompilerServices.NullableAttribute + Type System.Diagnostics.CodeAnalysis.AllowNullAttribute + Type System.Diagnostics.CodeAnalysis.MaybeNullAttribute + Type System.Diagnostics.CodeAnalysis.NotNullAttribute + Type System.Diagnostics.CodeAnalysis.NotNullWhenAttribute + Type System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute + Type Java.Interop.JniRuntime/JniValueManager/<>c__DisplayClass37_0 + Type Java.Interop.JniRuntime/JniTypeManager/<CreateGetTypesEnumerator>d__17 + Type Java.Interop.JniRuntime/JniTypeManager/<CreateGetTypesForSimpleReferenceEnumerator>d__19 - 512 assemblies/mscorlib.dll Summary: + 45056 Package size difference
1 parent 7efe903 commit 4e094f5

File tree

5 files changed

+162
-5
lines changed

5 files changed

+162
-5
lines changed

ApkDescription.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ void CompareEntries (KeyValuePair<string, FileProperties> entry, KeyValuePair<st
179179
var zipEntryOther = otherApk.Archive.ReadEntry (other.Key, true);
180180
zipEntryOther.Extract (tmpDirOther, other.Key);
181181

182-
diff.Compare (Path.Combine (tmpDir, entry.Key), Path.Combine (tmpDirOther, other.Key));
182+
diff.Compare (Path.Combine (tmpDir, entry.Key), Path.Combine (tmpDirOther, other.Key), " ");
183183

184184
Directory.Delete (tmpDir, true);
185185
Directory.Delete (tmpDirOther, true);

AssemblyDiff.cs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Reflection.Metadata;
5+
using System.Reflection.PortableExecutable;
6+
7+
namespace apkdiff {
8+
public class AssemblyDiff : EntryDiff {
9+
10+
MetadataReader reader1;
11+
MetadataReader reader2;
12+
13+
public AssemblyDiff ()
14+
{
15+
}
16+
17+
TypeDefinition GetTypeDefinition (MetadataReader reader, TypeDefinitionHandle handle, out string fullName)
18+
{
19+
var typeDef = reader.GetTypeDefinition (handle);
20+
var name = reader.GetString (typeDef.Name);
21+
var nspace = reader.GetString (typeDef.Namespace);
22+
23+
fullName = "";
24+
25+
if (typeDef.IsNested) {
26+
string declTypeFullName;
27+
28+
GetTypeDefinition (reader, typeDef.GetDeclaringType (), out declTypeFullName);
29+
fullName += declTypeFullName + "/";
30+
}
31+
32+
if (!string.IsNullOrEmpty (nspace))
33+
fullName += nspace + ".";
34+
35+
fullName += name;
36+
37+
return typeDef;
38+
}
39+
40+
public override void Compare (string file, string other, string padding)
41+
{
42+
var per1 = new PEReader (File.OpenRead (file));
43+
var per2 = new PEReader (File.OpenRead (other));
44+
45+
reader1 = per1.GetMetadataReader ();
46+
reader2 = per2.GetMetadataReader ();
47+
48+
var types1 = new Dictionary<string, TypeDefinition> (reader1.TypeDefinitions.Count);
49+
var types2 = new Dictionary<string, TypeDefinition> (reader2.TypeDefinitions.Count);
50+
51+
string fullName;
52+
53+
foreach (var typeHandle in reader1.TypeDefinitions) {
54+
var td = GetTypeDefinition (reader1, typeHandle, out fullName);
55+
types1 [fullName] = td;
56+
}
57+
58+
foreach (var typeHandle in reader2.TypeDefinitions) {
59+
var td = GetTypeDefinition (reader2, typeHandle, out fullName);
60+
types2 [fullName] = td;
61+
}
62+
63+
foreach (var pair in types1) {
64+
if (!types2.ContainsKey (pair.Key)) {
65+
Console.WriteLine ($"{padding} - Type {pair.Key}");
66+
} else
67+
CompareTypes (types1 [pair.Key], types2 [pair.Key], padding + " ");
68+
}
69+
70+
foreach (var pair in types2) {
71+
if (!types1.ContainsKey (pair.Key)) {
72+
Console.WriteLine ($"{padding} + Type {pair.Key}");
73+
}
74+
}
75+
}
76+
77+
string GetTypeName (MetadataReader reader, EntityHandle handle)
78+
{
79+
string fullName = "";
80+
81+
if (handle.Kind == HandleKind.TypeDefinition) {
82+
GetTypeDefinition (reader, (TypeDefinitionHandle) handle, out fullName);
83+
84+
return fullName;
85+
}
86+
87+
if (handle.Kind != HandleKind.TypeReference)
88+
return null;
89+
90+
var typeRef = reader.GetTypeReference ((TypeReferenceHandle)handle);
91+
var nspace = reader.GetString (typeRef.Namespace);
92+
93+
if (!string.IsNullOrEmpty (nspace))
94+
fullName += nspace + ".";
95+
96+
return fullName += reader.GetString (typeRef.Name);
97+
}
98+
99+
Dictionary<string, CustomAttribute> GetCustomAttributes (MetadataReader reader, CustomAttributeHandleCollection cac)
100+
{
101+
var dict = new Dictionary<string, CustomAttribute> ();
102+
103+
foreach (var handle in cac) {
104+
var ca = reader.GetCustomAttribute (handle);
105+
var cHandle = ca.Constructor;
106+
107+
string typeName;
108+
109+
switch (cHandle.Kind) {
110+
case HandleKind.MethodDefinition:
111+
var methodDef = reader.GetMethodDefinition ((MethodDefinitionHandle)cHandle);
112+
113+
typeName = GetTypeName (reader, methodDef.GetDeclaringType ());
114+
break;
115+
case HandleKind.MemberReference:
116+
var memberDef = reader.GetMemberReference ((MemberReferenceHandle)cHandle);
117+
118+
typeName = GetTypeName (reader, memberDef.Parent);
119+
break;
120+
default:
121+
Program.Warning ($"Unexpected EntityHandle kind: {cHandle.Kind}");
122+
continue;
123+
}
124+
125+
dict [typeName] = ca;
126+
}
127+
128+
return dict;
129+
}
130+
131+
void CompareCustomAttributes (CustomAttributeHandleCollection cac1, CustomAttributeHandleCollection cac2, string padding)
132+
{
133+
var dict1 = GetCustomAttributes (reader1, cac1);
134+
var dict2 = GetCustomAttributes (reader2, cac2);
135+
136+
foreach (var pair in dict1) {
137+
if (!dict2.ContainsKey (pair.Key)) {
138+
Console.WriteLine ($"{padding} - CustomAttribute {pair.Key}");
139+
}
140+
}
141+
142+
foreach (var pair in dict2) {
143+
if (!dict1.ContainsKey (pair.Key)) {
144+
Console.WriteLine ($"{padding} + CustomAttribute {pair.Key}");
145+
}
146+
}
147+
}
148+
149+
void CompareTypes (TypeDefinition type1, TypeDefinition type2, string padding)
150+
{
151+
CompareCustomAttributes (type1.GetCustomAttributes (), type2.GetCustomAttributes (), padding);
152+
}
153+
}
154+
}

EntryDiff.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ public abstract class EntryDiff {
55
public static EntryDiff ForExtension (string extension)
66
{
77
switch (extension) {
8+
case ".dll":
9+
return new AssemblyDiff ();
810
case ".so":
911
return new SharedLibraryDiff ();
1012
}
1113

1214
return null;
1315
}
1416

15-
public abstract void Compare (string file, string other);
17+
public abstract void Compare (string file, string other, string padding = null);
1618
}
1719
}

SharedLibraryDiff.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,12 @@ Dictionary<string, SymbolInfo> ParseNMOutput (string output)
6060
return symbols;
6161
}
6262

63-
public override void Compare (string file, string other)
63+
public override void Compare (string file, string other, string padding)
6464
{
6565
var sym1 = ParseNMOutput (RunNM (file));
6666
var sym2 = ParseNMOutput (RunNM (other));
6767

68-
Program.ColorWriteLine (" Symbol size difference", ConsoleColor.Yellow);
68+
Program.ColorWriteLine ($"{padding} Symbol size difference", ConsoleColor.Yellow);
6969

7070
var differences = new Dictionary<string, long> ();
7171
var singles = new HashSet<string> ();
@@ -96,7 +96,7 @@ public override void Compare (string file, string other)
9696

9797
var single = singles.Contains (diff.Key);
9898

99-
ApkDescription.PrintDifference (diff.Key, diff.Value, single ? $" *{(diff.Value > 0 ? 2 : 1)}" : null, " ");
99+
ApkDescription.PrintDifference (diff.Key, diff.Value, single ? $" *{(diff.Value > 0 ? 2 : 1)}" : null, padding);
100100
}
101101

102102
}

apkdiff.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<PackageReference Include="Mono.Options" Version="6.6.0.161" />
1212
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
1313
<PackageReference Include="Xamarin.LibZipSharp" Version="1.0.8" />
14+
<PackageReference Include="System.Reflection.Metadata" Version="1.8.0" />
1415
</ItemGroup>
1516
<Import Project="packages\Xamarin.LibZipSharp.1.0.8\build\Xamarin.LibZipSharp.targets" Condition="Exists('packages\Xamarin.LibZipSharp.1.0.8\build\Xamarin.LibZipSharp.targets')" />
1617
</Project>

0 commit comments

Comments
 (0)