Permalink
Browse files

Basic per-package method counting tool.

Iterates over the referenced method list in the .dex file and groups them by
the package that the class that defines it is in.
  • Loading branch information...
mihaip committed May 4, 2014
1 parent 6eb5da2 commit adbec36c03d2add90ab6b12e8e0de867391b378e
View
@@ -0,0 +1 @@
build/
View

This file was deleted.

Oops, something went wrong.
View
@@ -0,0 +1,31 @@
# dex-method-counts
Simple tool to output per-package method counts in an Android DEX executable
grouped by package, to aid in getting under the 65536 referenced method limit.
To use it:
$ ant jar
$ ./dex-method-counts path/to/App.apk # or .zip or .dex
You'll see output of the form:
Read in 65490 method IDs.
<root>: 65490
: 3
android: 6837
accessibilityservice: 6
bluetooth: 2
content: 248
pm: 22
res: 45
...
com: 53881
adjust: 283
sdk: 283
codebutler: 65
android_websockets: 65
...
The DEX file parsing is based on the `dexdeps` tool from
[the Android source tree](https://android.googlesource.com/platform/dalvik.git/+/master/tools/dexdeps/).
View

This file was deleted.

Oops, something went wrong.
View
@@ -0,0 +1,28 @@
<project>
<property name="src.dir" value="src"/>
<property name="build.dir" value="build"/>
<property name="classes.dir" value="${build.dir}/classes"/>
<property name="jar.dir" value="${build.dir}/jar"/>
<property name="main-class" value="info.persistent.dex.Main"/>
<target name="clean">
<delete dir="${build.dir}"/>
</target>
<target name="compile">
<mkdir dir="${classes.dir}"/>
<javac srcdir="${src.dir}"
destdir="${classes.dir}"
includeantruntime="false"/>
</target>
<target name="jar" depends="compile">
<mkdir dir="${jar.dir}"/>
<jar destfile="${jar.dir}/dex-method-counts.jar" basedir="${classes.dir}">
<manifest>
<attribute name="Main-Class" value="${main-class}"/>
</manifest>
</jar>
</target>
</project>
View
32 etc/dexdeps → dex-method-counts 100644 → 100755
@@ -14,36 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# Set up prog to be the path of this script, including following symlinks,
# and set up progdir to be the fully-qualified pathname of its directory.
prog="$0"
while [ -h "${prog}" ]; do
newProg=`/bin/ls -ld "${prog}"`
newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
if expr "x${newProg}" : 'x/' >/dev/null; then
prog="${newProg}"
else
progdir=`dirname "${prog}"`
prog="${progdir}/${newProg}"
fi
done
oldwd=`pwd`
progdir=`dirname "${prog}"`
cd "${progdir}"
progdir=`pwd`
prog="${progdir}"/`basename "${prog}"`
cd "${oldwd}"
jarfile=dexdeps.jar
libdir="$progdir"
if [ ! -r "$libdir/$jarfile" ]
then
libdir=`dirname "$progdir"`/tools/lib
fi
if [ ! -r "$libdir/$jarfile" ]
then
libdir=`dirname "$progdir"`/framework
fi
libdir="build/jar"
jarfile="dex-method-counts.jar"
if [ ! -r "$libdir/$jarfile" ]
then
echo `basename "$prog"`": can't find $jarfile"
View

This file was deleted.

Oops, something went wrong.
View

This file was deleted.

Oops, something went wrong.
@@ -419,6 +419,22 @@ private void addExternalMethodReferences(ClassRef[] sparseRefs) {
}
}
/**
* Returns the list of all method references.
*/
public MethodRef[] getMethodRefs() {
MethodRef[] methodRefs = new MethodRef[mMethodIds.length];
for (int i = 0; i < mMethodIds.length; i++) {
MethodIdItem methodId = mMethodIds[i];
methodRefs[i] = new MethodRef(
classNameFromTypeIndex(methodId.classIdx),
argArrayFromProtoIndex(methodId.protoIdx),
returnTypeFromProtoIndex(methodId.protoIdx),
mStrings[methodId.nameIdx]);
}
return methodRefs;
}
/*
* =======================================================================
@@ -244,7 +244,7 @@ private static void printXmlMethods(ClassRef cref) {
* Converts a single-character primitive type into its human-readable
* equivalent.
*/
static String primitiveTypeLabel(char typeChar) {
public static String primitiveTypeLabel(char typeChar) {
/* primitive type; substitute human-readable name in */
switch (typeChar) {
case 'B': return "byte";
@@ -269,7 +269,7 @@ static String primitiveTypeLabel(char typeChar) {
* example, "Ljava/lang/String;" becomes "java.lang.String", and
* "[I" becomes "int[].
*/
static String descriptorToDot(String descr) {
public static String descriptorToDot(String descr) {
int targetLen = descr.length();
int offset = 0;
int arrayDepth = 0;
@@ -317,7 +317,7 @@ static String descriptorToDot(String descr) {
/**
* Extracts the class name from a type descriptor.
*/
static String classNameOnly(String typeName) {
public static String classNameOnly(String typeName) {
String dotted = descriptorToDot(typeName);
int start = dotted.lastIndexOf(".");
@@ -332,7 +332,7 @@ static String classNameOnly(String typeName) {
* Extracts the package name from a type descriptor, and returns it in
* dotted form.
*/
static String packageNameOnly(String typeName) {
public static String packageNameOnly(String typeName) {
String dotted = descriptorToDot(typeName);
int end = dotted.lastIndexOf(".");

This file was deleted.

Oops, something went wrong.
@@ -0,0 +1,72 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package info.persistent.dex;
import com.android.dexdeps.DexData;
import com.android.dexdeps.MethodRef;
import com.android.dexdeps.Output;
import java.io.PrintStream;
import java.util.NavigableMap;
import java.util.TreeMap;
public class DexMethodCounts {
private static final PrintStream out = System.out;
private static class Node {
int count = 0;
NavigableMap<String, Node> children = new TreeMap<String, Node>();
void output(String indent) {
if (indent.length() == 0) {
out.println("<root>: " + count);
}
indent += " ";
for (String name : children.navigableKeySet()) {
Node child = children.get(name);
out.println(indent + name + ": " + child.count);
child.output(indent);
}
}
}
public static void generate(DexData dexData) {
MethodRef[] methodRefs = dexData.getMethodRefs();
out.println("Read in " + methodRefs.length + " method IDs.");
Node packageTree = new Node();
for (MethodRef methodRef : methodRefs) {
String packageName =
Output.packageNameOnly(methodRef.getDeclClassName());
String packageNamePieces[] = packageName.split("\\.");
Node packageNode = packageTree;
for (int i = 0; i < packageNamePieces.length; i++) {
packageNode.count++;
String name = packageNamePieces[i];
if (packageNode.children.containsKey(name)) {
packageNode = packageNode.children.get(name);
} else {
Node childPackageNode = new Node();
packageNode.children.put(name, childPackageNode);
packageNode = childPackageNode;
}
}
packageNode.count++;
}
packageTree.output("");
}
}
Oops, something went wrong.

0 comments on commit adbec36

Please sign in to comment.