mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-02 05:17:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
447
xpcom/base/AvailableMemoryTracker.cpp
Normal file
447
xpcom/base/AvailableMemoryTracker.cpp
Normal 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
|
||||
30
xpcom/base/AvailableMemoryTracker.h
Normal file
30
xpcom/base/AvailableMemoryTracker.h
Normal 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
|
||||
45
xpcom/base/ClearOnShutdown.cpp
Normal file
45
xpcom/base/ClearOnShutdown.cpp
Normal 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
|
||||
124
xpcom/base/ClearOnShutdown.h
Normal file
124
xpcom/base/ClearOnShutdown.h
Normal 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
|
||||
198
xpcom/base/CodeAddressService.h
Normal file
198
xpcom/base/CodeAddressService.h
Normal 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__
|
||||
140
xpcom/base/CountingAllocatorBase.h
Normal file
140
xpcom/base/CountingAllocatorBase.h
Normal 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
|
||||
1717
xpcom/base/CycleCollectedJSContext.cpp
Normal file
1717
xpcom/base/CycleCollectedJSContext.cpp
Normal file
File diff suppressed because it is too large
Load diff
494
xpcom/base/CycleCollectedJSContext.h
Normal file
494
xpcom/base/CycleCollectedJSContext.h
Normal 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
23
xpcom/base/Debug.cpp
Normal 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
21
xpcom/base/Debug.h
Normal 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__
|
||||
47
xpcom/base/DebuggerOnGCRunnable.cpp
Normal file
47
xpcom/base/DebuggerOnGCRunnable.cpp
Normal 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
|
||||
35
xpcom/base/DebuggerOnGCRunnable.h
Normal file
35
xpcom/base/DebuggerOnGCRunnable.h
Normal 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
|
||||
28
xpcom/base/DeferredFinalize.cpp
Normal file
28
xpcom/base/DeferredFinalize.cpp
Normal 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);
|
||||
}
|
||||
33
xpcom/base/DeferredFinalize.h
Normal file
33
xpcom/base/DeferredFinalize.h
Normal 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
1026
xpcom/base/ErrorList.h
Normal file
File diff suppressed because it is too large
Load diff
84
xpcom/base/ErrorNames.cpp
Normal file
84
xpcom/base/ErrorNames.cpp
Normal 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
25
xpcom/base/ErrorNames.h
Normal 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
|
||||
68
xpcom/base/HoldDropJSObjects.cpp
Normal file
68
xpcom/base/HoldDropJSObjects.cpp
Normal 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
|
||||
77
xpcom/base/HoldDropJSObjects.h
Normal file
77
xpcom/base/HoldDropJSObjects.h
Normal 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
|
||||
9
xpcom/base/JSObjectHolder.cpp
Normal file
9
xpcom/base/JSObjectHolder.cpp
Normal 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)
|
||||
42
xpcom/base/JSObjectHolder.h
Normal file
42
xpcom/base/JSObjectHolder.h
Normal 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
50
xpcom/base/LinuxUtils.cpp
Normal 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
34
xpcom/base/LinuxUtils.h
Normal 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
|
||||
170
xpcom/base/LogModulePrefWatcher.cpp
Normal file
170
xpcom/base/LogModulePrefWatcher.cpp
Normal 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
|
||||
42
xpcom/base/LogModulePrefWatcher.h
Normal file
42
xpcom/base/LogModulePrefWatcher.h
Normal 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
568
xpcom/base/Logging.cpp
Normal 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
255
xpcom/base/Logging.h
Normal 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
18
xpcom/base/MacHelpers.h
Normal 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
41
xpcom/base/MacHelpers.mm
Normal 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
|
||||
|
||||
54
xpcom/base/NSPRLogModulesParser.cpp
Normal file
54
xpcom/base/NSPRLogModulesParser.cpp
Normal 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
|
||||
22
xpcom/base/NSPRLogModulesParser.h
Normal file
22
xpcom/base/NSPRLogModulesParser.h
Normal 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
198
xpcom/base/OwningNonNull.h
Normal 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
96
xpcom/base/StaticMutex.h
Normal 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
270
xpcom/base/StaticPtr.h
Normal 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
|
||||
989
xpcom/base/SystemMemoryReporter.cpp
Normal file
989
xpcom/base/SystemMemoryReporter.cpp
Normal 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
|
||||
29
xpcom/base/SystemMemoryReporter.h
Normal file
29
xpcom/base/SystemMemoryReporter.h
Normal 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
159
xpcom/base/moz.build
Normal 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
336
xpcom/base/nsAgg.h
Normal 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
75
xpcom/base/nsAlgorithm.h
Normal 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
17
xpcom/base/nsAllocator.h
Normal 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
454
xpcom/base/nsAutoPtr.h
Normal 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
672
xpcom/base/nsAutoRef.h
Normal 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
12
xpcom/base/nsCom.h
Normal 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
|
||||
|
||||
|
||||
56
xpcom/base/nsConsoleMessage.cpp
Normal file
56
xpcom/base/nsConsoleMessage.cpp
Normal 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;
|
||||
}
|
||||
33
xpcom/base/nsConsoleMessage.h
Normal file
33
xpcom/base/nsConsoleMessage.h
Normal 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__ */
|
||||
473
xpcom/base/nsConsoleService.cpp
Normal file
473
xpcom/base/nsConsoleService.cpp
Normal 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;
|
||||
}
|
||||
118
xpcom/base/nsConsoleService.h
Normal file
118
xpcom/base/nsConsoleService.h
Normal 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__ */
|
||||
44
xpcom/base/nsCrashOnException.cpp
Normal file
44
xpcom/base/nsCrashOnException.cpp
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
23
xpcom/base/nsCrashOnException.h
Normal file
23
xpcom/base/nsCrashOnException.h
Normal 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
|
||||
4213
xpcom/base/nsCycleCollector.cpp
Normal file
4213
xpcom/base/nsCycleCollector.cpp
Normal file
File diff suppressed because it is too large
Load diff
72
xpcom/base/nsCycleCollector.h
Normal file
72
xpcom/base/nsCycleCollector.h
Normal 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__
|
||||
105
xpcom/base/nsCycleCollectorTraceJSHelpers.cpp
Normal file
105
xpcom/base/nsCycleCollectorTraceJSHelpers.cpp
Normal 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
607
xpcom/base/nsDebugImpl.cpp
Normal 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
41
xpcom/base/nsDebugImpl.h
Normal 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
513
xpcom/base/nsDumpUtils.cpp
Normal 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
203
xpcom/base/nsDumpUtils.h
Normal 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
219
xpcom/base/nsError.h
Normal 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
|
||||
50
xpcom/base/nsErrorService.cpp
Normal file
50
xpcom/base/nsErrorService.cpp
Normal 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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
37
xpcom/base/nsErrorService.h
Normal file
37
xpcom/base/nsErrorService.h
Normal 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__
|
||||
111
xpcom/base/nsGZFileWriter.cpp
Normal file
111
xpcom/base/nsGZFileWriter.cpp
Normal 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;
|
||||
}
|
||||
55
xpcom/base/nsGZFileWriter.h
Normal file
55
xpcom/base/nsGZFileWriter.h
Normal 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
|
||||
18
xpcom/base/nsIConsoleListener.idl
Normal file
18
xpcom/base/nsIConsoleListener.idl
Normal 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);
|
||||
};
|
||||
43
xpcom/base/nsIConsoleMessage.idl
Normal file
43
xpcom/base/nsIConsoleMessage.idl
Normal 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 }}
|
||||
%}
|
||||
56
xpcom/base/nsIConsoleService.idl
Normal file
56
xpcom/base/nsIConsoleService.idl
Normal 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"
|
||||
%}
|
||||
|
||||
164
xpcom/base/nsICycleCollectorListener.idl
Normal file
164
xpcom/base/nsICycleCollectorListener.idl
Normal 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
82
xpcom/base/nsIDebug2.idl
Normal 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);
|
||||
};
|
||||
49
xpcom/base/nsIErrorService.idl
Normal file
49
xpcom/base/nsIErrorService.idl
Normal 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} \
|
||||
}
|
||||
|
||||
%}
|
||||
86
xpcom/base/nsIException.idl
Normal file
86
xpcom/base/nsIException.idl
Normal 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();
|
||||
};
|
||||
82
xpcom/base/nsIGZFileWriter.idl
Normal file
82
xpcom/base/nsIGZFileWriter.idl
Normal 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
10
xpcom/base/nsIID.h
Normal 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 */
|
||||
36
xpcom/base/nsIInterfaceRequestor.idl
Normal file
36
xpcom/base/nsIInterfaceRequestor.idl
Normal 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);
|
||||
};
|
||||
|
||||
32
xpcom/base/nsIMacUtils.idl
Normal file
32
xpcom/base/nsIMacUtils.idl
Normal 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
78
xpcom/base/nsIMemory.idl
Normal 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();
|
||||
};
|
||||
|
||||
162
xpcom/base/nsIMemoryInfoDumper.idl
Normal file
162
xpcom/base/nsIMemoryInfoDumper.idl
Normal 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);
|
||||
};
|
||||
581
xpcom/base/nsIMemoryReporter.idl
Normal file
581
xpcom/base/nsIMemoryReporter.idl
Normal 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)
|
||||
|
||||
%}
|
||||
36
xpcom/base/nsIMessageLoop.idl
Normal file
36
xpcom/base/nsIMessageLoop.idl
Normal 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
22
xpcom/base/nsIMutable.idl
Normal 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;
|
||||
};
|
||||
25
xpcom/base/nsIProgrammingLanguage.idl
Normal file
25
xpcom/base/nsIProgrammingLanguage.idl
Normal 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;
|
||||
};
|
||||
20
xpcom/base/nsISecurityConsoleMessage.idl
Normal file
20
xpcom/base/nsISecurityConsoleMessage.idl
Normal 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
35
xpcom/base/nsISizeOf.h
Normal 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___ */
|
||||
90
xpcom/base/nsIStatusReporter.idl
Normal file
90
xpcom/base/nsIStatusReporter.idl
Normal 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 } }
|
||||
%}
|
||||
44
xpcom/base/nsISupports.idl
Normal file
44
xpcom/base/nsISupports.idl
Normal 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"
|
||||
%}
|
||||
85
xpcom/base/nsISupportsBase.h
Normal file
85
xpcom/base/nsISupportsBase.h
Normal 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
|
||||
39
xpcom/base/nsIUUIDGenerator.idl
Normal file
39
xpcom/base/nsIUUIDGenerator.idl
Normal 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);
|
||||
};
|
||||
49
xpcom/base/nsIVersionComparator.idl
Normal file
49
xpcom/base/nsIVersionComparator.idl
Normal 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);
|
||||
};
|
||||
|
||||
74
xpcom/base/nsIWeakReference.idl
Normal file
74
xpcom/base/nsIWeakReference.idl
Normal 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
|
||||
%}
|
||||
|
||||
86
xpcom/base/nsInterfaceRequestorAgg.cpp
Normal file
86
xpcom/base/nsInterfaceRequestorAgg.cpp
Normal 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;
|
||||
}
|
||||
38
xpcom/base/nsInterfaceRequestorAgg.h
Normal file
38
xpcom/base/nsInterfaceRequestorAgg.h
Normal 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__ )
|
||||
147
xpcom/base/nsMacUtilsImpl.cpp
Normal file
147
xpcom/base/nsMacUtilsImpl.cpp
Normal 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;
|
||||
}
|
||||
42
xpcom/base/nsMacUtilsImpl.h
Normal file
42
xpcom/base/nsMacUtilsImpl.h
Normal 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
184
xpcom/base/nsMemoryImpl.cpp
Normal 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
55
xpcom/base/nsMemoryImpl.h
Normal 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__
|
||||
830
xpcom/base/nsMemoryInfoDumper.cpp
Normal file
830
xpcom/base/nsMemoryInfoDumper.cpp
Normal 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
|
||||
|
||||
46
xpcom/base/nsMemoryInfoDumper.h
Normal file
46
xpcom/base/nsMemoryInfoDumper.h
Normal 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
|
||||
2717
xpcom/base/nsMemoryReporterManager.cpp
Normal file
2717
xpcom/base/nsMemoryReporterManager.cpp
Normal file
File diff suppressed because it is too large
Load diff
288
xpcom/base/nsMemoryReporterManager.h
Normal file
288
xpcom/base/nsMemoryReporterManager.h
Normal 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__
|
||||
172
xpcom/base/nsMessageLoop.cpp
Normal file
172
xpcom/base/nsMessageLoop.cpp
Normal 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);
|
||||
}
|
||||
31
xpcom/base/nsMessageLoop.h
Normal file
31
xpcom/base/nsMessageLoop.h
Normal 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);
|
||||
230
xpcom/base/nsObjCExceptions.h
Normal file
230
xpcom/base/nsObjCExceptions.h
Normal 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
109
xpcom/base/nsQueryObject.h
Normal 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
Loading…
Add table
Add a link
Reference in a new issue