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:
> 
> Yes, there does seem to be a lack of docs for dladdr(). But, I found this:
> 
> http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?coll=0650&db=man&fname=/usr/share/catman/p_man/cat3/libdl/dladdr.z
> 
> In particular:
> "The memory pointed to by *dli_fname* and *dli_sname* is within the 
> mapped object. If the object is closed with *dlclose()*, the pointers 
> become invalid."
> 
> So, its safe to assume you don't need a free().

Thanks a lot for confirming this. Since on Windows these are
still dynamically allocated, we need to free() them there.

The sixth revision of this patch (attached with this message)
takes care of this bit.

_Jv_uintptr has also been changed to _Jv_uintptr_t. The
_Jv_AddrInfo structure is still defined in java-stack.h,
but the _Jv_platform_dladdr() function is declared in the
respective platform.h. java-stack.h was pulling in a lot
of other headers, so I just forward-declare the _Jv_AddrInfo
structure and use it in the declaration.

I've tested this revision with an i686-pc-linux-gnu to
i686-pc-mingw32 cross-compiler. I hope to commit it
after testing with an i686-pc-linux-gnu native compiler
finishes successfully.

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

iD8DBQFEo1FxYb1hx2wRS48RAqa0AJ0a9yWkJDkU35jd2q0gIRezYq8/CACgjeG+
p6VJjuM3SoOYc2TaX0DRmF0=
=5uoN
-----END PGP SIGNATURE-----
Index: ChangeLog
from  Ranjit Mathew  <rmathew@gcc.gnu.org>

	* gcj/javaprims.h (_Jv_uintptr_t): New typedef similar to uintptr_t in
	C99.
	* include/java-stack.h: Include stdlib.h.
	(_Jv_AddrInfo): New structure to hold address information.
	* include/posix.h (_Jv_platform_dladdr): Declare.
	* include/win32.h (_Jv_platform_dladdr): Declare.
	(backtrace): Remove declaration.
	* 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.  Include platform.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 114838)
+++ 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_t __attribute__((__mode__(__pointer__)));
+
 class _Jv_Utf8Const
 {
   _Jv_ushort hash;
Index: include/java-stack.h
===================================================================
--- include/java-stack.h	(revision 114838)
+++ 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,35 @@ 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;
+
+  ~_Jv_AddrInfo (void)
+    {
+      // On systems with a real dladdr(), the file and symbol names given by
+      // _Jv_platform_dladdr() are not dynamically allocated.  On Windows,
+      // they are.
+
+#ifdef WIN32
+      if (file_name)
+        free ((void *)file_name);
+
+      if (sym_name)
+        free ((void *)sym_name);
+#endif /* WIN32 */
+    }
+};
 
 #endif /* __JV_STACKTRACE_H__ */
Index: include/posix.h
===================================================================
--- include/posix.h	(revision 114838)
+++ include/posix.h	(working copy)
@@ -194,4 +194,11 @@ _Jv_pipe (int filedes[2])
   return ::pipe (filedes);
 }
 
+// Forward declaration.  See java-stack.h for definition.
+struct _Jv_AddrInfo;
+
+// 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_POSIX_H__ */
Index: include/win32.h
===================================================================
--- include/win32.h	(revision 114838)
+++ include/win32.h	(working copy)
@@ -11,7 +11,7 @@ details.  */
 #ifndef __JV_WIN32_H__
 #define __JV_WIN32_H__
 
-// Enable UNICODE Support.?
+// Enable UNICODE support?
 
 #ifdef MINGW_LIBGCJ_UNICODE
 #define UNICODE
@@ -175,8 +175,11 @@ _Jv_platform_usleep (unsigned long usecs
 }
 #endif /* JV_HASH_SYNCHRONIZATION */
 
-/* Store up to SIZE return address of the current program state in
-   ARRAY and return the exact number of values stored.  */
-extern int backtrace (void **__array, int __size);
+// Forward declaration.  See java-stack.h for definition.
+struct _Jv_AddrInfo;
+
+// 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_WIN32_H__ */
Index: posix.cc
===================================================================
--- posix.cc	(revision 114838)
+++ 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 114838)
+++ 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,42 @@ _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;
+  }
+
+  char *file_name = (char *)(malloc (strlen (moduleName) + 1));
+  strcpy (file_name, moduleName);
+  info->file_name = file_name;
+
+  // FIXME.
+  info->base = NULL;
+  info->sym_name = NULL;
+  info->sym_addr = NULL;
+
+  return 1;
+}
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.
 
@@ -22,19 +22,44 @@ fallback_backtrace (_Jv_UnwindState *sta
 {
   register void *_ebp __asm__ ("ebp");
   register void *_esp __asm__ ("esp");
-  unsigned int *rfp;
+  _Jv_uintptr_t *rfp;
 
   int i = state->pos;
-  for (rfp = *(unsigned int**)_ebp;
+  for (rfp = *(_Jv_uintptr_t **)_ebp;
        rfp && i < state->length;
-       rfp = *(unsigned int **)rfp)
+       rfp = *(_Jv_uintptr_t **)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_t)rfp;
+      if (((_Jv_uintptr_t)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_t)ip - 1) == 0xE8 && i > state->pos
+          && state->frames[i-1].type == frame_native)
+        {
+          state->frames[i-1].start_ip
+            = (void *)((_Jv_uintptr_t)ip + 4 + *(_Jv_uintptr_t *)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,16 +9,13 @@ Libgcj License.  Please consult the file
 details.  */
 
 #include <config.h>
+#include <platform.h>
 
 #include <jvm.h>
 #include <gcj/cni.h>
 #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 +181,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 +226,6 @@ _Jv_StackTrace::getLineNumberForFrame(_J
           *sourceFileName = t->toString();
         }
     }
-#endif
 }
 
 // Look up class and method info for the given stack frame, setting 
@@ -283,7 +274,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 +293,7 @@ _Jv_StackTrace::GetStackTraceElements (_
       list->add (element);
     }
 
-#else /* SJLJ_EXCEPTIONS */
+#else /* SJLJ_EXCEPTIONS && !WIN32 */
 
   //JvSynchronized (ncodeMap);
   UpdateNCodeMap ();
@@ -370,7 +361,7 @@ _Jv_StackTrace::GetStackTraceElements (_
     }
   
   finder->close();
-#endif /* SJLJ_EXCEPTIONS */
+#endif /* SJLJ_EXCEPTIONS && !WIN32 */
 
   JArray<Object *> *array = JvNewObjectArray (list->size (), 
     &StackTraceElement::class$, NULL);
@@ -472,7 +463,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 +540,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]