/* NameFinder.java -- Translates addresses to StackTraceElements. Copyright (C) 2002, 2004 Free Software Foundation, Inc. This file is part of libgcj. This software is copyrighted work licensed under the terms of the Libgcj License. Please consult the file "LIBGCJ_LICENSE" for details. */ package gnu.gcj.runtime; import gnu.classpath.Configuration; import gnu.gcj.RawData; import java.lang.StringBuffer; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.IOException; import java.io.File; import java.util.Collections; import java.util.Iterator; import java.util.HashMap; import java.util.HashSet; import java.util.Set; /** * Lookup addresses (represented as longs) to find source & line number info. * * The following system property is available (defaults to true): *
  • * * *
  • * * close() should be called to get rid of all resources. * * This class is used from java.lang.VMThrowable. * * @author Mark Wielaard (mark@klomp.org) */ public class NameFinder { /** * The name of the binary to look up. */ private String binaryFile; private String sourceFile; private int lineNum; private HashMap procs = new HashMap(); /** * Set of binary files that addr2line should not be called on. */ private static Set blacklist = Collections.synchronizedSet(new HashSet()); private static boolean use_addr2line = Boolean.valueOf(System.getProperty ("gnu.gcj.runtime.NameFinder.use_addr2line", "true") ).booleanValue(); private static boolean show_raw = Boolean.valueOf(System.getProperty ("gnu.gcj.runtime.NameFinder.show_raw", "false") ).booleanValue(); /** * Return true if raw addresses should be printed in stacktraces * when no line number information is available. */ static final boolean showRaw() { return show_raw; } private static final boolean remove_unknown = Boolean.valueOf(System.getProperty ("gnu.gcj.runtime.NameFinder.remove_unknown", "true") ).booleanValue(); /** * Return true if non-Java frames should be removed from stack * traces. */ static final boolean removeUnknown() { return remove_unknown; } class Addr2Line { Process proc; BufferedWriter out; BufferedReader in; Addr2Line(String binaryFile) { try { String[] exec = new String[] {"addr2line", "-e", binaryFile}; Runtime runtime = Runtime.getRuntime(); proc = runtime.exec(exec); } catch (IOException ioe) { } if (proc != null) { in = new BufferedReader(new InputStreamReader(proc.getInputStream())); out = new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())); } } void close() { try { if (in != null) in.close(); if (out != null) out.close(); } catch (IOException x) {} if (proc != null) proc.destroy(); } } /** * Create a new NameFinder to lookup names in binaryFile. Call close to get rid of any * resources created while using the lookup methods. */ public NameFinder() { } /** * Returns the source file name if lookup() was successful. If the source file could not be * determined, the binary name will be returned instead. */ public String getSourceFile() { String file; if (sourceFile != null) file = sourceFile; else file = binaryFile; return file.substring(file.lastIndexOf(File.separator) + 1, file.length()); } /** * If lookup() was successful, returns the line number of addr. If the line number could not * be determined, -1 is returned. */ public int getLineNum() { return lineNum; } public void lookup (String file, long addr) { binaryFile = file; sourceFile = null; lineNum = -1; if (! use_addr2line || blacklist.contains(file)) return; Addr2Line addr2line = (Addr2Line) procs.get(file); if (addr2line == null) { addr2line = new Addr2Line(file); procs.put(file, addr2line); } if (addr2line.proc == null) { use_addr2line = false; return; } String hexAddr = "0x" + Long.toHexString(addr); String name; try { addr2line.out.write(hexAddr); addr2line.out.newLine(); addr2line.out.flush(); String result = addr2line.in.readLine(); if (result.indexOf("??") == -1) { int split = result.lastIndexOf(':'); sourceFile = result.substring(0, split); String lineNumStr = result.substring(split + 1, result.length()); lineNum = Integer.parseInt (lineNumStr); } else { /* This binary has no debug info (assuming addr was valid). Avoid repeat addr2line invocations. */ blacklist.add(binaryFile); } } catch (IOException ioe) { addr2line = null; } catch (NumberFormatException x) { } } /** * Returns human readable method name and aguments given a method type * signature as known to the interpreter and a classname. */ public static String demangleInterpreterMethod(String m, String cn) { int index = 0; int length = m.length(); StringBuffer sb = new StringBuffer(length); // Figure out the real method name if (m.startsWith("")) { String className; int i = cn.lastIndexOf('.'); if (i < 0) className = cn; else className = cn.substring(i + 1); sb.append(className); index += 7; } else { int i = m.indexOf('('); if (i > 0) { sb.append(m.substring(0,i)); index += i + 1; } } sb.append('('); // Demangle the type arguments int arrayDepth = 0; char c = (index < length) ? m.charAt(index) : ')'; while (c != ')') { String type; switch(c) { case 'B': type = "byte"; break; case 'C': type = "char"; break; case 'D': type = "double"; break; case 'F': type = "float"; break; case 'I': type = "int"; break; case 'J': type = "long"; break; case 'S': type = "short"; break; case 'Z': type = "boolean"; break; case 'L': int i = m.indexOf(';', index); if (i > 0) { type = m.substring(index+1, i); index = i; } else type = ""; break; case '[': type = ""; arrayDepth++; break; default: type = "'; } sb.append(type); // Handle arrays if (c != '[' && arrayDepth > 0) while (arrayDepth > 0) { sb.append("[]"); arrayDepth--; } index++; char nc = (index < length) ? m.charAt(index) : ')'; if (c != '[' && nc != ')') sb.append(", "); c = nc; } // Stop. We are not interested in the return type. sb.append(')'); return sb.toString(); } /** * Releases all resources used by this NameFinder. */ public void close() { Iterator itr = procs.values().iterator(); while (itr.hasNext()) { Addr2Line proc = (Addr2Line) itr.next(); proc.close(); } } }