Makefile.am (libgcj_la_SOURCES): Remove name-finder.cc.
* Makefile.am (libgcj_la_SOURCES): Remove name-finder.cc.
(core_java_source_files): Add VMThrowable.java and NameFinder.java
(nat_source_files): Remove natThrowable.cc, add natVMThrowable.cc
and natNameFinder.cc.
* Makefile.in: Regenerate.
* prims.cc: Use trace_enabled from VMThrowable.
* name-finder.cc: Removed.
* gcj/javaprims.h: Add class VMThrowable.
* gnu/gcj/runtime/NameFinder.java: New file.
* gnu/gcj/runtime/natNameFinder.cc: Likewise.
* include/name-finder.h: Removed.
* java/lang/Throwable.java (printStackTrace (PrintStream)): Use new
method stackTraceString().
(printStackTrace (PrintWriter)): Likewise.
(stackTraceString): Complete rewrite of old printStackTrace using
StringBuffer.
(stackTraceStringBuffer): New helper method for stackTraceString().
(fillInStackTrace): Delegate to VMTrowable.
(getStackTrace): Likewise.
(getStackTrace0): Removed.
(trace_enabled, stackTraceBytes): Moved to new VMThrowable.java.
(setStackTrace): Copy given array.
* java/lang/natThrowable.cc: Removed (replaced by natVMThrowable).
* java/lang/VMThrowable.java: New class.
* java/lang/natVMThrowable.cc: New file.
From-SVN: r56556
This commit is contained in:
committed by
Mark Wielaard
parent
4906d5d83e
commit
6e0532cdf6
@@ -0,0 +1,407 @@
|
||||
/* NameFinder.java -- Translates addresses to StackTraceElements.
|
||||
Copyright (C) 2002 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.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;
|
||||
|
||||
/**
|
||||
* Helper class that translates addresses (represented as longs) to a
|
||||
* StackTraceElement array.
|
||||
*
|
||||
* There are a couple of system properties that can be set to manipulate the
|
||||
* result (all default to true):
|
||||
* <li>
|
||||
* <ul><code>gnu.gcj.runtime.NameFinder.demangle</code>
|
||||
* Whether names should be demangled.</ul>
|
||||
* <ul><code>gnu.gcj.runtime.NameFinder.sanitize</code></ul>
|
||||
* Whether calls to initialize exceptions and starting the runtime system
|
||||
* should be removed from the stack trace. Only done when names are
|
||||
* demangled.</ul>
|
||||
* <ul><code>gnu.gcj.runtime.NameFinder.remove_unknown</code>
|
||||
* Wheter calls to unknown functions (class and method names are unknown)
|
||||
* should be removed from the stack trace. Only done when the stack is
|
||||
* sanitized.</ul>
|
||||
* <ul><code>gnu.gcj.runtime.NameFinder.use_addr2line</code>
|
||||
* Wheter an external process (addr2line or addr2name.awk) should be used
|
||||
* as fallback to convert the addresses to function names when the runtime
|
||||
* is unable to do it through <code>dladdr</code>.</ul>
|
||||
* </li>
|
||||
*
|
||||
* <code>close()</code> should be called to get rid of all resources.
|
||||
*
|
||||
* This class is used from <code>java.lang.VMThrowable</code>.
|
||||
*
|
||||
* Currently the <code>lookup(long[])</code> method is not thread safe.
|
||||
* It can easily be made thread safe by synchronizing access to all external
|
||||
* processes when used.
|
||||
*
|
||||
* @author Mark Wielaard (mark@klomp.org)
|
||||
*/
|
||||
public class NameFinder
|
||||
{
|
||||
// Set these to false when not needed.
|
||||
private static final boolean demangle
|
||||
= Boolean.valueOf(System.getProperty
|
||||
("gnu.gcj.runtime.NameFinder.demangle", "true")
|
||||
).booleanValue();
|
||||
private static final boolean sanitize
|
||||
= Boolean.valueOf(System.getProperty
|
||||
("gnu.gcj.runtime.NameFinder.sanitize", "true")
|
||||
).booleanValue();
|
||||
private static final boolean remove_unknown
|
||||
= Boolean.valueOf(System.getProperty
|
||||
("gnu.gcj.runtime.NameFinder.remove_unknown", "true")
|
||||
).booleanValue();
|
||||
private static final boolean use_addr2line
|
||||
= Boolean.valueOf(System.getProperty
|
||||
("gnu.gcj.runtime.NameFinder.use_addr2line", "true")
|
||||
).booleanValue();
|
||||
|
||||
/**
|
||||
* The name of the currently running executable.
|
||||
*/
|
||||
private final String executable;
|
||||
|
||||
/**
|
||||
* Process used for demangling names.
|
||||
*/
|
||||
private Process cppfilt;
|
||||
|
||||
private BufferedWriter cppfiltOut;
|
||||
private BufferedReader cppfiltIn;
|
||||
|
||||
/**
|
||||
* Process used for translating addresses to function/file names.
|
||||
*/
|
||||
private Process addr2line;
|
||||
|
||||
private BufferedWriter addr2lineOut;
|
||||
private BufferedReader addr2lineIn;
|
||||
|
||||
/**
|
||||
* Creates a new NameFinder. Call close to get rid of any resources
|
||||
* created while using the <code>lookup</code> methods.
|
||||
*/
|
||||
public NameFinder()
|
||||
{
|
||||
executable = getExecutable();
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
if (demangle)
|
||||
{
|
||||
try
|
||||
{
|
||||
String[] exec = new String[] {"c++filt", "-s", "java"};
|
||||
cppfilt = runtime.exec(exec);
|
||||
cppfiltIn = new BufferedReader
|
||||
(new InputStreamReader(cppfilt.getInputStream()));
|
||||
cppfiltOut = new BufferedWriter
|
||||
(new OutputStreamWriter(cppfilt.getOutputStream()));
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
if (cppfilt != null)
|
||||
cppfilt.destroy();
|
||||
cppfilt = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_addr2line)
|
||||
{
|
||||
try
|
||||
{
|
||||
String[] exec = new String[] {"addr2line", "-f", "-e", executable};
|
||||
addr2line = runtime.exec(exec);
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
try
|
||||
{
|
||||
String[] exec = new String[] {"addr2name.awk", executable};
|
||||
addr2line = runtime.exec(exec);
|
||||
}
|
||||
catch (IOException ioe2) { addr2line = null; }
|
||||
}
|
||||
|
||||
if (addr2line != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
addr2lineIn = new BufferedReader
|
||||
(new InputStreamReader(addr2line.getInputStream()));
|
||||
addr2lineOut = new BufferedWriter
|
||||
(new OutputStreamWriter(addr2line.getOutputStream()));
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
addr2line.destroy();
|
||||
addr2line = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the currently running process.
|
||||
*/
|
||||
native private static String getExecutable();
|
||||
|
||||
/**
|
||||
* Tries to use dladdr to create the nth StackTraceElement from the given
|
||||
* addresses. Returns null on failure.
|
||||
*/
|
||||
native private StackTraceElement dladdrLookup(RawData addrs, int n);
|
||||
|
||||
/**
|
||||
* Returns the nth element from the stack as a hex encoded String.
|
||||
*/
|
||||
native private String getAddrAsString(RawData addrs, int n);
|
||||
|
||||
/**
|
||||
* Creates the nth StackTraceElement from the given native stacktrace.
|
||||
*/
|
||||
private StackTraceElement lookup(RawData addrs, int n)
|
||||
{
|
||||
StackTraceElement result;
|
||||
|
||||
result = dladdrLookup(addrs, n);
|
||||
if (result == null)
|
||||
{
|
||||
String name = null;
|
||||
String file = null;
|
||||
String hex = getAddrAsString(addrs, n);
|
||||
|
||||
if (addr2line != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
addr2lineOut.write(hex);
|
||||
addr2lineOut.newLine();
|
||||
addr2lineOut.flush();
|
||||
name = addr2lineIn.readLine();
|
||||
file = addr2lineIn.readLine();
|
||||
}
|
||||
catch (IOException ioe) { addr2line = null; }
|
||||
}
|
||||
|
||||
if (name == null || "??".equals(name))
|
||||
name = hex;
|
||||
|
||||
result = createStackTraceElement(name, file);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an Throwable and a native stacktrace returns an array of
|
||||
* StackTraceElement containing class, method, file and linenumbers.
|
||||
*/
|
||||
public StackTraceElement[] lookup(Throwable t, RawData addrs, int length)
|
||||
{
|
||||
StackTraceElement[] elements = new StackTraceElement[length];
|
||||
for (int i=0; i < length; i++)
|
||||
elements[i] = lookup(addrs, i);
|
||||
|
||||
if (demangle && sanitize)
|
||||
return sanitizeStack(elements, t);
|
||||
else
|
||||
return elements;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes calls to initialize exceptions and the runtime system from
|
||||
* the stack trace including stack frames of which nothing usefull is known.
|
||||
* Throw away the top of the stack till we find the constructor(s)
|
||||
* of this Throwable or at least the contructors of java.lang.Throwable
|
||||
* or the actual fillInStackTrace call.
|
||||
* Also throw away from the top everything before and including a runtime
|
||||
* _Jv_Throw call.
|
||||
*/
|
||||
private static StackTraceElement[] sanitizeStack(StackTraceElement[] elements,
|
||||
Throwable t)
|
||||
{
|
||||
StackTraceElement[] stack;
|
||||
|
||||
String className = t.getClass().getName();
|
||||
String consName;
|
||||
int lastDot = className.lastIndexOf('.');
|
||||
if (lastDot == -1)
|
||||
consName = className + '(';
|
||||
else
|
||||
consName = className.substring(lastDot + 1) + '(';
|
||||
|
||||
int unknown = 0;
|
||||
int last_throw = -1;
|
||||
int length = elements.length;
|
||||
int end = length-1;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
String CName = elements[i].getClassName();
|
||||
String MName = elements[i].getMethodName();
|
||||
if ((CName == null && MName != null && MName.startsWith("_Jv_Throw"))
|
||||
||
|
||||
(CName != null
|
||||
&& (CName.equals(className)
|
||||
|| CName.equals("java.lang.Throwable")
|
||||
|| CName.equals("java.lang.VMThrowable"))
|
||||
&& MName != null
|
||||
&& (MName.startsWith(consName)
|
||||
|| MName.startsWith("Throwable(")
|
||||
|| MName.startsWith("fillInStackTrace("))))
|
||||
last_throw = i;
|
||||
else if (remove_unknown && CName == null
|
||||
&& (MName == null || MName.startsWith("0x")))
|
||||
unknown++;
|
||||
else if ("main(java.lang.String[])".equals(MName))
|
||||
{
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int begin = last_throw+1;
|
||||
|
||||
// Now filter out everything at the start and the end that is not part
|
||||
// of the "normal" user program including any elements that have no
|
||||
// usefull information whatsoever unless that means we filter out all info.
|
||||
int nr_elements = end-begin-unknown+1;
|
||||
if ((begin > 0 || end < length-1 || unknown > 0) && nr_elements > 0)
|
||||
{
|
||||
stack = new StackTraceElement[nr_elements];
|
||||
int pos =0;
|
||||
for (int i=begin; i<=end; i++)
|
||||
{
|
||||
String MName;
|
||||
if (unknown == 0
|
||||
|| !(elements[i].getClassName() == null
|
||||
&& ((MName = elements[i].getMethodName()) == null
|
||||
|| MName.startsWith("0x"))))
|
||||
{
|
||||
stack[pos] = elements[i];
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
stack = elements;
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a StackTraceElement given a string and a filename.
|
||||
* Splits the given string into the class and method part.
|
||||
* The string name will be a demangled to a fully qualified java method
|
||||
* string. The string file will be decomposed into a file name and possibly
|
||||
* a line number. The name should never be null, but the file may be if it
|
||||
* is unknown.
|
||||
*/
|
||||
private StackTraceElement createStackTraceElement(String name, String file)
|
||||
{
|
||||
if (!demangle)
|
||||
return new StackTraceElement(file, -1, null, name, false);
|
||||
|
||||
String s = demangleName(name);
|
||||
String methodName = s;
|
||||
String className = null;
|
||||
int bracket = s.indexOf('(');
|
||||
if (bracket > 0)
|
||||
{
|
||||
int dot = s.lastIndexOf('.', bracket);
|
||||
if (dot > 0)
|
||||
{
|
||||
className = s.substring(0, dot);
|
||||
methodName = s.substring(dot+1, s.length());
|
||||
}
|
||||
}
|
||||
|
||||
String fileName = file;
|
||||
int line = -1;
|
||||
if (fileName != null)
|
||||
{
|
||||
int colon = file.indexOf(':');
|
||||
if (colon > 0)
|
||||
{
|
||||
fileName = file.substring(0, colon);
|
||||
try
|
||||
{
|
||||
line = Integer.parseInt(file.substring(colon+1, file.length()));
|
||||
}
|
||||
catch (NumberFormatException nfe) { /* ignore */ }
|
||||
}
|
||||
|
||||
if (line == 0)
|
||||
line =-1;
|
||||
|
||||
if ("".equals(fileName) || "??".equals(fileName))
|
||||
fileName = null;
|
||||
else if (fileName != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
fileName = new File(fileName).getCanonicalPath();
|
||||
}
|
||||
catch (IOException ioe) { /* ignore */ }
|
||||
}
|
||||
}
|
||||
|
||||
return new StackTraceElement(fileName, line, className, methodName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Demangles the given String if possible. Returns the demangled String or
|
||||
* the original string if demangling is impossible.
|
||||
*/
|
||||
private String demangleName(String s)
|
||||
{
|
||||
if (cppfilt != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
cppfiltOut.write(s);
|
||||
cppfiltOut.newLine();
|
||||
cppfiltOut.flush();
|
||||
return cppfiltIn.readLine();
|
||||
}
|
||||
catch (IOException ioe) { cppfilt.destroy(); cppfilt = null; }
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases all resources used by this NameFinder.
|
||||
*/
|
||||
public void close()
|
||||
{
|
||||
if (cppfilt != null)
|
||||
cppfilt.destroy();
|
||||
|
||||
if (addr2line != null)
|
||||
addr2line.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls close to get rid of all resources.
|
||||
*/
|
||||
protected void finalize()
|
||||
{
|
||||
close();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user