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


Hello Ranjit

Great Work! Thank you for that.
With this stacktraces and reflection rewritten to not use the stack unwinder, windows becomes more and more useful.



> 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):

Question to that: I assume this only applies when classes aren't compiled into an executable? For compiled classes, I get:
at Hello.foo(outWin.exe)
instead your mentioned
at Hello.foo(/home/rmathew/src/tmp/Hello.java:15)


just curious...

regards
Marco


Ranjit Mathew wrote:
-----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]