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

Ranjit Mathew wrote:
> I will also test a native configuration on i686-pc-linux-gnu
> to exercise the POSIX code path.
> 
> OK to apply if that testing succeeds?

That testing did not succeed as-is. :-(

First off, I needed to add "const" qualifiers to file_name
and sym_name in _Jv_AddrInfo. Then I needed to cast the
arguments to the free() calls in the destructor for _Jv_AddrInfo
to "void *" to avoid errors complaining about invalid casts
from "const void *" to "void *".

More seriously, it appears that the free() calls in the
destructor are misguided - the effect ranges from a message
from glibc like "free(): invalid pointer 0x12345678!" to a
cryptic "Aborted".

There is no manual page for dladdr() on my system (though
there are for dlopen(), dlsym(), etc.) and online searches
didn't help much. Are the names not dynamically allocated
on all systems? I mean, is free() in this case non-kosher
on just glibc systems or is it the case for Solaris, etc.
as well?

Anyway, adding the "const" qualifiers and removing the
misguided free() calls make things work again on Linux.
I'm attaching the latest revision of this patch.

OK to apply?

Thanks,
Ranjit.

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

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


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

iD8DBQFEoopdYb1hx2wRS48RArq4AJ9SWhRYXcrp0P8Rhu1641v3OzQsVACfVnJS
9+usACwUUsq313icqsB7Gpg=
=xkhq
-----END PGP SIGNATURE-----
Index: ChangeLog
from  Ranjit Mathew  <rmathew@gcc.gnu.org>

	* gcj/javaprims.h (_Jv_uintptr): New typedef similar to uintptr_t in
	C99.
	* include/java-stack.h: Include stdlib.h.
	(_Jv_AddrInfo): New structure to hold address information.
	(_Jv_platform_dladdr): Declare.
	* posix.cc: Include dlfcn.h if available.  Include java-stack.h.
	(_Jv_platform_dladdr): Define.
	* win32.cc: Include string.h.  Include java-stack.h.
	(backtrace): Remove.
	(_Jv_platform_dladdr): Define.
	* 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: Do not include dlfcn.h.
	(_Jv_StackTrace::getLineNumberForFrame): Use _Jv_platform_dladdr()
	instead of dladdr().
	(_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: gcj/javaprims.h
===================================================================
--- gcj/javaprims.h	(revision 115049)
+++ gcj/javaprims.h	(working copy)
@@ -624,6 +624,10 @@ typedef unsigned short _Jv_ushort __attr
 typedef unsigned int _Jv_uint __attribute__((__mode__(__SI__)));
 typedef unsigned int _Jv_ulong __attribute__((__mode__(__DI__)));
 
+// The type to use when treating a pointer as an integer.  Similar to
+// uintptr_t in C99.
+typedef unsigned int _Jv_uintptr __attribute__((__mode__(__pointer__)));
+
 class _Jv_Utf8Const
 {
   _Jv_ushort hash;
Index: include/java-stack.h
===================================================================
--- include/java-stack.h	(revision 115049)
+++ include/java-stack.h	(working copy)
@@ -1,6 +1,6 @@
 // java-stack.h - Definitions for unwinding & inspecting the call stack.
 
-/* Copyright (C) 2005  Free Software Foundation
+/* Copyright (C) 2005, 2006  Free Software Foundation
 
    This file is part of libgcj.
 
@@ -11,6 +11,7 @@ details.  */
 #ifndef __JV_STACKTRACE_H__
 #define __JV_STACKTRACE_H__
 
+#include <stdlib.h>
 #include <unwind.h>
 
 #include <gcj/cni.h>
@@ -126,5 +127,21 @@ public:
   
 };
 
+// Information about a given address.
+struct _Jv_AddrInfo
+{
+  // File name of the defining module.
+  const char *file_name;
+  // Base address of the loaded module.
+  void *base;
+  // Name of the nearest symbol.
+  const char *sym_name;
+  // Address of the nearest symbol.
+  void *sym_addr;
+};
+
+// Given an address, determine the executable or shared object that defines
+// it and the nearest named symbol.
+extern int _Jv_platform_dladdr (const void *addr, _Jv_AddrInfo *info);
 
 #endif /* __JV_STACKTRACE_H__ */
Index: posix.cc
===================================================================
--- posix.cc	(revision 115049)
+++ posix.cc	(working copy)
@@ -17,7 +17,12 @@ details.  */
 #include <signal.h>
 #include <stdio.h>
 
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
 #include <jvm.h>
+#include <java-stack.h>
 #include <java/lang/Thread.h>
 #include <java/io/InterruptedIOException.h>
 #include <java/util/Properties.h>
@@ -203,3 +208,31 @@ _Jv_select (int n, fd_set *readfds, fd_s
   return 0;
 #endif
 }
+
+// Given an address, find the object that defines it and the nearest
+// defined symbol to that address.  Returns 0 if no object defines this
+// address.
+int
+_Jv_platform_dladdr (const void *addr, _Jv_AddrInfo *info)
+{
+  int ret_val = 0;
+
+#if defined (HAVE_DLFCN_H) && defined (HAVE_DLADDR)
+  Dl_info addr_info;
+  ret_val = dladdr (addr, &addr_info);
+  if (ret_val != 0)
+    {
+      info->file_name = addr_info.dli_fname;
+      info->base = addr_info.dli_fbase;
+      info->sym_name = addr_info.dli_sname;
+      info->sym_addr = addr_info.dli_saddr;
+    }
+#else
+  info->file_name = NULL;
+  info->base = NULL;
+  info->sym_name = NULL;
+  info->sym_addr = NULL;
+#endif
+
+  return ret_val;
+}
Index: win32.cc
===================================================================
--- win32.cc	(revision 115049)
+++ win32.cc	(working copy)
@@ -12,8 +12,11 @@ details.  */
 #include <platform.h>
 #include <sys/timeb.h>
 #include <stdlib.h>
+#include <string.h>
 #include <fcntl.h>
 
+#include <java-stack.h>
+
 #include <java/lang/ArithmeticException.h>
 #include <java/lang/UnsupportedOperationException.h>
 #include <java/io/IOException.h>
@@ -442,28 +445,6 @@ _Jv_platform_initProperties (java::util:
     }
 }
 
-/* Store up to SIZE return address of the current program state in
-   ARRAY and return the exact number of values stored.  */
-int
-backtrace (void **__array, int __size)
-{
-  register void *_ebp __asm__ ("ebp");
-  register void *_esp __asm__ ("esp");
-  unsigned int *rfp;
-
-  int i=0;
-  for (rfp = *(unsigned int**)_ebp;
-       rfp && i < __size;
-       rfp = *(unsigned int **)rfp)
-    {
-      int diff = *rfp - (unsigned int)rfp;
-      if ((void*)rfp < _esp || diff > 4 * 1024 || diff < 0) break;
-
-    __array[i++] = (void*)(rfp[1]-4);
-  }
-  return i;
-}
-
 int
 _Jv_pipe (int filedes[2])
 {
@@ -477,3 +458,41 @@ _Jv_platform_close_on_exec (HANDLE h)
   // no effect under Win9X.
   SetHandleInformation (h, HANDLE_FLAG_INHERIT, 0);
 }
+
+// Given an address, find the object that defines it and the nearest
+// defined symbol to that address.  Returns 0 if no object defines this
+// address.
+int
+_Jv_platform_dladdr (const void *addr, _Jv_AddrInfo *info)
+{
+  // 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 (addr, &mbi, sizeof (mbi)))
+  {
+    return 0;
+  }
+  
+  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 0;
+  }
+
+  info->file_name = (char *)(malloc (strlen (moduleName) + 1));
+  strcpy (info->file_name, moduleName);
+
+  // FIXME.
+  info->base = NULL;
+  info->sym_name = NULL;
+  info->sym_addr = NULL;
+
+  return 1;
+}
Index: sysdep/i386/backtrace.h
===================================================================
--- sysdep/i386/backtrace.h	(revision 115049)
+++ 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.
 
@@ -22,19 +22,44 @@ fallback_backtrace (_Jv_UnwindState *sta
 {
   register void *_ebp __asm__ ("ebp");
   register void *_esp __asm__ ("esp");
-  unsigned int *rfp;
+  _Jv_uintptr *rfp;
 
   int i = state->pos;
-  for (rfp = *(unsigned int**)_ebp;
+  for (rfp = *(_Jv_uintptr **)_ebp;
        rfp && i < state->length;
-       rfp = *(unsigned int **)rfp)
+       rfp = *(_Jv_uintptr **)rfp)
     {
-      int diff = *rfp - (unsigned int)rfp;
-      if ((void*)rfp < _esp || diff > 4 * 1024 || diff < 0)
+      /* 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 - (_Jv_uintptr)rfp;
+      if (((_Jv_uintptr)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 *)((_Jv_uintptr)ip - 1) == 0xE8 && i > state->pos
+          && state->frames[i-1].type == frame_native)
+        {
+          state->frames[i-1].start_ip
+            = (void *)((_Jv_uintptr)ip + 4 + *(_Jv_uintptr *)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 115049)
+++ stacktrace.cc	(working copy)
@@ -15,10 +15,6 @@ details.  */
 #include <java-interp.h>
 #include <java-stack.h>
 
-#ifdef HAVE_DLFCN_H
-#include <dlfcn.h>
-#endif
-
 #include <stdio.h>
 
 #include <java/lang/Class.h>
@@ -184,41 +180,36 @@ _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;
+
+  // Use _Jv_platform_dladdr() to determine in which binary the address IP
+  // resides.
+  _Jv_AddrInfo info;
   jstring binaryName = NULL;
   const char *argv0 = _Jv_GetSafeArg(0);
 
   void *ip = frame->ip;
   _Unwind_Ptr offset = 0;
 
-  if (dladdr (ip, &info))
+  if (_Jv_platform_dladdr (ip, &info))
     {
-      if (info.dli_fname)
-	binaryName = JvNewStringUTF (info.dli_fname);
+      if (info.file_name)
+	binaryName = JvNewStringUTF (info.file_name);
       else
         return;
 
-      if (*methodName == NULL && info.dli_sname)
-	*methodName = JvNewStringUTF (info.dli_sname);
+      if (*methodName == NULL && info.sym_name)
+	*methodName = JvNewStringUTF (info.sym_name);
 
       // addr2line expects relative addresses for shared libraries.
-      if (strcmp (info.dli_fname, argv0) == 0)
+      if (strcmp (info.file_name, argv0) == 0)
         offset = (_Unwind_Ptr) ip;
       else
-        offset = (_Unwind_Ptr) ip - (_Unwind_Ptr) info.dli_fbase;
+        offset = (_Unwind_Ptr) ip - (_Unwind_Ptr) info.base;
 
-      //printf ("linenum ip: %p\n", ip);
-      //printf ("%s: 0x%x\n", info.dli_fname, offset);
-      //offset -= sizeof(void *);
-      
       // 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;
 
-      // printf ("%s: 0x%x\n", info.dli_fname, offset);
-      
       finder->lookup (binaryName, (jlong) offset);
       *sourceFileName = finder->getSourceFile();
       *lineNum = finder->getLineNum();
@@ -234,7 +225,6 @@ _Jv_StackTrace::getLineNumberForFrame(_J
           *sourceFileName = t->toString();
         }
     }
-#endif
 }
 
 // Look up class and method info for the given stack frame, setting 
@@ -283,7 +273,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 +292,7 @@ _Jv_StackTrace::GetStackTraceElements (_
       list->add (element);
     }
 
-#else /* SJLJ_EXCEPTIONS */
+#else /* SJLJ_EXCEPTIONS && !WIN32 */
 
   //JvSynchronized (ncodeMap);
   UpdateNCodeMap ();
@@ -370,7 +360,7 @@ _Jv_StackTrace::GetStackTraceElements (_
     }
   
   finder->close();
-#endif /* SJLJ_EXCEPTIONS */
+#endif /* SJLJ_EXCEPTIONS && !WIN32 */
 
   JArray<Object *> *array = JvNewObjectArray (list->size (), 
     &StackTraceElement::class$, NULL);
@@ -472,7 +462,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 +539,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]