blob: 0742af193bc4389708c72a9b5b0c9ca245b49e6f [file] [log] [blame]
/* 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):
* <li>
* <ul><code>gnu.gcj.runtime.NameFinder.use_addr2line</code>
* Whether an external process, addr2line, should be used to look up
* source file and line number info. Throwable.printStackTrace() will
* be faster if this property is set to 'false'.
* </ul>
* <ul><code>gnu.gcj.runtime.NameFinder.remove_unknown</code>
* Whether calls to unknown functions (class and method names are unknown)
* should be removed from the stack trace. </ul>
* </li>
*
* <code>close()</code> should be called to get rid of all resources.
*
* This class is used from <code>java.lang.VMThrowable</code>.
*
* @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 <code>lookup</code> 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("<init>"))
{
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 = "<unknown ref>";
break;
case '[':
type = "";
arrayDepth++;
break;
default:
type = "<unknown " + c + '>';
}
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();
}
}
}