зеркало из https://github.com/mozilla/gecko-dev.git
Bug 696376 - Change how we find critical ranges so that it works on 10.6 too. r=dbaron.
Currently we use dlsym on pthread_cond_wait$UNIX2003 to find a function that indicates that new_sem_from_pool is on the stack. This works on 10.5, but on 10.6 I could not find a single reliable indicator that would work with dlsym. The good news is that dladdr works with any symbol, not just exported ones. To find the address of new_sem_from_pool, we set up a malloc logger and force a call to new_sem_from_pool. From the logger callback we walk the stack trying dladdr on every address. To force a call to new_sem_from_pool, the initialization code has to be the first to use semaphores, so it is now run from NS_LogInit. This works on 10.6 and 10.5 (but we have to look for "pthread_cond_wait$UNIX2003"). In 10.7 the call to malloc is gone, so we don't have to worry about critical addresses on it anymore. --HG-- extra : rebase_source : bba4ac9e3378c88f7037aa884511e473a57121f6
This commit is contained in:
Родитель
8a9208c6c6
Коммит
001dce7e90
|
@ -957,7 +957,7 @@ backtrace(tm_thread *t, int skip, int *immediate_abort)
|
|||
t->suppress_tracing++;
|
||||
|
||||
if (!stacks_enabled) {
|
||||
#if defined(XP_MACOSX) && defined(__i386)
|
||||
#if defined(XP_MACOSX)
|
||||
/* Walk the stack, even if stacks_enabled is false. We do this to
|
||||
check if we must set immediate_abort. */
|
||||
info->entries = 0;
|
||||
|
|
|
@ -41,9 +41,147 @@
|
|||
/* API for getting a stack trace of the C/C++ stack on the current thread */
|
||||
|
||||
#include "mozilla/Util.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsStackWalkPrivate.h"
|
||||
|
||||
#include "nsStackWalk.h"
|
||||
|
||||
// The presence of this address is the stack must stop the stack walk. If
|
||||
// there is no such address, the structure will be {NULL, true}.
|
||||
struct CriticalAddress {
|
||||
void* mAddr;
|
||||
bool mInit;
|
||||
};
|
||||
static CriticalAddress gCriticalAddress;
|
||||
|
||||
#if defined(HAVE_DLOPEN) || defined(XP_MACOSX)
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
|
||||
typedef void
|
||||
malloc_logger_t(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
|
||||
uintptr_t result, uint32_t num_hot_frames_to_skip);
|
||||
extern malloc_logger_t *malloc_logger;
|
||||
|
||||
static void
|
||||
stack_callback(void *pc, void *closure)
|
||||
{
|
||||
const char *name = reinterpret_cast<char *>(closure);
|
||||
Dl_info info;
|
||||
|
||||
// On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
|
||||
// stack shows up as having two pthread_cond_wait$UNIX2003 frames. The
|
||||
// correct one is the first that we find on our way up, so the
|
||||
// following check for gCriticalAddress.mAddr is critical.
|
||||
if (gCriticalAddress.mAddr || dladdr(pc, &info) == 0 ||
|
||||
info.dli_sname == NULL || strcmp(info.dli_sname, name) != 0)
|
||||
return;
|
||||
gCriticalAddress.mAddr = pc;
|
||||
}
|
||||
|
||||
#define MAC_OS_X_VERSION_10_7_HEX 0x00001070
|
||||
#define MAC_OS_X_VERSION_10_6_HEX 0x00001060
|
||||
|
||||
static PRInt32 OSXVersion()
|
||||
{
|
||||
static PRInt32 gOSXVersion = 0x0;
|
||||
if (gOSXVersion == 0x0) {
|
||||
OSErr err = ::Gestalt(gestaltSystemVersion, (SInt32*)&gOSXVersion);
|
||||
MOZ_ASSERT(err == noErr);
|
||||
}
|
||||
return gOSXVersion;
|
||||
}
|
||||
|
||||
static bool OnLionOrLater()
|
||||
{
|
||||
return (OSXVersion() >= MAC_OS_X_VERSION_10_7_HEX);
|
||||
}
|
||||
|
||||
static bool OnSnowLeopardOrLater()
|
||||
{
|
||||
return (OSXVersion() >= MAC_OS_X_VERSION_10_6_HEX);
|
||||
}
|
||||
|
||||
static void
|
||||
my_malloc_logger(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
|
||||
uintptr_t result, uint32_t num_hot_frames_to_skip)
|
||||
{
|
||||
static bool once = false;
|
||||
if (once)
|
||||
return;
|
||||
once = true;
|
||||
|
||||
// On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
|
||||
// stack shows up as having two pthread_cond_wait$UNIX2003 frames.
|
||||
const char *name = OnSnowLeopardOrLater() ? "new_sem_from_pool" :
|
||||
"pthread_cond_wait$UNIX2003";
|
||||
NS_StackWalk(stack_callback, 0, const_cast<char*>(name));
|
||||
}
|
||||
|
||||
void
|
||||
StackWalkInitCriticalAddress()
|
||||
{
|
||||
if(gCriticalAddress.mInit)
|
||||
return;
|
||||
gCriticalAddress.mInit = true;
|
||||
// We must not do work when 'new_sem_from_pool' calls realloc, since
|
||||
// it holds a non-reentrant spin-lock and we will quickly deadlock.
|
||||
// new_sem_from_pool is not directly accessible using dlsym, so
|
||||
// we force a situation where new_sem_from_pool is on the stack and
|
||||
// use dladdr to check the addresses.
|
||||
|
||||
MOZ_ASSERT(malloc_logger == NULL);
|
||||
malloc_logger = my_malloc_logger;
|
||||
|
||||
pthread_cond_t cond;
|
||||
int r = pthread_cond_init(&cond, 0);
|
||||
MOZ_ASSERT(r == 0);
|
||||
pthread_mutex_t mutex;
|
||||
r = pthread_mutex_init(&mutex,0);
|
||||
MOZ_ASSERT(r == 0);
|
||||
r = pthread_mutex_lock(&mutex);
|
||||
MOZ_ASSERT(r == 0);
|
||||
struct timespec abstime = {0, 1};
|
||||
r = pthread_cond_timedwait_relative_np(&cond, &mutex, &abstime);
|
||||
malloc_logger = NULL;
|
||||
|
||||
// On Lion, malloc is no longer called from pthread_cond_*wait*. This prevents
|
||||
// us from finding the address, but that is fine, since with no call to malloc
|
||||
// there is no critical address.
|
||||
MOZ_ASSERT(OnLionOrLater() || gCriticalAddress.mAddr != NULL);
|
||||
MOZ_ASSERT(r == ETIMEDOUT);
|
||||
r = pthread_mutex_unlock(&mutex);
|
||||
MOZ_ASSERT(r == 0);
|
||||
r = pthread_mutex_destroy(&mutex);
|
||||
MOZ_ASSERT(r == 0);
|
||||
r = pthread_cond_destroy(&cond);
|
||||
MOZ_ASSERT(r == 0);
|
||||
}
|
||||
|
||||
static bool IsCriticalAddress(void* aPC)
|
||||
{
|
||||
return gCriticalAddress.mAddr == aPC;
|
||||
}
|
||||
#else
|
||||
static bool IsCriticalAddress(void* aPC)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// We still initialize gCriticalAddress.mInit so that this code behaves
|
||||
// the same on all platforms. Otherwise a failure to init would be visible
|
||||
// only on OS X.
|
||||
void
|
||||
StackWalkInitCriticalAddress()
|
||||
{
|
||||
gCriticalAddress.mInit = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_IA64)) // WIN32 x86 stack walking code
|
||||
|
||||
#include "nscore.h"
|
||||
|
@ -655,6 +793,7 @@ EXPORT_XPCOM_API(nsresult)
|
|||
NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
|
||||
void *aClosure)
|
||||
{
|
||||
MOZ_ASSERT(gCriticalAddress.mInit);
|
||||
HANDLE myProcess, myThread;
|
||||
DWORD walkerReturn;
|
||||
struct WalkStackData data;
|
||||
|
@ -1140,12 +1279,6 @@ NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails,
|
|||
#define __USE_GNU
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_DLOPEN) || defined(XP_MACOSX)
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// This thing is exported by libstdc++
|
||||
// Yes, this is a gcc only hack
|
||||
#if defined(MOZ_DEMANGLE_SYMBOLS)
|
||||
|
@ -1348,6 +1481,7 @@ EXPORT_XPCOM_API(nsresult)
|
|||
NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
|
||||
void *aClosure)
|
||||
{
|
||||
MOZ_ASSERT(gCriticalAddress.mInit);
|
||||
struct my_user_args args;
|
||||
|
||||
if (!initialized)
|
||||
|
@ -1422,58 +1556,12 @@ NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails,
|
|||
extern void *__libc_stack_end; // from ld-linux.so
|
||||
#endif
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
struct AddressRange {
|
||||
void* mStart;
|
||||
void* mEnd;
|
||||
};
|
||||
// Addresses in this range must stop the stack walk
|
||||
static AddressRange gCriticalRange;
|
||||
|
||||
static void FindFunctionAddresses(const char* aName, AddressRange* aRange)
|
||||
{
|
||||
aRange->mStart = dlsym(RTLD_DEFAULT, aName);
|
||||
if (!aRange->mStart)
|
||||
return;
|
||||
aRange->mEnd = aRange->mStart;
|
||||
while (true) {
|
||||
Dl_info info;
|
||||
if (!dladdr(aRange->mEnd, &info))
|
||||
break;
|
||||
if (strcmp(info.dli_sname, aName))
|
||||
break;
|
||||
aRange->mEnd = (char*)aRange->mEnd + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void InitCriticalRanges()
|
||||
{
|
||||
if (gCriticalRange.mStart)
|
||||
return;
|
||||
// We must not do work when 'new_sem_from_pool' calls realloc, since
|
||||
// it holds a non-reentrant spin-lock and we will quickly deadlock.
|
||||
// new_sem_from_pool is not directly accessible using dladdr but its
|
||||
// code is bundled with pthread_cond_wait$UNIX2003 (on
|
||||
// Leopard anyway).
|
||||
FindFunctionAddresses("pthread_cond_wait$UNIX2003", &gCriticalRange);
|
||||
}
|
||||
|
||||
static bool InCriticalRange(void* aPC)
|
||||
{
|
||||
return gCriticalRange.mStart &&
|
||||
gCriticalRange.mStart <= aPC && aPC < gCriticalRange.mEnd;
|
||||
}
|
||||
#else
|
||||
static void InitCriticalRanges() {}
|
||||
static bool InCriticalRange(void* aPC) { return false; }
|
||||
#endif
|
||||
|
||||
EXPORT_XPCOM_API(nsresult)
|
||||
NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
|
||||
void *aClosure)
|
||||
{
|
||||
MOZ_ASSERT(gCriticalAddress.mInit);
|
||||
// Stack walking code courtesy Kipp's "leaky".
|
||||
InitCriticalRanges();
|
||||
|
||||
// Get the frame pointer
|
||||
void **bp;
|
||||
|
@ -1506,8 +1594,8 @@ NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
|
|||
#else // i386 or powerpc32 linux
|
||||
void *pc = *(bp+1);
|
||||
#endif
|
||||
if (InCriticalRange(pc)) {
|
||||
printf("Aborting stack trace, PC in critical range\n");
|
||||
if (IsCriticalAddress(pc)) {
|
||||
printf("Aborting stack trace, PC is critical\n");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
if (--skip < 0) {
|
||||
|
@ -1533,10 +1621,16 @@ static _Unwind_Reason_Code
|
|||
unwind_callback (struct _Unwind_Context *context, void *closure)
|
||||
{
|
||||
unwind_info *info = static_cast<unwind_info *>(closure);
|
||||
if (--info->skip < 0) {
|
||||
void *pc = reinterpret_cast<void *>(_Unwind_GetIP(context));
|
||||
(*info->callback)(pc, info->closure);
|
||||
if (IsCriticalAddress(pc)) {
|
||||
printf("Aborting stack trace, PC is critical\n");
|
||||
/* We just want to stop the walk, so any error code will do.
|
||||
Using _URC_NORMAL_STOP would probably be the most accurate,
|
||||
but it is not defined on Android for ARM. */
|
||||
return _URC_FOREIGN_EXCEPTION_CAUGHT;
|
||||
}
|
||||
if (--info->skip < 0)
|
||||
(*info->callback)(pc, info->closure);
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
|
@ -1544,13 +1638,15 @@ EXPORT_XPCOM_API(nsresult)
|
|||
NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
|
||||
void *aClosure)
|
||||
{
|
||||
MOZ_ASSERT(gCriticalAddress.mInit);
|
||||
unwind_info info;
|
||||
info.callback = aCallback;
|
||||
info.skip = aSkipFrames + 1;
|
||||
info.closure = aClosure;
|
||||
|
||||
_Unwind_Backtrace(unwind_callback, &info);
|
||||
|
||||
_Unwind_Reason_Code t = _Unwind_Backtrace(unwind_callback, &info);
|
||||
if (t != _URC_END_OF_STACK)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1620,6 +1716,7 @@ EXPORT_XPCOM_API(nsresult)
|
|||
NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
|
||||
void *aClosure)
|
||||
{
|
||||
MOZ_ASSERT(gCriticalAddress.mInit);
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is NS_WalkTheStack.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Mozilla Corporation (original author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* Initialize the critical sections for this platform so that we can
|
||||
* abort stack walks when needed.
|
||||
*/
|
||||
void
|
||||
StackWalkInitCriticalAddress(void);
|
|
@ -50,6 +50,7 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsCRT.h"
|
||||
#include <math.h>
|
||||
#include "nsStackWalkPrivate.h"
|
||||
#include "nsStackWalk.h"
|
||||
#include "nsString.h"
|
||||
|
||||
|
@ -923,6 +924,8 @@ nsTraceRefcntImpl::DemangleSymbol(const char * aSymbol,
|
|||
EXPORT_XPCOM_API(void)
|
||||
NS_LogInit()
|
||||
{
|
||||
// FIXME: This is called multiple times, we should probably not allow that.
|
||||
StackWalkInitCriticalAddress();
|
||||
#ifdef NS_IMPL_REFCNT_LOGGING
|
||||
if (++gInitCount)
|
||||
nsTraceRefcntImpl::SetActivityIsLegal(true);
|
||||
|
|
Загрузка…
Ссылка в новой задаче