This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows
- From: Ranjit Mathew <rmathew at gmail dot com>
- To: java-patches at gcc dot gnu dot org
- Date: Tue, 27 Jun 2006 20:58:39 +0530
- Subject: Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows
- Openpgp: url=http://ranjitmathew.hostingzero.com/aa_6C114B8F.txt
- References: <44A02481.207@gmail.com>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Ranjit Mathew wrote:
>
> The attached patch restores minimal stack traces on Windows.
Here's the third revision of this patch that implements Andrew's
suggestion. Since the other typedefs in javaprims.h seem to
omit the "_t" suffix, I've omitted it here as well.
By the way, the reason main() does not appear in the stack trace
is because it is called indirectly by call_main() in
"gnu/java/lang/natMainThread.cc". The generated code is something
like "CALL %8(%EBX)" instead of the normal "CALL xyz".
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 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFEoU6nYb1hx2wRS48RAlL3AKCL2bZAXXmfr0fjtRtBHaHXUgHs7ACgk1bm
UE/CpplrZchC5MEZNgZjH+s=
=vUSs
-----END PGP SIGNATURE-----
Index: ChangeLog
from Ranjit Mathew <rmathew@gcc.gnu.org>
* gcj/javaprims.h (_Jv_intptr, _Jv_uintptr): New typedefs similar to
intptr_t and uintptr_t in C99.
* 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: gcj/javaprims.h
===================================================================
--- gcj/javaprims.h (revision 114838)
+++ gcj/javaprims.h (working copy)
@@ -624,6 +624,11 @@ typedef unsigned short _Jv_ushort __attr
typedef unsigned int _Jv_uint __attribute__((__mode__(__SI__)));
typedef unsigned int _Jv_ulong __attribute__((__mode__(__DI__)));
+// Types to use when treating a pointer as an integer. Similar to
+// intptr_t and uintptr_t in C99.
+typedef int _Jv_intptr __attribute__((__mode__(__pointer__)));
+typedef unsigned int _Jv_uintptr __attribute__((__mode__(__pointer__)));
+
class _Jv_Utf8Const
{
_Jv_ushort hash;
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 *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 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;