summaryrefslogtreecommitdiff
path: root/mozglue/misc
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commitad18d877ddd2a44d98fa12ccd3dbbcf4d0ac4299 (patch)
tree10027f336435511475e392454359edea8e25895d /mozglue/misc
parent15477ed9af4859dacb069040b5d4de600803d3bc (diff)
downloadaura-central-ad18d877ddd2a44d98fa12ccd3dbbcf4d0ac4299.tar.gz
Add m-esr52 at 52.6.0
Diffstat (limited to 'mozglue/misc')
-rw-r--r--mozglue/misc/StackWalk.cpp1204
-rw-r--r--mozglue/misc/StackWalk.h163
-rw-r--r--mozglue/misc/StackWalk_windows.h21
-rw-r--r--mozglue/misc/TimeStamp.cpp92
-rw-r--r--mozglue/misc/TimeStamp.h609
-rw-r--r--mozglue/misc/TimeStamp_darwin.cpp206
-rw-r--r--mozglue/misc/TimeStamp_posix.cpp362
-rw-r--r--mozglue/misc/TimeStamp_windows.cpp576
-rw-r--r--mozglue/misc/TimeStamp_windows.h83
-rw-r--r--mozglue/misc/moz.build37
10 files changed, 3353 insertions, 0 deletions
diff --git a/mozglue/misc/StackWalk.cpp b/mozglue/misc/StackWalk.cpp
new file mode 100644
index 000000000..bb23b922a
--- /dev/null
+++ b/mozglue/misc/StackWalk.cpp
@@ -0,0 +1,1204 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* API for getting a stack trace of the C/C++ stack on the current thread */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/StackWalk.h"
+
+#include <string.h>
+
+using namespace mozilla;
+
+// The presence of this address is the stack must stop the stack walk. If
+// there is no such address, the structure will be {nullptr, true}.
+struct CriticalAddress
+{
+ void* mAddr;
+ bool mInit;
+};
+static CriticalAddress gCriticalAddress;
+
+// for _Unwind_Backtrace from libcxxrt or libunwind
+// cxxabi.h from libcxxrt implicitly includes unwind.h first
+#if defined(HAVE__UNWIND_BACKTRACE) && !defined(_GNU_SOURCE)
+#define _GNU_SOURCE
+#endif
+
+#if defined(HAVE_DLOPEN) || defined(XP_DARWIN)
+#include <dlfcn.h>
+#endif
+
+#if (defined(XP_DARWIN) && \
+ (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE)))
+#define MOZ_STACKWALK_SUPPORTS_MACOSX 1
+#else
+#define MOZ_STACKWALK_SUPPORTS_MACOSX 0
+#endif
+
+#if (defined(linux) && \
+ ((defined(__GNUC__) && (defined(__i386) || defined(PPC))) || \
+ defined(HAVE__UNWIND_BACKTRACE)))
+#define MOZ_STACKWALK_SUPPORTS_LINUX 1
+#else
+#define MOZ_STACKWALK_SUPPORTS_LINUX 0
+#endif
+
+#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
+#define HAVE___LIBC_STACK_END 1
+#else
+#define HAVE___LIBC_STACK_END 0
+#endif
+
+#if HAVE___LIBC_STACK_END
+extern MOZ_EXPORT void* __libc_stack_end; // from ld-linux.so
+#endif
+
+#ifdef ANDROID
+#include <algorithm>
+#include <unistd.h>
+#include <pthread.h>
+#endif
+
+#if MOZ_STACKWALK_SUPPORTS_MACOSX
+#include <pthread.h>
+#include <sys/errno.h>
+#ifdef MOZ_WIDGET_COCOA
+#include <CoreServices/CoreServices.h>
+#endif
+
+typedef void
+malloc_logger_t(uint32_t aType,
+ uintptr_t aArg1, uintptr_t aArg2, uintptr_t aArg3,
+ uintptr_t aResult, uint32_t aNumHotFramesToSkip);
+extern malloc_logger_t* malloc_logger;
+
+static void
+stack_callback(uint32_t aFrameNumber, void* aPc, void* aSp, void* aClosure)
+{
+ const char* name = static_cast<char*>(aClosure);
+ 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(aPc, &info) == 0 ||
+ !info.dli_sname || strcmp(info.dli_sname, name) != 0) {
+ return;
+ }
+ gCriticalAddress.mAddr = aPc;
+}
+
+static void
+my_malloc_logger(uint32_t aType,
+ uintptr_t aArg1, uintptr_t aArg2, uintptr_t aArg3,
+ uintptr_t aResult, uint32_t aNumHotFramesToSkip)
+{
+ 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 = "new_sem_from_pool";
+ MozStackWalk(stack_callback, /* skipFrames */ 0, /* maxFrames */ 0,
+ const_cast<char*>(name), 0, nullptr);
+}
+
+// This is called from NS_LogInit() and from the stack walking functions, but
+// only the first call has any effect. We need to call this function from both
+// places because it must run before any mutexes are created, and also before
+// any objects whose refcounts we're logging are created. Running this
+// function during NS_LogInit() ensures that we meet the first criterion, and
+// running this function during the stack walking functions ensures we meet the
+// second criterion.
+MFBT_API 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.
+
+ // malloc_logger can be set by external tools like 'Instruments' or 'leaks'
+ malloc_logger_t* old_malloc_logger = malloc_logger;
+ 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);
+
+ // restore the previous malloc logger
+ malloc_logger = old_malloc_logger;
+
+ 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.
+MFBT_API void
+StackWalkInitCriticalAddress()
+{
+ gCriticalAddress.mInit = true;
+}
+#endif
+
+#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_IA64)) // WIN32 x86 stack walking code
+
+#include <windows.h>
+#include <process.h>
+#include <stdio.h>
+#include <malloc.h>
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/StackWalk_windows.h"
+
+#include <imagehlp.h>
+// We need a way to know if we are building for WXP (or later), as if we are, we
+// need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill.
+// A value of 9 indicates we want to use the new APIs.
+#if API_VERSION_NUMBER < 9
+#error Too old imagehlp.h
+#endif
+
+struct WalkStackData
+{
+ // Are we walking the stack of the calling thread? Note that we need to avoid
+ // calling fprintf and friends if this is false, in order to avoid deadlocks.
+ bool walkCallingThread;
+ uint32_t skipFrames;
+ HANDLE thread;
+ HANDLE process;
+ HANDLE eventStart;
+ HANDLE eventEnd;
+ void** pcs;
+ uint32_t pc_size;
+ uint32_t pc_count;
+ uint32_t pc_max;
+ void** sps;
+ uint32_t sp_size;
+ uint32_t sp_count;
+ void* platformData;
+};
+
+DWORD gStackWalkThread;
+CRITICAL_SECTION gDbgHelpCS;
+
+// Routine to print an error message to standard error.
+static void
+PrintError(const char* aPrefix)
+{
+ LPSTR lpMsgBuf;
+ DWORD lastErr = GetLastError();
+ FormatMessageA(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr,
+ lastErr,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPSTR)&lpMsgBuf,
+ 0,
+ nullptr
+ );
+ fprintf(stderr, "### ERROR: %s: %s",
+ aPrefix, lpMsgBuf ? lpMsgBuf : "(null)\n");
+ fflush(stderr);
+ LocalFree(lpMsgBuf);
+}
+
+static unsigned int WINAPI WalkStackThread(void* aData);
+
+static bool
+EnsureWalkThreadReady()
+{
+ static bool walkThreadReady = false;
+ static HANDLE stackWalkThread = nullptr;
+ static HANDLE readyEvent = nullptr;
+
+ if (walkThreadReady) {
+ return walkThreadReady;
+ }
+
+ if (!stackWalkThread) {
+ readyEvent = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
+ FALSE /* initially non-signaled */,
+ nullptr);
+ if (!readyEvent) {
+ PrintError("CreateEvent");
+ return false;
+ }
+
+ unsigned int threadID;
+ stackWalkThread = (HANDLE)_beginthreadex(nullptr, 0, WalkStackThread,
+ (void*)readyEvent, 0, &threadID);
+ if (!stackWalkThread) {
+ PrintError("CreateThread");
+ ::CloseHandle(readyEvent);
+ readyEvent = nullptr;
+ return false;
+ }
+ gStackWalkThread = threadID;
+ ::CloseHandle(stackWalkThread);
+ }
+
+ MOZ_ASSERT((stackWalkThread && readyEvent) ||
+ (!stackWalkThread && !readyEvent));
+
+ // The thread was created. Try to wait an arbitrary amount of time (1 second
+ // should be enough) for its event loop to start before posting events to it.
+ DWORD waitRet = ::WaitForSingleObject(readyEvent, 1000);
+ if (waitRet == WAIT_TIMEOUT) {
+ // We get a timeout if we're called during static initialization because
+ // the thread will only start executing after we return so it couldn't
+ // have signalled the event. If that is the case, give up for now and
+ // try again next time we're called.
+ return false;
+ }
+ ::CloseHandle(readyEvent);
+ stackWalkThread = nullptr;
+ readyEvent = nullptr;
+
+
+ ::InitializeCriticalSection(&gDbgHelpCS);
+
+ return walkThreadReady = true;
+}
+
+static void
+WalkStackMain64(struct WalkStackData* aData)
+{
+ // Get a context for the specified thread.
+ CONTEXT context;
+ if (!aData->platformData) {
+ memset(&context, 0, sizeof(CONTEXT));
+ context.ContextFlags = CONTEXT_FULL;
+ if (!GetThreadContext(aData->thread, &context)) {
+ if (aData->walkCallingThread) {
+ PrintError("GetThreadContext");
+ }
+ return;
+ }
+ } else {
+ context = *static_cast<CONTEXT*>(aData->platformData);
+ }
+
+#if defined(_M_IX86) || defined(_M_IA64)
+ // Setup initial stack frame to walk from.
+ STACKFRAME64 frame64;
+ memset(&frame64, 0, sizeof(frame64));
+#ifdef _M_IX86
+ frame64.AddrPC.Offset = context.Eip;
+ frame64.AddrStack.Offset = context.Esp;
+ frame64.AddrFrame.Offset = context.Ebp;
+#elif defined _M_IA64
+ frame64.AddrPC.Offset = context.StIIP;
+ frame64.AddrStack.Offset = context.SP;
+ frame64.AddrFrame.Offset = context.RsBSP;
+#endif
+ frame64.AddrPC.Mode = AddrModeFlat;
+ frame64.AddrStack.Mode = AddrModeFlat;
+ frame64.AddrFrame.Mode = AddrModeFlat;
+ frame64.AddrReturn.Mode = AddrModeFlat;
+#endif
+
+ // Skip our own stack walking frames.
+ int skip = (aData->walkCallingThread ? 3 : 0) + aData->skipFrames;
+
+ // Now walk the stack.
+ while (true) {
+ DWORD64 addr;
+ DWORD64 spaddr;
+
+#if defined(_M_IX86) || defined(_M_IA64)
+ // 32-bit frame unwinding.
+ // Debug routines are not threadsafe, so grab the lock.
+ EnterCriticalSection(&gDbgHelpCS);
+ BOOL ok = StackWalk64(
+#if defined _M_IA64
+ IMAGE_FILE_MACHINE_IA64,
+#elif defined _M_IX86
+ IMAGE_FILE_MACHINE_I386,
+#endif
+ aData->process,
+ aData->thread,
+ &frame64,
+ &context,
+ nullptr,
+ SymFunctionTableAccess64, // function table access routine
+ SymGetModuleBase64, // module base routine
+ 0
+ );
+ LeaveCriticalSection(&gDbgHelpCS);
+
+ if (ok) {
+ addr = frame64.AddrPC.Offset;
+ spaddr = frame64.AddrStack.Offset;
+ } else {
+ addr = 0;
+ spaddr = 0;
+ if (aData->walkCallingThread) {
+ PrintError("WalkStack64");
+ }
+ }
+
+ if (!ok) {
+ break;
+ }
+
+#elif defined(_M_AMD64)
+ // 64-bit frame unwinding.
+ // Try to look up unwind metadata for the current function.
+ ULONG64 imageBase;
+ PRUNTIME_FUNCTION runtimeFunction =
+ RtlLookupFunctionEntry(context.Rip, &imageBase, NULL);
+
+ if (!runtimeFunction) {
+ // Alas, this is probably a JIT frame, for which we don't generate unwind
+ // info and so we have to give up.
+ break;
+ }
+
+ PVOID dummyHandlerData;
+ ULONG64 dummyEstablisherFrame;
+ RtlVirtualUnwind(UNW_FLAG_NHANDLER,
+ imageBase,
+ context.Rip,
+ runtimeFunction,
+ &context,
+ &dummyHandlerData,
+ &dummyEstablisherFrame,
+ nullptr);
+
+ addr = context.Rip;
+ spaddr = context.Rsp;
+
+#else
+#error "unknown platform"
+#endif
+
+ if (addr == 0) {
+ break;
+ }
+
+ if (skip-- > 0) {
+ continue;
+ }
+
+ if (aData->pc_count < aData->pc_size) {
+ aData->pcs[aData->pc_count] = (void*)addr;
+ }
+ ++aData->pc_count;
+
+ if (aData->sp_count < aData->sp_size) {
+ aData->sps[aData->sp_count] = (void*)spaddr;
+ }
+ ++aData->sp_count;
+
+ if (aData->pc_max != 0 && aData->pc_count == aData->pc_max) {
+ break;
+ }
+
+#if defined(_M_IX86) || defined(_M_IA64)
+ if (frame64.AddrReturn.Offset == 0) {
+ break;
+ }
+#endif
+ }
+}
+
+// The JIT needs to allocate executable memory. Because of the inanity of
+// the win64 APIs, this requires locks that stalk walkers also need. Provide
+// another lock to allow synchronization around these resources.
+#ifdef _M_AMD64
+
+struct CriticalSectionAutoInitializer {
+ CRITICAL_SECTION lock;
+
+ CriticalSectionAutoInitializer() {
+ InitializeCriticalSection(&lock);
+ }
+};
+
+static CriticalSectionAutoInitializer gWorkaroundLock;
+
+#endif // _M_AMD64
+
+MFBT_API void
+AcquireStackWalkWorkaroundLock()
+{
+#ifdef _M_AMD64
+ EnterCriticalSection(&gWorkaroundLock.lock);
+#endif
+}
+
+MFBT_API bool
+TryAcquireStackWalkWorkaroundLock()
+{
+#ifdef _M_AMD64
+ return TryEnterCriticalSection(&gWorkaroundLock.lock);
+#else
+ return true;
+#endif
+}
+
+MFBT_API void
+ReleaseStackWalkWorkaroundLock()
+{
+#ifdef _M_AMD64
+ LeaveCriticalSection(&gWorkaroundLock.lock);
+#endif
+}
+
+static unsigned int WINAPI
+WalkStackThread(void* aData)
+{
+ BOOL msgRet;
+ MSG msg;
+
+ // Call PeekMessage to force creation of a message queue so that
+ // other threads can safely post events to us.
+ ::PeekMessage(&msg, nullptr, WM_USER, WM_USER, PM_NOREMOVE);
+
+ // and tell the thread that created us that we're ready.
+ HANDLE readyEvent = (HANDLE)aData;
+ ::SetEvent(readyEvent);
+
+ while ((msgRet = ::GetMessage(&msg, (HWND)-1, 0, 0)) != 0) {
+ if (msgRet == -1) {
+ PrintError("GetMessage");
+ } else {
+ DWORD ret;
+
+ struct WalkStackData* data = (WalkStackData*)msg.lParam;
+ if (!data) {
+ continue;
+ }
+
+ // Don't suspend the calling thread until it's waiting for
+ // us; otherwise the number of frames on the stack could vary.
+ ret = ::WaitForSingleObject(data->eventStart, INFINITE);
+ if (ret != WAIT_OBJECT_0) {
+ PrintError("WaitForSingleObject");
+ }
+
+ // Suspend the calling thread, dump his stack, and then resume him.
+ // He's currently waiting for us to finish so now should be a good time.
+ ret = ::SuspendThread(data->thread);
+ if (ret == -1) {
+ PrintError("ThreadSuspend");
+ } else {
+ WalkStackMain64(data);
+
+ ret = ::ResumeThread(data->thread);
+ if (ret == -1) {
+ PrintError("ThreadResume");
+ }
+ }
+
+ ::SetEvent(data->eventEnd);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Walk the stack, translating PC's found into strings and recording the
+ * chain in aBuffer. For this to work properly, the DLLs must be rebased
+ * so that the address in the file agrees with the address in memory.
+ * Otherwise StackWalk will return FALSE when it hits a frame in a DLL
+ * whose in memory address doesn't match its in-file address.
+ */
+
+MFBT_API bool
+MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
+ uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
+ void* aPlatformData)
+{
+ StackWalkInitCriticalAddress();
+ static HANDLE myProcess = nullptr;
+ HANDLE myThread;
+ DWORD walkerReturn;
+ struct WalkStackData data;
+
+ if (!EnsureWalkThreadReady()) {
+ return false;
+ }
+
+ HANDLE currentThread = ::GetCurrentThread();
+ HANDLE targetThread =
+ aThread ? reinterpret_cast<HANDLE>(aThread) : currentThread;
+ data.walkCallingThread = (targetThread == currentThread);
+
+ // Have to duplicate handle to get a real handle.
+ if (!myProcess) {
+ if (!::DuplicateHandle(::GetCurrentProcess(),
+ ::GetCurrentProcess(),
+ ::GetCurrentProcess(),
+ &myProcess,
+ PROCESS_ALL_ACCESS, FALSE, 0)) {
+ if (data.walkCallingThread) {
+ PrintError("DuplicateHandle (process)");
+ }
+ return false;
+ }
+ }
+ if (!::DuplicateHandle(::GetCurrentProcess(),
+ targetThread,
+ ::GetCurrentProcess(),
+ &myThread,
+ THREAD_ALL_ACCESS, FALSE, 0)) {
+ if (data.walkCallingThread) {
+ PrintError("DuplicateHandle (thread)");
+ }
+ return false;
+ }
+
+ data.skipFrames = aSkipFrames;
+ data.thread = myThread;
+ data.process = myProcess;
+ void* local_pcs[1024];
+ data.pcs = local_pcs;
+ data.pc_count = 0;
+ data.pc_size = ArrayLength(local_pcs);
+ data.pc_max = aMaxFrames;
+ void* local_sps[1024];
+ data.sps = local_sps;
+ data.sp_count = 0;
+ data.sp_size = ArrayLength(local_sps);
+ data.platformData = aPlatformData;
+
+ if (aThread) {
+ // If we're walking the stack of another thread, we don't need to
+ // use a separate walker thread.
+ WalkStackMain64(&data);
+
+ if (data.pc_count > data.pc_size) {
+ data.pcs = (void**)_alloca(data.pc_count * sizeof(void*));
+ data.pc_size = data.pc_count;
+ data.pc_count = 0;
+ data.sps = (void**)_alloca(data.sp_count * sizeof(void*));
+ data.sp_size = data.sp_count;
+ data.sp_count = 0;
+ WalkStackMain64(&data);
+ }
+ } else {
+ data.eventStart = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
+ FALSE /* initially non-signaled */, nullptr);
+ data.eventEnd = ::CreateEvent(nullptr, FALSE /* auto-reset*/,
+ FALSE /* initially non-signaled */, nullptr);
+
+ ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data);
+
+ walkerReturn = ::SignalObjectAndWait(data.eventStart,
+ data.eventEnd, INFINITE, FALSE);
+ if (walkerReturn != WAIT_OBJECT_0 && data.walkCallingThread) {
+ PrintError("SignalObjectAndWait (1)");
+ }
+ if (data.pc_count > data.pc_size) {
+ data.pcs = (void**)_alloca(data.pc_count * sizeof(void*));
+ data.pc_size = data.pc_count;
+ data.pc_count = 0;
+ data.sps = (void**)_alloca(data.sp_count * sizeof(void*));
+ data.sp_size = data.sp_count;
+ data.sp_count = 0;
+ ::PostThreadMessage(gStackWalkThread, WM_USER, 0, (LPARAM)&data);
+ walkerReturn = ::SignalObjectAndWait(data.eventStart,
+ data.eventEnd, INFINITE, FALSE);
+ if (walkerReturn != WAIT_OBJECT_0 && data.walkCallingThread) {
+ PrintError("SignalObjectAndWait (2)");
+ }
+ }
+
+ ::CloseHandle(data.eventStart);
+ ::CloseHandle(data.eventEnd);
+ }
+
+ ::CloseHandle(myThread);
+
+ for (uint32_t i = 0; i < data.pc_count; ++i) {
+ (*aCallback)(i + 1, data.pcs[i], data.sps[i], aClosure);
+ }
+
+ return data.pc_count != 0;
+}
+
+
+static BOOL CALLBACK
+callbackEspecial64(
+ PCSTR aModuleName,
+ DWORD64 aModuleBase,
+ ULONG aModuleSize,
+ PVOID aUserContext)
+{
+ BOOL retval = TRUE;
+ DWORD64 addr = *(DWORD64*)aUserContext;
+
+ /*
+ * You'll want to control this if we are running on an
+ * architecture where the addresses go the other direction.
+ * Not sure this is even a realistic consideration.
+ */
+ const BOOL addressIncreases = TRUE;
+
+ /*
+ * If it falls in side the known range, load the symbols.
+ */
+ if (addressIncreases
+ ? (addr >= aModuleBase && addr <= (aModuleBase + aModuleSize))
+ : (addr <= aModuleBase && addr >= (aModuleBase - aModuleSize))
+ ) {
+ retval = !!SymLoadModule64(GetCurrentProcess(), nullptr,
+ (PSTR)aModuleName, nullptr,
+ aModuleBase, aModuleSize);
+ if (!retval) {
+ PrintError("SymLoadModule64");
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * SymGetModuleInfoEspecial
+ *
+ * Attempt to determine the module information.
+ * Bug 112196 says this DLL may not have been loaded at the time
+ * SymInitialize was called, and thus the module information
+ * and symbol information is not available.
+ * This code rectifies that problem.
+ */
+
+// New members were added to IMAGEHLP_MODULE64 (that show up in the
+// Platform SDK that ships with VC8, but not the Platform SDK that ships
+// with VC7.1, i.e., between DbgHelp 6.0 and 6.1), but we don't need to
+// use them, and it's useful to be able to function correctly with the
+// older library. (Stock Windows XP SP2 seems to ship with dbghelp.dll
+// version 5.1.) Since Platform SDK version need not correspond to
+// compiler version, and the version number in debughlp.h was NOT bumped
+// when these changes were made, ifdef based on a constant that was
+// added between these versions.
+#ifdef SSRVOPT_SETCONTEXT
+#define NS_IMAGEHLP_MODULE64_SIZE (((offsetof(IMAGEHLP_MODULE64, LoadedPdbName) + sizeof(DWORD64) - 1) / sizeof(DWORD64)) * sizeof(DWORD64))
+#else
+#define NS_IMAGEHLP_MODULE64_SIZE sizeof(IMAGEHLP_MODULE64)
+#endif
+
+BOOL SymGetModuleInfoEspecial64(HANDLE aProcess, DWORD64 aAddr,
+ PIMAGEHLP_MODULE64 aModuleInfo,
+ PIMAGEHLP_LINE64 aLineInfo)
+{
+ BOOL retval = FALSE;
+
+ /*
+ * Init the vars if we have em.
+ */
+ aModuleInfo->SizeOfStruct = NS_IMAGEHLP_MODULE64_SIZE;
+ if (aLineInfo) {
+ aLineInfo->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ }
+
+ /*
+ * Give it a go.
+ * It may already be loaded.
+ */
+ retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo);
+ if (retval == FALSE) {
+ /*
+ * Not loaded, here's the magic.
+ * Go through all the modules.
+ */
+ // Need to cast to PENUMLOADED_MODULES_CALLBACK64 because the
+ // constness of the first parameter of
+ // PENUMLOADED_MODULES_CALLBACK64 varies over SDK versions (from
+ // non-const to const over time). See bug 391848 and bug
+ // 415426.
+ BOOL enumRes = EnumerateLoadedModules64(
+ aProcess,
+ (PENUMLOADED_MODULES_CALLBACK64)callbackEspecial64,
+ (PVOID)&aAddr);
+ if (enumRes != FALSE) {
+ /*
+ * One final go.
+ * If it fails, then well, we have other problems.
+ */
+ retval = SymGetModuleInfo64(aProcess, aAddr, aModuleInfo);
+ }
+ }
+
+ /*
+ * If we got module info, we may attempt line info as well.
+ * We will not report failure if this does not work.
+ */
+ if (retval != FALSE && aLineInfo) {
+ DWORD displacement = 0;
+ BOOL lineRes = FALSE;
+ lineRes = SymGetLineFromAddr64(aProcess, aAddr, &displacement, aLineInfo);
+ if (!lineRes) {
+ // Clear out aLineInfo to indicate that it's not valid
+ memset(aLineInfo, 0, sizeof(*aLineInfo));
+ }
+ }
+
+ return retval;
+}
+
+static bool
+EnsureSymInitialized()
+{
+ static bool gInitialized = false;
+ bool retStat;
+
+ if (gInitialized) {
+ return gInitialized;
+ }
+
+ if (!EnsureWalkThreadReady()) {
+ return false;
+ }
+
+ SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
+ retStat = SymInitialize(GetCurrentProcess(), nullptr, TRUE);
+ if (!retStat) {
+ PrintError("SymInitialize");
+ }
+
+ gInitialized = retStat;
+ /* XXX At some point we need to arrange to call SymCleanup */
+
+ return retStat;
+}
+
+
+MFBT_API bool
+MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails)
+{
+ aDetails->library[0] = '\0';
+ aDetails->loffset = 0;
+ aDetails->filename[0] = '\0';
+ aDetails->lineno = 0;
+ aDetails->function[0] = '\0';
+ aDetails->foffset = 0;
+
+ if (!EnsureSymInitialized()) {
+ return false;
+ }
+
+ HANDLE myProcess = ::GetCurrentProcess();
+ BOOL ok;
+
+ // debug routines are not threadsafe, so grab the lock.
+ EnterCriticalSection(&gDbgHelpCS);
+
+ //
+ // Attempt to load module info before we attempt to resolve the symbol.
+ // This just makes sure we get good info if available.
+ //
+
+ DWORD64 addr = (DWORD64)aPC;
+ IMAGEHLP_MODULE64 modInfo;
+ IMAGEHLP_LINE64 lineInfo;
+ BOOL modInfoRes;
+ modInfoRes = SymGetModuleInfoEspecial64(myProcess, addr, &modInfo, &lineInfo);
+
+ if (modInfoRes) {
+ strncpy(aDetails->library, modInfo.LoadedImageName,
+ sizeof(aDetails->library));
+ aDetails->library[mozilla::ArrayLength(aDetails->library) - 1] = '\0';
+ aDetails->loffset = (char*)aPC - (char*)modInfo.BaseOfImage;
+
+ if (lineInfo.FileName) {
+ strncpy(aDetails->filename, lineInfo.FileName,
+ sizeof(aDetails->filename));
+ aDetails->filename[mozilla::ArrayLength(aDetails->filename) - 1] = '\0';
+ aDetails->lineno = lineInfo.LineNumber;
+ }
+ }
+
+ ULONG64 buffer[(sizeof(SYMBOL_INFO) +
+ MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
+ PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
+ pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+ pSymbol->MaxNameLen = MAX_SYM_NAME;
+
+ DWORD64 displacement;
+ ok = SymFromAddr(myProcess, addr, &displacement, pSymbol);
+
+ if (ok) {
+ strncpy(aDetails->function, pSymbol->Name,
+ sizeof(aDetails->function));
+ aDetails->function[mozilla::ArrayLength(aDetails->function) - 1] = '\0';
+ aDetails->foffset = static_cast<ptrdiff_t>(displacement);
+ }
+
+ LeaveCriticalSection(&gDbgHelpCS); // release our lock
+ return true;
+}
+
+// i386 or PPC Linux stackwalking code
+#elif HAVE_DLADDR && (HAVE__UNWIND_BACKTRACE || MOZ_STACKWALK_SUPPORTS_LINUX || MOZ_STACKWALK_SUPPORTS_MACOSX)
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+// On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
+// if __USE_GNU is defined. I suppose its some kind of standards
+// adherence thing.
+//
+#if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
+#define __USE_GNU
+#endif
+
+// This thing is exported by libstdc++
+// Yes, this is a gcc only hack
+#if defined(MOZ_DEMANGLE_SYMBOLS)
+#include <cxxabi.h>
+#endif // MOZ_DEMANGLE_SYMBOLS
+
+void DemangleSymbol(const char* aSymbol,
+ char* aBuffer,
+ int aBufLen)
+{
+ aBuffer[0] = '\0';
+
+#if defined(MOZ_DEMANGLE_SYMBOLS)
+ /* See demangle.h in the gcc source for the voodoo */
+ char* demangled = abi::__cxa_demangle(aSymbol, 0, 0, 0);
+
+ if (demangled) {
+ strncpy(aBuffer, demangled, aBufLen);
+ aBuffer[aBufLen - 1] = '\0';
+ free(demangled);
+ }
+#endif // MOZ_DEMANGLE_SYMBOLS
+}
+
+// {x86, ppc} x {Linux, Mac} stackwalking code.
+#if ((defined(__i386) || defined(PPC) || defined(__ppc__)) && \
+ (MOZ_STACKWALK_SUPPORTS_MACOSX || MOZ_STACKWALK_SUPPORTS_LINUX))
+
+MFBT_API bool
+MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
+ uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
+ void* aPlatformData)
+{
+ MOZ_ASSERT(!aThread);
+ MOZ_ASSERT(!aPlatformData);
+ StackWalkInitCriticalAddress();
+
+ // Get the frame pointer
+ void** bp = (void**)__builtin_frame_address(0);
+
+ void* stackEnd;
+#if HAVE___LIBC_STACK_END
+ stackEnd = __libc_stack_end;
+#elif defined(XP_DARWIN)
+ stackEnd = pthread_get_stackaddr_np(pthread_self());
+#elif defined(ANDROID)
+ pthread_attr_t sattr;
+ pthread_attr_init(&sattr);
+ pthread_getattr_np(pthread_self(), &sattr);
+ void* stackBase = stackEnd = nullptr;
+ size_t stackSize = 0;
+ if (gettid() != getpid()) {
+ // bionic's pthread_attr_getstack doesn't tell the truth for the main
+ // thread (see bug 846670). So don't use it for the main thread.
+ if (!pthread_attr_getstack(&sattr, &stackBase, &stackSize)) {
+ stackEnd = static_cast<char*>(stackBase) + stackSize;
+ } else {
+ stackEnd = nullptr;
+ }
+ }
+ if (!stackEnd) {
+ // So consider the current frame pointer + an arbitrary size of 8MB
+ // (modulo overflow ; not really arbitrary as it's the default stack
+ // size for the main thread) if pthread_attr_getstack failed for
+ // some reason (or was skipped).
+ static const uintptr_t kMaxStackSize = 8 * 1024 * 1024;
+ uintptr_t maxStackStart = uintptr_t(-1) - kMaxStackSize;
+ uintptr_t stackStart = std::max(maxStackStart, uintptr_t(bp));
+ stackEnd = reinterpret_cast<void*>(stackStart + kMaxStackSize);
+ }
+#else
+# error Unsupported configuration
+#endif
+ return FramePointerStackWalk(aCallback, aSkipFrames, aMaxFrames,
+ aClosure, bp, stackEnd);
+}
+
+#elif defined(HAVE__UNWIND_BACKTRACE)
+
+// libgcc_s.so symbols _Unwind_Backtrace@@GCC_3.3 and _Unwind_GetIP@@GCC_3.0
+#include <unwind.h>
+
+struct unwind_info
+{
+ MozWalkStackCallback callback;
+ int skip;
+ int maxFrames;
+ int numFrames;
+ bool isCriticalAbort;
+ void* closure;
+};
+
+static _Unwind_Reason_Code
+unwind_callback(struct _Unwind_Context* context, void* closure)
+{
+ unwind_info* info = static_cast<unwind_info*>(closure);
+ void* pc = reinterpret_cast<void*>(_Unwind_GetIP(context));
+ // TODO Use something like '_Unwind_GetGR()' to get the stack pointer.
+ if (IsCriticalAddress(pc)) {
+ info->isCriticalAbort = true;
+ // 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->numFrames++;
+ (*info->callback)(info->numFrames, pc, nullptr, info->closure);
+ if (info->maxFrames != 0 && info->numFrames == info->maxFrames) {
+ // Again, any error code that stops the walk will do.
+ return _URC_FOREIGN_EXCEPTION_CAUGHT;
+ }
+ }
+ return _URC_NO_REASON;
+}
+
+MFBT_API bool
+MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
+ uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
+ void* aPlatformData)
+{
+ MOZ_ASSERT(!aThread);
+ MOZ_ASSERT(!aPlatformData);
+ StackWalkInitCriticalAddress();
+ unwind_info info;
+ info.callback = aCallback;
+ info.skip = aSkipFrames + 1;
+ info.maxFrames = aMaxFrames;
+ info.numFrames = 0;
+ info.isCriticalAbort = false;
+ info.closure = aClosure;
+
+ (void)_Unwind_Backtrace(unwind_callback, &info);
+
+ // We ignore the return value from _Unwind_Backtrace and instead determine
+ // the outcome from |info|. There are two main reasons for this:
+ // - On ARM/Android bionic's _Unwind_Backtrace usually (always?) returns
+ // _URC_FAILURE. See
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=717853#c110.
+ // - If aMaxFrames != 0, we want to stop early, and the only way to do that
+ // is to make unwind_callback return something other than _URC_NO_REASON,
+ // which causes _Unwind_Backtrace to return a non-success code.
+ if (info.isCriticalAbort) {
+ return false;
+ }
+ return info.numFrames != 0;
+}
+
+#endif
+
+bool MFBT_API
+MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails)
+{
+ aDetails->library[0] = '\0';
+ aDetails->loffset = 0;
+ aDetails->filename[0] = '\0';
+ aDetails->lineno = 0;
+ aDetails->function[0] = '\0';
+ aDetails->foffset = 0;
+
+ Dl_info info;
+ int ok = dladdr(aPC, &info);
+ if (!ok) {
+ return true;
+ }
+
+ strncpy(aDetails->library, info.dli_fname, sizeof(aDetails->library));
+ aDetails->library[mozilla::ArrayLength(aDetails->library) - 1] = '\0';
+ aDetails->loffset = (char*)aPC - (char*)info.dli_fbase;
+
+ const char* symbol = info.dli_sname;
+ if (!symbol || symbol[0] == '\0') {
+ return true;
+ }
+
+ DemangleSymbol(symbol, aDetails->function, sizeof(aDetails->function));
+
+ if (aDetails->function[0] == '\0') {
+ // Just use the mangled symbol if demangling failed.
+ strncpy(aDetails->function, symbol, sizeof(aDetails->function));
+ aDetails->function[mozilla::ArrayLength(aDetails->function) - 1] = '\0';
+ }
+
+ aDetails->foffset = (char*)aPC - (char*)info.dli_saddr;
+ return true;
+}
+
+#else // unsupported platform.
+
+MFBT_API bool
+MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
+ uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
+ void* aPlatformData)
+{
+ MOZ_ASSERT(!aThread);
+ MOZ_ASSERT(!aPlatformData);
+ return false;
+}
+
+MFBT_API bool
+MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails)
+{
+ aDetails->library[0] = '\0';
+ aDetails->loffset = 0;
+ aDetails->filename[0] = '\0';
+ aDetails->lineno = 0;
+ aDetails->function[0] = '\0';
+ aDetails->foffset = 0;
+ return false;
+}
+
+#endif
+
+#if defined(XP_WIN) || defined (XP_MACOSX) || defined (XP_LINUX)
+namespace mozilla {
+bool
+FramePointerStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
+ uint32_t aMaxFrames, void* aClosure, void** bp,
+ void* aStackEnd)
+{
+ // Stack walking code courtesy Kipp's "leaky".
+
+ int32_t skip = aSkipFrames;
+ uint32_t numFrames = 0;
+ while (bp) {
+ void** next = (void**)*bp;
+ // bp may not be a frame pointer on i386 if code was compiled with
+ // -fomit-frame-pointer, so do some sanity checks.
+ // (bp should be a frame pointer on ppc(64) but checking anyway may help
+ // a little if the stack has been corrupted.)
+ // We don't need to check against the begining of the stack because
+ // we can assume that bp > sp
+ if (next <= bp ||
+ next > aStackEnd ||
+ (uintptr_t(next) & 3)) {
+ break;
+ }
+#if (defined(__ppc__) && defined(XP_MACOSX)) || defined(__powerpc64__)
+ // ppc mac or powerpc64 linux
+ void* pc = *(bp + 2);
+ bp += 3;
+#else // i386 or powerpc32 linux
+ void* pc = *(bp + 1);
+ bp += 2;
+#endif
+ if (IsCriticalAddress(pc)) {
+ return false;
+ }
+ if (--skip < 0) {
+ // Assume that the SP points to the BP of the function
+ // it called. We can't know the exact location of the SP
+ // but this should be sufficient for our use the SP
+ // to order elements on the stack.
+ numFrames++;
+ (*aCallback)(numFrames, pc, bp, aClosure);
+ if (aMaxFrames != 0 && numFrames == aMaxFrames) {
+ break;
+ }
+ }
+ bp = next;
+ }
+ return numFrames != 0;
+}
+} // namespace mozilla
+
+#else
+
+namespace mozilla {
+MFBT_API bool
+FramePointerStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
+ void* aClosure, void** aBp)
+{
+ return false;
+}
+}
+
+#endif
+
+MFBT_API void
+MozFormatCodeAddressDetails(char* aBuffer, uint32_t aBufferSize,
+ uint32_t aFrameNumber, void* aPC,
+ const MozCodeAddressDetails* aDetails)
+{
+ MozFormatCodeAddress(aBuffer, aBufferSize,
+ aFrameNumber, aPC, aDetails->function,
+ aDetails->library, aDetails->loffset,
+ aDetails->filename, aDetails->lineno);
+}
+
+MFBT_API void
+MozFormatCodeAddress(char* aBuffer, uint32_t aBufferSize, uint32_t aFrameNumber,
+ const void* aPC, const char* aFunction,
+ const char* aLibrary, ptrdiff_t aLOffset,
+ const char* aFileName, uint32_t aLineNo)
+{
+ const char* function = aFunction && aFunction[0] ? aFunction : "???";
+ if (aFileName && aFileName[0]) {
+ // We have a filename and (presumably) a line number. Use them.
+ snprintf(aBuffer, aBufferSize,
+ "#%02u: %s (%s:%u)",
+ aFrameNumber, function, aFileName, aLineNo);
+ } else if (aLibrary && aLibrary[0]) {
+ // We have no filename, but we do have a library name. Use it and the
+ // library offset, and print them in a way that scripts like
+ // fix_{linux,macosx}_stacks.py can easily post-process.
+ snprintf(aBuffer, aBufferSize,
+ "#%02u: %s[%s +0x%" PRIxPTR "]",
+ aFrameNumber, function, aLibrary, static_cast<uintptr_t>(aLOffset));
+ } else {
+ // We have nothing useful to go on. (The format string is split because
+ // '??)' is a trigraph and causes a warning, sigh.)
+ snprintf(aBuffer, aBufferSize,
+ "#%02u: ??? (???:???" ")",
+ aFrameNumber);
+ }
+}
diff --git a/mozglue/misc/StackWalk.h b/mozglue/misc/StackWalk.h
new file mode 100644
index 000000000..534c0bd82
--- /dev/null
+++ b/mozglue/misc/StackWalk.h
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* API for getting a stack trace of the C/C++ stack on the current thread */
+
+#ifndef mozilla_StackWalk_h
+#define mozilla_StackWalk_h
+
+/* WARNING: This file is intended to be included from C or C++ files. */
+
+#include "mozilla/Types.h"
+#include <stdint.h>
+
+/**
+ * The callback for MozStackWalk.
+ *
+ * @param aFrameNumber The frame number (starts at 1, not 0).
+ * @param aPC The program counter value.
+ * @param aSP The best approximation possible of what the stack
+ * pointer will be pointing to when the execution returns
+ * to executing that at aPC. If no approximation can
+ * be made it will be nullptr.
+ * @param aClosure Extra data passed in via MozStackWalk().
+ */
+typedef void
+(*MozWalkStackCallback)(uint32_t aFrameNumber, void* aPC, void* aSP,
+ void* aClosure);
+
+/**
+ * Call aCallback for the C/C++ stack frames on the current thread, from
+ * the caller of MozStackWalk to main (or above).
+ *
+ * @param aCallback Callback function, called once per frame.
+ * @param aSkipFrames Number of initial frames to skip. 0 means that
+ * the first callback will be for the caller of
+ * MozStackWalk.
+ * @param aMaxFrames Maximum number of frames to trace. 0 means no limit.
+ * @param aClosure Caller-supplied data passed through to aCallback.
+ * @param aThread The thread for which the stack is to be retrieved.
+ * Passing null causes us to walk the stack of the
+ * current thread. On Windows, this is a thread HANDLE.
+ * It is currently not supported on any other platform.
+ * @param aPlatformData Platform specific data that can help in walking the
+ * stack, this should be nullptr unless you really know
+ * what you're doing! This needs to be a pointer to a
+ * CONTEXT on Windows and should not be passed on other
+ * platforms.
+ *
+ * May skip some stack frames due to compiler optimizations or code
+ * generation.
+ *
+ */
+MFBT_API bool
+MozStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
+ uint32_t aMaxFrames, void* aClosure, uintptr_t aThread,
+ void* aPlatformData);
+
+typedef struct
+{
+ /*
+ * The name of the shared library or executable containing an
+ * address and the address's offset within that library, or empty
+ * string and zero if unknown.
+ */
+ char library[256];
+ ptrdiff_t loffset;
+ /*
+ * The name of the file name and line number of the code
+ * corresponding to the address, or empty string and zero if
+ * unknown.
+ */
+ char filename[256];
+ unsigned long lineno;
+ /*
+ * The name of the function containing an address and the address's
+ * offset within that function, or empty string and zero if unknown.
+ */
+ char function[256];
+ ptrdiff_t foffset;
+} MozCodeAddressDetails;
+
+/**
+ * For a given pointer to code, fill in the pieces of information used
+ * when printing a stack trace.
+ *
+ * @param aPC The code address.
+ * @param aDetails A structure to be filled in with the result.
+ */
+MFBT_API bool
+MozDescribeCodeAddress(void* aPC, MozCodeAddressDetails* aDetails);
+
+/**
+ * Format the information about a code address in a format suitable for
+ * stack traces on the current platform. When available, this string
+ * should contain the function name, source file, and line number. When
+ * these are not available, library and offset should be reported, if
+ * possible.
+ *
+ * Note that this output is parsed by several scripts including the fix*.py and
+ * make-tree.pl scripts in tools/rb/. It should only be change with care, and
+ * in conjunction with those scripts.
+ *
+ * @param aBuffer A string to be filled in with the description.
+ * The string will always be null-terminated.
+ * @param aBufferSize The size, in bytes, of aBuffer, including
+ * room for the terminating null. If the information
+ * to be printed would be larger than aBuffer, it
+ * will be truncated so that aBuffer[aBufferSize-1]
+ * is the terminating null.
+ * @param aFrameNumber The frame number.
+ * @param aPC The code address.
+ * @param aFunction The function name. Possibly null or the empty string.
+ * @param aLibrary The library name. Possibly null or the empty string.
+ * @param aLOffset The library offset.
+ * @param aFileName The filename. Possibly null or the empty string.
+ * @param aLineNo The line number. Possibly zero.
+ */
+MFBT_API void
+MozFormatCodeAddress(char* aBuffer, uint32_t aBufferSize, uint32_t aFrameNumber,
+ const void* aPC, const char* aFunction,
+ const char* aLibrary, ptrdiff_t aLOffset,
+ const char* aFileName, uint32_t aLineNo);
+
+/**
+ * Format the information about a code address in the same fashion as
+ * MozFormatCodeAddress.
+ *
+ * @param aBuffer A string to be filled in with the description.
+ * The string will always be null-terminated.
+ * @param aBufferSize The size, in bytes, of aBuffer, including
+ * room for the terminating null. If the information
+ * to be printed would be larger than aBuffer, it
+ * will be truncated so that aBuffer[aBufferSize-1]
+ * is the terminating null.
+ * @param aFrameNumber The frame number.
+ * @param aPC The code address.
+ * @param aDetails The value filled in by MozDescribeCodeAddress(aPC).
+ */
+MFBT_API void
+MozFormatCodeAddressDetails(char* aBuffer, uint32_t aBufferSize,
+ uint32_t aFrameNumber, void* aPC,
+ const MozCodeAddressDetails* aDetails);
+
+namespace mozilla {
+
+MFBT_API bool
+FramePointerStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames,
+ uint32_t aMaxFrames, void* aClosure, void** aBp,
+ void* aStackEnd);
+
+} // namespace mozilla
+
+/**
+ * Initialize the critical sections for this platform so that we can
+ * abort stack walks when needed.
+ */
+MFBT_API void
+StackWalkInitCriticalAddress(void);
+
+#endif
diff --git a/mozglue/misc/StackWalk_windows.h b/mozglue/misc/StackWalk_windows.h
new file mode 100644
index 000000000..5ffdd2068
--- /dev/null
+++ b/mozglue/misc/StackWalk_windows.h
@@ -0,0 +1,21 @@
+#ifndef mozilla_StackWalk_windows_h
+#define mozilla_StackWalk_windows_h
+
+#include "mozilla/Types.h"
+
+/**
+ * Allow stack walkers to work around the egregious win64 dynamic lookup table
+ * list API by locking around SuspendThread to avoid deadlock.
+ *
+ * See comment in StackWalk.cpp
+ */
+MFBT_API void
+AcquireStackWalkWorkaroundLock();
+
+MFBT_API bool
+TryAcquireStackWalkWorkaroundLock();
+
+MFBT_API void
+ReleaseStackWalkWorkaroundLock();
+
+#endif // mozilla_StackWalk_windows_h
diff --git a/mozglue/misc/TimeStamp.cpp b/mozglue/misc/TimeStamp.cpp
new file mode 100644
index 000000000..932b75c99
--- /dev/null
+++ b/mozglue/misc/TimeStamp.cpp
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Implementation of the OS-independent methods of the TimeStamp class
+ */
+
+#include "mozilla/TimeStamp.h"
+#include <stdio.h>
+#include <string.h>
+
+namespace mozilla {
+
+/**
+ * Wrapper class used to initialize static data used by the TimeStamp class
+ */
+struct TimeStampInitialization
+{
+ /**
+ * First timestamp taken when the class static initializers are run. This
+ * timestamp is used to sanitize timestamps coming from different sources.
+ */
+ TimeStamp mFirstTimeStamp;
+
+ /**
+ * Timestamp representing the time when the process was created. This field
+ * is populated lazily the first time this information is required and is
+ * replaced every time the process is restarted.
+ */
+ TimeStamp mProcessCreation;
+
+ TimeStampInitialization()
+ {
+ TimeStamp::Startup();
+ mFirstTimeStamp = TimeStamp::Now();
+ };
+
+ ~TimeStampInitialization()
+ {
+ TimeStamp::Shutdown();
+ };
+};
+
+static TimeStampInitialization sInitOnce;
+
+MFBT_API TimeStamp
+TimeStamp::ProcessCreation(bool& aIsInconsistent)
+{
+ aIsInconsistent = false;
+
+ if (sInitOnce.mProcessCreation.IsNull()) {
+ char* mozAppRestart = getenv("MOZ_APP_RESTART");
+ TimeStamp ts;
+
+ /* When calling PR_SetEnv() with an empty value the existing variable may
+ * be unset or set to the empty string depending on the underlying platform
+ * thus we have to check if the variable is present and not empty. */
+ if (mozAppRestart && (strcmp(mozAppRestart, "") != 0)) {
+ /* Firefox was restarted, use the first time-stamp we've taken as the new
+ * process startup time. */
+ ts = sInitOnce.mFirstTimeStamp;
+ } else {
+ TimeStamp now = Now();
+ uint64_t uptime = ComputeProcessUptime();
+
+ ts = now - TimeDuration::FromMicroseconds(uptime);
+
+ if ((ts > sInitOnce.mFirstTimeStamp) || (uptime == 0)) {
+ /* If the process creation timestamp was inconsistent replace it with
+ * the first one instead and notify that a telemetry error was
+ * detected. */
+ aIsInconsistent = true;
+ ts = sInitOnce.mFirstTimeStamp;
+ }
+ }
+
+ sInitOnce.mProcessCreation = ts;
+ }
+
+ return sInitOnce.mProcessCreation;
+}
+
+void
+TimeStamp::RecordProcessRestart()
+{
+ sInitOnce.mProcessCreation = TimeStamp();
+}
+
+} // namespace mozilla
diff --git a/mozglue/misc/TimeStamp.h b/mozglue/misc/TimeStamp.h
new file mode 100644
index 000000000..a1a0eb360
--- /dev/null
+++ b/mozglue/misc/TimeStamp.h
@@ -0,0 +1,609 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_TimeStamp_h
+#define mozilla_TimeStamp_h
+
+#include <stdint.h>
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/Types.h"
+
+namespace IPC {
+template<typename T> struct ParamTraits;
+} // namespace IPC
+
+#ifdef XP_WIN
+// defines TimeStampValue as a complex value keeping both
+// GetTickCount and QueryPerformanceCounter values
+#include "TimeStamp_windows.h"
+#endif
+
+namespace mozilla {
+
+#ifndef XP_WIN
+typedef uint64_t TimeStampValue;
+#endif
+
+class TimeStamp;
+
+/**
+ * Platform-specific implementation details of BaseTimeDuration.
+ */
+class BaseTimeDurationPlatformUtils
+{
+public:
+ static MFBT_API double ToSeconds(int64_t aTicks);
+ static MFBT_API double ToSecondsSigDigits(int64_t aTicks);
+ static MFBT_API int64_t TicksFromMilliseconds(double aMilliseconds);
+ static MFBT_API int64_t ResolutionInTicks();
+};
+
+/**
+ * Instances of this class represent the length of an interval of time.
+ * Negative durations are allowed, meaning the end is before the start.
+ *
+ * Internally the duration is stored as a int64_t in units of
+ * PR_TicksPerSecond() when building with NSPR interval timers, or a
+ * system-dependent unit when building with system clocks. The
+ * system-dependent unit must be constant, otherwise the semantics of
+ * this class would be broken.
+ *
+ * The ValueCalculator template parameter determines how arithmetic
+ * operations are performed on the integer count of ticks (mValue).
+ */
+template <typename ValueCalculator>
+class BaseTimeDuration
+{
+public:
+ // The default duration is 0.
+ constexpr BaseTimeDuration() : mValue(0) {}
+ // Allow construction using '0' as the initial value, for readability,
+ // but no other numbers (so we don't have any implicit unit conversions).
+ struct _SomethingVeryRandomHere;
+ MOZ_IMPLICIT BaseTimeDuration(_SomethingVeryRandomHere* aZero) : mValue(0)
+ {
+ MOZ_ASSERT(!aZero, "Who's playing funny games here?");
+ }
+ // Default copy-constructor and assignment are OK
+
+ // Converting copy-constructor and assignment operator
+ template <typename E>
+ explicit BaseTimeDuration(const BaseTimeDuration<E>& aOther)
+ : mValue(aOther.mValue)
+ { }
+
+ template <typename E>
+ BaseTimeDuration& operator=(const BaseTimeDuration<E>& aOther)
+ {
+ mValue = aOther.mValue;
+ return *this;
+ }
+
+ double ToSeconds() const
+ {
+ if (mValue == INT64_MAX) {
+ return PositiveInfinity<double>();
+ }
+ if (mValue == INT64_MIN) {
+ return NegativeInfinity<double>();
+ }
+ return BaseTimeDurationPlatformUtils::ToSeconds(mValue);
+ }
+ // Return a duration value that includes digits of time we think to
+ // be significant. This method should be used when displaying a
+ // time to humans.
+ double ToSecondsSigDigits() const
+ {
+ if (mValue == INT64_MAX) {
+ return PositiveInfinity<double>();
+ }
+ if (mValue == INT64_MIN) {
+ return NegativeInfinity<double>();
+ }
+ return BaseTimeDurationPlatformUtils::ToSecondsSigDigits(mValue);
+ }
+ double ToMilliseconds() const { return ToSeconds() * 1000.0; }
+ double ToMicroseconds() const { return ToMilliseconds() * 1000.0; }
+
+ // Using a double here is safe enough; with 53 bits we can represent
+ // durations up to over 280,000 years exactly. If the units of
+ // mValue do not allow us to represent durations of that length,
+ // long durations are clamped to the max/min representable value
+ // instead of overflowing.
+ static inline BaseTimeDuration FromSeconds(double aSeconds)
+ {
+ return FromMilliseconds(aSeconds * 1000.0);
+ }
+ static BaseTimeDuration FromMilliseconds(double aMilliseconds)
+ {
+ if (aMilliseconds == PositiveInfinity<double>()) {
+ return Forever();
+ }
+ if (aMilliseconds == NegativeInfinity<double>()) {
+ return FromTicks(INT64_MIN);
+ }
+ return FromTicks(
+ BaseTimeDurationPlatformUtils::TicksFromMilliseconds(aMilliseconds));
+ }
+ static inline BaseTimeDuration FromMicroseconds(double aMicroseconds)
+ {
+ return FromMilliseconds(aMicroseconds / 1000.0);
+ }
+
+ static BaseTimeDuration Forever()
+ {
+ return FromTicks(INT64_MAX);
+ }
+
+ BaseTimeDuration operator+(const BaseTimeDuration& aOther) const
+ {
+ return FromTicks(ValueCalculator::Add(mValue, aOther.mValue));
+ }
+ BaseTimeDuration operator-(const BaseTimeDuration& aOther) const
+ {
+ return FromTicks(ValueCalculator::Subtract(mValue, aOther.mValue));
+ }
+ BaseTimeDuration& operator+=(const BaseTimeDuration& aOther)
+ {
+ mValue = ValueCalculator::Add(mValue, aOther.mValue);
+ return *this;
+ }
+ BaseTimeDuration& operator-=(const BaseTimeDuration& aOther)
+ {
+ mValue = ValueCalculator::Subtract(mValue, aOther.mValue);
+ return *this;
+ }
+ BaseTimeDuration operator-() const
+ {
+ // We don't just use FromTicks(ValueCalculator::Subtract(0, mValue))
+ // since that won't give the correct result for -TimeDuration::Forever().
+ int64_t ticks;
+ if (mValue == INT64_MAX) {
+ ticks = INT64_MIN;
+ } else if (mValue == INT64_MIN) {
+ ticks = INT64_MAX;
+ } else {
+ ticks = -mValue;
+ }
+
+ return FromTicks(ticks);
+ }
+
+private:
+ // Block double multiplier (slower, imprecise if long duration) - Bug 853398.
+ // If required, use MultDouble explicitly and with care.
+ BaseTimeDuration operator*(const double aMultiplier) const = delete;
+
+ // Block double divisor (for the same reason, and because dividing by
+ // fractional values would otherwise invoke the int64_t variant, and rounding
+ // the passed argument can then cause divide-by-zero) - Bug 1147491.
+ BaseTimeDuration operator/(const double aDivisor) const = delete;
+
+public:
+ BaseTimeDuration MultDouble(double aMultiplier) const
+ {
+ return FromTicks(ValueCalculator::Multiply(mValue, aMultiplier));
+ }
+ BaseTimeDuration operator*(const int32_t aMultiplier) const
+ {
+ return FromTicks(ValueCalculator::Multiply(mValue, aMultiplier));
+ }
+ BaseTimeDuration operator*(const uint32_t aMultiplier) const
+ {
+ return FromTicks(ValueCalculator::Multiply(mValue, aMultiplier));
+ }
+ BaseTimeDuration operator*(const int64_t aMultiplier) const
+ {
+ return FromTicks(ValueCalculator::Multiply(mValue, aMultiplier));
+ }
+ BaseTimeDuration operator*(const uint64_t aMultiplier) const
+ {
+ if (aMultiplier > INT64_MAX) {
+ return Forever();
+ }
+ return FromTicks(ValueCalculator::Multiply(mValue, aMultiplier));
+ }
+ BaseTimeDuration operator/(const int64_t aDivisor) const
+ {
+ MOZ_ASSERT(aDivisor != 0, "Division by zero");
+ return FromTicks(ValueCalculator::Divide(mValue, aDivisor));
+ }
+ double operator/(const BaseTimeDuration& aOther) const
+ {
+#ifndef MOZ_B2G
+ // Bug 1066388 - This fails on B2G ICS Emulator
+ MOZ_ASSERT(aOther.mValue != 0, "Division by zero");
+#endif
+ return ValueCalculator::DivideDouble(mValue, aOther.mValue);
+ }
+ BaseTimeDuration operator%(const BaseTimeDuration& aOther) const
+ {
+ MOZ_ASSERT(aOther.mValue != 0, "Division by zero");
+ return FromTicks(ValueCalculator::Modulo(mValue, aOther.mValue));
+ }
+
+ template<typename E>
+ bool operator<(const BaseTimeDuration<E>& aOther) const
+ {
+ return mValue < aOther.mValue;
+ }
+ template<typename E>
+ bool operator<=(const BaseTimeDuration<E>& aOther) const
+ {
+ return mValue <= aOther.mValue;
+ }
+ template<typename E>
+ bool operator>=(const BaseTimeDuration<E>& aOther) const
+ {
+ return mValue >= aOther.mValue;
+ }
+ template<typename E>
+ bool operator>(const BaseTimeDuration<E>& aOther) const
+ {
+ return mValue > aOther.mValue;
+ }
+ template<typename E>
+ bool operator==(const BaseTimeDuration<E>& aOther) const
+ {
+ return mValue == aOther.mValue;
+ }
+ template<typename E>
+ bool operator!=(const BaseTimeDuration<E>& aOther) const
+ {
+ return mValue != aOther.mValue;
+ }
+ bool IsZero() const
+ {
+ return mValue == 0;
+ }
+ explicit operator bool() const
+ {
+ return mValue != 0;
+ }
+
+ // Return a best guess at the system's current timing resolution,
+ // which might be variable. BaseTimeDurations below this order of
+ // magnitude are meaningless, and those at the same order of
+ // magnitude or just above are suspect.
+ static BaseTimeDuration Resolution() {
+ return FromTicks(BaseTimeDurationPlatformUtils::ResolutionInTicks());
+ }
+
+ // We could define additional operators here:
+ // -- convert to/from other time units
+ // -- scale duration by a float
+ // but let's do that on demand.
+ // Comparing durations for equality will only lead to bugs on
+ // platforms with high-resolution timers.
+
+private:
+ friend class TimeStamp;
+ friend struct IPC::ParamTraits<mozilla::BaseTimeDuration<ValueCalculator>>;
+ template <typename>
+ friend class BaseTimeDuration;
+
+ static BaseTimeDuration FromTicks(int64_t aTicks)
+ {
+ BaseTimeDuration t;
+ t.mValue = aTicks;
+ return t;
+ }
+
+ static BaseTimeDuration FromTicks(double aTicks)
+ {
+ // NOTE: this MUST be a >= test, because int64_t(double(INT64_MAX))
+ // overflows and gives INT64_MIN.
+ if (aTicks >= double(INT64_MAX)) {
+ return FromTicks(INT64_MAX);
+ }
+
+ // This MUST be a <= test.
+ if (aTicks <= double(INT64_MIN)) {
+ return FromTicks(INT64_MIN);
+ }
+
+ return FromTicks(int64_t(aTicks));
+ }
+
+ // Duration, result is implementation-specific difference of two TimeStamps
+ int64_t mValue;
+};
+
+/**
+ * Perform arithmetic operations on the value of a BaseTimeDuration without
+ * doing strict checks on the range of values.
+ */
+class TimeDurationValueCalculator
+{
+public:
+ static int64_t Add(int64_t aA, int64_t aB) { return aA + aB; }
+ static int64_t Subtract(int64_t aA, int64_t aB) { return aA - aB; }
+
+ template <typename T>
+ static int64_t Multiply(int64_t aA, T aB)
+ {
+ static_assert(IsIntegral<T>::value,
+ "Using integer multiplication routine with non-integer type."
+ " Further specialization required");
+ return aA * static_cast<int64_t>(aB);
+ }
+
+ static int64_t Divide(int64_t aA, int64_t aB) { return aA / aB; }
+ static double DivideDouble(int64_t aA, int64_t aB)
+ {
+ return static_cast<double>(aA) / aB;
+ }
+ static int64_t Modulo(int64_t aA, int64_t aB) { return aA % aB; }
+};
+
+template <>
+inline int64_t
+TimeDurationValueCalculator::Multiply<double>(int64_t aA, double aB)
+{
+ return static_cast<int64_t>(aA * aB);
+}
+
+/**
+ * Specialization of BaseTimeDuration that uses TimeDurationValueCalculator for
+ * arithmetic on the mValue member.
+ *
+ * Use this class for time durations that are *not* expected to hold values of
+ * Forever (or the negative equivalent) or when such time duration are *not*
+ * expected to be used in arithmetic operations.
+ */
+typedef BaseTimeDuration<TimeDurationValueCalculator> TimeDuration;
+
+/**
+ * Instances of this class represent moments in time, or a special
+ * "null" moment. We do not use the non-monotonic system clock or
+ * local time, since they can be reset, causing apparent backward
+ * travel in time, which can confuse algorithms. Instead we measure
+ * elapsed time according to the system. This time can never go
+ * backwards (i.e. it never wraps around, at least not in less than
+ * five million years of system elapsed time). It might not advance
+ * while the system is sleeping. If TimeStamp::SetNow() is not called
+ * at all for hours or days, we might not notice the passage of some
+ * of that time.
+ *
+ * We deliberately do not expose a way to convert TimeStamps to some
+ * particular unit. All you can do is compute a difference between two
+ * TimeStamps to get a TimeDuration. You can also add a TimeDuration
+ * to a TimeStamp to get a new TimeStamp. You can't do something
+ * meaningless like add two TimeStamps.
+ *
+ * Internally this is implemented as either a wrapper around
+ * - high-resolution, monotonic, system clocks if they exist on this
+ * platform
+ * - PRIntervalTime otherwise. We detect wraparounds of
+ * PRIntervalTime and work around them.
+ *
+ * This class is similar to C++11's time_point, however it is
+ * explicitly nullable and provides an IsNull() method. time_point
+ * is initialized to the clock's epoch and provides a
+ * time_since_epoch() method that functions similiarly. i.e.
+ * t.IsNull() is equivalent to t.time_since_epoch() == decltype(t)::duration::zero();
+ */
+class TimeStamp
+{
+public:
+ /**
+ * Initialize to the "null" moment
+ */
+ constexpr TimeStamp() : mValue(0) {}
+ // Default copy-constructor and assignment are OK
+
+ /**
+ * The system timestamps are the same as the TimeStamp
+ * retrieved by mozilla::TimeStamp. Since we need this for
+ * vsync timestamps, we enable the creation of mozilla::TimeStamps
+ * on platforms that support vsync aligned refresh drivers / compositors
+ * Verified true as of Jan 31, 2015: B2G and OS X
+ * False on Windows 7
+ * UNTESTED ON OTHER PLATFORMS
+ */
+#if defined(MOZ_WIDGET_GONK) || defined(XP_DARWIN)
+ static TimeStamp FromSystemTime(int64_t aSystemTime)
+ {
+ static_assert(sizeof(aSystemTime) == sizeof(TimeStampValue),
+ "System timestamp should be same units as TimeStampValue");
+ return TimeStamp(aSystemTime);
+ }
+#endif
+
+ /**
+ * Return true if this is the "null" moment
+ */
+ bool IsNull() const { return mValue == 0; }
+
+ /**
+ * Return true if this is not the "null" moment, may be used in tests, e.g.:
+ * |if (timestamp) { ... }|
+ */
+ explicit operator bool() const
+ {
+ return mValue != 0;
+ }
+
+ /**
+ * Return a timestamp reflecting the current elapsed system time. This
+ * is monotonically increasing (i.e., does not decrease) over the
+ * lifetime of this process' XPCOM session.
+ *
+ * Now() is trying to ensure the best possible precision on each platform,
+ * at least one millisecond.
+ *
+ * NowLoRes() has been introduced to workaround performance problems of
+ * QueryPerformanceCounter on the Windows platform. NowLoRes() is giving
+ * lower precision, usually 15.6 ms, but with very good performance benefit.
+ * Use it for measurements of longer times, like >200ms timeouts.
+ */
+ static TimeStamp Now() { return Now(true); }
+ static TimeStamp NowLoRes() { return Now(false); }
+
+ /**
+ * Return a timestamp representing the time when the current process was
+ * created which will be comparable with other timestamps taken with this
+ * class. If the actual process creation time is detected to be inconsistent
+ * the @a aIsInconsistent parameter will be set to true, the returned
+ * timestamp however will still be valid though inaccurate.
+ *
+ * @param aIsInconsistent Set to true if an inconsistency was detected in the
+ * process creation time
+ * @returns A timestamp representing the time when the process was created,
+ * this timestamp is always valid even when errors are reported
+ */
+ static MFBT_API TimeStamp ProcessCreation(bool& aIsInconsistent);
+
+ /**
+ * Records a process restart. After this call ProcessCreation() will return
+ * the time when the browser was restarted instead of the actual time when
+ * the process was created.
+ */
+ static MFBT_API void RecordProcessRestart();
+
+ /**
+ * Compute the difference between two timestamps. Both must be non-null.
+ */
+ TimeDuration operator-(const TimeStamp& aOther) const
+ {
+ MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+ MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
+ static_assert(-INT64_MAX > INT64_MIN, "int64_t sanity check");
+ int64_t ticks = int64_t(mValue - aOther.mValue);
+ // Check for overflow.
+ if (mValue > aOther.mValue) {
+ if (ticks < 0) {
+ ticks = INT64_MAX;
+ }
+ } else {
+ if (ticks > 0) {
+ ticks = INT64_MIN;
+ }
+ }
+ return TimeDuration::FromTicks(ticks);
+ }
+
+ TimeStamp operator+(const TimeDuration& aOther) const
+ {
+ TimeStamp result = *this;
+ result += aOther;
+ return result;
+ }
+ TimeStamp operator-(const TimeDuration& aOther) const
+ {
+ TimeStamp result = *this;
+ result -= aOther;
+ return result;
+ }
+ TimeStamp& operator+=(const TimeDuration& aOther)
+ {
+ MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+ TimeStampValue value = mValue + aOther.mValue;
+ // Check for underflow.
+ // (We don't check for overflow because it's not obvious what the error
+ // behavior should be in that case.)
+ if (aOther.mValue < 0 && value > mValue) {
+ value = 0;
+ }
+ mValue = value;
+ return *this;
+ }
+ TimeStamp& operator-=(const TimeDuration& aOther)
+ {
+ MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+ TimeStampValue value = mValue - aOther.mValue;
+ // Check for underflow.
+ // (We don't check for overflow because it's not obvious what the error
+ // behavior should be in that case.)
+ if (aOther.mValue > 0 && value > mValue) {
+ value = 0;
+ }
+ mValue = value;
+ return *this;
+ }
+
+ bool operator<(const TimeStamp& aOther) const
+ {
+ MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+ MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
+ return mValue < aOther.mValue;
+ }
+ bool operator<=(const TimeStamp& aOther) const
+ {
+ MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+ MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
+ return mValue <= aOther.mValue;
+ }
+ bool operator>=(const TimeStamp& aOther) const
+ {
+ MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+ MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
+ return mValue >= aOther.mValue;
+ }
+ bool operator>(const TimeStamp& aOther) const
+ {
+ MOZ_ASSERT(!IsNull(), "Cannot compute with a null value");
+ MOZ_ASSERT(!aOther.IsNull(), "Cannot compute with aOther null value");
+ return mValue > aOther.mValue;
+ }
+ bool operator==(const TimeStamp& aOther) const
+ {
+ return IsNull()
+ ? aOther.IsNull()
+ : !aOther.IsNull() && mValue == aOther.mValue;
+ }
+ bool operator!=(const TimeStamp& aOther) const
+ {
+ return !(*this == aOther);
+ }
+
+ // Comparing TimeStamps for equality should be discouraged. Adding
+ // two TimeStamps, or scaling TimeStamps, is nonsense and must never
+ // be allowed.
+
+ static MFBT_API void Startup();
+ static MFBT_API void Shutdown();
+
+private:
+ friend struct IPC::ParamTraits<mozilla::TimeStamp>;
+ friend void StartupTimelineRecordExternal(int, uint64_t);
+
+ MOZ_IMPLICIT TimeStamp(TimeStampValue aValue) : mValue(aValue) {}
+
+ static MFBT_API TimeStamp Now(bool aHighResolution);
+
+ /**
+ * Computes the uptime of the current process in microseconds. The result
+ * is platform-dependent and needs to be checked against existing timestamps
+ * for consistency.
+ *
+ * @returns The number of microseconds since the calling process was started
+ * or 0 if an error was encountered while computing the uptime
+ */
+ static MFBT_API uint64_t ComputeProcessUptime();
+
+ /**
+ * When built with PRIntervalTime, a value of 0 means this instance
+ * is "null". Otherwise, the low 32 bits represent a PRIntervalTime,
+ * and the high 32 bits represent a counter of the number of
+ * rollovers of PRIntervalTime that we've seen. This counter starts
+ * at 1 to avoid a real time colliding with the "null" value.
+ *
+ * PR_INTERVAL_MAX is set at 100,000 ticks per second. So the minimum
+ * time to wrap around is about 2^64/100000 seconds, i.e. about
+ * 5,849,424 years.
+ *
+ * When using a system clock, a value is system dependent.
+ */
+ TimeStampValue mValue;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_TimeStamp_h */
diff --git a/mozglue/misc/TimeStamp_darwin.cpp b/mozglue/misc/TimeStamp_darwin.cpp
new file mode 100644
index 000000000..f30bc9846
--- /dev/null
+++ b/mozglue/misc/TimeStamp_darwin.cpp
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//
+// Implement TimeStamp::Now() with mach_absolute_time
+//
+// The "tick" unit for mach_absolute_time is defined using mach_timebase_info() which
+// gives a conversion ratio to nanoseconds. For more information see Apple's QA1398.
+//
+// This code is inspired by Chromium's time_mac.cc. The biggest
+// differences are that we explicitly initialize using
+// TimeStamp::Initialize() instead of lazily in Now() and that
+// we store the time value in ticks and convert when needed instead
+// of storing the time value in nanoseconds.
+
+#include <mach/mach_time.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "mozilla/TimeStamp.h"
+
+// Estimate of the smallest duration of time we can measure.
+static uint64_t sResolution;
+static uint64_t sResolutionSigDigs;
+
+static const uint64_t kNsPerMs = 1000000;
+static const uint64_t kUsPerSec = 1000000;
+static const double kNsPerMsd = 1000000.0;
+static const double kNsPerSecd = 1000000000.0;
+
+static bool gInitialized = false;
+static double sNsPerTick;
+
+static uint64_t
+ClockTime()
+{
+ // mach_absolute_time is it when it comes to ticks on the Mac. Other calls
+ // with less precision (such as TickCount) just call through to
+ // mach_absolute_time.
+ //
+ // At the time of writing mach_absolute_time returns the number of nanoseconds
+ // since boot. This won't overflow 64bits for 500+ years so we aren't going
+ // to worry about that possiblity
+ return mach_absolute_time();
+}
+
+static uint64_t
+ClockResolutionNs()
+{
+ uint64_t start = ClockTime();
+ uint64_t end = ClockTime();
+ uint64_t minres = (end - start);
+
+ // 10 total trials is arbitrary: what we're trying to avoid by
+ // looping is getting unlucky and being interrupted by a context
+ // switch or signal, or being bitten by paging/cache effects
+ for (int i = 0; i < 9; ++i) {
+ start = ClockTime();
+ end = ClockTime();
+
+ uint64_t candidate = (start - end);
+ if (candidate < minres) {
+ minres = candidate;
+ }
+ }
+
+ if (0 == minres) {
+ // measurable resolution is either incredibly low, ~1ns, or very
+ // high. fall back on NSPR's resolution assumption
+ minres = 1 * kNsPerMs;
+ }
+
+ return minres;
+}
+
+namespace mozilla {
+
+double
+BaseTimeDurationPlatformUtils::ToSeconds(int64_t aTicks)
+{
+ MOZ_ASSERT(gInitialized, "calling TimeDuration too early");
+ return (aTicks * sNsPerTick) / kNsPerSecd;
+}
+
+double
+BaseTimeDurationPlatformUtils::ToSecondsSigDigits(int64_t aTicks)
+{
+ MOZ_ASSERT(gInitialized, "calling TimeDuration too early");
+ // don't report a value < mResolution ...
+ int64_t valueSigDigs = sResolution * (aTicks / sResolution);
+ // and chop off insignificant digits
+ valueSigDigs = sResolutionSigDigs * (valueSigDigs / sResolutionSigDigs);
+ return (valueSigDigs * sNsPerTick) / kNsPerSecd;
+}
+
+int64_t
+BaseTimeDurationPlatformUtils::TicksFromMilliseconds(double aMilliseconds)
+{
+ MOZ_ASSERT(gInitialized, "calling TimeDuration too early");
+ double result = (aMilliseconds * kNsPerMsd) / sNsPerTick;
+ if (result > INT64_MAX) {
+ return INT64_MAX;
+ } else if (result < INT64_MIN) {
+ return INT64_MIN;
+ }
+
+ return result;
+}
+
+int64_t
+BaseTimeDurationPlatformUtils::ResolutionInTicks()
+{
+ MOZ_ASSERT(gInitialized, "calling TimeDuration too early");
+ return static_cast<int64_t>(sResolution);
+}
+
+void
+TimeStamp::Startup()
+{
+ if (gInitialized) {
+ return;
+ }
+
+ mach_timebase_info_data_t timebaseInfo;
+ // Apple's QA1398 suggests that the output from mach_timebase_info
+ // will not change while a program is running, so it should be safe
+ // to cache the result.
+ kern_return_t kr = mach_timebase_info(&timebaseInfo);
+ if (kr != KERN_SUCCESS) {
+ MOZ_RELEASE_ASSERT(false, "mach_timebase_info failed");
+ }
+
+ sNsPerTick = double(timebaseInfo.numer) / timebaseInfo.denom;
+
+ sResolution = ClockResolutionNs();
+
+ // find the number of significant digits in sResolution, for the
+ // sake of ToSecondsSigDigits()
+ for (sResolutionSigDigs = 1;
+ !(sResolutionSigDigs == sResolution ||
+ 10 * sResolutionSigDigs > sResolution);
+ sResolutionSigDigs *= 10);
+
+ gInitialized = true;
+
+ return;
+}
+
+void
+TimeStamp::Shutdown()
+{
+}
+
+TimeStamp
+TimeStamp::Now(bool aHighResolution)
+{
+ return TimeStamp(ClockTime());
+}
+
+// Computes and returns the process uptime in microseconds.
+// Returns 0 if an error was encountered.
+
+uint64_t
+TimeStamp::ComputeProcessUptime()
+{
+ struct timeval tv;
+ int rv = gettimeofday(&tv, nullptr);
+
+ if (rv == -1) {
+ return 0;
+ }
+
+ int mib[] = {
+ CTL_KERN,
+ KERN_PROC,
+ KERN_PROC_PID,
+ getpid(),
+ };
+ u_int mibLen = sizeof(mib) / sizeof(mib[0]);
+
+ struct kinfo_proc proc;
+ size_t bufferSize = sizeof(proc);
+ rv = sysctl(mib, mibLen, &proc, &bufferSize, nullptr, 0);
+
+ if (rv == -1) {
+ return 0;
+ }
+
+ uint64_t startTime =
+ ((uint64_t)proc.kp_proc.p_un.__p_starttime.tv_sec * kUsPerSec) +
+ proc.kp_proc.p_un.__p_starttime.tv_usec;
+ uint64_t now = (tv.tv_sec * kUsPerSec) + tv.tv_usec;
+
+ if (startTime > now) {
+ return 0;
+ }
+
+ return now - startTime;
+}
+
+} // namespace mozilla
diff --git a/mozglue/misc/TimeStamp_posix.cpp b/mozglue/misc/TimeStamp_posix.cpp
new file mode 100644
index 000000000..05deddea4
--- /dev/null
+++ b/mozglue/misc/TimeStamp_posix.cpp
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//
+// Implement TimeStamp::Now() with POSIX clocks.
+//
+// The "tick" unit for POSIX clocks is simply a nanosecond, as this is
+// the smallest unit of time representable by struct timespec. That
+// doesn't mean that a nanosecond is the resolution of TimeDurations
+// obtained with this API; see TimeDuration::Resolution;
+//
+
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+
+#if defined(__DragonFly__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#endif
+
+#if defined(__DragonFly__) || defined(__FreeBSD__)
+#include <sys/user.h>
+#endif
+
+#if defined(__NetBSD__)
+#undef KERN_PROC
+#define KERN_PROC KERN_PROC2
+#define KINFO_PROC struct kinfo_proc2
+#else
+#define KINFO_PROC struct kinfo_proc
+#endif
+
+#if defined(__DragonFly__)
+#define KP_START_SEC kp_start.tv_sec
+#define KP_START_USEC kp_start.tv_usec
+#elif defined(__FreeBSD__)
+#define KP_START_SEC ki_start.tv_sec
+#define KP_START_USEC ki_start.tv_usec
+#else
+#define KP_START_SEC p_ustart_sec
+#define KP_START_USEC p_ustart_usec
+#endif
+
+#include "mozilla/Sprintf.h"
+#include "mozilla/TimeStamp.h"
+#include <pthread.h>
+
+// Estimate of the smallest duration of time we can measure.
+static uint64_t sResolution;
+static uint64_t sResolutionSigDigs;
+
+static const uint16_t kNsPerUs = 1000;
+static const uint64_t kNsPerMs = 1000000;
+static const uint64_t kNsPerSec = 1000000000;
+static const double kNsPerMsd = 1000000.0;
+static const double kNsPerSecd = 1000000000.0;
+
+static uint64_t
+TimespecToNs(const struct timespec& aTs)
+{
+ uint64_t baseNs = uint64_t(aTs.tv_sec) * kNsPerSec;
+ return baseNs + uint64_t(aTs.tv_nsec);
+}
+
+static uint64_t
+ClockTimeNs()
+{
+ struct timespec ts;
+ // this can't fail: we know &ts is valid, and TimeStamp::Startup()
+ // checks that CLOCK_MONOTONIC is supported (and aborts if not)
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ // tv_sec is defined to be relative to an arbitrary point in time,
+ // but it would be madness for that point in time to be earlier than
+ // the Epoch. So we can safely assume that even if time_t is 32
+ // bits, tv_sec won't overflow while the browser is open. Revisit
+ // this argument if we're still building with 32-bit time_t around
+ // the year 2037.
+ return TimespecToNs(ts);
+}
+
+static uint64_t
+ClockResolutionNs()
+{
+ // NB: why not rely on clock_getres()? Two reasons: (i) it might
+ // lie, and (ii) it might return an "ideal" resolution that while
+ // theoretically true, could never be measured in practice. Since
+ // clock_gettime() likely involves a system call on your platform,
+ // the "actual" timing resolution shouldn't be lower than syscall
+ // overhead.
+
+ uint64_t start = ClockTimeNs();
+ uint64_t end = ClockTimeNs();
+ uint64_t minres = (end - start);
+
+ // 10 total trials is arbitrary: what we're trying to avoid by
+ // looping is getting unlucky and being interrupted by a context
+ // switch or signal, or being bitten by paging/cache effects
+ for (int i = 0; i < 9; ++i) {
+ start = ClockTimeNs();
+ end = ClockTimeNs();
+
+ uint64_t candidate = (start - end);
+ if (candidate < minres) {
+ minres = candidate;
+ }
+ }
+
+ if (0 == minres) {
+ // measurable resolution is either incredibly low, ~1ns, or very
+ // high. fall back on clock_getres()
+ struct timespec ts;
+ if (0 == clock_getres(CLOCK_MONOTONIC, &ts)) {
+ minres = TimespecToNs(ts);
+ }
+ }
+
+ if (0 == minres) {
+ // clock_getres probably failed. fall back on NSPR's resolution
+ // assumption
+ minres = 1 * kNsPerMs;
+ }
+
+ return minres;
+}
+
+namespace mozilla {
+
+double
+BaseTimeDurationPlatformUtils::ToSeconds(int64_t aTicks)
+{
+ return double(aTicks) / kNsPerSecd;
+}
+
+double
+BaseTimeDurationPlatformUtils::ToSecondsSigDigits(int64_t aTicks)
+{
+ // don't report a value < mResolution ...
+ int64_t valueSigDigs = sResolution * (aTicks / sResolution);
+ // and chop off insignificant digits
+ valueSigDigs = sResolutionSigDigs * (valueSigDigs / sResolutionSigDigs);
+ return double(valueSigDigs) / kNsPerSecd;
+}
+
+int64_t
+BaseTimeDurationPlatformUtils::TicksFromMilliseconds(double aMilliseconds)
+{
+ double result = aMilliseconds * kNsPerMsd;
+ if (result > INT64_MAX) {
+ return INT64_MAX;
+ } else if (result < INT64_MIN) {
+ return INT64_MIN;
+ }
+
+ return result;
+}
+
+int64_t
+BaseTimeDurationPlatformUtils::ResolutionInTicks()
+{
+ return static_cast<int64_t>(sResolution);
+}
+
+static bool gInitialized = false;
+
+void
+TimeStamp::Startup()
+{
+ if (gInitialized) {
+ return;
+ }
+
+ struct timespec dummy;
+ if (clock_gettime(CLOCK_MONOTONIC, &dummy) != 0) {
+ MOZ_CRASH("CLOCK_MONOTONIC is absent!");
+ }
+
+ sResolution = ClockResolutionNs();
+
+ // find the number of significant digits in sResolution, for the
+ // sake of ToSecondsSigDigits()
+ for (sResolutionSigDigs = 1;
+ !(sResolutionSigDigs == sResolution ||
+ 10 * sResolutionSigDigs > sResolution);
+ sResolutionSigDigs *= 10);
+
+ gInitialized = true;
+
+ return;
+}
+
+void
+TimeStamp::Shutdown()
+{
+}
+
+TimeStamp
+TimeStamp::Now(bool aHighResolution)
+{
+ return TimeStamp(ClockTimeNs());
+}
+
+#if defined(XP_LINUX) || defined(ANDROID)
+
+// Calculates the amount of jiffies that have elapsed since boot and up to the
+// starttime value of a specific process as found in its /proc/*/stat file.
+// Returns 0 if an error occurred.
+
+static uint64_t
+JiffiesSinceBoot(const char* aFile)
+{
+ char stat[512];
+
+ FILE* f = fopen(aFile, "r");
+ if (!f) {
+ return 0;
+ }
+
+ int n = fread(&stat, 1, sizeof(stat) - 1, f);
+
+ fclose(f);
+
+ if (n <= 0) {
+ return 0;
+ }
+
+ stat[n] = 0;
+
+ long long unsigned startTime = 0; // instead of uint64_t to keep GCC quiet
+ char* s = strrchr(stat, ')');
+
+ if (!s) {
+ return 0;
+ }
+
+ int rv = sscanf(s + 2,
+ "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u "
+ "%*u %*u %*u %*d %*d %*d %*d %*d %*d %llu",
+ &startTime);
+
+ if (rv != 1 || !startTime) {
+ return 0;
+ }
+
+ return startTime;
+}
+
+// Computes the interval that has elapsed between the thread creation and the
+// process creation by comparing the starttime fields in the respective
+// /proc/*/stat files. The resulting value will be a good approximation of the
+// process uptime. This value will be stored at the address pointed by aTime;
+// if an error occurred 0 will be stored instead.
+
+static void*
+ComputeProcessUptimeThread(void* aTime)
+{
+ uint64_t* uptime = static_cast<uint64_t*>(aTime);
+ long hz = sysconf(_SC_CLK_TCK);
+
+ *uptime = 0;
+
+ if (!hz) {
+ return nullptr;
+ }
+
+ char threadStat[40];
+ SprintfLiteral(threadStat, "/proc/self/task/%d/stat", (pid_t)syscall(__NR_gettid));
+
+ uint64_t threadJiffies = JiffiesSinceBoot(threadStat);
+ uint64_t selfJiffies = JiffiesSinceBoot("/proc/self/stat");
+
+ if (!threadJiffies || !selfJiffies) {
+ return nullptr;
+ }
+
+ *uptime = ((threadJiffies - selfJiffies) * kNsPerSec) / hz;
+ return nullptr;
+}
+
+// Computes and returns the process uptime in us on Linux & its derivatives.
+// Returns 0 if an error was encountered.
+
+uint64_t
+TimeStamp::ComputeProcessUptime()
+{
+ uint64_t uptime = 0;
+ pthread_t uptime_pthread;
+
+ if (pthread_create(&uptime_pthread, nullptr, ComputeProcessUptimeThread, &uptime)) {
+ MOZ_CRASH("Failed to create process uptime thread.");
+ return 0;
+ }
+
+ pthread_join(uptime_pthread, NULL);
+
+ return uptime / kNsPerUs;
+}
+
+#elif defined(__DragonFly__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__)
+
+// Computes and returns the process uptime in us on various BSD flavors.
+// Returns 0 if an error was encountered.
+
+uint64_t
+TimeStamp::ComputeProcessUptime()
+{
+ struct timespec ts;
+ int rv = clock_gettime(CLOCK_REALTIME, &ts);
+
+ if (rv == -1) {
+ return 0;
+ }
+
+ int mib[] = {
+ CTL_KERN,
+ KERN_PROC,
+ KERN_PROC_PID,
+ getpid(),
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ sizeof(KINFO_PROC),
+ 1,
+#endif
+ };
+ u_int mibLen = sizeof(mib) / sizeof(mib[0]);
+
+ KINFO_PROC proc;
+ size_t bufferSize = sizeof(proc);
+ rv = sysctl(mib, mibLen, &proc, &bufferSize, nullptr, 0);
+
+ if (rv == -1) {
+ return 0;
+ }
+
+ uint64_t startTime = ((uint64_t)proc.KP_START_SEC * kNsPerSec) +
+ (proc.KP_START_USEC * kNsPerUs);
+ uint64_t now = ((uint64_t)ts.tv_sec * kNsPerSec) + ts.tv_nsec;
+
+ if (startTime > now) {
+ return 0;
+ }
+
+ return (now - startTime) / kNsPerUs;
+}
+
+#else
+
+uint64_t
+TimeStamp::ComputeProcessUptime()
+{
+ return 0;
+}
+
+#endif
+
+} // namespace mozilla
diff --git a/mozglue/misc/TimeStamp_windows.cpp b/mozglue/misc/TimeStamp_windows.cpp
new file mode 100644
index 000000000..cd519affd
--- /dev/null
+++ b/mozglue/misc/TimeStamp_windows.cpp
@@ -0,0 +1,576 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Implement TimeStamp::Now() with QueryPerformanceCounter() controlled with
+// values of GetTickCount().
+
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/TimeStamp.h"
+
+#include <stdio.h>
+#include <intrin.h>
+#include <windows.h>
+
+// To enable logging define to your favorite logging API
+#define LOG(x)
+
+class AutoCriticalSection
+{
+public:
+ AutoCriticalSection(LPCRITICAL_SECTION aSection)
+ : mSection(aSection)
+ {
+ ::EnterCriticalSection(mSection);
+ }
+ ~AutoCriticalSection()
+ {
+ ::LeaveCriticalSection(mSection);
+ }
+private:
+ LPCRITICAL_SECTION mSection;
+};
+
+// Estimate of the smallest duration of time we can measure.
+static volatile ULONGLONG sResolution;
+static volatile ULONGLONG sResolutionSigDigs;
+static const double kNsPerSecd = 1000000000.0;
+static const LONGLONG kNsPerMillisec = 1000000;
+
+// ----------------------------------------------------------------------------
+// Global constants
+// ----------------------------------------------------------------------------
+
+// Tolerance to failures settings.
+//
+// What is the interval we want to have failure free.
+// in [ms]
+static const uint32_t kFailureFreeInterval = 5000;
+// How many failures we are willing to tolerate in the interval.
+static const uint32_t kMaxFailuresPerInterval = 4;
+// What is the threshold to treat fluctuations as actual failures.
+// in [ms]
+static const uint32_t kFailureThreshold = 50;
+
+// If we are not able to get the value of GTC time increment, use this value
+// which is the most usual increment.
+static const DWORD kDefaultTimeIncrement = 156001;
+
+// ----------------------------------------------------------------------------
+// Global variables, not changing at runtime
+// ----------------------------------------------------------------------------
+
+/**
+ * The [mt] unit:
+ *
+ * Many values are kept in ticks of the Performance Coutner x 1000,
+ * further just referred as [mt], meaning milli-ticks.
+ *
+ * This is needed to preserve maximum precision of the performance frequency
+ * representation. GetTickCount values in milliseconds are multiplied with
+ * frequency per second. Therefor we need to multiply QPC value by 1000 to
+ * have the same units to allow simple arithmentic with both QPC and GTC.
+ */
+
+#define ms2mt(x) ((x) * sFrequencyPerSec)
+#define mt2ms(x) ((x) / sFrequencyPerSec)
+#define mt2ms_f(x) (double(x) / sFrequencyPerSec)
+
+// Result of QueryPerformanceFrequency
+static LONGLONG sFrequencyPerSec = 0;
+
+// How much we are tolerant to GTC occasional loose of resoltion.
+// This number says how many multiples of the minimal GTC resolution
+// detected on the system are acceptable. This number is empirical.
+static const LONGLONG kGTCTickLeapTolerance = 4;
+
+// Base tolerance (more: "inability of detection" range) threshold is calculated
+// dynamically, and kept in sGTCResulutionThreshold.
+//
+// Schematically, QPC worked "100%" correctly if ((GTC_now - GTC_epoch) -
+// (QPC_now - QPC_epoch)) was in [-sGTCResulutionThreshold, sGTCResulutionThreshold]
+// interval every time we'd compared two time stamps.
+// If not, then we check the overflow behind this basic threshold
+// is in kFailureThreshold. If not, we condider it as a QPC failure. If too many
+// failures in short time are detected, QPC is considered faulty and disabled.
+//
+// Kept in [mt]
+static LONGLONG sGTCResulutionThreshold;
+
+// If QPC is found faulty for two stamps in this interval, we engage
+// the fault detection algorithm. For duration larger then this limit
+// we bypass using durations calculated from QPC when jitter is detected,
+// but don't touch the sUseQPC flag.
+//
+// Value is in [ms].
+static const uint32_t kHardFailureLimit = 2000;
+// Conversion to [mt]
+static LONGLONG sHardFailureLimit;
+
+// Conversion of kFailureFreeInterval and kFailureThreshold to [mt]
+static LONGLONG sFailureFreeInterval;
+static LONGLONG sFailureThreshold;
+
+// ----------------------------------------------------------------------------
+// Systemm status flags
+// ----------------------------------------------------------------------------
+
+// Flag for stable TSC that indicates platform where QPC is stable.
+static bool sHasStableTSC = false;
+
+// ----------------------------------------------------------------------------
+// Global state variables, changing at runtime
+// ----------------------------------------------------------------------------
+
+// Initially true, set to false when QPC is found unstable and never
+// returns back to true since that time.
+static bool volatile sUseQPC = true;
+
+// ----------------------------------------------------------------------------
+// Global lock
+// ----------------------------------------------------------------------------
+
+// Thread spin count before entering the full wait state for sTimeStampLock.
+// Inspired by Rob Arnold's work on PRMJ_Now().
+static const DWORD kLockSpinCount = 4096;
+
+// Common mutex (thanks the relative complexity of the logic, this is better
+// then using CMPXCHG8B.)
+// It is protecting the globals bellow.
+static CRITICAL_SECTION sTimeStampLock;
+
+// ----------------------------------------------------------------------------
+// Global lock protected variables
+// ----------------------------------------------------------------------------
+
+// Timestamp in future until QPC must behave correctly.
+// Set to now + kFailureFreeInterval on first QPC failure detection.
+// Set to now + E * kFailureFreeInterval on following errors,
+// where E is number of errors detected during last kFailureFreeInterval
+// milliseconds, calculated simply as:
+// E = (sFaultIntoleranceCheckpoint - now) / kFailureFreeInterval + 1.
+// When E > kMaxFailuresPerInterval -> disable QPC.
+//
+// Kept in [mt]
+static ULONGLONG sFaultIntoleranceCheckpoint = 0;
+
+// Used only when GetTickCount64 is not available on the platform.
+// Last result of GetTickCount call.
+//
+// Kept in [ms]
+static DWORD sLastGTCResult = 0;
+
+// Higher part of the 64-bit value of MozGetTickCount64,
+// incremented atomically.
+static DWORD sLastGTCRollover = 0;
+
+namespace mozilla {
+
+typedef ULONGLONG (WINAPI* GetTickCount64_t)();
+static GetTickCount64_t sGetTickCount64 = nullptr;
+
+// Function protecting GetTickCount result from rolling over,
+// result is in [ms]
+static ULONGLONG WINAPI
+MozGetTickCount64()
+{
+ DWORD GTC = ::GetTickCount();
+
+ // Cheaper then CMPXCHG8B
+ AutoCriticalSection lock(&sTimeStampLock);
+
+ // Pull the rollover counter forward only if new value of GTC goes way
+ // down under the last saved result
+ if ((sLastGTCResult > GTC) && ((sLastGTCResult - GTC) > (1UL << 30))) {
+ ++sLastGTCRollover;
+ }
+
+ sLastGTCResult = GTC;
+ return ULONGLONG(sLastGTCRollover) << 32 | sLastGTCResult;
+}
+
+// Result is in [mt]
+static inline ULONGLONG
+PerformanceCounter()
+{
+ LARGE_INTEGER pc;
+ ::QueryPerformanceCounter(&pc);
+ return pc.QuadPart * 1000ULL;
+}
+
+static void
+InitThresholds()
+{
+ DWORD timeAdjustment = 0, timeIncrement = 0;
+ BOOL timeAdjustmentDisabled;
+ GetSystemTimeAdjustment(&timeAdjustment,
+ &timeIncrement,
+ &timeAdjustmentDisabled);
+
+ LOG(("TimeStamp: timeIncrement=%d [100ns]", timeIncrement));
+
+ if (!timeIncrement) {
+ timeIncrement = kDefaultTimeIncrement;
+ }
+
+ // Ceiling to a millisecond
+ // Example values: 156001, 210000
+ DWORD timeIncrementCeil = timeIncrement;
+ // Don't want to round up if already rounded, values will be: 156000, 209999
+ timeIncrementCeil -= 1;
+ // Convert to ms, values will be: 15, 20
+ timeIncrementCeil /= 10000;
+ // Round up, values will be: 16, 21
+ timeIncrementCeil += 1;
+ // Convert back to 100ns, values will be: 160000, 210000
+ timeIncrementCeil *= 10000;
+
+ // How many milli-ticks has the interval rounded up
+ LONGLONG ticksPerGetTickCountResolutionCeiling =
+ (int64_t(timeIncrementCeil) * sFrequencyPerSec) / 10000LL;
+
+ // GTC may jump by 32 (2*16) ms in two steps, therefor use the ceiling value.
+ sGTCResulutionThreshold =
+ LONGLONG(kGTCTickLeapTolerance * ticksPerGetTickCountResolutionCeiling);
+
+ sHardFailureLimit = ms2mt(kHardFailureLimit);
+ sFailureFreeInterval = ms2mt(kFailureFreeInterval);
+ sFailureThreshold = ms2mt(kFailureThreshold);
+}
+
+static void
+InitResolution()
+{
+ // 10 total trials is arbitrary: what we're trying to avoid by
+ // looping is getting unlucky and being interrupted by a context
+ // switch or signal, or being bitten by paging/cache effects
+
+ ULONGLONG minres = ~0ULL;
+ int loops = 10;
+ do {
+ ULONGLONG start = PerformanceCounter();
+ ULONGLONG end = PerformanceCounter();
+
+ ULONGLONG candidate = (end - start);
+ if (candidate < minres) {
+ minres = candidate;
+ }
+ } while (--loops && minres);
+
+ if (0 == minres) {
+ minres = 1;
+ }
+
+ // Converting minres that is in [mt] to nanosecods, multiplicating
+ // the argument to preserve resolution.
+ ULONGLONG result = mt2ms(minres * kNsPerMillisec);
+ if (0 == result) {
+ result = 1;
+ }
+
+ sResolution = result;
+
+ // find the number of significant digits in mResolution, for the
+ // sake of ToSecondsSigDigits()
+ ULONGLONG sigDigs;
+ for (sigDigs = 1;
+ !(sigDigs == result || 10 * sigDigs > result);
+ sigDigs *= 10);
+
+ sResolutionSigDigs = sigDigs;
+}
+
+// ----------------------------------------------------------------------------
+// TimeStampValue implementation
+// ----------------------------------------------------------------------------
+MFBT_API
+TimeStampValue::TimeStampValue(ULONGLONG aGTC, ULONGLONG aQPC, bool aHasQPC)
+ : mGTC(aGTC)
+ , mQPC(aQPC)
+ , mHasQPC(aHasQPC)
+ , mIsNull(false)
+{
+}
+
+MFBT_API TimeStampValue&
+TimeStampValue::operator+=(const int64_t aOther)
+{
+ mGTC += aOther;
+ mQPC += aOther;
+ return *this;
+}
+
+MFBT_API TimeStampValue&
+TimeStampValue::operator-=(const int64_t aOther)
+{
+ mGTC -= aOther;
+ mQPC -= aOther;
+ return *this;
+}
+
+// If the duration is less then two seconds, perform check of QPC stability
+// by comparing both GTC and QPC calculated durations of this and aOther.
+MFBT_API uint64_t
+TimeStampValue::CheckQPC(const TimeStampValue& aOther) const
+{
+ uint64_t deltaGTC = mGTC - aOther.mGTC;
+
+ if (!mHasQPC || !aOther.mHasQPC) { // Both not holding QPC
+ return deltaGTC;
+ }
+
+ uint64_t deltaQPC = mQPC - aOther.mQPC;
+
+ if (sHasStableTSC) { // For stable TSC there is no need to check
+ return deltaQPC;
+ }
+
+ // Check QPC is sane before using it.
+ int64_t diff = DeprecatedAbs(int64_t(deltaQPC) - int64_t(deltaGTC));
+ if (diff <= sGTCResulutionThreshold) {
+ return deltaQPC;
+ }
+
+ // Treat absolutely for calibration purposes
+ int64_t duration = DeprecatedAbs(int64_t(deltaGTC));
+ int64_t overflow = diff - sGTCResulutionThreshold;
+
+ LOG(("TimeStamp: QPC check after %llums with overflow %1.4fms",
+ mt2ms(duration), mt2ms_f(overflow)));
+
+ if (overflow <= sFailureThreshold) { // We are in the limit, let go.
+ return deltaQPC;
+ }
+
+ // QPC deviates, don't use it, since now this method may only return deltaGTC.
+
+ if (!sUseQPC) { // QPC already disabled, no need to run the fault tolerance algorithm.
+ return deltaGTC;
+ }
+
+ LOG(("TimeStamp: QPC jittered over failure threshold"));
+
+ if (duration < sHardFailureLimit) {
+ // Interval between the two time stamps is very short, consider
+ // QPC as unstable and record a failure.
+ uint64_t now = ms2mt(sGetTickCount64());
+
+ AutoCriticalSection lock(&sTimeStampLock);
+
+ if (sFaultIntoleranceCheckpoint && sFaultIntoleranceCheckpoint > now) {
+ // There's already been an error in the last fault intollerant interval.
+ // Time since now to the checkpoint actually holds information on how many
+ // failures there were in the failure free interval we have defined.
+ uint64_t failureCount =
+ (sFaultIntoleranceCheckpoint - now + sFailureFreeInterval - 1) /
+ sFailureFreeInterval;
+ if (failureCount > kMaxFailuresPerInterval) {
+ sUseQPC = false;
+ LOG(("TimeStamp: QPC disabled"));
+ } else {
+ // Move the fault intolerance checkpoint more to the future, prolong it
+ // to reflect the number of detected failures.
+ ++failureCount;
+ sFaultIntoleranceCheckpoint = now + failureCount * sFailureFreeInterval;
+ LOG(("TimeStamp: recording %dth QPC failure", failureCount));
+ }
+ } else {
+ // Setup fault intolerance checkpoint in the future for first detected error.
+ sFaultIntoleranceCheckpoint = now + sFailureFreeInterval;
+ LOG(("TimeStamp: recording 1st QPC failure"));
+ }
+ }
+
+ return deltaGTC;
+}
+
+MFBT_API uint64_t
+TimeStampValue::operator-(const TimeStampValue& aOther) const
+{
+ if (mIsNull && aOther.mIsNull) {
+ return uint64_t(0);
+ }
+
+ return CheckQPC(aOther);
+}
+
+// ----------------------------------------------------------------------------
+// TimeDuration and TimeStamp implementation
+// ----------------------------------------------------------------------------
+
+MFBT_API double
+BaseTimeDurationPlatformUtils::ToSeconds(int64_t aTicks)
+{
+ // Converting before arithmetic avoids blocked store forward
+ return double(aTicks) / (double(sFrequencyPerSec) * 1000.0);
+}
+
+MFBT_API double
+BaseTimeDurationPlatformUtils::ToSecondsSigDigits(int64_t aTicks)
+{
+ // don't report a value < mResolution ...
+ LONGLONG resolution = sResolution;
+ LONGLONG resolutionSigDigs = sResolutionSigDigs;
+ LONGLONG valueSigDigs = resolution * (aTicks / resolution);
+ // and chop off insignificant digits
+ valueSigDigs = resolutionSigDigs * (valueSigDigs / resolutionSigDigs);
+ return double(valueSigDigs) / kNsPerSecd;
+}
+
+MFBT_API int64_t
+BaseTimeDurationPlatformUtils::TicksFromMilliseconds(double aMilliseconds)
+{
+ double result = ms2mt(aMilliseconds);
+ if (result > INT64_MAX) {
+ return INT64_MAX;
+ } else if (result < INT64_MIN) {
+ return INT64_MIN;
+ }
+
+ return result;
+}
+
+MFBT_API int64_t
+BaseTimeDurationPlatformUtils::ResolutionInTicks()
+{
+ return static_cast<int64_t>(sResolution);
+}
+
+static bool
+HasStableTSC()
+{
+ union
+ {
+ int regs[4];
+ struct
+ {
+ int nIds;
+ char cpuString[12];
+ };
+ } cpuInfo;
+
+ __cpuid(cpuInfo.regs, 0);
+ // Only allow Intel CPUs for now
+ // The order of the registers is reg[1], reg[3], reg[2]. We just adjust the
+ // string so that we can compare in one go.
+ if (_strnicmp(cpuInfo.cpuString, "GenuntelineI",
+ sizeof(cpuInfo.cpuString))) {
+ return false;
+ }
+
+ int regs[4];
+
+ // detect if the Advanced Power Management feature is supported
+ __cpuid(regs, 0x80000000);
+ if (regs[0] < 0x80000007) {
+ return false;
+ }
+
+ __cpuid(regs, 0x80000007);
+ // if bit 8 is set than TSC will run at a constant rate
+ // in all ACPI P-state, C-states and T-states
+ return regs[3] & (1 << 8);
+}
+
+static bool gInitialized = false;
+
+MFBT_API void
+TimeStamp::Startup()
+{
+ if (gInitialized) {
+ return;
+ }
+
+ gInitialized = true;
+
+ // Decide which implementation to use for the high-performance timer.
+
+ HMODULE kernelDLL = GetModuleHandleW(L"kernel32.dll");
+ sGetTickCount64 = reinterpret_cast<GetTickCount64_t>(
+ GetProcAddress(kernelDLL, "GetTickCount64"));
+ if (!sGetTickCount64) {
+ // If the platform does not support the GetTickCount64 (Windows XP doesn't),
+ // then use our fallback implementation based on GetTickCount.
+ sGetTickCount64 = MozGetTickCount64;
+ }
+
+ InitializeCriticalSectionAndSpinCount(&sTimeStampLock, kLockSpinCount);
+
+ sHasStableTSC = HasStableTSC();
+ LOG(("TimeStamp: HasStableTSC=%d", sHasStableTSC));
+
+ LARGE_INTEGER freq;
+ sUseQPC = ::QueryPerformanceFrequency(&freq);
+ if (!sUseQPC) {
+ // No Performance Counter. Fall back to use GetTickCount.
+ InitResolution();
+
+ LOG(("TimeStamp: using GetTickCount"));
+ return;
+ }
+
+ sFrequencyPerSec = freq.QuadPart;
+ LOG(("TimeStamp: QPC frequency=%llu", sFrequencyPerSec));
+
+ InitThresholds();
+ InitResolution();
+
+ return;
+}
+
+MFBT_API void
+TimeStamp::Shutdown()
+{
+ DeleteCriticalSection(&sTimeStampLock);
+}
+
+MFBT_API TimeStamp
+TimeStamp::Now(bool aHighResolution)
+{
+ // sUseQPC is volatile
+ bool useQPC = (aHighResolution && sUseQPC);
+
+ // Both values are in [mt] units.
+ ULONGLONG QPC = useQPC ? PerformanceCounter() : uint64_t(0);
+ ULONGLONG GTC = ms2mt(sGetTickCount64());
+ return TimeStamp(TimeStampValue(GTC, QPC, useQPC));
+}
+
+// Computes and returns the process uptime in microseconds.
+// Returns 0 if an error was encountered.
+
+MFBT_API uint64_t
+TimeStamp::ComputeProcessUptime()
+{
+ SYSTEMTIME nowSys;
+ GetSystemTime(&nowSys);
+
+ FILETIME now;
+ bool success = SystemTimeToFileTime(&nowSys, &now);
+
+ if (!success) {
+ return 0;
+ }
+
+ FILETIME start, foo, bar, baz;
+ success = GetProcessTimes(GetCurrentProcess(), &start, &foo, &bar, &baz);
+
+ if (!success) {
+ return 0;
+ }
+
+ ULARGE_INTEGER startUsec = {{
+ start.dwLowDateTime,
+ start.dwHighDateTime
+ }};
+ ULARGE_INTEGER nowUsec = {{
+ now.dwLowDateTime,
+ now.dwHighDateTime
+ }};
+
+ return (nowUsec.QuadPart - startUsec.QuadPart) / 10ULL;
+}
+
+} // namespace mozilla
diff --git a/mozglue/misc/TimeStamp_windows.h b/mozglue/misc/TimeStamp_windows.h
new file mode 100644
index 000000000..c87d34efc
--- /dev/null
+++ b/mozglue/misc/TimeStamp_windows.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_TimeStamp_windows_h
+#define mozilla_TimeStamp_windows_h
+
+#include "mozilla/Types.h"
+
+namespace mozilla {
+
+class TimeStamp;
+
+class TimeStampValue
+{
+ friend struct IPC::ParamTraits<mozilla::TimeStampValue>;
+ friend class TimeStamp;
+ friend void StartupTimelineRecordExternal(int, uint64_t);
+
+ // Both QPC and GTC are kept in [mt] units.
+ uint64_t mGTC;
+ uint64_t mQPC;
+ bool mHasQPC;
+ bool mIsNull;
+
+ MFBT_API TimeStampValue(uint64_t aGTC, uint64_t aQPC, bool aHasQPC);
+
+ MFBT_API uint64_t CheckQPC(const TimeStampValue& aOther) const;
+
+ struct _SomethingVeryRandomHere;
+ constexpr TimeStampValue(_SomethingVeryRandomHere* aNullValue)
+ : mGTC(0)
+ , mQPC(0)
+ , mHasQPC(false)
+ , mIsNull(true)
+ {
+ }
+
+public:
+ MFBT_API uint64_t operator-(const TimeStampValue& aOther) const;
+
+ TimeStampValue operator+(const int64_t aOther) const
+ {
+ return TimeStampValue(mGTC + aOther, mQPC + aOther, mHasQPC);
+ }
+ TimeStampValue operator-(const int64_t aOther) const
+ {
+ return TimeStampValue(mGTC - aOther, mQPC - aOther, mHasQPC);
+ }
+ MFBT_API TimeStampValue& operator+=(const int64_t aOther);
+ MFBT_API TimeStampValue& operator-=(const int64_t aOther);
+
+ bool operator<(const TimeStampValue& aOther) const
+ {
+ return int64_t(*this - aOther) < 0;
+ }
+ bool operator>(const TimeStampValue& aOther) const
+ {
+ return int64_t(*this - aOther) > 0;
+ }
+ bool operator<=(const TimeStampValue& aOther) const
+ {
+ return int64_t(*this - aOther) <= 0;
+ }
+ bool operator>=(const TimeStampValue& aOther) const
+ {
+ return int64_t(*this - aOther) >= 0;
+ }
+ bool operator==(const TimeStampValue& aOther) const
+ {
+ return int64_t(*this - aOther) == 0;
+ }
+ bool operator!=(const TimeStampValue& aOther) const
+ {
+ return int64_t(*this - aOther) != 0;
+ }
+};
+
+}
+
+#endif /* mozilla_TimeStamp_h */
diff --git a/mozglue/misc/moz.build b/mozglue/misc/moz.build
new file mode 100644
index 000000000..9c2ef399c
--- /dev/null
+++ b/mozglue/misc/moz.build
@@ -0,0 +1,37 @@
+FINAL_LIBRARY = 'mozglue'
+
+EXPORTS.mozilla += [
+ 'StackWalk.h',
+ 'TimeStamp.h',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ EXPORTS.mozilla += [
+ 'StackWalk_windows.h',
+ 'TimeStamp_windows.h',
+ ]
+
+SOURCES += [
+ 'StackWalk.cpp',
+ 'TimeStamp.cpp',
+]
+
+OS_LIBS += CONFIG['REALTIME_LIBS']
+
+DEFINES['IMPL_MFBT'] = True
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ SOURCES += [
+ 'TimeStamp_windows.cpp',
+ ]
+ OS_LIBS += ['dbghelp']
+elif CONFIG['HAVE_CLOCK_MONOTONIC']:
+ SOURCES += [
+ 'TimeStamp_posix.cpp',
+ ]
+elif CONFIG['OS_ARCH'] == 'Darwin':
+ SOURCES += [
+ 'TimeStamp_darwin.cpp',
+ ]
+elif CONFIG['COMPILE_ENVIRONMENT']:
+ error('No TimeStamp implementation on this platform. Build will not succeed')