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: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Bryce McKinlay wrote:
> 
> Looks good! Could the win32 part of getLineNumberForFrame() be split out 
> into its own function, perhaps in win32.cc? It would be nice to avoid 
> adding yet more #ifdef's in here.

That function has just three independent sections (with my patch):

  1. If the frame is from the interpreter.
  2. If the target has a working dladdr().
  3. If the target is Windows.

It's neatly separated in my opinion but I can change it if you
want me to. The problem is that I will have to anyway copy over
the code for #1 above, so I can't escape the #ifdef-ing. Moreover,
since this is stack tracing code, it belongs (again, in my opinion)
to stacktrace.cc more than it does elsewhere.

(The lengths some people would go to avoid doing work... ;-))

By the way Bryce, in fallback_backtrace() why do you set the IP of
the faulting instruction to be the operand part of a "CALL <XYZ>"
instruction instead of the actual beginning of the instruction? You
then compensate for it in getLineNumberForFrame() by decrementing
the offset by one. Is it because the DWARF-2 unwinder has the
same curious behaviour?

In any case, I had commented out a bit too much in
getLineNumberForFrame() in my last patch with the result that
offset was always set to 0. With that rectified as in the attached
patch, I now get file and line number information as well in the
stack trace (if addr2line is in the PATH, of course):
- ------------------------------- 8< -------------------------------
Exception in thread "main" java.lang.Exception: I don't like you!
   at Hello.snafu(/home/rmathew/src/tmp/Hello.java:4)
   at Hello.bar(/home/rmathew/src/tmp/Hello.java:10)
   at Hello.foo(/home/rmathew/src/tmp/Hello.java:15)
- ------------------------------- 8< -------------------------------

This seems usable.

Just for reference, here's the source file in question:
- ------------------------------- 8< -------------------------------
      1 public class Hello
      2 {
      3   static void snafu( ) throws Exception
      4   {
      5     throw new Exception( "I don't like you!");
      6   }
      7
      8   static void bar( ) throws Exception
      9   {
     10     snafu( );
     11   }
     12
     13   static void foo( ) throws Exception
     14   {
     15     bar( );
     16   }
     17
     18   public static void main( String[] args) throws Exception
     19   {
     20     System.out.println( "Hello World!");
     21     foo( );
     22   }
     23 }
- ------------------------------- 8< -------------------------------

Thanks,
Ranjit.

- --
Ranjit Mathew       Email: rmathew AT gmail DOT com

Bangalore, INDIA.     Web: http://rmathew.com/




-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFEoJalYb1hx2wRS48RAtO9AJ4wvHoPt90p7Rli2D1CFF7u+3FKqwCggOyG
R2C7d9jfecNyfNluwz/glMs=
=AeZr
-----END PGP SIGNATURE-----
Index: ChangeLog
from  Ranjit Mathew  <rmathew@gcc.gnu.org>

	* sysdep/i386/backtrace.h (fallback_backtrace): Check that a potential
	frame pointer value is 32-bit word-aligned.  Use operand of the CALL
	instruction calling the current function to find its starting address.
	* stacktrace.cc: Include platform.h.
	(_Jv_StackTrace::getLineNumberForFrame): Use VirtualQuery() trick on
	Windows to find the module containing a given address.
	(_Jv_StackTrace::GetStackTraceElements): Use nCodeMap even for Windows.
	(_Jv_StackTrace::GetClassContext): Use fallback_backtrace() for
	targets with SJLJ exceptions instead of using _Unwind_Backtrace().
	(_Jv_StackTrace::GetFirstNonSystemClassLoader): Likewise.

Index: sysdep/i386/backtrace.h
===================================================================
--- sysdep/i386/backtrace.h	(revision 114838)
+++ sysdep/i386/backtrace.h	(working copy)
@@ -1,6 +1,6 @@
 // backtrace.h - Fallback backtrace implementation. i386 implementation.
 
-/* Copyright (C) 2005  Free Software Foundation
+/* Copyright (C) 2005, 2006  Free Software Foundation
 
    This file is part of libgcj.
 
@@ -29,12 +29,37 @@ fallback_backtrace (_Jv_UnwindState *sta
        rfp && i < state->length;
        rfp = *(unsigned int **)rfp)
     {
+      /* Sanity checks to eliminate dubious-looking frame pointer chains.
+         The frame pointer should be a 32-bit word-aligned stack address.
+         Since the stack grows downwards on x86, the frame pointer must have
+         a value greater than the current value of the stack pointer, it
+         should not be below the supposed next frame pointer and it should
+         not be too far off from the supposed next frame pointer.  */
       int diff = *rfp - (unsigned int)rfp;
-      if ((void*)rfp < _esp || diff > 4 * 1024 || diff < 0)
+      if (((unsigned int)rfp & 0x00000003) != 0 || (void*)rfp < _esp
+          || diff > 4 * 1024 || diff < 0)
         break;
 
+      /* Use the return address in the calling function stored just before
+         the current frame pointer to locate the address operand part of the
+         "CALL <XYZ>" instruction in the calling function that called this
+         function.  */
+      void *ip = (void*)(rfp[1] - 4);
+
+      /* Verify that the instruction at this position is a "CALL <XYZ>" and
+         use its operand to determine the starting address of the function
+         that this function had called.  0xE8 is the opcode for this CALL
+         instruction variant.  */
+      if (*(unsigned char *)((unsigned int)ip - 1) == 0xE8 && i > state->pos
+          && state->frames[i-1].type == frame_native)
+        {
+          state->frames[i-1].start_ip
+            = (void *)((unsigned int)ip + 4 + *(unsigned int *)ip);
+        }
+
       state->frames[i].type = frame_native;
-      state->frames[i].ip = (void*)(rfp[1]-4);
+      state->frames[i].ip = ip;
+
       i++;
     }
   state->pos = i;
Index: stacktrace.cc
===================================================================
--- stacktrace.cc	(revision 114838)
+++ stacktrace.cc	(working copy)
@@ -9,6 +9,7 @@ Libgcj License.  Please consult the file
 details.  */
 
 #include <config.h>
+#include <platform.h>
 
 #include <jvm.h>
 #include <gcj/cni.h>
@@ -184,6 +185,7 @@ _Jv_StackTrace::getLineNumberForFrame(_J
       return;
     }
 #endif
+
   // Use dladdr() to determine in which binary the address IP resides.
 #if defined (HAVE_DLFCN_H) && defined (HAVE_DLADDR)
   Dl_info info;
@@ -235,6 +237,71 @@ _Jv_StackTrace::getLineNumberForFrame(_J
         }
     }
 #endif
+
+#ifdef WIN32
+  void *ip = frame->ip;
+
+  // Since we do not have dladdr() on Windows, we use a trick involving
+  // VirtualQuery() to find the module (EXE or DLL) that contains a given
+  // address.  This was taken from Matt Pietrek's "Under the Hood" column
+  // for the April 1997 issue of Microsoft Systems Journal.
+
+  MEMORY_BASIC_INFORMATION mbi;
+
+  if (!VirtualQuery (ip, &mbi, sizeof (mbi)))
+  {
+    return;
+  }
+  
+  HMODULE hMod = (HMODULE) mbi.AllocationBase;
+
+  char moduleName[MAX_PATH];
+
+  // FIXME: We explicitly use the ANSI variant of the function here.
+  if (!GetModuleFileNameA (hMod, moduleName, sizeof (moduleName)))
+  {
+    return;
+  }
+
+  jstring binaryName = JvNewStringUTF (moduleName);
+  const char *argv0 = _Jv_GetSafeArg(0);
+
+  _Unwind_Ptr offset = 0;
+
+  // FIXME: Uncomment the following when we figure out how to handle these
+  // for Windows.
+  //
+  // if (*methodName == NULL && info.dli_sname)
+  //   *methodName = JvNewStringUTF (info.dli_sname);
+  // 
+  // // addr2line expects relative addresses for shared libraries.
+  // if (strcmp (info.dli_fname, argv0) == 0)
+  //   offset = (_Unwind_Ptr) ip;
+  // else
+  //   offset = (_Unwind_Ptr) ip - (_Unwind_Ptr) info.dli_fbase;
+
+  offset = (_Unwind_Ptr) ip;
+
+
+  // The unwinder gives us the return address. In order to get the right
+  // line number for the stack trace, roll it back a little.
+  offset -= 1;
+
+  finder->lookup (binaryName, (jlong) offset);
+  *sourceFileName = finder->getSourceFile();
+  *lineNum = finder->getLineNum();
+  if (*lineNum == -1 && NameFinder::showRaw())
+    {
+      gnu::gcj::runtime::StringBuffer *t =
+        new gnu::gcj::runtime::StringBuffer(binaryName);
+      t->append ((jchar)' ');
+      t->append ((jchar)'[');
+      // + 1 to compensate for the - 1 adjustment above;
+      t->append (Long::toHexString (offset + 1));
+      t->append ((jchar)']');
+      *sourceFileName = t->toString();
+    }
+#endif /* WIN32 */
 }
 
 // Look up class and method info for the given stack frame, setting 
@@ -283,7 +350,7 @@ _Jv_StackTrace::GetStackTraceElements (_
 {
   ArrayList *list = new ArrayList ();
 
-#ifdef SJLJ_EXCEPTIONS
+#if defined (SJLJ_EXCEPTIONS) && ! defined (WIN32)
   // We can't use the nCodeMap without unwinder support. Instead,
   // fake the method name by giving the IP in hex - better than nothing.  
   jstring hex = JvNewStringUTF ("0x");
@@ -302,7 +369,7 @@ _Jv_StackTrace::GetStackTraceElements (_
       list->add (element);
     }
 
-#else /* SJLJ_EXCEPTIONS */
+#else /* SJLJ_EXCEPTIONS && !WIN32 */
 
   //JvSynchronized (ncodeMap);
   UpdateNCodeMap ();
@@ -370,7 +437,7 @@ _Jv_StackTrace::GetStackTraceElements (_
     }
   
   finder->close();
-#endif /* SJLJ_EXCEPTIONS */
+#endif /* SJLJ_EXCEPTIONS && !WIN32 */
 
   JArray<Object *> *array = JvNewObjectArray (list->size (), 
     &StackTraceElement::class$, NULL);
@@ -472,7 +539,13 @@ _Jv_StackTrace::GetClassContext (jclass 
   //JvSynchronized (ncodeMap);
   UpdateNCodeMap ();
 
+#ifdef SJLJ_EXCEPTIONS
+  // The Unwind interface doesn't work with the SJLJ exception model.
+  // Fall back to a platform-specific unwinder.
+  fallback_backtrace (&state);
+#else /* SJLJ_EXCEPTIONS */  
   _Unwind_Backtrace (UnwindTraceFn, &state);
+#endif /* SJLJ_EXCEPTIONS */  
 
   // Count the number of Java frames on the stack.
   int jframe_count = 0;
@@ -543,7 +616,13 @@ _Jv_StackTrace::GetFirstNonSystemClassLo
   //JvSynchronized (ncodeMap);
   UpdateNCodeMap ();
   
+#ifdef SJLJ_EXCEPTIONS
+  // The Unwind interface doesn't work with the SJLJ exception model.
+  // Fall back to a platform-specific unwinder.
+  fallback_backtrace (&state);
+#else /* SJLJ_EXCEPTIONS */  
   _Unwind_Backtrace (UnwindTraceFn, &state);
+#endif /* SJLJ_EXCEPTIONS */  
 
   if (state.trace_data)
     return (ClassLoader *) state.trace_data;

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