diff options
author | Bryce McKinlay <mckinlay@redhat.com> | 2005-03-10 19:02:21 +0000 |
---|---|---|
committer | Bryce McKinlay <bryce@gcc.gnu.org> | 2005-03-10 19:02:21 +0000 |
commit | 18744d9b72b028b5612e250966f9d165a40ae9d8 (patch) | |
tree | 7b6e8c5a43f4dcb0d8d4ac926f4562a978e11786 /libjava/gnu/gcj | |
parent | ca1593fda439eef1997ac28568337b52bf32460c (diff) | |
download | gcc-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.java | 522 |
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(); + } } } |