This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Cleaner interpreter stacktraces


Hi,

The following patch builds on the work that Tom did for getting more
meaningful stacktraces for interpreted methods. It adds a type signature
demangler for interpreted stack frames and sanitizes/obfuscates the
runtime interpreter calls from the user visible stacktrace (this can of
course be turned off). It also reverses the order in which the
stacktrace addr[] is initialized to get the interpreted stack frames in
the correct location.

With this patch running gij on the following program:

public class Test
{
  public static void main(String args[]) {
    a(0);
  } 

  static void a(int i) {
    b(null);
  }

  static void b(byte b[][][]) {
    c(null, null);
  }

  static void c(char c[], short[][] s) {
    d(0, null);
  }

  static void d(long j, String e) {
    e(0, 0);
  }

  static void e(float f, double d) {
    new java.io.File(null);
  }
}

compiled with gcj -C gives:

Exception in thread "main" java.lang.NullPointerException
   at java.io.File.File(java.lang.String)(/opt/gcc/lib/libgcj.so.3.0.0)
   at Test.e(float, double) (Unknown Source)
   at Test.d(long, java.lang.String) (Unknown Source)
   at Test.c(char[], short[][]) (Unknown Source)
   at Test.b(byte[][][]) (Unknown Source)
   at Test.a(int) (Unknown Source)
   at Test.main(java.lang.String[]) (Unknown Source)

Which is the same as you would get when compiling to a native binary
with gcj --main=Test Test.java.

2002-09-01  Mark Wielaard  <mark@klomp.org>

    * gnu/gcj/runtime/NameFinder.java (remove_interpreter): New field.
    (sanitizeStack): Correctly reset unknown and interpreter counters,
    detect interpreter runtime frames.
    (demangleInterpreterMethod): New method.
    * gnu/gcj/runtime/natNameFinder.cc (lookupInterp): Use new method.
    * java/lang/natVMThrowable.cc (fillInStackTrace): Change order of
    filling in addrs[].

OK to commit?

Cheers,

Mark
Index: gnu/gcj/runtime/NameFinder.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gnu/gcj/runtime/NameFinder.java,v
retrieving revision 1.2
diff -u -r1.2 NameFinder.java
--- gnu/gcj/runtime/NameFinder.java	29 Aug 2002 17:53:28 -0000	1.2
+++ gnu/gcj/runtime/NameFinder.java	1 Sep 2002 15:00:34 -0000
@@ -37,6 +37,10 @@
  *     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.remove_interpreter</code>
+ *     Wheter runtime interpreter calls (methods in the _Jv_InterpMethod class
+ *     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>
  *     Wheter an external process (addr2line or addr2name.awk) should be used
  *     as fallback to convert the addresses to function names when the runtime
@@ -68,6 +72,10 @@
 	  = Boolean.valueOf(System.getProperty
 		("gnu.gcj.runtime.NameFinder.remove_unknown", "true")
 	    ).booleanValue();
+  private static final boolean remove_interpreter
+	  = 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")
@@ -257,6 +265,7 @@
       consName = className.substring(lastDot + 1) + '(';
 
     int unknown = 0;
+    int interpreter = 0;
     int last_throw = -1;
     int length = elements.length;
     int end = length-1;
@@ -274,10 +283,20 @@
 	    && (MName.startsWith(consName)
 		|| MName.startsWith("Throwable(")
 		|| MName.startsWith("fillInStackTrace("))))
-	  last_throw = i;
+	  {
+	    last_throw = i;
+	    // Reset counting of unknown and interpreter frames.
+	    unknown = 0;
+	    interpreter = 0;
+	  }
 	else if (remove_unknown && CName == null 
 		 && (MName == null || MName.startsWith("0x")))
 	  unknown++;
+	else if (remove_interpreter
+		 && ((CName == null
+		      && MName != null && MName.startsWith("ffi_"))
+		     || (CName != null && CName.equals("_Jv_InterpMethod"))))
+	  interpreter++;
 	else if ("main(java.lang.String[])".equals(MName))
 	  {
 	    end = i;
@@ -287,20 +306,28 @@
     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)
+    // of the "normal" user program including any elements that are interpreter
+    // calls or have no usefull information whatsoever.
+    // Unless that means we filter out all info.
+    int nr_elements = end-begin-unknown-interpreter+1;
+    if ((begin > 0 || end < length-1 || unknown > 0 || interpreter > 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"))))
+	    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_interpreter
+		     && ((CName == null
+			 && MName != null && MName.startsWith("ffi_"))
+			|| (CName != null && CName.equals("_Jv_InterpMethod"))))
+	      ; // Skip interpreter runtime frame
+	    else
 	      {
 		stack[pos] = elements[i];
 		pos++;
@@ -392,6 +419,111 @@
     }
 
     return s;
+  }
+
+  /**
+   * 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();
   }
 
   /**
Index: gnu/gcj/runtime/natNameFinder.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gnu/gcj/runtime/natNameFinder.cc,v
retrieving revision 1.2
diff -u -r1.2 natNameFinder.cc
--- gnu/gcj/runtime/natNameFinder.cc	29 Aug 2002 17:53:28 -0000	1.2
+++ gnu/gcj/runtime/natNameFinder.cc	1 Sep 2002 15:00:34 -0000
@@ -95,7 +95,6 @@
 
   _Jv_InterpMethod *meth
     = reinterpret_cast<_Jv_InterpMethod *> (stack[n].interp);
-  // FIXME: demangle.
   java::lang::StringBuffer *sb = new java::lang::StringBuffer();
   sb->append(_Jv_NewStringUtf8Const(meth->self->name));
   sb->append(_Jv_NewStringUtf8Const(meth->self->signature));
@@ -103,9 +102,11 @@
   // bytecode debug information.  But currently we don't keep that
   // around.
   // FIXME: is using the defining class correct here?
+  java::lang::String *className = meth->defining_class->getName();
+  java::lang::String *methodName
+	  = demangleInterpreterMethod(sb->toString(), className);
   return new java::lang::StackTraceElement(NULL, -1,
-					   meth->defining_class->getName(),
-					   sb->toString(), false);
+					   className, methodName, false);
 #else // INTERPRETER
   return NULL;
 #endif // INTERPRETER
Index: java/lang/natVMThrowable.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/natVMThrowable.cc,v
retrieving revision 1.2
diff -u -r1.2 natVMThrowable.cc
--- java/lang/natVMThrowable.cc	29 Aug 2002 17:53:28 -0000	1.2
+++ java/lang/natVMThrowable.cc	1 Sep 2002 15:00:35 -0000
@@ -72,7 +72,7 @@
       state->length = n;
       int len = n;
       addrs = (_Jv_frame_info *) _Jv_Malloc (n * sizeof (_Jv_frame_info));
-      while (n--)
+      for (n=0; n < len; n++)
 	{
 	  addrs[n].addr = p[n];
 #ifdef INTERPRETER

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]