Bug 1492930 - Part 4. Add ImageMemoryReporter to support extra shared surfaces reporting. r=tnikkel

By delegating responsibility for shared surfaces reporting to imagelib,
we can cross reference the GPU shared surfaces cache with the local
surface cache in a content process (or the main process). This will
allow us to identify entries that are in the GPU cache but not in the
content/main process cache, and aid in debugging memory leaks. This
functionality is pref'd off by default behind image.mem.debug-reporting.

Additionally, we want to report every entry that was mapped into the
compositor process, in the compositor process memory report. This will
give us a sense of how much of our resident memory is consumed by mapped
images in absence of the more detailed cross referencing above.
This commit is contained in:
Andrew Osmond 2018-09-25 06:18:06 -04:00
parent d49646bb7a
commit 5d483b60ce
5 changed files with 292 additions and 0 deletions

View File

@ -557,6 +557,7 @@ private:
DECL_GFX_PREF(Live, "image.mem.discardable", ImageMemDiscardable, bool, false);
DECL_GFX_PREF(Once, "image.mem.animated.discardable", ImageMemAnimatedDiscardable, bool, false);
DECL_GFX_PREF(Live, "image.mem.animated.use_heap", ImageMemAnimatedUseHeap, bool, false);
DECL_GFX_PREF(Live, "image.mem.debug-reporting", ImageMemDebugReporting, bool, false);
DECL_GFX_PREF(Live, "image.mem.shared", ImageMemShared, bool, true);
DECL_GFX_PREF(Once, "image.mem.surfacecache.discard_factor", ImageMemSurfaceCacheDiscardFactor, uint32_t, 1);
DECL_GFX_PREF(Once, "image.mem.surfacecache.max_size_kb", ImageMemSurfaceCacheMaxSizeKB, uint32_t, 100 * 1024);

View File

@ -0,0 +1,187 @@
/* -*- 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 "ImageMemoryReporter.h"
#include "Image.h"
#include "mozilla/layers/SharedSurfacesParent.h"
#include "nsIMemoryReporter.h"
#include "nsISupportsImpl.h"
namespace mozilla {
namespace image {
ImageMemoryReporter::WebRenderReporter* ImageMemoryReporter::sWrReporter;
class ImageMemoryReporter::WebRenderReporter final : public nsIMemoryReporter
{
public:
NS_DECL_ISUPPORTS
WebRenderReporter()
{ }
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) override
{
layers::SharedSurfacesMemoryReport report;
layers::SharedSurfacesParent::AccumulateMemoryReport(report);
ReportSharedSurfaces(aHandleReport, aData, /* aIsForCompositor */ true, report);
return NS_OK;
}
private:
virtual ~WebRenderReporter()
{ }
};
NS_IMPL_ISUPPORTS(ImageMemoryReporter::WebRenderReporter, nsIMemoryReporter)
/* static */ void
ImageMemoryReporter::InitForWebRender()
{
MOZ_ASSERT(XRE_IsParentProcess() || XRE_IsGPUProcess());
if (!sWrReporter) {
sWrReporter = new WebRenderReporter();
RegisterStrongMemoryReporter(sWrReporter);
}
}
/* static */ void
ImageMemoryReporter::ShutdownForWebRender()
{
MOZ_ASSERT(XRE_IsParentProcess() || XRE_IsGPUProcess());
if (sWrReporter) {
UnregisterStrongMemoryReporter(sWrReporter);
sWrReporter = nullptr;
}
}
/* static */ void
ImageMemoryReporter::ReportSharedSurfaces(nsIHandleReportCallback* aHandleReport,
nsISupports* aData,
const layers::SharedSurfacesMemoryReport& aSharedSurfaces)
{
ReportSharedSurfaces(aHandleReport, aData,
/* aIsForCompositor */ false,
aSharedSurfaces);
}
/* static */ void
ImageMemoryReporter::ReportSharedSurfaces(nsIHandleReportCallback* aHandleReport,
nsISupports* aData,
bool aIsForCompositor,
const layers::SharedSurfacesMemoryReport& aSharedSurfaces)
{
MOZ_ASSERT_IF(aIsForCompositor, XRE_IsParentProcess() || XRE_IsGPUProcess());
MOZ_ASSERT_IF(!aIsForCompositor,
XRE_IsParentProcess() || XRE_IsContentProcess());
for (auto i = aSharedSurfaces.mSurfaces.begin();
i != aSharedSurfaces.mSurfaces.end(); ++i) {
ReportSharedSurface(aHandleReport, aData, aIsForCompositor, i->first, i->second);
}
}
/* static */ void
ImageMemoryReporter::ReportSharedSurface(nsIHandleReportCallback* aHandleReport,
nsISupports* aData,
bool aIsForCompositor,
uint64_t aExternalId,
const layers::SharedSurfacesMemoryReport::SurfaceEntry& aEntry)
{
nsAutoCString path;
if (aIsForCompositor) {
path.AppendLiteral("gfx/webrender/images/mapped_from_owner/");
} else {
path.AppendLiteral("gfx/webrender/images/owner_cache_missing/");
}
if (aIsForCompositor) {
path.AppendLiteral("pid=");
path.AppendInt(aEntry.mCreatorPid);
path.AppendLiteral("/");
}
if (gfxPrefs::ImageMemDebugReporting()) {
path.AppendPrintf("%016lx/", aExternalId);
}
path.AppendLiteral("image(");
path.AppendInt(aEntry.mSize.width);
path.AppendLiteral("x");
path.AppendInt(aEntry.mSize.height);
path.AppendLiteral(", compositor_ref:");
path.AppendInt(aEntry.mConsumers);
path.AppendLiteral(", creator_ref:");
path.AppendInt(aEntry.mCreatorRef);
path.AppendLiteral(")/decoded-nonheap");
size_t surfaceSize =
mozilla::ipc::SharedMemory::PageAlignedSize(aEntry.mSize.height *
aEntry.mStride);
// If this memory has already been reported elsewhere (e.g. as part of our
// explicit section in the surface cache), we don't want report it again as
// KIND_NONHEAP and have it counted again.
bool sameProcess = aEntry.mCreatorPid == base::GetCurrentProcId();
int32_t kind = aIsForCompositor && !sameProcess
? nsIMemoryReporter::KIND_NONHEAP
: nsIMemoryReporter::KIND_OTHER;
NS_NAMED_LITERAL_CSTRING(desc, "Decoded image data stored in shared memory.");
aHandleReport->Callback(EmptyCString(), path, kind,
nsIMemoryReporter::UNITS_BYTES,
surfaceSize, desc, aData);
}
/* static */ void
ImageMemoryReporter::AppendSharedSurfacePrefix(nsACString& aPathPrefix,
const SurfaceMemoryCounter& aCounter,
layers::SharedSurfacesMemoryReport& aSharedSurfaces)
{
uint64_t extId = aCounter.Values().ExternalId();
if (extId) {
auto gpuEntry = aSharedSurfaces.mSurfaces.find(extId);
if (gfxPrefs::ImageMemDebugReporting()) {
aPathPrefix.AppendPrintf(", external_id:%016lx", extId);
if (gpuEntry != aSharedSurfaces.mSurfaces.end()) {
aPathPrefix.AppendLiteral(", compositor_ref:");
aPathPrefix.AppendInt(gpuEntry->second.mConsumers);
} else {
aPathPrefix.AppendLiteral(", compositor_ref:missing");
}
}
if (gpuEntry != aSharedSurfaces.mSurfaces.end()) {
MOZ_ASSERT(gpuEntry->second.mCreatorRef);
aSharedSurfaces.mSurfaces.erase(gpuEntry);
}
}
}
/* static */ void
ImageMemoryReporter::TrimSharedSurfaces(const ImageMemoryCounter& aCounter,
layers::SharedSurfacesMemoryReport& aSharedSurfaces)
{
if (aSharedSurfaces.mSurfaces.empty()) {
return;
}
for (const SurfaceMemoryCounter& counter : aCounter.Surfaces()) {
uint64_t extId = counter.Values().ExternalId();
if (extId) {
auto gpuEntry = aSharedSurfaces.mSurfaces.find(extId);
if (gpuEntry != aSharedSurfaces.mSurfaces.end()) {
MOZ_ASSERT(gpuEntry->second.mCreatorRef);
aSharedSurfaces.mSurfaces.erase(gpuEntry);
}
}
}
}
} // image
} // mozilla

View File

@ -0,0 +1,96 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_image_ImageMemoryReporter_h
#define mozilla_image_ImageMemoryReporter_h
#include <cstdint>
#include "nsString.h"
#include "mozilla/layers/SharedSurfacesMemoryReport.h"
class nsISupports;
class nsIHandleReportCallback;
namespace mozilla {
namespace image {
struct ImageMemoryCounter;
struct SurfaceMemoryCounter;
class ImageMemoryReporter final
{
public:
/**
* Initializes image related memory reporting in the compositor process when
* using WebRender.
*/
static void InitForWebRender();
/**
* Tears down image related memory reporting in the compositor process when
* using WebRender.
*/
static void ShutdownForWebRender();
/**
* Report all remaining entries in the shared surface's memory report. This
* should be used by the content or main process to allow reporting any
* entries that is was unable to cross reference with the local surface cache.
* These are candidates for having been leaked. This should be used in
* conjunction with AppendSharedSurfacePrefix and/or TrimSharedSurfaces to
* produce the expected result.
*/
static void ReportSharedSurfaces(nsIHandleReportCallback* aHandleReport,
nsISupports* aData,
const layers::SharedSurfacesMemoryReport& aSharedSurfaces);
/**
* Adjust the path prefix for a surface to include any additional metadata for
* the shared surface, if any. It will also remove any corresponding entries
* in the given memory report.
*/
static void AppendSharedSurfacePrefix(nsACString& aPathPrefix,
const SurfaceMemoryCounter& aCounter,
layers::SharedSurfacesMemoryReport& aSharedSurfaces);
/**
* Remove all entries in the memory report for the given set of surfaces for
* an image. This is useful when we aren't reporting on a particular image
* because it isn't notable.
*/
static void TrimSharedSurfaces(const ImageMemoryCounter& aCounter,
layers::SharedSurfacesMemoryReport& aSharedSurfaces);
private:
/**
* Report all remaining entries in the shared surface's memory report.
*
* aIsForCompositor controls how to intepret what remains in the report. If
* true, this should mirror exactly what is currently in
* SharedSurfacesParent's cache. This will report entries that are currently
* mapped into the compositor process. If false, then we are in a content or
* main process, and it should have removed entries that also exist in its
* local surface cache -- thus any remaining entries are those that are
* candidates for leaks.
*/
static void ReportSharedSurfaces(nsIHandleReportCallback* aHandleReport,
nsISupports* aData,
bool aIsForCompositor,
const layers::SharedSurfacesMemoryReport& aSharedSurfaces);
static void ReportSharedSurface(nsIHandleReportCallback* aHandleReport,
nsISupports* aData,
bool aIsForCompositor,
uint64_t aExternalId,
const layers::SharedSurfacesMemoryReport::SurfaceEntry& aEntry);
class WebRenderReporter;
static WebRenderReporter* sWrReporter;
};
} // image
} // mozilla
#endif // mozilla_image_ImageMemoryReporter_h

View File

@ -53,6 +53,10 @@ EXPORTS += [
'SurfaceCacheUtils.h',
]
EXPORTS.mozilla.image += [
'ImageMemoryReporter.h',
]
UNIFIED_SOURCES += [
'AnimationFrameBuffer.cpp',
'AnimationSurfaceProvider.cpp',
@ -68,6 +72,7 @@ UNIFIED_SOURCES += [
'Image.cpp',
'ImageCacheKey.cpp',
'ImageFactory.cpp',
'ImageMemoryReporter.cpp',
'ImageOps.cpp',
'ImageWrapper.cpp',
'imgFrame.cpp',

View File

@ -4675,6 +4675,9 @@ pref("image.mem.animated.use_heap", true);
pref("image.mem.animated.use_heap", false);
#endif
// Enable extra information for debugging in the image memory reports.
pref("image.mem.debug-reporting", false);
// Decodes images into shared memory to allow direct use in separate
// rendering processes. Only applicable with WebRender.
pref("image.mem.shared", true);