* [MinGW] RFC/RFA: Get Partial Stack Traces on Windows @ 2006-06-26 18:16 Ranjit Mathew 2006-06-26 18:38 ` Bryce McKinlay ` (3 more replies) 0 siblings, 4 replies; 36+ messages in thread From: Ranjit Mathew @ 2006-06-26 18:16 UTC (permalink / raw) To: GCJ Patches; +Cc: mckinlay [-- Attachment #1: Type: text/plain, Size: 2421 bytes --] -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 [Bryce, CC-ing you explicitly to get your opinion on the fallback_backtrace() changes.] Hello, For targets like Windows that use SJLJ EH, GCJ-compiled programmes only get hexadecimal addresses in stacktraces and also crash on some exceptions as shown in: http://gcc.gnu.org/ml/java/2006-04/msg00025.html The attached patch restores minimal stack traces on Windows. I now get: - --------------------------- 8< --------------------------- Exception in thread "main" java.lang.Exception: I don't like you! at Hello.snafu(a.exe) at Hello.bar(a.exe) at Hello.foo(a.exe) - --------------------------- 8< --------------------------- (The sequence was main()->foo()->bar()->snafu() - I don't know yet why main() is not listed in the trace. :-( ) The crux of the patch is the change to fallback_backtrace() that uses the fact that a "CALL <XYZ>" instruction can be used to determine the starting address of the called function. (The operand is the offset of the called function relative to the address of the *next* instruction after the CALL.) Since it took me a while to understand what was going on in this function, I took the liberty of adding copious comments to this function to explain what is going on for the benefit of lesser mortals like yours truly. I also added another sanity test for a frame pointer value - it should be aligned on a word boundary. The second part of the patch uses a VirtualQuery()-based trick shown by Matt Pietrek in his "Under The Hood" column for the April '97 issue of MSJ to determine the module that contains a given address. This is because Windows does not have a direct equivalent of dladdr() as far as I can tell (I'm not a Windows programmer, so I might be quite wrong on this). The third part of the patch just ensures that we do not call _Unwind_Backtrace() for non-SJLJ targets as shown by the message linked above. Tested lightly using an i686-pc-linux-gnu to i686-pc-mingw32 cross-compiler. Comments? Suggestions? Approvals? ;-) 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 iD8DBQFEoCSBYb1hx2wRS48RAqBeAJ4+nJapJ8bjLC3WS8v2YiM0UHHXRQCgiC4o Iay49ETYGKAZocPmjAmujvo= =mGTH -----END PGP SIGNATURE----- [-- Attachment #2: p1.txt --] [-- Type: text/plain, Size: 7192 bytes --] 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> @@ -235,6 +236,68 @@ _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; + + // 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 +346,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 +365,7 @@ _Jv_StackTrace::GetStackTraceElements (_ list->add (element); } -#else /* SJLJ_EXCEPTIONS */ +#else /* SJLJ_EXCEPTIONS && !WIN32 */ //JvSynchronized (ncodeMap); UpdateNCodeMap (); @@ -370,7 +433,7 @@ _Jv_StackTrace::GetStackTraceElements (_ } finder->close(); -#endif /* SJLJ_EXCEPTIONS */ +#endif /* SJLJ_EXCEPTIONS && !WIN32 */ JArray<Object *> *array = JvNewObjectArray (list->size (), &StackTraceElement::class$, NULL); @@ -472,7 +535,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 +612,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; ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-26 18:16 [MinGW] RFC/RFA: Get Partial Stack Traces on Windows Ranjit Mathew @ 2006-06-26 18:38 ` Bryce McKinlay 2006-06-27 2:23 ` Ranjit Mathew 2006-06-26 19:06 ` Andrew Haley ` (2 subsequent siblings) 3 siblings, 1 reply; 36+ messages in thread From: Bryce McKinlay @ 2006-06-26 18:38 UTC (permalink / raw) To: Ranjit Mathew; +Cc: GCJ Patches Ranjit Mathew wrote: > The second part of the patch uses a VirtualQuery()-based trick > shown by Matt Pietrek in his "Under The Hood" column for the > April '97 issue of MSJ to determine the module that contains > a given address. This is because Windows does not have a direct > equivalent of dladdr() as far as I can tell (I'm not a Windows > programmer, so I might be quite wrong on this). > > The third part of the patch just ensures that we do not > call _Unwind_Backtrace() for non-SJLJ targets as shown by > the message linked above. > > Tested lightly using an i686-pc-linux-gnu to i686-pc-mingw32 > cross-compiler. > > Comments? Suggestions? Approvals? ;-) > 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. Bryce ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-26 18:38 ` Bryce McKinlay @ 2006-06-27 2:23 ` Ranjit Mathew 2006-06-27 11:27 ` Marco Trudel 2006-06-27 21:20 ` Bryce McKinlay 0 siblings, 2 replies; 36+ messages in thread From: Ranjit Mathew @ 2006-06-27 2:23 UTC (permalink / raw) To: Bryce McKinlay; +Cc: GCJ Patches [-- Attachment #1: Type: text/plain, Size: 2967 bytes --] -----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----- [-- Attachment #2: p1.txt --] [-- Type: text/plain, Size: 7461 bytes --] 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; ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-27 2:23 ` Ranjit Mathew @ 2006-06-27 11:27 ` Marco Trudel 2006-06-27 11:38 ` Ranjit Mathew 2006-06-27 21:20 ` Bryce McKinlay 1 sibling, 1 reply; 36+ messages in thread From: Marco Trudel @ 2006-06-27 11:27 UTC (permalink / raw) To: Ranjit Mathew; +Cc: GCJ Patches 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; ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-27 11:27 ` Marco Trudel @ 2006-06-27 11:38 ` Ranjit Mathew 2006-06-27 12:37 ` Marco Trudel 0 siblings, 1 reply; 36+ messages in thread From: Ranjit Mathew @ 2006-06-27 11:38 UTC (permalink / raw) To: Marco Trudel; +Cc: GCJ Patches On 6/27/06, Marco Trudel <mtrudel@gmx.ch> wrote: > > 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) You need to have compiled "Hello.java" with debugging information turned on. For example: gcj -g --main=Hello Hello.java You also need to ensure that "addr2line" (from binutils) is accessible from your PATH. If you have the MinGW "bin" folder in your PATH, that should suffice. HTH, Ranjit. -- Ranjit Mathew Email: rmathew AT gmail DOT com Bangalore, INDIA. Web: http://rmathew.com/ ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-27 11:38 ` Ranjit Mathew @ 2006-06-27 12:37 ` Marco Trudel 2006-06-27 13:14 ` Ranjit Mathew 0 siblings, 1 reply; 36+ messages in thread From: Marco Trudel @ 2006-06-27 12:37 UTC (permalink / raw) To: Ranjit Mathew; +Cc: GCJ Patches Ranjit Mathew wrote: > On 6/27/06, Marco Trudel <mtrudel@gmx.ch> wrote: > >> > 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) > > > You need to have compiled "Hello.java" with debugging > information turned on. For example: > > gcj -g --main=Hello Hello.java > > You also need to ensure that "addr2line" (from binutils) > is accessible from your PATH. If you have the MinGW > "bin" folder in your PATH, that should suffice. Ah, yes. It works as long as I do not strip the final executable. What does -g do? jc1 only tells me "Generate debug information in default format" and the executables keep the same size as withhout -g... Will it be slower? regards Marco ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-27 12:37 ` Marco Trudel @ 2006-06-27 13:14 ` Ranjit Mathew 0 siblings, 0 replies; 36+ messages in thread From: Ranjit Mathew @ 2006-06-27 13:14 UTC (permalink / raw) To: Marco Trudel; +Cc: GCJ Patches On 6/27/06, Marco Trudel <mtrudel@gmx.ch> wrote: > > Ah, yes. It works as long as I do not strip the final executable. > What does -g do? jc1 only tells me "Generate debug information in > default format" and the executables keep the same size as withhout -g... "-g" asks the compiler to include debugging information that you can use in a debugger like GDB. You should compare the output (the "Hello.s" file) of these two commands: gcj -g -S Hello.java gcj -S Hello.java to see the difference. GCC/MinGW uses the "stabs" debugging format while GCC/Linux uses the DWARF-2 debugging format. Since the debugging information maps programme instructions to source files and line numbers, GCJ uses this information (via addr2line) to show the source files and line numbers in an exception stack trace. > Will it be slower? As far as I know, no. HTH, Ranjit. -- Ranjit Mathew Email: rmathew AT gmail DOT com Bangalore, INDIA. Web: http://rmathew.com/ ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-27 2:23 ` Ranjit Mathew 2006-06-27 11:27 ` Marco Trudel @ 2006-06-27 21:20 ` Bryce McKinlay 1 sibling, 0 replies; 36+ messages in thread From: Bryce McKinlay @ 2006-06-27 21:20 UTC (permalink / raw) To: Ranjit Mathew; +Cc: GCJ Patches Ranjit Mathew wrote: > 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. > I was thinking you'd just move out the windows-specific part to a "platform_getLineNumberForFrame()" (or whatever), which would be called from getLineNumberForFrame() after the interpreter-specific code. I agree, we don't want to duplicate the interpreter part of the code. > 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? > Actually I didn't write fallback_backtrace(), that code was already there - but this sounds about right. The Dwarf-2 unwinder normally gives us the return address, which needs to be rolled back in order to get the correct line number. But, in the case of a frame where a fault occurred, it gives the address of the faulting instruction. I think there is actually a slight bug with this on Dwarf-2 since the "decrement the offset by one" isn't correct for a segfault/NullPointerException frame, so sometimes we get the wrong line number. Bryce ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-26 18:16 [MinGW] RFC/RFA: Get Partial Stack Traces on Windows Ranjit Mathew 2006-06-26 18:38 ` Bryce McKinlay @ 2006-06-26 19:06 ` Andrew Haley 2006-06-26 19:15 ` Tom Tromey 2006-06-27 1:29 ` Ranjit Mathew 2006-06-27 15:28 ` Ranjit Mathew 2006-06-28 2:51 ` Ranjit Mathew 3 siblings, 2 replies; 36+ messages in thread From: Andrew Haley @ 2006-06-26 19:06 UTC (permalink / raw) To: Ranjit Mathew; +Cc: GCJ Patches, mckinlay Ranjit Mathew writes: > -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA1 > > [Bryce, CC-ing you explicitly to get your opinion on the > fallback_backtrace() changes.] > > iD8DBQFEoCSBYb1hx2wRS48RAqBeAJ4+nJapJ8bjLC3WS8v2YiM0UHHXRQCgiC4o > Iay49ETYGKAZocPmjAmujvo= > =mGTH > -----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. What happens with -findirect-dispatch? > 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 Don't use unsigned int for a pointer; instead use int __attribute__((mode(pointer))). This is an int that is exactly the same size as a pointer. Andrew. ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-26 19:06 ` Andrew Haley @ 2006-06-26 19:15 ` Tom Tromey 2006-06-26 19:20 ` Andrew Haley 2006-06-27 1:29 ` Ranjit Mathew 1 sibling, 1 reply; 36+ messages in thread From: Tom Tromey @ 2006-06-26 19:15 UTC (permalink / raw) To: Andrew Haley; +Cc: Ranjit Mathew, GCJ Patches, mckinlay >>>>> "Andrew" == Andrew Haley <aph@redhat.com> writes: >> - if ((void*)rfp < _esp || diff > 4 * 1024 || diff < 0) >> + if (((unsigned int)rfp & 0x00000003) != 0 || (void*)rfp < _esp Andrew> Don't use unsigned int for a pointer; instead use Andrew> int __attribute__((mode(pointer))). This is an int that is exactly the Andrew> same size as a pointer. Maybe we ought to add a _Jv_intptr_t typedef somewhere. Tom ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-26 19:15 ` Tom Tromey @ 2006-06-26 19:20 ` Andrew Haley 0 siblings, 0 replies; 36+ messages in thread From: Andrew Haley @ 2006-06-26 19:20 UTC (permalink / raw) To: Tom Tromey; +Cc: Ranjit Mathew, GCJ Patches, mckinlay Tom Tromey writes: > >>>>> "Andrew" == Andrew Haley <aph@redhat.com> writes: > > >> - if ((void*)rfp < _esp || diff > 4 * 1024 || diff < 0) > >> + if (((unsigned int)rfp & 0x00000003) != 0 || (void*)rfp < _esp > > Andrew> Don't use unsigned int for a pointer; instead use > Andrew> int __attribute__((mode(pointer))). This is an int that is exactly the > Andrew> same size as a pointer. > > Maybe we ought to add a _Jv_intptr_t typedef somewhere. Mmm. It's used in a few places. Andrew. ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-26 19:06 ` Andrew Haley 2006-06-26 19:15 ` Tom Tromey @ 2006-06-27 1:29 ` Ranjit Mathew 2006-06-27 8:33 ` Andrew Haley 2006-07-05 9:22 ` Ranjit Mathew 1 sibling, 2 replies; 36+ messages in thread From: Ranjit Mathew @ 2006-06-27 1:29 UTC (permalink / raw) To: Andrew Haley; +Cc: GCJ Patches -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Andrew Haley wrote: > What happens with -findirect-dispatch? The stack-trace changes to: - --------------------------- 8< --------------------------- Exception in thread "main" java.lang.Exception: I don't like you! at java.lang.VMThrowable.fillInStackTrace(a.exe) at java.lang.Throwable.<init>(a.exe) - --------------------------- 8< --------------------------- However, I don't think the BC-ABI works on Windows as of now - I was actually expecting an ICE or a segfault somewhere and was quite surprised when neither of them happened. :-) - - if ((void*)rfp < _esp || diff > 4 * 1024 || diff < 0) + if (((unsigned int)rfp & 0x00000003) != 0 || (void*)rfp < _esp > Don't use unsigned int for a pointer; instead use > int __attribute__((mode(pointer))). This is an int that is exactly the > same size as a pointer. I was just following in the illustrious footsteps of Bryce. ;-) Considering that this file (sysdep/i386/backtrace.h) is x86-specific and is anyway full of otherwise utterly-non-portable-C stuff, does it matter? (The glibc 2.3.6 stdint.h header on my system just defines uintptr_t as "unsigned int" for non-64-bit systems.) 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 iD8DBQFEoIoGYb1hx2wRS48RAlLOAJ90dc0jwWgqTQW2xS7OcNz/atge1wCeJkz6 7h647AkFvcbzVpehwJcS3MM= =crhd -----END PGP SIGNATURE----- ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-27 1:29 ` Ranjit Mathew @ 2006-06-27 8:33 ` Andrew Haley 2006-06-27 9:11 ` Ranjit Mathew 2006-07-05 9:22 ` Ranjit Mathew 1 sibling, 1 reply; 36+ messages in thread From: Andrew Haley @ 2006-06-27 8:33 UTC (permalink / raw) To: Ranjit Mathew; +Cc: GCJ Patches Ranjit Mathew writes: > -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA1 > > Andrew Haley wrote: > > What happens with -findirect-dispatch? > > The stack-trace changes to: > - --------------------------- 8< --------------------------- > Exception in thread "main" java.lang.Exception: I don't like you! > at java.lang.VMThrowable.fillInStackTrace(a.exe) > at java.lang.Throwable.<init>(a.exe) > - --------------------------- 8< --------------------------- > > However, I don't think the BC-ABI works on Windows as of > now - I was actually expecting an ICE or a segfault somewhere > and was quite surprised when neither of them happened. :-) OK. Fair enough. > > - - if ((void*)rfp < _esp || diff > 4 * 1024 || diff < 0) > + if (((unsigned int)rfp & 0x00000003) != 0 || (void*)rfp < _esp > > > Don't use unsigned int for a pointer; instead use > > int __attribute__((mode(pointer))). This is an int that is exactly the > > same size as a pointer. > > I was just following in the illustrious footsteps of Bryce. ;-) Everything can be improved, y'know! > Considering that this file (sysdep/i386/backtrace.h) is x86-specific > and is anyway full of otherwise utterly-non-portable-C stuff, does > it matter? It deson't affect correctness, it's just a matter of clarity. It's effectively saying "this is a pointer that holds an int." Andrew. ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-27 8:33 ` Andrew Haley @ 2006-06-27 9:11 ` Ranjit Mathew 2006-06-27 9:29 ` Andrew Haley 2006-06-27 21:57 ` Bryce McKinlay 0 siblings, 2 replies; 36+ messages in thread From: Ranjit Mathew @ 2006-06-27 9:11 UTC (permalink / raw) To: Andrew Haley; +Cc: GCJ Patches -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Andrew Haley wrote: > Ranjit Mathew writes: > > - - if ((void*)rfp < _esp || diff > 4 * 1024 || diff < 0) > > + if (((unsigned int)rfp & 0x00000003) != 0 || (void*)rfp < _esp > > > > > Don't use unsigned int for a pointer; instead use > > > int __attribute__((mode(pointer))). This is an int that is exactly the > > > same size as a pointer. > > > > I was just following in the illustrious footsteps of Bryce. ;-) > > Everything can be improved, y'know! I was just kidding. > > Considering that this file (sysdep/i386/backtrace.h) is x86-specific > > and is anyway full of otherwise utterly-non-portable-C stuff, does > > it matter? > > It deson't affect correctness, it's just a matter of clarity. It's > effectively saying "this is a pointer that holds an int." Should I add something like the following for use everywhere or do you prefer something more localised to "sysdep/i386/backtrace.h"? - ---------------------------- 8< ---------------------------- Index: gcj/javaprims.h =================================================================== - --- gcj/javaprims.h (revision 115032) +++ gcj/javaprims.h (working copy) @@ -623,6 +623,8 @@ extern "C" void _Jv_RegisterClassHookDef typedef unsigned short _Jv_ushort __attribute__((__mode__(__HI__))); typedef unsigned int _Jv_uint __attribute__((__mode__(__SI__))); typedef unsigned int _Jv_ulong __attribute__((__mode__(__DI__))); +typedef int _Jv_intptr __attribute__((mode(pointer))); +typedef unsigned int _Jv_uintptr __attribute__((mode(pointer))); class _Jv_Utf8Const { - ---------------------------- 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.2 (MingW32) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFEoPYYYb1hx2wRS48RAir0AJ9x8+yJAmVdUuxJc1B31cVx338zUQCfZWiQ h54R+/XAyAXxs316yqRr6dw= =KEl+ -----END PGP SIGNATURE----- ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-27 9:11 ` Ranjit Mathew @ 2006-06-27 9:29 ` Andrew Haley 2006-06-27 21:57 ` Bryce McKinlay 1 sibling, 0 replies; 36+ messages in thread From: Andrew Haley @ 2006-06-27 9:29 UTC (permalink / raw) To: Ranjit Mathew; +Cc: GCJ Patches Ranjit Mathew writes: > -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA1 > > Andrew Haley wrote: > > Ranjit Mathew writes: > > > - - if ((void*)rfp < _esp || diff > 4 * 1024 || diff < 0) > > > + if (((unsigned int)rfp & 0x00000003) != 0 || (void*)rfp < _esp > > > > > > > Don't use unsigned int for a pointer; instead use > > > > int __attribute__((mode(pointer))). This is an int that is exactly the > > > > same size as a pointer. > > > > > > I was just following in the illustrious footsteps of Bryce. ;-) > > > > Everything can be improved, y'know! > > I was just kidding. OK. I guessed so. > > > Considering that this file (sysdep/i386/backtrace.h) is x86-specific > > > and is anyway full of otherwise utterly-non-portable-C stuff, does > > > it matter? > > > > It deson't affect correctness, it's just a matter of clarity. It's > > effectively saying "this is a pointer that holds an int." > > Should I add something like the following for use everywhere Sure. tromey suggested that, and I agree. > or do you prefer something more localised to > "sysdep/i386/backtrace.h"? I think tromey's suggestion of _Jv_intptr_t was better, as it emphasizes the link with the optional intptr_t. Having said that, is there any need for a signed pointer? I don't think so. It makes the most sense to define _Jv_uintptr_t. But we're in danger of arguing about what colour to paint the bicycle shed... I've caused enough trouble already; I'm outta here! :-) Andrew. ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-27 9:11 ` Ranjit Mathew 2006-06-27 9:29 ` Andrew Haley @ 2006-06-27 21:57 ` Bryce McKinlay 1 sibling, 0 replies; 36+ messages in thread From: Bryce McKinlay @ 2006-06-27 21:57 UTC (permalink / raw) To: Ranjit Mathew; +Cc: Andrew Haley, GCJ Patches Ranjit Mathew wrote: > Andrew Haley wrote: > >> Ranjit Mathew writes: >> > - - if ((void*)rfp < _esp || diff > 4 * 1024 || diff < 0) >> > + if (((unsigned int)rfp & 0x00000003) != 0 || (void*)rfp < _esp >> > >> > > Don't use unsigned int for a pointer; instead use >> > > int __attribute__((mode(pointer))). This is an int that is exactly the >> > > same size as a pointer. >> > >> > I was just following in the illustrious footsteps of Bryce. ;-) >> >> Everything can be improved, y'know! >> > > I was just kidding. > FWIW, I didn't write that (fallback_backtrace) code - it was just copied/moved from win32.cc. Actually, I think the old backtrace() in win32.cc can be deleted now. Bryce ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-27 1:29 ` Ranjit Mathew 2006-06-27 8:33 ` Andrew Haley @ 2006-07-05 9:22 ` Ranjit Mathew 2006-07-05 15:08 ` Bryce McKinlay 1 sibling, 1 reply; 36+ messages in thread From: Ranjit Mathew @ 2006-07-05 9:22 UTC (permalink / raw) To: java-patches -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Ranjit Mathew wrote: > Andrew Haley wrote: >>> What happens with -findirect-dispatch? > > The stack-trace changes to: > --------------------------- 8< --------------------------- > Exception in thread "main" java.lang.Exception: I don't like you! > at java.lang.VMThrowable.fillInStackTrace(a.exe) > at java.lang.Throwable.<init>(a.exe) > --------------------------- 8< --------------------------- Since methods are called via the atable, the calling instruction is no longer a simple "CALL <XYZ>". The hack in fallback_backtrace() therefore doesn't work for methods called via indirect dispatch. 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 iD8DBQFEq4R9Yb1hx2wRS48RAjLeAJ9rVvX1eza8wdbwhtBvuikJ7S/LSgCfQoVj 5+1En2FwUuqXnsTBwLutF+c= =dSwC -----END PGP SIGNATURE----- ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-07-05 9:22 ` Ranjit Mathew @ 2006-07-05 15:08 ` Bryce McKinlay 2006-07-05 15:51 ` Ranjit Mathew 0 siblings, 1 reply; 36+ messages in thread From: Bryce McKinlay @ 2006-07-05 15:08 UTC (permalink / raw) To: Ranjit Mathew; +Cc: java-patches Ranjit Mathew wrote: >> The stack-trace changes to: >> --------------------------- 8< --------------------------- >> Exception in thread "main" java.lang.Exception: I don't like you! >> at java.lang.VMThrowable.fillInStackTrace(a.exe) >> at java.lang.Throwable.<init>(a.exe) >> --------------------------- 8< --------------------------- >> > > Since methods are called via the atable, the calling > instruction is no longer a simple "CALL <XYZ>". The > hack in fallback_backtrace() therefore doesn't work > for methods called via indirect dispatch. > I don't think it'll work for ordinary virtual calls either, or even non-local calls in a non-static-linked binary. Bryce ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-07-05 15:08 ` Bryce McKinlay @ 2006-07-05 15:51 ` Ranjit Mathew 2006-07-05 16:07 ` Bryce McKinlay 0 siblings, 1 reply; 36+ messages in thread From: Ranjit Mathew @ 2006-07-05 15:51 UTC (permalink / raw) To: Bryce McKinlay; +Cc: java-patches On 7/5/06, Bryce McKinlay <mckinlay@redhat.com> wrote: > > Since methods are called via the atable, the calling > > instruction is no longer a simple "CALL <XYZ>". The > > hack in fallback_backtrace() therefore doesn't work > > for methods called via indirect dispatch. > > > > I don't think it'll work for ordinary virtual calls either, or even > non-local calls in a non-static-linked binary. Yes. I realise that my testing was always with static methods. :-( Perhaps we should try to resurrect the c++filt bit in NameFinder for such severely crippled targets... Thanks, Ranjit. -- Ranjit Mathew Email: rmathew AT gmail DOT com Bangalore, INDIA. Web: http://rmathew.com/ ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-07-05 15:51 ` Ranjit Mathew @ 2006-07-05 16:07 ` Bryce McKinlay 2006-07-05 17:13 ` Ranjit Mathew 0 siblings, 1 reply; 36+ messages in thread From: Bryce McKinlay @ 2006-07-05 16:07 UTC (permalink / raw) To: Ranjit Mathew; +Cc: java-patches Ranjit Mathew wrote: > Yes. I realise that my testing was always with static > methods. :-( > > Perhaps we should try to resurrect the c++filt bit in > NameFinder for such severely crippled targets... Using dlsym/c++filt is a poor solution because it is slow and relies on a binary which (windows) users are not likely to have installed. Even if we were to parse the method symbols directly, this approach is not usable for security and calling-class checks. A better solution, I think, would be to add a "method length" field to class metadata. This would mean that we could build a data structure of IP ranges and use that to find the method based just on the IP. A little tricky for CNI methods, though. Bryce ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-07-05 16:07 ` Bryce McKinlay @ 2006-07-05 17:13 ` Ranjit Mathew 2006-07-05 17:20 ` Andrew Haley 0 siblings, 1 reply; 36+ messages in thread From: Ranjit Mathew @ 2006-07-05 17:13 UTC (permalink / raw) To: Bryce McKinlay; +Cc: java-patches -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Bryce McKinlay wrote: > > A better solution, I think, would be to add a "method length" field to > class metadata. This would mean that we could build a data structure of > IP ranges and use that to find the method based just on the IP. A little > tricky for CNI methods, though. Without affecting the class metadata, there is perhaps another approach we can take: from the faulting instruction, scan backwards on an even address boundary for the function prologue "pushl %ebp; movl %esp, %ebp" (in any case we assume - -fno-omit-frame-pointer for fallback_backtrace()) for some arbitrary amount of code, say, 64K. Do you think this is viable? 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 iD8DBQFEq/NxYb1hx2wRS48RAj7uAJ96aNFuRCI5WFbT8nX+5U0HaO60XgCdE8Ab N9LoXEbpUO5zyzcc7IhDLLc= =/jPl -----END PGP SIGNATURE----- ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-07-05 17:13 ` Ranjit Mathew @ 2006-07-05 17:20 ` Andrew Haley 2006-07-05 17:35 ` Ranjit Mathew 0 siblings, 1 reply; 36+ messages in thread From: Andrew Haley @ 2006-07-05 17:20 UTC (permalink / raw) To: Ranjit Mathew; +Cc: Bryce McKinlay, java-patches Ranjit Mathew writes: > -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA1 > > Bryce McKinlay wrote: > > > > A better solution, I think, would be to add a "method length" field to > > class metadata. This would mean that we could build a data structure of > > IP ranges and use that to find the method based just on the IP. A little > > tricky for CNI methods, though. > > Without affecting the class metadata, there is perhaps another > approach we can take: from the faulting instruction, scan backwards > on an even address boundary for the function prologue "pushl %ebp; > movl %esp, %ebp" (in any case we assume - -fno-omit-frame-pointer > for fallback_backtrace()) for some arbitrary amount of code, say, > 64K. > > Do you think this is viable? Probably, but is it worth it? Wouldn't it be better to apply all those brain cycles to fixing exception handling to use DWARF rather than all these kludges? Andrew. ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-07-05 17:20 ` Andrew Haley @ 2006-07-05 17:35 ` Ranjit Mathew 2006-07-05 17:39 ` Andrew Haley 0 siblings, 1 reply; 36+ messages in thread From: Ranjit Mathew @ 2006-07-05 17:35 UTC (permalink / raw) To: Andrew Haley; +Cc: java-patches On 7/5/06, Andrew Haley <aph@redhat.com> wrote: > > Probably, but is it worth it? Wouldn't it be better to apply all those > brain cycles to fixing exception handling to use DWARF rather than all > these kludges? I couldn't agree with you more. However, switching to DW2 EH will not happen: 1. for GCC 4.2.0 2. unless someone figures out the unwinding-from-a-GUI-callback thing. Given these constraints, I think it is not that much of a waste of time to make stack traces work with SJLJ EH for MinGW. Thanks, Ranjit. -- Ranjit Mathew Email: rmathew AT gmail DOT com Bangalore, INDIA. Web: http://rmathew.com/ ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-07-05 17:35 ` Ranjit Mathew @ 2006-07-05 17:39 ` Andrew Haley 0 siblings, 0 replies; 36+ messages in thread From: Andrew Haley @ 2006-07-05 17:39 UTC (permalink / raw) To: Ranjit Mathew; +Cc: java-patches Ranjit Mathew writes: > On 7/5/06, Andrew Haley <aph@redhat.com> wrote: > > > > Probably, but is it worth it? Wouldn't it be better to apply all those > > brain cycles to fixing exception handling to use DWARF rather than all > > these kludges? > > I couldn't agree with you more. However, switching to DW2 EH > will not happen: > > 1. for GCC 4.2.0 > 2. unless someone figures out the unwinding-from-a-GUI-callback > thing. > > Given these constraints, I think it is not that much of a waste of time > to make stack traces work with SJLJ EH for MinGW. OK. Well, scanning backwards for the prologue should work. Andrew. ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-26 18:16 [MinGW] RFC/RFA: Get Partial Stack Traces on Windows Ranjit Mathew 2006-06-26 18:38 ` Bryce McKinlay 2006-06-26 19:06 ` Andrew Haley @ 2006-06-27 15:28 ` Ranjit Mathew 2006-06-27 19:57 ` Tom Tromey 2006-06-28 2:51 ` Ranjit Mathew 3 siblings, 1 reply; 36+ messages in thread From: Ranjit Mathew @ 2006-06-27 15:28 UTC (permalink / raw) To: java-patches [-- Attachment #1: Type: text/plain, Size: 945 bytes --] -----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----- [-- Attachment #2: p1.txt --] [-- Type: text/plain, Size: 8511 bytes --] 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; ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-27 15:28 ` Ranjit Mathew @ 2006-06-27 19:57 ` Tom Tromey 0 siblings, 0 replies; 36+ messages in thread From: Tom Tromey @ 2006-06-27 19:57 UTC (permalink / raw) To: Ranjit Mathew; +Cc: java-patches >>>>> "Ranjit" == Ranjit Mathew <rmathew@gmail.com> writes: Ranjit> Here's the third revision of this patch that implements Andrew's Ranjit> suggestion. Since the other typedefs in javaprims.h seem to Ranjit> omit the "_t" suffix, I've omitted it here as well. Either is fine by me. The comment helps, thanks. Ranjit> By the way, the reason main() does not appear in the stack trace Ranjit> is because it is called indirectly by call_main() in Ranjit> "gnu/java/lang/natMainThread.cc". The generated code is something Ranjit> like "CALL %8(%EBX)" instead of the normal "CALL xyz". Weird. We do this in at least one other place as well -- Class.newInstance. Ranjit> +// Types to use when treating a pointer as an integer. Similar to Ranjit> +// intptr_t and uintptr_t in C99. Ranjit> +typedef int _Jv_intptr __attribute__((__mode__(__pointer__))); Ranjit> +typedef unsigned int _Jv_uintptr __attribute__((__mode__(__pointer__))); I tend to think we just want one of these. I couldn't think of a situation where I'd need both. Ranjit> OK to apply? I'm going to defer to Bryce and/or Andrew on this one. Tom ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-26 18:16 [MinGW] RFC/RFA: Get Partial Stack Traces on Windows Ranjit Mathew ` (2 preceding siblings ...) 2006-06-27 15:28 ` Ranjit Mathew @ 2006-06-28 2:51 ` Ranjit Mathew 2006-06-28 13:56 ` Ranjit Mathew 3 siblings, 1 reply; 36+ messages in thread From: Ranjit Mathew @ 2006-06-28 2:51 UTC (permalink / raw) To: GCJ Patches; +Cc: mckinlay [-- Attachment #1: Type: text/plain, Size: 1470 bytes --] -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Ranjit Mathew wrote: > > The attached patch restores minimal stack traces on Windows. I'm attaching the fourth revision of this patch with this message. gcj/javaprims.h now defines just a single _Jv_uintptr type. include/java-stack.h now defines a _Jv_AddrInfo structure similar to Dl_info and a corresponding _Jv_platform_dladdr() function similar to dladdr(). This function is then defined in posix.cc and win32.cc using dladdr() and VirtualQuery() respectively. stacktrace.cc now uses _Jv_platform_dladdr() unconditionally and does not have to include either dlfcn.h or platform.h. I could be wrong, but I think we are leaking memory with every call to dladdr(). The destructor for _Jv_AddrInfo seeks to remedy this. I've removed backtrace() from win32.cc as it has been superseded by fallback_backtrace() in sysdep/i386/backtrace.h. Tested via an i686-pc-linux-gnu to i686-pc-mingw32 cross-compiler. 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? 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 iD8DBQFEoe7PYb1hx2wRS48RAkfXAKCIxk/aY3XmuxBzqLaatWm+LTW6aACfTFfR j9m8kxb3K/EeRQa57qX+q9E= =uq9r -----END PGP SIGNATURE----- [-- Attachment #2: p1.txt --] [-- Type: text/plain, Size: 13196 bytes --] 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 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 __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,30 @@ public: }; +// Information about a given address. +struct _Jv_AddrInfo +{ + // File name of the defining module. + char *file_name; + // Base address of the loaded module. + void *base; + // Name of the nearest symbol. + char *sym_name; + // Address of the nearest symbol. + void *sym_addr; + + ~_Jv_AddrInfo (void) + { + if (file_name) + free (file_name); + + if (sym_name) + free (sym_name); + } +}; + +// 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 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,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 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) @@ -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; ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-28 2:51 ` Ranjit Mathew @ 2006-06-28 13:56 ` Ranjit Mathew 2006-06-28 15:02 ` Bryce McKinlay 0 siblings, 1 reply; 36+ messages in thread From: Ranjit Mathew @ 2006-06-28 13:56 UTC (permalink / raw) To: java-patches [-- Attachment #1: Type: text/plain, Size: 1570 bytes --] -----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----- [-- Attachment #2: p1.txt --] [-- Type: text/plain, Size: 13070 bytes --] 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; ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-28 13:56 ` Ranjit Mathew @ 2006-06-28 15:02 ` Bryce McKinlay 2006-06-28 15:15 ` Andrew Haley 2006-06-29 4:06 ` Ranjit Mathew 0 siblings, 2 replies; 36+ messages in thread From: Bryce McKinlay @ 2006-06-28 15:02 UTC (permalink / raw) To: Ranjit Mathew; +Cc: java-patches Ranjit Mathew wrote: > 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? > 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(). > 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/ > > > ------------------------------------------------------------------------ > > Index: ChangeLog > from Ranjit Mathew <rmathew@gcc.gnu.org> > > * gcj/javaprims.h (_Jv_uintptr): New typedef similar to uintptr_t in > C99. > Hmm, is there some reason we can't just use uintptr_t? Doesn't GCC ensure it is always available? > * 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. > Cool. Splitting out _Jv_platform_dladdr really cleans up the stacktrace.cc code - this looks perfect, almost - stylistically I think it would be better to declare _Jv_platform_dladdr in platform.h along with the other _Jv_platform_* functions. I realize this means platform.h will have to typedef _Jv_AddrInfo, or include java-stack.h, but I don't think that is a significant problem. Otherwise, this is ok to commit. TVM! Bryce ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-28 15:02 ` Bryce McKinlay @ 2006-06-28 15:15 ` Andrew Haley 2006-06-28 15:45 ` Bryce McKinlay 2006-06-28 15:49 ` Bryce McKinlay 2006-06-29 4:06 ` Ranjit Mathew 1 sibling, 2 replies; 36+ messages in thread From: Andrew Haley @ 2006-06-28 15:15 UTC (permalink / raw) To: Bryce McKinlay; +Cc: Ranjit Mathew, java-patches Bryce McKinlay writes: > > Hmm, is there some reason we can't just use uintptr_t? Doesn't GCC > ensure it is always available? No. It isn't part of gcc, it's part of whatever C library is being used, and it's not guaranteed to be there. It's safer to do it this way. Andrew. ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-28 15:15 ` Andrew Haley @ 2006-06-28 15:45 ` Bryce McKinlay 2006-06-28 15:49 ` Bryce McKinlay 1 sibling, 0 replies; 36+ messages in thread From: Bryce McKinlay @ 2006-06-28 15:45 UTC (permalink / raw) To: Andrew Haley; +Cc: Ranjit Mathew, java-patches Andrew Haley wrote: > Bryce McKinlay writes: > > > > Hmm, is there some reason we can't just use uintptr_t? Doesn't GCC > > ensure it is always available? > > No. It isn't part of gcc, it's part of whatever C library is being > used, and it's not guaranteed to be there. It's safer to do it this > way. > Fair enough. Ranjit, can we call it _Jv_uintptr_t, then? Bryce ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-28 15:15 ` Andrew Haley 2006-06-28 15:45 ` Bryce McKinlay @ 2006-06-28 15:49 ` Bryce McKinlay 2006-06-28 16:11 ` Andrew Haley 1 sibling, 1 reply; 36+ messages in thread From: Bryce McKinlay @ 2006-06-28 15:49 UTC (permalink / raw) To: Andrew Haley; +Cc: Ranjit Mathew, java-patches Andrew Haley wrote: > Bryce McKinlay writes: > > > > Hmm, is there some reason we can't just use uintptr_t? Doesn't GCC > > ensure it is always available? > > No. It isn't part of gcc, it's part of whatever C library is being > used, and it's not guaranteed to be there. It's safer to do it this > way. We could also do something like: #if !defined HAVE_UINTPTR_T typedef unsigned int uintptr_t __attribute__((__mode__(__pointer__))); #endif Bryce ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-28 15:49 ` Bryce McKinlay @ 2006-06-28 16:11 ` Andrew Haley 0 siblings, 0 replies; 36+ messages in thread From: Andrew Haley @ 2006-06-28 16:11 UTC (permalink / raw) To: Bryce McKinlay; +Cc: Ranjit Mathew, java-patches Bryce McKinlay writes: > Andrew Haley wrote: > > Bryce McKinlay writes: > > > > > > Hmm, is there some reason we can't just use uintptr_t? Doesn't GCC > > > ensure it is always available? > > > > No. It isn't part of gcc, it's part of whatever C library is being > > used, and it's not guaranteed to be there. It's safer to do it this > > way. > > We could also do something like: > > #if !defined HAVE_UINTPTR_T > typedef unsigned int uintptr_t __attribute__((__mode__(__pointer__))); > #endif I suppose we could, but it seems a bit fussy for something we can define so easily. I think perhaps we have enough configury already. As I mentioned to Ranjit, this is turning into someting of a "what colour shall we paint the bike shed" issue... :-) Andrew. ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-28 15:02 ` Bryce McKinlay 2006-06-28 15:15 ` Andrew Haley @ 2006-06-29 4:06 ` Ranjit Mathew 2006-06-29 16:27 ` Ranjit Mathew 1 sibling, 1 reply; 36+ messages in thread From: Ranjit Mathew @ 2006-06-29 4:06 UTC (permalink / raw) To: Bryce McKinlay; +Cc: java-patches [-- Attachment #1: Type: text/plain, Size: 1617 bytes --] -----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----- [-- Attachment #2: p1.txt --] [-- Type: text/plain, Size: 15030 bytes --] 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; ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-29 4:06 ` Ranjit Mathew @ 2006-06-29 16:27 ` Ranjit Mathew 2006-06-29 18:27 ` Tom Tromey 0 siblings, 1 reply; 36+ messages in thread From: Ranjit Mathew @ 2006-06-29 16:27 UTC (permalink / raw) To: java-patches -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Ranjit Mathew wrote: > > 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. The i686-pc-linux-gnu native compiler built successfully and there were no regressions in the testsuire. Visual inspection of stacktraces with native and interpreted programmes also looked OK. I have therefore checked in the patch. 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 iD8DBQFEo/92Yb1hx2wRS48RAp/NAJ9CiHDCKcSK2CSLMIiBsPFMGmcv4wCfeG1n zJcDoDrDKvDnio5HVxpiO0o= =T8LJ -----END PGP SIGNATURE----- ^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows 2006-06-29 16:27 ` Ranjit Mathew @ 2006-06-29 18:27 ` Tom Tromey 0 siblings, 0 replies; 36+ messages in thread From: Tom Tromey @ 2006-06-29 18:27 UTC (permalink / raw) To: Ranjit Mathew; +Cc: java-patches >>>>> "Ranjit" == Ranjit Mathew <rmathew@gmail.com> writes: Ranjit> I have therefore checked in the patch. Excellent, thanks. And, thanks for persevering through the rewrites. Tom ^ permalink raw reply [flat|nested] 36+ messages in thread
end of thread, other threads:[~2006-07-05 17:39 UTC | newest] Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2006-06-26 18:16 [MinGW] RFC/RFA: Get Partial Stack Traces on Windows Ranjit Mathew 2006-06-26 18:38 ` Bryce McKinlay 2006-06-27 2:23 ` Ranjit Mathew 2006-06-27 11:27 ` Marco Trudel 2006-06-27 11:38 ` Ranjit Mathew 2006-06-27 12:37 ` Marco Trudel 2006-06-27 13:14 ` Ranjit Mathew 2006-06-27 21:20 ` Bryce McKinlay 2006-06-26 19:06 ` Andrew Haley 2006-06-26 19:15 ` Tom Tromey 2006-06-26 19:20 ` Andrew Haley 2006-06-27 1:29 ` Ranjit Mathew 2006-06-27 8:33 ` Andrew Haley 2006-06-27 9:11 ` Ranjit Mathew 2006-06-27 9:29 ` Andrew Haley 2006-06-27 21:57 ` Bryce McKinlay 2006-07-05 9:22 ` Ranjit Mathew 2006-07-05 15:08 ` Bryce McKinlay 2006-07-05 15:51 ` Ranjit Mathew 2006-07-05 16:07 ` Bryce McKinlay 2006-07-05 17:13 ` Ranjit Mathew 2006-07-05 17:20 ` Andrew Haley 2006-07-05 17:35 ` Ranjit Mathew 2006-07-05 17:39 ` Andrew Haley 2006-06-27 15:28 ` Ranjit Mathew 2006-06-27 19:57 ` Tom Tromey 2006-06-28 2:51 ` Ranjit Mathew 2006-06-28 13:56 ` Ranjit Mathew 2006-06-28 15:02 ` Bryce McKinlay 2006-06-28 15:15 ` Andrew Haley 2006-06-28 15:45 ` Bryce McKinlay 2006-06-28 15:49 ` Bryce McKinlay 2006-06-28 16:11 ` Andrew Haley 2006-06-29 4:06 ` Ranjit Mathew 2006-06-29 16:27 ` Ranjit Mathew 2006-06-29 18:27 ` Tom Tromey
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).