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]

Re: Cleaner stacktraces and diagnostics


Hi,

On Fri, 2005-02-11 at 23:15 +0100, Mark Wielaard wrote:
> On Fri, 2005-02-11 at 18:08 +0000, Andrew Haley wrote:
> > With your patch, a failure to find a class will return only the
> > classpath of the final loader that tried to find the class.  But the
> > user needs to see all the classpaths that were searched, not just the
> > last one.
> 
> OK if you think that is needed. It is indeed a nice GNU extension that
> our URLClassLoaders actually give the whole seearch path. I think I know
> how to extend that to provide the parent classloaders (plus search
> paths) without having to create and rechain extra
> ClassNotFoundExceptions.
> 
> Is the rest of the patch (minus the java.lang.ClassLoader.loadClass()
> part) OK? Then I commit that and provide a new patch for the chaining
> confusion and efficiency problem in loadClass() later.

Since the original patch hasn't been approved yet, I just add my
proposal for a nicer and more efficient parent class loader search path
in ClassNotFoundExceptions to it. I tested them together anyway. No test
regressions and eclipse works fine with this.

This does away with the creation of an extra ClassNotFoundException and
the double chaining in ClassLoader.loadClass() and properly chains
parents and parent search paths in the String representation of
URLClassLoader. It also makes the URLClassLoader ClassNotFoundException
construction a bit more efficient by introducing a (cached) toString()
implementation instead of calling Vector.toString() all the time (which
needs synchronization of the whole url vector for each exception
thrown).

This combined with the rest of the stacktrace cleanup and MainThread
diagnostics cleanup will give you (chained) NoClassDefFoundErrors like
the following:

Exception in thread "main" java.lang.NoClassDefFoundError: while resolving class: LoadingClass
   at java.lang.VMClassLoader.transformException(java.lang.Class, java.lang.Throwable) (/usr/lib/libgcj.so.6.0.0)
   at java.lang.VMClassLoader.resolveClass(java.lang.Class) (/usr/lib/libgcj.so.6.0.0)
   at java.lang.Class.initializeClass() (/usr/lib/libgcj.so.6.0.0)
   at java.lang.Class.newInstance() (/usr/lib/libgcj.so.6.0.0)
   at LoadClass.main(java.lang.String[]) (Unknown Source)
   at gnu.java.lang.MainThread.call_main() (/usr/lib/libgcj.so.6.0.0)
   at gnu.java.lang.MainThread.run() (/usr/lib/libgcj.so.6.0.0)
Caused by: java.lang.ClassNotFoundException: SuperClass not found in java.net.URLClassLoader{urls=[file:/tmp/], parent=gnu.gcj.runtime.SystemClassLoader{urls=[file:./], parent=gnu.gcj.runtime.VMClassLoader{urls=[core:/], parent=null}}}
   at java.net.URLClassLoader.findClass(java.lang.String) (/usr/lib/libgcj.so.6.0.0)
   at java.lang.ClassLoader.loadClass(java.lang.String, boolean) (/usr/lib/libgcj.so.6.0.0)
   at java.lang.VMClassLoader.defineClass(java.lang.ClassLoader, java.lang.String, byte[], int, int, java.security.ProtectionDomain) (/usr//lib/libgcj.so.6.0.0)
   at java.lang.ClassLoader.defineClass(java.lang.String, byte[], int, int, java.security.ProtectionDomain) (/usr/lib/libgcj.so.6.0.0)
   at java.security.SecureClassLoader.defineClass(java.lang.String, byte[], int, int, java.security.CodeSource) (/usr/lib/libgcj.so.6.0.0)
   at java.net.URLClassLoader.findClass(java.lang.String) (/usr/lib/libgcj.so.6.0.0)
   at java.lang.ClassLoader.loadClass(java.lang.String, boolean) (/usr/lib/libgcj.so.6.0.0)
   at java.lang.Class.forName(java.lang.String, boolean, java.lang.ClassLoader) (/usr/lib/libgcj.so.6.0.0)
   ...6 more

This should make it a bit easier to find the root cause when
strange/unexpected NoClassDefFoundErrors are thrown.

2005-02-12  Mark Wielaard  <mark@klomp.org>

        Fixes bug libgcj/8170
        * java/lang/ClassLoader.java (loadClass): Don't rewrap
        ClassNotFoundException.
        * gnu/java/lang/MainThread.java (run): Chain NoClassDefFoundError.
        * gnu/gcj/runtime/NameFinder.java (remove_interpreter): Removed.
        (remove_internal): New field superceding remove_interpreter.
        (sanitizeStack): Remove all no-package classes starting with "_Jv_".
        Remove no-class methods starting with "_Jv_". And Replace null
        class or method names with the empty string. Stop at either the
        MainThread or a real Thread run() method.
        (newElement): Made static.
        * java/net/URLClassLoader.java (findClass): Throw
        ClassNotFoundExceptions including urls, plus parent using toString().
        (thisString): New field.
        (toString): New method.

OK to commit?

Cheers,

Mark
Index: java/lang/ClassLoader.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/ClassLoader.java,v
retrieving revision 1.36
diff -u -r1.36 ClassLoader.java
--- java/lang/ClassLoader.java	7 Jan 2005 22:15:46 -0000	1.36
+++ java/lang/ClassLoader.java	12 Feb 2005 12:10:46 -0000
@@ -288,8 +288,6 @@
     if (c != null)
       return c;
 
-    ClassNotFoundException ex = null;
-
     // Can the class be loaded by a parent?
     try
       {
@@ -306,20 +304,9 @@
       }
     catch (ClassNotFoundException e)
       {
-	ex = e;
       }
     // Still not found, we have to do it ourself.
-    try
-      {
-	c = findClass(name);
-      }
-    catch (ClassNotFoundException cause)
-      {
-	if (ex != null)
-	  throw new ClassNotFoundException(ex.toString(), cause);
-	else
-	  throw cause;
-      }
+    c = findClass(name);
     if (resolve)
       resolveClass(c);
     return c;
Index: gnu/java/lang/MainThread.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gnu/java/lang/MainThread.java,v
retrieving revision 1.2
diff -u -r1.2 MainThread.java
--- gnu/java/lang/MainThread.java	29 Jul 2004 13:48:17 -0000	1.2
+++ gnu/java/lang/MainThread.java	12 Feb 2005 12:10:46 -0000
@@ -95,7 +95,9 @@
 	  }
 	catch (ClassNotFoundException x)
 	  {
-	    throw new NoClassDefFoundError(klass_name);
+	    NoClassDefFoundError ncdfe = new NoClassDefFoundError(klass_name);
+	    ncdfe.initCause(x);
+	    throw ncdfe;
 	  }
       }
 
Index: gnu/gcj/runtime/NameFinder.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gnu/gcj/runtime/NameFinder.java,v
retrieving revision 1.7
diff -u -r1.7 NameFinder.java
--- gnu/gcj/runtime/NameFinder.java	9 Apr 2004 04:39:24 -0000	1.7
+++ gnu/gcj/runtime/NameFinder.java	12 Feb 2005 12:10:46 -0000
@@ -37,8 +37,8 @@
  *     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_interpreter</code>
- *     Whether runtime interpreter calls (methods in the _Jv_InterpMethod class
+ * <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>
@@ -72,10 +72,18 @@
 	  = Boolean.valueOf(System.getProperty
 		("gnu.gcj.runtime.NameFinder.remove_unknown", "true")
 	    ).booleanValue();
-  private static final boolean remove_interpreter
-	  = Boolean.valueOf(System.getProperty
+
+  // 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();
+                             ).booleanValue()
+	     );
+
   private static final boolean use_addr2line
 	  = Boolean.valueOf(System.getProperty
 		("gnu.gcj.runtime.NameFinder.use_addr2line", "true")
@@ -280,7 +288,7 @@
       consName = className.substring(lastDot + 1) + '(';
 
     int unknown = 0;
-    int interpreter = 0;
+    int internal = 0;
     int last_throw = -1;
     int length = elements.length;
     int end = length-1;
@@ -300,19 +308,23 @@
 		|| MName.startsWith("fillInStackTrace("))))
 	  {
 	    last_throw = i;
-	    // Reset counting of unknown and interpreter frames.
+	    // Reset counting of unknown and internal frames.
 	    unknown = 0;
-	    interpreter = 0;
+	    internal = 0;
 	  }
 	else if (remove_unknown && CName == null 
 		 && (MName == null || MName.startsWith("0x")))
 	  unknown++;
-	else if (remove_interpreter
+	else if (remove_internal
 		 && ((CName == null
 		      && MName != null && MName.startsWith("ffi_"))
-		     || (CName != null && CName.equals("_Jv_InterpMethod"))))
-	  interpreter++;
-	else if ("main(java.lang.String[])".equals(MName))
+		     || (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;
@@ -321,11 +333,11 @@
     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 interpreter
+    // 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-interpreter+1;
-    if ((begin > 0 || end < length-1 || unknown > 0 || interpreter > 0)
+    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];
@@ -337,14 +349,27 @@
 	    if (remove_unknown && CName == null 
 		 && (MName == null || MName.startsWith("0x")))
 	      ; // Skip unknown frame
-	    else if (remove_interpreter
+	    else if (remove_internal
 		     && ((CName == null
-			 && MName != null && MName.startsWith("ffi_"))
-			|| (CName != null && CName.equals("_Jv_InterpMethod"))))
-	      ; // Skip interpreter runtime frame
+			  && MName != null && MName.startsWith("ffi_"))
+			 || (CName != null && CName.startsWith("_Jv_"))
+			 || (CName == null && MName != null
+			     && MName.startsWith("_Jv_"))))
+	      ; // Skip internal runtime frame
 	    else
 	      {
-		stack[pos] = elements[i];
+		// 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++;
 	      }
 	  }
@@ -359,11 +384,11 @@
    * Native helper method to create a StackTraceElement. Needed to work
    * around normal Java access restrictions.
    */
-  native private StackTraceElement newElement(String fileName,
-                                              int lineNumber,
-                                              String className,
-                                              String methName,
-                                              boolean isNative);
+  native static private StackTraceElement newElement(String fileName,
+						     int lineNumber,
+						     String className,
+						     String methName,
+						     boolean isNative);
 
   /**
    * Creates a StackTraceElement given a string and a filename.
Index: java/net/URLClassLoader.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/net/URLClassLoader.java,v
retrieving revision 1.22
diff -u -r1.22 URLClassLoader.java
--- java/net/URLClassLoader.java	25 Nov 2004 03:47:06 -0000	1.22
+++ java/net/URLClassLoader.java	12 Feb 2005 12:10:46 -0000
@@ -1,5 +1,6 @@
 /* URLClassLoader.java --  ClassLoader that loads classes from one or more URLs
-   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
+   Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -958,7 +959,7 @@
 	resource = loader.getResource(resourceName);
       }
     if (resource == null)
-      throw new ClassNotFoundException(className + " not found in " + urls);
+      throw new ClassNotFoundException(className + " not found in " + this);
 
     // Try to read the class data, create the CodeSource, Package and
     // construct the class (and watch out for those nasty IOExceptions)
@@ -1039,9 +1040,43 @@
       }
     catch (IOException ioe)
       {
-        throw new ClassNotFoundException(className, ioe);
+	ClassNotFoundException cnfe;
+	cnfe = new ClassNotFoundException(className + " not found in " + this);
+	cnfe.initCause(ioe);
+	throw cnfe;
       }
   }
+  
+  // Cached String representation of this URLClassLoader
+  private String thisString;
+  
+  /**
+   * Returns a String representation of this URLClassLoader giving the
+   * actual Class name, the URLs that are searched and the parent
+   * ClassLoader.
+   */
+  public String toString()
+  {
+    if (thisString == null)
+      {
+	StringBuffer sb = new StringBuffer();
+	sb.append(this.getClass().getName());
+	sb.append("{urls=[" );
+	URL[] thisURLs = getURLs();
+	for (int i = 0; i < thisURLs.length; i++)
+	  {
+	    sb.append(thisURLs[i]);
+	    if (i < thisURLs.length - 1)
+	      sb.append(',');
+	  }
+	sb.append(']');
+	sb.append(", parent=");
+	sb.append(getParent());
+	sb.append('}');
+	thisString = sb.toString();
+      }
+    return thisString;
+  }
 
   /**
    * Finds the first occurrence of a resource that can be found. The locations

Attachment: signature.asc
Description: This is a digitally signed message part


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