From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 13688 invoked by alias); 28 Jun 2006 13:56:30 -0000 Received: (qmail 13675 invoked by uid 22791); 28 Jun 2006 13:56:28 -0000 X-Spam-Check-By: sourceware.org Received: from main.gmane.org (HELO ciao.gmane.org) (80.91.229.2) by sourceware.org (qpsmtpd/0.31) with ESMTP; Wed, 28 Jun 2006 13:56:24 +0000 Received: from list by ciao.gmane.org with local (Exim 4.43) id 1FvaWc-0007zb-QB for java-patches@gcc.gnu.org; Wed, 28 Jun 2006 15:56:14 +0200 Received: from inet-netcache2-o.oracle.com ([148.87.1.171]) by main.gmane.org with esmtp (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Wed, 28 Jun 2006 15:56:14 +0200 Received: from rmathew by inet-netcache2-o.oracle.com with local (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Wed, 28 Jun 2006 15:56:14 +0200 To: java-patches@gcc.gnu.org From: Ranjit Mathew Subject: Re: [MinGW] RFC/RFA: Get Partial Stack Traces on Windows Date: Wed, 28 Jun 2006 13:56:00 -0000 Message-ID: References: <44A02481.207@gmail.com> <44A1EECF.2030303@gmail.com> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------000205090909090407080902" User-Agent: Thunderbird 1.5.0.4 (Windows/20060516) In-Reply-To: <44A1EECF.2030303@gmail.com> OpenPGP: url=http://rmathew.com/aa_6C114B8F.txt X-IsSubscribed: yes Mailing-List: contact java-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: java-patches-owner@gcc.gnu.org X-SW-Source: 2006-q2/txt/msg00512.txt.bz2 This is a multi-part message in MIME format. --------------000205090909090407080902 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-length: 1570 -----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----- --------------000205090909090407080902 Content-Type: text/plain; name="p1.txt" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="p1.txt" Content-length: 13070 Index: ChangeLog from Ranjit Mathew * 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 #include #include @@ -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 #include +#ifdef HAVE_DLFCN_H +#include +#endif + #include +#include #include #include #include @@ -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 #include #include +#include #include +#include + #include #include #include @@ -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 " 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 " 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 #include -#ifdef HAVE_DLFCN_H -#include -#endif - #include #include @@ -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 *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; --------------000205090909090407080902--