import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,447 @@
/* -*- 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/. */
#include "mozilla/AvailableMemoryTracker.h"
#if defined(XP_WIN)
#include "prinrval.h"
#include "prenv.h"
#include "nsIMemoryReporter.h"
#include "nsMemoryPressure.h"
#endif
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsIRunnable.h"
#include "nsISupports.h"
#include "nsThreadUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#if defined(XP_WIN)
# include "nsWindowsDllInterceptor.h"
# include <windows.h>
#endif
#if defined(MOZ_MEMORY)
# include "mozmemory.h"
#endif // MOZ_MEMORY
using namespace mozilla;
namespace {
#if defined(_M_IX86) && defined(XP_WIN)
uint32_t sLowVirtualMemoryThreshold = 0;
uint32_t sLowCommitSpaceThreshold = 0;
uint32_t sLowPhysicalMemoryThreshold = 0;
uint32_t sLowMemoryNotificationIntervalMS = 0;
Atomic<uint32_t> sNumLowVirtualMemEvents;
Atomic<uint32_t> sNumLowCommitSpaceEvents;
Atomic<uint32_t> sNumLowPhysicalMemEvents;
WindowsDllInterceptor sKernel32Intercept;
WindowsDllInterceptor sGdi32Intercept;
// Has Init() been called?
bool sInitialized = false;
// Has Activate() been called? The hooks don't do anything until this happens.
bool sHooksActive = false;
// Alas, we'd like to use mozilla::TimeStamp, but we can't, because it acquires
// a lock!
volatile bool sHasScheduledOneLowMemoryNotification = false;
volatile PRIntervalTime sLastLowMemoryNotificationTime;
// These are function pointers to the functions we wrap in Init().
void* (WINAPI* sVirtualAllocOrig)(LPVOID aAddress, SIZE_T aSize,
DWORD aAllocationType, DWORD aProtect);
void* (WINAPI* sMapViewOfFileOrig)(HANDLE aFileMappingObject,
DWORD aDesiredAccess, DWORD aFileOffsetHigh,
DWORD aFileOffsetLow, SIZE_T aNumBytesToMap);
HBITMAP(WINAPI* sCreateDIBSectionOrig)(HDC aDC, const BITMAPINFO* aBitmapInfo,
UINT aUsage, VOID** aBits,
HANDLE aSection, DWORD aOffset);
/**
* Fire a memory pressure event if it's been long enough since the last one we
* fired.
*/
bool
MaybeScheduleMemoryPressureEvent()
{
// If this interval rolls over, we may fire an extra memory pressure
// event, but that's not a big deal.
PRIntervalTime interval = PR_IntervalNow() - sLastLowMemoryNotificationTime;
if (sHasScheduledOneLowMemoryNotification &&
PR_IntervalToMilliseconds(interval) < sLowMemoryNotificationIntervalMS) {
return false;
}
// There's a bit of a race condition here, since an interval may be a
// 64-bit number, and 64-bit writes aren't atomic on x86-32. But let's
// not worry about it -- the races only happen when we're already
// experiencing memory pressure and firing notifications, so the worst
// thing that can happen is that we fire two notifications when we
// should have fired only one.
sHasScheduledOneLowMemoryNotification = true;
sLastLowMemoryNotificationTime = PR_IntervalNow();
NS_DispatchEventualMemoryPressure(MemPressure_New);
return true;
}
void
CheckMemAvailable()
{
if (!sHooksActive) {
return;
}
MEMORYSTATUSEX stat;
stat.dwLength = sizeof(stat);
bool success = GlobalMemoryStatusEx(&stat);
if (success) {
// sLowVirtualMemoryThreshold is in MB, but ullAvailVirtual is in bytes.
if (stat.ullAvailVirtual < sLowVirtualMemoryThreshold * 1024 * 1024) {
// If we're running low on virtual memory, unconditionally schedule the
// notification. We'll probably crash if we run out of virtual memory,
// so don't worry about firing this notification too often.
++sNumLowVirtualMemEvents;
NS_DispatchEventualMemoryPressure(MemPressure_New);
} else if (stat.ullAvailPageFile < sLowCommitSpaceThreshold * 1024 * 1024) {
if (MaybeScheduleMemoryPressureEvent()) {
++sNumLowCommitSpaceEvents;
}
} else if (stat.ullAvailPhys < sLowPhysicalMemoryThreshold * 1024 * 1024) {
if (MaybeScheduleMemoryPressureEvent()) {
++sNumLowPhysicalMemEvents;
}
}
}
}
LPVOID WINAPI
VirtualAllocHook(LPVOID aAddress, SIZE_T aSize,
DWORD aAllocationType,
DWORD aProtect)
{
// It's tempting to see whether we have enough free virtual address space for
// this allocation and, if we don't, synchronously fire a low-memory
// notification to free some before we allocate.
//
// Unfortunately that doesn't work, principally because code doesn't expect a
// call to malloc could trigger a GC (or call into the other routines which
// are triggered by a low-memory notification).
//
// I think the best we can do here is try to allocate the memory and check
// afterwards how much free virtual address space we have. If we're running
// low, we schedule a low-memory notification to run as soon as possible.
LPVOID result = sVirtualAllocOrig(aAddress, aSize, aAllocationType, aProtect);
// Don't call CheckMemAvailable for MEM_RESERVE if we're not tracking low
// virtual memory. Similarly, don't call CheckMemAvailable for MEM_COMMIT if
// we're not tracking low physical memory.
if ((sLowVirtualMemoryThreshold != 0 && aAllocationType & MEM_RESERVE) ||
(sLowPhysicalMemoryThreshold != 0 && aAllocationType & MEM_COMMIT)) {
CheckMemAvailable();
}
return result;
}
LPVOID WINAPI
MapViewOfFileHook(HANDLE aFileMappingObject,
DWORD aDesiredAccess,
DWORD aFileOffsetHigh,
DWORD aFileOffsetLow,
SIZE_T aNumBytesToMap)
{
LPVOID result = sMapViewOfFileOrig(aFileMappingObject, aDesiredAccess,
aFileOffsetHigh, aFileOffsetLow,
aNumBytesToMap);
CheckMemAvailable();
return result;
}
HBITMAP WINAPI
CreateDIBSectionHook(HDC aDC,
const BITMAPINFO* aBitmapInfo,
UINT aUsage,
VOID** aBits,
HANDLE aSection,
DWORD aOffset)
{
// There are a lot of calls to CreateDIBSection, so we make some effort not
// to CheckMemAvailable() for calls to CreateDIBSection which allocate only
// a small amount of memory.
// If aSection is non-null, CreateDIBSection won't allocate any new memory.
bool doCheck = false;
if (sHooksActive && !aSection && aBitmapInfo) {
uint16_t bitCount = aBitmapInfo->bmiHeader.biBitCount;
if (bitCount == 0) {
// MSDN says bitCount == 0 means that it figures out how many bits each
// pixel gets by examining the corresponding JPEG or PNG data. We'll just
// assume the worst.
bitCount = 32;
}
// |size| contains the expected allocation size in *bits*. Height may be
// negative (indicating the direction the DIB is drawn in), so we take the
// absolute value.
int64_t size = bitCount * aBitmapInfo->bmiHeader.biWidth *
aBitmapInfo->bmiHeader.biHeight;
if (size < 0) {
size *= -1;
}
// If we're allocating more than 1MB, check how much memory is left after
// the allocation.
if (size > 1024 * 1024 * 8) {
doCheck = true;
}
}
HBITMAP result = sCreateDIBSectionOrig(aDC, aBitmapInfo, aUsage, aBits,
aSection, aOffset);
if (doCheck) {
CheckMemAvailable();
}
return result;
}
static int64_t
LowMemoryEventsVirtualDistinguishedAmount()
{
return sNumLowVirtualMemEvents;
}
static int64_t
LowMemoryEventsPhysicalDistinguishedAmount()
{
return sNumLowPhysicalMemEvents;
}
class LowEventsReporter final : public nsIMemoryReporter
{
~LowEventsReporter() {}
public:
NS_DECL_ISUPPORTS
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) override
{
MOZ_COLLECT_REPORT(
"low-memory-events/virtual", KIND_OTHER, UNITS_COUNT_CUMULATIVE,
LowMemoryEventsVirtualDistinguishedAmount(),
"Number of low-virtual-memory events fired since startup. We fire such an "
"event if we notice there is less than memory.low_virtual_mem_threshold_mb of "
"virtual address space available (if zero, this behavior is disabled). The "
"process will probably crash if it runs out of virtual address space, so "
"this event is dire.");
MOZ_COLLECT_REPORT(
"low-memory-events/commit-space", KIND_OTHER, UNITS_COUNT_CUMULATIVE,
sNumLowCommitSpaceEvents,
"Number of low-commit-space events fired since startup. We fire such an "
"event if we notice there is less than memory.low_commit_space_threshold_mb of "
"commit space available (if zero, this behavior is disabled). Windows will "
"likely kill the process if it runs out of commit space, so this event is "
"dire.");
MOZ_COLLECT_REPORT(
"low-memory-events/physical", KIND_OTHER, UNITS_COUNT_CUMULATIVE,
LowMemoryEventsPhysicalDistinguishedAmount(),
"Number of low-physical-memory events fired since startup. We fire such an "
"event if we notice there is less than memory.low_physical_memory_threshold_mb "
"of physical memory available (if zero, this behavior is disabled). The "
"machine will start to page if it runs out of physical memory. This may "
"cause it to run slowly, but it shouldn't cause it to crash.");
return NS_OK;
}
};
NS_IMPL_ISUPPORTS(LowEventsReporter, nsIMemoryReporter)
#endif // defined(_M_IX86) && defined(XP_WIN)
/**
* This runnable is executed in response to a memory-pressure event; we spin
* the event-loop when receiving the memory-pressure event in the hope that
* other observers will synchronously free some memory that we'll be able to
* purge here.
*/
class nsJemallocFreeDirtyPagesRunnable final : public nsIRunnable
{
~nsJemallocFreeDirtyPagesRunnable() {}
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
};
NS_IMPL_ISUPPORTS(nsJemallocFreeDirtyPagesRunnable, nsIRunnable)
NS_IMETHODIMP
nsJemallocFreeDirtyPagesRunnable::Run()
{
MOZ_ASSERT(NS_IsMainThread());
#if defined(MOZ_MEMORY)
jemalloc_free_dirty_pages();
#endif
return NS_OK;
}
/**
* The memory pressure watcher is used for listening to memory-pressure events
* and reacting upon them. We use one instance per process currently only for
* cleaning up dirty unused pages held by jemalloc.
*/
class nsMemoryPressureWatcher final : public nsIObserver
{
~nsMemoryPressureWatcher() {}
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
void Init();
private:
static bool sFreeDirtyPages;
};
NS_IMPL_ISUPPORTS(nsMemoryPressureWatcher, nsIObserver)
bool nsMemoryPressureWatcher::sFreeDirtyPages = false;
/**
* Initialize and subscribe to the memory-pressure events. We subscribe to the
* observer service in this method and not in the constructor because we need
* to hold a strong reference to 'this' before calling the observer service.
*/
void
nsMemoryPressureWatcher::Init()
{
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
os->AddObserver(this, "memory-pressure", /* ownsWeak */ false);
}
Preferences::AddBoolVarCache(&sFreeDirtyPages, "memory.free_dirty_pages",
false);
}
/**
* Reacts to all types of memory-pressure events, launches a runnable to
* free dirty pages held by jemalloc.
*/
NS_IMETHODIMP
nsMemoryPressureWatcher::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
MOZ_ASSERT(!strcmp(aTopic, "memory-pressure"), "Unknown topic");
if (sFreeDirtyPages) {
nsCOMPtr<nsIRunnable> runnable = new nsJemallocFreeDirtyPagesRunnable();
NS_DispatchToMainThread(runnable);
}
return NS_OK;
}
} // namespace
namespace mozilla {
namespace AvailableMemoryTracker {
void
Activate()
{
#if defined(_M_IX86) && defined(XP_WIN)
MOZ_ASSERT(sInitialized);
MOZ_ASSERT(!sHooksActive);
Preferences::AddUintVarCache(&sLowVirtualMemoryThreshold,
"memory.low_virtual_mem_threshold_mb", 256);
Preferences::AddUintVarCache(&sLowPhysicalMemoryThreshold,
"memory.low_physical_memory_threshold_mb", 0);
Preferences::AddUintVarCache(&sLowCommitSpaceThreshold,
"memory.low_commit_space_threshold_mb", 256);
Preferences::AddUintVarCache(&sLowMemoryNotificationIntervalMS,
"memory.low_memory_notification_interval_ms",
10000);
RegisterStrongMemoryReporter(new LowEventsReporter());
RegisterLowMemoryEventsVirtualDistinguishedAmount(
LowMemoryEventsVirtualDistinguishedAmount);
RegisterLowMemoryEventsPhysicalDistinguishedAmount(
LowMemoryEventsPhysicalDistinguishedAmount);
sHooksActive = true;
#endif
// This object is held alive by the observer service.
RefPtr<nsMemoryPressureWatcher> watcher = new nsMemoryPressureWatcher();
watcher->Init();
}
void
Init()
{
// Do nothing on x86-64, because nsWindowsDllInterceptor is not thread-safe
// on 64-bit. (On 32-bit, it's probably thread-safe.) Even if we run Init()
// before any other of our threads are running, another process may have
// started a remote thread which could call VirtualAlloc!
//
// Moreover, the benefit of this code is less clear when we're a 64-bit
// process, because we aren't going to run out of virtual memory, and the
// system is likely to have a fair bit of physical memory.
#if defined(_M_IX86) && defined(XP_WIN)
// Don't register the hooks if we're a build instrumented for PGO: If we're
// an instrumented build, the compiler adds function calls all over the place
// which may call VirtualAlloc; this makes it hard to prevent
// VirtualAllocHook from reentering itself.
if (!PR_GetEnv("MOZ_PGO_INSTRUMENTED")) {
sKernel32Intercept.Init("Kernel32.dll");
sKernel32Intercept.AddHook("VirtualAlloc",
reinterpret_cast<intptr_t>(VirtualAllocHook),
reinterpret_cast<void**>(&sVirtualAllocOrig));
sKernel32Intercept.AddHook("MapViewOfFile",
reinterpret_cast<intptr_t>(MapViewOfFileHook),
reinterpret_cast<void**>(&sMapViewOfFileOrig));
sGdi32Intercept.Init("Gdi32.dll");
sGdi32Intercept.AddHook("CreateDIBSection",
reinterpret_cast<intptr_t>(CreateDIBSectionHook),
reinterpret_cast<void**>(&sCreateDIBSectionOrig));
}
sInitialized = true;
#endif
}
} // namespace AvailableMemoryTracker
} // namespace mozilla

View file

@ -0,0 +1,30 @@
/* -*- 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_AvailableMemoryTracker_h
#define mozilla_AvailableMemoryTracker_h
namespace mozilla {
namespace AvailableMemoryTracker {
// The AvailableMemoryTracker launches a memory pressure watcher on all
// platforms to react to low-memory situations and on Windows it implements
// the full functionality used to monitor how much memory is available.
//
// Init() must be called before any other threads have started, because it
// modifies the in-memory implementations of some DLL functions in
// non-thread-safe ways.
//
// The hooks don't do anything until Activate() is called. It's an error to
// call Activate() without first calling Init().
void Init();
void Activate();
} // namespace AvailableMemoryTracker
} // namespace mozilla
#endif // ifndef mozilla_AvailableMemoryTracker_h

View file

@ -0,0 +1,45 @@
/* -*- 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/. */
#include "mozilla/ClearOnShutdown.h"
namespace mozilla {
namespace ClearOnShutdown_Internal {
Array<StaticAutoPtr<ShutdownList>,
static_cast<size_t>(ShutdownPhase::ShutdownPhase_Length)> sShutdownObservers;
ShutdownPhase sCurrentShutdownPhase = ShutdownPhase::NotInShutdown;
} // namespace ClearOnShutdown_Internal
// Called when XPCOM is shutting down, after all shutdown notifications have
// been sent and after all threads' event loops have been purged.
void
KillClearOnShutdown(ShutdownPhase aPhase)
{
using namespace ClearOnShutdown_Internal;
MOZ_ASSERT(NS_IsMainThread());
// Shutdown only goes one direction...
MOZ_ASSERT(static_cast<size_t>(sCurrentShutdownPhase) < static_cast<size_t>(aPhase));
// It's impossible to add an entry for a "past" phase; this is blocked in
// ClearOnShutdown, but clear them out anyways in case there are phases
// that weren't passed to KillClearOnShutdown.
for (size_t phase = static_cast<size_t>(ShutdownPhase::First);
phase <= static_cast<size_t>(aPhase);
phase++) {
if (sShutdownObservers[static_cast<size_t>(phase)]) {
while (ShutdownObserver* observer = sShutdownObservers[static_cast<size_t>(phase)]->popFirst()) {
observer->Shutdown();
delete observer;
}
sShutdownObservers[static_cast<size_t>(phase)] = nullptr;
}
}
}
} // namespace mozilla

View file

@ -0,0 +1,124 @@
/* -*- 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_ClearOnShutdown_h
#define mozilla_ClearOnShutdown_h
#include "mozilla/LinkedList.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Array.h"
#include "MainThreadUtils.h"
/*
* This header exports one public method in the mozilla namespace:
*
* template<class SmartPtr>
* void ClearOnShutdown(SmartPtr *aPtr, aPhase=ShutdownPhase::ShutdownFinal)
*
* This function takes a pointer to a smart pointer and nulls the smart pointer
* on shutdown (and a particular phase of shutdown as needed). If a phase
* is specified, the ptr will be cleared at the start of that phase. Also,
* if a phase has already occurred when ClearOnShutdown() is called it will
* cause a MOZ_ASSERT. In case a phase is not explicitly cleared we will
* clear it on the next phase that occurs.
*
* This is useful if you have a global smart pointer object which you don't
* want to "leak" on shutdown.
*
* Although ClearOnShutdown will work with any smart pointer (i.e., nsCOMPtr,
* nsRefPtr, nsAutoPtr, StaticRefPtr, and StaticAutoPtr), you probably want to
* use it only with StaticRefPtr and StaticAutoPtr. There is no way to undo a
* call to ClearOnShutdown, so you can call it only on smart pointers which you
* know will live until the program shuts down. In practice, these are likely
* global variables, which should be Static{Ref,Auto}Ptr.
*
* ClearOnShutdown is currently main-thread only because we don't want to
* accidentally free an object from a different thread than the one it was
* created on.
*/
namespace mozilla {
// Must be contiguous starting at 0
enum class ShutdownPhase {
NotInShutdown = 0,
WillShutdown,
Shutdown,
ShutdownThreads,
ShutdownLoaders,
ShutdownFinal,
ShutdownPhase_Length, // never pass this value
First = WillShutdown, // for iteration
Last = ShutdownFinal
};
namespace ClearOnShutdown_Internal {
class ShutdownObserver : public LinkedListElement<ShutdownObserver>
{
public:
virtual void Shutdown() = 0;
virtual ~ShutdownObserver()
{
}
};
template<class SmartPtr>
class PointerClearer : public ShutdownObserver
{
public:
explicit PointerClearer(SmartPtr* aPtr)
: mPtr(aPtr)
{
}
virtual void Shutdown() override
{
if (mPtr) {
*mPtr = nullptr;
}
}
private:
SmartPtr* mPtr;
};
typedef LinkedList<ShutdownObserver> ShutdownList;
extern Array<StaticAutoPtr<ShutdownList>,
static_cast<size_t>(ShutdownPhase::ShutdownPhase_Length)> sShutdownObservers;
extern ShutdownPhase sCurrentShutdownPhase;
} // namespace ClearOnShutdown_Internal
template<class SmartPtr>
inline void
ClearOnShutdown(SmartPtr* aPtr, ShutdownPhase aPhase = ShutdownPhase::ShutdownFinal)
{
using namespace ClearOnShutdown_Internal;
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPhase != ShutdownPhase::ShutdownPhase_Length);
// Adding a ClearOnShutdown for a "past" phase is an error.
if (!(static_cast<size_t>(sCurrentShutdownPhase) < static_cast<size_t>(aPhase))) {
MOZ_ASSERT(false, "ClearOnShutdown for phase that already was cleared");
*aPtr = nullptr;
return;
}
if (!(sShutdownObservers[static_cast<size_t>(aPhase)])) {
sShutdownObservers[static_cast<size_t>(aPhase)] = new ShutdownList();
}
sShutdownObservers[static_cast<size_t>(aPhase)]->insertBack(new PointerClearer<SmartPtr>(aPtr));
}
// Called when XPCOM is shutting down, after all shutdown notifications have
// been sent and after all threads' event loops have been purged.
void KillClearOnShutdown(ShutdownPhase aPhase);
} // namespace mozilla
#endif

View file

@ -0,0 +1,198 @@
/* -*- 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 CodeAddressService_h__
#define CodeAddressService_h__
#include "mozilla/Assertions.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Types.h"
#include "mozilla/StackWalk.h"
namespace mozilla {
// This class is used to print details about code locations.
//
// |StringTable| must implement an Intern() method that returns an interned
// copy of the string that was passed in, as well as a standard
// SizeOfExcludingThis() method.
//
// |StringAlloc| must implement |copy| and |free|. |copy| copies a string,
// while |free| is used to free strings created by |copy|.
//
// |DescribeCodeAddressLock| is needed when the callers may be holding a lock
// used by MozDescribeCodeAddress. |DescribeCodeAddressLock| must implement
// static methods IsLocked(), Unlock() and Lock().
template <class StringTable,
class StringAlloc,
class DescribeCodeAddressLock>
class CodeAddressService
{
// GetLocation() is the key function in this class. It's basically a wrapper
// around MozDescribeCodeAddress.
//
// However, MozDescribeCodeAddress is very slow on some platforms, and we
// have lots of repeated (i.e. same PC) calls to it. So we do some caching
// of results. Each cached result includes two strings (|mFunction| and
// |mLibrary|), so we also optimize them for space in the following ways.
//
// - The number of distinct library names is small, e.g. a few dozen. There
// is lots of repetition, especially of libxul. So we intern them in their
// own table, which saves space over duplicating them for each cache entry.
//
// - The number of distinct function names is much higher, so we duplicate
// them in each cache entry. That's more space-efficient than interning
// because entries containing single-occurrence function names are quickly
// overwritten, and their copies released. In addition, empty function
// names are common, so we use nullptr to represent them compactly.
StringTable mLibraryStrings;
struct Entry
{
const void* mPc;
char* mFunction; // owned by the Entry; may be null
const char* mLibrary; // owned by mLibraryStrings; never null
// in a non-empty entry is in use
ptrdiff_t mLOffset;
char* mFileName; // owned by the Entry; may be null
uint32_t mLineNo:31;
uint32_t mInUse:1; // is the entry used?
Entry()
: mPc(0), mFunction(nullptr), mLibrary(nullptr), mLOffset(0),
mFileName(nullptr), mLineNo(0), mInUse(0)
{}
~Entry()
{
// We don't free mLibrary because it's externally owned.
StringAlloc::free(mFunction);
StringAlloc::free(mFileName);
}
void Replace(const void* aPc, const char* aFunction,
const char* aLibrary, ptrdiff_t aLOffset,
const char* aFileName, unsigned long aLineNo)
{
mPc = aPc;
// Convert "" to nullptr. Otherwise, make a copy of the name.
StringAlloc::free(mFunction);
mFunction = !aFunction[0] ? nullptr : StringAlloc::copy(aFunction);
StringAlloc::free(mFileName);
mFileName = !aFileName[0] ? nullptr : StringAlloc::copy(aFileName);
mLibrary = aLibrary;
mLOffset = aLOffset;
mLineNo = aLineNo;
mInUse = 1;
}
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
// Don't measure mLibrary because it's externally owned.
size_t n = 0;
n += aMallocSizeOf(mFunction);
n += aMallocSizeOf(mFileName);
return n;
}
};
// A direct-mapped cache. When doing dmd::Analyze() just after starting
// desktop Firefox (which is similar to analyzing after a longer-running
// session, thanks to the limit on how many records we print), a cache with
// 2^24 entries (which approximates an infinite-entry cache) has a ~91% hit
// rate. A cache with 2^12 entries has a ~83% hit rate, and takes up ~85 KiB
// (on 32-bit platforms) or ~150 KiB (on 64-bit platforms).
static const size_t kNumEntries = 1 << 12;
static const size_t kMask = kNumEntries - 1;
Entry mEntries[kNumEntries];
size_t mNumCacheHits;
size_t mNumCacheMisses;
public:
CodeAddressService()
: mEntries(), mNumCacheHits(0), mNumCacheMisses(0)
{
}
void GetLocation(uint32_t aFrameNumber, const void* aPc, char* aBuf,
size_t aBufLen)
{
MOZ_ASSERT(DescribeCodeAddressLock::IsLocked());
uint32_t index = HashGeneric(aPc) & kMask;
MOZ_ASSERT(index < kNumEntries);
Entry& entry = mEntries[index];
if (!entry.mInUse || entry.mPc != aPc) {
mNumCacheMisses++;
// MozDescribeCodeAddress can (on Linux) acquire a lock inside
// the shared library loader. Another thread might call malloc
// while holding that lock (when loading a shared library). So
// we have to exit the lock around this call. For details, see
// https://bugzilla.mozilla.org/show_bug.cgi?id=363334#c3
MozCodeAddressDetails details;
{
DescribeCodeAddressLock::Unlock();
(void)MozDescribeCodeAddress(const_cast<void*>(aPc), &details);
DescribeCodeAddressLock::Lock();
}
const char* library = mLibraryStrings.Intern(details.library);
entry.Replace(aPc, details.function, library, details.loffset,
details.filename, details.lineno);
} else {
mNumCacheHits++;
}
MOZ_ASSERT(entry.mPc == aPc);
MozFormatCodeAddress(aBuf, aBufLen, aFrameNumber, entry.mPc,
entry.mFunction, entry.mLibrary, entry.mLOffset,
entry.mFileName, entry.mLineNo);
}
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
size_t n = aMallocSizeOf(this);
for (uint32_t i = 0; i < kNumEntries; i++) {
n += mEntries[i].SizeOfExcludingThis(aMallocSizeOf);
}
n += mLibraryStrings.SizeOfExcludingThis(aMallocSizeOf);
return n;
}
size_t CacheCapacity() const { return kNumEntries; }
size_t CacheCount() const
{
size_t n = 0;
for (size_t i = 0; i < kNumEntries; i++) {
if (mEntries[i].mInUse) {
n++;
}
}
return n;
}
size_t NumCacheHits() const { return mNumCacheHits; }
size_t NumCacheMisses() const { return mNumCacheMisses; }
};
} // namespace mozilla
#endif // CodeAddressService_h__

View file

@ -0,0 +1,140 @@
/* -*- 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 CountingAllocatorBase_h
#define CountingAllocatorBase_h
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "nsIMemoryReporter.h"
namespace mozilla {
// This CRTP class handles several details of wrapping allocators and should
// be preferred to manually counting with MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC
// and MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE. The typical use is in a memory
// reporter for a particular third party library:
//
// class MyMemoryReporter : public CountingAllocatorBase<MyMemoryReporter>
// {
// ...
// NS_IMETHOD
// CollectReports(nsIHandleReportCallback* aHandleReport,
// nsISupports* aData, bool aAnonymize) override
// {
// MOZ_COLLECT_REPORT(
// "explicit/path/to/somewhere", KIND_HEAP, UNITS_BYTES,
// MemoryAllocated(),
// "A description of what we are reporting.");
//
// return NS_OK;
// }
// };
//
// ...somewhere later in the code...
// SetThirdPartyMemoryFunctions(MyMemoryReporter::CountingAlloc,
// MyMemoryReporter::CountingFree);
template<typename T>
class CountingAllocatorBase
{
public:
CountingAllocatorBase()
{
#ifdef DEBUG
// There must be only one instance of this class, due to |sAmount| being
// static.
static bool hasRun = false;
MOZ_ASSERT(!hasRun);
hasRun = true;
#endif
}
static size_t
MemoryAllocated()
{
return sAmount;
}
static void*
CountingMalloc(size_t size)
{
void* p = malloc(size);
sAmount += MallocSizeOfOnAlloc(p);
return p;
}
static void*
CountingCalloc(size_t nmemb, size_t size)
{
void* p = calloc(nmemb, size);
sAmount += MallocSizeOfOnAlloc(p);
return p;
}
static void*
CountingRealloc(void* p, size_t size)
{
size_t oldsize = MallocSizeOfOnFree(p);
void *pnew = realloc(p, size);
if (pnew) {
size_t newsize = MallocSizeOfOnAlloc(pnew);
sAmount += newsize - oldsize;
} else if (size == 0) {
// We asked for a 0-sized (re)allocation of some existing pointer
// and received NULL in return. 0-sized allocations are permitted
// to either return NULL or to allocate a unique object per call (!).
// For a malloc implementation that chooses the second strategy,
// that allocation may fail (unlikely, but possible).
//
// Given a NULL return value and an allocation size of 0, then, we
// don't know if that means the original pointer was freed or if
// the allocation of the unique object failed. If the original
// pointer was freed, then we have nothing to do here. If the
// allocation of the unique object failed, the original pointer is
// still valid and we ought to undo the decrement from above.
// However, we have no way of knowing how the underlying realloc
// implementation is behaving. Assuming that the original pointer
// was freed is the safest course of action. We do, however, need
// to note that we freed memory.
sAmount -= oldsize;
} else {
// realloc failed. The amount allocated hasn't changed.
}
return pnew;
}
// Some library code expects that realloc(x, 0) will free x, which is not
// the behavior of the version of jemalloc we're using, so this wrapped
// version of realloc is needed.
static void*
CountingFreeingRealloc(void* p, size_t size)
{
if (size == 0) {
CountingFree(p);
return nullptr;
}
return CountingRealloc(p, size);
}
static void
CountingFree(void* p)
{
sAmount -= MallocSizeOfOnFree(p);
free(p);
}
private:
// |sAmount| can be (implicitly) accessed by multiple threads, so it
// must be thread-safe.
static Atomic<size_t> sAmount;
MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)
MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)
};
} // namespace mozilla
#endif // CountingAllocatorBase_h

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,494 @@
/* -*- 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_CycleCollectedJSContext_h__
#define mozilla_CycleCollectedJSContext_h__
#include <queue>
#include "mozilla/DeferredFinalize.h"
#include "mozilla/mozalloc.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/SegmentedVector.h"
#include "jsapi.h"
#include "jsfriendapi.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "nsTArray.h"
#include "nsTHashtable.h"
class nsCycleCollectionNoteRootCallback;
class nsIException;
class nsIRunnable;
class nsThread;
class nsWrapperCache;
namespace js {
struct Class;
} // namespace js
namespace mozilla {
class JSGCThingParticipant: public nsCycleCollectionParticipant
{
public:
NS_IMETHOD_(void) Root(void*) override
{
MOZ_ASSERT(false, "Don't call Root on GC things");
}
NS_IMETHOD_(void) Unlink(void*) override
{
MOZ_ASSERT(false, "Don't call Unlink on GC things, as they may be dead");
}
NS_IMETHOD_(void) Unroot(void*) override
{
MOZ_ASSERT(false, "Don't call Unroot on GC things, as they may be dead");
}
NS_IMETHOD_(void) DeleteCycleCollectable(void* aPtr) override
{
MOZ_ASSERT(false, "Can't directly delete a cycle collectable GC thing");
}
NS_IMETHOD Traverse(void* aPtr, nsCycleCollectionTraversalCallback& aCb)
override;
NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(JSGCThingParticipant)
};
class JSZoneParticipant : public nsCycleCollectionParticipant
{
public:
constexpr JSZoneParticipant(): nsCycleCollectionParticipant()
{
}
NS_IMETHOD_(void) Root(void*) override
{
MOZ_ASSERT(false, "Don't call Root on GC things");
}
NS_IMETHOD_(void) Unlink(void*) override
{
MOZ_ASSERT(false, "Don't call Unlink on GC things, as they may be dead");
}
NS_IMETHOD_(void) Unroot(void*) override
{
MOZ_ASSERT(false, "Don't call Unroot on GC things, as they may be dead");
}
NS_IMETHOD_(void) DeleteCycleCollectable(void*) override
{
MOZ_ASSERT(false, "Can't directly delete a cycle collectable GC thing");
}
NS_IMETHOD Traverse(void* aPtr, nsCycleCollectionTraversalCallback& aCb)
override;
NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(JSZoneParticipant)
};
class IncrementalFinalizeRunnable;
// Contains various stats about the cycle collection.
struct CycleCollectorResults
{
CycleCollectorResults()
{
// Initialize here so when we increment mNumSlices the first time we're
// not using uninitialized memory.
Init();
}
void Init()
{
mForcedGC = false;
mMergedZones = false;
mAnyManual = false;
mVisitedRefCounted = 0;
mVisitedGCed = 0;
mFreedRefCounted = 0;
mFreedGCed = 0;
mFreedJSZones = 0;
mNumSlices = 1;
// mNumSlices is initialized to one, because we call Init() after the
// per-slice increment of mNumSlices has already occurred.
}
bool mForcedGC;
bool mMergedZones;
bool mAnyManual; // true if any slice of the CC was manually triggered, or at shutdown.
uint32_t mVisitedRefCounted;
uint32_t mVisitedGCed;
uint32_t mFreedRefCounted;
uint32_t mFreedGCed;
uint32_t mFreedJSZones;
uint32_t mNumSlices;
};
class CycleCollectedJSContext
{
friend class JSGCThingParticipant;
friend class JSZoneParticipant;
friend class IncrementalFinalizeRunnable;
protected:
CycleCollectedJSContext();
virtual ~CycleCollectedJSContext();
MOZ_IS_CLASS_INIT
nsresult Initialize(JSContext* aParentContext,
uint32_t aMaxBytes,
uint32_t aMaxNurseryBytes);
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
void UnmarkSkippableJSHolders();
virtual void
TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& aCb) {}
virtual void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) {}
virtual void CustomGCCallback(JSGCStatus aStatus) {}
virtual void CustomOutOfMemoryCallback() {}
virtual void CustomLargeAllocationFailureCallback() {}
std::queue<nsCOMPtr<nsIRunnable>> mPromiseMicroTaskQueue;
std::queue<nsCOMPtr<nsIRunnable>> mDebuggerPromiseMicroTaskQueue;
private:
void
DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
nsCycleCollectionTraversalCallback& aCb) const;
virtual bool
DescribeCustomObjects(JSObject* aObject, const js::Class* aClasp,
char (&aName)[72]) const
{
return false; // We did nothing.
}
void
NoteGCThingJSChildren(JS::GCCellPtr aThing,
nsCycleCollectionTraversalCallback& aCb) const;
void
NoteGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
nsCycleCollectionTraversalCallback& aCb) const;
virtual bool
NoteCustomGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
nsCycleCollectionTraversalCallback& aCb) const
{
return false; // We did nothing.
}
enum TraverseSelect {
TRAVERSE_CPP,
TRAVERSE_FULL
};
void
TraverseGCThing(TraverseSelect aTs, JS::GCCellPtr aThing,
nsCycleCollectionTraversalCallback& aCb);
void
TraverseZone(JS::Zone* aZone, nsCycleCollectionTraversalCallback& aCb);
static void
TraverseObjectShim(void* aData, JS::GCCellPtr aThing);
void TraverseNativeRoots(nsCycleCollectionNoteRootCallback& aCb);
static void TraceBlackJS(JSTracer* aTracer, void* aData);
static void TraceGrayJS(JSTracer* aTracer, void* aData);
static void GCCallback(JSContext* aContext, JSGCStatus aStatus, void* aData);
static void GCSliceCallback(JSContext* aContext, JS::GCProgress aProgress,
const JS::GCDescription& aDesc);
static void GCNurseryCollectionCallback(JSContext* aContext,
JS::GCNurseryProgress aProgress,
JS::gcreason::Reason aReason);
static void OutOfMemoryCallback(JSContext* aContext, void* aData);
static void LargeAllocationFailureCallback(void* aData);
static bool ContextCallback(JSContext* aCx, unsigned aOperation,
void* aData);
static JSObject* GetIncumbentGlobalCallback(JSContext* aCx);
static bool EnqueuePromiseJobCallback(JSContext* aCx,
JS::HandleObject aJob,
JS::HandleObject aAllocationSite,
JS::HandleObject aIncumbentGlobal,
void* aData);
#ifdef SPIDERMONKEY_PROMISE
static void PromiseRejectionTrackerCallback(JSContext* aCx,
JS::HandleObject aPromise,
PromiseRejectionHandlingState state,
void* aData);
#endif // SPIDERMONKEY_PROMISE
virtual void TraceNativeBlackRoots(JSTracer* aTracer) { };
void TraceNativeGrayRoots(JSTracer* aTracer);
void AfterProcessMicrotask(uint32_t aRecursionDepth);
public:
void ProcessStableStateQueue();
private:
void ProcessMetastableStateQueue(uint32_t aRecursionDepth);
public:
enum DeferredFinalizeType {
FinalizeIncrementally,
FinalizeNow,
};
void FinalizeDeferredThings(DeferredFinalizeType aType);
// Two conditions, JSOutOfMemory and JSLargeAllocationFailure, are noted in
// crash reports. Here are the values that can appear in the reports:
enum class OOMState : uint32_t {
// The condition has never happened. No entry appears in the crash report.
OK,
// We are currently reporting the given condition.
//
// Suppose a crash report contains "JSLargeAllocationFailure:
// Reporting". This means we crashed while executing memory-pressure
// observers, trying to shake loose some memory. The large allocation in
// question did not return null: it is still on the stack. Had we not
// crashed, it would have been retried.
Reporting,
// The condition has been reported since the last GC.
//
// If a crash report contains "JSOutOfMemory: Reported", that means a small
// allocation failed, and then we crashed, probably due to buggy
// error-handling code that ran after allocation returned null.
//
// This contrasts with "Reporting" which means that no error-handling code
// had executed yet.
Reported,
// The condition has happened, but a GC cycle ended since then.
//
// GC is taken as a proxy for "we've been banging on the heap a good bit
// now and haven't crashed; the OOM was probably handled correctly".
Recovered
};
private:
void AnnotateAndSetOutOfMemory(OOMState* aStatePtr, OOMState aNewState);
void OnGC(JSGCStatus aStatus);
void OnOutOfMemory();
void OnLargeAllocationFailure();
public:
void AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer);
void RemoveJSHolder(void* aHolder);
#ifdef DEBUG
bool IsJSHolder(void* aHolder);
void AssertNoObjectsToTrace(void* aPossibleJSHolder);
#endif
already_AddRefed<nsIException> GetPendingException() const;
void SetPendingException(nsIException* aException);
std::queue<nsCOMPtr<nsIRunnable>>& GetPromiseMicroTaskQueue();
std::queue<nsCOMPtr<nsIRunnable>>& GetDebuggerPromiseMicroTaskQueue();
nsCycleCollectionParticipant* GCThingParticipant();
nsCycleCollectionParticipant* ZoneParticipant();
nsresult TraverseRoots(nsCycleCollectionNoteRootCallback& aCb);
virtual bool UsefulToMergeZones() const;
void FixWeakMappingGrayBits() const;
bool AreGCGrayBitsValid() const;
void GarbageCollect(uint32_t aReason) const;
void NurseryWrapperAdded(nsWrapperCache* aCache);
void NurseryWrapperPreserved(JSObject* aWrapper);
void JSObjectsTenured();
void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
DeferredFinalizeFunction aFunc,
void* aThing);
void DeferredFinalize(nsISupports* aSupports);
void DumpJSHeap(FILE* aFile);
virtual void PrepareForForgetSkippable() = 0;
virtual void BeginCycleCollectionCallback() = 0;
virtual void EndCycleCollectionCallback(CycleCollectorResults& aResults) = 0;
virtual void DispatchDeferredDeletion(bool aContinuation, bool aPurge = false) = 0;
JSContext* Context() const
{
MOZ_ASSERT(mJSContext);
return mJSContext;
}
JS::RootingContext* RootingCx() const
{
MOZ_ASSERT(mJSContext);
return JS::RootingContext::get(mJSContext);
}
bool MicroTaskCheckpointDisabled() const
{
return mDisableMicroTaskCheckpoint;
}
void DisableMicroTaskCheckpoint(bool aDisable)
{
mDisableMicroTaskCheckpoint = aDisable;
}
class MOZ_RAII AutoDisableMicroTaskCheckpoint
{
public:
AutoDisableMicroTaskCheckpoint()
: mCCJSCX(CycleCollectedJSContext::Get())
{
mOldValue = mCCJSCX->MicroTaskCheckpointDisabled();
mCCJSCX->DisableMicroTaskCheckpoint(true);
}
~AutoDisableMicroTaskCheckpoint()
{
mCCJSCX->DisableMicroTaskCheckpoint(mOldValue);
}
CycleCollectedJSContext* mCCJSCX;
bool mOldValue;
};
protected:
JSContext* MaybeContext() const { return mJSContext; }
public:
// nsThread entrypoints
virtual void BeforeProcessTask(bool aMightBlock) { };
virtual void AfterProcessTask(uint32_t aRecursionDepth);
// microtask processor entry point
void AfterProcessMicrotask();
uint32_t RecursionDepth();
// Run in stable state (call through nsContentUtils)
void RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable);
// This isn't in the spec at all yet, but this gets the behavior we want for IDB.
// Runs after the current microtask completes.
void RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable);
// Get the current thread's CycleCollectedJSContext. Returns null if there
// isn't one.
static CycleCollectedJSContext* Get();
// Add aZone to the set of zones waiting for a GC.
void AddZoneWaitingForGC(JS::Zone* aZone)
{
mZonesWaitingForGC.PutEntry(aZone);
}
// Prepare any zones for GC that have been passed to AddZoneWaitingForGC()
// since the last GC or since the last call to PrepareWaitingZonesForGC(),
// whichever was most recent. If there were no such zones, prepare for a
// full GC.
void PrepareWaitingZonesForGC();
// Queue an async microtask to the current main or worker thread.
virtual void DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunnable);
// Storage for watching rejected promises waiting for some client to
// consume their rejection.
#ifdef SPIDERMONKEY_PROMISE
// Promises in this list have been rejected in the last turn of the
// event loop without the rejection being handled.
// Note that this can contain nullptrs in place of promises removed because
// they're consumed before it'd be reported.
JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>> mUncaughtRejections;
// Promises in this list have previously been reported as rejected
// (because they were in the above list), but the rejection was handled
// in the last turn of the event loop.
JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>> mConsumedRejections;
#else
// We store values as `nsISupports` to avoid adding compile-time dependencies
// from xpcom to dom/promise, but they can really only have a single concrete
// type.
nsTArray<nsCOMPtr<nsISupports /* Promise */>> mUncaughtRejections;
nsTArray<nsCOMPtr<nsISupports /* Promise */ >> mConsumedRejections;
#endif // SPIDERMONKEY_PROMISE
nsTArray<nsCOMPtr<nsISupports /* UncaughtRejectionObserver */ >> mUncaughtRejectionObservers;
private:
JSGCThingParticipant mGCThingCycleCollectorGlobal;
JSZoneParticipant mJSZoneCycleCollectorGlobal;
JSContext* mJSContext;
JS::GCSliceCallback mPrevGCSliceCallback;
JS::GCNurseryCollectionCallback mPrevGCNurseryCollectionCallback;
nsDataHashtable<nsPtrHashKey<void>, nsScriptObjectTracer*> mJSHolders;
typedef nsDataHashtable<nsFuncPtrHashKey<DeferredFinalizeFunction>, void*>
DeferredFinalizerTable;
DeferredFinalizerTable mDeferredFinalizerTable;
RefPtr<IncrementalFinalizeRunnable> mFinalizeRunnable;
nsCOMPtr<nsIException> mPendingException;
nsThread* mOwningThread; // Manual refcounting to avoid include hell.
struct RunInMetastableStateData
{
nsCOMPtr<nsIRunnable> mRunnable;
uint32_t mRecursionDepth;
};
nsTArray<nsCOMPtr<nsIRunnable>> mStableStateEvents;
nsTArray<RunInMetastableStateData> mMetastableStateEvents;
uint32_t mBaseRecursionDepth;
bool mDoingStableStates;
bool mDisableMicroTaskCheckpoint;
OOMState mOutOfMemoryState;
OOMState mLargeAllocationFailureState;
static const size_t kSegmentSize = 512;
SegmentedVector<nsWrapperCache*, kSegmentSize, InfallibleAllocPolicy>
mNurseryObjects;
SegmentedVector<JS::PersistentRooted<JSObject*>, kSegmentSize,
InfallibleAllocPolicy>
mPreservedNurseryObjects;
nsTHashtable<nsPtrHashKey<JS::Zone>> mZonesWaitingForGC;
struct EnvironmentPreparer : public js::ScriptEnvironmentPreparer {
void invoke(JS::HandleObject scope, Closure& closure) override;
};
EnvironmentPreparer mEnvironmentPreparer;
};
void TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer);
// Returns true if the JS::TraceKind is one the cycle collector cares about.
inline bool AddToCCKind(JS::TraceKind aKind)
{
return aKind == JS::TraceKind::Object || aKind == JS::TraceKind::Script || aKind == JS::TraceKind::Scope;
}
bool
GetBuildId(JS::BuildIdCharVector* aBuildID);
} // namespace mozilla
#endif // mozilla_CycleCollectedJSContext_h__

23
xpcom/base/Debug.cpp Normal file
View file

@ -0,0 +1,23 @@
/* -*- 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/. */
#include "mozilla/Debug.h"
#ifdef XP_WIN
#include <windows.h>
#endif
#ifdef XP_WIN
void
mozilla::PrintToDebugger(const char* aStr)
{
if (::IsDebuggerPresent()) {
::OutputDebugStringA(aStr);
}
}
#endif

21
xpcom/base/Debug.h Normal file
View file

@ -0,0 +1,21 @@
/* -*- 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_Debug_h__
#define mozilla_Debug_h__
namespace mozilla {
#ifdef XP_WIN
// Print aStr to a debugger if the debugger is attached.
void PrintToDebugger(const char* aStr);
#endif
} // namespace mozilla
#endif // mozilla_Debug_h__

View file

@ -0,0 +1,47 @@
/* -*- 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/. */
#include "mozilla/DebuggerOnGCRunnable.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/Move.h"
#include "js/Debug.h"
namespace mozilla {
/* static */ nsresult
DebuggerOnGCRunnable::Enqueue(JSContext* aCx, const JS::GCDescription& aDesc)
{
auto gcEvent = aDesc.toGCEvent(aCx);
if (!gcEvent) {
return NS_ERROR_OUT_OF_MEMORY;
}
RefPtr<DebuggerOnGCRunnable> runOnGC =
new DebuggerOnGCRunnable(Move(gcEvent));
return NS_DispatchToCurrentThread(runOnGC);
}
NS_IMETHODIMP
DebuggerOnGCRunnable::Run()
{
AutoJSAPI jsapi;
jsapi.Init();
if (!JS::dbg::FireOnGarbageCollectionHook(jsapi.cx(), Move(mGCData))) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
nsresult
DebuggerOnGCRunnable::Cancel()
{
mGCData = nullptr;
return NS_OK;
}
} // namespace mozilla

View file

@ -0,0 +1,35 @@
/* -*- 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_DebuggerOnGCRunnable_h
#define mozilla_DebuggerOnGCRunnable_h
#include "nsThreadUtils.h"
#include "js/GCAPI.h"
#include "mozilla/Move.h"
#include "mozilla/UniquePtr.h"
namespace mozilla {
// Runnable to fire the SpiderMonkey Debugger API's onGarbageCollection hook.
class DebuggerOnGCRunnable : public CancelableRunnable
{
JS::dbg::GarbageCollectionEvent::Ptr mGCData;
explicit DebuggerOnGCRunnable(JS::dbg::GarbageCollectionEvent::Ptr&& aGCData)
: mGCData(Move(aGCData))
{ }
public:
static nsresult Enqueue(JSContext* aCx, const JS::GCDescription& aDesc);
NS_DECL_NSIRUNNABLE
nsresult Cancel() override;
};
} // namespace mozilla
#endif // ifdef mozilla_dom_DebuggerOnGCRunnable_h

View file

@ -0,0 +1,28 @@
/* -*- 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/. */
#include "mozilla/DeferredFinalize.h"
#include "mozilla/Assertions.h"
#include "mozilla/CycleCollectedJSContext.h"
void
mozilla::DeferredFinalize(nsISupports* aSupports)
{
CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
MOZ_ASSERT(cx, "Should have a CycleCollectedJSContext by now");
cx->DeferredFinalize(aSupports);
}
void
mozilla::DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
DeferredFinalizeFunction aFunc,
void* aThing)
{
CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
MOZ_ASSERT(cx, "Should have a CycleCollectedJSContext by now");
cx->DeferredFinalize(aAppendFunc, aFunc, aThing);
}

View file

@ -0,0 +1,33 @@
/* -*- 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_DeferredFinalize_h
#define mozilla_DeferredFinalize_h
class nsISupports;
namespace mozilla {
// Called back from DeferredFinalize. Should add 'thing' to the array of smart
// pointers in 'pointers', creating the array if 'pointers' is null, and return
// the array.
typedef void* (*DeferredFinalizeAppendFunction)(void* aPointers, void* aThing);
// Called to finalize a number of objects. Slice is the number of objects to
// finalize. The return value indicates whether it finalized all objects in the
// buffer. If it returns true, the function will not be called again, so the
// function should free aData.
typedef bool (*DeferredFinalizeFunction)(uint32_t aSlice, void* aData);
void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
DeferredFinalizeFunction aFunc,
void* aThing);
void DeferredFinalize(nsISupports* aSupports);
} // namespace mozilla
#endif // mozilla_DeferredFinalize_h

1026
xpcom/base/ErrorList.h Normal file

File diff suppressed because it is too large Load diff

84
xpcom/base/ErrorNames.cpp Normal file
View file

@ -0,0 +1,84 @@
/* -*- 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/. */
#include "mozilla/ArrayUtils.h"
#include "mozilla/ErrorNames.h"
#include "nsString.h"
#include "prerror.h"
namespace {
struct ErrorEntry
{
nsresult value;
const char * name;
};
#undef ERROR
#define ERROR(key, val) {key, #key}
const ErrorEntry errors[] = {
#include "ErrorList.h"
};
#undef ERROR
} // unnamed namespace
namespace mozilla {
void
GetErrorName(nsresult rv, nsACString& name)
{
for (size_t i = 0; i < ArrayLength(errors); ++i) {
if (errors[i].value == rv) {
name.AssignASCII(errors[i].name);
return;
}
}
bool isSecurityError = NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_SECURITY;
// NS_ERROR_MODULE_SECURITY is the only module that is "allowed" to
// synthesize nsresult error codes that are not listed in ErrorList.h. (The
// NS_ERROR_MODULE_SECURITY error codes are synthesized from NSPR error
// codes.)
MOZ_ASSERT(isSecurityError);
name.AssignASCII(NS_SUCCEEDED(rv) ? "NS_ERROR_GENERATE_SUCCESS("
: "NS_ERROR_GENERATE_FAILURE(");
if (isSecurityError) {
name.AppendASCII("NS_ERROR_MODULE_SECURITY");
} else {
// This should never happen given the assertion above, so we don't bother
// trying to print a symbolic name for the module here.
name.AppendInt(NS_ERROR_GET_MODULE(rv));
}
name.AppendASCII(", ");
const char * nsprName = nullptr;
if (isSecurityError) {
// Invert the logic from NSSErrorsService::GetXPCOMFromNSSError
PRErrorCode nsprCode
= -1 * static_cast<PRErrorCode>(NS_ERROR_GET_CODE(rv));
nsprName = PR_ErrorToName(nsprCode);
// All NSPR error codes defined by NSPR or NSS should have a name mapping.
MOZ_ASSERT(nsprName);
}
if (nsprName) {
name.AppendASCII(nsprName);
} else {
name.AppendInt(NS_ERROR_GET_CODE(rv));
}
name.AppendASCII(")");
}
} // namespace mozilla

25
xpcom/base/ErrorNames.h Normal file
View file

@ -0,0 +1,25 @@
/* -*- 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_ErrorNames_h
#define mozilla_ErrorNames_h
#include "nsError.h"
class nsACString;
namespace mozilla {
// Maps the given nsresult to its symbolic name. For example,
// GetErrorName(NS_OK, name) will result in name == "NS_OK".
// When the symbolic name is unknown, name will be of the form
// "NS_ERROR_GENERATE_SUCCESS(<module>, <code>)" or
// "NS_ERROR_GENERATE_FAILURE(<module>, <code>)".
void GetErrorName(nsresult rv, nsACString& name);
} // namespace mozilla
#endif // mozilla_ErrorNames_h

View file

@ -0,0 +1,68 @@
/* -*- 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/. */
#include "mozilla/HoldDropJSObjects.h"
#include "mozilla/Assertions.h"
#include "mozilla/CycleCollectedJSContext.h"
namespace mozilla {
namespace cyclecollector {
void
HoldJSObjectsImpl(void* aHolder, nsScriptObjectTracer* aTracer)
{
CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
MOZ_ASSERT(cx, "Should have a CycleCollectedJSContext by now");
cx->AddJSHolder(aHolder, aTracer);
}
void
HoldJSObjectsImpl(nsISupports* aHolder)
{
nsXPCOMCycleCollectionParticipant* participant = nullptr;
CallQueryInterface(aHolder, &participant);
MOZ_ASSERT(participant, "Failed to QI to nsXPCOMCycleCollectionParticipant!");
MOZ_ASSERT(participant->CheckForRightISupports(aHolder),
"The result of QIing a JS holder should be the same as ToSupports");
HoldJSObjectsImpl(aHolder, participant);
}
void
DropJSObjectsImpl(void* aHolder)
{
CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
MOZ_ASSERT(cx, "Should have a CycleCollectedJSContext by now");
cx->RemoveJSHolder(aHolder);
}
void
DropJSObjectsImpl(nsISupports* aHolder)
{
#ifdef DEBUG
nsXPCOMCycleCollectionParticipant* participant = nullptr;
CallQueryInterface(aHolder, &participant);
MOZ_ASSERT(participant, "Failed to QI to nsXPCOMCycleCollectionParticipant!");
MOZ_ASSERT(participant->CheckForRightISupports(aHolder),
"The result of QIing a JS holder should be the same as ToSupports");
#endif
DropJSObjectsImpl(static_cast<void*>(aHolder));
}
} // namespace cyclecollector
#ifdef DEBUG
bool
IsJSHolder(void* aHolder)
{
CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
MOZ_ASSERT(cx, "Should have a CycleCollectedJSContext by now");
return cx->IsJSHolder(aHolder);
}
#endif
} // namespace mozilla

View file

@ -0,0 +1,77 @@
/* -*- 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_HoldDropJSObjects_h
#define mozilla_HoldDropJSObjects_h
#include "mozilla/TypeTraits.h"
#include "nsCycleCollectionParticipant.h"
class nsISupports;
class nsScriptObjectTracer;
// Only HoldJSObjects and DropJSObjects should be called directly.
namespace mozilla {
namespace cyclecollector {
void HoldJSObjectsImpl(void* aHolder, nsScriptObjectTracer* aTracer);
void HoldJSObjectsImpl(nsISupports* aHolder);
void DropJSObjectsImpl(void* aHolder);
void DropJSObjectsImpl(nsISupports* aHolder);
} // namespace cyclecollector
template<class T, bool isISupports = IsBaseOf<nsISupports, T>::value>
struct HoldDropJSObjectsHelper
{
static void Hold(T* aHolder)
{
cyclecollector::HoldJSObjectsImpl(aHolder,
NS_CYCLE_COLLECTION_PARTICIPANT(T));
}
static void Drop(T* aHolder)
{
cyclecollector::DropJSObjectsImpl(aHolder);
}
};
template<class T>
struct HoldDropJSObjectsHelper<T, true>
{
static void Hold(T* aHolder)
{
cyclecollector::HoldJSObjectsImpl(ToSupports(aHolder));
}
static void Drop(T* aHolder)
{
cyclecollector::DropJSObjectsImpl(ToSupports(aHolder));
}
};
template<class T>
void
HoldJSObjects(T* aHolder)
{
HoldDropJSObjectsHelper<T>::Hold(aHolder);
}
template<class T>
void
DropJSObjects(T* aHolder)
{
HoldDropJSObjectsHelper<T>::Drop(aHolder);
}
#ifdef DEBUG
bool IsJSHolder(void* aHolder);
#endif
} // namespace mozilla
#endif // mozilla_HoldDropJSObjects_h

View file

@ -0,0 +1,9 @@
/* -*- 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/. */
#include "JSObjectHolder.h"
NS_IMPL_ISUPPORTS(mozilla::JSObjectHolder, nsISupports)

View file

@ -0,0 +1,42 @@
/* -*- 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_JSObjectHolder_h
#define mozilla_JSObjectHolder_h
#include "js/RootingAPI.h"
#include "nsISupportsImpl.h"
namespace mozilla {
// This class is useful when something on one thread needs to keep alive
// a JS Object from another thread. If they are both on the same thread, the
// owning class should instead be made a cycle collected SCRIPT_HOLDER class.
// This object should only be AddRefed and Released on the same thread as
// mJSObject.
//
// Note that this keeps alive the JS object until it goes away, so be sure not to
// create cycles that keep alive the holder.
//
// JSObjectHolder is ISupports to make it usable with NS_ReleaseOnMainThread.
class JSObjectHolder final : public nsISupports
{
public:
JSObjectHolder(JSContext* aCx, JSObject* aObject) : mJSObject(aCx, aObject) {}
NS_DECL_ISUPPORTS
JSObject* GetJSObject() { return mJSObject; }
private:
~JSObjectHolder() {}
JS::PersistentRooted<JSObject*> mJSObject;
};
} // namespace mozilla
#endif // mozilla_JSObjectHolder_h

50
xpcom/base/LinuxUtils.cpp Normal file
View file

@ -0,0 +1,50 @@
/* -*- 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/. */
#include "LinuxUtils.h"
#if defined(XP_LINUX)
#include <ctype.h>
#include <stdio.h>
#include "nsPrintfCString.h"
namespace mozilla {
void
LinuxUtils::GetThreadName(pid_t aTid, nsACString& aName)
{
aName.Truncate();
if (aTid <= 0) {
return;
}
const size_t kBuffSize = 16; // 15 chars max + '\n'
char buf[kBuffSize];
nsPrintfCString path("/proc/%d/comm", aTid);
FILE* fp = fopen(path.get(), "r");
if (!fp) {
// The fopen could also fail if the thread exited before we got here.
return;
}
size_t len = fread(buf, 1, kBuffSize, fp);
fclose(fp);
// No need to strip the '\n', since isspace() includes it.
while (len > 0 &&
(isspace(buf[len - 1]) || isdigit(buf[len - 1]) ||
buf[len - 1] == '#' || buf[len - 1] == '_')) {
--len;
}
aName.Assign(buf, len);
}
}
#endif // XP_LINUX

34
xpcom/base/LinuxUtils.h Normal file
View file

@ -0,0 +1,34 @@
/* -*- 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_LinuxUtils_h
#define mozilla_LinuxUtils_h
#if defined(XP_LINUX)
#include <unistd.h>
#include "nsString.h"
namespace mozilla {
class LinuxUtils
{
public:
// Obtain the name of a thread, omitting any numeric suffix added by a
// thread pool library (as in, e.g., "Binder_2" or "mozStorage #1").
// The empty string is returned on error.
//
// Note: if this is ever needed on kernels older than 2.6.33 (early 2010),
// it will have to parse /proc/<pid>/status instead, because
// /proc/<pid>/comm didn't exist before then.
static void GetThreadName(pid_t aTid, nsACString& aName);
};
}
#endif // XP_LINUX
#endif

View file

@ -0,0 +1,170 @@
/* -*- 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/. */
#include "LogModulePrefWatcher.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h"
#include "nsString.h"
#include "nsXULAppAPI.h"
#include "base/process_util.h"
static const char kLoggingPrefPrefix[] = "logging.";
static const char kLoggingConfigPrefPrefix[] = "logging.config";
static const int kLoggingConfigPrefixLen = sizeof(kLoggingConfigPrefPrefix) - 1;
static const char kLoggingPrefClearOnStartup[] = "logging.config.clear_on_startup";
static const char kLoggingPrefLogFile[] = "logging.config.LOG_FILE";
static const char kLoggingPrefAddTimestamp[] = "logging.config.add_timestamp";
static const char kLoggingPrefSync[] = "logging.config.sync";
namespace mozilla {
NS_IMPL_ISUPPORTS(LogModulePrefWatcher, nsIObserver)
/**
* Resets all the preferences in the logging. branch
* This is needed because we may crash while logging, and this would cause us
* to log after restarting as well.
*
* If logging after restart is desired, set the logging.config.clear_on_startup
* pref to false, or use the MOZ_LOG_FILE and MOZ_LOG_MODULES env vars.
*/
void ResetExistingPrefs()
{
uint32_t count;
char** names;
nsresult rv = Preferences::GetRootBranch()->
GetChildList(kLoggingPrefPrefix, &count, &names);
if (NS_SUCCEEDED(rv) && count) {
for (size_t i = 0; i < count; i++) {
// Clearing the pref will cause it to reload, thus resetting the log level
Preferences::ClearUser(names[i]);
}
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, names);
}
}
/**
* Loads the log level from the given pref and updates the corresponding
* LogModule.
*/
static void
LoadPrefValue(const char* aName)
{
LogLevel logLevel = LogLevel::Disabled;
nsresult rv;
int32_t prefLevel = 0;
nsAutoCString prefValue;
if (strncmp(aName, kLoggingConfigPrefPrefix, kLoggingConfigPrefixLen) == 0) {
nsAutoCString prefName(aName);
if (prefName.EqualsLiteral(kLoggingPrefLogFile)) {
rv = Preferences::GetCString(aName, &prefValue);
// The pref was reset. Clear the user file.
if (NS_FAILED(rv) || prefValue.IsEmpty()) {
LogModule::SetLogFile(nullptr);
return;
}
// If the pref value doesn't have a PID placeholder, append it to the end.
if (!strstr(prefValue.get(), "%PID")) {
prefValue.Append("%PID");
}
LogModule::SetLogFile(prefValue.BeginReading());
} else if (prefName.EqualsLiteral(kLoggingPrefAddTimestamp)) {
bool addTimestamp = Preferences::GetBool(aName, false);
LogModule::SetAddTimestamp(addTimestamp);
} else if (prefName.EqualsLiteral(kLoggingPrefSync)) {
bool sync = Preferences::GetBool(aName, false);
LogModule::SetIsSync(sync);
}
return;
}
if (Preferences::GetInt(aName, &prefLevel) == NS_OK) {
logLevel = ToLogLevel(prefLevel);
} else if (Preferences::GetCString(aName, &prefValue) == NS_OK) {
if (prefValue.LowerCaseEqualsLiteral("error")) {
logLevel = LogLevel::Error;
} else if (prefValue.LowerCaseEqualsLiteral("warning")) {
logLevel = LogLevel::Warning;
} else if (prefValue.LowerCaseEqualsLiteral("info")) {
logLevel = LogLevel::Info;
} else if (prefValue.LowerCaseEqualsLiteral("debug")) {
logLevel = LogLevel::Debug;
} else if (prefValue.LowerCaseEqualsLiteral("verbose")) {
logLevel = LogLevel::Verbose;
}
}
const char* moduleName = aName + strlen(kLoggingPrefPrefix);
LogModule::Get(moduleName)->SetLevel(logLevel);
}
void
LoadExistingPrefs()
{
nsIPrefBranch* root = Preferences::GetRootBranch();
if (!root) {
return;
}
uint32_t count;
char** names;
nsresult rv = root->GetChildList(kLoggingPrefPrefix, &count, &names);
if (NS_SUCCEEDED(rv) && count) {
for (size_t i = 0; i < count; i++) {
LoadPrefValue(names[i]);
}
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, names);
}
}
LogModulePrefWatcher::LogModulePrefWatcher()
{
}
void
LogModulePrefWatcher::RegisterPrefWatcher()
{
RefPtr<LogModulePrefWatcher> prefWatcher = new LogModulePrefWatcher();
Preferences::AddStrongObserver(prefWatcher, kLoggingPrefPrefix);
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService && XRE_IsParentProcess()) {
observerService->AddObserver(prefWatcher, "browser-delayed-startup-finished", false);
}
LoadExistingPrefs();
}
NS_IMETHODIMP
LogModulePrefWatcher::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
if (strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic) == 0) {
NS_LossyConvertUTF16toASCII prefName(aData);
LoadPrefValue(prefName.get());
} else if (strcmp("browser-delayed-startup-finished", aTopic) == 0) {
bool clear = Preferences::GetBool(kLoggingPrefClearOnStartup, true);
if (clear) {
ResetExistingPrefs();
}
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->RemoveObserver(this, "browser-delayed-startup-finished");
}
}
return NS_OK;
}
} // namespace mozilla

View file

@ -0,0 +1,42 @@
/* -*- 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 LogModulePrefWatcher_h
#define LogModulePrefWatcher_h
#include "nsIObserver.h"
namespace mozilla {
/**
* Watches for changes to "logging.*" prefs and then updates the appropriate
* LogModule's log level. Both the integer and string versions of the LogLevel
* enum are supported.
*
* For example setting the pref "logging.Foo" to "Verbose" will set the
* LogModule for "Foo" to the LogLevel::Verbose level. Setting "logging.Bar" to
* 4 would set the LogModule for "Bar" to the LogLevel::Debug level.
*/
class LogModulePrefWatcher : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
/**
* Starts observing logging pref changes.
*/
static void RegisterPrefWatcher();
private:
LogModulePrefWatcher();
virtual ~LogModulePrefWatcher()
{
}
};
} // namespace mozilla
#endif // LogModulePrefWatcher_h

568
xpcom/base/Logging.cpp Normal file
View file

@ -0,0 +1,568 @@
/* -*- 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/. */
#include "mozilla/Logging.h"
#include <algorithm>
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/FileUtils.h"
#include "mozilla/Mutex.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Sprintf.h"
#include "mozilla/Atomics.h"
#include "mozilla/UniquePtrExtensions.h"
#include "nsClassHashtable.h"
#include "nsDebug.h"
#include "NSPRLogModulesParser.h"
#include "prenv.h"
#include "prprf.h"
#ifdef XP_WIN
#include <process.h>
#else
#include <sys/types.h>
#include <unistd.h>
#endif
// NB: Initial amount determined by auditing the codebase for the total amount
// of unique module names and padding up to the next power of 2.
const uint32_t kInitialModuleCount = 256;
// When rotate option is added to the modules list, this is the hardcoded
// number of files we create and rotate. When there is rotate:40,
// we will keep four files per process, each limited to 10MB. Sum is 40MB,
// the given limit.
const uint32_t kRotateFilesNumber = 4;
namespace mozilla {
namespace detail {
void log_print(const PRLogModuleInfo* aModule,
LogLevel aLevel,
const char* aFmt, ...)
{
va_list ap;
va_start(ap, aFmt);
char* buff = PR_vsmprintf(aFmt, ap);
PR_LogPrint("%s", buff);
PR_smprintf_free(buff);
va_end(ap);
}
void log_print(const LogModule* aModule,
LogLevel aLevel,
const char* aFmt, ...)
{
va_list ap;
va_start(ap, aFmt);
aModule->Printv(aLevel, aFmt, ap);
va_end(ap);
}
} // detail
LogLevel
ToLogLevel(int32_t aLevel)
{
aLevel = std::min(aLevel, static_cast<int32_t>(LogLevel::Verbose));
aLevel = std::max(aLevel, static_cast<int32_t>(LogLevel::Disabled));
return static_cast<LogLevel>(aLevel);
}
const char*
ToLogStr(LogLevel aLevel) {
switch (aLevel) {
case LogLevel::Error:
return "E";
case LogLevel::Warning:
return "W";
case LogLevel::Info:
return "I";
case LogLevel::Debug:
return "D";
case LogLevel::Verbose:
return "V";
case LogLevel::Disabled:
default:
MOZ_CRASH("Invalid log level.");
return "";
}
}
namespace detail {
/**
* A helper class providing reference counting for FILE*.
* It encapsulates the following:
* - the FILE handle
* - the order number it was created for when rotating (actual path)
* - number of active references
*/
class LogFile
{
FILE* mFile;
uint32_t mFileNum;
public:
LogFile(FILE* aFile, uint32_t aFileNum)
: mFile(aFile)
, mFileNum(aFileNum)
, mNextToRelease(nullptr)
{
}
~LogFile()
{
fclose(mFile);
delete mNextToRelease;
}
FILE* File() const { return mFile; }
uint32_t Num() const { return mFileNum; }
LogFile* mNextToRelease;
};
const char*
ExpandPIDMarker(const char* aFilename, char (&buffer)[2048])
{
MOZ_ASSERT(aFilename);
static const char kPIDToken[] = "%PID";
const char* pidTokenPtr = strstr(aFilename, kPIDToken);
if (pidTokenPtr &&
SprintfLiteral(buffer, "%.*s%s%d%s",
static_cast<int>(pidTokenPtr - aFilename), aFilename,
XRE_IsParentProcess() ? "-main." : "-child.",
base::GetCurrentProcId(),
pidTokenPtr + strlen(kPIDToken)) > 0)
{
return buffer;
}
return aFilename;
}
} // detail
namespace {
// Helper method that initializes an empty va_list to be empty.
void empty_va(va_list *va, ...)
{
va_start(*va, va);
va_end(*va);
}
}
class LogModuleManager
{
public:
LogModuleManager()
: mModulesLock("logmodules")
, mModules(kInitialModuleCount)
, mPrintEntryCount(0)
, mOutFile(nullptr)
, mToReleaseFile(nullptr)
, mOutFileNum(0)
, mOutFilePath(strdup(""))
, mMainThread(PR_GetCurrentThread())
, mSetFromEnv(false)
, mAddTimestamp(false)
, mIsSync(false)
, mRotate(0)
{
}
~LogModuleManager()
{
detail::LogFile* logFile = mOutFile.exchange(nullptr);
delete logFile;
}
/**
* Loads config from env vars if present.
*/
void Init()
{
bool shouldAppend = false;
bool addTimestamp = false;
bool isSync = false;
int32_t rotate = 0;
const char* modules = PR_GetEnv("MOZ_LOG");
if (!modules || !modules[0]) {
modules = PR_GetEnv("MOZ_LOG_MODULES");
if (modules) {
NS_WARNING("MOZ_LOG_MODULES is deprecated."
"\nPlease use MOZ_LOG instead.");
}
}
if (!modules || !modules[0]) {
modules = PR_GetEnv("NSPR_LOG_MODULES");
if (modules) {
NS_WARNING("NSPR_LOG_MODULES is deprecated."
"\nPlease use MOZ_LOG instead.");
}
}
NSPRLogModulesParser(modules,
[&shouldAppend, &addTimestamp, &isSync, &rotate]
(const char* aName, LogLevel aLevel, int32_t aValue) mutable {
if (strcmp(aName, "append") == 0) {
shouldAppend = true;
} else if (strcmp(aName, "timestamp") == 0) {
addTimestamp = true;
} else if (strcmp(aName, "sync") == 0) {
isSync = true;
} else if (strcmp(aName, "rotate") == 0) {
rotate = (aValue << 20) / kRotateFilesNumber;
} else {
LogModule::Get(aName)->SetLevel(aLevel);
}
});
// Rotate implies timestamp to make the files readable
mAddTimestamp = addTimestamp || rotate > 0;
mIsSync = isSync;
mRotate = rotate;
if (rotate > 0 && shouldAppend) {
NS_WARNING("MOZ_LOG: when you rotate the log, you cannot use append!");
}
const char* logFile = PR_GetEnv("MOZ_LOG_FILE");
if (!logFile || !logFile[0]) {
logFile = PR_GetEnv("NSPR_LOG_FILE");
}
if (logFile && logFile[0]) {
char buf[2048];
logFile = detail::ExpandPIDMarker(logFile, buf);
mOutFilePath.reset(strdup(logFile));
if (mRotate > 0) {
// Delete all the previously captured files, including non-rotated
// log files, so that users don't complain our logs eat space even
// after the rotate option has been added and don't happen to send
// us old large logs along with the rotated files.
remove(mOutFilePath.get());
for (uint32_t i = 0; i < kRotateFilesNumber; ++i) {
RemoveFile(i);
}
}
mOutFile = OpenFile(shouldAppend, mOutFileNum);
mSetFromEnv = true;
}
}
void SetLogFile(const char* aFilename)
{
// For now we don't allow you to change the file at runtime.
if (mSetFromEnv) {
NS_WARNING("LogModuleManager::SetLogFile - Log file was set from the "
"MOZ_LOG_FILE environment variable.");
return;
}
const char * filename = aFilename ? aFilename : "";
char buf[2048];
filename = detail::ExpandPIDMarker(filename, buf);
// Can't use rotate at runtime yet.
MOZ_ASSERT(mRotate == 0, "We don't allow rotate for runtime logfile changes");
mOutFilePath.reset(strdup(filename));
// Exchange mOutFile and set it to be released once all the writes are done.
detail::LogFile* newFile = OpenFile(false, 0);
detail::LogFile* oldFile = mOutFile.exchange(newFile);
// Since we don't allow changing the logfile if MOZ_LOG_FILE is already set,
// and we don't allow log rotation when setting it at runtime, mToReleaseFile
// will be null, so we're not leaking.
DebugOnly<detail::LogFile*> prevFile = mToReleaseFile.exchange(oldFile);
MOZ_ASSERT(!prevFile, "Should be null because rotation is not allowed");
// If we just need to release a file, we must force print, in order to
// trigger the closing and release of mToReleaseFile.
if (oldFile) {
va_list va;
empty_va(&va);
Print("Logger", LogLevel::Info, "Flushing old log files\n", va);
}
}
uint32_t GetLogFile(char *aBuffer, size_t aLength)
{
uint32_t len = strlen(mOutFilePath.get());
if (len + 1 > aLength) {
return 0;
}
snprintf(aBuffer, aLength, "%s", mOutFilePath.get());
return len;
}
void SetIsSync(bool aIsSync)
{
mIsSync = aIsSync;
}
void SetAddTimestamp(bool aAddTimestamp)
{
mAddTimestamp = aAddTimestamp;
}
detail::LogFile* OpenFile(bool aShouldAppend, uint32_t aFileNum)
{
FILE* file;
if (mRotate > 0) {
char buf[2048];
SprintfLiteral(buf, "%s.%d", mOutFilePath.get(), aFileNum);
// rotate doesn't support append.
file = fopen(buf, "w");
} else {
file = fopen(mOutFilePath.get(), aShouldAppend ? "a" : "w");
}
if (!file) {
return nullptr;
}
return new detail::LogFile(file, aFileNum);
}
void RemoveFile(uint32_t aFileNum)
{
char buf[2048];
SprintfLiteral(buf, "%s.%d", mOutFilePath.get(), aFileNum);
remove(buf);
}
LogModule* CreateOrGetModule(const char* aName)
{
OffTheBooksMutexAutoLock guard(mModulesLock);
LogModule* module = nullptr;
if (!mModules.Get(aName, &module)) {
module = new LogModule(aName, LogLevel::Disabled);
mModules.Put(aName, module);
}
return module;
}
void Print(const char* aName, LogLevel aLevel, const char* aFmt, va_list aArgs)
{
const size_t kBuffSize = 1024;
char buff[kBuffSize];
char* buffToWrite = buff;
// For backwards compat we need to use the NSPR format string versions
// of sprintf and friends and then hand off to printf.
va_list argsCopy;
va_copy(argsCopy, aArgs);
size_t charsWritten = PR_vsnprintf(buff, kBuffSize, aFmt, argsCopy);
va_end(argsCopy);
if (charsWritten == kBuffSize - 1) {
// We may have maxed out, allocate a buffer instead.
buffToWrite = PR_vsmprintf(aFmt, aArgs);
charsWritten = strlen(buffToWrite);
}
// Determine if a newline needs to be appended to the message.
const char* newline = "";
if (charsWritten == 0 || buffToWrite[charsWritten - 1] != '\n') {
newline = "\n";
}
FILE* out = stderr;
// In case we use rotate, this ensures the FILE is kept alive during
// its use. Increased before we load mOutFile.
++mPrintEntryCount;
detail::LogFile* outFile = mOutFile;
if (outFile) {
out = outFile->File();
}
// This differs from the NSPR format in that we do not output the
// opaque system specific thread pointer (ie pthread_t) cast
// to a long. The address of the current PR_Thread continues to be
// prefixed.
//
// Additionally we prefix the output with the abbreviated log level
// and the module name.
PRThread *currentThread = PR_GetCurrentThread();
const char *currentThreadName = (mMainThread == currentThread)
? "Main Thread"
: PR_GetThreadName(currentThread);
char noNameThread[40];
if (!currentThreadName) {
SprintfLiteral(noNameThread, "Unnamed thread %p", currentThread);
currentThreadName = noNameThread;
}
if (!mAddTimestamp) {
fprintf_stderr(out,
"[%s]: %s/%s %s%s",
currentThreadName, ToLogStr(aLevel),
aName, buffToWrite, newline);
} else {
PRExplodedTime now;
PR_ExplodeTime(PR_Now(), PR_GMTParameters, &now);
fprintf_stderr(
out,
"%04d-%02d-%02d %02d:%02d:%02d.%06d UTC - [%s]: %s/%s %s%s",
now.tm_year, now.tm_month + 1, now.tm_mday,
now.tm_hour, now.tm_min, now.tm_sec, now.tm_usec,
currentThreadName, ToLogStr(aLevel),
aName, buffToWrite, newline);
}
if (mIsSync) {
fflush(out);
}
if (buffToWrite != buff) {
PR_smprintf_free(buffToWrite);
}
if (mRotate > 0 && outFile) {
int32_t fileSize = ftell(out);
if (fileSize > mRotate) {
uint32_t fileNum = outFile->Num();
uint32_t nextFileNum = fileNum + 1;
if (nextFileNum >= kRotateFilesNumber) {
nextFileNum = 0;
}
// And here is the trick. The current out-file remembers its order
// number. When no other thread shifted the global file number yet,
// we are the thread to open the next file.
if (mOutFileNum.compareExchange(fileNum, nextFileNum)) {
// We can work with mToReleaseFile because we are sure the
// mPrintEntryCount can't drop to zero now - the condition
// to actually delete what's stored in that member.
// And also, no other thread can enter this piece of code
// because mOutFile is still holding the current file with
// the non-shifted number. The compareExchange() above is
// a no-op for other threads.
outFile->mNextToRelease = mToReleaseFile;
mToReleaseFile = outFile;
mOutFile = OpenFile(false, nextFileNum);
}
}
}
if (--mPrintEntryCount == 0 && mToReleaseFile) {
// We were the last Print() entered, if there is a file to release
// do it now. exchange() is atomic and makes sure we release the file
// only once on one thread.
detail::LogFile* release = mToReleaseFile.exchange(nullptr);
delete release;
}
}
private:
OffTheBooksMutex mModulesLock;
nsClassHashtable<nsCharPtrHashKey, LogModule> mModules;
// Print() entry counter, actually reflects concurrent use of the current
// output file. ReleaseAcquire ensures that manipulation with mOutFile
// and mToReleaseFile is synchronized by manipulation with this value.
Atomic<uint32_t, ReleaseAcquire> mPrintEntryCount;
// File to write to. ReleaseAcquire because we need to sync mToReleaseFile
// with this.
Atomic<detail::LogFile*, ReleaseAcquire> mOutFile;
// File to be released when reference counter drops to zero. This member
// is assigned mOutFile when the current file has reached the limit.
// It can be Relaxed, since it's synchronized with mPrintEntryCount
// manipulation and we do atomic exchange() on it.
Atomic<detail::LogFile*, Relaxed> mToReleaseFile;
// The next file number. This is mostly only for synchronization sake.
// Can have relaxed ordering, since we only do compareExchange on it which
// is atomic regardless ordering.
Atomic<uint32_t, Relaxed> mOutFileNum;
// Just keeps the actual file path for further use.
UniqueFreePtr<char[]> mOutFilePath;
PRThread *mMainThread;
bool mSetFromEnv;
Atomic<bool, Relaxed> mAddTimestamp;
Atomic<bool, Relaxed> mIsSync;
int32_t mRotate;
};
StaticAutoPtr<LogModuleManager> sLogModuleManager;
LogModule*
LogModule::Get(const char* aName)
{
// This is just a pass through to the LogModuleManager so
// that the LogModuleManager implementation can be kept internal.
MOZ_ASSERT(sLogModuleManager != nullptr);
return sLogModuleManager->CreateOrGetModule(aName);
}
void
LogModule::SetLogFile(const char* aFilename)
{
MOZ_ASSERT(sLogModuleManager);
sLogModuleManager->SetLogFile(aFilename);
}
uint32_t
LogModule::GetLogFile(char *aBuffer, size_t aLength)
{
MOZ_ASSERT(sLogModuleManager);
return sLogModuleManager->GetLogFile(aBuffer, aLength);
}
void
LogModule::SetAddTimestamp(bool aAddTimestamp)
{
sLogModuleManager->SetAddTimestamp(aAddTimestamp);
}
void
LogModule::SetIsSync(bool aIsSync)
{
sLogModuleManager->SetIsSync(aIsSync);
}
void
LogModule::Init()
{
// NB: This method is not threadsafe; it is expected to be called very early
// in startup prior to any other threads being run.
if (sLogModuleManager) {
// Already initialized.
return;
}
// NB: We intentionally do not register for ClearOnShutdown as that happens
// before all logging is complete. And, yes, that means we leak, but
// we're doing that intentionally.
sLogModuleManager = new LogModuleManager();
sLogModuleManager->Init();
}
void
LogModule::Printv(LogLevel aLevel, const char* aFmt, va_list aArgs) const
{
MOZ_ASSERT(sLogModuleManager != nullptr);
// Forward to LogModule manager w/ level and name
sLogModuleManager->Print(Name(), aLevel, aFmt, aArgs);
}
} // namespace mozilla

255
xpcom/base/Logging.h Normal file
View file

@ -0,0 +1,255 @@
/* -*- 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_logging_h
#define mozilla_logging_h
#include <string.h>
#include <stdarg.h>
#include "prlog.h"
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "mozilla/Likely.h"
#include "mozilla/MacroForEach.h"
// This file is a placeholder for a replacement to the NSPR logging framework
// that is defined in prlog.h. Currently it is just a pass through, but as
// work progresses more functionality will be swapped out in favor of
// mozilla logging implementations.
// We normally have logging enabled everywhere, but measurements showed that
// having logging enabled on Android is quite expensive (hundreds of kilobytes
// for both the format strings for logging and the code to perform all the
// logging calls). Because retrieving logs from a mobile device is
// comparatively more difficult for Android than it is for desktop and because
// desktop machines tend to be less space/bandwidth-constrained than Android
// devices, we've chosen to leave logging enabled on desktop, but disabled on
// Android. Given that logging can still be useful for development purposes,
// however, we leave logging enabled on Android developer builds.
#if !defined(ANDROID) || !defined(RELEASE_OR_BETA)
#define MOZ_LOGGING_ENABLED 1
#else
#define MOZ_LOGGING_ENABLED 0
#endif
namespace mozilla {
// While not a 100% mapping to PR_LOG's numeric values, mozilla::LogLevel does
// maintain a direct mapping for the Disabled, Debug and Verbose levels.
//
// Mappings of LogLevel to PR_LOG's numeric values:
//
// +---------+------------------+-----------------+
// | Numeric | NSPR Logging | Mozilla Logging |
// +---------+------------------+-----------------+
// | 0 | PR_LOG_NONE | Disabled |
// | 1 | PR_LOG_ALWAYS | Error |
// | 2 | PR_LOG_ERROR | Warning |
// | 3 | PR_LOG_WARNING | Info |
// | 4 | PR_LOG_DEBUG | Debug |
// | 5 | PR_LOG_DEBUG + 1 | Verbose |
// +---------+------------------+-----------------+
//
enum class LogLevel {
Disabled = 0,
Error,
Warning,
Info,
Debug,
Verbose,
};
/**
* Safely converts an integer into a valid LogLevel.
*/
LogLevel ToLogLevel(int32_t aLevel);
class LogModule
{
public:
~LogModule() { ::free(mName); }
/**
* Retrieves the module with the given name. If it does not already exist
* it will be created.
*
* @param aName The name of the module.
* @return A log module for the given name. This may be shared.
*/
static LogModule* Get(const char* aName);
static void Init();
/**
* Sets the log file to the given filename.
*/
static void SetLogFile(const char* aFilename);
/**
* @param aBuffer - pointer to a buffer
* @param aLength - the length of the buffer
*
* @return the actual length of the filepath.
*/
static uint32_t GetLogFile(char *aBuffer, size_t aLength);
/**
* @param aAddTimestamp If we should log a time stamp with every message.
*/
static void SetAddTimestamp(bool aAddTimestamp);
/**
* @param aIsSync If we should flush the file after every logged message.
*/
static void SetIsSync(bool aIsSync);
/**
* Indicates whether or not the given log level is enabled.
*/
bool ShouldLog(LogLevel aLevel) const { return mLevel >= aLevel; }
/**
* Retrieves the log module's current level.
*/
LogLevel Level() const { return mLevel; }
/**
* Sets the log module's level.
*/
void SetLevel(LogLevel level) { mLevel = level; }
/**
* Print a log message for this module.
*/
void Printv(LogLevel aLevel, const char* aFmt, va_list aArgs) const;
/**
* Retrieves the module name.
*/
const char* Name() const { return mName; }
private:
friend class LogModuleManager;
explicit LogModule(const char* aName, LogLevel aLevel)
: mName(strdup(aName)), mLevel(aLevel)
{
}
LogModule(LogModule&) = delete;
LogModule& operator=(const LogModule&) = delete;
char* mName;
Atomic<LogLevel, Relaxed> mLevel;
};
/**
* Helper class that lazy loads the given log module. This is safe to use for
* declaring static references to log modules and can be used as a replacement
* for accessing a LogModule directly.
*
* Example usage:
* static LazyLogModule sLayoutLog("layout");
*
* void Foo() {
* MOZ_LOG(sLayoutLog, LogLevel::Verbose, ("Entering foo"));
* }
*/
class LazyLogModule final
{
public:
explicit constexpr LazyLogModule(const char* aLogName)
: mLogName(aLogName)
, mLog(nullptr)
{
}
operator LogModule*()
{
// NB: The use of an atomic makes the reading and assignment of mLog
// thread-safe. There is a small chance that mLog will be set more
// than once, but that's okay as it will be set to the same LogModule
// instance each time. Also note LogModule::Get is thread-safe.
LogModule* tmp = mLog;
if (MOZ_UNLIKELY(!tmp)) {
tmp = LogModule::Get(mLogName);
mLog = tmp;
}
return tmp;
}
private:
const char* const mLogName;
Atomic<LogModule*, ReleaseAcquire> mLog;
};
namespace detail {
inline bool log_test(const PRLogModuleInfo* module, LogLevel level) {
MOZ_ASSERT(level != LogLevel::Disabled);
return module && module->level >= static_cast<int>(level);
}
/**
* A rather inefficient wrapper for PR_LogPrint that always allocates.
* PR_LogModuleInfo is deprecated so it's not worth the effort to do
* any better.
*/
void log_print(const PRLogModuleInfo* aModule,
LogLevel aLevel,
const char* aFmt, ...);
inline bool log_test(const LogModule* module, LogLevel level) {
MOZ_ASSERT(level != LogLevel::Disabled);
return module && module->ShouldLog(level);
}
void log_print(const LogModule* aModule,
LogLevel aLevel,
const char* aFmt, ...);
} // namespace detail
} // namespace mozilla
// Helper macro used convert MOZ_LOG's third parameter, |_args|, from a
// parenthesized form to a varargs form. For example:
// ("%s", "a message") => "%s", "a message"
#define MOZ_LOG_EXPAND_ARGS(...) __VA_ARGS__
#if MOZ_LOGGING_ENABLED
#define MOZ_LOG_TEST(_module,_level) mozilla::detail::log_test(_module, _level)
#else
// Define away MOZ_LOG_TEST here so the compiler will fold away entire
// logging blocks via dead code elimination, e.g.:
//
// if (MOZ_LOG_TEST(...)) {
// ...compute things to log and log them...
// }
//
// This also has the nice property that no special definition of MOZ_LOG is
// required when logging is disabled.
#define MOZ_LOG_TEST(_module,_level) false
#endif
#define MOZ_LOG(_module,_level,_args) \
PR_BEGIN_MACRO \
if (MOZ_LOG_TEST(_module,_level)) { \
mozilla::detail::log_print(_module, _level, MOZ_LOG_EXPAND_ARGS _args); \
} \
PR_END_MACRO
#undef PR_LOG
#undef PR_LOG_TEST
// This #define is a Logging.h-only knob! Don't encourage people to get fancy
// with their log definitions by exporting it outside of Logging.h.
#undef MOZ_LOGGING_ENABLED
#endif // mozilla_logging_h

18
xpcom/base/MacHelpers.h Normal file
View file

@ -0,0 +1,18 @@
/* -*- 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_MacHelpers_h
#define mozilla_MacHelpers_h
#include "nsString.h"
namespace mozilla {
nsresult GetSelectedCityInfo(nsAString& aCountryCode);
} // namespace mozilla
#endif

41
xpcom/base/MacHelpers.mm Normal file
View file

@ -0,0 +1,41 @@
/* -*- 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/. */
#include "nsString.h"
#include "MacHelpers.h"
#include "nsObjCExceptions.h"
#import <Foundation/Foundation.h>
namespace mozilla {
nsresult
GetSelectedCityInfo(nsAString& aCountryCode)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
// Can be replaced with [[NSLocale currentLocale] countryCode] once we build
// with the 10.12 SDK.
id countryCode = [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode];
if (![countryCode isKindOfClass:[NSString class]]) {
return NS_ERROR_FAILURE;
}
const char* countryCodeUTF8 = [(NSString*)countryCode UTF8String];
if (!countryCodeUTF8) {
return NS_ERROR_FAILURE;
}
AppendUTF8toUTF16(countryCodeUTF8, aCountryCode);
return NS_OK;
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}
} // namespace Mozilla

View file

@ -0,0 +1,54 @@
/* -*- 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/. */
#include "NSPRLogModulesParser.h"
#include "mozilla/Tokenizer.h"
const char kDelimiters[] = ", ";
const char kAdditionalWordChars[] = "_-";
namespace mozilla {
void
NSPRLogModulesParser(const char* aLogModules,
function<void(const char*, LogLevel, int32_t)> aCallback)
{
if (!aLogModules) {
return;
}
Tokenizer parser(aLogModules, kDelimiters, kAdditionalWordChars);
nsAutoCString moduleName;
// Format: LOG_MODULES="Foo:2,Bar, Baz:5"
while (parser.ReadWord(moduleName)) {
// Next should be :<level>, default to Error if not provided.
LogLevel logLevel = LogLevel::Error;
int32_t levelValue = 0;
if (parser.CheckChar(':')) {
// Check if a negative value is provided.
int32_t multiplier = 1;
if (parser.CheckChar([](const char aChar) { return aChar == '-'; })) {
multiplier = -1;
}
// NB: If a level isn't provided after the ':' we assume the default
// Error level is desired. This differs from NSPR which will stop
// processing the log module string in this case.
if (parser.ReadInteger(&levelValue)) {
logLevel = ToLogLevel(levelValue * multiplier);
}
}
aCallback(moduleName.get(), logLevel, levelValue);
// Skip ahead to the next token.
parser.SkipWhites();
}
}
} // namespace mozilla

View file

@ -0,0 +1,22 @@
/* -*- 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/. */
#include "mozilla/Logging.h"
#include "mozilla/Function.h"
namespace mozilla {
/**
* Helper function that parses the legacy NSPR_LOG_MODULES env var format
* for specifying log levels and logging options.
*
* @param aLogModules The log modules configuration string.
* @param aCallback The callback to invoke for each log module config entry.
*/
void NSPRLogModulesParser(const char* aLogModules,
function<void(const char*, LogLevel, int32_t)> aCallback);
} // namespace mozilla

198
xpcom/base/OwningNonNull.h Normal file
View file

@ -0,0 +1,198 @@
/* -*- 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/. */
/* A class for non-null strong pointers to reference-counted objects. */
#ifndef mozilla_OwningNonNull_h
#define mozilla_OwningNonNull_h
#include "nsAutoPtr.h"
#include "nsCycleCollectionNoteChild.h"
namespace mozilla {
template<class T>
class OwningNonNull
{
public:
OwningNonNull() {}
MOZ_IMPLICIT OwningNonNull(T& aValue)
{
init(&aValue);
}
template<class U>
MOZ_IMPLICIT OwningNonNull(already_AddRefed<U>&& aValue)
{
init(aValue);
}
template<class U>
MOZ_IMPLICIT OwningNonNull(const OwningNonNull<U>& aValue)
{
init(aValue);
}
// This is no worse than get() in terms of const handling.
operator T&() const
{
MOZ_ASSERT(mInited);
MOZ_ASSERT(mPtr, "OwningNonNull<T> was set to null");
return *mPtr;
}
operator T*() const
{
MOZ_ASSERT(mInited);
MOZ_ASSERT(mPtr, "OwningNonNull<T> was set to null");
return mPtr;
}
// Conversion to bool is always true, so delete to catch errors
explicit operator bool() const = delete;
T*
operator->() const
{
MOZ_ASSERT(mInited);
MOZ_ASSERT(mPtr, "OwningNonNull<T> was set to null");
return mPtr;
}
OwningNonNull<T>&
operator=(T* aValue)
{
init(aValue);
return *this;
}
OwningNonNull<T>&
operator=(T& aValue)
{
init(&aValue);
return *this;
}
template<class U>
OwningNonNull<T>&
operator=(already_AddRefed<U>&& aValue)
{
init(aValue);
return *this;
}
template<class U>
OwningNonNull<T>&
operator=(const OwningNonNull<U>& aValue)
{
init(aValue);
return *this;
}
// Don't allow assigning nullptr, it makes no sense
void operator=(decltype(nullptr)) = delete;
already_AddRefed<T> forget()
{
#ifdef DEBUG
mInited = false;
#endif
return mPtr.forget();
}
template<class U>
void
forget(U** aOther)
{
#ifdef DEBUG
mInited = false;
#endif
mPtr.forget(aOther);
}
// Make us work with smart pointer helpers that expect a get().
T* get() const
{
MOZ_ASSERT(mInited);
MOZ_ASSERT(mPtr);
return mPtr;
}
template<typename U>
void swap(U& aOther)
{
mPtr.swap(aOther);
#ifdef DEBUG
mInited = mPtr;
#endif
}
// We have some consumers who want to check whether we're inited in non-debug
// builds as well. Luckily, we have the invariant that we're inited precisely
// when mPtr is non-null.
bool isInitialized() const
{
MOZ_ASSERT(!!mPtr == mInited, "mInited out of sync with mPtr?");
return mPtr;
}
protected:
template<typename U>
void init(U&& aValue)
{
mPtr = aValue;
MOZ_ASSERT(mPtr);
#ifdef DEBUG
mInited = true;
#endif
}
RefPtr<T> mPtr;
#ifdef DEBUG
bool mInited = false;
#endif
};
template <typename T>
inline void
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
OwningNonNull<T>& aField,
const char* aName,
uint32_t aFlags = 0)
{
CycleCollectionNoteChild(aCallback, aField.get(), aName, aFlags);
}
} // namespace mozilla
// Declared in nsCOMPtr.h
template<class T> template<class U>
nsCOMPtr<T>::nsCOMPtr(const mozilla::OwningNonNull<U>& aOther)
: nsCOMPtr(aOther.get())
{}
template<class T> template<class U>
nsCOMPtr<T>&
nsCOMPtr<T>::operator=(const mozilla::OwningNonNull<U>& aOther)
{
return operator=(aOther.get());
}
// Declared in mozilla/RefPtr.h
template<class T> template<class U>
RefPtr<T>::RefPtr(const mozilla::OwningNonNull<U>& aOther)
: RefPtr(aOther.get())
{}
template<class T> template<class U>
RefPtr<T>&
RefPtr<T>::operator=(const mozilla::OwningNonNull<U>& aOther)
{
return operator=(aOther.get());
}
#endif // mozilla_OwningNonNull_h

96
xpcom/base/StaticMutex.h Normal file
View file

@ -0,0 +1,96 @@
/* -*- 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_StaticMutex_h
#define mozilla_StaticMutex_h
#include "mozilla/Atomics.h"
#include "mozilla/Mutex.h"
namespace mozilla {
/**
* StaticMutex is a Mutex that can (and in fact, must) be used as a
* global/static variable.
*
* The main reason to use StaticMutex as opposed to
* StaticAutoPtr<OffTheBooksMutex> is that we instantiate the StaticMutex in a
* thread-safe manner the first time it's used.
*
* The same caveats that apply to StaticAutoPtr apply to StaticMutex. In
* particular, do not use StaticMutex as a stack variable or a class instance
* variable, because this class relies on the fact that global variablies are
* initialized to 0 in order to initialize mMutex. It is only safe to use
* StaticMutex as a global or static variable.
*/
class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticMutex
{
public:
// In debug builds, check that mMutex is initialized for us as we expect by
// the compiler. In non-debug builds, don't declare a constructor so that
// the compiler can see that the constructor is trivial.
#ifdef DEBUG
StaticMutex()
{
MOZ_ASSERT(!mMutex);
}
#endif
void Lock()
{
Mutex()->Lock();
}
void Unlock()
{
Mutex()->Unlock();
}
void AssertCurrentThreadOwns()
{
#ifdef DEBUG
Mutex()->AssertCurrentThreadOwns();
#endif
}
private:
OffTheBooksMutex* Mutex()
{
if (mMutex) {
return mMutex;
}
OffTheBooksMutex* mutex = new OffTheBooksMutex("StaticMutex");
if (!mMutex.compareExchange(nullptr, mutex)) {
delete mutex;
}
return mMutex;
}
Atomic<OffTheBooksMutex*> mMutex;
// Disallow copy constructor, but only in debug mode. We only define
// a default constructor in debug mode (see above); if we declared
// this constructor always, the compiler wouldn't generate a trivial
// default constructor for us in non-debug mode.
#ifdef DEBUG
StaticMutex(StaticMutex& aOther);
#endif
// Disallow these operators.
StaticMutex& operator=(StaticMutex* aRhs);
static void* operator new(size_t) CPP_THROW_NEW;
static void operator delete(void*);
};
typedef BaseAutoLock<StaticMutex> StaticMutexAutoLock;
typedef BaseAutoUnlock<StaticMutex> StaticMutexAutoUnlock;
} // namespace mozilla
#endif

270
xpcom/base/StaticPtr.h Normal file
View file

@ -0,0 +1,270 @@
/* -*- 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_StaticPtr_h
#define mozilla_StaticPtr_h
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/RefPtr.h"
namespace mozilla {
/**
* StaticAutoPtr and StaticRefPtr are like nsAutoPtr and nsRefPtr, except they
* are suitable for use as global variables.
*
* In particular, a global instance of Static{Auto,Ref}Ptr doesn't cause the
* compiler to emit a static initializer (in release builds, anyway).
*
* In order to accomplish this, Static{Auto,Ref}Ptr must have a trivial
* constructor and destructor. As a consequence, it cannot initialize its raw
* pointer to 0 on construction, and it cannot delete/release its raw pointer
* upon destruction.
*
* Since the compiler guarantees that all global variables are initialized to
* 0, these trivial constructors are safe. Since we rely on this, the clang
* plugin, run as part of our "static analysis" builds, makes it a compile-time
* error to use Static{Auto,Ref}Ptr as anything except a global variable.
*
* Static{Auto,Ref}Ptr have a limited interface as compared to ns{Auto,Ref}Ptr;
* this is intentional, since their range of acceptable uses is smaller.
*/
template<class T>
class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticAutoPtr
{
public:
// In debug builds, check that mRawPtr is initialized for us as we expect
// by the compiler. In non-debug builds, don't declare a constructor
// so that the compiler can see that the constructor is trivial.
#ifdef DEBUG
StaticAutoPtr()
{
MOZ_ASSERT(!mRawPtr);
}
#endif
StaticAutoPtr<T>& operator=(T* aRhs)
{
Assign(aRhs);
return *this;
}
T* get() const { return mRawPtr; }
operator T*() const { return get(); }
T* operator->() const
{
MOZ_ASSERT(mRawPtr);
return get();
}
T& operator*() const { return *get(); }
private:
// Disallow copy constructor, but only in debug mode. We only define
// a default constructor in debug mode (see above); if we declared
// this constructor always, the compiler wouldn't generate a trivial
// default constructor for us in non-debug mode.
#ifdef DEBUG
StaticAutoPtr(StaticAutoPtr<T>& aOther);
#endif
void Assign(T* aNewPtr)
{
MOZ_ASSERT(!aNewPtr || mRawPtr != aNewPtr);
T* oldPtr = mRawPtr;
mRawPtr = aNewPtr;
delete oldPtr;
}
T* mRawPtr;
};
template<class T>
class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticRefPtr
{
public:
// In debug builds, check that mRawPtr is initialized for us as we expect
// by the compiler. In non-debug builds, don't declare a constructor
// so that the compiler can see that the constructor is trivial.
#ifdef DEBUG
StaticRefPtr()
{
MOZ_ASSERT(!mRawPtr);
}
#endif
StaticRefPtr<T>& operator=(T* aRhs)
{
AssignWithAddref(aRhs);
return *this;
}
StaticRefPtr<T>& operator=(const StaticRefPtr<T>& aRhs)
{
return (this = aRhs.mRawPtr);
}
StaticRefPtr<T>& operator=(already_AddRefed<T>& aRhs)
{
AssignAssumingAddRef(aRhs.take());
return *this;
}
StaticRefPtr<T>& operator=(already_AddRefed<T>&& aRhs)
{
AssignAssumingAddRef(aRhs.take());
return *this;
}
already_AddRefed<T>
forget()
{
T* temp = mRawPtr;
mRawPtr = nullptr;
return already_AddRefed<T>(temp);
}
T* get() const { return mRawPtr; }
operator T*() const { return get(); }
T* operator->() const
{
MOZ_ASSERT(mRawPtr);
return get();
}
T& operator*() const { return *get(); }
private:
void AssignWithAddref(T* aNewPtr)
{
if (aNewPtr) {
aNewPtr->AddRef();
}
AssignAssumingAddRef(aNewPtr);
}
void AssignAssumingAddRef(T* aNewPtr)
{
T* oldPtr = mRawPtr;
mRawPtr = aNewPtr;
if (oldPtr) {
oldPtr->Release();
}
}
T* MOZ_OWNING_REF mRawPtr;
};
namespace StaticPtr_internal {
class Zero;
} // namespace StaticPtr_internal
#define REFLEXIVE_EQUALITY_OPERATORS(type1, type2, eq_fn, ...) \
template<__VA_ARGS__> \
inline bool \
operator==(type1 lhs, type2 rhs) \
{ \
return eq_fn; \
} \
\
template<__VA_ARGS__> \
inline bool \
operator==(type2 lhs, type1 rhs) \
{ \
return rhs == lhs; \
} \
\
template<__VA_ARGS__> \
inline bool \
operator!=(type1 lhs, type2 rhs) \
{ \
return !(lhs == rhs); \
} \
\
template<__VA_ARGS__> \
inline bool \
operator!=(type2 lhs, type1 rhs) \
{ \
return !(lhs == rhs); \
}
// StaticAutoPtr (in)equality operators
template<class T, class U>
inline bool
operator==(const StaticAutoPtr<T>& aLhs, const StaticAutoPtr<U>& aRhs)
{
return aLhs.get() == aRhs.get();
}
template<class T, class U>
inline bool
operator!=(const StaticAutoPtr<T>& aLhs, const StaticAutoPtr<U>& aRhs)
{
return !(aLhs == aRhs);
}
REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, const U*,
lhs.get() == rhs, class T, class U)
REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, U*,
lhs.get() == rhs, class T, class U)
// Let us compare StaticAutoPtr to 0.
REFLEXIVE_EQUALITY_OPERATORS(const StaticAutoPtr<T>&, StaticPtr_internal::Zero*,
lhs.get() == nullptr, class T)
// StaticRefPtr (in)equality operators
template<class T, class U>
inline bool
operator==(const StaticRefPtr<T>& aLhs, const StaticRefPtr<U>& aRhs)
{
return aLhs.get() == aRhs.get();
}
template<class T, class U>
inline bool
operator!=(const StaticRefPtr<T>& aLhs, const StaticRefPtr<U>& aRhs)
{
return !(aLhs == aRhs);
}
REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, const U*,
lhs.get() == rhs, class T, class U)
REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, U*,
lhs.get() == rhs, class T, class U)
// Let us compare StaticRefPtr to 0.
REFLEXIVE_EQUALITY_OPERATORS(const StaticRefPtr<T>&, StaticPtr_internal::Zero*,
lhs.get() == nullptr, class T)
#undef REFLEXIVE_EQUALITY_OPERATORS
} // namespace mozilla
// Declared in mozilla/RefPtr.h
template<class T> template<class U>
RefPtr<T>::RefPtr(const mozilla::StaticRefPtr<U>& aOther)
: RefPtr(aOther.get())
{}
template<class T> template<class U>
RefPtr<T>&
RefPtr<T>::operator=(const mozilla::StaticRefPtr<U>& aOther)
{
return operator=(aOther.get());
}
#endif

View file

@ -0,0 +1,989 @@
/* -*- 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/. */
#include "mozilla/SystemMemoryReporter.h"
#include "mozilla/Attributes.h"
#include "mozilla/LinuxUtils.h"
#include "mozilla/PodOperations.h"
#include "mozilla/Preferences.h"
#include "mozilla/TaggedAnonymousMemory.h"
#include "mozilla/Unused.h"
#include "nsDataHashtable.h"
#include "nsIMemoryReporter.h"
#include "nsPrintfCString.h"
#include "nsString.h"
#include "nsTHashtable.h"
#include "nsHashKeys.h"
#include <dirent.h>
#include <inttypes.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
// This file implements a Linux-specific, system-wide memory reporter. It
// gathers all the useful memory measurements obtainable from the OS in a
// single place, giving a high-level view of memory consumption for the entire
// machine/device.
//
// Other memory reporters measure part of a single process's memory consumption.
// This reporter is different in that it measures memory consumption of many
// processes, and they end up in a single reports tree. This is a slight abuse
// of the memory reporting infrastructure, and therefore the results are given
// their own "process" called "System", which means they show up in about:memory
// in their own section, distinct from the per-process sections.
namespace mozilla {
namespace SystemMemoryReporter {
#if !defined(XP_LINUX)
#error "This won't work if we're not on Linux."
#endif
/**
* RAII helper that will close an open DIR handle.
*/
struct MOZ_STACK_CLASS AutoDir
{
explicit AutoDir(DIR* aDir) : mDir(aDir) {}
~AutoDir() { if (mDir) closedir(mDir); };
DIR* mDir;
};
/**
* RAII helper that will close an open FILE handle.
*/
struct MOZ_STACK_CLASS AutoFile
{
explicit AutoFile(FILE* aFile) : mFile(aFile) {}
~AutoFile() { if (mFile) fclose(mFile); }
FILE* mFile;
};
static bool
EndsWithLiteral(const nsCString& aHaystack, const char* aNeedle)
{
int32_t idx = aHaystack.RFind(aNeedle);
return idx != -1 && idx + strlen(aNeedle) == aHaystack.Length();
}
static void
GetDirname(const nsCString& aPath, nsACString& aOut)
{
int32_t idx = aPath.RFind("/");
if (idx == -1) {
aOut.Truncate();
} else {
aOut.Assign(Substring(aPath, 0, idx));
}
}
static void
GetBasename(const nsCString& aPath, nsACString& aOut)
{
nsCString out;
int32_t idx = aPath.RFind("/");
if (idx == -1) {
out.Assign(aPath);
} else {
out.Assign(Substring(aPath, idx + 1));
}
// On Android, some entries in /dev/ashmem end with "(deleted)" (e.g.
// "/dev/ashmem/libxul.so(deleted)"). We don't care about this modifier, so
// cut it off when getting the entry's basename.
if (EndsWithLiteral(out, "(deleted)")) {
out.Assign(Substring(out, 0, out.RFind("(deleted)")));
}
out.StripChars(" ");
aOut.Assign(out);
}
static bool
IsNumeric(const char* aStr)
{
MOZ_ASSERT(*aStr); // shouldn't see empty strings
while (*aStr) {
if (!isdigit(*aStr)) {
return false;
}
++aStr;
}
return true;
}
static bool
IsAnonymous(const nsACString& aName)
{
// Recent kernels have multiple [stack:nnnn] entries, where |nnnn| is a
// thread ID. However, the entire virtual memory area containing a thread's
// stack pointer is considered the stack for that thread, even if it was
// merged with an adjacent area containing non-stack data. So we treat them
// as regular anonymous memory. However, see below about tagged anonymous
// memory.
return aName.IsEmpty() ||
StringBeginsWith(aName, NS_LITERAL_CSTRING("[stack:"));
}
class SystemReporter final : public nsIMemoryReporter
{
~SystemReporter() {}
public:
NS_DECL_THREADSAFE_ISUPPORTS
#define REPORT(_path, _units, _amount, _desc) \
do { \
size_t __amount = _amount; /* evaluate _amount only once */ \
if (__amount > 0) { \
aHandleReport->Callback(NS_LITERAL_CSTRING("System"), _path, \
KIND_OTHER, _units, __amount, _desc, aData); \
} \
} while (0)
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) override
{
// There is lots of privacy-sensitive data in /proc. Just skip this
// reporter entirely when anonymization is required.
if (aAnonymize) {
return NS_OK;
}
if (!Preferences::GetBool("memory.system_memory_reporter")) {
return NS_OK;
}
// Read relevant fields from /proc/meminfo.
int64_t memTotal = 0, memFree = 0;
nsresult rv1 = ReadMemInfo(&memTotal, &memFree);
// Collect per-process reports from /proc/<pid>/smaps.
int64_t totalPss = 0;
nsresult rv2 = CollectProcessReports(aHandleReport, aData, &totalPss);
// Report the non-process numbers.
if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
int64_t other = memTotal - memFree - totalPss;
REPORT(NS_LITERAL_CSTRING("mem/other"), UNITS_BYTES, other,
NS_LITERAL_CSTRING(
"Memory which is neither owned by any user-space process nor free. Note that "
"this includes memory holding cached files from the disk which can be "
"reclaimed by the OS at any time."));
REPORT(NS_LITERAL_CSTRING("mem/free"), UNITS_BYTES, memFree,
NS_LITERAL_CSTRING(
"Memory which is free and not being used for any purpose."));
}
// Report reserved memory not included in memTotal.
CollectPmemReports(aHandleReport, aData);
// Report zram usage statistics.
CollectZramReports(aHandleReport, aData);
// Report kgsl graphics memory usage.
CollectKgslReports(aHandleReport, aData);
// Report ION memory usage.
CollectIonReports(aHandleReport, aData);
return NS_OK;
}
private:
// These are the cross-cutting measurements across all processes.
class ProcessSizes
{
public:
void Add(const nsACString& aKey, size_t aSize)
{
mTagged.Put(aKey, mTagged.Get(aKey) + aSize);
}
void Report(nsIHandleReportCallback* aHandleReport, nsISupports* aData)
{
for (auto iter = mTagged.Iter(); !iter.Done(); iter.Next()) {
nsCStringHashKey::KeyType key = iter.Key();
size_t amount = iter.UserData();
nsAutoCString path("processes/");
path.Append(key);
nsAutoCString desc("This is the sum of all processes' '");
desc.Append(key);
desc.AppendLiteral("' numbers.");
REPORT(path, UNITS_BYTES, amount, desc);
}
}
private:
nsDataHashtable<nsCStringHashKey, size_t> mTagged;
};
nsresult ReadMemInfo(int64_t* aMemTotal, int64_t* aMemFree)
{
FILE* f = fopen("/proc/meminfo", "r");
if (!f) {
return NS_ERROR_FAILURE;
}
int n1 = fscanf(f, "MemTotal: %" SCNd64 " kB\n", aMemTotal);
int n2 = fscanf(f, "MemFree: %" SCNd64 " kB\n", aMemFree);
fclose(f);
if (n1 != 1 || n2 != 1) {
return NS_ERROR_FAILURE;
}
// Convert from KB to B.
*aMemTotal *= 1024;
*aMemFree *= 1024;
return NS_OK;
}
nsresult CollectProcessReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData,
int64_t* aTotalPss)
{
*aTotalPss = 0;
ProcessSizes processSizes;
DIR* d = opendir("/proc");
if (NS_WARN_IF(!d)) {
return NS_ERROR_FAILURE;
}
struct dirent* ent;
while ((ent = readdir(d))) {
struct stat statbuf;
const char* pidStr = ent->d_name;
// Don't check the return value of stat() -- it can return -1 for these
// directories even when it has succeeded, apparently.
stat(pidStr, &statbuf);
if (S_ISDIR(statbuf.st_mode) && IsNumeric(pidStr)) {
nsCString processName("process(");
// Get the command name from cmdline. If that fails, the pid is still
// shown.
nsPrintfCString cmdlinePath("/proc/%s/cmdline", pidStr);
FILE* f = fopen(cmdlinePath.get(), "r");
if (f) {
static const size_t len = 256;
char buf[len];
if (fgets(buf, len, f)) {
processName.Append(buf);
// A hack: replace forward slashes with '\\' so they aren't treated
// as path separators. Consumers of this reporter (such as
// about:memory) have to undo this change.
processName.ReplaceChar('/', '\\');
processName.AppendLiteral(", ");
}
fclose(f);
}
processName.AppendLiteral("pid=");
processName.Append(pidStr);
processName.Append(')');
// Read the PSS values from the smaps file.
nsPrintfCString smapsPath("/proc/%s/smaps", pidStr);
f = fopen(smapsPath.get(), "r");
if (!f) {
// Processes can terminate between the readdir() call above and now,
// so just skip if we can't open the file.
continue;
}
ParseMappings(f, processName, aHandleReport, aData, &processSizes,
aTotalPss);
fclose(f);
// Report the open file descriptors for this process.
nsPrintfCString procFdPath("/proc/%s/fd", pidStr);
CollectOpenFileReports(aHandleReport, aData, procFdPath, processName);
}
}
closedir(d);
// Report the "processes/" tree.
processSizes.Report(aHandleReport, aData);
return NS_OK;
}
void ParseMappings(FILE* aFile,
const nsACString& aProcessName,
nsIHandleReportCallback* aHandleReport,
nsISupports* aData,
ProcessSizes* aProcessSizes,
int64_t* aTotalPss)
{
// The first line of an entry in /proc/<pid>/smaps looks just like an entry
// in /proc/<pid>/maps:
//
// address perms offset dev inode pathname
// 02366000-025d8000 rw-p 00000000 00:00 0 [heap]
//
// Each of the following lines contains a key and a value, separated
// by ": ", where the key does not contain either of those characters.
// Assuming more than this about the structure of those lines has
// failed to be future-proof in the past, so we avoid doing so.
//
// This makes it difficult to detect the start of a new entry
// until it's been removed from the stdio buffer, so we just loop
// over all lines in the file in this routine.
const int argCount = 8;
unsigned long long addrStart, addrEnd;
char perms[5];
unsigned long long offset;
// The 2.6 and 3.0 kernels allocate 12 bits for the major device number and
// 20 bits for the minor device number. Future kernels might allocate more.
// 64 bits ought to be enough for anybody.
char devMajor[17];
char devMinor[17];
unsigned int inode;
char line[1025];
// This variable holds the path of the current entry, or is void
// if we're scanning for the start of a new entry.
nsAutoCString currentPath;
int pathOffset;
currentPath.SetIsVoid(true);
while (fgets(line, sizeof(line), aFile)) {
if (currentPath.IsVoid()) {
int n = sscanf(line,
"%llx-%llx %4s %llx "
"%16[0-9a-fA-F]:%16[0-9a-fA-F] %u %n",
&addrStart, &addrEnd, perms, &offset, devMajor,
devMinor, &inode, &pathOffset);
if (n >= argCount - 1) {
currentPath.Assign(line + pathOffset);
currentPath.StripChars("\n");
}
continue;
}
// Now that we have a name and other metadata, scan for the PSS.
size_t pss_kb;
int n = sscanf(line, "Pss: %zu", &pss_kb);
if (n < 1) {
continue;
}
size_t pss = pss_kb * 1024;
if (pss > 0) {
nsAutoCString name, description, tag;
GetReporterNameAndDescription(currentPath.get(), perms, name, description, tag);
nsAutoCString processMemPath("mem/processes/");
processMemPath.Append(aProcessName);
processMemPath.Append('/');
processMemPath.Append(name);
REPORT(processMemPath, UNITS_BYTES, pss, description);
// Increment the appropriate aProcessSizes values, and the total.
aProcessSizes->Add(tag, pss);
*aTotalPss += pss;
}
// Now that we've seen the PSS, we're done with this entry.
currentPath.SetIsVoid(true);
}
}
void GetReporterNameAndDescription(const char* aPath,
const char* aPerms,
nsACString& aName,
nsACString& aDesc,
nsACString& aTag)
{
aName.Truncate();
aDesc.Truncate();
aTag.Truncate();
// If aPath points to a file, we have its absolute path; it might
// also be a bracketed pseudo-name (see below). In either case
// there is also some whitespace to trim.
nsAutoCString absPath;
absPath.Append(aPath);
absPath.StripChars(" ");
if (absPath.EqualsLiteral("[heap]")) {
aName.AppendLiteral("anonymous/brk-heap");
aDesc.AppendLiteral(
"Memory in anonymous mappings within the boundaries defined by "
"brk() / sbrk(). This is likely to be just a portion of the "
"application's heap; the remainder lives in other anonymous mappings. "
"This corresponds to '[heap]' in /proc/<pid>/smaps.");
aTag = aName;
} else if (absPath.EqualsLiteral("[stack]")) {
aName.AppendLiteral("stack/main-thread");
aDesc.AppendPrintf(
"The stack size of the process's main thread. This corresponds to "
"'[stack]' in /proc/<pid>/smaps.");
aTag = aName;
} else if (MozTaggedMemoryIsSupported() &&
StringBeginsWith(absPath, NS_LITERAL_CSTRING("[stack:"))) {
// If tagged memory is supported, we can be reasonably sure that
// the virtual memory area containing the stack hasn't been
// merged with unrelated heap memory. (This prevents the
// "[stack:" entries from reaching the IsAnonymous case below.)
pid_t tid = atoi(absPath.get() + 7);
nsAutoCString threadName, escapedThreadName;
LinuxUtils::GetThreadName(tid, threadName);
if (threadName.IsEmpty()) {
threadName.AssignLiteral("<unknown>");
}
escapedThreadName.Assign(threadName);
escapedThreadName.StripChars("()");
escapedThreadName.ReplaceChar('/', '\\');
aName.AppendLiteral("stack/non-main-thread");
aName.AppendLiteral("/name(");
aName.Append(escapedThreadName);
aName.Append(')');
aTag = aName;
aName.AppendPrintf("/thread(%d)", tid);
aDesc.AppendPrintf("The stack size of a non-main thread named '%s' with "
"thread ID %d. This corresponds to '[stack:%d]' "
"in /proc/%d/smaps.", threadName.get(), tid, tid);
} else if (absPath.EqualsLiteral("[vdso]")) {
aName.AppendLiteral("vdso");
aDesc.AppendLiteral(
"The virtual dynamically-linked shared object, also known as the "
"'vsyscall page'. This is a memory region mapped by the operating "
"system for the purpose of allowing processes to perform some "
"privileged actions without the overhead of a syscall.");
aTag = aName;
} else if (StringBeginsWith(absPath, NS_LITERAL_CSTRING("[anon:")) &&
EndsWithLiteral(absPath, "]")) {
// It's tagged memory; see also "mfbt/TaggedAnonymousMemory.h".
nsAutoCString tag(Substring(absPath, 6, absPath.Length() - 7));
aName.AppendLiteral("anonymous/");
aName.Append(tag);
aTag = aName;
aDesc.AppendLiteral("Memory in anonymous mappings tagged with '");
aDesc.Append(tag);
aDesc.Append('\'');
} else if (!IsAnonymous(absPath)) {
// We now know it's an actual file. Truncate this to its
// basename, and put the absolute path in the description.
nsAutoCString basename, dirname;
GetBasename(absPath, basename);
GetDirname(absPath, dirname);
// Hack: A file is a shared library if the basename contains ".so" and
// its dirname contains "/lib", or if the basename ends with ".so".
if (EndsWithLiteral(basename, ".so") ||
(basename.Find(".so") != -1 && dirname.Find("/lib") != -1)) {
aName.AppendLiteral("shared-libraries/");
aTag = aName;
if (strncmp(aPerms, "r-x", 3) == 0) {
aTag.AppendLiteral("read-executable");
} else if (strncmp(aPerms, "rw-", 3) == 0) {
aTag.AppendLiteral("read-write");
} else if (strncmp(aPerms, "r--", 3) == 0) {
aTag.AppendLiteral("read-only");
} else {
aTag.AppendLiteral("other");
}
} else {
aName.AppendLiteral("other-files");
if (EndsWithLiteral(basename, ".xpi")) {
aName.AppendLiteral("/extensions");
} else if (dirname.Find("/fontconfig") != -1) {
aName.AppendLiteral("/fontconfig");
} else {
aName.AppendLiteral("/misc");
}
aTag = aName;
aName.Append('/');
}
aName.Append(basename);
aDesc.Append(absPath);
} else {
if (MozTaggedMemoryIsSupported()) {
aName.AppendLiteral("anonymous/untagged");
aDesc.AppendLiteral("Memory in untagged anonymous mappings.");
aTag = aName;
} else {
aName.AppendLiteral("anonymous/outside-brk");
aDesc.AppendLiteral("Memory in anonymous mappings outside the "
"boundaries defined by brk() / sbrk().");
aTag = aName;
}
}
aName.AppendLiteral("/[");
aName.Append(aPerms);
aName.Append(']');
// Append the permissions. This is useful for non-verbose mode in
// about:memory when the filename is long and goes of the right side of the
// window.
aDesc.AppendLiteral(" [");
aDesc.Append(aPerms);
aDesc.Append(']');
}
void CollectPmemReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData)
{
// The pmem subsystem allocates physically contiguous memory for
// interfacing with hardware. In order to ensure availability,
// this memory is reserved during boot, and allocations are made
// within these regions at runtime.
//
// There are typically several of these pools allocated at boot.
// The /sys/kernel/pmem_regions directory contains a subdirectory
// for each one. Within each subdirectory, the files we care
// about are "size" (the total amount of physical memory) and
// "mapped_regions" (a list of the current allocations within that
// area).
DIR* d = opendir("/sys/kernel/pmem_regions");
if (!d) {
return;
}
struct dirent* ent;
while ((ent = readdir(d))) {
const char* name = ent->d_name;
uint64_t size;
int scanned;
// Skip "." and ".." (and any other dotfiles).
if (name[0] == '.') {
continue;
}
// Read the total size. The file gives the size in decimal and
// hex, in the form "13631488(0xd00000)"; we parse the former.
nsPrintfCString sizePath("/sys/kernel/pmem_regions/%s/size", name);
FILE* sizeFile = fopen(sizePath.get(), "r");
if (NS_WARN_IF(!sizeFile)) {
continue;
}
scanned = fscanf(sizeFile, "%" SCNu64, &size);
fclose(sizeFile);
if (NS_WARN_IF(scanned != 1)) {
continue;
}
// Read mapped regions; format described below.
uint64_t freeSize = size;
nsPrintfCString regionsPath("/sys/kernel/pmem_regions/%s/mapped_regions",
name);
FILE* regionsFile = fopen(regionsPath.get(), "r");
if (regionsFile) {
static const size_t bufLen = 4096;
char buf[bufLen];
while (fgets(buf, bufLen, regionsFile)) {
int pid;
// Skip header line.
if (strncmp(buf, "pid #", 5) == 0) {
continue;
}
// Line format: "pid N:" + zero or more "(Start,Len) ".
// N is decimal; Start and Len are in hex.
scanned = sscanf(buf, "pid %d", &pid);
if (NS_WARN_IF(scanned != 1)) {
continue;
}
for (const char* nextParen = strchr(buf, '(');
nextParen != nullptr;
nextParen = strchr(nextParen + 1, '(')) {
uint64_t mapStart, mapLen;
scanned = sscanf(nextParen + 1, "%" SCNx64 ",%" SCNx64,
&mapStart, &mapLen);
if (NS_WARN_IF(scanned != 2)) {
break;
}
nsPrintfCString path("mem/pmem/used/%s/segment(pid=%d, "
"offset=0x%" PRIx64 ")", name, pid, mapStart);
nsPrintfCString desc("Physical memory reserved for the \"%s\" pool "
"and allocated to a buffer.", name);
REPORT(path, UNITS_BYTES, mapLen, desc);
freeSize -= mapLen;
}
}
fclose(regionsFile);
}
nsPrintfCString path("mem/pmem/free/%s", name);
nsPrintfCString desc("Physical memory reserved for the \"%s\" pool and "
"unavailable to the rest of the system, but not "
"currently allocated.", name);
REPORT(path, UNITS_BYTES, freeSize, desc);
}
closedir(d);
}
void
CollectIonReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData)
{
// ION is a replacement for PMEM (and other similar allocators).
//
// More details from http://lwn.net/Articles/480055/
// "Like its PMEM-like predecessors, ION manages one or more memory pools,
// some of which are set aside at boot time to combat fragmentation or to
// serve special hardware needs. GPUs, display controllers, and cameras
// are some of the hardware blocks that may have special memory
// requirements."
//
// The file format starts as follows:
// client pid size
// ----------------------------------------------------
// adsprpc-smd 1 4096
// fd900000.qcom,mdss_mdp 1 1658880
// ----------------------------------------------------
// orphaned allocations (info is from last known client):
// Homescreen 24100 294912 0 1
// b2g 23987 1658880 0 1
// mdss_fb0 401 1658880 0 1
// b2g 23987 4096 0 1
// Built-in Keyboa 24205 61440 0 1
// ----------------------------------------------------
// <other stuff>
//
// For our purposes we only care about the first portion of the file noted
// above which contains memory alloations (both sections). The term
// "orphaned" is misleading, it appears that every allocation not by the
// first process is considered orphaned on FxOS devices.
// The first three fields of each entry interest us:
// 1) client - Essentially the process name. We limit client names to 63
// characters, in theory they should never be greater than 15
// due to thread name length limitations.
// 2) pid - The ID of the allocating process, read as a uint32_t.
// 3) size - The size of the allocation in bytes, read as as a uint64_t.
const char* const kFormatString = "%63s %" SCNu32 " %" SCNu64;
const int kNumFields = 3;
const size_t kStringSize = 64;
const char* const kIonIommuPath = "/sys/kernel/debug/ion/iommu";
FILE* iommu = fopen(kIonIommuPath, "r");
if (!iommu) {
return;
}
AutoFile iommuGuard(iommu);
const size_t kBufferLen = 256;
char buffer[kBufferLen];
char client[kStringSize];
uint32_t pid;
uint64_t size;
// Ignore the header line.
Unused << fgets(buffer, kBufferLen, iommu);
// Ignore the separator line.
Unused << fgets(buffer, kBufferLen, iommu);
const char* const kSep = "----";
const size_t kSepLen = 4;
// Read non-orphaned entries.
while (fgets(buffer, kBufferLen, iommu) &&
strncmp(kSep, buffer, kSepLen) != 0) {
if (sscanf(buffer, kFormatString, client, &pid, &size) == kNumFields) {
nsPrintfCString entryPath("ion-memory/%s (pid=%d)", client, pid);
REPORT(entryPath, UNITS_BYTES, size,
NS_LITERAL_CSTRING("An ION kernel memory allocation."));
}
}
// Ignore the orphaned header.
Unused << fgets(buffer, kBufferLen, iommu);
// Read orphaned entries.
while (fgets(buffer, kBufferLen, iommu) &&
strncmp(kSep, buffer, kSepLen) != 0) {
if (sscanf(buffer, kFormatString, client, &pid, &size) == kNumFields) {
nsPrintfCString entryPath("ion-memory/%s (pid=%d)", client, pid);
REPORT(entryPath, UNITS_BYTES, size,
NS_LITERAL_CSTRING("An ION kernel memory allocation."));
}
}
// Ignore the rest of the file.
}
uint64_t
ReadSizeFromFile(const char* aFilename)
{
FILE* sizeFile = fopen(aFilename, "r");
if (NS_WARN_IF(!sizeFile)) {
return 0;
}
uint64_t size = 0;
Unused << fscanf(sizeFile, "%" SCNu64, &size);
fclose(sizeFile);
return size;
}
void
CollectZramReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData)
{
// zram usage stats files can be found under:
// /sys/block/zram<id>
// |--> disksize - Maximum amount of uncompressed data that can be
// stored on the disk (bytes)
// |--> orig_data_size - Uncompressed size of data in the disk (bytes)
// |--> compr_data_size - Compressed size of the data in the disk (bytes)
// |--> num_reads - Number of attempted reads to the disk (count)
// |--> num_writes - Number of attempted writes to the disk (count)
//
// Each file contains a single integer value in decimal form.
DIR* d = opendir("/sys/block");
if (!d) {
return;
}
struct dirent* ent;
while ((ent = readdir(d))) {
const char* name = ent->d_name;
// Skip non-zram entries.
if (strncmp("zram", name, 4) != 0) {
continue;
}
// Report disk size statistics.
nsPrintfCString diskSizeFile("/sys/block/%s/disksize", name);
nsPrintfCString origSizeFile("/sys/block/%s/orig_data_size", name);
uint64_t diskSize = ReadSizeFromFile(diskSizeFile.get());
uint64_t origSize = ReadSizeFromFile(origSizeFile.get());
uint64_t unusedSize = diskSize - origSize;
nsPrintfCString diskUsedPath("zram-disksize/%s/used", name);
nsPrintfCString diskUsedDesc(
"The uncompressed size of data stored in \"%s.\" "
"This excludes zero-filled pages since "
"no memory is allocated for them.", name);
REPORT(diskUsedPath, UNITS_BYTES, origSize, diskUsedDesc);
nsPrintfCString diskUnusedPath("zram-disksize/%s/unused", name);
nsPrintfCString diskUnusedDesc(
"The amount of uncompressed data that can still be "
"be stored in \"%s\"", name);
REPORT(diskUnusedPath, UNITS_BYTES, unusedSize, diskUnusedDesc);
// Report disk accesses.
nsPrintfCString readsFile("/sys/block/%s/num_reads", name);
nsPrintfCString writesFile("/sys/block/%s/num_writes", name);
uint64_t reads = ReadSizeFromFile(readsFile.get());
uint64_t writes = ReadSizeFromFile(writesFile.get());
nsPrintfCString readsDesc(
"The number of reads (failed or successful) done on "
"\"%s\"", name);
nsPrintfCString readsPath("zram-accesses/%s/reads", name);
REPORT(readsPath, UNITS_COUNT_CUMULATIVE, reads, readsDesc);
nsPrintfCString writesDesc(
"The number of writes (failed or successful) done "
"on \"%s\"", name);
nsPrintfCString writesPath("zram-accesses/%s/writes", name);
REPORT(writesPath, UNITS_COUNT_CUMULATIVE, writes, writesDesc);
// Report compressed data size.
nsPrintfCString comprSizeFile("/sys/block/%s/compr_data_size", name);
uint64_t comprSize = ReadSizeFromFile(comprSizeFile.get());
nsPrintfCString comprSizeDesc(
"The compressed size of data stored in \"%s\"",
name);
nsPrintfCString comprSizePath("zram-compr-data-size/%s", name);
REPORT(comprSizePath, UNITS_BYTES, comprSize, comprSizeDesc);
}
closedir(d);
}
void
CollectOpenFileReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData,
const nsACString& aProcPath,
const nsACString& aProcessName)
{
// All file descriptors opened by a process are listed under
// /proc/<pid>/fd/<numerical_fd>. Each entry is a symlink that points to the
// path that was opened. This can be an actual file, a socket, a pipe, an
// anon_inode, or possibly an uncategorized device.
const char kFilePrefix[] = "/";
const char kSocketPrefix[] = "socket:";
const char kPipePrefix[] = "pipe:";
const char kAnonInodePrefix[] = "anon_inode:";
const nsCString procPath(aProcPath);
DIR* d = opendir(procPath.get());
if (!d) {
return;
}
char linkPath[PATH_MAX + 1];
struct dirent* ent;
while ((ent = readdir(d))) {
const char* fd = ent->d_name;
// Skip "." and ".." (and any other dotfiles).
if (fd[0] == '.') {
continue;
}
nsPrintfCString fullPath("%s/%s", procPath.get(), fd);
ssize_t linkPathSize = readlink(fullPath.get(), linkPath, PATH_MAX);
if (linkPathSize > 0) {
linkPath[linkPathSize] = '\0';
#define CHECK_PREFIX(prefix) \
(strncmp(linkPath, prefix, sizeof(prefix) - 1) == 0)
const char* category = nullptr;
const char* descriptionPrefix = nullptr;
if (CHECK_PREFIX(kFilePrefix)) {
category = "files"; // No trailing slash, the file path will have one
descriptionPrefix = "An open";
} else if (CHECK_PREFIX(kSocketPrefix)) {
category = "sockets/";
descriptionPrefix = "A socket";
} else if (CHECK_PREFIX(kPipePrefix)) {
category = "pipes/";
descriptionPrefix = "A pipe";
} else if (CHECK_PREFIX(kAnonInodePrefix)) {
category = "anon_inodes/";
descriptionPrefix = "An anon_inode";
} else {
category = "";
descriptionPrefix = "An uncategorized";
}
#undef CHECK_PREFIX
const nsCString processName(aProcessName);
nsPrintfCString entryPath("open-fds/%s/%s%s/%s",
processName.get(), category, linkPath, fd);
nsPrintfCString entryDescription("%s file descriptor opened by the process",
descriptionPrefix);
REPORT(entryPath, UNITS_COUNT, 1, entryDescription);
}
}
closedir(d);
}
void
CollectKgslReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData)
{
// Each process that uses kgsl memory will have an entry under
// /sys/kernel/debug/kgsl/proc/<pid>/mem. This file format includes a
// header and then entries with types as follows:
// gpuaddr useraddr size id flags type usage sglen
// hexaddr hexaddr int int str str str int
// We care primarily about the usage and size.
// For simplicity numbers will be uint64_t, strings 63 chars max.
const char* const kScanFormat =
"%" SCNx64 " %" SCNx64 " %" SCNu64 " %" SCNu64
" %63s %63s %63s %" SCNu64;
const int kNumFields = 8;
const size_t kStringSize = 64;
DIR* d = opendir("/sys/kernel/debug/kgsl/proc/");
if (!d) {
return;
}
AutoDir dirGuard(d);
struct dirent* ent;
while ((ent = readdir(d))) {
const char* pid = ent->d_name;
// Skip "." and ".." (and any other dotfiles).
if (pid[0] == '.') {
continue;
}
nsPrintfCString memPath("/sys/kernel/debug/kgsl/proc/%s/mem", pid);
FILE* memFile = fopen(memPath.get(), "r");
if (NS_WARN_IF(!memFile)) {
continue;
}
AutoFile fileGuard(memFile);
// Attempt to map the pid to a more useful name.
nsAutoCString procName;
LinuxUtils::GetThreadName(atoi(pid), procName);
if (procName.IsEmpty()) {
procName.Append("pid=");
procName.Append(pid);
} else {
procName.Append(" (pid=");
procName.Append(pid);
procName.Append(")");
}
uint64_t gpuaddr, useraddr, size, id, sglen;
char flags[kStringSize];
char type[kStringSize];
char usage[kStringSize];
// Bypass the header line.
char buff[1024];
Unused << fgets(buff, 1024, memFile);
while (fscanf(memFile, kScanFormat, &gpuaddr, &useraddr, &size, &id,
flags, type, usage, &sglen) == kNumFields) {
nsPrintfCString entryPath("kgsl-memory/%s/%s", procName.get(), usage);
REPORT(entryPath, UNITS_BYTES, size,
NS_LITERAL_CSTRING("A kgsl graphics memory allocation."));
}
}
}
};
NS_IMPL_ISUPPORTS(SystemReporter, nsIMemoryReporter)
void
Init()
{
RegisterStrongMemoryReporter(new SystemReporter());
}
} // namespace SystemMemoryReporter
} // namespace mozilla

View file

@ -0,0 +1,29 @@
/* -*- 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_SystemMemoryReporter_h_
#define mozilla_SystemMemoryReporter_h_
namespace mozilla {
namespace SystemMemoryReporter {
// This only works on Linux, but to make callers' lives easier, we stub out
// empty functions on other platforms.
#if defined(XP_LINUX)
void
Init();
#else
void
Init()
{
}
#endif
} // namespace SystemMemoryReporter
} // namespace mozilla
#endif

159
xpcom/base/moz.build Normal file
View file

@ -0,0 +1,159 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
XPIDL_SOURCES += [
'nsIConsoleListener.idl',
'nsIConsoleMessage.idl',
'nsIConsoleService.idl',
'nsICycleCollectorListener.idl',
'nsIDebug2.idl',
'nsIErrorService.idl',
'nsIException.idl',
'nsIGZFileWriter.idl',
'nsIInterfaceRequestor.idl',
'nsIMemory.idl',
'nsIMemoryInfoDumper.idl',
'nsIMemoryReporter.idl',
'nsIMessageLoop.idl',
'nsIMutable.idl',
'nsIProgrammingLanguage.idl',
'nsISecurityConsoleMessage.idl',
'nsIStatusReporter.idl',
'nsISupports.idl',
'nsIUUIDGenerator.idl',
'nsIVersionComparator.idl',
'nsIWeakReference.idl',
'nsrootidl.idl',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
XPIDL_SOURCES += [
'nsIMacUtils.idl',
]
EXPORTS.mozilla += [
'MacHelpers.h',
]
UNIFIED_SOURCES += [
'MacHelpers.mm',
]
XPIDL_MODULE = 'xpcom_base'
EXPORTS += [
'CodeAddressService.h',
'ErrorList.h',
'nsAgg.h',
'nsAlgorithm.h',
'nsAutoPtr.h',
'nsAutoRef.h',
'nsCom.h',
'nscore.h',
'nsCycleCollector.h',
'nsDebugImpl.h',
'nsDumpUtils.h',
'nsError.h',
'nsGZFileWriter.h',
'nsIID.h',
'nsInterfaceRequestorAgg.h',
'nsISizeOf.h',
'nsISupportsBase.h',
'nsObjCExceptions.h',
'nsQueryObject.h',
'nsTraceRefcnt.h',
'nsWeakPtr.h',
]
if CONFIG['OS_ARCH'] == 'WINNT':
EXPORTS += [
'nsWindowsHelpers.h',
]
EXPORTS.mozilla += [
'AvailableMemoryTracker.h',
'ClearOnShutdown.h',
'CountingAllocatorBase.h',
'CycleCollectedJSContext.h',
'Debug.h',
'DebuggerOnGCRunnable.h',
'DeferredFinalize.h',
'ErrorNames.h',
'HoldDropJSObjects.h',
'JSObjectHolder.h',
'LinuxUtils.h',
'Logging.h',
'nsMemoryInfoDumper.h',
'OwningNonNull.h',
'StaticMutex.h',
'StaticPtr.h',
'SystemMemoryReporter.h',
]
# nsDebugImpl isn't unified because we disable PGO so that NS_ABORT_OOM isn't
# optimized away oddly.
SOURCES += [
'nsDebugImpl.cpp',
]
SOURCES['nsDebugImpl.cpp'].no_pgo = True
UNIFIED_SOURCES += [
'AvailableMemoryTracker.cpp',
'ClearOnShutdown.cpp',
'CycleCollectedJSContext.cpp',
'Debug.cpp',
'DebuggerOnGCRunnable.cpp',
'DeferredFinalize.cpp',
'ErrorNames.cpp',
'HoldDropJSObjects.cpp',
'JSObjectHolder.cpp',
'Logging.cpp',
'LogModulePrefWatcher.cpp',
'nsConsoleMessage.cpp',
'nsConsoleService.cpp',
'nsCycleCollector.cpp',
'nsCycleCollectorTraceJSHelpers.cpp',
'nsDumpUtils.cpp',
'nsErrorService.cpp',
'nsGZFileWriter.cpp',
'nsInterfaceRequestorAgg.cpp',
'nsMemoryImpl.cpp',
'nsMemoryInfoDumper.cpp',
'nsMemoryReporterManager.cpp',
'nsMessageLoop.cpp',
'NSPRLogModulesParser.cpp',
'nsSecurityConsoleMessage.cpp',
'nsStatusReporterManager.cpp',
'nsSystemInfo.cpp',
'nsTraceRefcnt.cpp',
'nsUUIDGenerator.cpp',
'nsVersionComparatorImpl.cpp',
]
if CONFIG['OS_ARCH'] == 'Linux':
SOURCES += [
'LinuxUtils.cpp',
'SystemMemoryReporter.cpp',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
SOURCES += [
'nsMacUtilsImpl.cpp',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
SOURCES += [
'nsCrashOnException.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'../build',
'/xpcom/ds',
]
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
CXXFLAGS += CONFIG['TK_CFLAGS']

336
xpcom/base/nsAgg.h Normal file
View file

@ -0,0 +1,336 @@
/* -*- 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 nsAgg_h___
#define nsAgg_h___
#include "nsISupports.h"
#include "nsCycleCollectionParticipant.h"
////////////////////////////////////////////////////////////////////////////////
// Put NS_DECL_AGGREGATED or NS_DECL_CYCLE_COLLECTING_AGGREGATED in your class's
// declaration.
#define NS_DECL_AGGREGATED \
NS_DECL_ISUPPORTS \
NS_DECL_AGGREGATED_HELPER
#define NS_DECL_CYCLE_COLLECTING_AGGREGATED \
NS_DECL_CYCLE_COLLECTING_ISUPPORTS \
NS_DECL_AGGREGATED_HELPER
#define NS_DECL_AGGREGATED_HELPER \
public: \
\
/** \
* Returns the nsISupports pointer of the inner object (aka the \
* aggregatee). This pointer is really only useful to the outer object \
* (aka the aggregator), which can use it to hold on to the inner \
* object. Anything else wants the nsISupports pointer of the outer \
* object (gotten by QI'ing inner or outer to nsISupports). This method \
* returns a non-addrefed pointer. \
* @return the nsISupports pointer of the inner object \
*/ \
nsISupports* InnerObject(void) { return &fAggregated; } \
\
/** \
* Returns true if this object is part of an aggregated object. \
*/ \
bool IsPartOfAggregated(void) { return fOuter != InnerObject(); } \
\
private: \
\
/* You must implement this operation instead of the nsISupports */ \
/* methods. */ \
nsresult \
AggregatedQueryInterface(const nsIID& aIID, void** aInstancePtr); \
\
class Internal : public nsISupports { \
public: \
\
Internal() {} \
\
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); \
NS_IMETHOD_(MozExternalRefCountType) AddRef(void); \
NS_IMETHOD_(MozExternalRefCountType) Release(void); \
\
NS_DECL_OWNINGTHREAD \
}; \
\
friend class Internal; \
\
nsISupports* MOZ_UNSAFE_REF("fOuter can either point to fAggregated " \
"or to an outer object, and the safety " \
"of this reference depends on the exact " \
"lifetime semantics of the AddRef/Release " \
"functions created by these macros.") \
fOuter; \
Internal fAggregated; \
\
public: \
#define NS_DECL_AGGREGATED_CYCLE_COLLECTION_CLASS(_class) \
class NS_CYCLE_COLLECTION_INNERCLASS \
: public nsXPCOMCycleCollectionParticipant \
{ \
public: \
NS_IMETHOD_(void) Unlink(void *p) override; \
NS_IMETHOD Traverse(void *p, nsCycleCollectionTraversalCallback &cb) \
override; \
NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(_class) \
NS_IMETHOD_(void) DeleteCycleCollectable(void* p) override \
{ \
NS_CYCLE_COLLECTION_CLASSNAME(_class):: \
Downcast(static_cast<nsISupports*>(p))->DeleteCycleCollectable(); \
} \
static _class* Downcast(nsISupports* s) \
{ \
return (_class*)((char*)(s) - offsetof(_class, fAggregated)); \
} \
static nsISupports* Upcast(_class *p) \
{ \
return p->InnerObject(); \
} \
static nsXPCOMCycleCollectionParticipant* GetParticipant() \
{ \
return &_class::NS_CYCLE_COLLECTION_INNERNAME; \
} \
}; \
NS_CHECK_FOR_RIGHT_PARTICIPANT_IMPL(_class); \
static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
// Put this in your class's constructor:
#define NS_INIT_AGGREGATED(outer) \
PR_BEGIN_MACRO \
fOuter = outer ? outer : &fAggregated; \
PR_END_MACRO
// Put this in your class's implementation file:
#define NS_IMPL_AGGREGATED(_class) \
\
NS_IMPL_AGGREGATED_HELPER(_class) \
\
NS_IMETHODIMP_(MozExternalRefCountType) \
_class::Internal::AddRef(void) \
{ \
_class* agg = (_class*)((char*)(this) - offsetof(_class, fAggregated)); \
MOZ_ASSERT(int32_t(agg->mRefCnt) >= 0, "illegal refcnt"); \
NS_ASSERT_OWNINGTHREAD(_class); \
++agg->mRefCnt; \
NS_LOG_ADDREF(this, agg->mRefCnt, #_class, sizeof(*this)); \
return agg->mRefCnt; \
} \
\
NS_IMETHODIMP_(MozExternalRefCountType) \
_class::Internal::Release(void) \
{ \
_class* agg = (_class*)((char*)(this) - offsetof(_class, fAggregated)); \
MOZ_ASSERT(int32_t(agg->mRefCnt) > 0, "dup release"); \
NS_ASSERT_OWNINGTHREAD(_class); \
--agg->mRefCnt; \
NS_LOG_RELEASE(this, agg->mRefCnt, #_class); \
if (agg->mRefCnt == 0) { \
agg->mRefCnt = 1; /* stabilize */ \
delete agg; \
return 0; \
} \
return agg->mRefCnt; \
} \
#define NS_IMPL_CYCLE_COLLECTING_AGGREGATED(_class) \
\
NS_IMPL_AGGREGATED_HELPER(_class) \
\
NS_IMETHODIMP_(MozExternalRefCountType) \
_class::Internal::AddRef(void) \
{ \
_class* agg = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Downcast(this); \
MOZ_ASSERT(int32_t(agg->mRefCnt) >= 0, "illegal refcnt"); \
NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class); \
nsrefcnt count = agg->mRefCnt.incr(this); \
NS_LOG_ADDREF(this, count, #_class, sizeof(*agg)); \
return count; \
} \
NS_IMETHODIMP_(MozExternalRefCountType) \
_class::Internal::Release(void) \
{ \
_class* agg = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Downcast(this); \
MOZ_ASSERT(int32_t(agg->mRefCnt) > 0, "dup release"); \
NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class); \
nsrefcnt count = agg->mRefCnt.decr(this); \
NS_LOG_RELEASE(this, count, #_class); \
return count; \
} \
NS_IMETHODIMP_(void) \
_class::DeleteCycleCollectable(void) \
{ \
delete this; \
}
#define NS_IMPL_AGGREGATED_HELPER(_class) \
NS_IMETHODIMP \
_class::QueryInterface(const nsIID& aIID, void** aInstancePtr) \
{ \
return fOuter->QueryInterface(aIID, aInstancePtr); \
} \
\
NS_IMETHODIMP_(MozExternalRefCountType) \
_class::AddRef(void) \
{ \
return fOuter->AddRef(); \
} \
\
NS_IMETHODIMP_(MozExternalRefCountType) \
_class::Release(void) \
{ \
return fOuter->Release(); \
} \
\
NS_IMETHODIMP \
_class::Internal::QueryInterface(const nsIID& aIID, void** aInstancePtr) \
{ \
_class* agg = (_class*)((char*)(this) - offsetof(_class, fAggregated)); \
return agg->AggregatedQueryInterface(aIID, aInstancePtr); \
} \
/**
* To make aggregated objects participate in cycle collection we need to enable
* the outer object (aggregator) to traverse/unlink the objects held by the
* inner object (the aggregatee). We can't just make the inner object QI'able to
* NS_CYCLECOLLECTIONPARTICIPANT_IID, we don't want to return the inner object's
* nsCycleCollectionParticipant for the outer object (which will happen if the
* outer object doesn't participate in cycle collection itself).
* NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID enables the outer object to get
* the inner objects nsCycleCollectionParticipant.
*
* There are three cases:
* - No aggregation
* QI'ing to NS_CYCLECOLLECTIONPARTICIPANT_IID will return the inner
* object's nsCycleCollectionParticipant.
*
* - Aggregation and outer object does not participate in cycle collection
* QI'ing to NS_CYCLECOLLECTIONPARTICIPANT_IID will not return anything.
*
* - Aggregation and outer object does participate in cycle collection
* QI'ing to NS_CYCLECOLLECTIONPARTICIPANT_IID will return the outer
* object's nsCycleCollectionParticipant. The outer object's
* nsCycleCollectionParticipant can then QI the inner object to
* NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID to get the inner object's
* nsCycleCollectionParticipant, which it can use to traverse/unlink the
* objects reachable from the inner object.
*/
#define NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID \
{ \
0x32889b7e, \
0xe4fe, \
0x43f4, \
{ 0x85, 0x31, 0xb5, 0x28, 0x23, 0xa2, 0xe9, 0xfc } \
}
/**
* Just holds the IID so NS_GET_IID works.
*/
class nsAggregatedCycleCollectionParticipant
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID)
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsAggregatedCycleCollectionParticipant,
NS_AGGREGATED_CYCLECOLLECTIONPARTICIPANT_IID)
// for use with QI macros in nsISupportsUtils.h:
#define NS_INTERFACE_MAP_BEGIN_AGGREGATED(_class) \
NS_IMPL_AGGREGATED_QUERY_HEAD(_class)
#define NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_AGGREGATED(_class) \
NS_IMPL_QUERY_CYCLE_COLLECTION(_class)
#define NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION_AGGREGATED(_class) \
NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_AGGREGATED(_class) \
NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION_ISUPPORTS(_class)
#define NS_IMPL_AGGREGATED_QUERY_HEAD(_class) \
nsresult \
_class::AggregatedQueryInterface(REFNSIID aIID, void** aInstancePtr) \
{ \
NS_ASSERTION(aInstancePtr, \
"AggregatedQueryInterface requires a non-NULL result ptr!"); \
if ( !aInstancePtr ) \
return NS_ERROR_NULL_POINTER; \
nsISupports* foundInterface; \
if ( aIID.Equals(NS_GET_IID(nsISupports)) ) \
foundInterface = InnerObject(); \
else
#define NS_IMPL_AGGREGATED_QUERY_CYCLE_COLLECTION(_class) \
if (aIID.Equals(IsPartOfAggregated() ? \
NS_GET_IID(nsCycleCollectionParticipant) : \
NS_GET_IID(nsAggregatedCycleCollectionParticipant))) \
foundInterface = NS_CYCLE_COLLECTION_PARTICIPANT(_class); \
else
#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_AGGREGATED(_class) \
NS_IMETHODIMP \
NS_CYCLE_COLLECTION_CLASSNAME(_class)::Traverse \
(void *p, nsCycleCollectionTraversalCallback &cb) \
{ \
nsISupports *s = static_cast<nsISupports*>(p); \
MOZ_ASSERT(CheckForRightISupports(s), \
"not the nsISupports pointer we expect"); \
_class *tmp = static_cast<_class*>(Downcast(s)); \
if (!tmp->IsPartOfAggregated()) \
NS_IMPL_CYCLE_COLLECTION_DESCRIBE(_class, tmp->mRefCnt.get())
#define NS_GENERIC_AGGREGATED_CONSTRUCTOR(_InstanceClass) \
static nsresult \
_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \
void **aResult) \
{ \
*aResult = nullptr; \
if (NS_WARN_IF(aOuter && !aIID.Equals(NS_GET_IID(nsISupports)))) \
return NS_ERROR_INVALID_ARG; \
\
RefPtr<_InstanceClass> inst = new _InstanceClass(aOuter); \
if (!inst) { \
return NS_ERROR_OUT_OF_MEMORY; \
} \
\
nsISupports* inner = inst->InnerObject(); \
nsresult rv = inner->QueryInterface(aIID, aResult); \
\
return rv; \
} \
#define NS_GENERIC_AGGREGATED_CONSTRUCTOR_INIT(_InstanceClass, _InitMethod) \
static nsresult \
_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \
void **aResult) \
{ \
*aResult = nullptr; \
if (NS_WARN_IF(aOuter && !aIID.Equals(NS_GET_IID(nsISupports)))) \
return NS_ERROR_INVALID_ARG; \
\
RefPtr<_InstanceClass> inst = new _InstanceClass(aOuter); \
if (!inst) { \
return NS_ERROR_OUT_OF_MEMORY; \
} \
\
nsISupports* inner = inst->InnerObject(); \
NS_ADDREF(inner); \
nsresult rv = inst->_InitMethod(); \
if (NS_SUCCEEDED(rv)) { \
rv = inner->QueryInterface(aIID, aResult); \
} \
NS_RELEASE(inner); \
\
return rv; \
} \
#endif /* nsAgg_h___ */

75
xpcom/base/nsAlgorithm.h Normal file
View file

@ -0,0 +1,75 @@
/* -*- 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 nsAlgorithm_h___
#define nsAlgorithm_h___
#include "nsCharTraits.h" // for |nsCharSourceTraits|, |nsCharSinkTraits|
template <class T>
inline T
NS_ROUNDUP(const T& aA, const T& aB)
{
return ((aA + (aB - 1)) / aB) * aB;
}
// We use these instead of std::min/max because we can't include the algorithm
// header in all of XPCOM because the stl wrappers will error out when included
// in parts of XPCOM. These functions should never be used outside of XPCOM.
template <class T>
inline const T&
XPCOM_MIN(const T& aA, const T& aB)
{
return aB < aA ? aB : aA;
}
// Must return b when a == b in case a is -0
template <class T>
inline const T&
XPCOM_MAX(const T& aA, const T& aB)
{
return aA > aB ? aA : aB;
}
namespace mozilla {
template <class T>
inline const T&
clamped(const T& aA, const T& aMin, const T& aMax)
{
MOZ_ASSERT(aMax >= aMin,
"clamped(): aMax must be greater than or equal to aMin");
return XPCOM_MIN(XPCOM_MAX(aA, aMin), aMax);
}
} // namespace mozilla
template <class InputIterator, class T>
inline uint32_t
NS_COUNT(InputIterator& aFirst, const InputIterator& aLast, const T& aValue)
{
uint32_t result = 0;
for (; aFirst != aLast; ++aFirst)
if (*aFirst == aValue) {
++result;
}
return result;
}
template <class InputIterator, class OutputIterator>
inline OutputIterator&
copy_string(const InputIterator& aFirst, const InputIterator& aLast,
OutputIterator& aResult)
{
typedef nsCharSourceTraits<InputIterator> source_traits;
typedef nsCharSinkTraits<OutputIterator> sink_traits;
sink_traits::write(aResult, source_traits::read(aFirst),
source_traits::readable_distance(aFirst, aLast));
return aResult;
}
#endif // !defined(nsAlgorithm_h___)

17
xpcom/base/nsAllocator.h Normal file
View file

@ -0,0 +1,17 @@
/* -*- 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/. */
////////////////////////////////////////////////////////////////////////////////
// obsolete
////////////////////////////////////////////////////////////////////////////////
#ifndef nsAllocator_h__
#define nsAllocator_h__
#include "nsAgg.h"
#include "nsIFactory.h"
#endif // nsAllocator_h__

454
xpcom/base/nsAutoPtr.h Normal file
View file

@ -0,0 +1,454 @@
/* -*- 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 nsAutoPtr_h
#define nsAutoPtr_h
#include "nsCOMPtr.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TypeTraits.h"
#include "nsCycleCollectionNoteChild.h"
#include "mozilla/MemoryReporting.h"
/*****************************************************************************/
// template <class T> class nsAutoPtrGetterTransfers;
template <class T>
class nsAutoPtr
{
private:
static_assert(!mozilla::IsScalar<T>::value, "If you are using "
"nsAutoPtr to hold an array, use UniquePtr<T[]> instead");
void**
begin_assignment()
{
assign(0);
return reinterpret_cast<void**>(&mRawPtr);
}
void
assign(T* aNewPtr)
{
T* oldPtr = mRawPtr;
if (aNewPtr && aNewPtr == oldPtr) {
NS_RUNTIMEABORT("Logic flaw in the caller");
}
mRawPtr = aNewPtr;
delete oldPtr;
}
// |class Ptr| helps us prevent implicit "copy construction"
// through |operator T*() const| from a |const nsAutoPtr<T>|
// because two implicit conversions in a row aren't allowed.
// It still allows assignment from T* through implicit conversion
// from |T*| to |nsAutoPtr<T>::Ptr|
class Ptr
{
public:
MOZ_IMPLICIT Ptr(T* aPtr)
: mPtr(aPtr)
{
}
operator T*() const
{
return mPtr;
}
private:
T* MOZ_NON_OWNING_REF mPtr;
};
private:
T* MOZ_OWNING_REF mRawPtr;
public:
typedef T element_type;
~nsAutoPtr()
{
delete mRawPtr;
}
// Constructors
nsAutoPtr()
: mRawPtr(0)
// default constructor
{
}
MOZ_IMPLICIT nsAutoPtr(Ptr aRawPtr)
: mRawPtr(aRawPtr)
// construct from a raw pointer (of the right type)
{
}
// This constructor shouldn't exist; we should just use the &&
// constructor.
nsAutoPtr(nsAutoPtr<T>& aSmartPtr)
: mRawPtr(aSmartPtr.forget())
// Construct by transferring ownership from another smart pointer.
{
}
template <typename I>
MOZ_IMPLICIT nsAutoPtr(nsAutoPtr<I>& aSmartPtr)
: mRawPtr(aSmartPtr.forget())
// Construct by transferring ownership from another smart pointer.
{
}
nsAutoPtr(nsAutoPtr<T>&& aSmartPtr)
: mRawPtr(aSmartPtr.forget())
// Construct by transferring ownership from another smart pointer.
{
}
template <typename I>
MOZ_IMPLICIT nsAutoPtr(nsAutoPtr<I>&& aSmartPtr)
: mRawPtr(aSmartPtr.forget())
// Construct by transferring ownership from another smart pointer.
{
}
// Assignment operators
nsAutoPtr<T>&
operator=(T* aRhs)
// assign from a raw pointer (of the right type)
{
assign(aRhs);
return *this;
}
nsAutoPtr<T>& operator=(nsAutoPtr<T>& aRhs)
// assign by transferring ownership from another smart pointer.
{
assign(aRhs.forget());
return *this;
}
template <typename I>
nsAutoPtr<T>& operator=(nsAutoPtr<I>& aRhs)
// assign by transferring ownership from another smart pointer.
{
assign(aRhs.forget());
return *this;
}
nsAutoPtr<T>& operator=(nsAutoPtr<T>&& aRhs)
{
assign(aRhs.forget());
return *this;
}
template <typename I>
nsAutoPtr<T>& operator=(nsAutoPtr<I>&& aRhs)
{
assign(aRhs.forget());
return *this;
}
// Other pointer operators
T*
get() const
/*
Prefer the implicit conversion provided automatically by
|operator T*() const|. Use |get()| _only_ to resolve
ambiguity.
*/
{
return mRawPtr;
}
operator T*() const
/*
...makes an |nsAutoPtr| act like its underlying raw pointer
type whenever it is used in a context where a raw pointer
is expected. It is this operator that makes an |nsAutoPtr|
substitutable for a raw pointer.
Prefer the implicit use of this operator to calling |get()|,
except where necessary to resolve ambiguity.
*/
{
return get();
}
T*
forget()
{
T* temp = mRawPtr;
mRawPtr = 0;
return temp;
}
T*
operator->() const
{
NS_PRECONDITION(mRawPtr != 0,
"You can't dereference a NULL nsAutoPtr with operator->().");
return get();
}
template <typename R, typename... Args>
class Proxy
{
typedef R (T::*member_function)(Args...);
T* mRawPtr;
member_function mFunction;
public:
Proxy(T* aRawPtr, member_function aFunction)
: mRawPtr(aRawPtr),
mFunction(aFunction)
{
}
template<typename... ActualArgs>
R operator()(ActualArgs&&... aArgs)
{
return ((*mRawPtr).*mFunction)(mozilla::Forward<ActualArgs>(aArgs)...);
}
};
template <typename R, typename C, typename... Args>
Proxy<R, Args...> operator->*(R (C::*aFptr)(Args...)) const
{
NS_PRECONDITION(mRawPtr != 0,
"You can't dereference a NULL nsAutoPtr with operator->*().");
return Proxy<R, Args...>(get(), aFptr);
}
nsAutoPtr<T>*
get_address()
// This is not intended to be used by clients. See |address_of|
// below.
{
return this;
}
const nsAutoPtr<T>*
get_address() const
// This is not intended to be used by clients. See |address_of|
// below.
{
return this;
}
public:
T&
operator*() const
{
NS_PRECONDITION(mRawPtr != 0,
"You can't dereference a NULL nsAutoPtr with operator*().");
return *get();
}
T**
StartAssignment()
{
#ifndef NSCAP_FEATURE_INLINE_STARTASSIGNMENT
return reinterpret_cast<T**>(begin_assignment());
#else
assign(0);
return reinterpret_cast<T**>(&mRawPtr);
#endif
}
};
template <class T>
inline nsAutoPtr<T>*
address_of(nsAutoPtr<T>& aPtr)
{
return aPtr.get_address();
}
template <class T>
inline const nsAutoPtr<T>*
address_of(const nsAutoPtr<T>& aPtr)
{
return aPtr.get_address();
}
template <class T>
class nsAutoPtrGetterTransfers
/*
...
This class is designed to be used for anonymous temporary objects in the
argument list of calls that return COM interface pointers, e.g.,
nsAutoPtr<IFoo> fooP;
...->GetTransferedPointer(getter_Transfers(fooP))
DO NOT USE THIS TYPE DIRECTLY IN YOUR CODE. Use |getter_Transfers()| instead.
When initialized with a |nsAutoPtr|, as in the example above, it returns
a |void**|, a |T**|, or an |nsISupports**| as needed, that the
outer call (|GetTransferedPointer| in this case) can fill in.
This type should be a nested class inside |nsAutoPtr<T>|.
*/
{
public:
explicit
nsAutoPtrGetterTransfers(nsAutoPtr<T>& aSmartPtr)
: mTargetSmartPtr(aSmartPtr)
{
// nothing else to do
}
operator void**()
{
return reinterpret_cast<void**>(mTargetSmartPtr.StartAssignment());
}
operator T**()
{
return mTargetSmartPtr.StartAssignment();
}
T*&
operator*()
{
return *(mTargetSmartPtr.StartAssignment());
}
private:
nsAutoPtr<T>& mTargetSmartPtr;
};
template <class T>
inline nsAutoPtrGetterTransfers<T>
getter_Transfers(nsAutoPtr<T>& aSmartPtr)
/*
Used around a |nsAutoPtr| when
...makes the class |nsAutoPtrGetterTransfers<T>| invisible.
*/
{
return nsAutoPtrGetterTransfers<T>(aSmartPtr);
}
// Comparing two |nsAutoPtr|s
template <class T, class U>
inline bool
operator==(const nsAutoPtr<T>& aLhs, const nsAutoPtr<U>& aRhs)
{
return static_cast<const T*>(aLhs.get()) == static_cast<const U*>(aRhs.get());
}
template <class T, class U>
inline bool
operator!=(const nsAutoPtr<T>& aLhs, const nsAutoPtr<U>& aRhs)
{
return static_cast<const T*>(aLhs.get()) != static_cast<const U*>(aRhs.get());
}
// Comparing an |nsAutoPtr| to a raw pointer
template <class T, class U>
inline bool
operator==(const nsAutoPtr<T>& aLhs, const U* aRhs)
{
return static_cast<const T*>(aLhs.get()) == static_cast<const U*>(aRhs);
}
template <class T, class U>
inline bool
operator==(const U* aLhs, const nsAutoPtr<T>& aRhs)
{
return static_cast<const U*>(aLhs) == static_cast<const T*>(aRhs.get());
}
template <class T, class U>
inline bool
operator!=(const nsAutoPtr<T>& aLhs, const U* aRhs)
{
return static_cast<const T*>(aLhs.get()) != static_cast<const U*>(aRhs);
}
template <class T, class U>
inline bool
operator!=(const U* aLhs, const nsAutoPtr<T>& aRhs)
{
return static_cast<const U*>(aLhs) != static_cast<const T*>(aRhs.get());
}
template <class T, class U>
inline bool
operator==(const nsAutoPtr<T>& aLhs, U* aRhs)
{
return static_cast<const T*>(aLhs.get()) == const_cast<const U*>(aRhs);
}
template <class T, class U>
inline bool
operator==(U* aLhs, const nsAutoPtr<T>& aRhs)
{
return const_cast<const U*>(aLhs) == static_cast<const T*>(aRhs.get());
}
template <class T, class U>
inline bool
operator!=(const nsAutoPtr<T>& aLhs, U* aRhs)
{
return static_cast<const T*>(aLhs.get()) != const_cast<const U*>(aRhs);
}
template <class T, class U>
inline bool
operator!=(U* aLhs, const nsAutoPtr<T>& aRhs)
{
return const_cast<const U*>(aLhs) != static_cast<const T*>(aRhs.get());
}
// Comparing an |nsAutoPtr| to |nullptr|
template <class T>
inline bool
operator==(const nsAutoPtr<T>& aLhs, decltype(nullptr))
{
return aLhs.get() == nullptr;
}
template <class T>
inline bool
operator==(decltype(nullptr), const nsAutoPtr<T>& aRhs)
{
return nullptr == aRhs.get();
}
template <class T>
inline bool
operator!=(const nsAutoPtr<T>& aLhs, decltype(nullptr))
{
return aLhs.get() != nullptr;
}
template <class T>
inline bool
operator!=(decltype(nullptr), const nsAutoPtr<T>& aRhs)
{
return nullptr != aRhs.get();
}
/*****************************************************************************/
#endif // !defined(nsAutoPtr_h)

672
xpcom/base/nsAutoRef.h Normal file
View file

@ -0,0 +1,672 @@
/* -*- 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 nsAutoRef_h_
#define nsAutoRef_h_
#include "mozilla/Attributes.h"
#include "nscore.h" // for nullptr, bool
template <class T> class nsSimpleRef;
template <class T> class nsAutoRefBase;
template <class T> class nsReturnRef;
template <class T> class nsReturningRef;
/**
* template <class T> class nsAutoRef
*
* A class that holds a handle to a resource that must be released.
* No reference is added on construction.
*
* No copy constructor nor copy assignment operators are available, so the
* resource will be held until released on destruction or explicitly
* |reset()| or transferred through provided methods.
*
* The publicly available methods are the public methods on this class and its
* public base classes |nsAutoRefBase<T>| and |nsSimpleRef<T>|.
*
* For ref-counted resources see also |nsCountedRef<T>|.
* For function return values see |nsReturnRef<T>|.
*
* For each class |T|, |nsAutoRefTraits<T>| or |nsSimpleRef<T>| must be
* specialized to use |nsAutoRef<T>| and |nsCountedRef<T>|.
*
* @param T A class identifying the type of reference held by the
* |nsAutoRef<T>| and the unique set methods for managing references
* to the resource (defined by |nsAutoRefTraits<T>| or
* |nsSimpleRef<T>|).
*
* Often this is the class representing the resource. Sometimes a
* new possibly-incomplete class may need to be declared.
*
*
* Example: An Automatically closing file descriptor
*
* // References that are simple integral types (as file-descriptors are)
* // usually need a new class to represent the resource and how to handle its
* // references.
* class nsRawFD;
*
* // Specializing nsAutoRefTraits<nsRawFD> describes how to manage file
* // descriptors, so that nsAutoRef<nsRawFD> provides automatic closing of
* // its file descriptor on destruction.
* template <>
* class nsAutoRefTraits<nsRawFD> {
* public:
* // The file descriptor is held in an int.
* typedef int RawRef;
* // -1 means that there is no file associated with the handle.
* static int Void() { return -1; }
* // The file associated with a file descriptor is released with close().
* static void Release(RawRef aFD) { close(aFD); }
* };
*
* // A function returning a file descriptor that must be closed.
* nsReturnRef<nsRawFD> get_file(const char *filename) {
* // Constructing from a raw file descriptor assumes ownership.
* nsAutoRef<nsRawFD> fd(open(filename, O_RDONLY));
* fcntl(fd, F_SETFD, FD_CLOEXEC);
* return fd.out();
* }
*
* void f() {
* unsigned char buf[1024];
*
* // Hold a file descriptor for /etc/hosts in fd1.
* nsAutoRef<nsRawFD> fd1(get_file("/etc/hosts"));
*
* nsAutoRef<nsRawFD> fd2;
* fd2.steal(fd1); // fd2 takes the file descriptor from fd1
* ssize_t count = read(fd1, buf, 1024); // error fd1 has no file
* count = read(fd2, buf, 1024); // reads from /etc/hosts
*
* // If the file descriptor is not stored then it is closed.
* get_file("/etc/login.defs"); // login.defs is closed
*
* // Now use fd1 to hold a file descriptor for /etc/passwd.
* fd1 = get_file("/etc/passwd");
*
* // The nsAutoRef<nsRawFD> can give up the file descriptor if explicitly
* // instructed, but the caller must then ensure that the file is closed.
* int rawfd = fd1.disown();
*
* // Assume ownership of another file descriptor.
* fd1.own(open("/proc/1/maps");
*
* // On destruction, fd1 closes /proc/1/maps and fd2 closes /etc/hosts,
* // but /etc/passwd is not closed.
* }
*
*/
template <class T>
class nsAutoRef : public nsAutoRefBase<T>
{
protected:
typedef nsAutoRef<T> ThisClass;
typedef nsAutoRefBase<T> BaseClass;
typedef nsSimpleRef<T> SimpleRef;
typedef typename BaseClass::RawRefOnly RawRefOnly;
typedef typename BaseClass::LocalSimpleRef LocalSimpleRef;
public:
nsAutoRef()
{
}
// Explicit construction is required so as not to risk unintentionally
// releasing the resource associated with a raw ref.
explicit nsAutoRef(RawRefOnly aRefToRelease)
: BaseClass(aRefToRelease)
{
}
// Construction from a nsReturnRef<T> function return value, which expects
// to give up ownership, transfers ownership.
// (nsReturnRef<T> is converted to const nsReturningRef<T>.)
explicit nsAutoRef(const nsReturningRef<T>& aReturning)
: BaseClass(aReturning)
{
}
// The only assignment operator provided is for transferring from an
// nsReturnRef smart reference, which expects to pass its ownership to
// another object.
//
// With raw references and other smart references, the type of the lhs and
// its taking and releasing nature is often not obvious from an assignment
// statement. Assignment from a raw ptr especially is not normally
// expected to release the reference.
//
// Use |steal| for taking ownership from other smart refs.
//
// For raw references, use |own| to indicate intention to have the
// resource released.
//
// Or, to create another owner of the same reference, use an nsCountedRef.
ThisClass& operator=(const nsReturningRef<T>& aReturning)
{
BaseClass::steal(aReturning.mReturnRef);
return *this;
}
// Conversion to a raw reference allow the nsAutoRef<T> to often be used
// like a raw reference.
operator typename SimpleRef::RawRef() const
{
return this->get();
}
// Transfer ownership from another smart reference.
void steal(ThisClass& aOtherRef)
{
BaseClass::steal(aOtherRef);
}
// Assume ownership of a raw ref.
//
// |own| has similar function to |steal|, and is useful for receiving
// ownership from a return value of a function. It is named differently
// because |own| requires more care to ensure that the function intends to
// give away ownership, and so that |steal| can be safely used, knowing
// that it won't steal ownership from any methods returning raw ptrs to
// data owned by a foreign object.
void own(RawRefOnly aRefToRelease)
{
BaseClass::own(aRefToRelease);
}
// Exchange ownership with |aOther|
void swap(ThisClass& aOther)
{
LocalSimpleRef temp;
temp.SimpleRef::operator=(*this);
SimpleRef::operator=(aOther);
aOther.SimpleRef::operator=(temp);
}
// Release the reference now.
void reset()
{
this->SafeRelease();
LocalSimpleRef empty;
SimpleRef::operator=(empty);
}
// Pass out the reference for a function return values.
nsReturnRef<T> out()
{
return nsReturnRef<T>(this->disown());
}
// operator->() and disown() are provided by nsAutoRefBase<T>.
// The default nsSimpleRef<T> provides get().
private:
// No copy constructor
explicit nsAutoRef(ThisClass& aRefToSteal);
};
/**
* template <class T> class nsCountedRef
*
* A class that creates (adds) a new reference to a resource on construction
* or assignment and releases on destruction.
*
* This class is similar to nsAutoRef<T> and inherits its methods, but also
* provides copy construction and assignment operators that enable more than
* one concurrent reference to the same resource.
*
* Specialize |nsAutoRefTraits<T>| or |nsSimpleRef<T>| to use this. This
* class assumes that the resource itself counts references and so can only be
* used when |T| represents a reference-counting resource.
*/
template <class T>
class nsCountedRef : public nsAutoRef<T>
{
protected:
typedef nsCountedRef<T> ThisClass;
typedef nsAutoRef<T> BaseClass;
typedef nsSimpleRef<T> SimpleRef;
typedef typename BaseClass::RawRef RawRef;
public:
nsCountedRef()
{
}
// Construction and assignment from a another nsCountedRef
// or a raw ref copies and increments the ref count.
nsCountedRef(const ThisClass& aRefToCopy)
{
SimpleRef::operator=(aRefToCopy);
SafeAddRef();
}
ThisClass& operator=(const ThisClass& aRefToCopy)
{
if (this == &aRefToCopy) {
return *this;
}
this->SafeRelease();
SimpleRef::operator=(aRefToCopy);
SafeAddRef();
return *this;
}
// Implicit conversion from another smart ref argument (to a raw ref) is
// accepted here because construction and assignment safely creates a new
// reference without interfering with the reference to copy.
explicit nsCountedRef(RawRef aRefToCopy)
: BaseClass(aRefToCopy)
{
SafeAddRef();
}
ThisClass& operator=(RawRef aRefToCopy)
{
this->own(aRefToCopy);
SafeAddRef();
return *this;
}
// Construction and assignment from an nsReturnRef function return value,
// which expects to give up ownership, transfers ownership.
explicit nsCountedRef(const nsReturningRef<T>& aReturning)
: BaseClass(aReturning)
{
}
ThisClass& operator=(const nsReturningRef<T>& aReturning)
{
BaseClass::operator=(aReturning);
return *this;
}
protected:
// Increase the reference count if there is a resource.
void SafeAddRef()
{
if (this->HaveResource()) {
this->AddRef(this->get());
}
}
};
/**
* template <class T> class nsReturnRef
*
* A type for function return values that hold a reference to a resource that
* must be released. See also |nsAutoRef<T>::out()|.
*/
template <class T>
class nsReturnRef : public nsAutoRefBase<T>
{
protected:
typedef nsAutoRefBase<T> BaseClass;
typedef typename BaseClass::RawRefOnly RawRefOnly;
public:
// For constructing a return value with no resource
nsReturnRef()
{
}
// For returning a smart reference from a raw reference that must be
// released. Explicit construction is required so as not to risk
// unintentionally releasing the resource associated with a raw ref.
MOZ_IMPLICIT nsReturnRef(RawRefOnly aRefToRelease)
: BaseClass(aRefToRelease)
{
}
// Copy construction transfers ownership
nsReturnRef(nsReturnRef<T>& aRefToSteal)
: BaseClass(aRefToSteal)
{
}
MOZ_IMPLICIT nsReturnRef(const nsReturningRef<T>& aReturning)
: BaseClass(aReturning)
{
}
// Conversion to a temporary (const) object referring to this object so
// that the reference may be passed from a function return value
// (temporary) to another smart reference. There is no need to use this
// explicitly. Simply assign a nsReturnRef<T> function return value to a
// smart reference.
operator nsReturningRef<T>()
{
return nsReturningRef<T>(*this);
}
// No conversion to RawRef operator is provided on nsReturnRef, to ensure
// that the return value is not carelessly assigned to a raw ptr (and the
// resource then released). If passing to a function that takes a raw
// ptr, use get or disown as appropriate.
};
/**
* template <class T> class nsReturningRef
*
* A class to allow ownership to be transferred from nsReturnRef function
* return values.
*
* It should not be necessary for clients to reference this
* class directly. Simply pass an nsReturnRef<T> to a parameter taking an
* |nsReturningRef<T>|.
*
* The conversion operator on nsReturnRef constructs a temporary wrapper of
* class nsReturningRef<T> around a non-const reference to the nsReturnRef.
* The wrapper can then be passed as an rvalue parameter.
*/
template <class T>
class nsReturningRef
{
private:
friend class nsReturnRef<T>;
explicit nsReturningRef(nsReturnRef<T>& aReturnRef)
: mReturnRef(aReturnRef)
{
}
public:
nsReturnRef<T>& mReturnRef;
};
/**
* template <class T> class nsAutoRefTraits
*
* A class describing traits of references managed by the default
* |nsSimpleRef<T>| implementation and thus |nsAutoRef<T>| and |nsCountedRef|.
* The default |nsSimpleRef<T> is suitable for resources with handles that
* have a void value. (If there is no such void value for a handle,
* specialize |nsSimpleRef<T>|.)
*
* Specializations must be provided for each class |T| according to the
* following pattern:
*
* // The template parameter |T| should be a class such that the set of fields
* // in class nsAutoRefTraits<T> is unique for class |T|. Usually the
* // resource object class is sufficient. For handles that are simple
* // integral typedefs, a new unique possibly-incomplete class may need to be
* // declared.
*
* template <>
* class nsAutoRefTraits<T>
* {
* // Specializations must provide a typedef for RawRef, describing the
* // type of the handle to the resource.
* typedef <handle-type> RawRef;
*
* // Specializations should define Void(), a function returning a value
* // suitable for a handle that does not have an associated resource.
* //
* // The return type must be a suitable as the parameter to a RawRef
* // constructor and operator==.
* //
* // If this method is not accessible then some limited nsAutoRef
* // functionality will still be available, but the default constructor,
* // |reset|, and most transfer of ownership methods will not be available.
* static <return-type> Void();
*
* // Specializations must define Release() to properly finalize the
* // handle to a non-void custom-deleted or reference-counted resource.
* static void Release(RawRef aRawRef);
*
* // For reference-counted resources, if |nsCountedRef<T>| is to be used,
* // specializations must define AddRef to increment the reference count
* // held by a non-void handle.
* // (AddRef() is not necessary for |nsAutoRef<T>|.)
* static void AddRef(RawRef aRawRef);
* };
*
* See nsPointerRefTraits for example specializations for simple pointer
* references. See nsAutoRef for an example specialization for a non-pointer
* reference.
*/
template <class T> class nsAutoRefTraits;
/**
* template <class T> class nsPointerRefTraits
*
* A convenience class useful as a base class for specializations of
* |nsAutoRefTraits<T>| where the handle to the resource is a pointer to |T|.
* By inheriting from this class, definitions of only Release(RawRef) and
* possibly AddRef(RawRef) need to be added.
*
* Examples of use:
*
* template <>
* class nsAutoRefTraits<PRFileDesc> : public nsPointerRefTraits<PRFileDesc>
* {
* public:
* static void Release(PRFileDesc *ptr) { PR_Close(ptr); }
* };
*
* template <>
* class nsAutoRefTraits<FcPattern> : public nsPointerRefTraits<FcPattern>
* {
* public:
* static void Release(FcPattern *ptr) { FcPatternDestroy(ptr); }
* static void AddRef(FcPattern *ptr) { FcPatternReference(ptr); }
* };
*/
template <class T>
class nsPointerRefTraits
{
public:
// The handle is a pointer to T.
typedef T* RawRef;
// A nullptr does not have a resource.
static RawRef Void()
{
return nullptr;
}
};
/**
* template <class T> class nsSimpleRef
*
* Constructs a non-smart reference, and provides methods to test whether
* there is an associated resource and (if so) get its raw handle.
*
* A default implementation is suitable for resources with handles that have a
* void value. This is not intended for direct use but used by |nsAutoRef<T>|
* and thus |nsCountedRef<T>|.
*
* Specialize this class if there is no particular void value for the resource
* handle. A specialized implementation must also provide Release(RawRef),
* and, if |nsCountedRef<T>| is required, AddRef(RawRef), as described in
* nsAutoRefTraits<T>.
*/
template <class T>
class nsSimpleRef : protected nsAutoRefTraits<T>
{
protected:
// The default implementation uses nsAutoRefTrait<T>.
// Specializations need not define this typedef.
typedef nsAutoRefTraits<T> Traits;
// The type of the handle to the resource.
// A specialization must provide a typedef for RawRef.
typedef typename Traits::RawRef RawRef;
// Construct with no resource.
//
// If this constructor is not accessible then some limited nsAutoRef
// functionality will still be available, but the default constructor,
// |reset|, and most transfer of ownership methods will not be available.
nsSimpleRef()
: mRawRef(Traits::Void())
{
}
// Construct with a handle to a resource.
// A specialization must provide this.
explicit nsSimpleRef(RawRef aRawRef)
: mRawRef(aRawRef)
{
}
// Test whether there is an associated resource. A specialization must
// provide this. The function is permitted to always return true if the
// default constructor is not accessible, or if Release (and AddRef) can
// deal with void handles.
bool HaveResource() const
{
return mRawRef != Traits::Void();
}
public:
// A specialization must provide get() or loose some functionality. This
// is inherited by derived classes and the specialization may choose
// whether it is public or protected.
RawRef get() const
{
return mRawRef;
}
private:
RawRef mRawRef;
};
/**
* template <class T> class nsAutoRefBase
*
* Internal base class for |nsAutoRef<T>| and |nsReturnRef<T>|.
* Adds release on destruction to a |nsSimpleRef<T>|.
*/
template <class T>
class nsAutoRefBase : public nsSimpleRef<T>
{
protected:
typedef nsAutoRefBase<T> ThisClass;
typedef nsSimpleRef<T> SimpleRef;
typedef typename SimpleRef::RawRef RawRef;
nsAutoRefBase()
{
}
// A type for parameters that should be passed a raw ref but should not
// accept implicit conversions (from another smart ref). (The only
// conversion to this type is from a raw ref so only raw refs will be
// accepted.)
class RawRefOnly
{
public:
MOZ_IMPLICIT RawRefOnly(RawRef aRawRef)
: mRawRef(aRawRef)
{
}
operator RawRef() const
{
return mRawRef;
}
private:
RawRef mRawRef;
};
// Construction from a raw ref assumes ownership
explicit nsAutoRefBase(RawRefOnly aRefToRelease)
: SimpleRef(aRefToRelease)
{
}
// Constructors that steal ownership
explicit nsAutoRefBase(ThisClass& aRefToSteal)
: SimpleRef(aRefToSteal.disown())
{
}
explicit nsAutoRefBase(const nsReturningRef<T>& aReturning)
: SimpleRef(aReturning.mReturnRef.disown())
{
}
~nsAutoRefBase()
{
SafeRelease();
}
// An internal class providing access to protected nsSimpleRef<T>
// constructors for construction of temporary simple references (that are
// not ThisClass).
class LocalSimpleRef : public SimpleRef
{
public:
LocalSimpleRef()
{
}
explicit LocalSimpleRef(RawRef aRawRef)
: SimpleRef(aRawRef)
{
}
};
private:
ThisClass& operator=(const ThisClass& aSmartRef) = delete;
public:
RawRef operator->() const
{
return this->get();
}
// Transfer ownership to a raw reference.
//
// THE CALLER MUST ENSURE THAT THE REFERENCE IS EXPLICITLY RELEASED.
//
// Is this really what you want to use? Using this removes any guarantee
// of release. Use nsAutoRef<T>::out() for return values, or an
// nsAutoRef<T> modifiable lvalue for an out parameter. Use disown() when
// the reference must be stored in a POD type object, such as may be
// preferred for a namespace-scope object with static storage duration,
// for example.
RawRef disown()
{
RawRef temp = this->get();
LocalSimpleRef empty;
SimpleRef::operator=(empty);
return temp;
}
protected:
// steal and own are protected because they make no sense on nsReturnRef,
// but steal is implemented on this class for access to aOtherRef.disown()
// when aOtherRef is an nsReturnRef;
// Transfer ownership from another smart reference.
void steal(ThisClass& aOtherRef)
{
own(aOtherRef.disown());
}
// Assume ownership of a raw ref.
void own(RawRefOnly aRefToRelease)
{
SafeRelease();
LocalSimpleRef ref(aRefToRelease);
SimpleRef::operator=(ref);
}
// Release a resource if there is one.
void SafeRelease()
{
if (this->HaveResource()) {
this->Release(this->get());
}
}
};
#endif // !defined(nsAutoRef_h_)

12
xpcom/base/nsCom.h Normal file
View file

@ -0,0 +1,12 @@
/* -*- 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 nsCom_h__
#define nsCom_h__
#include "nscore.h"
#endif

View file

@ -0,0 +1,56 @@
/* -*- 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/. */
/*
* Base implementation for console messages.
*/
#include "nsConsoleMessage.h"
#include "jsapi.h"
NS_IMPL_ISUPPORTS(nsConsoleMessage, nsIConsoleMessage)
nsConsoleMessage::nsConsoleMessage()
: mTimeStamp(0)
, mMessage()
{
}
nsConsoleMessage::nsConsoleMessage(const char16_t* aMessage)
{
mTimeStamp = JS_Now() / 1000;
mMessage.Assign(aMessage);
}
NS_IMETHODIMP
nsConsoleMessage::GetMessageMoz(char16_t** aResult)
{
*aResult = ToNewUnicode(mMessage);
return NS_OK;
}
NS_IMETHODIMP
nsConsoleMessage::GetLogLevel(uint32_t* aLogLevel)
{
*aLogLevel = nsConsoleMessage::info;
return NS_OK;
}
NS_IMETHODIMP
nsConsoleMessage::GetTimeStamp(int64_t* aTimeStamp)
{
*aTimeStamp = mTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
nsConsoleMessage::ToString(nsACString& /*UTF8*/ aResult)
{
CopyUTF16toUTF8(mMessage, aResult);
return NS_OK;
}

View file

@ -0,0 +1,33 @@
/* -*- 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 __nsconsolemessage_h__
#define __nsconsolemessage_h__
#include "mozilla/Attributes.h"
#include "nsIConsoleMessage.h"
#include "nsString.h"
class nsConsoleMessage final : public nsIConsoleMessage
{
public:
nsConsoleMessage();
explicit nsConsoleMessage(const char16_t* aMessage);
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICONSOLEMESSAGE
private:
~nsConsoleMessage()
{
}
int64_t mTimeStamp;
nsString mMessage;
};
#endif /* __nsconsolemessage_h__ */

View file

@ -0,0 +1,473 @@
/* -*- 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/. */
/*
* Maintains a circular buffer of recent messages, and notifies
* listeners when new messages are logged.
*/
/* Threadsafe. */
#include "nsMemory.h"
#include "nsCOMArray.h"
#include "nsThreadUtils.h"
#include "nsConsoleService.h"
#include "nsConsoleMessage.h"
#include "nsIClassInfoImpl.h"
#include "nsIConsoleListener.h"
#include "nsPrintfCString.h"
#include "nsProxyRelease.h"
#include "nsIScriptError.h"
#include "nsISupportsPrimitives.h"
#include "mozilla/Preferences.h"
#if defined(ANDROID)
#include <android/log.h>
#include "mozilla/dom/ContentChild.h"
#endif
#ifdef XP_WIN
#include <windows.h>
#endif
#ifdef MOZ_TASK_TRACER
#include "GeckoTaskTracer.h"
using namespace mozilla::tasktracer;
#endif
using namespace mozilla;
NS_IMPL_ADDREF(nsConsoleService)
NS_IMPL_RELEASE(nsConsoleService)
NS_IMPL_CLASSINFO(nsConsoleService, nullptr,
nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON,
NS_CONSOLESERVICE_CID)
NS_IMPL_QUERY_INTERFACE_CI(nsConsoleService, nsIConsoleService, nsIObserver)
NS_IMPL_CI_INTERFACE_GETTER(nsConsoleService, nsIConsoleService, nsIObserver)
static bool sLoggingEnabled = true;
static bool sLoggingBuffered = true;
#if defined(ANDROID)
static bool sLoggingLogcat = true;
#endif // defined(ANDROID)
nsConsoleService::MessageElement::~MessageElement()
{
}
nsConsoleService::nsConsoleService()
: mCurrentSize(0)
, mDeliveringMessage(false)
, mLock("nsConsoleService.mLock")
{
// XXX grab this from a pref!
// hm, but worry about circularity, bc we want to be able to report
// prefs errs...
mMaximumSize = 250;
}
void
nsConsoleService::ClearMessagesForWindowID(const uint64_t innerID)
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mLock);
for (MessageElement* e = mMessages.getFirst(); e != nullptr; ) {
// Only messages implementing nsIScriptError interface expose the
// inner window ID.
nsCOMPtr<nsIScriptError> scriptError = do_QueryInterface(e->Get());
if (!scriptError) {
e = e->getNext();
continue;
}
uint64_t innerWindowID;
nsresult rv = scriptError->GetInnerWindowID(&innerWindowID);
if (NS_FAILED(rv) || innerWindowID != innerID) {
e = e->getNext();
continue;
}
MessageElement* next = e->getNext();
e->remove();
delete e;
mCurrentSize--;
MOZ_ASSERT(mCurrentSize < mMaximumSize);
e = next;
}
}
void
nsConsoleService::ClearMessages()
{
// NB: A lock is not required here as it's only called from |Reset| which
// locks for us and from the dtor.
while (!mMessages.isEmpty()) {
MessageElement* e = mMessages.popFirst();
delete e;
}
mCurrentSize = 0;
}
nsConsoleService::~nsConsoleService()
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
ClearMessages();
}
class AddConsolePrefWatchers : public Runnable
{
public:
explicit AddConsolePrefWatchers(nsConsoleService* aConsole) : mConsole(aConsole)
{
}
NS_IMETHOD Run() override
{
Preferences::AddBoolVarCache(&sLoggingEnabled, "consoleservice.enabled", true);
Preferences::AddBoolVarCache(&sLoggingBuffered, "consoleservice.buffered", true);
#if defined(ANDROID)
Preferences::AddBoolVarCache(&sLoggingLogcat, "consoleservice.logcat", true);
#endif // defined(ANDROID)
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
MOZ_ASSERT(obs);
obs->AddObserver(mConsole, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
obs->AddObserver(mConsole, "inner-window-destroyed", false);
if (!sLoggingBuffered) {
mConsole->Reset();
}
return NS_OK;
}
private:
RefPtr<nsConsoleService> mConsole;
};
nsresult
nsConsoleService::Init()
{
NS_DispatchToMainThread(new AddConsolePrefWatchers(this));
return NS_OK;
}
namespace {
class LogMessageRunnable : public Runnable
{
public:
LogMessageRunnable(nsIConsoleMessage* aMessage, nsConsoleService* aService)
: mMessage(aMessage)
, mService(aService)
{ }
NS_DECL_NSIRUNNABLE
private:
nsCOMPtr<nsIConsoleMessage> mMessage;
RefPtr<nsConsoleService> mService;
};
NS_IMETHODIMP
LogMessageRunnable::Run()
{
MOZ_ASSERT(NS_IsMainThread());
// Snapshot of listeners so that we don't reenter this hash during
// enumeration.
nsCOMArray<nsIConsoleListener> listeners;
mService->CollectCurrentListeners(listeners);
mService->SetIsDelivering();
for (int32_t i = 0; i < listeners.Count(); ++i) {
listeners[i]->Observe(mMessage);
}
mService->SetDoneDelivering();
return NS_OK;
}
} // namespace
// nsIConsoleService methods
NS_IMETHODIMP
nsConsoleService::LogMessage(nsIConsoleMessage* aMessage)
{
return LogMessageWithMode(aMessage, OutputToLog);
}
// This can be called off the main thread.
nsresult
nsConsoleService::LogMessageWithMode(nsIConsoleMessage* aMessage,
nsConsoleService::OutputMode aOutputMode)
{
if (!aMessage) {
return NS_ERROR_INVALID_ARG;
}
if (!sLoggingEnabled) {
return NS_OK;
}
if (NS_IsMainThread() && mDeliveringMessage) {
nsCString msg;
aMessage->ToString(msg);
NS_WARNING(nsPrintfCString("Reentrancy error: some client attempted "
"to display a message to the console while in a console listener. "
"The following message was discarded: \"%s\"", msg.get()).get());
return NS_ERROR_FAILURE;
}
RefPtr<LogMessageRunnable> r;
nsCOMPtr<nsIConsoleMessage> retiredMessage;
/*
* Lock while updating buffer, and while taking snapshot of
* listeners array.
*/
{
MutexAutoLock lock(mLock);
#if defined(ANDROID)
if (sLoggingLogcat && aOutputMode == OutputToLog) {
nsCString msg;
aMessage->ToString(msg);
/** Attempt to use the process name as the log tag. */
mozilla::dom::ContentChild* child =
mozilla::dom::ContentChild::GetSingleton();
nsCString appName;
if (child) {
child->GetProcessName(appName);
} else {
appName = "GeckoConsole";
}
uint32_t logLevel = 0;
aMessage->GetLogLevel(&logLevel);
android_LogPriority logPriority = ANDROID_LOG_INFO;
switch (logLevel) {
case nsIConsoleMessage::debug:
logPriority = ANDROID_LOG_DEBUG;
break;
case nsIConsoleMessage::info:
logPriority = ANDROID_LOG_INFO;
break;
case nsIConsoleMessage::warn:
logPriority = ANDROID_LOG_WARN;
break;
case nsIConsoleMessage::error:
logPriority = ANDROID_LOG_ERROR;
break;
}
__android_log_print(logPriority, appName.get(), "%s", msg.get());
}
#endif
#ifdef XP_WIN
if (IsDebuggerPresent()) {
nsString msg;
aMessage->GetMessageMoz(getter_Copies(msg));
msg.Append('\n');
OutputDebugStringW(msg.get());
}
#endif
#ifdef MOZ_TASK_TRACER
{
nsCString msg;
aMessage->ToString(msg);
int prefixPos = msg.Find(GetJSLabelPrefix());
if (prefixPos >= 0) {
nsDependentCSubstring submsg(msg, prefixPos);
AddLabel("%s", submsg.BeginReading());
}
}
#endif
if (sLoggingBuffered) {
MessageElement* e = new MessageElement(aMessage);
mMessages.insertBack(e);
if (mCurrentSize != mMaximumSize) {
mCurrentSize++;
} else {
MessageElement* p = mMessages.popFirst();
MOZ_ASSERT(p);
p->swapMessage(retiredMessage);
delete p;
}
}
if (mListeners.Count() > 0) {
r = new LogMessageRunnable(aMessage, this);
}
}
if (retiredMessage) {
// Release |retiredMessage| on the main thread in case it is an instance of
// a mainthread-only class like nsScriptErrorWithStack and we're off the
// main thread.
NS_ReleaseOnMainThread(retiredMessage.forget());
}
if (r) {
// avoid failing in XPCShell tests
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
if (mainThread) {
NS_DispatchToMainThread(r.forget());
}
}
return NS_OK;
}
void
nsConsoleService::CollectCurrentListeners(
nsCOMArray<nsIConsoleListener>& aListeners)
{
MutexAutoLock lock(mLock);
for (auto iter = mListeners.Iter(); !iter.Done(); iter.Next()) {
nsIConsoleListener* value = iter.UserData();
aListeners.AppendObject(value);
}
}
NS_IMETHODIMP
nsConsoleService::LogStringMessage(const char16_t* aMessage)
{
if (!sLoggingEnabled) {
return NS_OK;
}
RefPtr<nsConsoleMessage> msg(new nsConsoleMessage(aMessage));
return this->LogMessage(msg);
}
NS_IMETHODIMP
nsConsoleService::GetMessageArray(uint32_t* aCount,
nsIConsoleMessage*** aMessages)
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mLock);
if (mMessages.isEmpty()) {
/*
* Make a 1-length output array so that nobody gets confused,
* and return a count of 0. This should result in a 0-length
* array object when called from script.
*/
nsIConsoleMessage** messageArray = (nsIConsoleMessage**)
moz_xmalloc(sizeof(nsIConsoleMessage*));
*messageArray = nullptr;
*aMessages = messageArray;
*aCount = 0;
return NS_OK;
}
MOZ_ASSERT(mCurrentSize <= mMaximumSize);
nsIConsoleMessage** messageArray =
static_cast<nsIConsoleMessage**>(moz_xmalloc(sizeof(nsIConsoleMessage*)
* mCurrentSize));
uint32_t i = 0;
for (MessageElement* e = mMessages.getFirst(); e != nullptr; e = e->getNext()) {
nsCOMPtr<nsIConsoleMessage> m = e->Get();
m.forget(&messageArray[i]);
i++;
}
MOZ_ASSERT(i == mCurrentSize);
*aCount = i;
*aMessages = messageArray;
return NS_OK;
}
NS_IMETHODIMP
nsConsoleService::RegisterListener(nsIConsoleListener* aListener)
{
if (!NS_IsMainThread()) {
NS_ERROR("nsConsoleService::RegisterListener is main thread only.");
return NS_ERROR_NOT_SAME_THREAD;
}
nsCOMPtr<nsISupports> canonical = do_QueryInterface(aListener);
MutexAutoLock lock(mLock);
if (mListeners.GetWeak(canonical)) {
// Reregistering a listener isn't good
return NS_ERROR_FAILURE;
}
mListeners.Put(canonical, aListener);
return NS_OK;
}
NS_IMETHODIMP
nsConsoleService::UnregisterListener(nsIConsoleListener* aListener)
{
if (!NS_IsMainThread()) {
NS_ERROR("nsConsoleService::UnregisterListener is main thread only.");
return NS_ERROR_NOT_SAME_THREAD;
}
nsCOMPtr<nsISupports> canonical = do_QueryInterface(aListener);
MutexAutoLock lock(mLock);
if (!mListeners.GetWeak(canonical)) {
// Unregistering a listener that was never registered?
return NS_ERROR_FAILURE;
}
mListeners.Remove(canonical);
return NS_OK;
}
NS_IMETHODIMP
nsConsoleService::Reset()
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
/*
* Make sure nobody trips into the buffer while it's being reset
*/
MutexAutoLock lock(mLock);
ClearMessages();
return NS_OK;
}
NS_IMETHODIMP
nsConsoleService::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
// Dump all our messages, in case any are cycle collected.
Reset();
// We could remove ourselves from the observer service, but it is about to
// drop all observers anyways, so why bother.
} else if (!strcmp(aTopic, "inner-window-destroyed")) {
nsCOMPtr<nsISupportsPRUint64> supportsInt = do_QueryInterface(aSubject);
MOZ_ASSERT(supportsInt);
uint64_t windowId;
MOZ_ALWAYS_SUCCEEDS(supportsInt->GetData(&windowId));
ClearMessagesForWindowID(windowId);
} else {
MOZ_CRASH();
}
return NS_OK;
}

View file

@ -0,0 +1,118 @@
/* -*- 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/. */
/*
* nsConsoleService class declaration.
*/
#ifndef __nsconsoleservice_h__
#define __nsconsoleservice_h__
#include "mozilla/Attributes.h"
#include "mozilla/Mutex.h"
#include "nsInterfaceHashtable.h"
#include "nsHashKeys.h"
#include "nsIConsoleService.h"
class nsConsoleService final : public nsIConsoleService,
public nsIObserver
{
public:
nsConsoleService();
nsresult Init();
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICONSOLESERVICE
NS_DECL_NSIOBSERVER
void SetIsDelivering()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mDeliveringMessage);
mDeliveringMessage = true;
}
void SetDoneDelivering()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mDeliveringMessage);
mDeliveringMessage = false;
}
// This is a variant of LogMessage which allows the caller to determine
// if the message should be output to an OS-specific log. This is used on
// B2G to control whether the message is logged to the android log or not.
enum OutputMode {
SuppressLog,
OutputToLog
};
virtual nsresult LogMessageWithMode(nsIConsoleMessage* aMessage,
OutputMode aOutputMode);
typedef nsInterfaceHashtable<nsISupportsHashKey,
nsIConsoleListener> ListenerHash;
void CollectCurrentListeners(nsCOMArray<nsIConsoleListener>& aListeners);
private:
class MessageElement : public mozilla::LinkedListElement<MessageElement>
{
public:
explicit MessageElement(nsIConsoleMessage* aMessage) : mMessage(aMessage)
{}
nsIConsoleMessage* Get()
{
return mMessage.get();
}
// Swap directly into an nsCOMPtr to avoid spurious refcount
// traffic off the main thread in debug builds from
// NSCAP_ASSERT_NO_QUERY_NEEDED().
void swapMessage(nsCOMPtr<nsIConsoleMessage>& aRetVal)
{
mMessage.swap(aRetVal);
}
~MessageElement();
private:
nsCOMPtr<nsIConsoleMessage> mMessage;
MessageElement(const MessageElement&) = delete;
MessageElement& operator=(const MessageElement&) = delete;
MessageElement(MessageElement&&) = delete;
MessageElement& operator=(MessageElement&&) = delete;
};
~nsConsoleService();
void ClearMessagesForWindowID(const uint64_t innerID);
void ClearMessages();
mozilla::LinkedList<MessageElement> mMessages;
// The current size of mMessages.
uint32_t mCurrentSize;
// The maximum size of mMessages.
uint32_t mMaximumSize;
// Are we currently delivering a console message on the main thread? If
// so, we suppress incoming messages on the main thread only, to avoid
// infinite repitition.
bool mDeliveringMessage;
// Listeners to notify whenever a new message is logged.
ListenerHash mListeners;
// To serialize interesting methods.
mozilla::Mutex mLock;
};
#endif /* __nsconsoleservice_h__ */

View file

@ -0,0 +1,44 @@
/* -*- 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/. */
#include "nsCrashOnException.h"
#include "nsCOMPtr.h"
#include "nsServiceManagerUtils.h"
#ifdef MOZ_CRASHREPORTER
#include "nsICrashReporter.h"
#endif
namespace mozilla {
static int
ReportException(EXCEPTION_POINTERS* aExceptionInfo)
{
#ifdef MOZ_CRASHREPORTER
nsCOMPtr<nsICrashReporter> cr =
do_GetService("@mozilla.org/toolkit/crash-reporter;1");
if (cr) {
cr->WriteMinidumpForException(aExceptionInfo);
}
#endif
return EXCEPTION_EXECUTE_HANDLER;
}
XPCOM_API(LRESULT)
CallWindowProcCrashProtected(WNDPROC aWndProc, HWND aHWnd, UINT aMsg,
WPARAM aWParam, LPARAM aLParam)
{
MOZ_SEH_TRY {
return aWndProc(aHWnd, aMsg, aWParam, aLParam);
}
MOZ_SEH_EXCEPT(ReportException(GetExceptionInformation())) {
::TerminateProcess(::GetCurrentProcess(), 253);
}
return 0; // not reached
}
}

View file

@ -0,0 +1,23 @@
/* -*- 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 nsCrashOnException_h
#define nsCrashOnException_h
#include <nscore.h>
#include <windows.h>
namespace mozilla {
// Call a given window procedure, and catch any Win32 exceptions raised from it,
// and report them as crashes.
XPCOM_API(LRESULT) CallWindowProcCrashProtected(WNDPROC aWndProc, HWND aHWnd,
UINT aMsg, WPARAM aWParam,
LPARAM aLParam);
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,72 @@
/* -*- 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 nsCycleCollector_h__
#define nsCycleCollector_h__
class nsICycleCollectorListener;
class nsICycleCollectorLogSink;
class nsISupports;
template<class T> struct already_AddRefed;
#include "nsError.h"
#include "nsID.h"
#include "js/SliceBudget.h"
namespace mozilla {
class CycleCollectedJSContext;
} // namespace mozilla
bool nsCycleCollector_init();
void nsCycleCollector_startup();
typedef void (*CC_BeforeUnlinkCallback)(void);
void nsCycleCollector_setBeforeUnlinkCallback(CC_BeforeUnlinkCallback aCB);
typedef void (*CC_ForgetSkippableCallback)(void);
void nsCycleCollector_setForgetSkippableCallback(CC_ForgetSkippableCallback aCB);
void nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes = false,
bool aAsyncSnowWhiteFreeing = false);
void nsCycleCollector_prepareForGarbageCollection();
// If an incremental cycle collection is in progress, finish it.
void nsCycleCollector_finishAnyCurrentCollection();
void nsCycleCollector_dispatchDeferredDeletion(bool aContinuation = false,
bool aPurge = false);
bool nsCycleCollector_doDeferredDeletion();
already_AddRefed<nsICycleCollectorLogSink> nsCycleCollector_createLogSink();
void nsCycleCollector_collect(nsICycleCollectorListener* aManualListener);
void nsCycleCollector_collectSlice(js::SliceBudget& budget,
bool aPreferShorterSlices = false);
uint32_t nsCycleCollector_suspectedCount();
// If aDoCollect is true, then run the GC and CC a few times before
// shutting down the CC completely.
void nsCycleCollector_shutdown(bool aDoCollect = true);
// Helpers for interacting with JS
void nsCycleCollector_registerJSContext(mozilla::CycleCollectedJSContext* aCx);
void nsCycleCollector_forgetJSContext();
#define NS_CYCLE_COLLECTOR_LOGGER_CID \
{ 0x58be81b4, 0x39d2, 0x437c, \
{ 0x94, 0xea, 0xae, 0xde, 0x2c, 0x62, 0x08, 0xd3 } }
extern nsresult
nsCycleCollectorLoggerConstructor(nsISupports* aOuter,
const nsIID& aIID,
void** aInstancePtr);
#endif // nsCycleCollector_h__

View file

@ -0,0 +1,105 @@
/* -*- 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/. */
#include "nsCycleCollectionParticipant.h"
#include "jsapi.h"
#include "jsfriendapi.h"
void
CycleCollectionNoteEdgeNameImpl(nsCycleCollectionTraversalCallback& aCallback,
const char* aName,
uint32_t aFlags)
{
nsAutoCString arrayEdgeName(aName);
if (aFlags & CycleCollectionEdgeNameArrayFlag) {
arrayEdgeName.AppendLiteral("[i]");
}
aCallback.NoteNextEdgeName(arrayEdgeName.get());
}
void
nsScriptObjectTracer::NoteJSChild(JS::GCCellPtr aGCThing, const char* aName,
void* aClosure)
{
nsCycleCollectionTraversalCallback* cb =
static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, aName);
if (mozilla::AddToCCKind(aGCThing.kind())) {
cb->NoteJSChild(aGCThing);
}
}
void
TraceCallbackFunc::Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
void* aClosure) const
{
if (aPtr->unbarrieredGet().isMarkable()) {
mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
}
}
void
TraceCallbackFunc::Trace(JS::Heap<jsid>* aPtr, const char* aName,
void* aClosure) const
{
if (JSID_IS_GCTHING(aPtr->unbarrieredGet())) {
mCallback(JSID_TO_GCTHING(aPtr->unbarrieredGet()), aName, aClosure);
}
}
void
TraceCallbackFunc::Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
void* aClosure) const
{
if (*aPtr) {
mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
}
}
void
TraceCallbackFunc::Trace(JSObject** aPtr, const char* aName,
void* aClosure) const
{
if (*aPtr) {
mCallback(JS::GCCellPtr(*aPtr), aName, aClosure);
}
}
void
TraceCallbackFunc::Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
void* aClosure) const
{
if (*aPtr) {
mCallback(JS::GCCellPtr(aPtr->unbarrieredGetPtr()), aName, aClosure);
}
}
void
TraceCallbackFunc::Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
void* aClosure) const
{
if (*aPtr) {
mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
}
}
void
TraceCallbackFunc::Trace(JS::Heap<JSString*>* aPtr, const char* aName,
void* aClosure) const
{
if (*aPtr) {
mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
}
}
void
TraceCallbackFunc::Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
void* aClosure) const
{
if (*aPtr) {
mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
}
}

607
xpcom/base/nsDebugImpl.cpp Normal file
View file

@ -0,0 +1,607 @@
/* -*- 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/. */
// Chromium headers must come before Mozilla headers.
#include "base/process_util.h"
#include "mozilla/Atomics.h"
#include "nsDebugImpl.h"
#include "nsDebug.h"
#ifdef MOZ_CRASHREPORTER
# include "nsExceptionHandler.h"
#endif
#include "nsString.h"
#include "nsXULAppAPI.h"
#include "prprf.h"
#include "nsError.h"
#include "prerror.h"
#include "prerr.h"
#include "prenv.h"
#ifdef ANDROID
#include <android/log.h>
#endif
#ifdef _WIN32
/* for getenv() */
#include <stdlib.h>
#endif
#include "nsTraceRefcnt.h"
#if defined(XP_UNIX)
#include <signal.h>
#endif
#if defined(XP_WIN)
#include <tchar.h>
#include "nsString.h"
#endif
#if defined(XP_MACOSX) || defined(__DragonFly__) || defined(__FreeBSD__) \
|| defined(__NetBSD__) || defined(__OpenBSD__)
#include <stdbool.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#endif
#if defined(__OpenBSD__)
#include <sys/proc.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(XP_MACOSX)
#define KP_FLAGS kp_proc.p_flag
#elif defined(__DragonFly__)
#define KP_FLAGS kp_flags
#elif defined(__FreeBSD__)
#define KP_FLAGS ki_flag
#elif defined(__OpenBSD__) && !defined(_P_TRACED)
#define KP_FLAGS p_psflags
#define P_TRACED PS_TRACED
#else
#define KP_FLAGS p_flag
#endif
#include "mozilla/mozalloc_abort.h"
static void
Abort(const char* aMsg);
static void
RealBreak();
static void
Break(const char* aMsg);
#if defined(_WIN32)
#include <windows.h>
#include <signal.h>
#include <malloc.h> // for _alloca
#elif defined(XP_UNIX)
#include <stdlib.h>
#endif
using namespace mozilla;
static const char* sMultiprocessDescription = nullptr;
static Atomic<int32_t> gAssertionCount;
NS_IMPL_QUERY_INTERFACE(nsDebugImpl, nsIDebug2)
NS_IMETHODIMP_(MozExternalRefCountType)
nsDebugImpl::AddRef()
{
return 2;
}
NS_IMETHODIMP_(MozExternalRefCountType)
nsDebugImpl::Release()
{
return 1;
}
NS_IMETHODIMP
nsDebugImpl::Assertion(const char* aStr, const char* aExpr,
const char* aFile, int32_t aLine)
{
NS_DebugBreak(NS_DEBUG_ASSERTION, aStr, aExpr, aFile, aLine);
return NS_OK;
}
NS_IMETHODIMP
nsDebugImpl::Warning(const char* aStr, const char* aFile, int32_t aLine)
{
NS_DebugBreak(NS_DEBUG_WARNING, aStr, nullptr, aFile, aLine);
return NS_OK;
}
NS_IMETHODIMP
nsDebugImpl::Break(const char* aFile, int32_t aLine)
{
NS_DebugBreak(NS_DEBUG_BREAK, nullptr, nullptr, aFile, aLine);
return NS_OK;
}
NS_IMETHODIMP
nsDebugImpl::Abort(const char* aFile, int32_t aLine)
{
NS_DebugBreak(NS_DEBUG_ABORT, nullptr, nullptr, aFile, aLine);
return NS_OK;
}
NS_IMETHODIMP
nsDebugImpl::GetIsDebugBuild(bool* aResult)
{
#ifdef DEBUG
*aResult = true;
#else
*aResult = false;
#endif
return NS_OK;
}
NS_IMETHODIMP
nsDebugImpl::GetAssertionCount(int32_t* aResult)
{
*aResult = gAssertionCount;
return NS_OK;
}
NS_IMETHODIMP
nsDebugImpl::GetIsDebuggerAttached(bool* aResult)
{
*aResult = false;
#if defined(XP_WIN)
*aResult = ::IsDebuggerPresent();
#elif defined(XP_MACOSX) || defined(__DragonFly__) || defined(__FreeBSD__) \
|| defined(__NetBSD__) || defined(__OpenBSD__)
// Specify the info we're looking for
int mib[] = {
CTL_KERN,
KERN_PROC,
KERN_PROC_PID,
getpid(),
#if defined(__NetBSD__) || defined(__OpenBSD__)
sizeof(KINFO_PROC),
1,
#endif
};
u_int mibSize = sizeof(mib) / sizeof(int);
KINFO_PROC info;
size_t infoSize = sizeof(info);
memset(&info, 0, infoSize);
if (sysctl(mib, mibSize, &info, &infoSize, nullptr, 0)) {
// if the call fails, default to false
*aResult = false;
return NS_OK;
}
if (info.KP_FLAGS & P_TRACED) {
*aResult = true;
}
#endif
return NS_OK;
}
/* static */ void
nsDebugImpl::SetMultiprocessMode(const char* aDesc)
{
sMultiprocessDescription = aDesc;
}
/**
* Implementation of the nsDebug methods. Note that this code is
* always compiled in, in case some other module that uses it is
* compiled with debugging even if this library is not.
*/
enum nsAssertBehavior
{
NS_ASSERT_UNINITIALIZED,
NS_ASSERT_WARN,
NS_ASSERT_SUSPEND,
NS_ASSERT_STACK,
NS_ASSERT_TRAP,
NS_ASSERT_ABORT,
NS_ASSERT_STACK_AND_ABORT
};
static nsAssertBehavior
GetAssertBehavior()
{
static nsAssertBehavior gAssertBehavior = NS_ASSERT_UNINITIALIZED;
if (gAssertBehavior != NS_ASSERT_UNINITIALIZED) {
return gAssertBehavior;
}
gAssertBehavior = NS_ASSERT_WARN;
const char* assertString = PR_GetEnv("XPCOM_DEBUG_BREAK");
if (!assertString || !*assertString) {
return gAssertBehavior;
}
if (!strcmp(assertString, "warn")) {
return gAssertBehavior = NS_ASSERT_WARN;
}
if (!strcmp(assertString, "suspend")) {
return gAssertBehavior = NS_ASSERT_SUSPEND;
}
if (!strcmp(assertString, "stack")) {
return gAssertBehavior = NS_ASSERT_STACK;
}
if (!strcmp(assertString, "abort")) {
return gAssertBehavior = NS_ASSERT_ABORT;
}
if (!strcmp(assertString, "trap") || !strcmp(assertString, "break")) {
return gAssertBehavior = NS_ASSERT_TRAP;
}
if (!strcmp(assertString, "stack-and-abort")) {
return gAssertBehavior = NS_ASSERT_STACK_AND_ABORT;
}
fprintf(stderr, "Unrecognized value of XPCOM_DEBUG_BREAK\n");
return gAssertBehavior;
}
struct FixedBuffer
{
FixedBuffer() : curlen(0)
{
buffer[0] = '\0';
}
char buffer[500];
uint32_t curlen;
};
static int
StuffFixedBuffer(void* aClosure, const char* aBuf, uint32_t aLen)
{
if (!aLen) {
return 0;
}
FixedBuffer* fb = (FixedBuffer*)aClosure;
// strip the trailing null, we add it again later
if (aBuf[aLen - 1] == '\0') {
--aLen;
}
if (fb->curlen + aLen >= sizeof(fb->buffer)) {
aLen = sizeof(fb->buffer) - fb->curlen - 1;
}
if (aLen) {
memcpy(fb->buffer + fb->curlen, aBuf, aLen);
fb->curlen += aLen;
fb->buffer[fb->curlen] = '\0';
}
return aLen;
}
EXPORT_XPCOM_API(void)
NS_DebugBreak(uint32_t aSeverity, const char* aStr, const char* aExpr,
const char* aFile, int32_t aLine)
{
FixedBuffer nonPIDBuf;
FixedBuffer buf;
const char* sevString = "WARNING";
switch (aSeverity) {
case NS_DEBUG_ASSERTION:
sevString = "###!!! ASSERTION";
break;
case NS_DEBUG_BREAK:
sevString = "###!!! BREAK";
break;
case NS_DEBUG_ABORT:
sevString = "###!!! ABORT";
break;
default:
aSeverity = NS_DEBUG_WARNING;
}
#define PRINT_TO_NONPID_BUFFER(...) PR_sxprintf(StuffFixedBuffer, &nonPIDBuf, __VA_ARGS__)
PRINT_TO_NONPID_BUFFER("%s: ", sevString);
if (aStr) {
PRINT_TO_NONPID_BUFFER("%s: ", aStr);
}
if (aExpr) {
PRINT_TO_NONPID_BUFFER("'%s', ", aExpr);
}
if (aFile) {
PRINT_TO_NONPID_BUFFER("file %s, ", aFile);
}
if (aLine != -1) {
PRINT_TO_NONPID_BUFFER("line %d", aLine);
}
#undef PRINT_TO_NONPID_BUFFER
// Print "[PID]" or "[Desc PID]" at the beginning of the message.
#define PRINT_TO_BUFFER(...) PR_sxprintf(StuffFixedBuffer, &buf, __VA_ARGS__)
PRINT_TO_BUFFER("[");
if (sMultiprocessDescription) {
PRINT_TO_BUFFER("%s ", sMultiprocessDescription);
}
PRINT_TO_BUFFER("%d] %s", base::GetCurrentProcId(), nonPIDBuf.buffer);
#undef PRINT_TO_BUFFER
// errors on platforms without a debugdlg ring a bell on stderr
#if !defined(XP_WIN)
if (aSeverity != NS_DEBUG_WARNING) {
fprintf(stderr, "\07");
}
#endif
#ifdef ANDROID
__android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", buf.buffer);
#endif
// Write the message to stderr unless it's a warning and MOZ_IGNORE_WARNINGS
// is set.
if (!(PR_GetEnv("MOZ_IGNORE_WARNINGS") && aSeverity == NS_DEBUG_WARNING)) {
fprintf(stderr, "%s\n", buf.buffer);
fflush(stderr);
}
switch (aSeverity) {
case NS_DEBUG_WARNING:
return;
case NS_DEBUG_BREAK:
Break(buf.buffer);
return;
case NS_DEBUG_ABORT: {
#if defined(MOZ_CRASHREPORTER)
// Updating crash annotations in the child causes us to do IPC. This can
// really cause trouble if we're asserting from within IPC code. So we
// have to do without the annotations in that case.
if (XRE_IsParentProcess()) {
// Don't include the PID in the crash report annotation to
// allow faceting on crash-stats.mozilla.org.
nsCString note("xpcom_runtime_abort(");
note += nonPIDBuf.buffer;
note += ")";
CrashReporter::AppendAppNotesToCrashReport(note);
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AbortMessage"),
nsDependentCString(nonPIDBuf.buffer));
}
#endif // MOZ_CRASHREPORTER
#if defined(DEBUG) && defined(_WIN32)
RealBreak();
#endif
#if defined(DEBUG)
nsTraceRefcnt::WalkTheStack(stderr);
#endif
Abort(buf.buffer);
return;
}
}
// Now we deal with assertions
gAssertionCount++;
switch (GetAssertBehavior()) {
case NS_ASSERT_WARN:
return;
case NS_ASSERT_SUSPEND:
#ifdef XP_UNIX
fprintf(stderr, "Suspending process; attach with the debugger.\n");
kill(0, SIGSTOP);
#else
Break(buf.buffer);
#endif
return;
case NS_ASSERT_STACK:
nsTraceRefcnt::WalkTheStack(stderr);
return;
case NS_ASSERT_STACK_AND_ABORT:
nsTraceRefcnt::WalkTheStack(stderr);
// Fall through to abort
MOZ_FALLTHROUGH;
case NS_ASSERT_ABORT:
Abort(buf.buffer);
return;
case NS_ASSERT_TRAP:
case NS_ASSERT_UNINITIALIZED: // Default to "trap" behavior
Break(buf.buffer);
return;
}
}
static void
Abort(const char* aMsg)
{
mozalloc_abort(aMsg);
}
static void
RealBreak()
{
#if defined(_WIN32)
::DebugBreak();
#elif defined(XP_MACOSX)
raise(SIGTRAP);
#elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__))
asm("int $3");
#elif defined(__arm__)
asm(
#ifdef __ARM_ARCH_4T__
/* ARMv4T doesn't support the BKPT instruction, so if the compiler target
* is ARMv4T, we want to ensure the assembler will understand that ARMv5T
* instruction, while keeping the resulting object tagged as ARMv4T.
*/
".arch armv5t\n"
".object_arch armv4t\n"
#endif
"BKPT #0");
#elif defined(SOLARIS)
#if defined(__i386__) || defined(__i386) || defined(__x86_64__)
asm("int $3");
#else
raise(SIGTRAP);
#endif
#else
#warning do not know how to break on this platform
#endif
}
// Abort() calls this function, don't call it!
static void
Break(const char* aMsg)
{
#if defined(_WIN32)
static int ignoreDebugger;
if (!ignoreDebugger) {
const char* shouldIgnoreDebugger = getenv("XPCOM_DEBUG_DLG");
ignoreDebugger =
1 + (shouldIgnoreDebugger && !strcmp(shouldIgnoreDebugger, "1"));
}
if ((ignoreDebugger == 2) || !::IsDebuggerPresent()) {
DWORD code = IDRETRY;
/* Create the debug dialog out of process to avoid the crashes caused by
* Windows events leaking into our event loop from an in process dialog.
* We do this by launching windbgdlg.exe (built in xpcom/windbgdlg).
* See http://bugzilla.mozilla.org/show_bug.cgi?id=54792
*/
PROCESS_INFORMATION pi;
STARTUPINFOW si;
wchar_t executable[MAX_PATH];
wchar_t* pName;
memset(&pi, 0, sizeof(pi));
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.wShowWindow = SW_SHOW;
// 2nd arg of CreateProcess is in/out
wchar_t* msgCopy = (wchar_t*)_alloca((strlen(aMsg) + 1) * sizeof(wchar_t));
wcscpy(msgCopy, NS_ConvertUTF8toUTF16(aMsg).get());
if (GetModuleFileNameW(GetModuleHandleW(L"xpcom.dll"), executable, MAX_PATH) &&
(pName = wcsrchr(executable, '\\')) != nullptr &&
wcscpy(pName + 1, L"windbgdlg.exe") &&
CreateProcessW(executable, msgCopy, nullptr, nullptr,
false, DETACHED_PROCESS | NORMAL_PRIORITY_CLASS,
nullptr, nullptr, &si, &pi)) {
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, &code);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
switch (code) {
case IDABORT:
//This should exit us
raise(SIGABRT);
//If we are ignored exit this way..
_exit(3);
case IDIGNORE:
return;
}
}
RealBreak();
#elif defined(XP_MACOSX)
/* Note that we put this Mac OS X test above the GNUC/x86 test because the
* GNUC/x86 test is also true on Intel Mac OS X and we want the PPC/x86
* impls to be the same.
*/
RealBreak();
#elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__))
RealBreak();
#elif defined(__arm__)
RealBreak();
#elif defined(SOLARIS)
RealBreak();
#else
#warning do not know how to break on this platform
#endif
}
nsresult
nsDebugImpl::Create(nsISupports* aOuter, const nsIID& aIID, void** aInstancePtr)
{
static const nsDebugImpl* sImpl;
if (NS_WARN_IF(aOuter)) {
return NS_ERROR_NO_AGGREGATION;
}
if (!sImpl) {
sImpl = new nsDebugImpl();
}
return const_cast<nsDebugImpl*>(sImpl)->QueryInterface(aIID, aInstancePtr);
}
////////////////////////////////////////////////////////////////////////////////
nsresult
NS_ErrorAccordingToNSPR()
{
PRErrorCode err = PR_GetError();
switch (err) {
case PR_OUT_OF_MEMORY_ERROR: return NS_ERROR_OUT_OF_MEMORY;
case PR_WOULD_BLOCK_ERROR: return NS_BASE_STREAM_WOULD_BLOCK;
case PR_FILE_NOT_FOUND_ERROR: return NS_ERROR_FILE_NOT_FOUND;
case PR_READ_ONLY_FILESYSTEM_ERROR: return NS_ERROR_FILE_READ_ONLY;
case PR_NOT_DIRECTORY_ERROR: return NS_ERROR_FILE_NOT_DIRECTORY;
case PR_IS_DIRECTORY_ERROR: return NS_ERROR_FILE_IS_DIRECTORY;
case PR_LOOP_ERROR: return NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
case PR_FILE_EXISTS_ERROR: return NS_ERROR_FILE_ALREADY_EXISTS;
case PR_FILE_IS_LOCKED_ERROR: return NS_ERROR_FILE_IS_LOCKED;
case PR_FILE_TOO_BIG_ERROR: return NS_ERROR_FILE_TOO_BIG;
case PR_NO_DEVICE_SPACE_ERROR: return NS_ERROR_FILE_NO_DEVICE_SPACE;
case PR_NAME_TOO_LONG_ERROR: return NS_ERROR_FILE_NAME_TOO_LONG;
case PR_DIRECTORY_NOT_EMPTY_ERROR: return NS_ERROR_FILE_DIR_NOT_EMPTY;
case PR_NO_ACCESS_RIGHTS_ERROR: return NS_ERROR_FILE_ACCESS_DENIED;
default: return NS_ERROR_FAILURE;
}
}
void
NS_ABORT_OOM(size_t aSize)
{
#if defined(MOZ_CRASHREPORTER)
CrashReporter::AnnotateOOMAllocationSize(aSize);
#endif
MOZ_CRASH("OOM");
}

41
xpcom/base/nsDebugImpl.h Normal file
View file

@ -0,0 +1,41 @@
/* -*- 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 nsDebugImpl_h
#define nsDebugImpl_h
#include "nsIDebug2.h"
class nsDebugImpl : public nsIDebug2
{
public:
nsDebugImpl() = default;
NS_DECL_ISUPPORTS
NS_DECL_NSIDEBUG2
static nsresult Create(nsISupports* aOuter, const nsIID& aIID,
void** aInstancePtr);
/*
* Inform nsDebugImpl that we're in multiprocess mode.
*
* If aDesc is not nullptr, the string it points to must be
* statically-allocated (i.e., it must be a string literal).
*/
static void SetMultiprocessMode(const char* aDesc);
};
#define NS_DEBUG_CONTRACTID "@mozilla.org/xpcom/debug;1"
#define NS_DEBUG_CID \
{ /* cb6cdb94-e417-4601-b4a5-f991bf41453d */ \
0xcb6cdb94, \
0xe417, \
0x4601, \
{0xb4, 0xa5, 0xf9, 0x91, 0xbf, 0x41, 0x45, 0x3d} \
}
#endif // nsDebugImpl_h

513
xpcom/base/nsDumpUtils.cpp Normal file
View file

@ -0,0 +1,513 @@
/* -*- 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/. */
#include "nsDumpUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "prenv.h"
#include <errno.h>
#include "mozilla/Services.h"
#include "nsIObserverService.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Unused.h"
#ifdef XP_UNIX // {
#include "mozilla/Preferences.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
using namespace mozilla;
/*
* The following code supports triggering a registered callback upon
* receiving a specific signal.
*
* Take about:memory for example, we register
* 1. doGCCCDump for doMemoryReport
* 2. doMemoryReport for sDumpAboutMemorySignum(SIGRTMIN)
* and sDumpAboutMemoryAfterMMUSignum(SIGRTMIN+1).
*
* When we receive one of these signals, we write the signal number to a pipe.
* The IO thread then notices that the pipe has been written to, and kicks off
* the appropriate task on the main thread.
*
* This scheme is similar to using signalfd(), except it's portable and it
* doesn't require the use of sigprocmask, which is problematic because it
* masks signals received by child processes.
*
* In theory, we could use Chromium's MessageLoopForIO::CatchSignal() for this.
* But that uses libevent, which does not handle the realtime signals (bug
* 794074).
*/
// This is the write-end of a pipe that we use to notice when a
// specific signal occurs.
static Atomic<int> sDumpPipeWriteFd(-1);
const char* const FifoWatcher::kPrefName =
"memory_info_dumper.watch_fifo.enabled";
static void
DumpSignalHandler(int aSignum)
{
// This is a signal handler, so everything in here needs to be
// async-signal-safe. Be careful!
if (sDumpPipeWriteFd != -1) {
uint8_t signum = static_cast<int>(aSignum);
Unused << write(sDumpPipeWriteFd, &signum, sizeof(signum));
}
}
NS_IMPL_ISUPPORTS(FdWatcher, nsIObserver);
void
FdWatcher::Init()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
os->AddObserver(this, "xpcom-shutdown", /* ownsWeak = */ false);
XRE_GetIOMessageLoop()->PostTask(NewRunnableMethod(this, &FdWatcher::StartWatching));
}
// Implementations may call this function multiple times if they ensure that
// it's safe to call OpenFd() multiple times and they call StopWatching()
// first.
void
FdWatcher::StartWatching()
{
MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
MOZ_ASSERT(mFd == -1);
mFd = OpenFd();
if (mFd == -1) {
LOG("FdWatcher: OpenFd failed.");
return;
}
MessageLoopForIO::current()->WatchFileDescriptor(
mFd, /* persistent = */ true,
MessageLoopForIO::WATCH_READ,
&mReadWatcher, this);
}
// Since implementations can call StartWatching() multiple times, they can of
// course call StopWatching() multiple times.
void
FdWatcher::StopWatching()
{
MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
mReadWatcher.StopWatchingFileDescriptor();
if (mFd != -1) {
close(mFd);
mFd = -1;
}
}
StaticRefPtr<SignalPipeWatcher> SignalPipeWatcher::sSingleton;
/* static */ SignalPipeWatcher*
SignalPipeWatcher::GetSingleton()
{
if (!sSingleton) {
sSingleton = new SignalPipeWatcher();
sSingleton->Init();
ClearOnShutdown(&sSingleton);
}
return sSingleton;
}
void
SignalPipeWatcher::RegisterCallback(uint8_t aSignal,
PipeCallback aCallback)
{
MutexAutoLock lock(mSignalInfoLock);
for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); ++i) {
if (mSignalInfo[i].mSignal == aSignal) {
LOG("Register Signal(%d) callback failed! (DUPLICATE)", aSignal);
return;
}
}
SignalInfo signalInfo = { aSignal, aCallback };
mSignalInfo.AppendElement(signalInfo);
RegisterSignalHandler(signalInfo.mSignal);
}
void
SignalPipeWatcher::RegisterSignalHandler(uint8_t aSignal)
{
struct sigaction action;
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
action.sa_handler = DumpSignalHandler;
if (aSignal) {
if (sigaction(aSignal, &action, nullptr)) {
LOG("SignalPipeWatcher failed to register sig %d.", aSignal);
}
} else {
MutexAutoLock lock(mSignalInfoLock);
for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); i++) {
if (sigaction(mSignalInfo[i].mSignal, &action, nullptr)) {
LOG("SignalPipeWatcher failed to register signal(%d) "
"dump signal handler.", mSignalInfo[i].mSignal);
}
}
}
}
SignalPipeWatcher::~SignalPipeWatcher()
{
if (sDumpPipeWriteFd != -1) {
StopWatching();
}
}
int
SignalPipeWatcher::OpenFd()
{
MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
// Create a pipe. When we receive a signal in our signal handler, we'll
// write the signum to the write-end of this pipe.
int pipeFds[2];
if (pipe(pipeFds)) {
LOG("SignalPipeWatcher failed to create pipe.");
return -1;
}
// Close this pipe on calls to exec().
fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC);
fcntl(pipeFds[1], F_SETFD, FD_CLOEXEC);
int readFd = pipeFds[0];
sDumpPipeWriteFd = pipeFds[1];
RegisterSignalHandler();
return readFd;
}
void
SignalPipeWatcher::StopWatching()
{
MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
// Close sDumpPipeWriteFd /after/ setting the fd to -1.
// Otherwise we have the (admittedly far-fetched) race where we
//
// 1) close sDumpPipeWriteFd
// 2) open a new fd with the same number as sDumpPipeWriteFd
// had.
// 3) receive a signal, then write to the fd.
int pipeWriteFd = sDumpPipeWriteFd.exchange(-1);
close(pipeWriteFd);
FdWatcher::StopWatching();
}
void
SignalPipeWatcher::OnFileCanReadWithoutBlocking(int aFd)
{
MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
uint8_t signum;
ssize_t numReceived = read(aFd, &signum, sizeof(signum));
if (numReceived != sizeof(signum)) {
LOG("Error reading from buffer in "
"SignalPipeWatcher::OnFileCanReadWithoutBlocking.");
return;
}
{
MutexAutoLock lock(mSignalInfoLock);
for (SignalInfoArray::index_type i = 0; i < mSignalInfo.Length(); i++) {
if (signum == mSignalInfo[i].mSignal) {
mSignalInfo[i].mCallback(signum);
return;
}
}
}
LOG("SignalPipeWatcher got unexpected signum.");
}
StaticRefPtr<FifoWatcher> FifoWatcher::sSingleton;
/* static */ FifoWatcher*
FifoWatcher::GetSingleton()
{
if (!sSingleton) {
nsAutoCString dirPath;
Preferences::GetCString(
"memory_info_dumper.watch_fifo.directory", &dirPath);
sSingleton = new FifoWatcher(dirPath);
sSingleton->Init();
ClearOnShutdown(&sSingleton);
}
return sSingleton;
}
/* static */ bool
FifoWatcher::MaybeCreate()
{
MOZ_ASSERT(NS_IsMainThread());
if (!XRE_IsParentProcess()) {
// We want this to be main-process only, since two processes can't listen
// to the same fifo.
return false;
}
if (!Preferences::GetBool(kPrefName, false)) {
LOG("Fifo watcher disabled via pref.");
return false;
}
// The FifoWatcher is held alive by the observer service.
if (!sSingleton) {
GetSingleton();
}
return true;
}
void
FifoWatcher::RegisterCallback(const nsCString& aCommand, FifoCallback aCallback)
{
MutexAutoLock lock(mFifoInfoLock);
for (FifoInfoArray::index_type i = 0; i < mFifoInfo.Length(); ++i) {
if (mFifoInfo[i].mCommand.Equals(aCommand)) {
LOG("Register command(%s) callback failed! (DUPLICATE)", aCommand.get());
return;
}
}
FifoInfo aFifoInfo = { aCommand, aCallback };
mFifoInfo.AppendElement(aFifoInfo);
}
FifoWatcher::~FifoWatcher()
{
}
int
FifoWatcher::OpenFd()
{
// If the memory_info_dumper.directory pref is specified, put the fifo
// there. Otherwise, put it into the system's tmp directory.
nsCOMPtr<nsIFile> file;
nsresult rv;
if (mDirPath.Length() > 0) {
rv = XRE_GetFileFromPath(mDirPath.get(), getter_AddRefs(file));
if (NS_FAILED(rv)) {
LOG("FifoWatcher failed to open file \"%s\"", mDirPath.get());
return -1;
}
} else {
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return -1;
}
}
rv = file->AppendNative(NS_LITERAL_CSTRING("debug_info_trigger"));
if (NS_WARN_IF(NS_FAILED(rv))) {
return -1;
}
nsAutoCString path;
rv = file->GetNativePath(path);
if (NS_WARN_IF(NS_FAILED(rv))) {
return -1;
}
// unlink might fail because the file doesn't exist, or for other reasons.
// But we don't care it fails; any problems will be detected later, when we
// try to mkfifo or open the file.
if (unlink(path.get())) {
LOG("FifoWatcher::OpenFifo unlink failed; errno=%d. "
"Continuing despite error.", errno);
}
if (mkfifo(path.get(), 0766)) {
LOG("FifoWatcher::OpenFifo mkfifo failed; errno=%d", errno);
return -1;
}
#ifdef ANDROID
// Android runs with a umask, so we need to chmod our fifo to make it
// world-writable.
chmod(path.get(), 0666);
#endif
int fd;
do {
// The fifo will block until someone else has written to it. In
// particular, open() will block until someone else has opened it for
// writing! We want open() to succeed and read() to block, so we open
// with NONBLOCK and then fcntl that away.
fd = open(path.get(), O_RDONLY | O_NONBLOCK);
} while (fd == -1 && errno == EINTR);
if (fd == -1) {
LOG("FifoWatcher::OpenFifo open failed; errno=%d", errno);
return -1;
}
// Make fd blocking now that we've opened it.
if (fcntl(fd, F_SETFL, 0)) {
close(fd);
return -1;
}
return fd;
}
void
FifoWatcher::OnFileCanReadWithoutBlocking(int aFd)
{
MOZ_ASSERT(XRE_GetIOMessageLoop() == MessageLoopForIO::current());
char buf[1024];
int nread;
do {
// sizeof(buf) - 1 to leave space for the null-terminator.
nread = read(aFd, buf, sizeof(buf));
} while (nread == -1 && errno == EINTR);
if (nread == -1) {
// We want to avoid getting into a situation where
// OnFileCanReadWithoutBlocking is called in an infinite loop, so when
// something goes wrong, stop watching the fifo altogether.
LOG("FifoWatcher hit an error (%d) and is quitting.", errno);
StopWatching();
return;
}
if (nread == 0) {
// If we get EOF, that means that the other side closed the fifo. We need
// to close and re-open the fifo; if we don't,
// OnFileCanWriteWithoutBlocking will be called in an infinite loop.
LOG("FifoWatcher closing and re-opening fifo.");
StopWatching();
StartWatching();
return;
}
nsAutoCString inputStr;
inputStr.Append(buf, nread);
// Trimming whitespace is important because if you do
// |echo "foo" >> debug_info_trigger|,
// it'll actually write "foo\n" to the fifo.
inputStr.Trim("\b\t\r\n");
{
MutexAutoLock lock(mFifoInfoLock);
for (FifoInfoArray::index_type i = 0; i < mFifoInfo.Length(); i++) {
const nsCString commandStr = mFifoInfo[i].mCommand;
if (inputStr == commandStr.get()) {
mFifoInfo[i].mCallback(inputStr);
return;
}
}
}
LOG("Got unexpected value from fifo; ignoring it.");
}
#endif // XP_UNIX }
// In Android case, this function will open a file named aFilename under
// /data/local/tmp/"aFoldername".
// Otherwise, it will open a file named aFilename under "NS_OS_TEMP_DIR".
/* static */ nsresult
nsDumpUtils::OpenTempFile(const nsACString& aFilename, nsIFile** aFile,
const nsACString& aFoldername, Mode aMode)
{
#ifdef ANDROID
// For Android, first try the downloads directory which is world-readable
// rather than the temp directory which is not.
if (!*aFile) {
char* env = PR_GetEnv("DOWNLOADS_DIRECTORY");
if (env) {
NS_NewNativeLocalFile(nsCString(env), /* followLinks = */ true, aFile);
}
}
#endif
nsresult rv;
if (!*aFile) {
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, aFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
#ifdef ANDROID
// /data/local/tmp is a true tmp directory; anyone can create a file there,
// but only the user which created the file can remove it. We want non-root
// users to be able to remove these files, so we write them into a
// subdirectory of the temp directory and chmod 777 that directory.
if (aFoldername != EmptyCString()) {
rv = (*aFile)->AppendNative(aFoldername);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// It's OK if this fails; that probably just means that the directory
// already exists.
Unused << (*aFile)->Create(nsIFile::DIRECTORY_TYPE, 0777);
nsAutoCString dirPath;
rv = (*aFile)->GetNativePath(dirPath);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
while (chmod(dirPath.get(), 0777) == -1 && errno == EINTR) {
}
}
#endif
nsCOMPtr<nsIFile> file(*aFile);
rv = file->AppendNative(aFilename);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (aMode == CREATE_UNIQUE) {
rv = file->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0666);
} else {
rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0666);
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
#ifdef ANDROID
// Make this file world-read/writable; the permissions passed to the
// CreateUnique call above are not sufficient on Android, which runs with a
// umask.
nsAutoCString path;
rv = file->GetNativePath(path);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
while (chmod(path.get(), 0666) == -1 && errno == EINTR) {
}
#endif
return NS_OK;
}

203
xpcom/base/nsDumpUtils.h Normal file
View file

@ -0,0 +1,203 @@
/* -*- 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_nsDumpUtils_h
#define mozilla_nsDumpUtils_h
#include "nsIObserver.h"
#include "base/message_loop.h"
#include "nsXULAppAPI.h"
#include "nsThreadUtils.h"
#include "mozilla/Mutex.h"
#include "mozilla/StaticPtr.h"
#include "nsTArray.h"
#ifdef LOG
#undef LOG
#endif
#ifdef ANDROID
#include "android/log.h"
#define LOG(...) __android_log_print(ANDROID_LOG_INFO, "Gecko:DumpUtils", ## __VA_ARGS__)
#else
#define LOG(...)
#endif
#ifdef XP_UNIX // {
/**
* Abstract base class for something which watches an fd and takes action when
* we can read from it without blocking.
*/
class FdWatcher
: public MessageLoopForIO::Watcher
, public nsIObserver
{
protected:
MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
int mFd;
virtual ~FdWatcher()
{
// StopWatching should have run.
MOZ_ASSERT(mFd == -1);
}
public:
FdWatcher()
: mFd(-1)
{
MOZ_ASSERT(NS_IsMainThread());
}
/**
* Open the fd to watch. If we encounter an error, return -1.
*/
virtual int OpenFd() = 0;
/**
* Called when you can read() from the fd without blocking. Note that this
* function is also called when you're at eof (read() returns 0 in this case).
*/
virtual void OnFileCanReadWithoutBlocking(int aFd) override = 0;
virtual void OnFileCanWriteWithoutBlocking(int aFd) override {};
NS_DECL_THREADSAFE_ISUPPORTS
/**
* Initialize this object. This should be called right after the object is
* constructed. (This would go in the constructor, except we interact with
* XPCOM, which we can't do from a constructor because our refcount is 0 at
* that point.)
*/
void Init();
// Implementations may call this function multiple times if they ensure that
virtual void StartWatching();
// Since implementations can call StartWatching() multiple times, they can of
// course call StopWatching() multiple times.
virtual void StopWatching();
NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) override
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
XRE_GetIOMessageLoop()->PostTask(mozilla::NewRunnableMethod(this, &FdWatcher::StopWatching));
return NS_OK;
}
};
typedef void (*FifoCallback)(const nsCString& aInputStr);
struct FifoInfo
{
nsCString mCommand;
FifoCallback mCallback;
};
typedef nsTArray<FifoInfo> FifoInfoArray;
class FifoWatcher : public FdWatcher
{
public:
/**
* The name of the preference used to enable/disable the FifoWatcher.
*/
static const char* const kPrefName;
static FifoWatcher* GetSingleton();
static bool MaybeCreate();
void RegisterCallback(const nsCString& aCommand, FifoCallback aCallback);
virtual ~FifoWatcher();
virtual int OpenFd();
virtual void OnFileCanReadWithoutBlocking(int aFd);
private:
nsAutoCString mDirPath;
static mozilla::StaticRefPtr<FifoWatcher> sSingleton;
explicit FifoWatcher(nsCString aPath)
: mDirPath(aPath)
, mFifoInfoLock("FifoWatcher.mFifoInfoLock")
{
}
mozilla::Mutex mFifoInfoLock; // protects mFifoInfo
FifoInfoArray mFifoInfo;
};
typedef void (*PipeCallback)(const uint8_t aRecvSig);
struct SignalInfo
{
uint8_t mSignal;
PipeCallback mCallback;
};
typedef nsTArray<SignalInfo> SignalInfoArray;
class SignalPipeWatcher : public FdWatcher
{
public:
static SignalPipeWatcher* GetSingleton();
void RegisterCallback(uint8_t aSignal, PipeCallback aCallback);
void RegisterSignalHandler(uint8_t aSignal = 0);
virtual ~SignalPipeWatcher();
virtual int OpenFd();
virtual void StopWatching();
virtual void OnFileCanReadWithoutBlocking(int aFd);
private:
static mozilla::StaticRefPtr<SignalPipeWatcher> sSingleton;
SignalPipeWatcher()
: mSignalInfoLock("SignalPipeWatcher.mSignalInfoLock")
{
MOZ_ASSERT(NS_IsMainThread());
}
mozilla::Mutex mSignalInfoLock; // protects mSignalInfo
SignalInfoArray mSignalInfo;
};
#endif // XP_UNIX }
class nsDumpUtils
{
public:
enum Mode {
CREATE,
CREATE_UNIQUE
};
/**
* This function creates a new unique file based on |aFilename| in a
* world-readable temp directory. This is the system temp directory
* or, in the case of Android, the downloads directory. If |aFile| is
* non-null, it is assumed to point to a folder, and that folder is used
* instead.
*/
static nsresult OpenTempFile(const nsACString& aFilename,
nsIFile** aFile,
const nsACString& aFoldername = EmptyCString(),
Mode aMode = CREATE_UNIQUE);
};
#endif

219
xpcom/base/nsError.h Normal file
View file

@ -0,0 +1,219 @@
/* -*- 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 nsError_h__
#define nsError_h__
#ifndef __cplusplus
#error nsError.h no longer supports C sources
#endif
#include "mozilla/Attributes.h"
#include "mozilla/Likely.h"
#include <stdint.h>
/*
* To add error code to your module, you need to do the following:
*
* 1) Add a module offset code. Add yours to the bottom of the list
* right below this comment, adding 1.
*
* 2) In your module, define a header file which uses one of the
* NE_ERROR_GENERATExxxxxx macros. Some examples below:
*
* #define NS_ERROR_MYMODULE_MYERROR1 NS_ERROR_GENERATE(NS_ERROR_SEVERITY_ERROR,NS_ERROR_MODULE_MYMODULE,1)
* #define NS_ERROR_MYMODULE_MYERROR2 NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_MYMODULE,2)
* #define NS_ERROR_MYMODULE_MYERROR3 NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_MYMODULE,3)
*
*/
/**
* @name Standard Module Offset Code. Each Module should identify a unique number
* and then all errors associated with that module become offsets from the
* base associated with that module id. There are 16 bits of code bits for
* each module.
*/
#define NS_ERROR_MODULE_XPCOM 1
#define NS_ERROR_MODULE_BASE 2
#define NS_ERROR_MODULE_GFX 3
#define NS_ERROR_MODULE_WIDGET 4
#define NS_ERROR_MODULE_CALENDAR 5
#define NS_ERROR_MODULE_NETWORK 6
#define NS_ERROR_MODULE_PLUGINS 7
#define NS_ERROR_MODULE_LAYOUT 8
#define NS_ERROR_MODULE_HTMLPARSER 9
#define NS_ERROR_MODULE_RDF 10
#define NS_ERROR_MODULE_UCONV 11
#define NS_ERROR_MODULE_REG 12
#define NS_ERROR_MODULE_FILES 13
#define NS_ERROR_MODULE_DOM 14
#define NS_ERROR_MODULE_IMGLIB 15
#define NS_ERROR_MODULE_MAILNEWS 16
#define NS_ERROR_MODULE_EDITOR 17
#define NS_ERROR_MODULE_XPCONNECT 18
#define NS_ERROR_MODULE_PROFILE 19
#define NS_ERROR_MODULE_LDAP 20
#define NS_ERROR_MODULE_SECURITY 21
#define NS_ERROR_MODULE_DOM_XPATH 22
/* 23 used to be NS_ERROR_MODULE_DOM_RANGE (see bug 711047) */
#define NS_ERROR_MODULE_URILOADER 24
#define NS_ERROR_MODULE_CONTENT 25
#define NS_ERROR_MODULE_PYXPCOM 26
#define NS_ERROR_MODULE_XSLT 27
#define NS_ERROR_MODULE_IPC 28
#define NS_ERROR_MODULE_SVG 29
#define NS_ERROR_MODULE_STORAGE 30
#define NS_ERROR_MODULE_SCHEMA 31
#define NS_ERROR_MODULE_DOM_FILE 32
#define NS_ERROR_MODULE_DOM_INDEXEDDB 33
#define NS_ERROR_MODULE_DOM_FILEHANDLE 34
#define NS_ERROR_MODULE_SIGNED_JAR 35
#define NS_ERROR_MODULE_DOM_FILESYSTEM 36
#define NS_ERROR_MODULE_DOM_BLUETOOTH 37
#define NS_ERROR_MODULE_SIGNED_APP 38
#define NS_ERROR_MODULE_DOM_ANIM 39
#define NS_ERROR_MODULE_DOM_PUSH 40
#define NS_ERROR_MODULE_DOM_MEDIA 41
/* NS_ERROR_MODULE_GENERAL should be used by modules that do not
* care if return code values overlap. Callers of methods that
* return such codes should be aware that they are not
* globally unique. Implementors should be careful about blindly
* returning codes from other modules that might also use
* the generic base.
*/
#define NS_ERROR_MODULE_GENERAL 51
/**
* @name Severity Code. This flag identifies the level of warning
*/
#define NS_ERROR_SEVERITY_SUCCESS 0
#define NS_ERROR_SEVERITY_ERROR 1
/**
* @name Mozilla Code. This flag separates consumers of mozilla code
* from the native platform
*/
#define NS_ERROR_MODULE_BASE_OFFSET 0x45
/* Helpers for defining our enum, to be undef'd later */
#define SUCCESS_OR_FAILURE(sev, module, code) \
((uint32_t)(sev) << 31) | \
((uint32_t)(module + NS_ERROR_MODULE_BASE_OFFSET) << 16) | \
(uint32_t)(code)
#define SUCCESS(code) \
SUCCESS_OR_FAILURE(NS_ERROR_SEVERITY_SUCCESS, MODULE, code)
#define FAILURE(code) \
SUCCESS_OR_FAILURE(NS_ERROR_SEVERITY_ERROR, MODULE, code)
/**
* @name Standard return values
*/
/*@{*/
enum class nsresult : uint32_t
{
#undef ERROR
#define ERROR(key, val) key = val
#include "ErrorList.h"
#undef ERROR
};
/*
* enum classes don't place their initializers in the global scope, so we need
* constants for compatibility with old code.
*/
const nsresult
#define ERROR(key, val) key = nsresult::key
#include "ErrorList.h"
#undef ERROR
;
#undef SUCCESS_OR_FAILURE
#undef SUCCESS
#undef FAILURE
/**
* @name Standard Error Handling Macros
* @return 0 or 1 (false/true with bool type for C++)
*/
inline uint32_t
NS_FAILED_impl(nsresult aErr)
{
return static_cast<uint32_t>(aErr) & 0x80000000;
}
#define NS_FAILED(_nsresult) ((bool)MOZ_UNLIKELY(NS_FAILED_impl(_nsresult)))
#define NS_SUCCEEDED(_nsresult) ((bool)MOZ_LIKELY(!NS_FAILED_impl(_nsresult)))
/* Check that our enum type is actually uint32_t as expected */
static_assert(((nsresult)0) < ((nsresult)-1),
"nsresult must be an unsigned type");
static_assert(sizeof(nsresult) == sizeof(uint32_t),
"nsresult must be 32 bits");
#define MOZ_ALWAYS_SUCCEEDS(expr) MOZ_ALWAYS_TRUE(NS_SUCCEEDED(expr))
/**
* @name Standard Error Generating Macros
*/
#define NS_ERROR_GENERATE(sev, module, code) \
(nsresult)(((uint32_t)(sev) << 31) | \
((uint32_t)(module + NS_ERROR_MODULE_BASE_OFFSET) << 16) | \
((uint32_t)(code)))
#define NS_ERROR_GENERATE_SUCCESS(module, code) \
NS_ERROR_GENERATE(NS_ERROR_SEVERITY_SUCCESS, module, code)
#define NS_ERROR_GENERATE_FAILURE(module, code) \
NS_ERROR_GENERATE(NS_ERROR_SEVERITY_ERROR, module, code)
/*
* This will return the nsresult corresponding to the most recent NSPR failure
* returned by PR_GetError.
*
***********************************************************************
* Do not depend on this function. It will be going away!
***********************************************************************
*/
extern nsresult
NS_ErrorAccordingToNSPR();
/**
* @name Standard Macros for retrieving error bits
*/
inline constexpr uint16_t
NS_ERROR_GET_CODE(nsresult aErr)
{
return uint32_t(aErr) & 0xffff;
}
inline constexpr uint16_t
NS_ERROR_GET_MODULE(nsresult aErr)
{
return ((uint32_t(aErr) >> 16) - NS_ERROR_MODULE_BASE_OFFSET) & 0x1fff;
}
inline bool
NS_ERROR_GET_SEVERITY(nsresult aErr)
{
return uint32_t(aErr) >> 31;
}
#ifdef _MSC_VER
#pragma warning(disable: 4251) /* 'nsCOMPtr<class nsIInputStream>' needs to have dll-interface to be used by clients of class 'nsInputStream' */
#pragma warning(disable: 4275) /* non dll-interface class 'nsISupports' used as base for dll-interface class 'nsIRDFNode' */
#endif
#endif

View file

@ -0,0 +1,50 @@
/* -*- 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/. */
#include "nsErrorService.h"
#include "nsCRTGlue.h"
#include "nsAutoPtr.h"
NS_IMPL_ISUPPORTS(nsErrorService, nsIErrorService)
nsresult
nsErrorService::Create(nsISupports* aOuter, const nsIID& aIID,
void** aInstancePtr)
{
if (NS_WARN_IF(aOuter)) {
return NS_ERROR_NO_AGGREGATION;
}
RefPtr<nsErrorService> serv = new nsErrorService();
return serv->QueryInterface(aIID, aInstancePtr);
}
NS_IMETHODIMP
nsErrorService::RegisterErrorStringBundle(int16_t aErrorModule,
const char* aStringBundleURL)
{
mErrorStringBundleURLMap.Put(aErrorModule, new nsCString(aStringBundleURL));
return NS_OK;
}
NS_IMETHODIMP
nsErrorService::UnregisterErrorStringBundle(int16_t aErrorModule)
{
mErrorStringBundleURLMap.Remove(aErrorModule);
return NS_OK;
}
NS_IMETHODIMP
nsErrorService::GetErrorStringBundle(int16_t aErrorModule, char** aResult)
{
nsCString* bundleURL = mErrorStringBundleURLMap.Get(aErrorModule);
if (!bundleURL) {
return NS_ERROR_FAILURE;
}
*aResult = ToNewCString(*bundleURL);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,37 @@
/* -*- 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 nsErrorService_h__
#define nsErrorService_h__
#include "mozilla/Attributes.h"
#include "nsIErrorService.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
class nsErrorService final : public nsIErrorService
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIERRORSERVICE
nsErrorService()
{
}
static nsresult
Create(nsISupports* aOuter, const nsIID& aIID, void** aInstancePtr);
private:
~nsErrorService()
{
}
nsClassHashtable<nsUint32HashKey, nsCString> mErrorStringBundleURLMap;
};
#endif // nsErrorService_h__

View file

@ -0,0 +1,111 @@
/* -*- 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/. */
#include "nsGZFileWriter.h"
#include "nsIFile.h"
#include "nsString.h"
#include "zlib.h"
#ifdef XP_WIN
#include <io.h>
#define _dup dup
#else
#include <unistd.h>
#endif
NS_IMPL_ISUPPORTS(nsGZFileWriter, nsIGZFileWriter)
nsGZFileWriter::nsGZFileWriter(Operation aMode)
: mMode(aMode)
, mInitialized(false)
, mFinished(false)
{
}
nsGZFileWriter::~nsGZFileWriter()
{
if (mInitialized && !mFinished) {
Finish();
}
}
NS_IMETHODIMP
nsGZFileWriter::Init(nsIFile* aFile)
{
if (NS_WARN_IF(mInitialized) ||
NS_WARN_IF(mFinished)) {
return NS_ERROR_FAILURE;
}
// Get a FILE out of our nsIFile. Convert that into a file descriptor which
// gzip can own. Then close our FILE, leaving only gzip's fd open.
FILE* file;
nsresult rv = aFile->OpenANSIFileDesc(mMode == Create ? "wb" : "ab", &file);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return InitANSIFileDesc(file);
}
NS_IMETHODIMP
nsGZFileWriter::InitANSIFileDesc(FILE* aFile)
{
mGZFile = gzdopen(dup(fileno(aFile)), mMode == Create ? "wb" : "ab");
fclose(aFile);
// gzdopen returns nullptr on error.
if (NS_WARN_IF(!mGZFile)) {
return NS_ERROR_FAILURE;
}
mInitialized = true;
return NS_OK;
}
NS_IMETHODIMP
nsGZFileWriter::Write(const nsACString& aStr)
{
if (NS_WARN_IF(!mInitialized) ||
NS_WARN_IF(mFinished)) {
return NS_ERROR_FAILURE;
}
// gzwrite uses a return value of 0 to indicate failure. Otherwise, it
// returns the number of uncompressed bytes written. To ensure we can
// distinguish between success and failure, don't call gzwrite when we have 0
// bytes to write.
if (aStr.IsEmpty()) {
return NS_OK;
}
// gzwrite never does a short write -- that is, the return value should
// always be either 0 or aStr.Length(), and we shouldn't have to call it
// multiple times in order to get it to read the whole buffer.
int rv = gzwrite(mGZFile, aStr.BeginReading(), aStr.Length());
if (NS_WARN_IF(rv != static_cast<int>(aStr.Length()))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
nsGZFileWriter::Finish()
{
if (NS_WARN_IF(!mInitialized) ||
NS_WARN_IF(mFinished)) {
return NS_ERROR_FAILURE;
}
mFinished = true;
gzclose(mGZFile);
// Ignore errors from gzclose; it's not like there's anything we can do about
// it, at this point!
return NS_OK;
}

View file

@ -0,0 +1,55 @@
/* -*- 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 nsGZFileWriter_h
#define nsGZFileWriter_h
#include "nsIGZFileWriter.h"
#include "zlib.h"
/**
* A simple class for writing .gz files.
*/
class nsGZFileWriter final : public nsIGZFileWriter
{
virtual ~nsGZFileWriter();
public:
enum Operation {
Append,
Create
};
explicit nsGZFileWriter(Operation aMode = Create);
NS_DECL_ISUPPORTS
NS_DECL_NSIGZFILEWRITER
/**
* nsIGZFileWriter exposes two non-virtual overloads of Write(). We
* duplicate them here so that you can call these overloads on a pointer to
* the concrete nsGZFileWriter class.
*/
MOZ_MUST_USE nsresult Write(const char* aStr)
{
return nsIGZFileWriter::Write(aStr);
}
MOZ_MUST_USE nsresult Write(const char* aStr, uint32_t aLen)
{
return nsIGZFileWriter::Write(aStr, aLen);
}
private:
Operation mMode;
bool mInitialized;
bool mFinished;
gzFile mGZFile;
};
#endif

View file

@ -0,0 +1,18 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
/*
* Used by the console service to notify listeners of new console messages.
*/
#include "nsISupports.idl"
interface nsIConsoleMessage;
[scriptable, function, uuid(35c400a4-5792-438c-b915-65e30d58d557)]
interface nsIConsoleListener : nsISupports
{
void observe(in nsIConsoleMessage aMessage);
};

View file

@ -0,0 +1,43 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "nsISupports.idl"
/**
* This is intended as a base interface; implementations may want to
* provide an object that can be qi'ed to provide more specific
* message information.
*/
[scriptable, uuid(3aba9617-10e2-4839-83ae-2e6fc4df428b)]
interface nsIConsoleMessage : nsISupports
{
/** Log level constants. */
const uint32_t debug = 0;
const uint32_t info = 1;
const uint32_t warn = 2;
const uint32_t error = 3;
/**
* The log level of this message.
*/
readonly attribute uint32_t logLevel;
/**
* The time (in milliseconds from the Epoch) that the message instance
* was initialised.
* The timestamp is initialized as JS_now/1000 so that it can be
* compared to Date.now in Javascript.
*/
readonly attribute long long timeStamp;
[binaryname(MessageMoz)] readonly attribute wstring message;
AUTF8String toString();
};
%{ C++
#define NS_CONSOLEMESSAGE_CID \
{ 0x024efc9e, 0x54dc, 0x4844, { 0x80, 0x4b, 0x41, 0xd3, 0xf3, 0x69, 0x90, 0x73 }}
%}

View file

@ -0,0 +1,56 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
#include "nsISupports.idl"
interface nsIConsoleListener;
interface nsIConsoleMessage;
[scriptable, uuid(0eb81d20-c37e-42d4-82a8-ca9ae96bdf52)]
interface nsIConsoleService : nsISupports
{
void logMessage(in nsIConsoleMessage message);
/**
* Convenience method for logging simple messages.
*/
void logStringMessage(in wstring message);
/**
* Get an array of all the messages logged so far. If no messages
* are logged, this function will return a count of 0, but still
* will allocate one word for messages, so as to show up as a
* 0-length array when called from script.
*/
void getMessageArray([optional] out uint32_t count,
[retval, array, size_is(count)] out nsIConsoleMessage messages);
/**
* To guard against stack overflows from listeners that could log
* messages (it's easy to do this inadvertently from listeners
* implemented in JavaScript), we don't call any listeners when
* another error is already being logged.
*/
void registerListener(in nsIConsoleListener listener);
/**
* Each registered listener should also be unregistered.
*/
void unregisterListener(in nsIConsoleListener listener);
/**
* Clear the message buffer (e.g. for privacy reasons).
*/
void reset();
};
%{ C++
#define NS_CONSOLESERVICE_CID \
{ 0x7e3ff85c, 0x1dd2, 0x11b2, { 0x8d, 0x4b, 0xeb, 0x45, 0x2c, 0xb0, 0xff, 0x40 }}
#define NS_CONSOLESERVICE_CONTRACTID "@mozilla.org/consoleservice;1"
%}

View file

@ -0,0 +1,164 @@
/* 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/. */
#include "nsISupports.idl"
%{C++
#include <stdio.h>
class nsCycleCollectorLogger;
%}
[ptr] native FILE(FILE);
[ptr] native nsCycleCollectorLoggerPtr (nsCycleCollectorLogger);
interface nsIFile;
/**
* A set of interfaces for recording the cycle collector's work. An instance
* of @mozilla.org/cycle-collector-logger;1 can be configured to enable various
* options, then passed to the cycle collector when it runs.
* Note that additional logging options are available by setting environment
* variables, as described at the top of nsCycleCollector.cpp.
*/
/**
* nsICycleCollectorHandler is the interface JS code should implement to
* receive the results logged by a @mozilla.org/cycle-collector-logger;1
* instance. Pass an instance of this to the logger's 'processNext' method
* after the collection has run. This will describe the objects the cycle
* collector visited, the edges it found, and the conclusions it reached
* about the liveness of objects.
*
* In more detail:
* - For each node in the graph:
* - a call is made to either |noteRefCountedObject| or |noteGCedObject|, to
* describe the node itself; and
* - for each edge starting at that node, a call is made to |noteEdge|.
*
* - Then, a series of calls are made to:
* - |describeRoot|, for reference-counted nodes that the CC has identified as
* being alive because there are unknown references to those nodes.
* - |describeGarbage|, for nodes the cycle collector has identified as garbage.
*
* Any node not mentioned in a call to |describeRoot| or |describeGarbage| is
* neither a root nor garbage. The cycle collector was able to find all of the
* edges implied by the node's reference count.
*/
[scriptable, uuid(7f093367-1492-4b89-87af-c01dbc831246)]
interface nsICycleCollectorHandler : nsISupports
{
void noteRefCountedObject(in ACString aAddress,
in unsigned long aRefCount,
in ACString aObjectDescription);
void noteGCedObject(in ACString aAddress,
in boolean aMarked,
in ACString aObjectDescription,
in ACString aCompartmentAddress);
void noteEdge(in ACString aFromAddress,
in ACString aToAddress,
in ACString aEdgeName);
void describeRoot(in ACString aAddress,
in unsigned long aKnownEdges);
void describeGarbage(in ACString aAddress);
};
/**
* This interface allows replacing the log-writing backend for an
* nsICycleCollectorListener. As this interface is also called while
* the cycle collector is running, it cannot be implemented in JS.
*/
[scriptable, builtinclass, uuid(3ad9875f-d0e4-4ac2-87e3-f127f6c02ce1)]
interface nsICycleCollectorLogSink : nsISupports
{
[noscript] void open(out FILE aGCLog, out FILE aCCLog);
void closeGCLog();
void closeCCLog();
// This string will appear somewhere in the log's filename.
attribute AString filenameIdentifier;
// This is the process ID; it can be changed if logging is on behalf
// of another process.
attribute int32_t processIdentifier;
// The GC log file, if logging to files.
readonly attribute nsIFile gcLog;
// The CC log file, if logging to files.
readonly attribute nsIFile ccLog;
};
/**
* This interface is used to configure some reporting options for the cycle
* collector. This interface cannot be implemented by JavaScript code, as it
* is called while the cycle collector is running.
*
* To analyze cycle collection data in JS:
*
* - Create an instance of @mozilla.org/cycle-collector-logger;1, which
* implements this interface.
*
* - Set its |disableLog| property to true. This prevents the logger from
* printing messages about each method call to a temporary log file.
*
* - Set its |wantAfterProcessing| property to true. This tells the logger
* to record calls to its methods in memory. The |processNext| method
* returns events from this record.
*
* - Perform a collection using the logger. For example, call
* |nsIDOMWindowUtils|'s |garbageCollect| method, passing the logger as
* the |aListener| argument.
*
* - When the collection is complete, loop calling the logger's
* |processNext| method, passing a JavaScript object that implements
* nsICycleCollectorHandler. This JS code is free to allocate and operate
* on objects however it pleases: the cycle collector has finished its
* work, and the JS code is simply consuming recorded data.
*/
[scriptable, builtinclass, uuid(703b53b6-24f6-40c6-9ea9-aeb2dc53d170)]
interface nsICycleCollectorListener : nsISupports
{
// Return a listener that directs the cycle collector to traverse
// objects that it knows won't be collectable.
//
// Note that even this listener will not visit every node in the heap;
// the cycle collector can't see the entire heap. But while this
// listener is in use, the collector disables some optimizations it
// normally uses to avoid certain classes of objects that are certainly
// alive. So, if your purpose is to get a view of the portion of the
// heap that is of interest to the cycle collector, and not simply find
// garbage, then you should use the listener this returns.
//
// Note that this does not necessarily return a new listener; rather, it may
// simply set a flag on this listener (a side effect!) and return it.
nsICycleCollectorListener allTraces();
// True if this listener will behave like one returned by allTraces().
readonly attribute boolean wantAllTraces;
// If true, do not log each method call to a temporary file.
// Initially false.
attribute boolean disableLog;
// If |disableLog| is false, this object will be sent the log text.
attribute nsICycleCollectorLogSink logSink;
// If true, record all method calls in memory, to be retrieved later
// using |processNext|. Initially false.
attribute boolean wantAfterProcessing;
// Report the next recorded event to |aHandler|, and remove it from the
// record. Return false if there isn't anything more to process.
//
// Note that we only record events to report here if our
// |wantAfterProcessing| property is true.
boolean processNext(in nsICycleCollectorHandler aHandler);
// Return the current object as an nsCycleCollectorLogger*, which is the
// only class that should be implementing this interface. We need the
// concrete implementation type to help the GC rooting analysis.
[noscript] nsCycleCollectorLoggerPtr asLogger();
};

82
xpcom/base/nsIDebug2.idl Normal file
View file

@ -0,0 +1,82 @@
/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
/* 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/. */
/* interface to expose information about calls to NS_DebugBreak */
#include "nsISupports.idl"
/**
* For use by consumers in scripted languages (JavaScript, Java, Python,
* Perl, ...).
*
* @note C/C++ consumers who are planning to use the nsIDebug2 interface with
* the "@mozilla.org/xpcom;1" contract should use NS_DebugBreak from xpcom
* glue instead.
*
*/
[scriptable, uuid(9641dc15-10fb-42e3-a285-18be90a5c10b)]
interface nsIDebug2 : nsISupports
{
/**
* Whether XPCOM was compiled with DEBUG defined. This often
* correlates to whether other code (e.g., Firefox, XULRunner) was
* compiled with DEBUG defined.
*/
readonly attribute boolean isDebugBuild;
/**
* The number of assertions since process start.
*/
readonly attribute long assertionCount;
/**
* Whether a debugger is currently attached.
* Supports Windows + Mac
*/
readonly attribute bool isDebuggerAttached;
/**
* Show an assertion and trigger nsIDebug2.break().
*
* @param aStr assertion message
* @param aExpr expression that failed
* @param aFile file containing assertion
* @param aLine line number of assertion
*/
void assertion(in string aStr,
in string aExpr,
in string aFile,
in long aLine);
/**
* Show a warning.
*
* @param aStr warning message
* @param aFile file containing assertion
* @param aLine line number of assertion
*/
void warning(in string aStr,
in string aFile,
in long aLine);
/**
* Request to break into a debugger.
*
* @param aFile file containing break request
* @param aLine line number of break request
*/
void break(in string aFile,
in long aLine);
/**
* Request the process to trigger a fatal abort.
*
* @param aFile file containing abort request
* @param aLine line number of abort request
*/
void abort(in string aFile,
in long aLine);
};

View file

@ -0,0 +1,49 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
#include "nsISupports.idl"
/**
* nsIErrorService: This is an interim service that allows nsresult codes to be mapped to
* string bundles that can be used to look up error messages. String bundle keys can also
* be mapped.
*
* This service will eventually get replaced by extending xpidl to allow errors to be defined.
* (http://bugzilla.mozilla.org/show_bug.cgi?id=13423).
*/
[scriptable, uuid(afe1f190-a3c2-11e3-a5e2-0800200c9a66)]
interface nsIErrorService : nsISupports
{
/**
* Registers a string bundle URL for an error module. Error modules are obtained from
* nsresult code with NS_ERROR_GET_MODULE.
*/
void registerErrorStringBundle(in short errorModule, in string stringBundleURL);
/**
* Unregisters a string bundle URL for an error module.
*/
void unregisterErrorStringBundle(in short errorModule);
/**
* Retrieves a string bundle URL for an error module.
*/
string getErrorStringBundle(in short errorModule);
};
%{C++
// The global nsIErrorService:
#define NS_ERRORSERVICE_NAME "Error Service"
#define NS_ERRORSERVICE_CONTRACTID "@mozilla.org/xpcom/error-service;1"
#define NS_ERRORSERVICE_CID \
{ /* 744afd5e-5f8c-11d4-9877-00c04fa0cf4a */ \
0x744afd5e, \
0x5f8c, \
0x11d4, \
{0x98, 0x77, 0x00, 0xc0, 0x4f, 0xa0, 0xcf, 0x4a} \
}
%}

View file

@ -0,0 +1,86 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
/*
* Interfaces for representing cross-language exceptions and stack traces.
*/
#include "nsISupports.idl"
[scriptable, builtinclass, uuid(28bfb2a2-5ea6-4738-918b-049dc4d51f0b)]
interface nsIStackFrame : nsISupports
{
// see nsIProgrammingLanguage for list of language consts
readonly attribute uint32_t language;
readonly attribute AUTF8String languageName;
[implicit_jscontext]
readonly attribute AString filename;
[implicit_jscontext]
readonly attribute AString name;
// Valid line numbers begin at '1'. '0' indicates unknown.
[implicit_jscontext]
readonly attribute int32_t lineNumber;
[implicit_jscontext]
readonly attribute int32_t columnNumber;
readonly attribute AUTF8String sourceLine;
[implicit_jscontext]
readonly attribute AString asyncCause;
[implicit_jscontext]
readonly attribute nsIStackFrame asyncCaller;
[implicit_jscontext]
readonly attribute nsIStackFrame caller;
// Returns a formatted stack string that looks like the sort of
// string that would be returned by .stack on JS Error objects.
// Only works on JS-language stack frames.
[implicit_jscontext]
readonly attribute AString formattedStack;
// Returns the underlying SavedFrame object for native JavaScript stacks,
// or null if this is not a native JavaScript stack frame.
readonly attribute jsval nativeSavedFrame;
[implicit_jscontext]
AUTF8String toString();
};
[scriptable, builtinclass, uuid(4371b5bf-6845-487f-8d9d-3f1e4a9badd2)]
interface nsIException : nsISupports
{
// A custom message set by the thrower.
[binaryname(MessageMoz)] readonly attribute AUTF8String message;
// The nsresult associated with this exception.
readonly attribute nsresult result;
// The name of the error code (ie, a string repr of |result|)
readonly attribute AUTF8String name;
// Filename location. This is the location that caused the
// error, which may or may not be a source file location.
// For example, standard language errors would generally have
// the same location as their top stack entry. File
// parsers may put the location of the file they were parsing,
// etc.
// null indicates "no data"
[implicit_jscontext]
readonly attribute AString filename;
// Valid line numbers begin at '1'. '0' indicates unknown.
[implicit_jscontext]
readonly attribute uint32_t lineNumber;
// Valid column numbers begin at 0.
// We don't have an unambiguous indicator for unknown.
readonly attribute uint32_t columnNumber;
// A stack trace, if available.
readonly attribute nsIStackFrame location;
// Arbitary data for the implementation.
readonly attribute nsISupports data;
// A generic formatter - make it suitable to print, etc.
[implicit_jscontext]
AUTF8String toString();
};

View file

@ -0,0 +1,82 @@
/* -*- 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/. */
#include "nsISupports.idl"
%{C++
#include "nsDependentString.h"
#include <stdio.h>
%}
interface nsIFile;
[ptr] native FILE(FILE);
/**
* A simple interface for writing to a .gz file.
*
* Note that the file that this interface produces has a different format than
* what you'd get if you compressed your data as a gzip stream and dumped the
* result to a file.
*
* The standard gunzip tool cannot decompress a raw gzip stream, but can handle
* the files produced by this interface.
*/
[scriptable, uuid(6bd5642c-1b90-4499-ba4b-199f27efaba5)]
interface nsIGZFileWriter : nsISupports
{
/**
* Initialize this object. We'll write our gzip'ed data to the given file,
* overwriting its contents if the file exists.
*
* init() will return an error if called twice. It's an error to call any
* other method on this interface without first calling init().
*/
[must_use] void init(in nsIFile file);
/**
* Alternate version of init() for use when the file is already opened;
* e.g., with a FileDescriptor passed over IPC.
*/
[noscript, must_use] void initANSIFileDesc(in FILE file);
/**
* Write the given string to the file.
*/
[must_use] void write(in AUTF8String str);
/*
* The following two overloads of Write() are C++ because we can't overload
* methods in XPIDL. Anyway, they don't add much functionality for JS
* callers.
*/
%{C++
/**
* Write the given char* to the file (not including the null-terminator).
*/
MOZ_MUST_USE nsresult Write(const char* str)
{
return Write(str, strlen(str));
}
/**
* Write |length| bytes of |str| to the file.
*/
MOZ_MUST_USE nsresult Write(const char* str, uint32_t len)
{
return Write(nsDependentCString(str, len));
}
%}
/**
* Close this nsIGZFileWriter. Classes implementing nsIGZFileWriter will run
* this method when the underlying object is destroyed, so it's not strictly
* necessary to explicitly call it from your code.
*
* It's an error to call this method twice, and it's an error to call write()
* after finish() has been called.
*/
void finish();
};

10
xpcom/base/nsIID.h Normal file
View file

@ -0,0 +1,10 @@
/* -*- 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 __nsIID_h
#define __nsIID_h
#include "nsID.h"
#endif /* __nsIID_h */

View file

@ -0,0 +1,36 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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/. */
#include "nsISupports.idl"
/**
* The nsIInterfaceRequestor interface defines a generic interface for
* requesting interfaces that a given object might provide access to.
* This is very similar to QueryInterface found in nsISupports.
* The main difference is that interfaces returned from GetInterface()
* are not required to provide a way back to the object implementing this
* interface. The semantics of QI() dictate that given an interface A that
* you QI() on to get to interface B, you must be able to QI on B to get back
* to A. This interface however allows you to obtain an interface C from A
* that may or most likely will not have the ability to get back to A.
*/
[scriptable, uuid(033A1470-8B2A-11d3-AF88-00A024FFC08C)]
interface nsIInterfaceRequestor : nsISupports
{
/**
* Retrieves the specified interface pointer.
*
* @param uuid The IID of the interface being requested.
* @param result [out] The interface pointer to be filled in if
* the interface is accessible.
* @throws NS_NOINTERFACE - interface not accessible.
* @throws NS_ERROR* - method failure.
*/
void getInterface(in nsIIDRef uuid,
[iid_is(uuid),retval] out nsQIResult result);
};

View file

@ -0,0 +1,32 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "nsISupports.idl"
/**
* nsIMacUtils: Generic globally-available Mac-specific utilities.
*/
[scriptable, uuid(5E9072D7-FF95-455E-9466-8AF9841A72EC)]
interface nsIMacUtils : nsISupports
{
/**
* True when the main executable is a fat file supporting at least
* ppc and x86 (universal binary).
*/
readonly attribute boolean isUniversalBinary;
/**
* Returns a string containing a list of architectures delimited
* by "-". Architecture sets are always in the same order:
* ppc > i386 > ppc64 > x86_64 > (future additions)
*/
readonly attribute AString architecturesInBinary;
/**
* True when running under binary translation (Rosetta).
*/
readonly attribute boolean isTranslated;
};

78
xpcom/base/nsIMemory.idl Normal file
View file

@ -0,0 +1,78 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
#include "nsISupports.idl"
/**
*
* nsIMemory: interface to allocate and deallocate memory. Also provides
* for notifications in low-memory situations.
*
* The frozen exported symbols moz_xmalloc, moz_xrealloc, and free
* provide a more efficient way to access XPCOM memory allocation. Using
* those symbols is preferred to using the methods on this interface.
*
* A client that wishes to be notified of low memory situations (for
* example, because the client maintains a large memory cache that
* could be released when memory is tight) should register with the
* observer service (see nsIObserverService) using the topic
* "memory-pressure". There are specific types of notications
* that can occur. These types will be passed as the |aData|
* parameter of the of the "memory-pressure" notification:
*
* "low-memory"
* This will be passed as the extra data when the pressure
* observer is being asked to flush for low-memory conditions.
*
* "low-memory-ongoing"
* This will be passed when we continue to be in a low-memory
* condition and we want to flush caches and do other cheap
* forms of memory minimization, but heavy handed approaches like
* a GC are unlikely to succeed.
*
* "-no-forward"
* This is appended to the above two parameters when the resulting
* notification should not be forwarded to the child processes.
*
* "heap-minimize"
* This will be passed as the extra data when the pressure
* observer is being asked to flush because of a heap minimize
* call.
*
* "alloc-failure"
* This will be passed as the extra data when the pressure
* observer has been asked to flush because a malloc() or
* realloc() has failed.
*
* "lowering-priority"
* This will be passed as the extra data when the priority of a child
* process is lowered. The pressure observers could take the chance to
* clear caches that could be easily regenerated. This type of
* notification only appears in child processes.
*/
[scriptable, uuid(1e004834-6d8f-425a-bc9c-a2812ed43bb7)]
interface nsIMemory : nsISupports
{
/**
* Attempts to shrink the heap.
* @param immediate - if true, heap minimization will occur
* immediately if the call was made on the main thread. If
* false, the flush will be scheduled to happen when the app is
* idle.
* @throws NS_ERROR_FAILURE if 'immediate' is set an the call
* was not on the application's main thread.
*/
void heapMinimize(in boolean immediate);
/**
* This predicate can be used to determine if the platform is a "low-memory"
* platform. Callers may use this to dynamically tune their behaviour
* to favour reduced memory usage at the expense of performance. The value
* returned by this function will not change over the lifetime of the process.
*/
boolean isLowMemoryPlatform();
};

View file

@ -0,0 +1,162 @@
/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "nsISupports.idl"
interface nsIFile;
interface nsICycleCollectorLogSink;
[scriptable, function, uuid(2dea18fc-fbfa-4bf7-ad45-0efaf5495f5e)]
interface nsIFinishDumpingCallback : nsISupports
{
void callback(in nsISupports data);
};
/**
* Callback interface for |dumpGCAndCCLogsToFile|, below. Note that
* these method calls can occur before |dumpGCAndCCLogsToFile|
* returns.
*/
[scriptable, uuid(dc1b2b24-65bd-441b-b6bd-cb5825a7ed14)]
interface nsIDumpGCAndCCLogsCallback : nsISupports
{
/**
* Called whenever a process has successfully finished dumping its GC/CC logs.
* Incomplete dumps (e.g., if the child crashes or is killed due to memory
* exhaustion) are not reported.
*
* @param aGCLog The file that the GC log was written to.
*
* @param aCCLog The file that the CC log was written to.
*
* @param aIsParent indicates whether this log file pair is from the
* parent process.
*/
void onDump(in nsIFile aGCLog,
in nsIFile aCCLog,
in bool aIsParent);
/**
* Called when GC/CC logging has finished, after all calls to |onDump|.
*/
void onFinish();
};
[scriptable, builtinclass, uuid(48541b74-47ee-4a62-9557-7f4b809bda5c)]
interface nsIMemoryInfoDumper : nsISupports
{
/**
* This dumps gzipped memory reports for this process and its child
* processes. If a file of the given name exists, it will be overwritten.
*
* @param aFilename The output file.
*
* @param aFinishDumping The callback called on completion.
*
* @param aFinishDumpingData The environment for the callback.
*
* @param aAnonymize Should the reports be anonymized?
*
* Sample output, annotated with comments for explanatory purposes.
*
* {
* // The version number of the format, which will be incremented each time
* // backwards-incompatible changes are made. A mandatory integer.
* "version": 1
*
* // Equal to nsIMemoryReporterManager::hasMozMallocUsableSize. A
* // mandatory boolean.
* "hasMozMallocUsableSize": true,
*
* // The memory reports. A mandatory array.
* "reports": [
* // The properties correspond to the arguments of
* // nsIHandleReportCallback::callback. Every one is mandatory.
* {"process":"Main Process (pid 12345)", "path":"explicit/foo/bar",
* "kind":1, "units":0, "amount":2000000, "description":"Foo bar."},
* {"process":"Main Process (pid 12345)", "path":"heap-allocated",
* "kind":1, "units":0, "amount":3000000, "description":"Heap allocated."},
* {"process":"Main Process (pid 12345)", "path":"vsize",
* "kind":1, "units":0, "amount":10000000, "description":"Vsize."}
* ]
* }
*/
void dumpMemoryReportsToNamedFile(in AString aFilename,
in nsIFinishDumpingCallback aFinishDumping,
in nsISupports aFinishDumpingData,
in boolean aAnonymize);
/**
* Similar to dumpMemoryReportsToNamedFile, this method dumps gzipped memory
* reports for this process and its child processes to files in the tmp
* directory called memory-reports-<identifier>-<pid>.json.gz (or something
* similar, such as memory-reports-<identifier>-<pid>-1.json.gz; no existing
* file will be overwritten).
*
* If DMD is enabled, this method also dumps gzipped DMD output for this
* process and its child processes to files in the tmp directory called
* dmd-<identifier>-<pid>.txt.gz (or something similar; again, no existing
* file will be overwritten).
*
* @param aIdentifier this identifier will appear in the filename of our
* about:memory dump and those of our children.
*
* If the identifier is empty, the implementation may set it arbitrarily
* and use that new value for its own dump and the dumps of its child
* processes. For example, the implementation may set |aIdentifier| to the
* number of seconds since the epoch.
*
* @param aAnonymize Should the reports be anonymized?
*
* @param aMinimizeMemoryUsage indicates whether we should run a series of
* gc/cc's in an attempt to reduce our memory usage before collecting our
* memory report.
*/
void dumpMemoryInfoToTempDir(
in AString aIdentifier,
in boolean aAnonymize,
in boolean aMinimizeMemoryUsage);
/**
* Dump GC and CC logs to files in the OS's temp directory (or in
* $MOZ_CC_LOG_DIRECTORY, if that environment variable is specified).
*
* @param aIdentifier If aIdentifier is non-empty, this string will appear in
* the filenames of the logs we create (both for this process and, if
* aDumpChildProcesses is true, for our child processes).
*
* If aIdentifier is empty, the implementation may set it to an
* arbitrary value; for example, it may set aIdentifier to the number
* of seconds since the epoch.
*
* @param aDumpAllTraces indicates whether we should run an all-traces CC
* log. An all-traces log visits all objects currently eligible for cycle
* collection, while a non-all-traces log avoids visiting some objects
* which we know are reachable.
*
* All-traces logs are much bigger than the alternative, but they may be
* helpful when trying to understand why a particular object is alive. For
* example, a non-traces-log will skip references held by an active
* document; if your object is being held alive by such a document, you
* probably want to see those references.
*
* @param aDumpChildProcesses indicates whether we should call
* DumpGCAndCCLogsToFile in our child processes. If so, the child processes
* will dump their children, and so on.
*
*/
void dumpGCAndCCLogsToFile(in AString aIdentifier,
in bool aDumpAllTraces,
in bool aDumpChildProcesses,
in nsIDumpGCAndCCLogsCallback aCallback);
/**
* Like |dumpGCAndCCLogsToFile|, but sends the logs to the given log
* sink object instead of accessing the filesystem directly, and
* dumps the current process only.
*/
void dumpGCAndCCLogsToSink(in bool aDumpAllTraces,
in nsICycleCollectorLogSink aSink);
};

View file

@ -0,0 +1,581 @@
/* -*- 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/. */
#include "nsISupports.idl"
%{C++
#include <stdio.h>
%}
interface mozIDOMWindowProxy;
interface nsIRunnable;
interface nsISimpleEnumerator;
[ptr] native FILE(FILE);
/*
* Memory reporters measure Firefox's memory usage. They are primarily used to
* generate the about:memory page. You should read
* https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Memory_reporting
* before writing a memory reporter.
*/
[scriptable, function, uuid(62ef0e1c-dbd6-11e3-aa75-3c970e9f4238)]
interface nsIMemoryReporterCallback : nsISupports
{
/*
* The arguments to the callback are as follows.
*
*
* |process| The name of the process containing this reporter. Each
* reporter initially has "" in this field, indicating that it applies to the
* current process. (This is true even for reporters in a child process.)
* When a reporter from a child process is copied into the main process, the
* copy has its 'process' field set appropriately.
*
*
* |path| The path that this memory usage should be reported under. Paths
* are '/'-delimited, eg. "a/b/c".
*
* Each reporter can be viewed as representing a leaf node in a tree.
* Internal nodes of the tree don't have reporters. So, for example, the
* reporters "explicit/a/b", "explicit/a/c", "explicit/d/e", and
* "explicit/d/f" define this tree:
*
* explicit
* |--a
* | |--b [*]
* | \--c [*]
* \--d
* |--e [*]
* \--f [*]
*
* Nodes marked with a [*] have a reporter. Notice that the internal
* nodes are implicitly defined by the paths.
*
* Nodes within a tree should not overlap measurements, otherwise the
* parent node measurements will be double-counted. So in the example
* above, |b| should not count any allocations counted by |c|, and vice
* versa.
*
* All nodes within each tree must have the same units.
*
* If you want to include a '/' not as a path separator, e.g. because the
* path contains a URL, you need to convert each '/' in the URL to a '\'.
* Consumers of the path will undo this change. Any other '\' character
* in a path will also be changed. This is clumsy but hasn't caused any
* problems so far.
*
* The paths of all reporters form a set of trees. Trees can be
* "degenerate", i.e. contain a single entry with no '/'.
*
*
* |kind| There are three kinds of memory reporters.
*
* - HEAP: reporters measuring memory allocated by the heap allocator,
* e.g. by calling malloc, calloc, realloc, memalign, operator new, or
* operator new[]. Reporters in this category must have units
* UNITS_BYTES.
*
* - NONHEAP: reporters measuring memory which the program explicitly
* allocated, but does not live on the heap. Such memory is commonly
* allocated by calling one of the OS's memory-mapping functions (e.g.
* mmap, VirtualAlloc, or vm_allocate). Reporters in this category
* must have units UNITS_BYTES.
*
* - OTHER: reporters which don't fit into either of these categories.
* They can have any units.
*
* The kind only matters for reporters in the "explicit" tree;
* aboutMemory.js uses it to calculate "heap-unclassified".
*
*
* |units| The units on the reporter's amount. One of the following.
*
* - BYTES: The amount contains a number of bytes.
*
* - COUNT: The amount is an instantaneous count of things currently in
* existence. For instance, the number of tabs currently open would have
* units COUNT.
*
* - COUNT_CUMULATIVE: The amount contains the number of times some event
* has occurred since the application started up. For instance, the
* number of times the user has opened a new tab would have units
* COUNT_CUMULATIVE.
*
* The amount returned by a reporter with units COUNT_CUMULATIVE must
* never decrease over the lifetime of the application.
*
* - PERCENTAGE: The amount contains a fraction that should be expressed as
* a percentage. NOTE! The |amount| field should be given a value 100x
* the actual percentage; this number will be divided by 100 when shown.
* This allows a fractional percentage to be shown even though |amount| is
* an integer. E.g. if the actual percentage is 12.34%, |amount| should
* be 1234.
*
* Values greater than 100% are allowed.
*
*
* |amount| The numeric value reported by this memory reporter. Accesses
* can fail if something goes wrong when getting the amount.
*
*
* |description| A human-readable description of this memory usage report.
*/
void callback(in ACString process, in AUTF8String path, in int32_t kind,
in int32_t units, in int64_t amount,
in AUTF8String description, in nsISupports data);
};
/*
* An nsIMemoryReporter reports one or more memory measurements via a
* callback function which is called once for each measurement.
*
* An nsIMemoryReporter that reports a single measurement is sometimes called a
* "uni-reporter". One that reports multiple measurements is sometimes called
* a "multi-reporter".
*
* aboutMemory.js is the most important consumer of memory reports. It
* places the following constraints on reports.
*
* - All reports within a single sub-tree must have the same units.
*
* - There may be an "explicit" tree. If present, it represents
* non-overlapping regions of memory that have been explicitly allocated with
* an OS-level allocation (e.g. mmap/VirtualAlloc/vm_allocate) or a
* heap-level allocation (e.g. malloc/calloc/operator new). Reporters in
* this tree must have kind HEAP or NONHEAP, units BYTES.
*
* It is preferred, but not required, that report descriptions use complete
* sentences (i.e. start with a capital letter and end with a period, or
* similar).
*/
[scriptable, uuid(92a36db1-46bd-4fe6-988e-47db47236d8b)]
interface nsIMemoryReporter : nsISupports
{
/*
* Run the reporter.
*
* If |anonymize| is true, the memory reporter should anonymize any
* privacy-sensitive details in memory report paths, by replacing them with a
* string such as "<anonymized>". Anonymized memory reports may be sent
* automatically via crash reports or telemetry.
*
* The following things are considered privacy-sensitive.
*
* - Content domains and URLs, and information derived from them.
* - Content data, such as strings.
* - Details about content code, such as filenames, function names or stack
* traces.
* - Details about or data from the user's system, such as filenames.
* - Running apps.
*
* In short, anything that could identify parts of the user's browsing
* history is considered privacy-sensitive.
*
* The following thing are not considered privacy-sensitive.
*
* - Chrome domains and URLs.
* - Information about installed extensions.
*/
void collectReports(in nsIMemoryReporterCallback callback,
in nsISupports data,
in boolean anonymize);
/*
* Kinds. See the |kind| comment in nsIMemoryReporterCallback.
*/
const int32_t KIND_NONHEAP = 0;
const int32_t KIND_HEAP = 1;
const int32_t KIND_OTHER = 2;
/*
* Units. See the |units| comment in nsIMemoryReporterCallback.
*/
const int32_t UNITS_BYTES = 0;
const int32_t UNITS_COUNT = 1;
const int32_t UNITS_COUNT_CUMULATIVE = 2;
const int32_t UNITS_PERCENTAGE = 3;
};
[scriptable, function, uuid(548b3909-c04d-4ca6-8466-b8bee3837457)]
interface nsIFinishReportingCallback : nsISupports
{
void callback(in nsISupports data);
};
[scriptable, builtinclass, uuid(2998574d-8993-407a-b1a5-8ad7417653e1)]
interface nsIMemoryReporterManager : nsISupports
{
/*
* Initialize.
*/
[must_use] void init();
/*
* Register the given nsIMemoryReporter. The Manager service will hold a
* strong reference to the given reporter, and will be responsible for freeing
* the reporter at shutdown. You may manually unregister the reporter with
* unregisterStrongReporter() at any point.
*/
void registerStrongReporter(in nsIMemoryReporter reporter);
void registerStrongAsyncReporter(in nsIMemoryReporter reporter);
/*
* Like registerReporter, but the Manager service will hold a weak reference
* via a raw pointer to the given reporter. The reporter should be
* unregistered before shutdown.
* You cannot register JavaScript components with this function! Always
* register your JavaScript components with registerStrongReporter().
*/
void registerWeakReporter(in nsIMemoryReporter reporter);
void registerWeakAsyncReporter(in nsIMemoryReporter reporter);
/*
* Unregister the given memory reporter, which must have been registered with
* registerStrongReporter(). You normally don't need to unregister your
* strong reporters, as nsIMemoryReporterManager will take care of that at
* shutdown.
*/
void unregisterStrongReporter(in nsIMemoryReporter reporter);
/*
* Unregister the given memory reporter, which must have been registered with
* registerWeakReporter().
*/
void unregisterWeakReporter(in nsIMemoryReporter reporter);
/*
* These functions should only be used for testing purposes.
*/
void blockRegistrationAndHideExistingReporters();
void unblockRegistrationAndRestoreOriginalReporters();
void registerStrongReporterEvenIfBlocked(in nsIMemoryReporter aReporter);
/*
* Get memory reports for the current process and all child processes.
* |handleReport| is called for each report, and |finishReporting| is called
* once all reports have been handled.
*
* |finishReporting| is called even if, for example, some child processes
* fail to report back. However, calls to this method will silently and
* immediately abort -- and |finishReporting| will not be called -- if a
* previous getReports() call is still in flight, i.e. if it has not yet
* finished invoking |finishReporting|. The silent abort is because the
* in-flight request will finish soon, and the caller would very likely just
* catch and ignore any error anyway.
*
* If |anonymize| is true, it indicates that the memory reporters should
* anonymize any privacy-sensitive data (see above).
*/
void getReports(in nsIMemoryReporterCallback handleReport,
in nsISupports handleReportData,
in nsIFinishReportingCallback finishReporting,
in nsISupports finishReportingData,
in boolean anonymize);
/*
* As above, but: If |minimizeMemoryUsage| is true, then each process will
* minimize its memory usage (see the |minimizeMemoryUsage| method) before
* gathering its report. If DMD is enabled and |DMDDumpIdent| is non-empty
* then write a DMD report to a file in the usual temporary directory (see
* |dumpMemoryInfoToTempDir| in |nsIMemoryInfoDumper|.)
*/
[noscript] void
getReportsExtended(in nsIMemoryReporterCallback handleReport,
in nsISupports handleReportData,
in nsIFinishReportingCallback finishReporting,
in nsISupports finishReportingData,
in boolean anonymize,
in boolean minimizeMemoryUsage,
in AString DMDDumpIdent);
/*
* As above, but if DMD is enabled and |DMDFile| is non-null then
* write a DMD report to that file and close it.
*/
[noscript] void
getReportsForThisProcessExtended(in nsIMemoryReporterCallback handleReport,
in nsISupports handleReportData,
in boolean anonymize,
in FILE DMDFile,
in nsIFinishReportingCallback finishReporting,
in nsISupports finishReportingData);
/*
* Called by an asynchronous memory reporter upon completion.
*/
[noscript] void endReport();
/*
* The memory reporter manager, for the most part, treats reporters
* registered with it as a black box. However, there are some
* "distinguished" amounts (as could be reported by a memory reporter) that
* the manager provides as attributes, because they are sufficiently
* interesting that we want external code (e.g. telemetry) to be able to rely
* on them.
*
* Note that these are not reporters and so getReports() does not look at
* them. However, distinguished amounts can be embedded in a reporter.
*
* Access to these attributes can fail. In particular, some of them are not
* available on all platforms.
*
* If you add a new distinguished amount, please update
* toolkit/components/aboutmemory/tests/test_memoryReporters.xul.
*
* |vsize| (UNITS_BYTES) The virtual size, i.e. the amount of address space
* taken up.
*
* |vsizeMaxContiguous| (UNITS_BYTES) The size of the largest contiguous
* block of virtual memory.
*
* |resident| (UNITS_BYTES) The resident size (a.k.a. RSS or physical memory
* used).
*
* |residentFast| (UNITS_BYTES) This is like |resident|, but on Mac OS
* |resident| can purge pages, which is slow. It also affects the result of
* |residentFast|, and so |resident| and |residentFast| should not be used
* together.
*
* |residentPeak| (UNITS_BYTES) The peak resident size.
*
* |residentUnique| (UNITS_BYTES) The unique set size (a.k.a. USS).
*
* |heapAllocated| (UNITS_BYTES) Memory mapped by the heap allocator.
*
* |heapOverheadFraction| (UNITS_PERCENTAGE) In the heap allocator, this is
* the fraction of committed heap bytes that are overhead. Like all
* UNITS_PERCENTAGE measurements, its amount is multiplied by 100x so it can
* be represented by an int64_t.
*
* |JSMainRuntimeGCHeap| (UNITS_BYTES) Size of the main JS runtime's GC
* heap.
*
* |JSMainRuntimeTemporaryPeak| (UNITS_BYTES) Peak size of the transient
* storage in the main JSRuntime.
*
* |JSMainRuntimeCompartments{System,User}| (UNITS_COUNT) The number of
* {system,user} compartments in the main JS runtime.
*
* |imagesContentUsedUncompressed| (UNITS_BYTES) Memory used for decoded
* raster images in content.
*
* |storageSQLite| (UNITS_BYTES) Memory used by SQLite.
*
* |lowMemoryEvents{Virtual,Physical}| (UNITS_COUNT_CUMULATIVE) The number
* of low-{virtual,physical}-memory events that have occurred since the
* process started.
*
* |ghostWindows| (UNITS_COUNT) The number of ghost windows.
*
* |pageFaultsHard| (UNITS_COUNT_CUMULATIVE) The number of hard (a.k.a.
* major) page faults that have occurred since the process started.
*/
[must_use] readonly attribute int64_t vsize;
[must_use] readonly attribute int64_t vsizeMaxContiguous;
[must_use] readonly attribute int64_t resident;
[must_use] readonly attribute int64_t residentFast;
[must_use] readonly attribute int64_t residentPeak;
[must_use] readonly attribute int64_t residentUnique;
[must_use] readonly attribute int64_t heapAllocated;
[must_use] readonly attribute int64_t heapOverheadFraction;
[must_use] readonly attribute int64_t JSMainRuntimeGCHeap;
[must_use] readonly attribute int64_t JSMainRuntimeTemporaryPeak;
[must_use] readonly attribute int64_t JSMainRuntimeCompartmentsSystem;
[must_use] readonly attribute int64_t JSMainRuntimeCompartmentsUser;
[must_use] readonly attribute int64_t imagesContentUsedUncompressed;
[must_use] readonly attribute int64_t storageSQLite;
[must_use] readonly attribute int64_t lowMemoryEventsVirtual;
[must_use] readonly attribute int64_t lowMemoryEventsPhysical;
[must_use] readonly attribute int64_t ghostWindows;
[must_use] readonly attribute int64_t pageFaultsHard;
/*
* This attribute indicates if moz_malloc_usable_size() works.
*/
[infallible] readonly attribute boolean hasMozMallocUsableSize;
/*
* These attributes indicate DMD's status. "Enabled" means enabled at
* build-time.
*/
[infallible] readonly attribute boolean isDMDEnabled;
[infallible] readonly attribute boolean isDMDRunning;
/*
* Run a series of GC/CC's in an attempt to minimize the application's memory
* usage. When we're finished, we invoke the given runnable if it's not
* null.
*/
[must_use] void minimizeMemoryUsage(in nsIRunnable callback);
/*
* Measure the memory that is known to be owned by this tab, split up into
* several broad categories. Note that this will be an underestimate of the
* true number, due to imperfect memory reporter coverage (corresponding to
* about:memory's "heap-unclassified"), and due to some memory shared between
* tabs not being counted.
*
* The time taken for the measurement (split into JS and non-JS parts) is
* also returned.
*/
[must_use]
void sizeOfTab(in mozIDOMWindowProxy window,
out int64_t jsObjectsSize, out int64_t jsStringsSize,
out int64_t jsOtherSize, out int64_t domSize,
out int64_t styleSize, out int64_t otherSize,
out int64_t totalSize,
out double jsMilliseconds, out double nonJSMilliseconds);
};
%{C++
#include "js/TypeDecls.h"
#include "nsStringGlue.h"
#include "nsTArray.h"
class nsPIDOMWindowOuter;
// nsIHandleReportCallback is a better name, but keep nsIMemoryReporterCallback
// around for backwards compatibility.
typedef nsIMemoryReporterCallback nsIHandleReportCallback;
namespace mozilla {
// All the following registration/unregistration functions don't use
// MOZ_MUST_USE because ignoring failures is common and reasonable.
// Register a memory reporter. The manager service will hold a strong
// reference to this reporter.
XPCOM_API(nsresult) RegisterStrongMemoryReporter(nsIMemoryReporter* aReporter);
XPCOM_API(nsresult) RegisterStrongAsyncMemoryReporter(nsIMemoryReporter* aReporter);
// Register a memory reporter. The manager service will hold a weak reference
// to this reporter.
XPCOM_API(nsresult) RegisterWeakMemoryReporter(nsIMemoryReporter* aReporter);
XPCOM_API(nsresult) RegisterWeakAsyncMemoryReporter(nsIMemoryReporter* aReporter);
// Unregister a strong memory reporter.
XPCOM_API(nsresult) UnregisterStrongMemoryReporter(nsIMemoryReporter* aReporter);
// Unregister a weak memory reporter.
XPCOM_API(nsresult) UnregisterWeakMemoryReporter(nsIMemoryReporter* aReporter);
// The memory reporter manager provides access to several distinguished
// amounts via attributes. Some of these amounts are provided by Gecko
// components that cannot be accessed directly from XPCOM code. So we provide
// the following functions for those components to be registered with the
// manager.
typedef int64_t (*InfallibleAmountFn)();
#define DECL_REGISTER_DISTINGUISHED_AMOUNT(kind, name) \
nsresult Register##name##DistinguishedAmount(kind##AmountFn aAmountFn);
#define DECL_UNREGISTER_DISTINGUISHED_AMOUNT(name) \
nsresult Unregister##name##DistinguishedAmount();
DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeGCHeap)
DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeTemporaryPeak)
DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem)
DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser)
DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, ImagesContentUsedUncompressed)
DECL_UNREGISTER_DISTINGUISHED_AMOUNT(ImagesContentUsedUncompressed)
DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, StorageSQLite)
DECL_UNREGISTER_DISTINGUISHED_AMOUNT(StorageSQLite)
DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual)
DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical)
DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows)
#undef DECL_REGISTER_DISTINGUISHED_AMOUNT
#undef DECL_UNREGISTER_DISTINGUISHED_AMOUNT
// Likewise for per-tab measurement.
typedef MOZ_MUST_USE nsresult (*JSSizeOfTabFn)(JSObject* aObj,
size_t* aJsObjectsSize,
size_t* aJsStringSize,
size_t* aJsPrivateSize,
size_t* aJsOtherSize);
typedef MOZ_MUST_USE nsresult (*NonJSSizeOfTabFn)(nsPIDOMWindowOuter* aWindow,
size_t* aDomSize,
size_t* aStyleSize,
size_t* aOtherSize);
nsresult RegisterJSSizeOfTab(JSSizeOfTabFn aSizeOfTabFn);
nsresult RegisterNonJSSizeOfTab(NonJSSizeOfTabFn aSizeOfTabFn);
}
#if defined(MOZ_DMD)
#if !defined(MOZ_MEMORY)
#error "MOZ_DMD requires MOZ_MEMORY"
#endif
#include "DMD.h"
#define MOZ_REPORT(ptr) mozilla::dmd::Report(ptr)
#define MOZ_REPORT_ON_ALLOC(ptr) mozilla::dmd::ReportOnAlloc(ptr)
#else
#define MOZ_REPORT(ptr)
#define MOZ_REPORT_ON_ALLOC(ptr)
#endif // defined(MOZ_DMD)
// Functions generated via this macro should be used by all traversal-based
// memory reporters. Such functions return |moz_malloc_size_of(ptr)|; this
// will always be zero on some obscure platforms.
//
// You might be wondering why we have a macro that creates multiple functions
// that differ only in their name, instead of a single MallocSizeOf function.
// It's mostly to help with DMD integration, though it sometimes also helps
// with debugging and temporary ad hoc profiling. The function name chosen
// doesn't matter greatly, but it's best to make it similar to the path used by
// the relevant memory reporter(s).
#define MOZ_DEFINE_MALLOC_SIZE_OF(fn) \
static size_t fn(const void* aPtr) \
{ \
MOZ_REPORT(aPtr); \
return moz_malloc_size_of(aPtr); \
}
// Functions generated by the next two macros should be used by wrapping
// allocators that report heap blocks as soon as they are allocated and
// unreport them as soon as they are freed. Such allocators are used in cases
// where we have third-party code that we cannot modify. The two functions
// must always be used in tandem.
#define MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(fn) \
static size_t fn(const void* aPtr) \
{ \
MOZ_REPORT_ON_ALLOC(aPtr); \
return moz_malloc_size_of(aPtr); \
}
#define MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(fn) \
static size_t fn(const void* aPtr) \
{ \
return moz_malloc_size_of(aPtr); \
}
// This macro assumes the presence of appropriate |aHandleReport| and |aData|
// variables. The (void) is there because we should always ignore the return
// value of the callback, because callback failures aren't fatal.
#define MOZ_COLLECT_REPORT(path, kind, units, amount, description) \
(void)aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(path), \
kind, units, amount, \
NS_LITERAL_CSTRING(description), aData)
%}

View file

@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "nsISupports.idl"
interface nsIRunnable;
/**
* This service allows access to the current thread's Chromium MessageLoop
* instance, with some extra sugar added. If you're calling from C++, it may
* or may not make sense for you to use this interface. If you're calling from
* JS, you don't have a choice!
*
* Right now, you can only call PostIdleTask(), and the wrath of khuey is
* stopping you from adding other methods.
*
* nsIMessageLoop's contractid is "@mozilla.org/message-loop;1".
*/
[scriptable, uuid(3E8C58E8-E52C-43E0-8E66-669CA788FF5F)]
interface nsIMessageLoop : nsISupports
{
/**
* Posts a task to be run when this thread's message loop is idle, or after
* ensureRunsAfterMS milliseconds have elapsed. (That is, the task is
* guaranteed to run /eventually/.)
*
* Note that if the event loop is busy, we will hold a reference to the task
* until ensureRunsAfterMS milliseconds have elapsed. Be careful when
* specifying long timeouts and tasks which hold references to windows or
* other large objects, because you can leak memory in a difficult-to-detect
* way!
*/
void postIdleTask(in nsIRunnable task, in uint32_t ensureRunsAfterMS);
};

22
xpcom/base/nsIMutable.idl Normal file
View file

@ -0,0 +1,22 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
#include "nsISupports.idl"
/**
* nsIMutable defines an interface to be implemented by objects which
* can be made immutable.
*/
[scriptable, uuid(321578d0-03c1-4d95-8821-021ac612d18d)]
interface nsIMutable : nsISupports
{
/**
* Control whether or not this object can be modified. If the flag is
* false, no modification is allowed. Once the flag has been set to false,
* it cannot be reset back to true -- attempts to do so throw
* NS_ERROR_INVALID_ARG.
*/
attribute boolean mutable;
};

View file

@ -0,0 +1,25 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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/. */
#include "nsISupports.idl"
/**
* Legacy constants for specifying programming languages.
*
* JAVASCRIPT is needed to avoid breaking addons that use it in nsIClassInfo
* to define fields that are no longer needed.
*
* UNKNOWN and JAVASCRIPT are also used in implementations of
* nsIStackFrame::language.
*/
[scriptable, uuid(02ad9f22-3c98-46f3-be4e-2f5c9299e29a)]
interface nsIProgrammingLanguage : nsISupports
{
const uint32_t UNKNOWN = 0;
// 1 is unused.
const uint32_t JAVASCRIPT = 2;
};

View file

@ -0,0 +1,20 @@
/* 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/. */
#include "nsISupports.idl"
/*
* Holds localization message tag and message category
* for security related console messages.
*/
[uuid(FE9FC9B6-DDE2-11E2-A8F1-0A326188709B)]
interface nsISecurityConsoleMessage : nsISupports
{
attribute AString tag;
attribute AString category;
};
%{ C++
#define NS_SECURITY_CONSOLE_MESSAGE_CONTRACTID "@mozilla.org/securityconsole/message;1"
%}

35
xpcom/base/nsISizeOf.h Normal file
View file

@ -0,0 +1,35 @@
/* -*- 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 nsISizeOf_h___
#define nsISizeOf_h___
#include "mozilla/MemoryReporting.h"
#include "nsISupports.h"
#define NS_ISIZEOF_IID \
{0x61d05579, 0xd7ec, 0x485c, \
{ 0xa4, 0x0c, 0x31, 0xc7, 0x9a, 0x5c, 0xf9, 0xf3 }}
class nsISizeOf : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISIZEOF_IID)
/**
* Measures the size of the things pointed to by the object.
*/
virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
/**
* Like SizeOfExcludingThis, but also includes the size of the object itself.
*/
virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsISizeOf, NS_ISIZEOF_IID)
#endif /* nsISizeOf_h___ */

View file

@ -0,0 +1,90 @@
/* -*- 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/. */
#include "nsISupports.idl"
interface nsISimpleEnumerator;
/*
* Status reporters show Firefox's service status.
*/
[scriptable, uuid(ffcb716c-deeb-44ea-9d9d-ab25dc6980a8)]
interface nsIStatusReporter : nsISupports
{
readonly attribute ACString name;
/*
* The name of the process containing this reporter. Each reporter initially
* has "" in this field, indicating that it applies to the current process.
*/
readonly attribute ACString process;
/*
* A human-readable status description.
*/
readonly attribute AUTF8String description;
};
[scriptable, uuid(fd531273-3319-4fcd-90f2-9f53876c3828)]
interface nsIStatusReporterManager : nsISupports
{
/*
* Return an enumerator of nsIStatusReporters that are currently registered.
*/
nsISimpleEnumerator enumerateReporters();
/*
* Register the given nsIStatusReporter. After a reporter is registered,
* it will be available via enumerateReporters(). The Manager service
* will hold a strong reference to the given reporter.
*/
void registerReporter(in nsIStatusReporter reporter);
/*
* Unregister the given status reporter.
*/
void unregisterReporter(in nsIStatusReporter reporter);
/*
* Initialize.
*/
void init();
/*
* Dump service status as a json file
*/
void dumpReports();
};
%{C++
/*
* Note that this defaults 'process' to "", which is usually what's desired.
*/
#define NS_STATUS_REPORTER_IMPLEMENT(_classname, _name, _desc_Function) \
class StatusReporter_##_classname final : public nsIStatusReporter { \
~StatusReporter_##_classname() {} \
public: \
NS_DECL_ISUPPORTS \
NS_IMETHOD GetName(nsACString &name) override \
{ name.AssignLiteral(_name); return NS_OK; } \
NS_IMETHOD GetProcess(nsACString &process) override \
{ process.Truncate(); return NS_OK; } \
NS_IMETHOD GetDescription(nsACString &desc) override \
{ _desc_Function(desc); return NS_OK; } \
}; \
NS_IMPL_ISUPPORTS(StatusReporter_##_classname, nsIStatusReporter)
#define NS_STATUS_REPORTER_NAME(_classname) StatusReporter_##_classname
nsresult NS_RegisterStatusReporter(nsIStatusReporter *reporter);
nsresult NS_UnregisterStatusReporter(nsIStatusReporter *reporter);
nsresult NS_DumpStatusReporter();
#define NS_STATUS_REPORTER_MANAGER_CID \
{ 0xe8eb4e7e, 0xf2cf, 0x45e5, \
{ 0xb8, 0xa4, 0x6a, 0x0f, 0x50, 0x18, 0x84, 0x63 } }
%}

View file

@ -0,0 +1,44 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
/**
* The mother of all xpcom interfaces.
*/
/* In order to get both the right typelib and the right header we force
* the 'real' output from xpidl to be commented out in the generated header
* and includes a copy of the original nsISupports.h. This is all just to deal
* with the Mac specific ": public __comobject" thing.
*/
#include "nsrootidl.idl"
%{C++
/*
* Start commenting out the C++ versions of the below in the output header
*/
#if 0
%}
[scriptable, uuid(00000000-0000-0000-c000-000000000046)]
interface nsISupports {
void QueryInterface(in nsIIDRef uuid,
[iid_is(uuid),retval] out nsQIResult result);
[noscript, notxpcom] nsrefcnt AddRef();
[noscript, notxpcom] nsrefcnt Release();
};
%{C++
/*
* End commenting out the C++ versions of the above in the output header
*/
#endif
%}
%{C++
#include "nsISupportsBase.h"
#include "nsISupportsUtils.h"
%}

View file

@ -0,0 +1,85 @@
/* -*- 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/. */
// IWYU pragma: private, include "nsISupports.h"
#ifndef nsISupportsBase_h__
#define nsISupportsBase_h__
#ifndef nscore_h___
#include "nscore.h"
#endif
#ifndef nsID_h__
#include "nsID.h"
#endif
/*@{*/
/**
* IID for the nsISupports interface
* {00000000-0000-0000-c000-000000000046}
*
* To maintain binary compatibility with COM's IUnknown, we define the IID
* of nsISupports to be the same as that of COM's IUnknown.
*/
#define NS_ISUPPORTS_IID \
{ 0x00000000, 0x0000, 0x0000, \
{0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46} }
/**
* Basic component object model interface. Objects which implement
* this interface support runtime interface discovery (QueryInterface)
* and a reference counted memory model (AddRef/Release). This is
* modelled after the win32 IUnknown API.
*/
class NS_NO_VTABLE nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISUPPORTS_IID)
/**
* @name Methods
*/
//@{
/**
* A run time mechanism for interface discovery.
* @param aIID [in] A requested interface IID
* @param aInstancePtr [out] A pointer to an interface pointer to
* receive the result.
* @return <b>NS_OK</b> if the interface is supported by the associated
* instance, <b>NS_NOINTERFACE</b> if it is not.
*
* aInstancePtr must not be null.
*/
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) = 0;
/**
* Increases the reference count for this interface.
* The associated instance will not be deleted unless
* the reference count is returned to zero.
*
* @return The resulting reference count.
*/
NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
/**
* Decreases the reference count for this interface.
* Generally, if the reference count returns to zero,
* the associated instance is deleted.
*
* @return The resulting reference count.
*/
NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
//@}
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsISupports, NS_ISUPPORTS_IID)
/*@}*/
#endif

View file

@ -0,0 +1,39 @@
/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "nsISupports.idl"
[ptr] native nsNonConstIDPtr(nsID);
/**
* nsIUUIDGenerator is implemented by a service that can generate
* universally unique identifiers, ideally using any platform-native
* method for generating UUIDs.
*/
[scriptable, uuid(138ad1b2-c694-41cc-b201-333ce936d8b8)]
interface nsIUUIDGenerator : nsISupports
{
/**
* Obtains a new UUID using appropriate platform-specific methods to
* obtain a nsID that can be considered to be globally unique.
*
* @returns an nsID filled in with a new UUID.
*
* @throws NS_ERROR_FAILURE if a UUID cannot be generated (e.g. if
* an underlying source of randomness is not available)
*/
nsIDPtr generateUUID();
/**
* Obtain a new UUID like the generateUUID method, but place it in
* the provided nsID pointer instead of allocating a new nsID.
*
* @param id an existing nsID pointer where the UUID will be stored.
*
* @throws NS_ERROR_FAILURE if a UUID cannot be generated (e.g. if
* an underlying source of randomness is not available)
*/
[noscript] void generateUUIDInPlace(in nsNonConstIDPtr id);
};

View file

@ -0,0 +1,49 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "nsISupports.idl"
/**
* Version strings are dot-separated sequences of version-parts.
*
* A version-part consists of up to four parts, all of which are optional:
*
* <number-a><string-b><number-c><string-d (everything else)>
*
* A version-part may also consist of a single asterisk "*" which indicates
* "infinity".
*
* Numbers are base-10, and are zero if left out.
* Strings are compared bytewise.
*
* For additional backwards compatibility, if "string-b" is "+" then
* "number-a" is incremented by 1 and "string-b" becomes "pre".
*
* 1.0pre1
* < 1.0pre2
* < 1.0 == 1.0.0 == 1.0.0.0
* < 1.1pre == 1.1pre0 == 1.0+
* < 1.1pre1a
* < 1.1pre1
* < 1.1pre10a
* < 1.1pre10
*
* Although not required by this interface, it is recommended that
* numbers remain within the limits of a signed char, i.e. -127 to 128.
*/
[scriptable, uuid(e6cd620a-edbb-41d2-9e42-9a2ffc8107f3)]
interface nsIVersionComparator : nsISupports
{
/**
* Compare two version strings
* @param A The first version
* @param B The second version
* @returns < 0 if A < B
* = 0 if A == B
* > 0 if A > B
*/
long compare(in ACString A, in ACString B);
};

View file

@ -0,0 +1,74 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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/. */
#include "nsISupports.idl"
%{C++
#include "mozilla/MemoryReporting.h"
%}
/**
* An instance of |nsIWeakReference| is a proxy object that cooperates with
* its referent to give clients a non-owning, non-dangling reference. Clients
* own the proxy, and should generally manage it with an |nsCOMPtr| (see the
* type |nsWeakPtr| for a |typedef| name that stands out) as they would any
* other XPCOM object. The |QueryReferent| member function provides a
* (hopefully short-lived) owning reference on demand, through which clients
* can get useful access to the referent, while it still exists.
*
* @version 1.0
* @see nsISupportsWeakReference
* @see nsWeakReference
* @see nsWeakPtr
*/
[scriptable, uuid(9188bc85-f92e-11d2-81ef-0060083a0bcf)]
interface nsIWeakReference : nsISupports
{
/**
* |QueryReferent| queries the referent, if it exists, and like |QueryInterface|, produces
* an owning reference to the desired interface. It is designed to look and act exactly
* like (a proxied) |QueryInterface|. Don't hold on to the produced interface permanently;
* that would defeat the purpose of using a non-owning |nsIWeakReference| in the first place.
*/
void QueryReferent( in nsIIDRef uuid, [iid_is(uuid), retval] out nsQIResult result );
%{C++
virtual size_t SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
%}
};
/**
* |nsISupportsWeakReference| is a factory interface which produces appropriate
* instances of |nsIWeakReference|. Weak references in this scheme can only be
* produced for objects that implement this interface.
*
* @version 1.0
* @see nsIWeakReference
* @see nsSupportsWeakReference
*/
[scriptable, uuid(9188bc86-f92e-11d2-81ef-0060083a0bcf)]
interface nsISupportsWeakReference : nsISupports
{
/**
* |GetWeakReference| produces an appropriate instance of |nsIWeakReference|.
* As with all good XPCOM `getters', you own the resulting interface and should
* manage it with an |nsCOMPtr|.
*
* @see nsIWeakReference
* @see nsWeakPtr
* @see nsCOMPtr
*/
nsIWeakReference GetWeakReference();
};
%{C++
#ifdef MOZILLA_INTERNAL_API
#include "nsIWeakReferenceUtils.h"
#endif
%}

View file

@ -0,0 +1,86 @@
/* -*- 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/. */
#include "nsInterfaceRequestorAgg.h"
#include "nsIInterfaceRequestor.h"
#include "nsCOMPtr.h"
#include "mozilla/Attributes.h"
#include "nsThreadUtils.h"
#include "nsProxyRelease.h"
class nsInterfaceRequestorAgg final : public nsIInterfaceRequestor
{
public:
// XXX This needs to support threadsafe refcounting until we fix bug 243591.
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINTERFACEREQUESTOR
nsInterfaceRequestorAgg(nsIInterfaceRequestor* aFirst,
nsIInterfaceRequestor* aSecond,
nsIEventTarget* aConsumerTarget = nullptr)
: mFirst(aFirst)
, mSecond(aSecond)
, mConsumerTarget(aConsumerTarget)
{
if (!mConsumerTarget) {
mConsumerTarget = NS_GetCurrentThread();
}
}
private:
~nsInterfaceRequestorAgg();
nsCOMPtr<nsIInterfaceRequestor> mFirst, mSecond;
nsCOMPtr<nsIEventTarget> mConsumerTarget;
};
NS_IMPL_ISUPPORTS(nsInterfaceRequestorAgg, nsIInterfaceRequestor)
NS_IMETHODIMP
nsInterfaceRequestorAgg::GetInterface(const nsIID& aIID, void** aResult)
{
nsresult rv = NS_ERROR_NO_INTERFACE;
if (mFirst) {
rv = mFirst->GetInterface(aIID, aResult);
}
if (mSecond && NS_FAILED(rv)) {
rv = mSecond->GetInterface(aIID, aResult);
}
return rv;
}
nsInterfaceRequestorAgg::~nsInterfaceRequestorAgg()
{
NS_ProxyRelease(mConsumerTarget, mFirst.forget());
NS_ProxyRelease(mConsumerTarget, mSecond.forget());
}
nsresult
NS_NewInterfaceRequestorAggregation(nsIInterfaceRequestor* aFirst,
nsIInterfaceRequestor* aSecond,
nsIInterfaceRequestor** aResult)
{
*aResult = new nsInterfaceRequestorAgg(aFirst, aSecond);
if (!*aResult) {
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ADDREF(*aResult);
return NS_OK;
}
nsresult
NS_NewInterfaceRequestorAggregation(nsIInterfaceRequestor* aFirst,
nsIInterfaceRequestor* aSecond,
nsIEventTarget* aTarget,
nsIInterfaceRequestor** aResult)
{
*aResult = new nsInterfaceRequestorAgg(aFirst, aSecond, aTarget);
if (!*aResult) {
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ADDREF(*aResult);
return NS_OK;
}

View file

@ -0,0 +1,38 @@
/* -*- 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 nsInterfaceRequestorAgg_h__
#define nsInterfaceRequestorAgg_h__
#include "nsError.h"
class nsIEventTarget;
class nsIInterfaceRequestor;
/**
* This function returns an instance of nsIInterfaceRequestor that aggregates
* two nsIInterfaceRequestor instances. Its GetInterface method queries
* aFirst for the requested interface and will query aSecond only if aFirst
* failed to supply the requested interface. Both aFirst and aSecond may
* be null, and will be released on the main thread when the aggregator is
* destroyed.
*/
extern nsresult
NS_NewInterfaceRequestorAggregation(nsIInterfaceRequestor* aFirst,
nsIInterfaceRequestor* aSecond,
nsIInterfaceRequestor** aResult);
/**
* Like the previous method, but aFirst and aSecond will be released on the
* provided target thread.
*/
extern nsresult
NS_NewInterfaceRequestorAggregation(nsIInterfaceRequestor* aFirst,
nsIInterfaceRequestor* aSecond,
nsIEventTarget* aTarget,
nsIInterfaceRequestor** aResult);
#endif // !defined( nsInterfaceRequestorAgg_h__ )

View file

@ -0,0 +1,147 @@
/* -*- 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/. */
#include "nsMacUtilsImpl.h"
#include <CoreFoundation/CoreFoundation.h>
NS_IMPL_ISUPPORTS(nsMacUtilsImpl, nsIMacUtils)
nsresult
nsMacUtilsImpl::GetArchString(nsAString& aArchString)
{
if (!mBinaryArchs.IsEmpty()) {
aArchString.Assign(mBinaryArchs);
return NS_OK;
}
aArchString.Truncate();
bool foundPPC = false,
foundX86 = false,
foundPPC64 = false,
foundX86_64 = false;
CFBundleRef mainBundle = ::CFBundleGetMainBundle();
if (!mainBundle) {
return NS_ERROR_FAILURE;
}
CFArrayRef archList = ::CFBundleCopyExecutableArchitectures(mainBundle);
if (!archList) {
return NS_ERROR_FAILURE;
}
CFIndex archCount = ::CFArrayGetCount(archList);
for (CFIndex i = 0; i < archCount; i++) {
CFNumberRef arch =
static_cast<CFNumberRef>(::CFArrayGetValueAtIndex(archList, i));
int archInt = 0;
if (!::CFNumberGetValue(arch, kCFNumberIntType, &archInt)) {
::CFRelease(archList);
return NS_ERROR_FAILURE;
}
if (archInt == kCFBundleExecutableArchitecturePPC) {
foundPPC = true;
} else if (archInt == kCFBundleExecutableArchitectureI386) {
foundX86 = true;
} else if (archInt == kCFBundleExecutableArchitecturePPC64) {
foundPPC64 = true;
} else if (archInt == kCFBundleExecutableArchitectureX86_64) {
foundX86_64 = true;
}
}
::CFRelease(archList);
// The order in the string must always be the same so
// don't do this in the loop.
if (foundPPC) {
mBinaryArchs.AppendLiteral("ppc");
}
if (foundX86) {
if (!mBinaryArchs.IsEmpty()) {
mBinaryArchs.Append('-');
}
mBinaryArchs.AppendLiteral("i386");
}
if (foundPPC64) {
if (!mBinaryArchs.IsEmpty()) {
mBinaryArchs.Append('-');
}
mBinaryArchs.AppendLiteral("ppc64");
}
if (foundX86_64) {
if (!mBinaryArchs.IsEmpty()) {
mBinaryArchs.Append('-');
}
mBinaryArchs.AppendLiteral("x86_64");
}
aArchString.Assign(mBinaryArchs);
return (aArchString.IsEmpty() ? NS_ERROR_FAILURE : NS_OK);
}
NS_IMETHODIMP
nsMacUtilsImpl::GetIsUniversalBinary(bool* aIsUniversalBinary)
{
if (NS_WARN_IF(!aIsUniversalBinary)) {
return NS_ERROR_INVALID_ARG;
}
*aIsUniversalBinary = false;
nsAutoString archString;
nsresult rv = GetArchString(archString);
if (NS_FAILED(rv)) {
return rv;
}
// The delimiter char in the arch string is '-', so if that character
// is in the string we know we have multiple architectures.
*aIsUniversalBinary = (archString.Find("-") > -1);
return NS_OK;
}
NS_IMETHODIMP
nsMacUtilsImpl::GetArchitecturesInBinary(nsAString& aArchString)
{
return GetArchString(aArchString);
}
// True when running under binary translation (Rosetta).
NS_IMETHODIMP
nsMacUtilsImpl::GetIsTranslated(bool* aIsTranslated)
{
#ifdef __ppc__
static bool sInitialized = false;
// Initialize sIsNative to 1. If the sysctl fails because it doesn't
// exist, then translation is not possible, so the process must not be
// running translated.
static int32_t sIsNative = 1;
if (!sInitialized) {
size_t sz = sizeof(sIsNative);
sysctlbyname("sysctl.proc_native", &sIsNative, &sz, nullptr, 0);
sInitialized = true;
}
*aIsTranslated = !sIsNative;
#else
// Translation only exists for ppc code. Other architectures aren't
// translated.
*aIsTranslated = false;
#endif
return NS_OK;
}

View file

@ -0,0 +1,42 @@
/* -*- 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 nsMacUtilsImpl_h___
#define nsMacUtilsImpl_h___
#include "nsIMacUtils.h"
#include "nsString.h"
#include "mozilla/Attributes.h"
class nsMacUtilsImpl final : public nsIMacUtils
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMACUTILS
nsMacUtilsImpl()
{
}
private:
~nsMacUtilsImpl()
{
}
nsresult GetArchString(nsAString& aArchString);
// A string containing a "-" delimited list of architectures
// in our binary.
nsString mBinaryArchs;
};
// Global singleton service
// 697BD3FD-43E5-41CE-AD5E-C339175C0818
#define NS_MACUTILSIMPL_CID \
{0x697BD3FD, 0x43E5, 0x41CE, {0xAD, 0x5E, 0xC3, 0x39, 0x17, 0x5C, 0x08, 0x18}}
#define NS_MACUTILSIMPL_CONTRACTID "@mozilla.org/xpcom/mac-utils;1"
#endif /* nsMacUtilsImpl_h___ */

184
xpcom/base/nsMemoryImpl.cpp Normal file
View file

@ -0,0 +1,184 @@
/* -*- 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/. */
#include "nsMemoryImpl.h"
#include "nsThreadUtils.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsISimpleEnumerator.h"
#include "nsCOMPtr.h"
#include "mozilla/Services.h"
#ifdef ANDROID
#include <stdio.h>
// Minimum memory threshold for a device to be considered
// a low memory platform. This value has be in sync with
// Java's equivalent threshold, defined in
// mobile/android/base/util/HardwareUtils.java
#define LOW_MEMORY_THRESHOLD_KB (384 * 1024)
#endif
static nsMemoryImpl sGlobalMemory;
NS_IMPL_QUERY_INTERFACE(nsMemoryImpl, nsIMemory)
NS_IMETHODIMP
nsMemoryImpl::HeapMinimize(bool aImmediate)
{
return FlushMemory(u"heap-minimize", aImmediate);
}
NS_IMETHODIMP
nsMemoryImpl::IsLowMemoryPlatform(bool* aResult)
{
#ifdef ANDROID
static int sLowMemory = -1; // initialize to unknown, lazily evaluate to 0 or 1
if (sLowMemory == -1) {
sLowMemory = 0; // assume "not low memory" in case file operations fail
*aResult = false;
// check if MemTotal from /proc/meminfo is less than LOW_MEMORY_THRESHOLD_KB
FILE* fd = fopen("/proc/meminfo", "r");
if (!fd) {
return NS_OK;
}
uint64_t mem = 0;
int rv = fscanf(fd, "MemTotal: %llu kB", &mem);
if (fclose(fd)) {
return NS_OK;
}
if (rv != 1) {
return NS_OK;
}
sLowMemory = (mem < LOW_MEMORY_THRESHOLD_KB) ? 1 : 0;
}
*aResult = (sLowMemory == 1);
#else
*aResult = false;
#endif
return NS_OK;
}
/*static*/ nsresult
nsMemoryImpl::Create(nsISupports* aOuter, const nsIID& aIID, void** aResult)
{
if (NS_WARN_IF(aOuter)) {
return NS_ERROR_NO_AGGREGATION;
}
return sGlobalMemory.QueryInterface(aIID, aResult);
}
nsresult
nsMemoryImpl::FlushMemory(const char16_t* aReason, bool aImmediate)
{
nsresult rv = NS_OK;
if (aImmediate) {
// They've asked us to run the flusher *immediately*. We've
// got to be on the UI main thread for us to be able to do
// that...are we?
if (!NS_IsMainThread()) {
NS_ERROR("can't synchronously flush memory: not on UI thread");
return NS_ERROR_FAILURE;
}
}
bool lastVal = sIsFlushing.exchange(true);
if (lastVal) {
return NS_OK;
}
PRIntervalTime now = PR_IntervalNow();
// Run the flushers immediately if we can; otherwise, proxy to the
// UI thread an run 'em asynchronously.
if (aImmediate) {
rv = RunFlushers(aReason);
} else {
// Don't broadcast more than once every 1000ms to avoid being noisy
if (PR_IntervalToMicroseconds(now - sLastFlushTime) > 1000) {
sFlushEvent.mReason = aReason;
rv = NS_DispatchToMainThread(&sFlushEvent);
}
}
sLastFlushTime = now;
return rv;
}
nsresult
nsMemoryImpl::RunFlushers(const char16_t* aReason)
{
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
// Instead of:
// os->NotifyObservers(this, "memory-pressure", aReason);
// we are going to do this manually to see who/what is
// deallocating.
nsCOMPtr<nsISimpleEnumerator> e;
os->EnumerateObservers("memory-pressure", getter_AddRefs(e));
if (e) {
nsCOMPtr<nsIObserver> observer;
bool loop = true;
while (NS_SUCCEEDED(e->HasMoreElements(&loop)) && loop) {
nsCOMPtr<nsISupports> supports;
e->GetNext(getter_AddRefs(supports));
if (!supports) {
continue;
}
observer = do_QueryInterface(supports);
observer->Observe(observer, "memory-pressure", aReason);
}
}
}
sIsFlushing = false;
return NS_OK;
}
// XXX need NS_IMPL_STATIC_ADDREF/RELEASE
NS_IMETHODIMP_(MozExternalRefCountType)
nsMemoryImpl::FlushEvent::AddRef()
{
return 2;
}
NS_IMETHODIMP_(MozExternalRefCountType)
nsMemoryImpl::FlushEvent::Release()
{
return 1;
}
NS_IMPL_QUERY_INTERFACE(nsMemoryImpl::FlushEvent, nsIRunnable)
NS_IMETHODIMP
nsMemoryImpl::FlushEvent::Run()
{
sGlobalMemory.RunFlushers(mReason);
return NS_OK;
}
mozilla::Atomic<bool>
nsMemoryImpl::sIsFlushing;
PRIntervalTime
nsMemoryImpl::sLastFlushTime = 0;
nsMemoryImpl::FlushEvent
nsMemoryImpl::sFlushEvent;
nsresult
NS_GetMemoryManager(nsIMemory** aResult)
{
return sGlobalMemory.QueryInterface(NS_GET_IID(nsIMemory), (void**)aResult);
}

55
xpcom/base/nsMemoryImpl.h Normal file
View file

@ -0,0 +1,55 @@
/* -*- 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 nsMemoryImpl_h__
#define nsMemoryImpl_h__
#include "mozilla/Atomics.h"
#include "nsIMemory.h"
#include "nsIRunnable.h"
// nsMemoryImpl is a static object. We can do this because it doesn't have
// a constructor/destructor or any instance members. Please don't add
// instance member variables, only static member variables.
class nsMemoryImpl : public nsIMemory
{
public:
// We don't use the generic macros because we are a special static object
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aResult) override;
NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override
{
return 1;
}
NS_IMETHOD_(MozExternalRefCountType) Release(void) override
{
return 1;
}
NS_DECL_NSIMEMORY
static nsresult Create(nsISupports* aOuter,
const nsIID& aIID, void** aResult);
nsresult FlushMemory(const char16_t* aReason, bool aImmediate);
nsresult RunFlushers(const char16_t* aReason);
protected:
struct FlushEvent : public nsIRunnable
{
constexpr FlushEvent() : mReason(nullptr) {}
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIRUNNABLE
const char16_t* mReason;
};
static mozilla::Atomic<bool> sIsFlushing;
static FlushEvent sFlushEvent;
static PRIntervalTime sLastFlushTime;
};
#endif // nsMemoryImpl_h__

View file

@ -0,0 +1,830 @@
/* -*- 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/. */
#include "mozilla/JSONWriter.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/nsMemoryInfoDumper.h"
#include "mozilla/DebugOnly.h"
#include "nsDumpUtils.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ContentChild.h"
#include "nsIConsoleService.h"
#include "nsCycleCollector.h"
#include "nsICycleCollectorListener.h"
#include "nsIMemoryReporter.h"
#include "nsDirectoryServiceDefs.h"
#include "nsGZFileWriter.h"
#include "nsJSEnvironment.h"
#include "nsPrintfCString.h"
#include "nsISimpleEnumerator.h"
#include "nsServiceManagerUtils.h"
#include "nsIFile.h"
#ifdef XP_WIN
#include <process.h>
#ifndef getpid
#define getpid _getpid
#endif
#else
#include <unistd.h>
#endif
#ifdef XP_UNIX
#define MOZ_SUPPORTS_FIFO 1
#endif
#if defined(XP_LINUX) || defined(__FreeBSD__)
#define MOZ_SUPPORTS_RT_SIGNALS 1
#endif
#if defined(MOZ_SUPPORTS_RT_SIGNALS)
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif
#if defined(MOZ_SUPPORTS_FIFO)
#include "mozilla/Preferences.h"
#endif
using namespace mozilla;
using namespace mozilla::dom;
namespace {
class DumpMemoryInfoToTempDirRunnable : public Runnable
{
public:
DumpMemoryInfoToTempDirRunnable(const nsAString& aIdentifier,
bool aAnonymize, bool aMinimizeMemoryUsage)
: mIdentifier(aIdentifier)
, mAnonymize(aAnonymize)
, mMinimizeMemoryUsage(aMinimizeMemoryUsage)
{
}
NS_IMETHOD Run() override
{
nsCOMPtr<nsIMemoryInfoDumper> dumper =
do_GetService("@mozilla.org/memory-info-dumper;1");
dumper->DumpMemoryInfoToTempDir(mIdentifier, mAnonymize,
mMinimizeMemoryUsage);
return NS_OK;
}
private:
const nsString mIdentifier;
const bool mAnonymize;
const bool mMinimizeMemoryUsage;
};
class GCAndCCLogDumpRunnable final
: public Runnable
, public nsIDumpGCAndCCLogsCallback
{
public:
NS_DECL_ISUPPORTS_INHERITED
GCAndCCLogDumpRunnable(const nsAString& aIdentifier,
bool aDumpAllTraces,
bool aDumpChildProcesses)
: mIdentifier(aIdentifier)
, mDumpAllTraces(aDumpAllTraces)
, mDumpChildProcesses(aDumpChildProcesses)
{
}
NS_IMETHOD Run() override
{
nsCOMPtr<nsIMemoryInfoDumper> dumper =
do_GetService("@mozilla.org/memory-info-dumper;1");
dumper->DumpGCAndCCLogsToFile(mIdentifier, mDumpAllTraces,
mDumpChildProcesses, this);
return NS_OK;
}
NS_IMETHOD OnDump(nsIFile* aGCLog, nsIFile* aCCLog, bool aIsParent) override
{
return NS_OK;
}
NS_IMETHOD OnFinish() override
{
return NS_OK;
}
private:
~GCAndCCLogDumpRunnable() {}
const nsString mIdentifier;
const bool mDumpAllTraces;
const bool mDumpChildProcesses;
};
NS_IMPL_ISUPPORTS_INHERITED(GCAndCCLogDumpRunnable, Runnable,
nsIDumpGCAndCCLogsCallback)
} // namespace
#if defined(MOZ_SUPPORTS_RT_SIGNALS) // {
namespace {
/*
* The following code supports dumping about:memory upon receiving a signal.
*
* We listen for the following signals:
*
* - SIGRTMIN: Dump our memory reporters (and those of our child
* processes),
* - SIGRTMIN + 1: Dump our memory reporters (and those of our child
* processes) after minimizing memory usage, and
* - SIGRTMIN + 2: Dump the GC and CC logs in this and our child processes.
*
* When we receive one of these signals, we write the signal number to a pipe.
* The IO thread then notices that the pipe has been written to, and kicks off
* the appropriate task on the main thread.
*
* This scheme is similar to using signalfd(), except it's portable and it
* doesn't require the use of sigprocmask, which is problematic because it
* masks signals received by child processes.
*
* In theory, we could use Chromium's MessageLoopForIO::CatchSignal() for this.
* But that uses libevent, which does not handle the realtime signals (bug
* 794074).
*/
// It turns out that at least on some systems, SIGRTMIN is not a compile-time
// constant, so these have to be set at runtime.
static uint8_t sDumpAboutMemorySignum; // SIGRTMIN
static uint8_t sDumpAboutMemoryAfterMMUSignum; // SIGRTMIN + 1
static uint8_t sGCAndCCDumpSignum; // SIGRTMIN + 2
void doMemoryReport(const uint8_t aRecvSig)
{
// Dump our memory reports (but run this on the main thread!).
bool minimize = aRecvSig == sDumpAboutMemoryAfterMMUSignum;
LOG("SignalWatcher(sig %d) dispatching memory report runnable.", aRecvSig);
RefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
/* anonymize = */ false,
minimize);
NS_DispatchToMainThread(runnable);
}
void doGCCCDump(const uint8_t aRecvSig)
{
LOG("SignalWatcher(sig %d) dispatching GC/CC log runnable.", aRecvSig);
// Dump GC and CC logs (from the main thread).
RefPtr<GCAndCCLogDumpRunnable> runnable =
new GCAndCCLogDumpRunnable(/* identifier = */ EmptyString(),
/* allTraces = */ true,
/* dumpChildProcesses = */ true);
NS_DispatchToMainThread(runnable);
}
} // namespace
#endif // MOZ_SUPPORTS_RT_SIGNALS }
#if defined(MOZ_SUPPORTS_FIFO) // {
namespace {
void
doMemoryReport(const nsCString& aInputStr)
{
bool minimize = aInputStr.EqualsLiteral("minimize memory report");
LOG("FifoWatcher(command:%s) dispatching memory report runnable.",
aInputStr.get());
RefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
/* anonymize = */ false,
minimize);
NS_DispatchToMainThread(runnable);
}
void
doGCCCDump(const nsCString& aInputStr)
{
bool doAllTracesGCCCDump = aInputStr.EqualsLiteral("gc log");
LOG("FifoWatcher(command:%s) dispatching GC/CC log runnable.", aInputStr.get());
RefPtr<GCAndCCLogDumpRunnable> runnable =
new GCAndCCLogDumpRunnable(/* identifier = */ EmptyString(),
doAllTracesGCCCDump,
/* dumpChildProcesses = */ true);
NS_DispatchToMainThread(runnable);
}
bool
SetupFifo()
{
#ifdef DEBUG
static bool fifoCallbacksRegistered = false;
#endif
if (!FifoWatcher::MaybeCreate()) {
return false;
}
MOZ_ASSERT(!fifoCallbacksRegistered,
"FifoWatcher callbacks should be registered only once");
FifoWatcher* fw = FifoWatcher::GetSingleton();
// Dump our memory reports (but run this on the main thread!).
fw->RegisterCallback(NS_LITERAL_CSTRING("memory report"),
doMemoryReport);
fw->RegisterCallback(NS_LITERAL_CSTRING("minimize memory report"),
doMemoryReport);
// Dump GC and CC logs (from the main thread).
fw->RegisterCallback(NS_LITERAL_CSTRING("gc log"),
doGCCCDump);
fw->RegisterCallback(NS_LITERAL_CSTRING("abbreviated gc log"),
doGCCCDump);
#ifdef DEBUG
fifoCallbacksRegistered = true;
#endif
return true;
}
void
OnFifoEnabledChange(const char* /*unused*/, void* /*unused*/)
{
LOG("%s changed", FifoWatcher::kPrefName);
if (SetupFifo()) {
Preferences::UnregisterCallback(OnFifoEnabledChange,
FifoWatcher::kPrefName,
nullptr);
}
}
} // namespace
#endif // MOZ_SUPPORTS_FIFO }
NS_IMPL_ISUPPORTS(nsMemoryInfoDumper, nsIMemoryInfoDumper)
nsMemoryInfoDumper::nsMemoryInfoDumper()
{
}
nsMemoryInfoDumper::~nsMemoryInfoDumper()
{
}
/* static */ void
nsMemoryInfoDumper::Initialize()
{
#if defined(MOZ_SUPPORTS_RT_SIGNALS)
SignalPipeWatcher* sw = SignalPipeWatcher::GetSingleton();
// Dump memory reporters (and those of our child processes)
sDumpAboutMemorySignum = SIGRTMIN;
sw->RegisterCallback(sDumpAboutMemorySignum, doMemoryReport);
// Dump our memory reporters after minimizing memory usage
sDumpAboutMemoryAfterMMUSignum = SIGRTMIN + 1;
sw->RegisterCallback(sDumpAboutMemoryAfterMMUSignum, doMemoryReport);
// Dump the GC and CC logs in this and our child processes.
sGCAndCCDumpSignum = SIGRTMIN + 2;
sw->RegisterCallback(sGCAndCCDumpSignum, doGCCCDump);
#endif
#if defined(MOZ_SUPPORTS_FIFO)
if (!SetupFifo()) {
// NB: This gets loaded early enough that it's possible there is a user pref
// set to enable the fifo watcher that has not been loaded yet. Register
// to attempt to initialize if the fifo watcher becomes enabled by
// a user pref.
Preferences::RegisterCallback(OnFifoEnabledChange,
FifoWatcher::kPrefName,
nullptr);
}
#endif
}
static void
EnsureNonEmptyIdentifier(nsAString& aIdentifier)
{
if (!aIdentifier.IsEmpty()) {
return;
}
// If the identifier is empty, set it to the number of whole seconds since the
// epoch. This identifier will appear in the files that this process
// generates and also the files generated by this process's children, allowing
// us to identify which files are from the same memory report request.
aIdentifier.AppendInt(static_cast<int64_t>(PR_Now()) / 1000000);
}
// Use XPCOM refcounting to fire |onFinish| when all reference-holders
// (remote dump actors or the |DumpGCAndCCLogsToFile| activation itself)
// have gone away.
class nsDumpGCAndCCLogsCallbackHolder final
: public nsIDumpGCAndCCLogsCallback
{
public:
NS_DECL_ISUPPORTS
explicit nsDumpGCAndCCLogsCallbackHolder(nsIDumpGCAndCCLogsCallback* aCallback)
: mCallback(aCallback)
{
}
NS_IMETHOD OnFinish() override
{
return NS_ERROR_UNEXPECTED;
}
NS_IMETHOD OnDump(nsIFile* aGCLog, nsIFile* aCCLog, bool aIsParent) override
{
return mCallback->OnDump(aGCLog, aCCLog, aIsParent);
}
private:
~nsDumpGCAndCCLogsCallbackHolder()
{
Unused << mCallback->OnFinish();
}
nsCOMPtr<nsIDumpGCAndCCLogsCallback> mCallback;
};
NS_IMPL_ISUPPORTS(nsDumpGCAndCCLogsCallbackHolder, nsIDumpGCAndCCLogsCallback)
NS_IMETHODIMP
nsMemoryInfoDumper::DumpGCAndCCLogsToFile(const nsAString& aIdentifier,
bool aDumpAllTraces,
bool aDumpChildProcesses,
nsIDumpGCAndCCLogsCallback* aCallback)
{
nsString identifier(aIdentifier);
EnsureNonEmptyIdentifier(identifier);
nsCOMPtr<nsIDumpGCAndCCLogsCallback> callbackHolder =
new nsDumpGCAndCCLogsCallbackHolder(aCallback);
if (aDumpChildProcesses) {
nsTArray<ContentParent*> children;
ContentParent::GetAll(children);
for (uint32_t i = 0; i < children.Length(); i++) {
ContentParent* cp = children[i];
nsCOMPtr<nsICycleCollectorLogSink> logSink =
nsCycleCollector_createLogSink();
logSink->SetFilenameIdentifier(identifier);
logSink->SetProcessIdentifier(cp->Pid());
Unused << cp->CycleCollectWithLogs(aDumpAllTraces, logSink,
callbackHolder);
}
}
nsCOMPtr<nsICycleCollectorListener> logger =
do_CreateInstance("@mozilla.org/cycle-collector-logger;1");
if (aDumpAllTraces) {
nsCOMPtr<nsICycleCollectorListener> allTracesLogger;
logger->AllTraces(getter_AddRefs(allTracesLogger));
logger = allTracesLogger;
}
nsCOMPtr<nsICycleCollectorLogSink> logSink;
logger->GetLogSink(getter_AddRefs(logSink));
logSink->SetFilenameIdentifier(identifier);
nsJSContext::CycleCollectNow(logger);
nsCOMPtr<nsIFile> gcLog, ccLog;
logSink->GetGcLog(getter_AddRefs(gcLog));
logSink->GetCcLog(getter_AddRefs(ccLog));
callbackHolder->OnDump(gcLog, ccLog, /* parent = */ true);
return NS_OK;
}
NS_IMETHODIMP
nsMemoryInfoDumper::DumpGCAndCCLogsToSink(bool aDumpAllTraces,
nsICycleCollectorLogSink* aSink)
{
nsCOMPtr<nsICycleCollectorListener> logger =
do_CreateInstance("@mozilla.org/cycle-collector-logger;1");
if (aDumpAllTraces) {
nsCOMPtr<nsICycleCollectorListener> allTracesLogger;
logger->AllTraces(getter_AddRefs(allTracesLogger));
logger = allTracesLogger;
}
logger->SetLogSink(aSink);
nsJSContext::CycleCollectNow(logger);
return NS_OK;
}
static void
MakeFilename(const char* aPrefix, const nsAString& aIdentifier,
int aPid, const char* aSuffix, nsACString& aResult)
{
aResult = nsPrintfCString("%s-%s-%d.%s",
aPrefix,
NS_ConvertUTF16toUTF8(aIdentifier).get(),
aPid, aSuffix);
}
// This class wraps GZFileWriter so it can be used with JSONWriter, overcoming
// the following two problems:
// - It provides a JSONWriterFunc::Write() that calls nsGZFileWriter::Write().
// - It can be stored as a UniquePtr, whereas nsGZFileWriter is refcounted.
class GZWriterWrapper : public JSONWriteFunc
{
public:
explicit GZWriterWrapper(nsGZFileWriter* aGZWriter)
: mGZWriter(aGZWriter)
{}
void Write(const char* aStr)
{
// Ignore any failure because JSONWriteFunc doesn't have a mechanism for
// handling errors.
Unused << mGZWriter->Write(aStr);
}
nsresult Finish() { return mGZWriter->Finish(); }
private:
RefPtr<nsGZFileWriter> mGZWriter;
};
// We need two callbacks: one that handles reports, and one that is called at
// the end of reporting. Both the callbacks need access to the same JSONWriter,
// so we implement both of them in this one class.
class HandleReportAndFinishReportingCallbacks final
: public nsIHandleReportCallback, public nsIFinishReportingCallback
{
public:
NS_DECL_ISUPPORTS
HandleReportAndFinishReportingCallbacks(UniquePtr<JSONWriter> aWriter,
nsIFinishDumpingCallback* aFinishDumping,
nsISupports* aFinishDumpingData)
: mWriter(Move(aWriter))
, mFinishDumping(aFinishDumping)
, mFinishDumpingData(aFinishDumpingData)
{
}
// This is the callback for nsIHandleReportCallback.
NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
int32_t aKind, int32_t aUnits, int64_t aAmount,
const nsACString& aDescription,
nsISupports* aData) override
{
nsAutoCString process;
if (aProcess.IsEmpty()) {
// If the process is empty, the report originated with the process doing
// the dumping. In that case, generate the process identifier, which is
// of the form "$PROCESS_NAME (pid $PID)", or just "(pid $PID)" if we
// don't have a process name. If we're the main process, we let
// $PROCESS_NAME be "Main Process".
if (XRE_IsParentProcess()) {
// We're the main process.
process.AssignLiteral("Main Process");
} else if (ContentChild* cc = ContentChild::GetSingleton()) {
// Try to get the process name from ContentChild.
cc->GetProcessName(process);
}
ContentChild::AppendProcessId(process);
} else {
// Otherwise, the report originated with another process and already has a
// process name. Just use that.
process = aProcess;
}
mWriter->StartObjectElement();
{
mWriter->StringProperty("process", process.get());
mWriter->StringProperty("path", PromiseFlatCString(aPath).get());
mWriter->IntProperty("kind", aKind);
mWriter->IntProperty("units", aUnits);
mWriter->IntProperty("amount", aAmount);
mWriter->StringProperty("description",
PromiseFlatCString(aDescription).get());
}
mWriter->EndObject();
return NS_OK;
}
// This is the callback for nsIFinishReportingCallback.
NS_IMETHOD Callback(nsISupports* aData) override
{
mWriter->EndArray(); // end of "reports" array
mWriter->End();
// The call to Finish() deallocates the memory allocated by the first Write
// call. Because that memory was live while the memory reporters ran and
// was measured by them -- by "heap-allocated" if nothing else -- we want
// DMD to see it as well. So we deliberately don't call Finish() until
// after DMD finishes.
nsresult rv = static_cast<GZWriterWrapper*>(mWriter->WriteFunc())->Finish();
NS_ENSURE_SUCCESS(rv, rv);
if (!mFinishDumping) {
return NS_OK;
}
return mFinishDumping->Callback(mFinishDumpingData);
}
private:
~HandleReportAndFinishReportingCallbacks() {}
UniquePtr<JSONWriter> mWriter;
nsCOMPtr<nsIFinishDumpingCallback> mFinishDumping;
nsCOMPtr<nsISupports> mFinishDumpingData;
};
NS_IMPL_ISUPPORTS(HandleReportAndFinishReportingCallbacks,
nsIHandleReportCallback, nsIFinishReportingCallback)
class TempDirFinishCallback final : public nsIFinishDumpingCallback
{
public:
NS_DECL_ISUPPORTS
TempDirFinishCallback(nsIFile* aReportsTmpFile,
const nsCString& aReportsFinalFilename)
: mReportsTmpFile(aReportsTmpFile)
, mReportsFilename(aReportsFinalFilename)
{
}
NS_IMETHOD Callback(nsISupports* aData) override
{
// Rename the memory reports file, now that we're done writing all the
// files. Its final name is "memory-report<-identifier>-<pid>.json.gz".
nsCOMPtr<nsIFile> reportsFinalFile;
nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
getter_AddRefs(reportsFinalFile));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
#ifdef ANDROID
rv = reportsFinalFile->AppendNative(NS_LITERAL_CSTRING("memory-reports"));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
#endif
rv = reportsFinalFile->AppendNative(mReportsFilename);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = reportsFinalFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoString reportsFinalFilename;
rv = reportsFinalFile->GetLeafName(reportsFinalFilename);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mReportsTmpFile->MoveTo(/* directory */ nullptr,
reportsFinalFilename);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Write a message to the console.
nsCOMPtr<nsIConsoleService> cs =
do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsString path;
mReportsTmpFile->GetPath(path);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsString msg = NS_LITERAL_STRING("nsIMemoryInfoDumper dumped reports to ");
msg.Append(path);
return cs->LogStringMessage(msg.get());
}
private:
~TempDirFinishCallback() {}
nsCOMPtr<nsIFile> mReportsTmpFile;
nsCString mReportsFilename;
};
NS_IMPL_ISUPPORTS(TempDirFinishCallback, nsIFinishDumpingCallback)
static nsresult
DumpMemoryInfoToFile(
nsIFile* aReportsFile,
nsIFinishDumpingCallback* aFinishDumping,
nsISupports* aFinishDumpingData,
bool aAnonymize,
bool aMinimizeMemoryUsage,
nsAString& aDMDIdentifier)
{
RefPtr<nsGZFileWriter> gzWriter = new nsGZFileWriter();
nsresult rv = gzWriter->Init(aReportsFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
auto jsonWriter =
MakeUnique<JSONWriter>(MakeUnique<GZWriterWrapper>(gzWriter));
nsCOMPtr<nsIMemoryReporterManager> mgr =
do_GetService("@mozilla.org/memory-reporter-manager;1");
// This is the first write to the file, and it causes |aWriter| to allocate
// over 200 KiB of memory.
jsonWriter->Start();
{
// Increment this number if the format changes.
jsonWriter->IntProperty("version", 1);
jsonWriter->BoolProperty("hasMozMallocUsableSize",
mgr->GetHasMozMallocUsableSize());
jsonWriter->StartArrayProperty("reports");
}
RefPtr<HandleReportAndFinishReportingCallbacks>
handleReportAndFinishReporting =
new HandleReportAndFinishReportingCallbacks(Move(jsonWriter),
aFinishDumping,
aFinishDumpingData);
rv = mgr->GetReportsExtended(handleReportAndFinishReporting, nullptr,
handleReportAndFinishReporting, nullptr,
aAnonymize,
aMinimizeMemoryUsage,
aDMDIdentifier);
return rv;
}
NS_IMETHODIMP
nsMemoryInfoDumper::DumpMemoryReportsToNamedFile(
const nsAString& aFilename,
nsIFinishDumpingCallback* aFinishDumping,
nsISupports* aFinishDumpingData,
bool aAnonymize)
{
MOZ_ASSERT(!aFilename.IsEmpty());
// Create the file.
nsCOMPtr<nsIFile> reportsFile;
nsresult rv = NS_NewLocalFile(aFilename, false, getter_AddRefs(reportsFile));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
reportsFile->InitWithPath(aFilename);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
bool exists;
rv = reportsFile->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!exists) {
rv = reportsFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
nsString dmdIdent = EmptyString();
return DumpMemoryInfoToFile(reportsFile, aFinishDumping, aFinishDumpingData,
aAnonymize, /* minimizeMemoryUsage = */ false,
dmdIdent);
}
NS_IMETHODIMP
nsMemoryInfoDumper::DumpMemoryInfoToTempDir(const nsAString& aIdentifier,
bool aAnonymize,
bool aMinimizeMemoryUsage)
{
nsString identifier(aIdentifier);
EnsureNonEmptyIdentifier(identifier);
// Open a new file named something like
//
// incomplete-memory-report-<identifier>-<pid>.json.gz
//
// in NS_OS_TEMP_DIR for writing. When we're finished writing the report,
// we'll rename this file and get rid of the "incomplete-" prefix.
//
// We do this because we don't want scripts which poll the filesystem
// looking for memory report dumps to grab a file before we're finished
// writing to it.
// The "unified" indicates that we merge the memory reports from all
// processes and write out one file, rather than a separate file for
// each process as was the case before bug 946407. This is so that
// the get_about_memory.py script in the B2G repository can
// determine when it's done waiting for files to appear.
nsCString reportsFinalFilename;
MakeFilename("unified-memory-report", identifier, getpid(), "json.gz",
reportsFinalFilename);
nsCOMPtr<nsIFile> reportsTmpFile;
nsresult rv;
// In Android case, this function will open a file named aFilename under
// specific folder (/data/local/tmp/memory-reports). Otherwise, it will
// open a file named aFilename under "NS_OS_TEMP_DIR".
rv = nsDumpUtils::OpenTempFile(NS_LITERAL_CSTRING("incomplete-") +
reportsFinalFilename,
getter_AddRefs(reportsTmpFile),
NS_LITERAL_CSTRING("memory-reports"));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
RefPtr<TempDirFinishCallback> finishDumping =
new TempDirFinishCallback(reportsTmpFile, reportsFinalFilename);
return DumpMemoryInfoToFile(reportsTmpFile, finishDumping, nullptr,
aAnonymize, aMinimizeMemoryUsage, identifier);
}
#ifdef MOZ_DMD
dmd::DMDFuncs::Singleton dmd::DMDFuncs::sSingleton;
nsresult
nsMemoryInfoDumper::OpenDMDFile(const nsAString& aIdentifier, int aPid,
FILE** aOutFile)
{
if (!dmd::IsRunning()) {
*aOutFile = nullptr;
return NS_OK;
}
// Create a filename like dmd-<identifier>-<pid>.json.gz, which will be used
// if DMD is enabled.
nsCString dmdFilename;
MakeFilename("dmd", aIdentifier, aPid, "json.gz", dmdFilename);
// Open a new DMD file named |dmdFilename| in NS_OS_TEMP_DIR for writing,
// and dump DMD output to it. This must occur after the memory reporters
// have been run (above), but before the memory-reports file has been
// renamed (so scripts can detect the DMD file, if present).
nsresult rv;
nsCOMPtr<nsIFile> dmdFile;
rv = nsDumpUtils::OpenTempFile(dmdFilename,
getter_AddRefs(dmdFile),
NS_LITERAL_CSTRING("memory-reports"));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = dmdFile->OpenANSIFileDesc("wb", aOutFile);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "OpenANSIFileDesc failed");
// Print the path, because on some platforms (e.g. Mac) it's not obvious.
nsCString path;
rv = dmdFile->GetNativePath(path);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
dmd::StatusMsg("opened %s for writing\n", path.get());
return rv;
}
nsresult
nsMemoryInfoDumper::DumpDMDToFile(FILE* aFile)
{
RefPtr<nsGZFileWriter> gzWriter = new nsGZFileWriter();
nsresult rv = gzWriter->InitANSIFileDesc(aFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Dump DMD's memory reports analysis to the file.
dmd::Analyze(MakeUnique<GZWriterWrapper>(gzWriter));
rv = gzWriter->Finish();
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Finish failed");
return rv;
}
#endif // MOZ_DMD

View file

@ -0,0 +1,46 @@
/* -*- 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_nsMemoryInfoDumper_h
#define mozilla_nsMemoryInfoDumper_h
#include "nsIMemoryInfoDumper.h"
#include <stdio.h>
/**
* This class facilitates dumping information about our memory usage to disk.
*
* Its cpp file also has Linux-only code which watches various OS signals and
* dumps memory info upon receiving a signal. You can activate these listeners
* by calling Initialize().
*/
class nsMemoryInfoDumper : public nsIMemoryInfoDumper
{
virtual ~nsMemoryInfoDumper();
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMEMORYINFODUMPER
nsMemoryInfoDumper();
static void Initialize();
#ifdef MOZ_DMD
// Open an appropriately named file for a DMD report. If DMD is
// disabled, return a null FILE* instead.
static nsresult OpenDMDFile(const nsAString& aIdentifier, int aPid,
FILE** aOutFile);
// Write a DMD report to the given file and close it.
static nsresult DumpDMDToFile(FILE* aFile);
#endif
};
#define NS_MEMORY_INFO_DUMPER_CID \
{ 0x00bd71fb, 0x7f09, 0x4ec3, \
{ 0x96, 0xaf, 0xa0, 0xb5, 0x22, 0xb7, 0x79, 0x69 } }
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,288 @@
/* -*- 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 nsMemoryReporterManager_h__
#define nsMemoryReporterManager_h__
#include "mozilla/Mutex.h"
#include "nsHashKeys.h"
#include "nsIMemoryReporter.h"
#include "nsITimer.h"
#include "nsServiceManagerUtils.h"
#include "nsTHashtable.h"
namespace mozilla {
namespace dom {
class ContentParent;
class MemoryReport;
} // namespace dom
} // namespace mozilla
class nsITimer;
class nsMemoryReporterManager final : public nsIMemoryReporterManager
{
virtual ~nsMemoryReporterManager();
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIMEMORYREPORTERMANAGER
nsMemoryReporterManager();
// Gets the memory reporter manager service.
static nsMemoryReporterManager* GetOrCreate()
{
nsCOMPtr<nsIMemoryReporterManager> imgr =
do_GetService("@mozilla.org/memory-reporter-manager;1");
return static_cast<nsMemoryReporterManager*>(imgr.get());
}
typedef nsDataHashtable<nsRefPtrHashKey<nsIMemoryReporter>, bool> StrongReportersTable;
typedef nsDataHashtable<nsPtrHashKey<nsIMemoryReporter>, bool> WeakReportersTable;
// Inter-process memory reporting proceeds as follows.
//
// - GetReports() (declared within NS_DECL_NSIMEMORYREPORTERMANAGER)
// synchronously gets memory reports for the current process, sets up some
// state (mPendingProcessesState) for when child processes report back --
// including a timer -- and starts telling child processes to get memory
// reports. Control then returns to the main event loop.
//
// The number of concurrent child process reports is limited by the pref
// "memory.report_concurrency" in order to prevent the memory overhead of
// memory reporting from causing problems, especially on B2G when swapping
// to compressed RAM; see bug 1154053.
//
// - HandleChildReport() is called (asynchronously) once per child process
// reporter callback.
//
// - EndProcessReport() is called (asynchronously) once per process that
// finishes reporting back, including the parent. If all processes do so
// before time-out, the timer is cancelled. If there are child processes
// whose requests have not yet been sent, they will be started until the
// concurrency limit is (again) reached.
//
// - TimeoutCallback() is called (asynchronously) if all the child processes
// don't respond within the time threshold.
//
// - FinishReporting() finishes things off. It is *always* called -- either
// from EndChildReport() (if all child processes have reported back) or
// from TimeoutCallback() (if time-out occurs).
//
// All operations occur on the main thread.
//
// The above sequence of steps is a "request". A partially-completed request
// is described as "in flight".
//
// Each request has a "generation", a unique number that identifies it. This
// is used to ensure that each reports from a child process corresponds to
// the appropriate request from the parent process. (It's easier to
// implement a generation system than to implement a child report request
// cancellation mechanism.)
//
// Failures are mostly ignored, because it's (a) typically the most sensible
// thing to do, and (b) often hard to do anything else. The following are
// the failure cases of note.
//
// - If a request is made while the previous request is in flight, the new
// request is ignored, as per getReports()'s specification. No error is
// reported, because the previous request will complete soon enough.
//
// - If one or more child processes fail to respond within the time limit,
// things will proceed as if they don't exist. No error is reported,
// because partial information is better than nothing.
//
// - If a child process reports after the time-out occurs, it is ignored.
// (Generation checking will ensure it is ignored even if a subsequent
// request is in flight; this is the main use of generations.) No error
// is reported, because there's nothing sensible to be done about it at
// this late stage.
//
// - If the time-out occurs after a child process has sent some reports but
// before it has signaled completion (see bug 1151597), then what it
// successfully sent will be included, with no explicit indication that it
// is incomplete.
//
// Now, what what happens if a child process is created/destroyed in the
// middle of a request? Well, PendingProcessesState is initialized with an array
// of child process actors as of when the report started. So...
//
// - If a process is created after reporting starts, it won't be sent a
// request for reports. So the reported data will reflect how things were
// when the request began.
//
// - If a process is destroyed before it starts reporting back, the reported
// data will reflect how things are when the request ends.
//
// - If a process is destroyed after it starts reporting back but before it
// finishes, the reported data will contain a partial report for it.
//
// - If a process is destroyed after reporting back, but before all other
// child processes have reported back, it will be included in the reported
// data. So the reported data will reflect how things were when the
// request began.
//
// The inconsistencies between these cases are unfortunate but difficult to
// avoid. It's enough of an edge case to not be worth doing more.
//
void HandleChildReport(uint32_t aGeneration,
const mozilla::dom::MemoryReport& aChildReport);
void EndProcessReport(uint32_t aGeneration, bool aSuccess);
// Functions that (a) implement distinguished amounts, and (b) are outside of
// this module.
struct AmountFns
{
mozilla::InfallibleAmountFn mJSMainRuntimeGCHeap;
mozilla::InfallibleAmountFn mJSMainRuntimeTemporaryPeak;
mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsSystem;
mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsUser;
mozilla::InfallibleAmountFn mImagesContentUsedUncompressed;
mozilla::InfallibleAmountFn mStorageSQLite;
mozilla::InfallibleAmountFn mLowMemoryEventsVirtual;
mozilla::InfallibleAmountFn mLowMemoryEventsPhysical;
mozilla::InfallibleAmountFn mGhostWindows;
AmountFns()
{
mozilla::PodZero(this);
}
};
AmountFns mAmountFns;
// Convenience function to get RSS easily from other code. This is useful
// when debugging transient memory spikes with printf instrumentation.
static int64_t ResidentFast();
// Convenience function to get peak RSS easily from other code.
static int64_t ResidentPeak();
// Convenience function to get USS easily from other code. This is useful
// when debugging unshared memory pages for forked processes.
static int64_t ResidentUnique();
// Functions that measure per-tab memory consumption.
struct SizeOfTabFns
{
mozilla::JSSizeOfTabFn mJS;
mozilla::NonJSSizeOfTabFn mNonJS;
SizeOfTabFns()
{
mozilla::PodZero(this);
}
};
SizeOfTabFns mSizeOfTabFns;
private:
MOZ_MUST_USE nsresult
RegisterReporterHelper(nsIMemoryReporter* aReporter,
bool aForce, bool aStrongRef, bool aIsAsync);
MOZ_MUST_USE nsresult StartGettingReports();
// No MOZ_MUST_USE here because ignoring the result is common and reasonable.
nsresult FinishReporting();
void DispatchReporter(nsIMemoryReporter* aReporter, bool aIsAsync,
nsIHandleReportCallback* aHandleReport,
nsISupports* aHandleReportData,
bool aAnonymize);
static void TimeoutCallback(nsITimer* aTimer, void* aData);
// Note: this timeout needs to be long enough to allow for the
// possibility of DMD reports and/or running on a low-end phone.
static const uint32_t kTimeoutLengthMS = 50000;
mozilla::Mutex mMutex;
bool mIsRegistrationBlocked;
StrongReportersTable* mStrongReporters;
WeakReportersTable* mWeakReporters;
// These two are only used for testing purposes.
StrongReportersTable* mSavedStrongReporters;
WeakReportersTable* mSavedWeakReporters;
uint32_t mNextGeneration;
// Used to keep track of state of which processes are currently running and
// waiting to run memory reports. Holds references to parameters needed when
// requesting a memory report and finishing reporting.
struct PendingProcessesState
{
uint32_t mGeneration;
bool mAnonymize;
bool mMinimize;
nsCOMPtr<nsITimer> mTimer;
nsTArray<RefPtr<mozilla::dom::ContentParent>> mChildrenPending;
uint32_t mNumProcessesRunning;
uint32_t mNumProcessesCompleted;
uint32_t mConcurrencyLimit;
nsCOMPtr<nsIHandleReportCallback> mHandleReport;
nsCOMPtr<nsISupports> mHandleReportData;
nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
nsCOMPtr<nsISupports> mFinishReportingData;
nsString mDMDDumpIdent;
PendingProcessesState(uint32_t aGeneration, bool aAnonymize, bool aMinimize,
uint32_t aConcurrencyLimit,
nsIHandleReportCallback* aHandleReport,
nsISupports* aHandleReportData,
nsIFinishReportingCallback* aFinishReporting,
nsISupports* aFinishReportingData,
const nsAString& aDMDDumpIdent);
};
// Used to keep track of the state of the asynchronously run memory
// reporters. The callback and file handle used when all memory reporters
// have finished are also stored here.
struct PendingReportersState
{
// Number of memory reporters currently running.
uint32_t mReportsPending;
// Callback for when all memory reporters have completed.
nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
nsCOMPtr<nsISupports> mFinishReportingData;
// File handle to write a DMD report to if requested.
FILE* mDMDFile;
PendingReportersState(nsIFinishReportingCallback* aFinishReporting,
nsISupports* aFinishReportingData,
FILE* aDMDFile)
: mReportsPending(0)
, mFinishReporting(aFinishReporting)
, mFinishReportingData(aFinishReportingData)
, mDMDFile(aDMDFile)
{
}
};
// When this is non-null, a request is in flight. Note: We use manual
// new/delete for this because its lifetime doesn't match block scope or
// anything like that.
PendingProcessesState* mPendingProcessesState;
// This is reinitialized each time a call to GetReports is initiated.
PendingReportersState* mPendingReportersState;
PendingProcessesState* GetStateForGeneration(uint32_t aGeneration);
static MOZ_MUST_USE bool
StartChildReport(mozilla::dom::ContentParent* aChild,
const PendingProcessesState* aState);
};
#define NS_MEMORY_REPORTER_MANAGER_CID \
{ 0xfb97e4f5, 0x32dd, 0x497a, \
{ 0xba, 0xa2, 0x7d, 0x1e, 0x55, 0x7, 0x99, 0x10 } }
#endif // nsMemoryReporterManager_h__

View file

@ -0,0 +1,172 @@
/* -*- 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/. */
#include "nsMessageLoop.h"
#include "mozilla/WeakPtr.h"
#include "base/message_loop.h"
#include "base/task.h"
#include "nsIRunnable.h"
#include "nsITimer.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsComponentManagerUtils.h"
#include "nsThreadUtils.h"
using namespace mozilla;
namespace {
/**
* This Task runs its nsIRunnable when Run() is called, or after
* aEnsureRunsAfterMS milliseconds have elapsed since the object was
* constructed.
*
* Note that the MessageLoop owns this object and will delete it after it calls
* Run(). Tread lightly.
*/
class MessageLoopIdleTask
: public Runnable
, public SupportsWeakPtr<MessageLoopIdleTask>
{
public:
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MessageLoopIdleTask)
MessageLoopIdleTask(nsIRunnable* aTask, uint32_t aEnsureRunsAfterMS);
NS_IMETHOD Run() override;
private:
nsresult Init(uint32_t aEnsureRunsAfterMS);
nsCOMPtr<nsIRunnable> mTask;
nsCOMPtr<nsITimer> mTimer;
virtual ~MessageLoopIdleTask() {}
};
/**
* This timer callback calls MessageLoopIdleTask::Run() when its timer fires.
* (The timer can't call back into MessageLoopIdleTask directly since that's
* not a refcounted object; it's owned by the MessageLoop.)
*
* We keep a weak reference to the MessageLoopIdleTask, although a raw pointer
* should in theory suffice: When the MessageLoopIdleTask runs (right before
* the MessageLoop deletes it), it cancels its timer. But the weak pointer
* saves us from worrying about an edge case somehow messing us up here.
*/
class MessageLoopTimerCallback
: public nsITimerCallback
{
public:
explicit MessageLoopTimerCallback(MessageLoopIdleTask* aTask);
NS_DECL_ISUPPORTS
NS_DECL_NSITIMERCALLBACK
private:
WeakPtr<MessageLoopIdleTask> mTask;
virtual ~MessageLoopTimerCallback() {}
};
MessageLoopIdleTask::MessageLoopIdleTask(nsIRunnable* aTask,
uint32_t aEnsureRunsAfterMS)
: mTask(aTask)
{
// Init() really shouldn't fail, but if it does, we schedule our runnable
// immediately, because it's more important to guarantee that we run the task
// eventually than it is to run the task when we're idle.
nsresult rv = Init(aEnsureRunsAfterMS);
if (NS_FAILED(rv)) {
NS_WARNING("Running idle task early because we couldn't initialize our timer.");
NS_DispatchToCurrentThread(mTask);
mTask = nullptr;
mTimer = nullptr;
}
}
nsresult
MessageLoopIdleTask::Init(uint32_t aEnsureRunsAfterMS)
{
mTimer = do_CreateInstance("@mozilla.org/timer;1");
if (NS_WARN_IF(!mTimer)) {
return NS_ERROR_UNEXPECTED;
}
RefPtr<MessageLoopTimerCallback> callback =
new MessageLoopTimerCallback(this);
return mTimer->InitWithCallback(callback, aEnsureRunsAfterMS,
nsITimer::TYPE_ONE_SHOT);
}
NS_IMETHODIMP
MessageLoopIdleTask::Run()
{
// Null out our pointers because if Run() was called by the timer, this
// object will be kept alive by the MessageLoop until the MessageLoop calls
// Run().
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
if (mTask) {
mTask->Run();
mTask = nullptr;
}
return NS_OK;
}
MessageLoopTimerCallback::MessageLoopTimerCallback(MessageLoopIdleTask* aTask)
: mTask(aTask)
{
}
NS_IMETHODIMP
MessageLoopTimerCallback::Notify(nsITimer* aTimer)
{
// We don't expect to hit the case when the timer fires but mTask has been
// deleted, because mTask should cancel the timer before the mTask is
// deleted. But you never know...
NS_WARNING_ASSERTION(mTask, "This timer shouldn't have fired.");
if (mTask) {
mTask->Run();
}
return NS_OK;
}
NS_IMPL_ISUPPORTS(MessageLoopTimerCallback, nsITimerCallback)
} // namespace
NS_IMPL_ISUPPORTS(nsMessageLoop, nsIMessageLoop)
NS_IMETHODIMP
nsMessageLoop::PostIdleTask(nsIRunnable* aTask, uint32_t aEnsureRunsAfterMS)
{
// The message loop owns MessageLoopIdleTask and deletes it after calling
// Run(). Be careful...
RefPtr<MessageLoopIdleTask> idle =
new MessageLoopIdleTask(aTask, aEnsureRunsAfterMS);
MessageLoop::current()->PostIdleTask(idle.forget());
return NS_OK;
}
nsresult
nsMessageLoopConstructor(nsISupports* aOuter,
const nsIID& aIID,
void** aInstancePtr)
{
if (NS_WARN_IF(aOuter)) {
return NS_ERROR_NO_AGGREGATION;
}
nsISupports* messageLoop = new nsMessageLoop();
return messageLoop->QueryInterface(aIID, aInstancePtr);
}

View file

@ -0,0 +1,31 @@
/* -*- 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/. */
#include "nsIMessageLoop.h"
/*
* nsMessageLoop implements nsIMessageLoop, which wraps Chromium's MessageLoop
* class and adds a bit of sugar.
*/
class nsMessageLoop : public nsIMessageLoop
{
NS_DECL_ISUPPORTS
NS_DECL_NSIMESSAGELOOP
private:
virtual ~nsMessageLoop()
{
}
};
#define NS_MESSAGE_LOOP_CID \
{0x67b3ac0c, 0xd806, 0x4d48, \
{0x93, 0x9e, 0x6a, 0x81, 0x9e, 0x6c, 0x24, 0x8f}}
extern nsresult
nsMessageLoopConstructor(nsISupports* aOuter,
const nsIID& aIID,
void** aInstancePtr);

View file

@ -0,0 +1,230 @@
/* -*- 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/. */
// Undo the damage that exception_defines.h does.
#undef try
#undef catch
#ifndef nsObjCExceptions_h_
#define nsObjCExceptions_h_
#import <Foundation/Foundation.h>
#ifdef DEBUG
#import <ExceptionHandling/NSExceptionHandler.h>
#endif
#if defined(MOZ_CRASHREPORTER) && defined(__cplusplus)
#include "nsICrashReporter.h"
#include "nsCOMPtr.h"
#include "nsServiceManagerUtils.h"
#endif
#include <unistd.h>
#include <signal.h>
#include "nsError.h"
// Undo the damage that exception_defines.h does.
#undef try
#undef catch
/* NOTE: Macros that claim to abort no longer abort, see bug 486574.
* If you actually want to log and abort, call "nsObjCExceptionLogAbort"
* from an exception handler. At some point we will fix this by replacing
* all macros in the tree with appropriately-named macros.
*/
// See Mozilla bug 163260.
// This file can only be included in an Objective-C context.
__attribute__((unused))
static void
nsObjCExceptionLog(NSException* aException)
{
NSLog(@"Mozilla has caught an Obj-C exception [%@: %@]",
[aException name], [aException reason]);
#if defined(MOZ_CRASHREPORTER) && defined(__cplusplus)
// Attach exception info to the crash report.
nsCOMPtr<nsICrashReporter> crashReporter =
do_GetService("@mozilla.org/toolkit/crash-reporter;1");
if (crashReporter) {
crashReporter->AppendObjCExceptionInfoToAppNotes(static_cast<void*>(aException));
}
#endif
#ifdef DEBUG
@try {
// Try to get stack information out of the exception. 10.5 returns the stack
// info with the callStackReturnAddresses selector.
NSArray* stackTrace = nil;
if ([aException respondsToSelector:@selector(callStackReturnAddresses)]) {
NSArray* addresses = (NSArray*)
[aException performSelector:@selector(callStackReturnAddresses)];
if ([addresses count]) {
stackTrace = addresses;
}
}
// 10.4 doesn't respond to callStackReturnAddresses so we'll try to pull the
// stack info out of the userInfo. It might not be there, sadly :(
if (!stackTrace) {
stackTrace = [[aException userInfo] objectForKey:NSStackTraceKey];
}
if (stackTrace) {
// The command line should look like this:
// /usr/bin/atos -p <pid> -printHeader <stack frame addresses>
NSMutableArray* args =
[NSMutableArray arrayWithCapacity:[stackTrace count] + 3];
[args addObject:@"-p"];
int pid = [[NSProcessInfo processInfo] processIdentifier];
[args addObject:[NSString stringWithFormat:@"%d", pid]];
[args addObject:@"-printHeader"];
unsigned int stackCount = [stackTrace count];
unsigned int stackIndex = 0;
for (; stackIndex < stackCount; stackIndex++) {
unsigned long address =
[[stackTrace objectAtIndex:stackIndex] unsignedLongValue];
[args addObject:[NSString stringWithFormat:@"0x%lx", address]];
}
NSPipe* outPipe = [NSPipe pipe];
NSTask* task = [[NSTask alloc] init];
[task setLaunchPath:@"/usr/bin/atos"];
[task setArguments:args];
[task setStandardOutput:outPipe];
[task setStandardError:outPipe];
NSLog(@"Generating stack trace for Obj-C exception...");
// This will throw an exception if the atos tool cannot be found, and in
// that case we'll just hit our @catch block below.
[task launch];
[task waitUntilExit];
[task release];
NSData* outData =
[[outPipe fileHandleForReading] readDataToEndOfFile];
NSString* outString =
[[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding];
NSLog(@"Stack trace:\n%@", outString);
[outString release];
} else {
NSLog(@"<No stack information available for Obj-C exception>");
}
}
@catch (NSException* exn) {
NSLog(@"Failed to generate stack trace for Obj-C exception [%@: %@]",
[exn name], [exn reason]);
}
#endif
}
__attribute__((unused))
static void
nsObjCExceptionAbort()
{
// We need to raise a mach-o signal here, the Mozilla crash reporter on
// Mac OS X does not respond to POSIX signals. Raising mach-o signals directly
// is tricky so we do it by just derefing a null pointer.
int* foo = nullptr;
*foo = 1;
}
__attribute__((unused))
static void
nsObjCExceptionLogAbort(NSException* aException)
{
nsObjCExceptionLog(aException);
nsObjCExceptionAbort();
}
#define NS_OBJC_TRY(_e, _fail) \
@try { _e; } \
@catch(NSException *_exn) { \
nsObjCExceptionLog(_exn); \
_fail; \
}
#define NS_OBJC_TRY_EXPR(_e, _fail) \
({ \
typeof(_e) _tmp; \
@try { _tmp = (_e); } \
@catch(NSException *_exn) { \
nsObjCExceptionLog(_exn); \
_fail; \
} \
_tmp; \
})
#define NS_OBJC_TRY_EXPR_NULL(_e) \
NS_OBJC_TRY_EXPR(_e, 0)
#define NS_OBJC_TRY_IGNORE(_e) \
NS_OBJC_TRY(_e, )
// To reduce code size the abort versions do not reuse above macros. This allows
// catch blocks to only contain one call.
#define NS_OBJC_TRY_ABORT(_e) \
@try { _e; } \
@catch(NSException *_exn) { \
nsObjCExceptionLog(_exn); \
}
#define NS_OBJC_TRY_EXPR_ABORT(_e) \
({ \
typeof(_e) _tmp; \
@try { _tmp = (_e); } \
@catch(NSException *_exn) { \
nsObjCExceptionLog(_exn); \
} \
_tmp; \
})
// For wrapping blocks of Obj-C calls. Does not actually terminate.
#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK @try {
#define NS_OBJC_END_TRY_ABORT_BLOCK } @catch(NSException *_exn) { \
nsObjCExceptionLog(_exn); \
}
// Same as above ABORT_BLOCK but returns a value after the try/catch block to
// suppress compiler warnings. This allows us to avoid having to refactor code
// to get scoping right when wrapping an entire method.
#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL @try {
#define NS_OBJC_END_TRY_ABORT_BLOCK_NIL } @catch(NSException *_exn) { \
nsObjCExceptionLog(_exn); \
} \
return nil;
#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL @try {
#define NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL } @catch(NSException *_exn) { \
nsObjCExceptionLog(_exn); \
} \
return nullptr;
#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT @try {
#define NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT } @catch(NSException *_exn) { \
nsObjCExceptionLog(_exn); \
} \
return NS_ERROR_FAILURE;
#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN @try {
#define NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(_rv) } @catch(NSException *_exn) { \
nsObjCExceptionLog(_exn);\
} \
return _rv;
#endif // nsObjCExceptions_h_

109
xpcom/base/nsQueryObject.h Normal file
View file

@ -0,0 +1,109 @@
/* -*- 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 nsQueryObject_h
#define nsQueryObject_h
#include "mozilla/Attributes.h"
#include "nsCOMPtr.h"
#include "mozilla/RefPtr.h"
/*****************************************************************************/
template<class T>
class MOZ_STACK_CLASS nsQueryObject final : public nsCOMPtr_helper
{
public:
explicit nsQueryObject(T* aRawPtr)
: mRawPtr(aRawPtr)
{
}
virtual nsresult NS_FASTCALL operator()(const nsIID& aIID,
void** aResult) const
{
nsresult status = mRawPtr ? mRawPtr->QueryInterface(aIID, aResult)
: NS_ERROR_NULL_POINTER;
return status;
}
private:
T* MOZ_NON_OWNING_REF mRawPtr;
};
template<class T>
class MOZ_STACK_CLASS nsQueryObjectWithError final : public nsCOMPtr_helper
{
public:
nsQueryObjectWithError(T* aRawPtr, nsresult* aErrorPtr)
: mRawPtr(aRawPtr), mErrorPtr(aErrorPtr)
{
}
virtual nsresult NS_FASTCALL operator()(const nsIID& aIID,
void** aResult) const
{
nsresult status = mRawPtr ? mRawPtr->QueryInterface(aIID, aResult)
: NS_ERROR_NULL_POINTER;
if (mErrorPtr) {
*mErrorPtr = status;
}
return status;
}
private:
T* MOZ_NON_OWNING_REF mRawPtr;
nsresult* mErrorPtr;
};
/*****************************************************************************/
/*****************************************************************************/
template<class T>
inline nsQueryObject<T>
do_QueryObject(T* aRawPtr)
{
return nsQueryObject<T>(aRawPtr);
}
template<class T>
inline nsQueryObject<T>
do_QueryObject(nsCOMPtr<T>& aRawPtr)
{
return nsQueryObject<T>(aRawPtr);
}
template<class T>
inline nsQueryObject<T>
do_QueryObject(RefPtr<T>& aRawPtr)
{
return nsQueryObject<T>(aRawPtr);
}
template<class T>
inline nsQueryObjectWithError<T>
do_QueryObject(T* aRawPtr, nsresult* aErrorPtr)
{
return nsQueryObjectWithError<T>(aRawPtr, aErrorPtr);
}
template<class T>
inline nsQueryObjectWithError<T>
do_QueryObject(nsCOMPtr<T>& aRawPtr, nsresult* aErrorPtr)
{
return nsQueryObjectWithError<T>(aRawPtr, aErrorPtr);
}
template<class T>
inline nsQueryObjectWithError<T>
do_QueryObject(RefPtr<T>& aRawPtr, nsresult* aErrorPtr)
{
return nsQueryObjectWithError<T>(aRawPtr, aErrorPtr);
}
/*****************************************************************************/
#endif // !defined(nsQueryObject_h)

Some files were not shown because too many files have changed in this diff Show more