/* * This file is part of JOP, the Java Optimized Processor * see <http://www.jopdesign.com/> * * Copyright (C) 2011, Stefan Hepp (stefan@stefant.org). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ import com.jopdesign.common.misc.AppInfoError; import com.jopdesign.common.misc.Cmdline; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Just a small helper class to look up various symbols in a .jop link file. * @author Stefan Hepp (stefan@stefant.org) */ public class JopLookup { @SuppressWarnings({"PublicField"}) private static class ClassEntry { public String name; public int methodTable; public int cpIndex; public int size; public int superRef; public Map<Integer, String> methods; public Map<Integer, Integer> constmap; private ClassEntry(String name, int methodTable, int cpIndex) { this.name = name; this.methodTable = methodTable; this.cpIndex = cpIndex; this.methods = new HashMap<Integer, String>(); this.constmap = new HashMap<Integer, Integer>(); } } @SuppressWarnings({"PublicField"}) private static class MethodEntry { public String name, code; public List<Integer> positions; public Map<Integer, String> instructions; private MethodEntry(String name, String code) { this.name = name; this.code = code; positions = new ArrayList<Integer>(); instructions = new HashMap<Integer, String>(); } public String getClassName() { return name.substring(0, name.indexOf(':')); } public ClassEntry getClassEntry() { String clsName = getClassName(); for (ClassEntry cls : classes.values()) { if (cls.name.equals(clsName)) { return cls; } } return null; } public int getAddress() { ClassEntry entry = getClassEntry(); for (Integer address : entry.methods.keySet()) { if (entry.methods.get(address).equals(name.replace(':','.'))) { return address; } } return -1; } } private static Map<Integer, String> fields = new HashMap<Integer, String>(); private static Map<Integer, String> bytecode = new HashMap<Integer, String>(); private static Map<Integer, ClassEntry> classes = new HashMap<Integer, ClassEntry>(); private static Map<String, MethodEntry> methods = new HashMap<String, MethodEntry>(); public static void usage() { System.out.println("Usage: JopLookup [<jop-link-file>]"); System.out.println(); System.out.println("If no jop-file is specified, JopLookup tries to use java/target/dist/bin/*.jop"); System.out.println("This tool can be used to find methods, fields and instructions in the .jop file by address"); } public static void main(String[] args) { File jopFile = null; if (args.length == 0) { // try to find it .. File binDir = new File("java/target/dist/bin"); String[] files = binDir.list(); for (String file : files) { if (file.endsWith(".jop")) { jopFile = new File(binDir, file); } } if (jopFile == null) { usage(); System.exit(1); } else { System.out.println("Using link file "+jopFile); } } else if (args.length > 1) { usage(); System.exit(1); } else if ("--help".equals(args[0]) || "-h".equals(args[0])) { usage(); System.exit(0); } else { jopFile = new File(args[0]); } File txtFile = new File(jopFile.toString()+".txt"); File linkFile = new File(jopFile.toString()+".link.txt"); if (!jopFile.exists()) { System.out.println("Jopfile " + jopFile + " does not exist"); System.exit(1); } if (!txtFile.exists()) { System.out.println("Jop-txt-file " + txtFile + " does not exist"); System.exit(1); } if (!linkFile.exists()) { System.out.println("Linkfile " + linkFile + " does not exist"); System.exit(1); } readLinkFile(linkFile); readTxtFile(txtFile); Cmdline cmdline = new Cmdline(); while (true) { String[] qArgs = cmdline.readInput(); if (cmdline.isExit(qArgs)) { return; } try { processQuery(qArgs); } catch (NumberFormatException e) { System.out.println("Error: "+e); e.printStackTrace(); } } } private static void readLinkFile(File linkFile) { BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(linkFile)); ClassEntry current = null; while (true) { String entry = reader.readLine(); if (entry == null) { break; } String[] tab = entry.trim().split(" "); if (entry.startsWith("static ")) { fields.put(Integer.parseInt(tab[2]), tab[1]); } if (entry.startsWith("bytecode ")) { bytecode.put(Integer.parseInt(tab[2]), tab[1]); } if (entry.startsWith("class")) { current = new ClassEntry(tab[1], Integer.parseInt(tab[2]), Integer.parseInt(tab[3])); classes.put(current.methodTable-5, current); } assert(current != null); if (entry.startsWith(" -instSize ")) { current.size = Integer.parseInt(tab[1]); } if (entry.startsWith(" -super ")) { current.superRef = Integer.parseInt(tab[1]); } if (entry.startsWith(" -mtab ")) { current.methods.put(Integer.parseInt(tab[2]), tab[1]); } if (entry.startsWith(" -constmap ")) { current.constmap.put(Integer.parseInt(tab[2]), Integer.parseInt(tab[1])); } } } catch (FileNotFoundException e) { throw new AppInfoError(e); } catch (IOException e) { throw new AppInfoError(e); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { //noinspection ThrowFromFinallyBlock throw new AppInfoError(e); } } } } private static void readTxtFile(File txtFile) { BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(txtFile)); String lastline = null; MethodEntry current = null; while (true) { String entry = reader.readLine(); if (entry == null) { break; } if (entry.isEmpty()) { current = null; continue; } if (entry.startsWith("Code(")) { assert lastline != null; current = new MethodEntry(lastline, entry); methods.put(lastline.replace(':','.'), current); continue; } if (current != null) { int pos = entry.indexOf(':'); int loc = Integer.parseInt(entry.substring(0,pos)); current.positions.add(loc); current.instructions.put(loc, entry); continue; } lastline = entry; } } catch (FileNotFoundException e) { throw new AppInfoError(e); } catch (IOException e) { throw new AppInfoError(e); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { //noinspection ThrowFromFinallyBlock throw new AppInfoError(e); } } } } private static void processQuery(String[] args) throws NumberFormatException { if (args.length == 0 || "".equals(args[0]) || "help".equals(args[0])) { System.out.println("Usage: "); System.out.println("<address> Print link info at address"); System.out.println("c[lass] <address> Print class info of the class at the address"); System.out.println("m[ethod] <mp|fqn> Print instructions of method"); System.out.println("pc [<mp>] <pc> Print instruction at program counter"); return; } if ("class".equals(args[0]) || "c".equals(args[0])) { int loc = Integer.parseInt(args[1]); if (classes.containsKey(loc)) { ClassEntry entry = classes.get(loc); System.out.println(entry.name); System.out.println("super: "+entry.superRef+", cp: "+entry.cpIndex); for (Integer key : entry.methods.keySet()) { System.out.println("-mtab "+entry.methods.get(key)+" "+key); } } return; } if ("pc".equals(args[0])) { if (args.length == 2) { int pc = Integer.parseInt(args[1]); System.out.println(lookupPC(pc)); return; } else { int mp = Integer.parseInt(args[1]); int pc = Integer.parseInt(args[2]); String method = lookupLoc(mp); MethodEntry entry = methods.get(method); if (entry == null) { System.out.println("Unknown method"); return; } System.out.println(entry.instructions.get(pc)); return; } } if ("method".equals(args[0]) || "m".equals(args[0])) { MethodEntry entry = methods.get(args[1]); if (entry == null) { int mp = Integer.parseInt(args[1]); String method = bytecode.get(mp); if (method == null) { for (ClassEntry cls : classes.values()) { for (Integer key : cls.methods.keySet()) { if ( key == mp ) { method = cls.methods.get(key); break; } } } } entry = methods.get(method); } if (entry == null) { System.out.println("Unknown method"); return; } printMethod(entry); return; } int loc = Integer.parseInt(args[0]); System.out.println(lookupLoc(loc)); } private static String lookupLoc(int loc) { if (fields.containsKey(loc)) { return "Field " + fields.get(loc); } if (bytecode.containsKey(loc)) { return "Bytecode of "+bytecode.get(loc); } if (classes.containsKey(loc)) { ClassEntry entry = classes.get(loc); return "Class "+entry.name; } for (ClassEntry entry: classes.values()) { String clsName = "class "+ (entry.methodTable-5) + " (" +entry.name+")"; if (entry.cpIndex == loc) { return "Constantpool of "+clsName; } for (Integer key : entry.methods.keySet()) { if (key == loc) { String method = entry.methods.get(key); int bc = -1; for (Integer k : bytecode.keySet()) { if (bytecode.get(k).equals(method)) { bc = k; break; } } return "MTab entry " + method + ", code at "+bc+", in " +clsName; } } } return lookupPC(loc); } private static String lookupPC(int pc) { int offset = Integer.MAX_VALUE; String method = null; for (Integer start : bytecode.keySet()) { // find nearest method start if (start > pc || pc - start > offset) { continue; } offset = pc - start; method = bytecode.get(start); } MethodEntry entry = methods.get(method); if (entry == null) { return "Unknown"; } return method+" [address "+entry.getAddress()+", local pc: "+offset+"]: "+entry.instructions.get(offset); } private static void printMethod(MethodEntry entry) { System.out.println(entry.name); System.out.println(entry.code); for (Integer i : entry.positions) { System.out.println(entry.instructions.get(i)); } } }