aboutsummaryrefslogtreecommitdiff
path: root/libjava/gnu/gcj
diff options
context:
space:
mode:
authorBryce McKinlay <mckinlay@redhat.com>2005-03-10 19:02:21 +0000
committerBryce McKinlay <bryce@gcc.gnu.org>2005-03-10 19:02:21 +0000
commit18744d9b72b028b5612e250966f9d165a40ae9d8 (patch)
tree7b6e8c5a43f4dcb0d8d4ac926f4562a978e11786 /libjava/gnu/gcj
parentca1593fda439eef1997ac28568337b52bf32460c (diff)
downloadgcc-18744d9b72b028b5612e250966f9d165a40ae9d8.zip
gcc-18744d9b72b028b5612e250966f9d165a40ae9d8.tar.gz
gcc-18744d9b72b028b5612e250966f9d165a40ae9d8.tar.bz2
New Stack Trace infrastructure.
2005-03-10 Bryce McKinlay <mckinlay@redhat.com> New Stack Trace infrastructure. * Makefile.am (libgcj0_convenience_la_SOURCES): Add stacktrace.cc. (gnu/gcj/runtime/StackTrace.lo): Removed. (ordinary_java_source_files): Remove obsolete files. (nat_source_files): Remove obsolete files. Add natVMThrowable.cc. * configure.host (fallback_backtrace_h): Set backtrace header for mingw and cygwin targets. * configure.ac: Make symlink for fallback backtrace headers. * Makefile.in, configure: Rebuilt. * defineclass.cc (_Jv_ClassReader::read_one_code_attribute): Read 'LineNumberTable' attribute. (_Jv_ClassReader::read_one_class_attribute): Read 'SourceFile' attribute. (_Jv_ClassReader::handleCodeAttribute): Initialize method line table fields. * exception.cc: Remove unused include. * interpret.cc (DIRECT_THREADED, insn_slot): Moved to java-interp.h. (SAVE_PC): New macro. Save current PC in the interpreter frame. (NULLCHECK, NULLARRAYCHECK): Use SAVE_PC. (_Jv_InterpMethod::compile): Translate bytecode PC values in the line table to direct threaded instruction values. (_Jv_StartOfInterpreter, _Jv_EndOfInterpreter): Removed. (_Jv_InterpMethod::run): No longer member function. All callers updated. Remove _Unwind calls. Call SAVE_PC whenever a call is made or where an instruction could throw. (_Jv_InterpMethod::get_source_line): New. Look up source line numbers in line_table. * prims.cc (catch_segv): Construct exception after MAKE_THROW_FRAME. (catch_fpe): Likewise. * stacktrace.cc: New file. Stack trace code now here. * gnu/gcj/runtime/MethodRef.java: * gnu/gcj/runtime/NameFinder.java: Mostly reimplemented. Now simply calls addr2line to look up PC addresses in a given binary or shared library. * gnu/gcj/runtime/StackTrace.java, gnu/gcj/runtime/natNameFinder.cc, gnu/gcj/runtime/natStackTrace.cc: Removed. * gnu/java/lang/MainThread.java (call_main): Add comment warning that this function name is specially recognised by the stack trace code and shouldn't be changed. * include/java-interp.h (DIRECT_THREADED, insn_slot): Moved here. (struct _Jv_LineTableEntry, line_table, line_table_len): New. (_Jv_InterpMethod::run): Update declaration. (_Jv_StackTrace_): New friend. NameFinder and StackTrace no longer friends. (_Jv_InterpFrame): Renamed from _Jv_MethodChain. Add PC field. * include/java-stack.h: New file. Declarations for stack tracing. * include/jvm.h (_Jv_Frame_info): Removed. * java/lang/Class.h: Update friend declarations. * java/lang/VMClassLoader.java (getSystemClassLoader): Simplify exception message. * java/lang/VMThrowable.java (fillInStackTrace): Now native. (getStackTrace): Now native. (data): New RawDataManaged field. * java/lang/natClass.cc: Update includes. (forName): Use _Jv_StackTrace::GetCallingClass for calling-classloader check. (getClassLoader): Likewise. * java/lang/natRuntime.cc: Update includes. (_load): Use _Jv_StackTrace::GetFirstNonSystemClassLoader. * java/lang/natVMSecurityManager.cc: Update includes. (getClassContext): Use _Jv_StackTrace::GetClassContext. * java/lang/natVMThrowable.cc: New file. Native methods for VMThrowable. * java/lang/reflect/natArray.cc: Update includes. (newInstance): Use _Jv_StackTrace::GetCallingClass to implement accessibility check. * java/lang/reflect/natConstructor.cc: Update includes. (newInstance): Use _Jv_StackTrace::GetCallingClass to implement accessibility check. * java/lang/reflect/natField.cc: Update includes. (getAddr): Use _Jv_StackTrace::GetCallingClass to implement accessibility check. * java/lang/reflect/natMethod.cc: Update includes. (invoke): Use _Jv_StackTrace::GetCallingClass to implement accessibility check. * java/util/natResourceBundle.cc: Update includes. (getCallingClassLoader): Use _Jv_StackTrace::GetCallingClass. * java/util/logging/natLogger.cc: Update includes. Use _Jv_StackTrace::GetCallerInfo to get call-site info. * sysdep/generic/backtrace.h: Fallback backtrace code. Stub implementation. * sysdep/i386/backtrace.h: New. Fallback backtrace code. i386 implementation. From-SVN: r96253
Diffstat (limited to 'libjava/gnu/gcj')
-rw-r--r--libjava/gnu/gcj/runtime/NameFinder.java522
1 files changed, 108 insertions, 414 deletions
diff --git a/libjava/gnu/gcj/runtime/NameFinder.java b/libjava/gnu/gcj/runtime/NameFinder.java
index b14bbf9..5469f08 100644
--- a/libjava/gnu/gcj/runtime/NameFinder.java
+++ b/libjava/gnu/gcj/runtime/NameFinder.java
@@ -9,6 +9,7 @@ details. */
package gnu.gcj.runtime;
+import gnu.classpath.Configuration;
import gnu.gcj.RawData;
import java.lang.StringBuffer;
@@ -19,456 +20,156 @@ import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.IOException;
import java.io.File;
+import java.util.Iterator;
+import java.util.HashMap;
+
/**
- * Helper class that translates addresses (represented as longs) to a
- * StackTraceElement array.
+ * Lookup addresses (represented as longs) to find source & line number info.
*
- * There are a couple of system properties that can be set to manipulate the
- * result (all default to true):
+ * The following system property is available (defaults 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>
- * Whether 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.remove_internal</code>
- * Whether runtime internal calls (methods in the internal _Jv_* classes
- * and functions starting with 'ffi_') should be removed from the stack
- * trace. Only done when the stack is sanitized.</ul>
* <ul><code>gnu.gcj.runtime.NameFinder.use_addr2line</code>
- * Whether 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>
+ * 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>
* </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();
-
- // The remove_interpreter name is an old 3.3/3.4 (deprecated) synonym.
- private static final boolean remove_internal
- = (Boolean.valueOf(System.getProperty
- ("gnu.gcj.runtime.NameFinder.remove_internal", "true")
- ).booleanValue()
- ||
- Boolean.valueOf(System.getProperty
- ("gnu.gcj.runtime.NameFinder.remove_interpreter", "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.
+ * The name of the binary to look up.
*/
- private Process addr2line;
+ private String binaryFile;
+ private String sourceFile;
+ private int lineNum;
+ private HashMap procs = new HashMap();
- private BufferedWriter addr2lineOut;
- private BufferedReader addr2lineIn;
-
- /**
- * Flag set if using addr2name.awk instead of addr2line from binutils.
- */
- private boolean usingAddr2name = false;
+ private static final boolean use_addr2line
+ = Boolean.valueOf(System.getProperty
+ ("gnu.gcj.runtime.NameFinder.use_addr2line", "true")
+ ).booleanValue();
- /**
- * Creates a new NameFinder. Call close to get rid of any resources
- * created while using the <code>lookup</code> methods.
- */
- public NameFinder()
+ class Addr2Line
{
- executable = getExecutable();
- Runtime runtime = Runtime.getRuntime();
- if (demangle)
+ Process proc;
+ BufferedWriter out;
+ BufferedReader in;
+
+ Addr2Line(String binaryFile)
{
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()));
- }
+ {
+ String[] exec = new String[] {"addr2line", "-e", binaryFile};
+ Runtime runtime = Runtime.getRuntime();
+ proc = runtime.exec(exec);
+ }
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);
- usingAddr2name = true;
- }
- catch (IOException ioe2) { addr2line = null; }
- }
-
- if (addr2line != null)
- {
- addr2lineIn = new BufferedReader
- (new InputStreamReader(addr2line.getInputStream()));
- addr2lineOut = new BufferedWriter
- (new OutputStreamWriter(addr2line.getOutputStream()));
- }
}
- }
-
- /**
- * 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);
-
- /**
- * Returns the label that is exported for the given method name.
- */
- native private String getExternalLabel(String name);
-
- /**
- * If nth element of stack is an interpreted frame, return the
- * element representing the method being interpreted.
- */
- native private StackTraceElement lookupInterp(RawData addrs, int n);
- /**
- * Creates the nth StackTraceElement from the given native stacktrace.
- */
- private StackTraceElement lookup(RawData addrs, int n)
- {
- StackTraceElement result;
-
- result = lookupInterp(addrs, n);
- if (result == null)
- result = dladdrLookup(addrs, n);
- if (result == null)
+ if (proc != 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();
-
- // addr2line uses symbolic debugging information instead
- // of the actually exported labels as addr2name.awk does.
- // This name might need some modification, depending on
- // the system, to make it a label like that returned
- // by addr2name.awk or dladdr.
- if (! usingAddr2name)
- if (name != null && ! "??".equals (name))
- name = getExternalLabel (name);
- }
- catch (IOException ioe) { addr2line = null; }
- }
-
- if (name == null || "??".equals(name))
- name = hex;
-
- result = createStackTraceElement(name, file);
+ in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+ out = new BufferedWriter(new OutputStreamWriter(proc.getOutputStream()));
+ }
+ }
+
+ void close()
+ {
+ try
+ {
+ in.close();
+ out.close();
}
+ catch (IOException x) {}
- return result;
+ proc.destroy();
+ }
}
/**
- * Given an Throwable and a native stacktrace returns an array of
- * StackTraceElement containing class, method, file and linenumbers.
+ * 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 StackTraceElement[] lookup(Throwable t, StackTrace trace)
+ public NameFinder()
{
- RawData addrs = trace.stackTraceAddrs();
- int length = trace.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.
+ * Returns the source file name if lookup() was successful. If the source file could not be
+ * determined, the binary name will be returned instead.
*/
- private static StackTraceElement[] sanitizeStack(StackTraceElement[] elements,
- Throwable t)
+ public String getSourceFile()
{
- StackTraceElement[] stack;
-
- String className = t.getClass().getName();
- String consName;
- int lastDot = className.lastIndexOf('.');
- if (lastDot == -1)
- consName = className + '(';
+ String file;
+ if (sourceFile != null)
+ file = sourceFile;
else
- consName = className.substring(lastDot + 1) + '(';
-
- int unknown = 0;
- int internal = 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;
- // Reset counting of unknown and internal frames.
- unknown = 0;
- internal = 0;
- }
- else if (remove_unknown && CName == null
- && (MName == null || MName.startsWith("0x")))
- unknown++;
- else if (remove_internal
- && ((CName == null
- && MName != null && MName.startsWith("ffi_"))
- || (CName != null && CName.startsWith("_Jv_"))
- || (CName == null && MName != null
- && MName.startsWith("_Jv_"))))
- internal++;
- else if (("java.lang.Thread".equals(CName)
- || "gnu.java.lang.MainThread".equals(CName))
- && "run()".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 are internal
- // calls or have no usefull information whatsoever.
- // Unless that means we filter out all info.
- int nr_elements = end - begin - unknown - internal + 1;
- if ((begin > 0 || end < length-1 || unknown > 0 || internal > 0)
- && nr_elements > 0)
- {
- stack = new StackTraceElement[nr_elements];
- int pos =0;
- for (int i=begin; i<=end; i++)
- {
- String MName = elements[i].getMethodName();
- String CName = elements[i].getClassName();
- if (remove_unknown && CName == null
- && (MName == null || MName.startsWith("0x")))
- ; // Skip unknown frame
- else if (remove_internal
- && ((CName == null
- && MName != null && MName.startsWith("ffi_"))
- || (CName != null && CName.startsWith("_Jv_"))
- || (CName == null && MName != null
- && MName.startsWith("_Jv_"))))
- ; // Skip internal runtime frame
- else
- {
- // Null Class or Method name in elements are not allowed.
- if (MName == null || CName == null)
- {
- MName = MName == null ? "" : MName;
- CName = CName == null ? "" : CName;
- stack[pos] = newElement(elements[i].getFileName(),
- elements[i].getLineNumber(),
- CName, MName,
- elements[i].isNativeMethod());
- }
- else
- stack[pos] = elements[i];
- pos++;
- }
- }
- }
- else
- stack = elements;
-
- return stack;
+ file = binaryFile;
+
+ return file.substring(file.lastIndexOf(File.separator) + 1, file.length());
}
/**
- * Native helper method to create a StackTraceElement. Needed to work
- * around normal Java access restrictions.
- */
- native static private StackTraceElement newElement(String fileName,
- int lineNumber,
- String className,
- String methName,
- boolean isNative);
-
- /**
- * 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 lookup() was successful, returns the line number of addr. If the line number could not
+ * be determined, -1 is returned.
+ */
+ public int getLineNum()
{
- if (!demangle)
- return newElement(file, -1, null, name, false);
-
- String s = demangleName(name);
- String methodName = s;
- String className = null;
- int bracket = s.indexOf('(');
- if (bracket > 0)
+ return lineNum;
+ }
+
+ public void lookup (String file, long addr)
+ {
+ binaryFile = file;
+ sourceFile = null;
+ lineNum = -1;
+
+ if (! use_addr2line)
+ return;
+ Addr2Line addr2line = (Addr2Line) procs.get(file);
+ if (addr2line == null)
{
- int dot = s.lastIndexOf('.', bracket);
- if (dot > 0)
- {
- className = s.substring(0, dot);
- methodName = s.substring(dot+1, s.length());
- }
+ addr2line = new Addr2Line(file);
+ procs.put(file, addr2line);
}
-
- String fileName = file;
- int line = -1;
- if (fileName != null)
+
+ if (addr2line.proc == null)
+ return;
+
+ String hexAddr = "0x" + Long.toHexString(addr);
+ String name;
+
+ try
{
- int colon = file.lastIndexOf(':');
- if (colon > 0)
- {
- fileName = file.substring(0, colon);
- try
- {
- line = Integer.parseInt(file.substring(colon+1, file.length()));
- }
- catch (NumberFormatException nfe) { /* ignore */ }
- }
+ addr2line.out.write(hexAddr);
+ addr2line.out.newLine();
+ addr2line.out.flush();
+ String result = addr2line.in.readLine();
- 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 newElement(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
+ if (result.indexOf("??") == -1)
{
- cppfiltOut.write(s);
- cppfiltOut.newLine();
- cppfiltOut.flush();
- return cppfiltIn.readLine();
+ int split = result.lastIndexOf(':');
+ sourceFile = result.substring(0, split);
+ String lineNumStr = result.substring(split + 1, result.length());
+ lineNum = Integer.parseInt (lineNumStr);
}
- catch (IOException ioe) { cppfilt.destroy(); cppfilt = null; }
- }
-
- return s;
+ }
+ catch (IOException ioe)
+ {
+ addr2line = null;
+ }
+ catch (NumberFormatException x)
+ {
+ }
}
/**
@@ -508,7 +209,7 @@ public class NameFinder
// Demangle the type arguments
int arrayDepth = 0;
char c = (index < length) ? m.charAt(index) : ')';
- while (c != ')')
+ while (c != ')')
{
String type;
switch(c)
@@ -581,18 +282,11 @@ public class 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();
+ Iterator itr = procs.values().iterator();
+ while (itr.hasNext())
+ {
+ Addr2Line proc = (Addr2Line) itr.next();
+ proc.close();
+ }
}
}