aboutsummaryrefslogtreecommitdiff
path: root/libjava/gnu
diff options
context:
space:
mode:
authormark <mark@138bc75d-0d04-0410-961f-82ee72b054a4>2002-08-24 22:46:19 +0000
committermark <mark@138bc75d-0d04-0410-961f-82ee72b054a4>2002-08-24 22:46:19 +0000
commit22bd464fb14254908fedcdc08a0a8a3068c62098 (patch)
tree2194f5746d8d192d6202800f3df90a8ccb1675e0 /libjava/gnu
parentbf81fbc51e651ae09cf076d677f052f460aca5fd (diff)
* 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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@56556 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/gnu')
-rw-r--r--libjava/gnu/gcj/runtime/NameFinder.java407
-rw-r--r--libjava/gnu/gcj/runtime/natNameFinder.cc84
2 files changed, 491 insertions, 0 deletions
diff --git a/libjava/gnu/gcj/runtime/NameFinder.java b/libjava/gnu/gcj/runtime/NameFinder.java
new file mode 100644
index 00000000000..60f47ac64fe
--- /dev/null
+++ b/libjava/gnu/gcj/runtime/NameFinder.java
@@ -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();
+ }
+}
diff --git a/libjava/gnu/gcj/runtime/natNameFinder.cc b/libjava/gnu/gcj/runtime/natNameFinder.cc
new file mode 100644
index 00000000000..42cc164c324
--- /dev/null
+++ b/libjava/gnu/gcj/runtime/natNameFinder.cc
@@ -0,0 +1,84 @@
+// natNameFinder.cc - native helper methods for NameFiner.java
+
+/* 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. */
+
+/**
+ * @author Mark Wielaard (mark@klomp.org)
+ * Based on the old name-finder.cc by Andrew Haley <aph@cygnus.com>.
+ */
+
+#include <config.h>
+
+#include <gcj/cni.h>
+#include <jvm.h>
+#include <java/lang/String.h>
+#include <java/lang/StackTraceElement.h>
+
+#include <gnu/gcj/runtime/NameFinder.h>
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+java::lang::String*
+gnu::gcj::runtime::NameFinder::getExecutable (void)
+{
+ return JvNewStringLatin1 (_Jv_ThisExecutable ());
+}
+
+java::lang::String*
+gnu::gcj::runtime::NameFinder::getAddrAsString(RawData* addrs, jint n)
+{
+ void **p = (void **) addrs;
+ typedef unsigned word_t __attribute ((mode (word)));
+ word_t w = (word_t) p[n];
+ int digits = sizeof (void *) * 2;
+ char hex[digits+5];
+
+ strcpy (hex, "0x");
+ for (int i = digits - 1; i >= 0; i--)
+ {
+ int digit = w % 16;
+
+ w /= 16;
+ hex[i+2] = digit > 9 ? 'a' + digit - 10 : '0' + digit;
+ }
+ hex [digits+2] = 0;
+
+ return JvNewStringLatin1(hex);
+}
+
+java::lang::StackTraceElement*
+gnu::gcj::runtime::NameFinder::dladdrLookup(RawData* addrs, jint n)
+{
+#if defined (HAVE_DLFCN_H) && defined (HAVE_DLADDR)
+ extern char **_Jv_argv;
+ char name[1024];
+ char file_name[1024];
+ void **stack = (void **) addrs;
+ void* p = stack[n];
+ Dl_info dl_info;
+
+ if (dladdr (p, &dl_info))
+ {
+ if (dl_info.dli_fname)
+ strncpy (file_name, dl_info.dli_fname, sizeof file_name);
+ if (dl_info.dli_sname)
+ strncpy (name, dl_info.dli_sname, sizeof name);
+
+ /* Don't trust dladdr() if the address is from the main program. */
+ if (dl_info.dli_fname != NULL
+ && dl_info.dli_sname != NULL
+ && (_Jv_argv == NULL || strcmp (file_name, _Jv_argv[0]) != 0))
+ return createStackTraceElement (JvNewStringLatin1 (name),
+ JvNewStringLatin1 (file_name));
+ }
+#endif
+ return NULL;
+}