-
-
Save kbsriram/a55a360a3457f4d11d8d to your computer and use it in GitHub Desktop.
Generating android dependencies
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.kbsriram.depviz.jvm; | |
import java.io.DataInput; | |
import java.io.IOException; | |
import java.util.List; | |
import java.util.ArrayList; | |
public class CExtractUses | |
{ | |
public final static class Info | |
{ | |
private Info() | |
{ | |
m_classes = new ArrayList<String>(); | |
m_codesize = 0l; | |
} | |
public List<String> getClasses() | |
{ return m_classes; } | |
public long getCodeSize() | |
{ return m_codesize; } | |
private void addClass(String cl) | |
{ m_classes.add(cl); } | |
private void addCodeSize(long l) | |
{ m_codesize += l; } | |
private final List<String> m_classes; | |
private long m_codesize; | |
} | |
// Given an inputstrem (from a .class file) grab all the | |
// classnames that it uses. | |
public final static Info ofBytecode(DataInput inp) | |
throws IOException | |
{ | |
Info ret = new Info(); | |
// u4 magic | |
if (inp.readInt() != 0xcafebabe) { | |
throw new IOException("bad class file"); | |
} | |
// u2 minor | |
inp.readUnsignedShort(); | |
// u2 major | |
inp.readUnsignedShort(); | |
int cpcnt = inp.readUnsignedShort(); | |
String[] symbols = new String[cpcnt+1]; | |
List<Integer> cnames = new ArrayList<Integer>(); | |
int cpidx = 1; | |
while (cpidx <= (cpcnt-1)) { | |
cpidx += readPoolInfo(cpidx, inp, symbols, cnames); | |
} | |
for (int nameidx: cnames) { | |
ret.addClass(symbols[nameidx]); | |
} | |
// u2 access | |
inp.readUnsignedShort(); | |
// u2 this | |
inp.readUnsignedShort(); | |
// u2 super | |
inp.readUnsignedShort(); | |
// u2 ninterfaces | |
int niface = inp.readUnsignedShort(); | |
// u2 interfaces[ninterfaces] | |
for (int i=0; i<niface; i++) { | |
inp.readUnsignedShort(); | |
} | |
// u2 nfields; | |
int nfields = inp.readUnsignedShort(); | |
// System.out.println("nfields="+nfields); | |
for (int i=0; i<nfields; i++) { | |
readFieldInfo(inp, symbols); | |
} | |
// u2 nmethods; | |
int nmethods = inp.readUnsignedShort(); | |
// System.out.println("nmethods="+nmethods); | |
for (int i=0; i<nmethods; i++) { | |
readMethodInfo(inp, symbols, ret); | |
} | |
// System.out.println("nmethods="+nmethods); | |
return ret; | |
} | |
private final static void readFieldInfo(DataInput inp, String[] symbols) | |
throws IOException | |
{ | |
// u2 access | |
inp.readUnsignedShort(); | |
// u2 name | |
int name = inp.readUnsignedShort(); | |
// System.out.println(symbols[name]); | |
// u2 desc_idx | |
inp.readUnsignedShort(); | |
// u2 nattrs | |
int nattrs = inp.readUnsignedShort(); | |
for (int i=0; i<nattrs; i++) { | |
readAttributeInfo(inp, symbols, null); | |
} | |
} | |
private final static void readMethodInfo | |
(DataInput inp, String[] symbols, Info info) | |
throws IOException | |
{ | |
// u2 access | |
inp.readUnsignedShort(); | |
// u2 name | |
int name = inp.readUnsignedShort(); | |
// System.out.println("\t"+symbols[name]); | |
// u2 desc_idx | |
inp.readUnsignedShort(); | |
// u2 nattrs | |
int nattrs = inp.readUnsignedShort(); | |
for (int i=0; i<nattrs; i++) { | |
readAttributeInfo(inp, symbols, info); | |
} | |
} | |
private final static void readAttributeInfo | |
(DataInput inp, String[] symbols, Info info) | |
throws IOException | |
{ | |
// u2 name | |
int name = inp.readUnsignedShort(); | |
// u4 alen | |
long l = ((long)inp.readInt()) & (0xffffffffl); | |
if ((info != null) && ("Code".equals(symbols[name]))) { | |
readCodeAttribute(inp, symbols, info); | |
} | |
else { | |
// skip u1 info[alen] | |
for (long i=0; i<l; i++) { | |
inp.readByte(); | |
} | |
} | |
} | |
private final static void readCodeAttribute | |
(DataInput inp, String[] symbols, Info info) | |
throws IOException | |
{ | |
// u2 max-stack | |
inp.readUnsignedShort(); | |
// u2 max-locals | |
inp.readUnsignedShort(); | |
// u4 code-len | |
long l = ((long)inp.readInt()) & (0xffffffffl); | |
info.addCodeSize(l); | |
for (long i=0; i<l; i++) { | |
inp.readByte(); | |
} | |
// u2 nexceptions | |
int nex = inp.readUnsignedShort(); | |
for (int i=0; i<nex; i++) { | |
// u2 start | |
inp.readUnsignedShort(); | |
// u2 handler | |
inp.readUnsignedShort(); | |
// u2 catch-type | |
inp.readUnsignedShort(); | |
} | |
// u2 attrcnt | |
int nattr = inp.readUnsignedShort(); | |
for (int i=0; i<nattr; i++) { | |
readAttributeInfo(inp, symbols, info); | |
} | |
} | |
private final static int readPoolInfo | |
(int idx, DataInput inp, String[] symbols, List<Integer> cnames) | |
throws IOException | |
{ | |
int tag = inp.readUnsignedByte(); | |
// System.out.println("tag= "+CONSTANT_NAMES[tag]); | |
int ret = 1; | |
switch (tag) { | |
case CONSTANT_Class: | |
// u2 name-index | |
cnames.add(inp.readUnsignedShort()); | |
break; | |
case CONSTANT_Utf8: | |
symbols[idx] = inp.readUTF(); | |
break; | |
case CONSTANT_Methodref: | |
case CONSTANT_InterfaceMethodref: | |
case CONSTANT_NameAndType: | |
case CONSTANT_Fieldref: | |
// u2 classindex | |
inp.readUnsignedShort(); | |
// name_type_index | |
inp.readUnsignedShort(); | |
break; | |
case CONSTANT_Integer: | |
case CONSTANT_Float: | |
// u4 bytes | |
inp.readInt(); | |
break; | |
case CONSTANT_String: | |
// u2 sidx | |
inp.readUnsignedShort(); | |
break; | |
case CONSTANT_Long: | |
case CONSTANT_Double: | |
// u8 bytes | |
inp.readInt(); | |
inp.readInt(); | |
// also, occupies 2 slots in pool. | |
ret = 2; | |
break; | |
default: | |
throw new IOException("missing tag: "+tag); | |
} | |
return ret; | |
} | |
public final static byte CONSTANT_Utf8 = 1; | |
public final static byte CONSTANT_Integer = 3; | |
public final static byte CONSTANT_Float = 4; | |
public final static byte CONSTANT_Long = 5; | |
public final static byte CONSTANT_Double = 6; | |
public final static byte CONSTANT_Class = 7; | |
public final static byte CONSTANT_Fieldref = 9; | |
public final static byte CONSTANT_String = 8; | |
public final static byte CONSTANT_Methodref = 10; | |
public final static byte CONSTANT_InterfaceMethodref = 11; | |
public final static byte CONSTANT_NameAndType = 12; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.kbsriram.depviz; | |
import com.kbsriram.depviz.jvm.CExtractUses; | |
import java.io.BufferedInputStream; | |
import java.io.DataInputStream; | |
import java.io.DataInput; | |
import java.util.Enumeration; | |
import java.util.List; | |
import java.io.FileInputStream; | |
import java.io.IOException; | |
import java.util.zip.ZipFile; | |
import java.util.zip.ZipEntry; | |
public class CMain | |
{ | |
public final static void main(String args[]) | |
throws IOException | |
{ | |
for (int i=0; i<args.length; i++) { | |
dumpJar(args[i]); | |
} | |
} | |
private final static void dumpJar(String path) | |
throws IOException | |
{ | |
ZipFile zf = new ZipFile(path); | |
Enumeration<? extends ZipEntry> zentries = zf.entries(); | |
while (zentries.hasMoreElements()) { | |
ZipEntry ze = zentries.nextElement(); | |
String zname = ze.getName(); | |
if (!zname.endsWith(".class")) { continue; } | |
String cname = zname.substring(0, zname.length()-6); | |
DataInputStream din = | |
new DataInputStream | |
(new BufferedInputStream | |
(zf.getInputStream(ze))); | |
dumpClass(cname, din); | |
din.close(); | |
} | |
} | |
private final static void dumpClass | |
(String cname, DataInput inp) | |
throws IOException | |
{ | |
System.out.print(cname); | |
System.out.print(":"); | |
CExtractUses.Info info = CExtractUses.ofBytecode(inp); | |
System.out.print(info.getCodeSize()); | |
System.out.print(":"); | |
boolean first = true; | |
for (String use: info.getClasses()) { | |
if (first) { first = false; } | |
else { System.out.print(","); } | |
System.out.print(cleanup(use)); | |
} | |
System.out.println(); | |
} | |
private final static String cleanup(String cname) | |
{ | |
int idx = cname.lastIndexOf('['); | |
if (idx >= 0) { return cname.substring(idx+1); } | |
else { return cname; } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.kbsriram.depviz; | |
import java.io.BufferedReader; | |
import java.io.FileReader; | |
import java.io.IOException; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
public class CSvg | |
{ | |
public final static void main(String args[]) | |
throws IOException | |
{ | |
BufferedReader br = new BufferedReader | |
(new FileReader(args[0])); | |
String line; | |
Map<String,Integer> cls2pos = new HashMap<String,Integer>(); | |
Map<String,Long> cls2len = new HashMap<String,Long>(); | |
List<String> cls = new ArrayList<String>(); | |
Map<String,List<String>> in_arcs = | |
new HashMap<String,List<String>>(); | |
int pos = 0; | |
long maxlen = 0l; | |
double avlen = 0d; | |
while ((line = br.readLine()) != null) { | |
String kv[] = line.split(":"); | |
String cname = kv[0]; | |
if (!cname.startsWith("android/")) { continue; } | |
long clen = Long.parseLong(kv[1]); | |
String outs[] = kv[2].split(","); | |
cls.add(cname); | |
cls2pos.put(cname, pos); | |
cls2len.put(cname, clen); | |
if (maxlen < clen) { maxlen = clen; } | |
avlen += clen; | |
for (int i=0; i<outs.length; i++) { | |
String out = outs[i]; | |
if (out.equals(cname)) { continue; } | |
List<String> ins = in_arcs.get(out); | |
if (ins == null) { | |
ins = new ArrayList<String>(); | |
in_arcs.put(out, ins); | |
} | |
ins.add(cname); | |
} | |
pos++; | |
} | |
br.close(); | |
dumpHeader(); | |
int max = cls2pos.size(); | |
avlen /= max; | |
double logmaxlen = Math.log(1+maxlen); | |
for (String cname: cls) { | |
List<String> ins = in_arcs.get(cname); | |
int srcpos = cls2pos.get(cname); | |
System.out.println("<g>"); | |
drawClass(cname, cls2len.get(cname), avlen, srcpos, max); | |
if (ins != null) { | |
for (String target: ins) { | |
drawPath(cname, srcpos, target, cls2pos.get(target), max); | |
} | |
} | |
System.out.println("</g>"); | |
} | |
} | |
private final static void dumpHeader() | |
{ | |
System.out.println("<body>"); | |
System.out.println("<style>"); | |
System.out.println | |
("path { stroke:rgba(204,0,0,0.1); stroke-width:1; fill:none; }"); | |
System.out.println | |
("g:hover>path { stroke:rgba(0,150,204,1); }"); | |
System.out.println | |
("text { font-size: 3px; fill: none; stroke: none}"); | |
System.out.println | |
("g:hover>text { font-size: 18px; fill: rgba(0, 150, 204, 1);}"); | |
System.out.println | |
("line { stroke:rgba(204, 204, 204, 1); stroke-width: 1;}"); | |
System.out.println | |
("g:hover>line { stroke:rgba(102, 153, 0, 0.5); stroke-width: 4;}"); | |
System.out.println("</style>"); | |
System.out.println("<svg height='1000' width='1000'>"); | |
} | |
private final static void dumpFooter() | |
{ | |
System.out.println("</svg>"); | |
System.out.println("</body>"); | |
} | |
private final static String getPackage(String cn) | |
{ | |
String[] fields = cn.split("/"); | |
switch (fields.length) { | |
case 0: return cn; | |
case 1: return cn; | |
case 2: return "android"; | |
default: | |
return fields[1]; | |
} | |
} | |
private final static void drawPath | |
(String srcname, int src, String targname, Integer targI, int max) | |
{ | |
if (targI == null) { return; } | |
int[] srcpos = asPosition(src, max); | |
int targ = targI.intValue(); | |
int[] targpos = asPosition(targ, max); | |
int dx = srcpos[0]-targpos[0]; | |
int dy = srcpos[1]-targpos[1]; | |
double dist = Math.sqrt(dx*dx+dy*dy); | |
// dist is atmost the diameter of the circle. | |
int[] ctrlpos = asScaledPosition((src+targ)/2d, max, 1-dist/500f); | |
//drawClassName(srcname, 0l, src, max); | |
System.out.println | |
("<path d='M "+srcpos[0]+" "+srcpos[1]+ | |
" Q "+ctrlpos[0]+" "+ctrlpos[1]+ | |
" "+targpos[0]+" "+targpos[1]+"'/>"); | |
//drawClassName(targname, 0l, targ, max, "quiet"); | |
} | |
private final static void drawClass | |
(String cname, double clen, double avlen, int src, int max) | |
{ | |
int[] srcpos = asPosition(src, max); | |
int angle = (int) ((double)src/(double)max*360d + 0.5d); | |
//double normalized_len = Math.log(1+clen)/logmaxlen; | |
double normalized_len = clen/avlen; | |
int line_len = (int) (0.5d + normalized_len*20d); | |
System.out.print | |
("<text x='"+srcpos[0]+"' y='"+srcpos[1]+ | |
"' transform='rotate("+angle+" "+srcpos[0]+","+srcpos[1]+")'>"); | |
System.out.print(" "+cname); | |
System.out.println("</text>"); | |
System.out.println | |
("<line x1='"+srcpos[0]+"' y1='"+srcpos[1]+ | |
"' x2='"+(srcpos[0]+line_len)+"' y2='"+srcpos[1]+ | |
"' transform='rotate("+angle+" "+srcpos[0]+","+srcpos[1]+")'/>"); | |
} | |
private final static int[] asPosition(double pos, double max) | |
{ | |
double v = (pos/max)*Math.PI*2d; // radians, angle. | |
int[] ret = new int[2]; | |
ret[0] = (int) (500d+250d*Math.cos(v)+0.5d); | |
ret[1] = (int) (500d+250d*Math.sin(v)+0.5d); | |
return ret; | |
} | |
private final static int[] asScaledPosition | |
(double pos, double max, double frac) | |
{ | |
double v = (pos/max)*Math.PI*2d; // radians, angle. | |
int[] ret = new int[2]; | |
ret[0] = (int) (500d+250d*Math.cos(v)*frac+0.5d); | |
ret[1] = (int) (500d+250d*Math.sin(v)*frac+0.5d); | |
return ret; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
java -cp depviz.jar com.kbsriram.depviz.CMain ~/android-sdk-macosx/platforms/android-19/android.jar | grep '^android' > /tmp/deps.txt | |
sort /tmp/deps.txt > /tmp/sorted_deps.txt | |
java -cp depviz.jar com.kbsriram.depviz.CSvg /tmp/sorted_deps.txt > /tmp/android.html |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment